Files
deepseek-harness/examples/desktop/docs/visualize-checklist-report.md
T
ZiyaZhang e8f5c0b51b feat(desktop): DSH Electron desktop shell — harness internals visualized
Minimal Electron shell over the DSH JSON-RPC runtime — a first-look at
what a ChatGPT.app-style host on top of the DeepSeek Harness looks
like, with the harness's normally-invisible internals (trace timeline,
context surface, subagent tree, compaction, plugin registry, rubrics)
brought forward as first-class UI surfaces so plugin authors and
researchers can see what the agent is actually doing.

Runs against three keyless-to-live profiles (stdio-echo works on
master out of the box; daemon-echo / daemon-vibe-echo activate once
the daemon-demo lands; stdio-deepseek and daemon-vibe hit the real
DeepSeek API when you supply a key). HARNESS_DEV auto-resolves to the
in-repo runtime when this shell ships under examples/desktop/, so a
fresh clone launches without config; env DSH_DEV_ROOT overrides for
custom layouts, and a sibling deepseek-harness-dev/ checkout is the
original dev workflow.

Cold-clone gate (P0 fixes for first-time-clone usability):
- HARNESS_DEV: 3-candidate resolver (env → walk-up in-repo marker →
  sibling), unit-tested via mock fs so ordering is locked without
  needing either real layout on disk.
- config yml leaves rewritten at assemble time so the sibling-clone
  paths (../../deepseek-harness-dev/examples/echo-agent/…) become
  the in-repo paths (../../echo-agent/…) in the released tree —
  source yml stays usable for local dev, released tree ships a
  working shape.
- pnpm-workspace.yaml allowBuilds.electron = true (was placeholder).
- missing-key card in stdio-deepseek offers a one-click switch to
  stdio-echo (the keyless profile that works on master) rather than
  daemon-echo (blocked on the not-yet-shipped daemon-demo).
- assemble-oss-release.sh rewrites the source-side breadcrumb name
  'dsh-desktop-demo' → 'dsh-desktop' for the released package.json.

FOUC guard on the onboarding gate (41fc5df carried) keeps the
first-launch splash from flashing before the runtime probe finishes.

Test suite (1634 tests in source, 3990 in the runtime repo) covers
resolver ordering, renderer classifiers, trace timeline shape,
compaction diff rendering, rubric parity, and the missing-key
onboarding paths.
2026-07-18 12:59:34 -07:00

7.8 KiB

Visualize checklist report — fix/default-profile-showcase

Branch: fix/default-profile-showcase Latest verification run: default profile stdio-deepseek, real DeepSeek API, 2 turns / 1 session, 2026-07-18 18:42 UTC. Script: scripts/showcase-12x12-verify.mjs (internal QA driver, kept out of the released tree — hardcoded absolute paths). Raw evidence: /tmp/dsh-showcase/result.json + replay.log (audit trail).

What lands in this branch

Four commits, each with a test lock:

  1. Config (config/deepseek-jsonrpc.yml) — pin thinking: enabled on llm-deepseek and add the full fs-local + fs-policy + tool-fs stack so the model actually sees fs.edit. Test: test/deepseek-jsonrpc-showcase.test.js.
  2. Renderer fallback (src/renderer/renderer.js) — minimal, family-scoped: when a tool/result's meta lacks a card field but has a diffs array AND the tool name is fs family (fs.edit / fs.write / fs.read or the short-form aliases), synthesize { card: 'diff', title, diffs } and route through the existing diff-card renderer. Test: test/renderer-diff-card-fallback.test.js + test/fixtures/fs-edit-wire-shape.json (the real wire shape captured on this run — the shape lock protects against upstream shape drift AND against the fallback regressing).
  3. Upstream ledger (docs/upstream-ledger.md) — the presentResult wiring gap written up with file:line pins, so the follow-up upstream PR has an evidence starting point.
  4. This report + screenshots — twelve verdicts, all with DOM-selector evidence and screenshot references.

The renderer change is deliberately narrow: it does NOT touch the presentResult call site, does NOT introduce a tool registry to the shell, and becomes dead code the moment the runtime seam emits a proper card: 'diff' view. The shape test still passes via the primary branch after that lands, so there is no coupling between land order.

12/12 verdict table

# Item Verdict Evidence
1 user bubble PASS .msg .role-label matches present in both turns of the stream.
2 assistant stream PASS .msg.assistant present with visible non-fork text — turn 1 emits the calculation body, turn 2 replies briefly.
3 reasoning fold PASS 6 .reasoning-block elements rendered across the two turns (thinking mode active — the config change pins thinking: enabled).
4 tool call row + args PASS 4 .tool-block rows across the two turns (bash + fs.read + fs.edit + supporting call); { } brace-button clicks reveal args.
5 diff card (family=fs) PASS 1 .card-diff element rendered after fs.edit; wire meta {diffs:[...]} (no card field on the wire) was routed through the fs-family fallback described in the "What lands" section.
6 terminal card (bash) PASS bash .tool-block.family-bash present; card body carries command text + captured stdout.
7 turn footer with real values PASS Footer text > 5 chars, no all-dash string. Shows real usage/duration numbers.
8 trace row + tri-view PASS .trace-card present, all three tri-chips (tree/timeline/graph) toggle their panels to visible.
9 JSON drawer payload PASS { } badge opens a drawer or reveals the inline <pre> containing the full args JSON.
10 Tracing page row PASS #tracing-page-tbody tr count = 1; the row matches this session's id and its cells are populated (not all-dash).
11 step boundary chips PASS .trace-step-chips / .trace-step-meta present in the stream.
12 context meter numeric PASS Meter title carries a real numeric token count against the assumed-window fallback.

12/12 pass.

Screenshots:

What the config change does

Two edits to config/deepseek-jsonrpc.yml:

  1. Pinned thinking: enabled on the llm-deepseek entry. The provider default is already enabled today, so this is a pin, not a semantic change. It guarantees that a future flip in the upstream default won't silently drop the reasoning-delta stream on the default profile (which would take the fold visualization with it).
  2. Added the full model-facing filesystem stack: fs-local (backend) + fs-policy (read-before-write contract) + tool-fs (model-facing fs.read/edit/write). The user's brief asked for fs-local alone (matching deepseek-vibe.yml), but the model needs tool-fs to actually see fs.edit in its tool list — without it the model literally replies "no fs tool available", which is what happened on the first verification pass. All three pieces are copy-mirrored from examples/coding-agent/cordis.yml, the canonical composition.

Test lock: test/deepseek-jsonrpc-showcase.test.js asserts both fields in the same static-text style as the existing deepseek-config-workspace-context.test.js.

What the renderer fallback does

Same tool wires the same meta shape today whether it succeeds through presentResult() or not — the runtime persists the tool's raw execute() return verbatim (see the upstream ledger). For fs.edit that shape is { diffs: [...] } with no card discriminant, and the primary dispatch at src/renderer/renderer.js:4744 was routing it to the raw-text fallback branch.

The added branch is scoped strictly to (!isError) + (fs-family tool name) + (view.diffs array present) + (no card field). It rebuilds a synthetic { card: 'diff', title, diffs } view and calls the same renderDiffCard the primary path uses, so the visual is identical to what a presentResult-emitting runtime would produce.

Profile tool matrix (default vs. vibe)

Both profiles now share the same headline-visualization surface. The other tools differ (vibe carries web + tool-cordis on top).

Profile thinking bash fs backend fs policy fs tools web tool-cordis
stdio-deepseek (default) enabled (pinned) yes fs-local fs-policy tool-fs
stdio-vibe-deepseek provider-default yes fs-local yes yes

Upstream ledger reference

The presentResult wiring gap — root cause of the "diff card never renders without a shell-side workaround" symptom — is written up as ledger entry L-1 in docs/upstream-ledger.md, with file-line pins into deepseek-harness:

  • packages/fs/tool-fs/src/edit.ts:92-96execute() returns raw {diffs} meta.
  • packages/fs/tool-fs/src/edit.ts:111-116presentResult() authors {card:'diff', ...} but is never invoked.
  • packages/core/agent-loop/src/loop.ts:584-594 — the runtime persists execute() meta verbatim on the wire.
  • packages/core/tools/src/index.ts:787-790 — the tool-registry side of the same seam.

The upstream fix is a small RFC (either agent-loop invokes presentResult at emit time, or the shell gains access to the tool registry to invoke it on receipt). Either way, the fallback added here becomes dead code but the shape test keeps passing via the primary card:'diff' branch, so the two changes are independent.

Isolation discipline followed

  • CDP 9310 (not 9273/9299).
  • --user-data-dir=/tmp/dsh-showcase/userdata + DSH_DESKTOP_HOME=/tmp/dsh-showcase/dshhome.
  • DSH_DEV_ROOT=$HOME/harness/deepseek-harness-dev.
  • .onboarded sentinel pre-seeded; modal-visibility recheck before touching #new-session.
  • DEEPSEEK_API_KEY read from ~/harness/deepseek-harness-dev/.env, never logged.
  • DSH_QA explicitly unset.
  • Child processes reclaimed on exit (SIGTERM then SIGKILL).
  • No touch to ~/.dsh-desktop, no touch to the user's Electron install.

Cost

2 real API turns on the final green run (well under the ≤6 budget). Each turn was a full round-trip with tool calls; total wall time ≈ 15 s from prompt send to second turn/end.