DeepSeek Harness slash command blank in a new session
The command ran; the UI skipped the whole page as a blank session in DeepSeek Harness. In a new session both command/run and command/done reach the log with kind set to success, but the snapshot's activity test does not count command nodes as content, so rendering stops at the blank phase: the render function is not broken, "what counts as content" just has two separate definitions. According to the discussion, the reporter reproduced this on the Web profile and community members reviewed the ui-conversation snapshot contract (discussion).
Confirm the command ran in DeepSeek Harness
Bottom line: read the log first, run a control second, and only then suspect the front end, because "executed" and "displayed" are different things in this chain.
- Open
~/.dsh/sessions/.../session.v4.jsonl.zstdand search forcommand/run. Expected: something like{"type":"command/run","seq":3,"data":{"name":"balance","source":{"kind":"user"}}}. - Search for
command/donenext. Expected:seq4,kindsuccess, and the samecommandId. - In the same workspace, open a session that already has messages and run the same
/balance. Expected: this time it displays normally. - Compare where the command events sit in both logs: in the populated session, message events precede the command; in the new session, the command is the first renderable content.
Root cause: two content tests, and command nodes fail both
Bottom line: snapshot.ts computes active from activeTargets plus blank / awaitingFirstTurn, and command nodes participate in neither. No DSH plugin is involved in this path; the decision lives in the host front end. Other blank-session causes are covered in blank session and hidden sidebar.
const active = conversation.activeTargets.size > 0
|| (!session.blank && !session.awaitingFirstTurn)
return active ? 'active' : session.promptAttempted ? 'engaging' : 'blank'
In this scenario both conditions fail:
- Command nodes never enter any view's
isActive, soactiveTargetsis empty. - The session never sent a message, so
session.blankis true andawaitingFirstTurnis also true, leaving the second term unable to catch it. - The phase resolves to
blank, the UI treats the session as having no content, and the command result is skipped. - The result is already persisted, so neither a refresh nor a reopen makes it appear.
Side by side, the only difference between the three session shapes is the phase:
| Session shape | activeTargets | blank / awaitingFirstTurn | Resulting phase |
|---|---|---|---|
| New session, command only | Empty | Both true | blank, not rendered |
| New session, command plus placeholder message | Empty | At least one false | active, rendered |
| Populated session, command | Possibly empty | Both false | active, rendered |
Changing isActive alone is not enough, and changing the phase alone is not enough either: the two tests are independent, so either change both or merge them into one "does this session have renderable content" function. This also explains how seq 3 and 4 can exist while the screen stays empty — storage follows events, rendering follows the snapshot, and the two never shared a definition of content.
Fix 1: count command nodes as active content in DeepSeek Harness
Bottom line: make command nodes a source of activity and delete the duplicated phase test, so the two definitions cannot drift apart again.
- Find the projection that produces the snapshot and identify which node a command event becomes. Expected: the node exposes
commandIdand the result text. - Count command nodes in
activeTargets, or add a command term directly to theactiveexpression:
const active = hasVisibleCommand(conversation)
|| conversation.activeTargets.size > 0
|| (!session.blank && !session.awaitingFirstTurn)
- Build and run
/balancein a new session. Expected: the result appears immediately. - Refresh and run it again. Expected: the result is still there. Switch to an empty session and back. Expected: no duplicate rendering.
- Compare a command-only session with a truly empty one. Expected: the first is active, the second stays blank.
Fix 2: insert a placeholder message after the DeepSeek Harness command
Bottom line: if you would rather not touch the phase test, the most robust detour is to let the command produce content, turning the blank session into a session with content.
- After the handler returns, append an ordinary message node containing a summary of the result and a line saying where it came from.
- Persist that message before or together with
command/done. Expected: the log shows both in seq order. - Run
/versionsin a new session. Expected: the message brings the session into the normal render path and the command result displays alongside it. - Check refresh, export and the session-list title. Expected: the placeholder does not rename the session after the command.
- Replay an old session that has only
command/doneand no placeholder. Expected: it stays blank, because this path does not retro-fit history.
Be explicit about the cost: the transcript gains a message, and a command-only session is no longer command-only.
Fix 3: render DeepSeek Harness command results independently of the phase
Bottom line: mount command results as their own block outside the phase gate — the most contained change of the three and the least dependent on the backend.
- Find the render entry for command results in the session view and confirm it currently sits inside the active branch.
- Move the command block outside the phase decision and render directly from the command nodes in the snapshot.
- Give the block an empty state and a collapse rule: no commands means no space; commands are shown in time order.
- Run
/balanceand/model-syncin a new session. Expected: both results appear. Run a command that fails. Expected: the error text is visible too. - Narrow the window to mobile width. Expected: the command block neither pushes the composer away nor collapses to zero height.
- Go offline and refresh. Expected: results restore from the local snapshot without an empty-state flicker.
Verify the DeepSeek Harness fix, and know when to roll back
Bottom line: acceptance covers "it renders", "it does not duplicate" and "old sessions do not regress", and missing one of the three breaks another session shape.
- Run
/balancein a new session. Expected: output is visible. Press Ctrl+F5 for a hard refresh. Expected: still visible, once. - Run the same command in a populated session. Expected: same position and styling as before the change.
- Run the same command twice in a row. Expected: two independent results, not deduplicated.
- If the command block and the transcript message duplicate each other, revert to the single-change version, confirm which path is maintained, and only then decide whether to merge the tests.
- Open the same session in two browser tabs. Expected: the result appears once, on the side that ran it, without a duplicate inserted by the concurrent refresh.
- Add the reproduction to regression tests: new session, one command, assert a visible output node exists.
Limits
Bottom line: the fixes change how commands are counted or drawn, and each leaves a different edge case behind.
- Only the Web profile reproduction is documented in the discussion; the same snapshot contract applies elsewhere, but verify on your own client.
- Fix 2 writes extra content into the session, which changes exports and any tooling that counts messages.
- Fix 3 bypasses the phase gate for commands only; if another feature also depends on the phase, it still sees
blank. - The code shape above comes from community review of the snapshot contract; upstream has not fixed it, so re-check after an upgrade.
DeepSeek Harness FAQ
Bottom line: these questions separate execution failures from rendering failures, which is the whole diagnosis.
Q: Does the command's side effect still happen, even with a blank screen? A: Yes. The handler runs and the event is persisted; only the display is missing. Re-running it because you saw nothing may apply the effect twice.
Q: I only changed the phase test and it works in the chat view. Is that enough?
A: Only for that view. Any other view that renders commands still fails its own isActive, and activeTargets remains empty.
Q: Why does a hard refresh not help? A: The snapshot is rebuilt from the same events, and the phase decision is stable, so the outcome is identical. This is not stale state.
FAQ
It did: the DeepSeek Harness session log contains both command/run and command/done with kind set to success, which means the handler ran and returned a result. The blank screen is a render-time decision, not a failed command.
Because the DeepSeek Harness session phase depends on whether any content exists. A session with messages fails both blank and awaitingFirstTurn and is judged active, so command results render as usual. A brand-new session sets both flags, so it falls into the blank branch.
No — in DeepSeek Harness, activeTargets aggregates isActive across views while the session phase separately reads blank and awaitingFirstTurn. Command nodes count in neither place, so changing one still leaves the other blocking it; change both or collapse them into one function.
Yes — DeepSeek Harness accepts an ordinary message node appended after command/done, so the session is no longer blank and the existing render path draws it. The cost is one extra message in the transcript, and refresh and export must accept that shape.
Related Terms
- session snapshot
- A read-only projection the front end renders from, containing the session phase, active targets and rendered nodes. Rendering decisions use the snapshot rather than reading the persistence log directly.— https://github.com/deepseek-ai/deepseek-harness/discussions/10266
- activeTargets
- The set in the snapshot that aggregates whether each view considers itself to have active content. A non-empty set makes the session active; membership comes from each view's isActive decision.— https://github.com/deepseek-ai/deepseek-harness/discussions/10266
- command/done
- The event written when a slash command finishes, carrying commandId, kind and the output text. It is the durable carrier of the result, but whether it renders depends on the snapshot phase.— https://github.com/deepseek-ai/deepseek-harness/discussions/10266
Sources
- deepseek-harness Discussion #10266: slash command results are not shown in a new session (command-only snapshots judged inactive)· deepseek-ai (GitHub Discussions)
- deepseek-harness repository: review of the ui-conversation snapshot contract· deepseek-ai (GitHub)