Append-only. Decisions are D-NNN; work items are W-NNN. Never rewrite
an entry; supersede it with a new one that references the old.
dashi is built outside the DSH repository as @antst/dashi (TUI plugin)
and @antst/dashi-app (shipped tui profile), launched as
dsh --profile tui. Zero required DSH core changes. Upstream PRs (CLI
alias, public helper exports) are optional and never a gate.
Why: the owner does not control upstream, upstream deleted its own TUI on
2026-08-04, and DSH's CLI already boots arbitrary profiles.
Repo ai/dashi on Forgejo (ssh://git@forgejo.antst.net:224/ai/dashi.git).
Zero GitHub name collisions. dshterm was considered and rejected for
pronunciation; dsh-tui is taken.
Copyright (c) 2026 Anton Starikov. DSH and pi-tui are both MIT.
DESIGN.md revision (1464 to 817 lines) accepted after gate check of 13
required changes, KISS filter, and contradiction pass. Key architecture:
one process, ctx.sessionController called in-process for list, fork,
history, cancel; one pure foldCells over event slices; one reducer plus
one effect runner; one current root exposed via ctx.tuiRoot.current()
and a single tui/root-changed event; no TUI persistence.
The unverifiable pi-tui version pin was removed before commit; the patch is described by behavior with a citation to the DSH archived note.
AGENTS.md (working agreement), LEDGER.md (this file), and one gate script
pnpm gate (typecheck, lint, tests, import lint). No CI until a remote
pipeline is needed. No further process tooling until a step is repeated
by hand three times.
Windows is not a supported target and not a release gate. No ConPTY tests,
no Windows-specific code paths, no pwsh handling. pi-tui's native
modifier-key modules are optional and unused on Linux; do not build them.
Test matrix: Linux and macOS PTYs, tmux, screen, SSH-like TERM values.
Why: the owner's users are on Linux and macOS; every Windows branch is
untested code.
The profile is named dashi, not tui, to avoid colliding with any
future upstream tui profile. Launch forms: dsh --profile dashi [args]
and the short dashi [args]. dashi is an npm bin in @antst/dashi-app:
a tiny Node file that invokes DSH's CLI entry in-process with
--profile dashi prepended; if that entry is not importable, it spawns
the pinned dsh bin from dashi's own node_modules. No shell script.
Why: one install command, DSH version pinned by our lockfile, no PATH
coupling. DESIGN.md still says tui; fix in the next doc pass.
Supersedes the profile name in D-001.
Supersedes the dashi bin in D-007. DSH resolves --profile NAME to
$DSH_HOME/profiles/NAME, created by dsh plugin --profile NAME add <published bundle>; the published @deepseek-ai/dsh package has a bin
only, nothing importable (cited by the builder: apps/cli/src/args.ts:130-145,
packages/boot/app-boot/src/profile.ts:127-134 and 805-837,
apps/cli/src/plugin.ts:120-130). Supported install:
npm i -g @deepseek-ai/dsh, then dsh plugin --profile dashi add @antst/dashi-app, then dsh --profile dashi. README documents
alias dashi='dsh --profile dashi'. @antst/dashi-app does not depend on
the DSH CLI package; it declares DSH peers the way DSH's own published
bundles do, and the profile pins DSH.
Rejected: a launcher that composes the profile on first run (lifecycle
and version drift), and a CLI dependency inside the bundle (breaks the
profile-local install). A zero-dependency exec-only launcher package may
be added later if users ask; it must never compose profiles.
The profile name dashi from D-007 stands.
Surveyed: openma-ai/Martty, huiliyi37/dsh-tianshu-tui, dsh-tui/dsh-tui, ccch1mneyyy/dsh-TUI, jame100101/dsh-terminal-ui, mervyn-teo/dsh-plugin-terminal, hust-open-atom-club/oh-dsh and others by search. Adopted:
- A validated-DSH-versions allowlist checked by the gate and, at boot, a one-line warning when the profile's DSH version is outside it. Two independent projects had to add this after drift across DSH pre-releases. It is a list and a comparison, nothing more.
- The import-boundary lint already in W-001 is the same idea as
dsh-TUI's
verify:boundary; keep it. - dsh-tui/dsh-tui is the closest architectural peer (pi-tui, out-of-tree
bundle over
@deepseek-ai/dsh-base,cordis.patch.ymlwithinject:ordering). Use its bundle composition as a reference for W-001; do not copy code. - Ctrl+O cycling a tool card collapsed, expanded, hidden (from dsh-tui/dsh-tui) goes into the feature map at the next doc pass. Rejected:
- Any local preference directory (dsh-TUI writes ~8 JSON files under
~/.dsh-tui/); dashi keeps no files. - LSP diagnostics, vision bridges, memory recall, i18n frameworks, and packaging subtrees; these are the observed feature-creep path.
- ACP out-of-process attachment (Martty). Its
_dsh/cordis/tui/*namespace is not in DSH at the pinned commit; in-process stays. - Forking the harness (dsh-terminal-ui).
- Patch-surface snapshots (dsh-TUI) until dashi actually disables or overrides an upstream Cordis row; then reconsider.
hust-open-atom-club/oh-dsh is a multi-surface distribution, not a TUI; it
vendors ccch1mneyyy/dsh-TUI and bootstraps profiles by shelling the
unmodified dsh plugin --profile <name> add <path> (its src/main.ts:879)
and launching dsh --profile <name> as a child process. D-008 stands.
Nothing adopted. Noted for later, not for dashi: the standalone
@deepseek-harness-tui/dsh-auth plugin provides subscription OAuth and
can be added to a profile by users themselves; auth is not dashi's job.
oh-dsh bootstraps its built-in profiles by writing
$DSH_HOME/profiles/<name>/package.json directly, merging its bundle
list into manifest.dsh.profile.bundles (its src/profile.ts:152); it
uses dsh plugin --profile X add <path> only for ad hoc local plugins.
Its TUI profile is ['@deepseek-ai/dsh-base', '@deepseek-harness-tui/dsh-tui', ...].
For dashi, D-008 still stands: users install with dsh plugin add, and
dashi never writes profile manifests on a user's machine. The dev-loop
script may write a throwaway manifest under an explicit DSH_HOME if that
is simpler than dsh plugin add with a local path; the builder decides
and cites the DSH manifest schema.
dashi is TypeScript in the DSH process. No sidecar process, no native addon, no second language. Why: DSH and pi-tui already own the only parts where native code earns its place (PTY, sandbox, terminal mechanics); a sidecar reintroduces the protocol and lifecycle boundary rejected in DESIGN.md; an addon adds a toolchain and prebuild matrix. If a profiler shows a hotspot: fix the algorithm, then laziness, and only then propose a native module as a ledger decision with measured numbers.
pi-tui 0.84.4's Editor always draws top and bottom horizontal rules with
symmetric padding and has no prompt-prefix hook (builder citation:
packages/tui/src/components/editor.ts:228-236, 482-497, 521-531,
573-588); editor-component.ts is only an interface. v1 uses the editor
unchanged: the top rule is the composer separator already in the design
mock, and the › prefix is dropped. Rejected: pnpm patch (what the old
DSH TUI did; carried on every upgrade), subclass stripping rendered rows
(undocumented output), own editor (2k+ lines). Optional, never a gate:
an upstream pi-tui PR adding frame and prefix options, specified by
the archived DSH note 2026-07-24-tui-shell-prompt-editor. DESIGN.md's
composer mock is updated at the next doc pass. W-002's stop condition
is resolved; the frameless check is removed from its scope.
Surveyed as of 2026-09-02 against the hard constraints (in-process Node
= 22, no reconciler, no native addon, permissive license, maintained, alt-screen diff redraw, editor with undo and paste, Markdown, CJK/emoji width): ink 7.1 (React + Yoga, no editor), terminal-kit 3.1 (no diff engine, basic input), blessed/neo-blessed/reblessed (dead), @unblessed (alpha), terminui 0.3 (renderer primitive only), clack (prompt flows), termui (Ink wrapper), OpenTUI 0.5.10. OpenTUI is the strongest alternative and reconciler-free at its core, but it runs under Node only on 26.4+ through the experimental
node:ffibehind--experimental-ffi(Node docs: memory-unsafe), and it loads prebuilt Zig libraries per platform; DSH's floor is Node 22 and dashi does not control the DSH process's flags. pi-tui has no frameless/prefix editor option through 0.84.4 (see D-013). Maintenance risk is ordinary: org-owned, outside contributors, several releases a month; pin exactly, upgrade on our schedule with the headless screen tests as the gate. Reopen only on new evidence: DSH raising its Node floor to a line wherenode:ffiis stable and default-on, or a pure-TypeScript library with an editor and Markdown appearing.
DSH's single-writer guarantee is in-process only: the persistence
contract claims atomic ownership (packages/session/session-persistence/src/index.ts:149-160)
but the JSONL backend's writer tracker is a per-process Map
(session-persistence-jsonl/src/storage.ts:319-361), and controller
resume adds no OS lock (api/session-controller/src/agent.ts:398-432).
Two dsh processes can resume the same session at once (builder's
two-PTY test). dashi does not add a lock file, a PID file, or any
detection: that would be TUI-owned persistence with stale-lock
reconciliation and a second authority. Consequences: the "second
writer rejected" statements in DESIGN.md 7.3 and D-008's --continue
wording are withdrawn at the next doc pass and replaced by "DSH gap:
no cross-process session lock"; W-003's writer-held acceptance item is
removed. Optional upstream PR, never a gate: an advisory flock on the
session directory in dsh-session-persistence-jsonl.
Optional upstream changes are handled by the Agent Sessions Codex lane
upstream-prs (cwd ~/upstream, gpt-5.6-sol, xhigh, no auto-archive),
one PR at a time, plan approved by the architect before each PR is
opened, drafts under the owner's GitHub account. Queue: (1) DSH
cross-process session writer lock (D-015); (2) pi-tui Editor frame
and prefix options (D-013); (3) DSH FileSystem delete primitive
(roller D-003). Held: command presenters, ignorable custom event types,
public ui-chat helper exports. None is a gate for dashi or roller.
The three held PRs in D-016 are queued to the lane when a product can consume them: command presenters after roller's restore command fixes its output; ignorable custom event types right after, if a durable restore event would replace the text report; ui-chat helper exports when dashi's fold reaches Phase B and the imports would delete lines.
Supersedes the PR mechanics in D-016 and D-017. DSH's CONTRIBUTING.md
states external pull requests are not accepted and asks for bug reports
in GitHub Discussions. pi's CONTRIBUTING.md auto-closes issues and PRs
from contributors without a maintainer lgtm; a PR may follow only
after that reply. The upstream-prs lane therefore files one
well-evidenced report per item through the sanctioned channel, text
approved by the architect before posting, and opens a pi PR only if a
maintainer grants lgtm. Consequence: every named DSH gap stays a gap
for the foreseeable future; dashi and roller are designed for that.
DSH's session controller retains the AgentHandle for every root it creates or resumes and exposes no release (pinned packages/api/session-controller/src/agent.ts:63-66, 428-432, 460-485; index.ts:185-212); only the handle holder may dispose (packages/core/agent/src/index.ts:151-168). dashi therefore cannot dispose a previous root after /new, /resume, /fork, or /rewind. The DESIGN.md binding rule "dispose previous through DSH" is withdrawn at the next doc pass and replaced by: the previous root is unbound and stays live and idle until profile teardown, which is the Web UI's own model. Safety rule added: /new, /resume, /fork, and /rewind require an idle root and offer to interrupt first, so an unbound root is never running unattended with unclaimed approvals. Rejected: calling ctx.agents directly (duplicates controller composition) and any upstream API as a gate. W-006's carry-over test is redefined: on /new and /resume, tui/root-changed fires with the exact previous and current agents, the previous agent stays registered and idle, and all roots are torn down at exit by the profile.
The frame/prefix change is on the fork and linked from pi issue 9032. No pings, no rework unless a maintainer asks, no carrying the patch. If no maintainer response by the time Phase B closes (W-011 accepted), the item is closed in the ledger and the branch stays as a record. D-013 stands regardless.
Requirement from the Agent Sessions architect session: a DSH lane busy
on a long turn is unreachable over ACP. Answer given: no harness or
dashi change; an AS-owned Cordis plugin in the profile calls
Agent.steer (next step boundary, the interject) or Agent.followup
(own next turn) with a caller-identified UserMessage; DSH appends
agent/inbox/spliced on acceptance (the receipt) and user/message
with the same id when consumed; results correlate by turn for followup
and by seq range for steer, as dashi's W-004 PTY test already proves
from the log. Caller chooses placement; rejection is DSH's own error.
Root via ctx.tuiRoot.current() in dashi profiles, ctx.agents.roots()
headless. This is DESIGN.md section 11 as intended; nothing to build here.
W-001 through W-011 accepted. No maintainer response on pi issue 9032 by Phase B close, so the item is closed; the fork branch stays as a record and D-013 stands. Phase C (hardening) begins after W-012.
Owner report: in full-screen mode the mouse wheel cycles prompt
history instead of scrolling, because mouse tracking is off, the
terminal translates wheel to arrow keys in the alternate screen, and
Up/Down are bound to recall. Decision: inline (main screen) becomes
the default, matching Claude Code and giving native scrollback, wheel,
and selection; --fullscreen remains and gains wheel scrolling via
pi-tui mouse tracking mapped to the existing scroll actions. DESIGN.md
5.1 default and 13.1 mouse rule are updated in W-013.
Owner request. @antst/dashi-launcher, a third package in the dashi
workspace with zero dependencies, exposing the dashi bin: it execs
dsh --profile dashi from PATH with the user's arguments, stdio
inherited and exit code propagated, and prints one hint when dsh is
missing or the profile does not exist. It never composes or installs a
profile, never pins DSH, and never reads or writes any file. This is
the launcher D-008 permitted "if users ask". Install becomes
npm i -g @deepseek-ai/dsh @antst/dashi-launcher, then the one
profile-add command; the README keeps the shell alias as the
no-extra-package alternative (owner decision 2026-09-03).
dashi-app now disables 24 upstream Cordis rows (the agent plane moved
behind agent presets, identical to web-app), so D-009's condition is
met. One test in Phase C asserts (a) every row dashi overrides or
disables exists in the installed @deepseek-ai/dsh-base patch, (b)
dashi's disabled set equals the set DSH's own web-app bundle disables,
and (c) every row dashi inserts that originates upstream exists in
web-app's inserted rows with the same package name, dashi's own row
excepted (corrected 2026-09-03: inserted rows come from web-app, not
base). A DSH upgrade that renames or adds agent-plane rows fails the
gate instead of silently dropping a tool. No snapshot files; the
comparison reads the installed packages at the validated version.
W-017's clean install from packed tarballs exposed that @antst/dashi
declared DSH services as dependencies, which in an out-of-tree profile
produced a second DSH tool registry and broke tool dispatch. The
workspace had masked it. Fix: DSH services are peer ranges in
@antst/dashi, matching DSH's own bundles and roller; pi-tui stays a
direct dependency; @antst/dashi-app gains the upstream code-runtime
row and its worker-thread package as web-app declares them. Rule: no
package publishes until the clean-install test passes from packed
tarballs; the 0.1.0-alpha.1 release commit predates this fix and would
have shipped the defect.
D-023's claim that inline mode gave native scrolling was false because dashi sliced the transcript to terminal height before rendering (renderer.ts:426-437), so nothing ever entered scrollback. pi-tui's TuiMainScreen renders the whole tree, scrolls the main screen by emitting newlines, and writes only changed rows on append (pinned pi packages/tui/src/tui-main-screen.ts:262-333, 461-545, 599-614), which is the model pi's own coding agent uses. Decision: inline mode hands the renderer the entire loaded chronological transcript plus the live tail; committed rows enter native scrollback once; the live cell, decisions, overlays, composer, and status are the mutable suffix; mouse tracking stays off so wheel and selection are the terminal's; PageUp and Ctrl+Home open the history overlay; older pages load only there and in full screen. Known and accepted: a change above the viewport (Ctrl+O card mode, resize) makes pi-tui clear and replay the loaded document, as it does for pi itself. Full screen keeps the viewport model. The reducer and fold are unchanged.
The AS-owned adapter (agent-sessions 8427b2a) is credited on DSH
0.1.2-alpha.5 as the first native implementer of AS's peer protocol
v1: base bundle plus adapter as the app, sessionController.create
with a caller id, Agent.steer/followup with agent/inbox/spliced
as receipt, cancel with keepInbox and canceled-splice reporting,
explicit presets (danger-full-access for yolo; the bundle's own
workspace-write-noninteractive row otherwise), cold confirmation via
sessionController.list, archive = cancel, idle, flush, appExit. No
dashi or DSH change was needed. dashi's interactive profile gains
the same adapter later (B-002).
Owner's first real install failed to load: DSH published a full
0.1.2-rc.1 set on 2026-09-03 (06:07-06:21 UTC), the dsh CLI declares
its own packages with caret ranges, so a fresh install mixed an alpha.5
CLI with rc.1 libraries whose imports the alpha.5 modules lack
(builder diagnosis: profiles/dashi lock resolves nine packages at
rc.1; first failing import is session-controller lib/index.js:10
admitPromptContent). D-008's "the profile pins DSH" was wrong: nothing
pinned it. Decision: @antst/dashi and @antst/dashi-app declare DSH
peer and dependency versions as the exact validated version, not
carets, so an incompatible installation fails to resolve rather than
load a mixed tree; the clean-install test asserts every resolved
@deepseek-ai/* version equals the validated version; the README
install instructions pin the CLI and its packages with an override
until DSH pins its own (corrected 2026-09-03: pnpm 11 ignores
pnpm.overrides in package.json; the override goes in
pnpm-workspace.yaml as overrides: {'@deepseek-ai/*': <v>}, which
pnpm 10 also reads; npm users use the overrides field). Moving to rc.1 is a separate
validation item, never an implicit upgrade.
Owner rule: effort goes to supporting the current DSH, not the past
one. When DSH publishes a newer release, dashi and roller validate
against it first (bump the allowlist and the pinned reference tree,
run the gate, fix what dashi's own code broke, report contract
changes); the exact pin from D-029 then carries the new version. The
alpha.5 pin plan for the owner's home install is withdrawn (their pnpm
10 also ignores a scoped wildcard override). Order: W-021 (validate
0.1.2-rc.1) before W-020 (exact pins), and W-020's pin value is rc.1.
Owner's home install made coherent at rc.1 on 2026-09-03 by removing
node_modules and the lockfile and reinstalling (pnpm keeps stale
peer-only packages on incremental installs); the dashi profile was
recreated on it and boots with the not-validated warning pending
W-021. README upgrade note (W-020): upgrading DSH means a fresh
lockfile, not an incremental install.
The scoped wildcard override is ignored by pnpm 10.28 (clean reproduction 2026-09-03: dsh-base resolved rc.1 despite an alpha.5 wildcard) and honored only by pnpm 11. Since dashi's packages pin DSH exactly (D-029) and we follow DSH forward (D-030), the README drops the override block entirely: install the CLI at the exact validated version, upgrade with a fresh lockfile, and expect dashi to be re-validated when DSH moves. An exact-name override list is rejected as brittle. Owner's profile rebuilt on 0.1.0-alpha.3 plus roller 0.1.1 and boots with no warning.
Owner report: the rewind pickers are confusing ("Before turn 2 · ls", "Edit and resend", "Conversation only") and the natural case, restore files and edit the prompt, is unreachable; steered prompts are absent. Decision, from Claude Code's documented flow: the first screen lists every prompt sent in the session by its text; the second screen offers "Restore code and conversation", "Restore conversation", "Restore code", "Never mind"; after a conversation restore the selected prompt's text is placed in the composer and never auto-sent; "Restore code" runs roller in the current session without a fork; the code options appear only when roller is installed. No "resend" variants, no "latest" row (that is /fork), no summarize options (DSH compaction is whole-session; a range summary is a named DSH gap). Steered prompts are listed and marked mid-turn; selecting one rewinds to the start of the turn that consumed it, because DSH forks only at turn boundaries (named DSH gap). Double Esc with text in the composer clears the draft into history instead of opening rewind, as Claude Code does.
Owner asks that dashi always come with roller. The profile bundle
@antst/dashi-app is the composition point, so it depends on
@antst/roller at an exact version and inserts roller's row after
dashi's; one dsh plugin add @antst/dashi-app installs both. The TUI
plugin @antst/dashi keeps no dependency on roller and still detects
roller-restore by name, so a custom profile without roller works
with the code rows absent. Releases of dashi-app bump the roller pin
deliberately; the validated-versions gate covers it.
Owner decision: same as roller D-007/W-008. After W-026 merges, the
current tree is pushed as a single initial commit to
https://github.com/antst/dashi; Forgejo stays as remote forgejo
with the old history; main is the release branch (tags), develop
the integration and default branch, work branches as pull requests
against develop, both branches protected so ledger edits also go
through pull requests. CI publishes previews of all three packages to
pkg.pr.new from develop and pull requests, and to npm with
provenance on GitHub releases, in dependency order (dashi, dashi-app,
launcher).
Owner decision: when W-027 is accepted, the builder prepares the release (bump all three manifests to 0.1.0-alpha.7, one CHANGELOG section covering W-025, W-027, W-032) as a pull request against develop; the architect merges develop to main and pushes the tag. No new work item starts before the tag is pushed. Plugin management in the session (Claude Code's /plugin and /mcp) is a real gap under rule 1 and becomes W-034, first item after the tag.
Owner ruling. For a DSH session the Agent Sessions name IS the DSH
session title: one fact, owned by DSH's title service. dashi --name
and /rename are therefore the only name surfaces; the comms plugin
reads the title and sets it when a wrapper passes a name through the
environment. Groups have no DSH-owned fact, so dashi exposes them as
-g <group> / --group <group>, repeatable, the same spelling as the
Agent Sessions launchers, implemented as the native
/agent-sessions group <g> command run at startup (the W-025 rule:
launch flags are the interactive commands applied before the first
prompt). The launcher stays a plain forwarder; no environment
synthesis. dashi-app bundles @agent-sessions/dsh-comms at an exact
published version (closes B-002).
Owner decision: roller-exec (peer codex-roller), idle since roller W-010, also builds dashi items. Rules: its own checkout at ~/dtui-2 (never ~/dtui, which belongs to dsh-exec); one builder per checkout; items assigned by the architect with disjoint code areas; same handoff protocol, same gate, same squash-merge. Assignment now: W-039 /config and W-041 /login and /logout to roller-exec; W-037, W-038, W-040 stay with dsh-exec. Later items are assigned at acceptance time.
Owner decision: macOS is a primary target and mac behavior cannot be
fixed blind from Linux. A Codex peer on the owner's mac
(mac-dashi, host mbp-lan, checkout /Users/antst/work/ai/dashi)
joins as a builder under the same protocol as dsh-exec and
roller-exec: branches from develop, PRs against develop, hosted gate,
squash-merge by the operations peer. It owns mac items; W-066's
hosted macos-latest job stays the durable regression gate. Its first
job is the host gate on the mac plus a real-terminal check, reported
as a classified list before any code.
D-039 (2026-09-05) Agent Sessions: launch token selects the lane profile; -g via environment; amends D-036
Owner rulings relayed through the Agent Sessions architect and the
accepted proposal (agent-sessions-dsh repo, docs/PROPOSAL.md,
73aafda). There is no --lane flag. Lane mode is the presence of
AGENTBUS_LAUNCH_TOKEN in the environment; the daemon runs
dashi with an empty argv. The dashi launcher does exactly two
things beyond forwarding: with the token present it spawns
dsh --profile agentbus (headless bundle + the plugin row with
mode: lane + session-controller and workspace rows), otherwise
--profile dashi; and it parses -g/--group (repeatable,
comma-splitting) into AGENTBUS_GROUPS as a JSON array string,
forwarding everything else untouched. The plugin never reads argv.
D-036's startup /agent-sessions group call is dropped: groups have
one source, the environment; the plugin's /agent-sessions command
is read-only in v1. The package is @agentbus/dsh (the
predecessor dsh-comms becomes a deprecated alias); dashi-app ships
it in peer mode by default at an exact npm pin; one connection per
root session; one name, the DSH title, re-announced in place on the
same connection. Cost recorded: the launcher is no longer a pure
forwarder (about 35 lines), and dsh --profile dashi without the
launcher has no -g.
Rename 2026-09-05: the bus product is agentbus; package @agentbus/dsh, kit @agentbus/kit, profile name agentbus, environment AGENTBUS_*.
Owner: dsh-exec. Branch w-001-scaffold.
Scope: pnpm workspace with packages/dashi and packages/dashi-app;
TypeScript config; pinned dependencies on published @deepseek-ai/*
packages at the versions matching DSH 0.1.2-alpha.5 and
@earendil-works/pi-tui@0.84.4; pnpm gate script running typecheck,
lint, unit tests, and an import lint that forbids persistence backends,
ui-* client packages, and any path import into node_modules source;
dsh --profile dashi boots the profile and exits cleanly with a plain
"dashi: profile booted" line and no terminal takeover yet.
Added 2026-09-02 (D-007): profile name is dashi.
Added 2026-09-02 (D-008): no dashi bin. Mirror a published DSH bundle's
package.json shape for @antst/dashi-app (peer ranges, no CLI dependency).
Provide one script for the dev loop that installs the local workspace
build into a dashi-dev profile and boots it; document it in README.
report in the handoff how DSH resolves --profile NAME to a package
(cite file:line in the pinned DSH tree) and whether the CLI entry is
importable in-process.
Not in scope: any renderer, any transcript, any command.
Acceptance evidence: gate output pasted in handoff; the boot line
reproduced in a PTY test; pnpm ls shows no unpinned or duplicate
@deepseek-ai versions; total new source under 300 lines excluding
config.
Owner: dsh-exec. Branch w-002-terminal-shell.
Scope: dsh --profile dashi takes over the terminal and shows the
empty document: header (cwd, no session yet), an empty transcript, the
composer, and the status line, in pi-tui alt screen by default and main
screen with --inline. TerminalGuard per DESIGN.md 9.6 owning raw mode,
bracketed paste, cursor, alt screen, and synchronized output, with one
idempotent async disposer reachable from every exit path including the
fail-loud release lesson (DSH note 2026-07-31). Application skeleton:
one immutable ViewState, one pure reducer, one effect runner, key
contexts for the portable minimum that applies without an agent
(Ctrl+C twice or Ctrl+D on empty composer exits, Ctrl+L redraws,
Ctrl+J newline, Ctrl+Z suspend/resume on Unix, Esc closes nothing yet).
Composer delegates editing to pi-tui's editor used unchanged (D-013:
framed, no prefix).
Not in scope: creating or resuming any DSH root, transcript content,
commands, completion, overlays, approvals, mouse.
Acceptance evidence: gate passes; @xterm/headless screen tests at 48,
80, and 160 columns and 8, 24 rows for both renderers; PTY tests prove
the terminal is restored after clean exit, Ctrl+C twice, SIGINT,
SIGTERM, a thrown error inside the effect runner, and closed stdin;
suspend/resume redraws; typing and pasting multi-line text into the
composer works; no pi-tui import outside the allowed modules; the
reducer has no async code and no DSH imports; new source under 900
lines excluding tests.
W-003 Root lifecycle and streaming transcript — status: accepted 2026-09-02 (66946bb, merged to main)
Gate notes for later phases: line cache has no theme key (one theme
exists); renderer recomputes all loaded cells per frame, bounded by the
50-message follow snapshot; viewport-plus-overscan is the Phase B lazy
materialization item.
Owner: dsh-exec. Branch w-003-root-and-transcript.
Scope: the first real conversation. Startup per DESIGN.md 5.2: fresh
session by default with a DSH-minted UUID, --name TITLE as a flushed
native title, --resume UUID exact, --continue per D-008 semantics,
[PROMPT] submitted after the first frame. Root creation and resume go
through ctx.sessionController where it has the operation and
ctx.agents otherwise; concurrent resume is a DSH gap (D-015), not tested.
ctx.tuiRoot.current() and the single tui/root-changed event per
DESIGN.md 7.2, with the binding rules as written (no more states).
Header shows title or UUID and cwd; /status is not in scope.
Enter submits via Agent.followup; Esc and Ctrl+C interrupt via
agent.cancel({kind:'user'},{keepInbox:true}); two-stage Ctrl+C exit
from W-002 stays. Transcript: foldCells v1 over event slices loaded
through ctx.sessionController page/follow, rendering user messages,
assistant text with streaming live tail coalesced to at most 30 fps,
provider-visible reasoning collapsed after completion, one generic
tool row per call/result pair (presenters come in W-004), turn
outcomes, and errors. Status line: root state, model id, and what Esc
does. Exit ordering per DESIGN.md 7.2 including drain and the
one-line summary with the resume command.
Model replacement for tests: reuse DSH's own test-support or fake
provider if the pinned tree has one (look in packages/test-support and
packages/llm); if not, the smallest recorded-stream provider plugin
that DSH's llm contract allows, in tests only, and cite the contract.
Not in scope: tool presenters and diffs, approvals, questions,
steer/queue send mode, commands, completion, pickers, rewind, search,
history overlay, images.
Acceptance evidence: gate passes; PTY test with the recorded provider
runs a full turn (prompt, streamed answer, one tool call rendered as a
generic row) and exits with the summary line; a second PTY run resumes
by exact UUID and shows the prior turn; --name proves a
session/title event in the durable log with no messages sent;
interrupt mid-stream leaves the terminal restored and the session
resumable; tui/root-changed fires exactly once at bind and once at
exit in the specified order (unit test with a fixture plugin);
foldCells is one exported pure function under 300 lines with fixture
tests over recorded logs; the reducer still imports nothing; new
production source under 1200 lines.
W-004 Tool cards, approvals, questions, steer and queue — status: accepted 2026-09-02 (443afd2, merged to main)
Carry-over tests into W-005: sanitizer test must include OSC and C1
cases; a test for an approval arriving while the composer holds text.
Owner: dsh-exec. Branch w-004-tools-and-decisions.
Scope: the generic tool row becomes presenter-backed: presentCall and
presentResult select the card, one generic card is the only fallback,
no argument parsing. Diff cells show file name and +/- totals collapsed
and a bounded contextual diff expanded. Large bodies bounded to first
12 and last 8 lines with an omitted-line count; no detail overlay yet.
Ctrl+O cycles one global tool-card mode: collapsed, expanded, hidden
(D-009). Approvals: DSH's approval/request becomes one decision
overlay with Allow once, Reject, Cancel, exactly the choices DSH
supports; one FIFO decision queue whose head owns focus; terminal loss
or exit rejects. Questions: DSH's user-questions service becomes one
overlay supporting batches, arrow and number selection, and custom
text, answered through DSH's own responder. Send mode: Ctrl+T toggles
steer or next-turn while the root is running; the footer states what
Enter does; Enter calls Agent.steer or Agent.followup per DESIGN.md
6.4; idle Enter unchanged.
Not in scope: commands, completion, pickers, detail or history overlay,
search, rewind, images, permission cycling, plan mode.
Acceptance evidence: gate passes; PTY with the replay provider drives
a turn whose tool call requires approval under a preset that asks, the
overlay is answered by keys, the durable log shows the decision, and
the card renders through the tool's own presenter; a fixture tool that
asks a batch question is answered including custom text; a steer sent
mid-stream appears at the next step and a next-turn message runs after
the turn ends, both proven from the durable log; Ctrl+O cycles through
the three modes in a headless screen test; hostile presenter output
(ANSI, control characters, oversized text) is sanitized in a unit
test; the reducer imports nothing; foldCells stays under 300 lines;
new production source under 1200 lines.
W-005 Commands, completion, pickers, session commands — status: accepted 2026-09-02 (13baa2a, merged to main)
Carry-over test into W-006: assert from the durable log or events that
the previous root's agent/disposed follows tui/root-changed on /new and
/resume.
Owner: dsh-exec. Branch w-005-commands-and-pickers.
First commit: the two carry-over tests from W-004.
Scope: slash commands per DESIGN.md 6.6. / completion with two
labeled groups: ctx.commands entries plus the small TUI-owned set,
and user-invocable skills from ctx.skills; completion inserts text
only; a submitted line starting with / goes to ctx.commands.execute
whole, and an undefined result means it was ordinary prompt input.
TUI-owned commands: /help, /status (unabridged UUID, title, cwd,
lineage, model, effort, permission preset, usage if DSH exposes it),
/new [--name TITLE], /resume [UUID] with a picker over
ctx.sessionController list showing UUID, title, cwd, updated,
/rename TITLE (native rename then flush), /model picker over the
model catalog with effort, /permission bare form as a picker over
ctx.permissionPresets, /queue TEXT, /exit. Root replacement for
/new and /resume follows the DESIGN.md 7.2 binding rules already
implemented. Shift+Tab cycles presets with a TUI-owned confirmation
when the target preset's approval policy is never (D-013 wording
rule applies: TUI-owned, keyed on the preset's policy). ? stays
ordinary input; F1 opens help. Overlays: exactly one at a time; a
decision preempts and closes any picker.
Not in scope: @ path completion, images, fork/rewind, history and
search overlays, external editor, stash, copy, inline polish.
Acceptance evidence: gate passes; PTY tests with the replay provider
prove: a native DSH command (use /compact or another shipped one)
executes and its command/run and command/done render as a command
cell not a prompt; a skill token in a prompt is not executed as a
command; /rename yields a session/title event; /new then
/resume via the picker switches roots with tui/root-changed firing
in order; /model changes the durable model selection; Shift+Tab
into danger-full-access shows the confirmation and a single keypress
does not switch; headless tests cover completion rendering for both
groups and each picker at 80 and 48 columns; the reducer imports
nothing; new production source under 1200 lines.
W-006 Fork, rewind, roller integration, prompt recall — status: accepted 2026-09-02 (ada4bfa, merged to main)
Verified: native fork with parent untouched and resumable; roller
end-to-end restore proven through the installed profile (closes roller
D-005); SQLite query provider enabled declaratively in the profile
patch; previous roots retained idle per D-019. One send-back: the
double-Esc PTY test needed a gap above pi-tui's 10 ms escape window.
Owner: dsh-exec. Branch w-006-rewind.
First commit: the carry-over test from W-005 as redefined by D-019.
Scope: /fork and /rewind per DESIGN.md section 8 and D-002:
require idle (offer to interrupt), present balanced boundaries as the
state before each human prompt plus the latest completed boundary,
call ctx.sessionController.fork for the chosen boundary, switch
roots by the existing replacement path, and put the selected prompt
back in the composer when "edit and resend" was chosen. Esc Esc on
an idle empty composer opens the rewind picker. The picker states
"Files will not be changed" unless roller is present. roller
integration: when ctx.commands lists roller-restore for the root,
the picker offers "conversation and files"; choosing it forks, then
executes /roller-restore <turn-end-seq> in the child through
ctx.commands.execute, and the command cell shows roller's report.
dashi has no dependency on roller; presence is checked by name at
picker-open time only. Prompt recall: Up and Down over the current
session's human prompts and Ctrl+R reverse search over
ctx.sessionQuery in the current cwd, inserting text only. /status
shows "forked from at turn N" for children.
Not in scope: history overlay, transcript search, lazy paging, images,
path completion, external editor, stash, copy.
Acceptance evidence: gate passes; PTY with the replay provider: a
three-turn session, /rewind to before turn 2 with "edit and resend"
yields a new root whose log is the parent prefix plus nothing, the
composer holds the turn-2 prompt, and the parent session is unchanged
and resumable; the same with roller installed in the dashi-dev profile
(add @antst/roller from its local build) and "conversation and
files" restores the workspace files to their turn-1 bytes and shows
roller's report cell, which is the end-to-end proof roller D-005
deferred to dashi; Esc Esc on idle opens the picker and on a running
root does nothing; Ctrl+R finds a prompt from an earlier session in
the same cwd; the reducer imports nothing; new production source
under 900 lines.
W-007 History overlay, transcript search, lazy history — status: accepted 2026-09-02 (f2ac2b5, merged to main)
Verified: materialized cells at the top of a 200k-cell transcript
within viewport plus 8; key-to-frame p95 10.26 ms; cold resume of a
200k-event session under 3 s from the resume command; OSC 52 payload
decoded and sanitized in tests. Carry-over into W-008: a test for copy
unavailability on TERM=dumb, and make that detection explicit about
the terminal type rather than the presence of an injected terminal.
Owner: dsh-exec. Branch w-007-history.
Scope: the Phase B navigation items. Lazy history: on start and
resume, load through ctx.sessionController page/follow so that only
the viewport plus an overscan window of cells is materialized; older
history pages in on demand when the user scrolls up (PageUp,
Ctrl+Home) and is evicted from the line cache by the existing bounded
rule; a 200k-event fixture session must open within DESIGN.md's gate.
Scrolling: PageUp/PageDown and Ctrl+Home/Ctrl+End over the transcript
while the composer keeps focus; when scrolled away from the end, new
activity increments a "new output" marker instead of moving the
viewport. Ctrl+O remains the card mode toggle. The history overlay
opens with /history: arrows select a
cell, Enter expands it, y copies its plain text through one
clipboard helper (OSC 52 or a platform helper; report unavailability,
never execute model text), / searches within the overlay, Esc
returns. Transcript search: Ctrl+F opens an in-transcript search
bar over loaded cells with next/previous and a match count; /copy
copies the latest completed assistant response as plain text; the
line cache gains a theme-generation key now that the cache is
exercised by paging (W-003 note).
Not in scope: composer extras (@ paths, images, external editor,
stash), plans/todos/jobs/subagents presentation, inline and
accessible polish, performance tuning beyond the one open gate.
Acceptance evidence: gate passes three consecutive runs; a recorded
200k-event session fixture (generate it once in tests, do not commit
it) resumes to a usable frame under 3 s after persistence load and
composer key-to-frame stays under 25 ms p95 measured in the headless
harness; scrolling up pages older history in and the cell count
materialized never exceeds viewport plus overscan (assert on the
renderer); the new-output marker appears and clears; the history
overlay, Ctrl+F search with counts, y copy, and /copy are proven
in headless tests, with clipboard output asserted on the terminal
output stream and never containing unsanitized sequences; the reducer
imports nothing; new production source under 900 lines.
W-008 Composer extras: paths, images, external editor, stash — status: accepted 2026-09-02 (ab0fade, merged to main)
Verified: cwd-bounded @ listing, durable ImageAttachmentRef on the
log, editor round-trip with mode 0600 temp file removed and terminal
modes compared, stash swap semantics, CSI-u modified Enter. Recorded
copy: at-trigger.ts (20 lines) mirrors the @ branch of pinned
ui-input-trigger core/detect.ts:50-59 because the published package
does not export it; a public export upstream is optional.
Owner: dsh-exec. Branch w-008-composer.
First commit: the carry-over test from W-007.
Scope: DESIGN.md composer items not yet built. @ path completion
rooted at the session cwd using DSH's existing input-trigger
matching (packages/client/ui-input-trigger core detect, pure) and
DSH's file reference service if the profile has one; selecting a
path inserts text, selecting an image-capable file attaches it.
Images: --image PATH (repeatable) resolved through DSH's attachment
policy and attached to the initial prompt or preloaded as chips;
visible attachment chips with remove and inspect; attachments travel
with the submitted message through DSH's own message attachment
path; no image decoding in dashi. Ctrl+G edits the draft in $VISUAL
then $EDITOR through TerminalGuard's cooked-terminal handoff, with a
mode-0600 temporary file unlinked on every path; saving replaces
text only. Ctrl+S stashes or restores one draft including cursor and
attachments, process-local. Ctrl+J newline remains; Shift+Enter and
Alt+Enter map to newline when the terminal reports them distinctly.
Not in scope: plans, todos, jobs, subagents, compaction presentation;
inline and accessible polish; performance work.
Acceptance evidence: gate passes three runs; PTY tests prove @
completion lists files under cwd only and never above it, an image
attached via @ and via --image reaches the durable user message
through DSH's attachment representation (assert on the log), Ctrl+G
round-trips through a fake $EDITOR that appends text with the
terminal restored afterwards and the temp file gone, Ctrl+S stash and
restore including an attachment, and Shift+Enter inserts a newline
when the PTY reports the distinct sequence; the reducer imports
nothing; new production source under 900 lines.
W-009 Plans, todos, jobs, subagents, compaction, context meter — status: accepted 2026-09-03 (c779da8, merged to main)
Verified: one PTY scenario through real plan, todo, subagent, job,
compaction, and token-meter paths; child events never in the root
transcript; neutral meter; second 34 ms timer accepted because it
debounces an independent control stream that would otherwise
enumerate sessions per subagent token. Recorded copy: the context
percentage formula from ui-conversation context-occupancy.ts:15-24.
Carry-over into W-010: README and DESIGN.md key table lack Ctrl+B.
Owner: dsh-exec. Branch w-009-presentation.
Scope: render what DSH's existing services already produce, nothing
more. Plan and todo cells: compact current state from the plan and
todo services' events or projections, earlier revisions collapsed;
plan-mode review answered through the existing decision overlay
(plan-review intent in DSH's question waterfall). Subagent and job
rows: one row per DSH subagent or job with label, state, elapsed
time, and DSH's own bounded summary; child transcripts never enter
the root transcript; a details overlay opens on demand and is the one
overlay. Compaction: rendered as a notice cell from DSH's compaction
events; /compact already works. Context meter: the status line
shows a neutral "NN% context" from the token-meter projection exactly
as the Web UI computes it (projected or pressure tokens over the
context window, clamped, shown only when both exist); DSH defines no
severity thresholds, so dashi shows none (amended 2026-09-02).
Not in scope: inline and accessible polish, performance table, any
new DSH capability; if a service is absent from the profile the cell
kind is simply never produced.
Acceptance evidence: gate passes three runs; PTY tests with the
replay provider drive: a plan created and revised (earlier revision
collapses), a plan-mode review answered through the overlay with the
durable outcome on the log, a todo list updated twice, a subagent
run whose child events do not appear in the root transcript while its
row shows state and summary and its details overlay opens, a job
started and completed, and a compaction with the notice cell and the
meter moving; foldCells stays under 320 lines (state the count) and
the reducer imports nothing; new production source under 900 lines.
W-010 Shell escape, bell, documentation pass — status: accepted 2026-09-03 (e50ed72, merged to main)
Verified: shell escape through ctx.shell only with DSH result fields,
injection inside Agent.runMaintenance (public DSH API, same use as
compaction-basic), replay-proven model request content, bell on both
triggers and silent in accessible mode, DESIGN.md 829 lines with every
withdrawn claim gone.
Owner: dsh-exec. Branch w-010-shell-and-docs.
First commit: README and DESIGN.md key table gain Ctrl+B (details
overlay) and Ctrl+O (card mode).
Scope: !command per DESIGN.md: a line starting with ! on an idle
root runs a bounded noninteractive command through DSH's own
shell/subprocess and sandbox services with the human as actor, never
as a tool call; the command and its capped stdout and stderr become
one identified message through Agent.inject with source
{kind:'plugin', plugin:'dashi', form:'notice', summary} (the shape
settled in the design pass), durable and visible to the next model
step without starting one; DSH cannot classify a command as
interactive or backgrounded before running it (amended 2026-09-03), so
dashi does not try: it calls ctx.shell.run only, with stdin closed, a
30 s deadline, and 32 KiB per stream; a command that waits for input
gets EOF, a command past the deadline is reported as timed out with
the suggestion to suspend with Ctrl+Z for interactive programs, and
detached processes are stated as unmanaged in the README; the
human's own command runs outside the tool approval path but inside
the session's sandbox policy. Bell: one BEL written through the terminal seam
when a decision (approval or question) is enqueued while the root was
running, and when a turn ends after running longer than a fixed
threshold; state the threshold; never in accessible mode; never
twice for one event. Documentation pass on DESIGN.md: fold in the
accepted decisions that changed it (D-007/D-008 profile and install,
D-013 framed editor, D-015 no writer lock, D-019 previous roots
retained, neutral meter, Ctrl+O and Ctrl+B), renumber nothing else,
keep it under 900 lines, and remove every sentence the ledger has
withdrawn; README gains the "Known DSH gaps" list matching the ledger.
Not in scope: clipboard image paste, inline and accessible polish,
performance table, vim mode.
Acceptance evidence: gate passes three runs; PTY tests prove !echo
output appears as a shell cell, the durable log holds one injected
message with the stated source and no model turn started, a
command that reads stdin gets EOF and ends, a command exceeding the
deadline is killed and reported as timed out with the suspend
suggestion, a sandbox-denied command reports the denial, and the next real prompt's model request
includes the injected content (assert through the replay provider's
recorded request or DSH's request/context event); the bell is
asserted on the terminal output stream for both triggers and absent
in accessible mode; DESIGN.md contains none of the withdrawn
sentences (list them in the handoff with grep proof); new production
source under 500 lines.
W-011 Clipboard image paste, inline and accessible polish, Phase B exit — status: accepted 2026-09-03 (c576252, merged to main)
Verified: 138 tests; measured p95 composer 0.3 ms normal and 10-13 ms
at 200k cells, streaming 28.9 fps with no forced scroll, 200k-event
resume with 2,000 tool cells about 2.2 s from launch, warm 1k-session
list 35 ms, heap delta 24 MiB, spawn-to-first-frame about 1.0 s. Phase B
is closed. Finding: DESIGN.md 5.3 sessions list --json was never
scheduled; opened as W-012.
Owner: dsh-exec. Branch w-011-phase-b-exit.
Scope: Ctrl+V when the composer receives no bracketed-paste text:
one boundary module asks the platform clipboard for an image (macOS
pngpaste if present, else osascript; Linux wl-paste then
xclip), hands the bytes to the existing image-input path, and
reports one line when no helper or no image is available; no helper
is bundled or installed. Inline mode: every feature works on the
main-screen renderer: pickers, decisions, history overlay, search,
details, rewind; output stays bounded; PTY tests run the daily flow
under --inline and prove no alternate-screen sequence is emitted.
Accessible mode: no spinner (a changing ASCII status word), ASCII
markers beside semantic status, overlays announce title, item count,
and selected item as text, linear reading order, no color-only
meaning; headless tests at 80 columns. Performance: every gate in
DESIGN.md's table has a measuring test; report the numbers in the
handoff. Phase B exit: README daily-use walkthrough from install to
rewind; Known DSH gaps complete against the ledger; dsh --profile dashi first-frame time measured and reported.
Not in scope: vim mode, Phase C matrix (tmux, screen, SSH terms,
fault injection), any new DSH capability.
Acceptance evidence: gate passes three runs; Ctrl+V with a fake
clipboard helper on PATH attaches an image that reaches the durable
log, and with no helper reports one line; the inline PTY flow
covers prompt, tool card, approval, picker, rewind, and exit with no
alt-screen sequence; accessible headless tests prove the announced
text for each overlay and the absence of spinner frames; the four
DESIGN.md performance gates each have a passing measurement; new
production source under 600 lines.
W-012 sessions list --json and small carry-overs — status: accepted 2026-09-03 (899b8f3, merged to main)
Verified: pure 67-line parser and formatter, list mode exits before any
terminal object exists, PTY proves cwd filtering and the JSON envelope
with no alt-screen sequence. 112 production lines.
Owner: dsh-exec. Branch w-012-sessions-list.
First commit: bell-count unit cases for OSC 8 and OSC 52 with both
BEL and ST terminators.
Scope: the DESIGN.md 5.3 subcommand as a profile-provided entry:
dsh --profile dashi sessions list [--cwd PATH | --all] [--json]
prints the session catalog from ctx.sessionController list without
taking over the terminal and exits: human columns UUID, TITLE, CWD,
UPDATED; JSON envelope {version: 1, sessions: [...]} with sessionId,
title (nullable), cwd, updatedAt, and no running field (design
revision accepted in D-004: the flag is process-local).
sessions show stays dropped. Refuse when the profile boots with a
TTY takeover already requested (mutually exclusive with prompt,
--resume, --continue, --name, --image).
Not in scope: anything else; Phase C follows.
Acceptance evidence: gate passes; a PTY test proves the human table
and the JSON envelope for two sessions in one cwd and the --all
switch, no alt-screen sequence, exit 0; new production source under
120 lines.
Verified: inline default with no alt-screen or mouse sequences; full
screen enables pi-tui tracking and a five-line pure wheel filter
behind a one-to-one forwarding Terminal; wheel scrolls without
disturbing the draft. 40 production lines. Nit for W-014: drop the
dead --fullscreen before --inline in one PTY launch string.
Owner: dsh-exec. Branch w-013-scrolling.
Scope per D-023: flip the default to inline and add --fullscreen;
in full-screen mode enable pi-tui mouse tracking and map wheel up and
down to the transcript scroll actions (state the lines per notch),
leaving Up/Down as prompt recall; no click handling beyond what the
design already allows; in inline mode mouse tracking stays off.
Update DESIGN.md 5.1 (default) and 13.1 (mouse rule) and README.
Acceptance evidence: gate passes; a PTY test in full-screen sends
wheel sequences and proves the transcript scrolls while the composer
text is unchanged and prompt recall is not triggered; a PTY test
proves inline mode emits no mouse-tracking enable sequence; the
default launch emits no alt-screen sequence; new production source
under 120 lines.
Verified by direct reading: 23-line bin, zero dependencies, inherited
stdio, signal forwarding, exact exit propagation, ENOENT hint only;
pack is bin, README, manifest; PTY proves identical first frame to the
direct command. 154 tests.
Owner: dsh-exec. Branch w-014-launcher.
Scope per D-024: packages/dashi-launcher with a dashi bin of under
30 lines and zero dependencies; execs dsh found on PATH with
--profile dashi prepended; on ENOENT prints one line saying DSH is
not installed with the install command; no profile-missing hint (amended
2026-09-03): DSH's own diagnostic already says create it with 'dsh plugin --profile dashi add <package>' (pinned app-boot profile.ts:
805-815), and detecting it would need a stderr tee; stdio is fully
inherited; the exit code is propagated exactly; signals are forwarded
so Ctrl+C reaches DSH. Package manifest like the others (MIT, repository, engines,
public access), version 0.1.0-alpha.1, README with the three-line
install. Import lint covers it. README and DESIGN.md 5.1 gain the
launcher form and keep the alias as the alternative.
Acceptance evidence: gate passes; PTY tests prove dashi boots the
real profile identically to dsh --profile dashi (same first frame),
propagates a nonzero exit, prints the DSH-missing hint on ENOENT, and that the bin has
no dependency (npm pack contents listed); under 30 lines.
W-015 Live slash completion and immediate command feedback — status: accepted 2026-09-03 (fd52c3a, merged to main)
Verified: completion opens on / and @ without Tab and narrows by
prefix then substring; the cause of the silent command was the submit
effect awaiting execute inside the single queue, fixed by resolving on
the command/run admission record; one animation timer exists only
while a command is pending; unmatched pending command cells are
dropped at the seed boundary. 159 production lines, 160 tests.
Owner: dsh-exec. Branch w-015-command-ux.
Owner reports from daily use (2026-09-03): (1) the completion popup
opens only on Tab; Claude Code opens it as soon as / is typed at the
start of the line and narrows on every keystroke; (2) /compact
gives no feedback until it finishes.
Scope: completion opens automatically on / at line start and on @
anywhere, filters live by prefix then substring on each keystroke,
arrows move, Enter accepts, Esc closes, Tab still accepts; no new
state beyond the existing overlay and the current filter string.
Command feedback: the command cell appears the moment DSH logs
command/run, in a pending state with the spinner (or the ASCII
status word in accessible mode) and elapsed time, the status line
says "running /name", and command/done completes the cell; find
why the run record is not rendered before completion (a coalescing
or await-ordering defect in the runtime) and fix the cause, not the
symptom. Applies to every native and TUI command.
Acceptance evidence: gate passes; headless tests prove the popup
opens on / and @ without Tab, narrows on typing, and Enter
executes the highlighted command; a PTY test with a slow fixture
command proves the pending row and status text appear before
command/done and complete after it, and the same for /compact
through the replay provider; new production source under 200 lines.
Verified: /clear, /tasks, /context, /agents, /export over existing
services; the profile now disables the same 24 agent-plane rows as
DSH's Web profile, required by the preset package's mount audit, with
the default preset re-mounting them; skills proven end to end from
.dsh/skills; /mcp omitted as a DSH gap. 174 production lines, 163 tests.
Owner: dsh-exec. Branch w-016-parity.
Basis: audit of Claude Code's slash commands against the dashi
profile (2026-09-03). Skills already work Claude-style: DSH's skill
tool injects a skill named by a /name token in a user message
(pinned packages/skill/tool-skill/src/index.ts:177-203) and dashi's
completion lists user-invocable skills; the gap is discovery paths.
Scope, each a small command over an existing DSH service, none
adding state: /clear as an alias of /new; /export [path]
writing the current session transcript as Markdown to a file under
the session cwd through DSH's session query or controller history
(DSH's own export command is Web-only: session-log-export index.ts:76);
/tasks opening the existing jobs and subagents overlay; /context
showing the token-meter contextBreakdown projection; /agents as a
picker over agent presets if the session controller exposes preset
selection, else a listing (cite); /mcp as read-only status of
connected servers and tools if the MCP client exposes a list (cite),
else omitted and named as a DSH gap. Skills discovery: DSH's own paths only
(.dsh/skills, .agents/skills, configured custom dirs,
$DSH_HOME/skills, ~/.agents/skills); dashi never reads
.claude/skills or ~/.claude/skills, because DSH may need different
skills than Claude (owner decision 2026-09-03). README gains a skills
section: DSH discovery paths, SKILL.md format, the /name gesture,
and that .claude/commands/*.md prompt files are not a DSH mechanism. README Known DSH gaps gains: login, hooks, add-dir, memory
editing, Git diff, autocompact tuning, MCP management.
Not in scope: IDE, vim, theme, any Anthropic-account feature, any new
DSH capability.
Acceptance evidence: gate passes; each new command has a headless or
PTY test through the real registry; /export output verified as a
file with the turn's user and assistant text; skills from a
.dsh/skills fixture appear in completion and inject on /name in a
PTY test; new production source under 400
lines.
W-017 Phase C: terminal matrix, fault injection, drift and install checks — status: accepted 2026-09-03 (a62de47, merged to main)
Verified: 187 tests, gate about 4.4 min; private tmux and screen
servers with in-pane restoration; TERM matrix including dumb; resize
synchronized on observed markers; four fault injections restore and
resume; drift test reads installed packages; clean install from packed
tarballs through the launcher; stray commit 4a685df reverted exactly
and reimplemented without its cross-branch coupling. 8 production
lines. Note for W-019: the clean-install test forces @antst/dashi to
the packed tarball via an override, which could mask a declared-range
drift; assert the declared range satisfies the packed version.
Owner: dsh-exec. Branch w-017-hardening.
Scope: (1) tmux and screen under private test servers, never the
user's: the inline daily flow and a full-screen flow with wheel, with
terminal restoration asserted inside the multiplexer pane; (2) TERM
matrix for the same flows: xterm-256color, tmux-256color, screen,
linux, and dumb (dumb must degrade to the accessible-style status
word and refuse nothing it can render); (3) resize during streaming
and during an open decision overlay in both renderers, asserting no
corruption and the overlay still answerable; (4) fault injection not
yet covered: broken stdout (EPIPE) mid-stream, stdin closed
mid-decision, SIGTERM during a pending command, a thrown presenter
during resize; each must restore the terminal and leave the session
resumable; (5) the D-025 drift test; (6) a clean-install test: pack
all three dashi packages, install into a fresh DSH_HOME with the
published DSH and dsh plugin add, run the daily flow through the
launcher, no workspace links; (7) README section "Terminals" stating
what is verified. macOS PTY runs are listed as verified only if a
macOS host runs the suite; otherwise the README says Linux-verified
and macOS by design.
Not in scope: new features; vim mode; anything the ledger lists as a
DSH gap.
Acceptance evidence: gate passes three runs with the matrix included
(report total gate time); every new case cites its assertion; new
production source under 100 lines (this item is tests and docs).
W-018 Inline scrolling defect — status: accepted 2026-09-03 (e8d91c2, merged to main); owner confirmed scrolling by hand 2026-09-03
Verified by direct reading: 8 added lines; inline passes the whole
loaded transcript to TuiMainScreen; PageUp and Ctrl+Home open history
inline; PTY proves scrollback retention, no clear between chunks, no
alt-screen or mouse sequences. Carry-over into W-017: the launcher
signal timeout and the long-turn bell timing tests were flaky once in
a full run; make both deterministic (wait on observed state, not
elapsed time).
Owner: dsh-exec. Branch w-018-inline-scroll.
Owner report 2026-09-03: scrolling still does not work in the default
(inline) mode. Diagnosis to confirm: dashi renders a fixed-height
viewport slice and repaints in place, so committed lines never enter
the terminal's scrollback, and inline mode has mouse tracking off, so
the wheel reaches neither the terminal's history nor dashi. D-023's
claim that inline gives native wheel scrolling was wrong for dashi.
Step 1, report before code: reproduce in the dashi-dev profile with the
launcher; then establish from pi-tui 0.84.4 source what TuiMainScreen
does when the component tree exceeds terminal height (does it emit
overflow lines once into scrollback and repaint only the visible
bottom, as pi's own coding agent relies on), with file:line.
Then propose one of two designs with a size estimate: (A) inline mode
enables the same mouse tracking and wheel filter as full screen,
dashi keeps the viewport model in both modes, native selection needs
the terminal's bypass modifier; (B) inline mode is append-only:
committed cells are emitted once through the main-screen renderer and
flow into native scrollback, only the live tail, decisions, overlays,
composer, and status repaint, PageUp and Ctrl+Home in inline mode open
the history overlay instead of paging in place, lazy paging stays for
full screen and the overlay, mouse tracking stays off, wheel and
native selection are the terminal's. State which the reducer and fold
support unchanged. The architect chooses; the preference is (B) if
pi-tui supports it without patching, because it is the Claude Code
model and needs no mouse tracking.
Acceptance evidence: PTY test in the default mode proves that after a
transcript longer than the terminal height the older lines are present
in the PTY's scrollback (baseY above zero and the first turn's text
retrievable) with no alt-screen or mouse sequences; a streaming turn
appends without a full replay (assert the output between two chunks
contains no clear sequence); PageUp opens the history overlay inline;
full-screen behavior unchanged; README states the inline replay
caveat for Ctrl+O and resize; the owner confirms by hand; new
production source under 70 lines.
W-019 Release readiness: 0.1.0-alpha.2 — status: accepted 2026-09-03 (bc2798a, merged to main, tagged v0.1.0-alpha.2)
Verified by direct reading: identical versions, app range asserted
against the packed plugin, DESIGN.md 871 lines with no withdrawn
claim, factual CHANGELOG, host-contract fixture reads the accessor
once per event, stray worktree removed, pack listings clean. Zero
production lines. The v0.1.0-alpha.1 tag remains as history and must
not be published (D-026).
Owner: dsh-exec. Branch w-019-release.
Scope: (1) versions 0.1.0-alpha.2 for @antst/dashi, @antst/dashi-app,
@antst/dashi-launcher, identical; the app's declared range on the
plugin must satisfy that version and the clean-install test asserts
it in addition to the override (W-017 note); (2) a short CHANGELOG.md
at the repo root listing what the alpha contains and the Known DSH
gaps, no marketing; (3) DESIGN.md doc pass for everything since the
W-010 pass: inline default and append-only document (D-023, D-027),
launcher (D-024), packaging as peers (D-026), sessions list, parity
commands, agent presets in the profile, drift test, terminals
section; under 900 lines; (4) the DESIGN.md section 15.5 host-contract
fixture test if not already present: a tiny fixture plugin reading
ctx.tuiRoot.current() once and observing exact tui/root-changed
pairs across /new and /resume, using only public exports (cite if
W-003's tui-root tests already satisfy it); (5) git worktree list
hygiene: report any stray worktrees under /home/antst other than
dtui, dtui-main, roller, roller-main, and do not remove them, list
them for the architect; (6) pnpm pack --dry-run for all three
packages listed in the handoff.
Acceptance evidence: gate passes three runs; pack listings; the
clean-install test range assertion; DESIGN.md contains no withdrawn
claim (grep proof for viewport claims about inline mode, dashi bin
absence claims, and dependencies for DSH services); new production
source under 40 lines.
W-020 Exact DSH pins and resolved-graph assertion — status: accepted 2026-09-03 (43a145f, merged to main, tagged v0.1.0-alpha.3)
Verified by direct reading: 24 and 6 exact rc.1 pins, boot
cross-check of dsh-base against the CLI, README pnpm-only install and
fresh-lockfile upgrade note; 189 tests three runs. Open doubt: the
wildcard override is verified on pnpm 11 in the clean-install test;
verification on pnpm 10 is done during the owner's profile rebuild.
Owner: dsh-exec. Branch w-020-exact-pins.
Scope per D-029: exact versions for every @deepseek-ai/* peer and
dependency in @antst/dashi and @antst/dashi-app (validated-dsh-versions
remains the single source; a gate check asserts the manifests match
it); clean-install test asserts the resolved graph (every
@deepseek-ai/* in the fresh profile and in the fresh CLI install
equals the validated version) and fails otherwise; README install
section: pnpm install @deepseek-ai/dsh@<v> plus a pnpm-workspace.yaml
overrides block pinning @deepseek-ai/* to <v>, with the reason
in one sentence; npm is stated as unsupported for prerelease DSH
because its overrides field has no scoped wildcard (builder verified
2026-09-03: npm accepted the field and still resolved about 200
packages at rc.1) and a generated exact map is rejected as brittle; the
clean-install test may keep npm for the fresh CLI prefix if pnpm's
ignored-builds gate makes the pnpm path larger; the boot version probe additionally checks one library
package version (dsh-base) against the CLI version and warns on
mismatch. Also: recreate the owner's profile against their rc.1 home install
(with the architect's go), and verify boot.
Added 2026-09-03: this item also bumps all three dashi packages to
0.1.0-alpha.3 with a CHANGELOG entry ("validated against DSH
0.1.2-rc.1; DSH versions pinned exactly; upgrade requires a fresh
lockfile") and a README upgrade note that upgrading DSH means removing
node_modules and the lockfile, not an incremental install.
Acceptance evidence: gate passes three runs; the clean-install test
proves the exact graph; a deliberate mismatch fixture fails the
assertion; the owner's installed dsh --profile dashi boots and
answers one prompt; new production source under 40 lines.
Zero production changes; no relied-on contract moved; reference tree
is tag dsh-v0.1.2-rc.1 (a66e470); 187 tests green three runs plus one
confirmation. The 25 initial failures were the workspace lockfile's
stale peer-only alpha.5 packages (tool scheduler Symbol mismatch,
rc.1 tool-calls.ts:170, tools/src/index.ts:459), cured by a clean
node_modules and lockfile install, the same hazard as the owner's home.
Owner: dsh-exec. Branch w-021-rc1.
Scope: bump validated-dsh-versions to 0.1.2-rc.1 and the pinned
reference tree, run the full gate, and report every failure with the
DSH change that caused it; fix dashi where its own code broke; stop
and report where rc.1 removed or changed a DSH contract dashi relies
on. The result is either an accepted rc.1 pin or a ledger decision to
stay on alpha.5 with the reasons.
Acceptance evidence: gate green three runs at rc.1 or a stop report;
roller gets the same treatment in its own ledger afterwards.
W-022 README install note without the override — status: accepted 2026-09-03 (4c282cb, merged to main)
Docs only; grep-proven; 189 tests.
Owner: dsh-exec. Branch w-022-readme-install.
Scope per D-031: remove the pnpm-workspace.yaml override block and its
justification from the root and package READMEs and DESIGN.md; keep
the exact CLI version in the install command, the npm-unsupported
sentence (DSH's own caret ranges resolve a mixed prerelease graph
under npm), and the fresh-lockfile upgrade note; add one sentence that
dashi validates each DSH release and its packages pin DSH exactly. If
the clean-install test writes that override into its fresh prefix,
remove it there too and keep the resolved-graph assertion. Docs only;
zero production lines.
Acceptance evidence: gate passes once; grep proves no overrides text
remains in the READMEs and DESIGN.md.
Verified by direct reading: Ctrl+D delegates to the existing Ctrl+C
arm; 14 production lines; 191 tests.
Owner: dsh-exec. Branch w-023-ctrl-d.
Owner report from daily use (2026-09-03): a single Ctrl+D exits dashi;
Claude Code requires it twice, and a single keystroke risks an
accidental kill. Scope: Ctrl+D on an idle root with an empty composer
arms exit exactly as Ctrl+C does, showing the same "press again to
exit" status, and exits on the second press; any other key disarms;
Ctrl+C and Ctrl+D share one arm state, so Ctrl+C then Ctrl+D also
exits (two keystrokes either way). Behavior with a nonempty composer
or a running root is unchanged. Update the key table in DESIGN.md,
the README, and /help. Reducer-only change plus tests.
Acceptance evidence: gate passes; reducer tests for single press
(armed, no exit), second press (exit), disarm on another key, and the
mixed Ctrl+C then Ctrl+D case; the existing PTY exit tests that used a
single Ctrl+D are updated to two; new production source under 20
lines.
Verified by direct reading: bare --resume and -r open the cwd-scoped
picker before the terminal binds, -c aliases --continue, --all in
picker mode, unknown exact id exits 1 with DSH's own message. 59
production lines; 194 tests.
Owner: dsh-exec. Branch w-024-resume.
Owner report from daily use (2026-09-03): "dashi does not support
--resume". Step 1, reproduce through the installed launcher on the
owner's rc.1 profile (read-only): dashi --resume, dashi --resume <uuid of an existing session>, dashi --continue, and report the
exact output and exit code of each. Step 2, parity with Claude Code:
bare --resume (and -r) opens the session picker at startup, the
same picker /resume uses, over the current cwd with --all
honored; --resume <UUID> stays exact; --continue (and -c) stays
as is; a UUID that does not exist prints DSH's own not-found error
and exits 1; --resume combined with a prompt argument resumes then
submits the prompt. Key table, README, /help, and DESIGN.md 5.1
updated. If step 1 shows the launcher or DSH drops the argument,
report before fixing.
Acceptance evidence: gate passes; PTY tests for bare --resume opening
the picker and selecting a session, exact --resume, unknown UUID
error, and --resume with a prompt, all through the launcher; new
production source under 60 lines.
W-025 Launch flags: permission, yolo, model, effort — status: accepted 2026-09-03 (PR #17 merged into develop)
Flags are the interactive commands applied before TUI bind: --model
(with --provider inferred for a unique catalog match, required when
several providers list the id), --effort, --permission , and
--yolo / --dangerously-skip-permissions as one alias for the
danger-full-access preset. DSH persists the model selection as its
default exactly as /model does; dashi stores and restores nothing
(session-only selection is a DSH gap, see Upstream reports). Unlisted
model ids bind because the DSH catalog is advisory; unknown preset,
provider, and effort relay DSH's own errors.
Owner: dsh-exec. Branch w-025-launch-flags.
Owner report (2026-09-03): no --yolo or
--dangerously-skip-permissions. Audit: DESIGN.md 5.1 lists
--permission PRESET, --model ID, --effort ID, --provider ID as
launch-scoped flags and none is implemented. Scope: --permission <preset> applied at bind through the same native /permission
command path the picker uses (durable DSH fact, no dashi state);
--dangerously-skip-permissions and --yolo as aliases for
--permission danger-full-access, with no confirmation at launch
because the flag is the explicit consent, and the status line showing
the preset as it already does; --model <id> and --effort <id>
applied through sessionController.selectModel at bind, with
--provider accepted only if the catalog needs it to disambiguate
(cite); unknown preset or model prints DSH's own error and exits 1.
All launch-scoped: nothing written to settings. Key table, README,
/help, DESIGN.md 5.1 updated.
Acceptance evidence: gate passes; PTY tests through the launcher prove
--yolo starts with danger-full-access and a bash escalation runs
without an approval overlay, --permission read-only refuses a write,
--model changes the durable selection, unknown values exit 1 with
DSH's message; new production source under 80 lines.
W-026 Rewind flow per Claude Code — status: accepted 2026-09-03 (d4182dd, merged to main); owner hand check on alpha.4 confirmed 2026-09-03 ("much better")
Verified: pure rewind module, four action rows with roller-gated code
rows, first-prompt paths through create plus selectModel and
/roller-restore start, double Esc draft recall; 93 production lines;
197 tests.
Owner: dsh-exec. Branch w-026-rewind-flow.
Scope per D-032. Screen 1, title "Rewind to a prompt": one row per
human prompt in session order (steered ones included, suffixed
"· mid-turn"), text truncated to width, newest last, cursor on the
last; Esc closes. Screen 2, title is the selected prompt text
truncated: rows "Restore code and conversation", "Restore
conversation", "Restore code", "Never mind"; the two code rows only
when roller-restore is listed by name; "Never mind" returns to
screen 1. Actions: conversation restore = existing controller fork at
the boundary before that prompt (for a mid-turn prompt, the boundary
before its turn), switch roots, composer set to the prompt text;
code and conversation = the same plus /roller-restore <seq> in the
child; code only = /roller-restore <seq> in the current root, no
fork. First prompt (ruled 2026-09-03; DSH cannot fork before the
first completed turn, commands.ts:211-227): conversation restore =
sessionController.create of a fresh root in the same cwd with the
current session's agent preset and model selection passed
explicitly, then switch roots, composer set to the prompt text; code
only = /roller-restore start in the current root (roller W-006);
code and conversation = /roller-restore start in the current root
first, then the fresh root. /status shows no lineage for that root. Remove the "latest" row, the "edit and resend" and
"conversation only" labels, and the "Files will not be changed"
notice (replace with the absence of the code rows plus one README
sentence). Double Esc with a nonempty composer clears the draft into
prompt history (Up recalls) instead of opening rewind. rewind.ts
stays pure; the reducer imports nothing. README names two DSH gaps:
fork granularity is per turn, and no range summarization.
Acceptance evidence: gate passes; PTY tests through the launcher for
each of the three restore actions with roller installed (files
restored for the code paths, untouched for conversation only),
"Never mind" returning to screen 1, code rows absent without roller,
a steered prompt listed and rewinding to its turn start, and double
Esc clearing a draft recallable with Up; headless captures of both
screens at 80 columns in the handoff; new production source under
150 lines; the owner confirms by hand.
Fold classifies on plugin source fields only (DSH rc.1 ContextFormed
forms instructions, catalog, snapshot, notice, recall; relay
unchanged); collapsed row expands through existing card mode and the
history overlay; reducer unchanged; 28 production lines. Amendment
during review: "any" means context DSH injects; dashi's own shell
command messages keep their shell cell in durable history, since that
is output the user asked for.
Owner: dsh-exec. Branch w-027-context-cells.
Observed in the W-024 reproduction: on resume, DSH's injected
workspace instructions (a plugin message with a context form, e.g.
instructions or snapshot, carrying AGENTS.md) render in full as a
transcript cell, dozens of lines above the first prompt. Scope: any
user-role message whose source is {kind:'plugin'} with a
ContextFormed form (instructions, catalog, snapshot, notice, recall)
renders as one collapsed row: "Context ·
Verified by direct reading: one pure resolver (id shape, exact title,
substring, cwd or all) shared by launch and /resume; native titles
only; 56 production lines; 196 tests.
Owner: dsh-exec. Branch w-028-resume-by-name.
Owner requirement (2026-09-03): --resume <value> and /resume <value> accept a session name as well as a UUID. Resolution, in
order: a value shaped like a DSH session id resumes exactly; else an
exact title match among sessions in the current cwd (all cwds with
--all); if exactly one matches, resume it; if several match, open
the existing picker restricted to the matches; if none match, fall
back to a case-insensitive substring match with the same one, many,
none rule; none at all prints one line naming the value and exits 1
(or, for /resume, shows the notice). Titles come from DSH's native
session title only; no dashi name store. --name TITLE on launch and
/rename TITLE are already native and unchanged; README documents
the three together in one short "Sessions" section, including that
names need not be unique and the picker resolves duplicates.
Acceptance evidence: gate passes; PTY tests through the launcher:
--resume <unique title> resumes it; two sessions with the same
title open the picker showing only those two; substring match; no
match exits 1; /resume <title> in-session; new production source
under 60 lines.
W-029 dashi-app bundles roller — status: accepted 2026-09-03 (PR #1 merged into develop; released as 0.1.0-alpha.4)
dashi-app pins @antst/roller 0.1.2 and inserts its row; clean-install
proves /roller-restore and the code rows after adding only dashi-app;
8 mechanism lines; 197 tests.
Owner: dsh-exec. Branch w-029-bundle-roller.
Scope per D-033: @antst/dashi-app depends on @antst/roller at the
exact version from roller's main (0.1.2 once roller bumps for W-006;
coordinate: roller-exec bumps first), inserts the roller row in
cordis.patch.yml after dashi's row, and its package README says roller
is included. @antst/roller@0.1.2 is published on npm (2026-09-03), so the
dependency resolves from the registry with no tarball override.
Clean-install test: after only dsh plugin add @antst/dashi-app, the
profile lists roller-restore in /help and the rewind picker shows
the code rows. README install section unchanged (one add command);
the owner's profile rebuild then adds only dashi-app.
Acceptance evidence: gate passes; clean-install test proves the above;
no change to @antst/dashi; new production source under 10 lines.
Hosted gate green; preview blocked only by the missing pkg.pr.new
app; release workflow publishes the three packages in order with
provenance and a prerelease dist-tag; two test-only readiness fixes
(screen server wait, launcher pack timeout).
Owner: dsh-exec. Branch w-030-ci from develop; pull request against
develop.
Scope: mirror roller's two workflows. ci.yml: on pull_request and
push to develop and main: checkout, pnpm from the packageManager
field, Node 22, apt install tmux and screen (the gate needs both),
pnpm install --frozen-lockfile, pnpm gate (allow 20 minutes); on
pull_request and develop pushes, after the gate: pnpx pkg-pr-new publish ./packages/dashi ./packages/dashi-app ./packages/dashi-launcher --pnpm --compact. release.yml: on GitHub
release published: gate, then pnpm publish --access public --provenance --no-git-checks in packages/dashi, then dashi-app, then
dashi-launcher, in that order, with id-token: write; a prerelease tag
publishes under the npm alpha dist-tag (derive from the version
string). package.json repository, homepage, and bugs point to
https://github.com/antst/dashi with the directory field. README
"Development" section. Nothing else.
Acceptance evidence: PR CI green on GitHub except the preview step
until the owner installs the pkg.pr.new app; both YAML pass
actionlint; zero production source changes.
One 2000 ms timer in the effect runner dispatching the existing
disarm action; PTY proves 250 ms exits and 2.5 s re-arms; also the
CI-only 3x ceiling for measured gates and observed waits (test-budget
module), with the decision-EOF test waiting on observed state. 13
production lines; 199 tests; hosted run green with previews.
Owner: dsh-exec. Branch w-031-exit-arm-timeout; PR against develop.
Owner report from daily use (2026-09-03): the exit arm never expires;
a first Ctrl+D and a second one a minute later still exit. Claude
Code's arm lapses after a short window. Scope: when the reducer arms
exit (Ctrl+C or Ctrl+D), it emits one effect that schedules a single
timer of 2000 ms dispatching the existing disarm-exit action; a
second press inside the window exits; a press after it re-arms with
the hint again; any other key still disarms immediately and clears
the timer; arming again resets the timer. One timer, cancelled on
disarm and on dispose; no new state beyond the existing arm flag.
Key table and README say "press again within two seconds". This is a
key-chord timer, which DESIGN.md's render scheduling permits.
Acceptance evidence: gate passes; reducer tests for arm, disarm via
the timer action, re-arm after expiry; a PTY test proves a second
Ctrl+D after 2.5 s does not exit and the hint is gone, while a second
within 500 ms exits; new production source under 25 lines.
W-032 One container for the gate, locally and in CI — status: accepted 2026-09-03 (PR #13 merged into develop)
Dockerfile (node:22-bookworm-slim, git, procps, screen, tmux, bubblewrap;
drops to the host uid) and one gate:docker script that holds the three
outer-Docker allowances nested bubblewrap needs. ci.yml and release.yml
run only that script. The inner DSH bubblewrap policy stays under test;
no Docker-specific skips.
Owner: dsh-exec. Branch w-032-gate-container; PR against develop.
Owner request (2026-09-03): run CI in Docker. Scope, smallest form: a
single Dockerfile at the repo root on the official Node 22 image
with pnpm (from the packageManager field via corepack), tmux, GNU
screen, git, and a UTF-8 locale; ci.yml runs its gate job with
container: built from that Dockerfile (or the image built and
cached by the workflow, whichever is fewer lines); a pnpm gate:docker script builds the image and runs pnpm gate inside it
with the workspace mounted, so the local and hosted gates are the
same environment. No compose, no multi-stage build, no registry
publish, no change to the gate itself. README "Development" gains one
sentence.
Acceptance evidence: PR CI green with the container; pnpm gate:docker green locally with the same test count; zero production
source changes.
Tag push gates, validates the tag against all three manifests,
creates the GitHub release from the CHANGELOG section, publishes the
three packages in order skipping versions already on npm. First npm
publish of dashi is manual (trusted publishing needs existing
packages); the alpha.6 tag is the OIDC proof.
Owner: dsh-exec. Branch w-033-release-on-tag; PR against develop.
Owner request (2026-09-03), mirroring roller W-010: release.yml
triggers on push of tags matching v*; jobs in order: gate, then
create the GitHub release for that tag with gh release create using
the CHANGELOG.md section for that version as notes (fallback:
auto-generated), --prerelease for prerelease versions,
permissions: contents: write; then the existing three-package npm
publish in dependency order with provenance and the version-derived
dist-tag. Idempotent on rerun. README release procedure: bump,
changelog, merge to main, push the tag.
Acceptance evidence: PR CI green; actionlint clean; the alpha.5 tag
exercises it; zero production source changes.
W-034 Plugin management in the session — status: accepted 2026-09-03 (PR #24 squash-merged into develop)
Lookup showed dsh plugin is a pnpm passthrough with no enable/disable
verb and mcp-client keeps connection state private, so: /plugins reads
the DSH plugin-inventory Remote (dashi-app now mounts it) plus the
configured serverName for mcp-client rows, no health claim; /plugin
forwards verbatim to dsh plugin --profile <running profile>
(name derived from ctx.baseUrl, shell-quoted) through the human shell,
which now overlays DSH_HOME via dsh-home-paths for ! too; one
next-launch notice, no restart mechanism. Addendum: -h/--help with a
"dashi on DSH " header shared with /help; --version stays DSH's
root flag (the launcher does not rewrite argv). Enable/disable and MCP
status are DSH gaps in README and the queued upstream report. 59
production lines; 220 tests.
Claude Code has /plugin (install, remove, enable, disable) and /mcp
(server list with connection status). DSH manages plugins only from
the CLI (dsh plugin --profile <name> add <pkg>), MCP servers are
mcp-client plugin rows in the profile patch, and no roster or status
service exists (README names the /mcp gap). Scope, in order:
- Bounded lookup first, reported before code: the exact
dsh pluginverbs in rc.1 (add, remove, list, anything for the patch-rowdisabledflag), and whether mcp-client exposes any per-server connection state a list can read. Cite file:line. /plugins: read-only list of what the running profile loaded, from the Cordis registry: row id, package name, version, fiber state; mcp-client rows in the same list with their server name and connection state when DSH exposes it. No separate /mcp; an MCP server is a plugin row in DSH./plugin add <pkg>and/plugin remove <pkg>: run DSH's own CLI for the current profile through the existing shell-command mechanism, show its output, then offer to exit so the launcher restarts the profile. dashi writes no files and owns no installer.- Enable/disable only if the DSH CLI has verbs for it; otherwise
named as a DSH gap in README and queued as an upstream report, not
patched by editing cordis.patch.yml from dashi.
Owner: dsh-exec. Branch
w-034-plugins; PR against develop. Acceptance evidence: PTY test listing the shipped profile's rows (dashi, roller, at least one DSH row) with states; PTY test that/plugin addof a packed tarball runs the DSH CLI and the restart offer appears; unknown package relays the DSH CLI error; README and /help updated; no new state in the reducer beyond the transient list.
Cited table against the current Claude Code roster: ~20 done, ~25 doable over existing DSH services, ~30 DSH gaps with the missing surface named. Spot-check of twelve citations by an independent reader: ten exact, two partial (recordKeyFor is exported earlier in auth.ts than cited; DSH's ShellExecutor has no interactive TTY, so an $EDITOR launch must be dashi's own inherit-stdio child, see W-037). Reversals: /login and /logout doable; /hooks a gap; whole-tool --tools doable, pattern permission rules a gap. Work items W-037 onward are cut from the doable rows in daily-use order; gaps get one README sentence each and join the upstream report queue. No code. Walk Claude Code's interactive command and launch-flag roster (current docs) against dashi and produce one table in the handoff: item, dashi status (done / doable / DSH gap), and for every doable item the DSH service or package that owns the fact, cited file:line in the pinned rc.1 tree. Candidates known before the audit: /memory (open the instruction files DSH loaded in $EDITOR), /diff (git diff since session start or last turn), /hooks (list hooks DSH loaded), /status or /config (effective settings, model, preset, profile, workspace), /export (transcript to a named file on explicit request), /init (AGENTS.md skeleton), and the likely gaps /login, /logout, /add-dir, autocompact tuning. Anything the audit finds that a developer uses daily in Claude Code is in scope; anything not about driving DSH from a terminal is out. The architect turns doable rows into W-036 onward in order of daily use; gaps are named in README and queued as upstream reports. Owner: dsh-exec. Branch none; handoff is the table, cited. Acceptance evidence: every row cited or marked as a gap with the missing DSH surface named; no row left as "unknown".
W-036 agentbus plugin in dashi-app, launcher token check, and -g — status: in preparation 2026-09-05 on branch w-036-agentbus (per D-039; merge blocked until @agentbus/dsh is on npm)
Per D-036. Scope: dashi-app adds @agentbus/dsh-comms at exact
0.4.0 as a dependency and a patch row (sibling of dashi and roller;
activation is service-driven, row order is irrelevant); dashi parses
-g <group> / --group <group> repeatable and runs one
/agent-sessions group <g> [<g> ...] call with all names (additive,
one re-hello) before TUI bind, relaying
the DSH command result on error (unknown command means the comms
plugin is not loaded; say so verbatim). No name flag beyond the
existing --name; no env variables set by dashi or the launcher.
README: one paragraph on Agent Sessions presence (starts when the root
session exists; needs the Agent Sessions host daemon installed on the
machine; delivered messages arrive as ordinary input), groups, and
that the session title is the peer name. Verified with the Agent
Sessions architect 2026-09-03: all six design points confirmed.
Owner requirement (2026-09-03): with no daemon on the host nothing
breaks and dashi keeps working, messaging simply unavailable, and this
must not require complex machinery. Gate on the comms plugin (owned by
Agent Sessions, gated by the architect before dashi pins it):
activation never throws or blocks startup; no transcript error or
hello failure to dismiss, at most one log line; /agent-sessions group while disconnected returns a plain command error; the tool
returns a plain error instead of hanging; reconnect is at most one
fixed-interval retry or absent.
Acceptance evidence: dashi starts and works normally with no daemon
present (the shipped-profile PTY tests run without one); PTY test with
the shipped profile showing the comms row in /plugins and the group
applied (assert through the plugin's own command output or its
exposed service, not by parsing logs); clean-install test still
passes with the added dependency; production source under 40 lines.
Builder note allowed on the exact 0.4.0 version once published.
Defect found and fixed on the way: dashi-app had mirrored the web
app's disable list wholesale, including agent-instructions, so the
shipped profile loaded no AGENTS.md (the web app disables the row
because it injects its own). Cause it went unseen: W-027's fixture
hand-wrote source {kind:'plugin', plugin:'agent-instructions'} while
live rc.1 emits {kind:'agent-instructions'}; the fold now covers the
native shape and a PTY test proves a fixture AGENTS.md reaches the
model context in the shipped profile. profile-drift names this one
intentional divergence with a reason. /memory lists paths and scopes
from DSH's durable agent-instructions sources and opens the file via
the existing cooked-terminal editor path ($EDITOR, fallback vi, argv
not shell string). 49 production lines; 222 tests. Every alpha.7
install has the instructions defect: alpha.8 follows promptly.
List the instruction files DSH loaded for this session (paths and
scope from the agent-instructions rendered set, PARITY.md row
/memory), select one, open it in $EDITOR (fallback vi) as a child
with inherited stdio while the TUI is suspended, then resume and
redraw. DSH's skill watcher already reloads; instructions reload per
DSH's own rules, and the row says which. No file written by dashi.
Acceptance evidence: PTY test that /memory lists AGENTS.md from the
fixture cwd and that selecting it runs $EDITOR (a recording script)
on that exact path, terminal restored after; production source under
50 lines.
/diff runs git diff --no-ext-diff --no-color HEAD -- in the session
header cwd through ctx.shell under the session sandbox, read only,
stderr relayed verbatim; /diff turn slices DSH's log snapshot at the
last turn/start and renders DSH's recorded write/edit hunks through
the existing fold and presenters; the info overlay gained an optional
cells field and Ctrl+O inside overlays reuses toggle-tool-mode. 49
production lines; 224 tests. Note for W-040: the 64 KiB stdout cap in
diff-view.ts is a separate literal; share the human-shell constant.
Show the current git diff of the session cwd (via ctx.shell, read
only) and, with an argument turn, the write/edit hunks DSH recorded
in tool-result metadata for the last turn (PARITY.md row /diff),
rendered with the existing presenter/card mode. Acceptance: PTY test
with a fixture repo; production source under 50 lines.
/config lists namespaces with DSH-redacted value/base/user layers
(describe({redactSecrets:true})); /config NS KEY=VALUE parses a
JSON scalar with string fallback and calls the provider's validated
update, or mutate with path ops for dotted keys; DSH's own errors
verbatim; PTY test proves DSH persisted the change to settings.yaml.
34 production lines; 223 tests.
Show effective settings per namespace from SettingsProvider.describe
(value, base, user) and accept key=value edits through the
provider's validated update; errors relayed verbatim. Acceptance: PTY
test reading and changing one setting and seeing DSH persist it;
production source under 50 lines.
/quit, /reset, /continue, /branch are the existing definitions re-registered under a second name; /effort reuses the model activation with the current provider/model; -n, --agent, --session-id, --fork-session pass through SessionCreateRequest and SessionController.fork; diff-view uses the shared stream constant. 39 production lines; 232 tests. Note to both builders: line caps bound mechanisms, not formatting; do not pack statements or type fields onto one line to meet a cap, ask for a higher cap instead. Registered aliases over existing behavior: /quit, /reset, /continue, /branch (=/fork), /effort ; launch flags -n (=--name), --agent , --session-id , --fork-session with --resume/ --continue. Each is one registration or one parser line over the cited DSH operation; no new mechanism. Acceptance: one PTY test per alias; production source under 40 lines.
/login lists AuthorizationService flows and begins one, relaying notice/text/secret/select steps through the existing decision overlay (secret answers masked, including bracketed pastes); /logout deletes the credential record by key without reading it. Review found the typed secret surviving in pi-tui's Editor undo history (setText pushes an undo snapshot; Ctrl+- is handled inside the Editor); fix: the Editor instance is replaced synchronously when a secret question resolves, so the old undo stack is unreachable; PTY test presses Ctrl+- after submit and scans terminal output and session.jsonl for the sentinel. dashi-app mounts dsh-authorization. Cap raised to 70 lines for the fix; 70 production lines; 233 tests. /login lists AuthorizationService flows and begins the chosen one, relaying the provider-owned interactive steps; /logout deletes the provider's credential record (PARITY.md rows). Acceptance: PTY test with a fake flow from the replay provider or a fixture flow; production source under 60 lines. Never print secrets.
Fixed template through ctx.fs.writeText createIfAbsent under the
session sandbox; DSH's existing-file error verbatim; README states
DSH reads the file in a new or resumed session (agent-instructions
composes its baseline at first request, no watcher). PTY test: create,
refusal on second run, fresh session shows the Context row. 19
production lines; 234 tests.
/init writes a starter AGENTS.md in the session cwd through ctx.fs
(atomic create, refuses to overwrite an existing file, DSH error
verbatim) with a short fixed template: project name from the cwd
basename, a "Working agreement" heading, and three placeholder
bullets; then reports the path. DSH's agent-instructions plugin
picks it up per its own rules; say which in README (next session or
live). Acceptance: PTY test in an empty fixture cwd, then a second
/init refused; production source under 30 lines.
One reducer action (copy-assistant) replaces copy-latest; /copy N
picks the Nth latest completed assistant cell; /copy code opens the
existing list overlay with fenced blocks (language and first line)
and selection reuses the OSC 52 copy effect. 36 production lines;
236 tests.
/copy N copies the Nth latest assistant message (1 = latest, the
existing /copy behavior); /copy with argument code opens the
existing list overlay with the fenced code blocks of the latest
assistant message (language and first line as label) and copies the
chosen block through the existing OSC 52 path. Source is the DSH log
via the existing fold; no new state beyond the transient list.
Acceptance: renderer test with a recorded log containing two code
blocks; PTY test for /copy 2; production source under 40 lines.
Existing list overlay over ctx.skills.list/get for the agent scope
and cwd; substring filter on name and description; selection inserts
/<name> into the composer; no cache. 35 production lines; 234
tests.
/skills lists the skills DSH resolved for this session from the
session skill catalog (name, description, invocation policy, source,
path; PARITY.md row /skills) in the existing list overlay; /skills TEXT filters by substring on name and description; selecting a row
inserts /<skill-name> into the composer (DSH human invocation is
first-class per tool-skill). Read only; no state beyond the transient
list; DSH's filesystem watcher already reloads skills, so no reload
command. Acceptance: PTY test with a fixture skill directory showing
the row and the filter; production source under 40 lines.
W-045 --tools and --disallowedTools — status: accepted 2026-09-04 (PR #53 squash-merged; owner roller-exec)
Parser branch in index.ts; one ToolRuntime.restrict call in prepare()
so every root (fresh, resume, continue, fork) carries the mask, the
disposer owned by the binding; DSH's unknown-name error relayed with
exit 2; pattern rules named as a DSH gap. 32 production lines; 237
tests. Ships in alpha.10.
Launch flags --tools a,b (allow only these tool names) and
--disallowedTools a,b (deny these names) applied to the root agent
through ToolRuntime.restrict (agent-scoped allow/deny masks by whole
registered name; PARITY.md row --tools). Both flags also apply on
--resume/--continue. Unknown tool names: relay DSH's error if
restrict validates, otherwise list the unknown names against the
registered roster in one dashi error and exit 2. Pattern rules
(Bash(git *), paths) are a DSH gap: one README sentence; no dashi
matching. Acceptance: PTY test that a denied tool is absent from the
model's tool list (replay fixture records the tool roster) and that an
allowed-only list hides the rest; production source under 40 lines.
One handler for /tasks and /bashes: no argument opens the existing
details overlay, Enter on a job row opens JobRegistry.read output as
an info-overlay cell, kill ID calls JobRegistry.kill; /subtask TEXT
calls SubagentRuntime.startContinuable with the base profile's spawn
provider. jobs and subagents injected; dsh-jobs exact peer. 48
production lines; 237 tests. Ships in alpha.10.
/tasks (existing list) gains selection: Enter opens the job's
output through JobRegistry.read in the info overlay with cells;
/tasks kill ID calls JobRegistry.kill; /bashes is an alias of
/tasks. /subtask TEXT starts a continuable child through
SubagentRuntime with the text as its first prompt and reports the
child id; the existing subagent surface shows it. All owners are DSH
(PARITY.md rows). Acceptance: PTY tests for read, kill, and subtask
creation with the replay provider; production source under 50 lines.
W-047 --system-prompt and --append-system-prompt — status: accepted 2026-09-04 (PR #59 squash-merged; owner roller-exec)
Four flags parsed in index.ts, file variants read once at launch
(missing file: LaunchArgumentError, exit 2); prepare() registers a
complete SystemPrompt section or an ordinary final section on the
root, disposers on the binding, so resume/continue/fork carry them.
PTY test inspects the recorded request's system prompt. 32
production lines; 239 tests. Ships in alpha.11.
Launch flags --system-prompt TEXT, --system-prompt-file PATH,
--append-system-prompt TEXT, --append-system-prompt-file PATH
(PARITY.md row). Replace variants register one SystemPrompt section
declared complete; append variants register one ordinary section
at the end; scope is the root agent, applied in prepare() like
W-045 so resume/continue/fork carry it. File variants read the file
once at launch through node:fs (a launch argument, not session
state); missing file is a launch error, exit 2. No dashi persistence.
Acceptance: PTY test with the replay provider proving the request's
system prompt content for replace and append; production source
under 40 lines.
dashi-app mounts dsh-schedule; /loop INTERVAL TEXT, /loop, /loop stop
ID call the root agent's native schedule_create/list/delete tools
through ctx.tools.execute under the agent initiator; DSH errors
verbatim (five-minute floor is DSH's). PTY: create, list, 1m
rejection, stop; in-process fake-timer test drives the unmodified
DSH runtime through two firings and a stop. 45 production lines; 240
tests. Ships in alpha.11.
/loop INTERVAL TEXT (interval like 5m, 1h) schedules a fixed-rate
reminder through @deepseek-ai/dsh-schedule for the root agent whose
firing submits TEXT as a prompt; /loop lists active schedules;
/loop stop ID cancels. DSH owns the durable schedule records
(PARITY.md row /loop). Acceptance: PTY test with a short interval
showing two firings and a stop; production source under 50 lines.
Launch parser accepts --verbose and initializes the existing
toolMode to expanded; Ctrl+O cycle unchanged. PTY contrast test on a
resumed recorded Bash turn. 10 production lines; 242 tests. Ships in
alpha.11.
Launch flag --verbose: the initial presentation opens with every
tool card and context row expanded (the existing card mode set to
expanded at start) and stays a launch-scoped presentation choice;
Ctrl+O still toggles. No new state field beyond the existing tool
mode. Acceptance: PTY test that a resumed session shows an expanded
tool card on the first screen with --verbose and collapsed without;
production source under 15 lines.
One snapshot of DSH's tokenUsage and sessionStats projections into
the info overlay (four token buckets, turns, steps, model, tool, and
measured wall time); dashi-app mounts dsh-session-stats like the web
app; per-model breakdown, tool-call count, plan limits and cost are
DSH gaps in README. 38 production lines; 242 tests. Ships in
alpha.11.
/usage shows session totals from DSH's token usage projection and
session stats (PARITY.md rows /usage, /cost, /stats): input, output,
cached tokens, turns, tool calls, wall time, per-model breakdown when
present; read only from DSH projections, rendered in the info overlay.
Plan limits and cost are a DSH gap (one README sentence). Acceptance:
PTY test after a replayed turn showing nonzero totals; production
source under 40 lines.
One shared askAside: fork at DSH's latest completed boundary (no
atSeq, so a running source is accepted and its open tail excluded),
resolve and rename the child through the controller, steer one
prompt, follow to turn/end, fold the child suffix through the
existing fold into the info overlay titled with the boundary turn.
Both commands recordInput false; the root gains only its
command/run and command/done pair. Cold picker title for large forks
is the named DSH gap (upstream queue). 46 production lines; 246
tests. Ships in alpha.11.
Owner request 2026-09-04. Claude Code's /btw asks a side question
without touching the conversation; PARITY.md marks it a DSH gap
(no no-history model call). DSH-native shape instead: /btw TEXT
forks the current session at its latest completed turn
(SessionController.fork, cold-readable), titles the fork
btw · <first words>, submits TEXT as the fork's next prompt, waits
for its turn/end, and shows the assistant text in the info overlay
with cells (Ctrl+O expands). The main root is untouched; the fork
stays a normal DSH session (resumable from the sessions list; DSH
has no root-release, README says so). While the root is running, the
fork is taken at the previous completed turn boundary (fork is cold,
from the log) and the answer is labelled with that turn number, so
/btw works mid-turn without seeing the in-progress prompt; only if
SessionController.fork itself refuses a running source does dashi
relay that refusal (cite the check). /recap is /btw with the fixed prompt "Summarize this
conversation so far in ten lines: goal, decisions, open items." No
new state; the wait reuses the existing turn-end follow used by
executeAccepted. Acceptance: PTY test with the replay provider
proving the answer appears, the root's log gains only the command's
own command/run and command/done pair (DSH logs every command; use
recordInput false) and no turn/start, user/message, assistant/,
tool/, or turn/end, and the fork exists with the title; production source under 50 lines.
W-053 /plugin fails under the sandbox — status: accepted 2026-09-04 (PR #72 squash-merged; owner roller-exec)
runHumanShell takes a defaulted useSessionSandbox flag; /plugin
passes false and resolves a per-call danger-full-access policy
(ShellExecRequest.sandboxPolicy is the resolved per-call policy,
rc.1 shell/types.ts:77-78); ! unchanged. Default-preset PTY case
added. 6 production lines; 244 tests. Ships in alpha.11.
Defect, owner report 2026-09-04: /plugin add <pkg> in a normal
session fails with EROFS: read-only file system on
~/.dsh/profiles/dashi/_tmp_... because the nested dsh plugin
runs through the human shell under the session sandbox policy, which
mounts the profile directory read-only. W-034's PTY test launched with
--yolo, so the sandbox was off and the defect stayed hidden. Fix: the
/plugin passthrough runs the nested DSH CLI without the session
sandbox (it is DSH's own profile-management command, explicitly typed
by the user); human ! commands keep the sandbox. Acceptance: PTY
test under the default permission preset (no --yolo) that
/plugin add <packed tarball> succeeds and the profile manifest
changes; the existing --yolo case stays. Production change under 10
lines. Ships in alpha.11.
One pure window function in the renderer sizes list and info overlays to the viewport, keeps the cursor row visible, and prints exact "↑ N more" / "↓ N more" markers; info overlays scroll with Up/Down/PageUp/PageDown through one optional scrollOffset, the limit passed in the action from the renderer so the reducer never reads the terminal; a one-line isOverlayFocused override stops pi-tui's alternate screen from consuming PageUp/PageDown while a dashi overlay is open. 49 production lines; 252 tests. Ships in alpha.12. Defect, owner report 2026-09-04: a list overlay with more rows than the screen (e.g. /model with a long catalog) draws all rows, the tail is off screen and unreachable, while up/down keep moving the cursor through the invisible part. Fix in the renderer only: a list overlay draws a window of rows sized to the available viewport height (minus title and frame) that always contains the cursor, with a one-line marker for hidden rows above and below (e.g. "↑ 12 more" and "↓ 30 more"); an info overlay taller than the viewport scrolls with up/down and PageUp/PageDown, cursor-free, with the same markers. Windowing is pure presentation computed at render from the overlay cursor and the terminal size; no new reducer state beyond an info-overlay scroll offset if one is needed. Both inline and full-screen modes. Acceptance: renderer tests at 24 rows with a 60-row list (cursor at 0, middle, end) and a 60-line info overlay; PTY test that /model on a small terminal shows the markers and that the last row is reachable and selectable; production source under 50 lines. Ships in alpha.11.
Completed, non-accessible assistant cells render through pi-tui's Markdown component with one dashi theme (headings, emphasis, code spans, fenced blocks, lists, quotes) inside the existing cell cache; streaming cells and accessible mode stay plain; /copy keeps the raw text. PAGE_CELLS exported and reused for info paging. 28 production lines; 256 tests. Ships in alpha.12. Survey 2026-09-04 (dsh-tui/dsh-tui, ccch1mneyyy/dsh-TUI; ideas only, the second repo admits leaked-source ports, never adapt its code). dashi renders assistant text plain; Claude Code renders markdown. Render completed assistant cells through pi-tui's own Markdown component (headings, emphasis, code spans, fenced blocks, lists), no new dependency; streaming chunks stay plain until the cell completes; /copy and /copy code keep the raw text. Accessible mode stays plain. Acceptance: renderer tests with a recorded log containing each construct at 80 columns in inline and full-screen modes; production source under 40 lines.
W-056 Persistent status line — status: accepted 2026-09-04 (PR #84 squash-merged; owner roller-exec)
One line above the composer from live projection snapshots (modelSelection, permissions, tokenUsage, contextPressure) read at render, plus the git branch via ctx.shell under an explicit read-only policy at launch and on turn/end only, kept as the single branch field on the binding and redrawn only on change; hidden in accessible mode and under the exit arm; truncates branch then cache rate at the right. 49 production lines; 257 tests. Ships in alpha.12. One always-visible line above the composer (Claude Code has one): model, permission preset, context tokens used with the model's limit when the projection exposes it, cache-hit rate from tokenUsage, and the git branch of the session cwd read once at launch and on each turn end through ctx.shell (read only). Built only from projections dashi already reads for /status and /usage; no new state beyond the last shell result; hidden in accessible mode; the existing exit-arm text keeps priority. Acceptance: renderer tests at 80 and 120 columns; PTY test that the branch and model appear; production source under 50 lines.
W-057 MCP server stderr corrupts the terminal — status: accepted 2026-09-04 (PR #88 squash-merged; production source 0)
Confirmed DSH gap: mcp-client builds StdioClientTransport without a stderr option (rc.1 packages/mcp/mcp-client/src/transport.ts:31-39) and the MCP SDK spawns with stderr inherited (stdio.js:48-75); the terminal guard wraps only pi-tui's terminal, not fd 2. A shipped-profile PTY fixture asserts today's broken behavior so a DSH fix flips it. README names the gap; no dashi workaround. Bounded lookup, then report. Third-party TUIs document that @deepseek-ai/dsh-mcp-client spawns stdio servers with stderr inherited, so server logs write to fd 2 and corrupt rendering. (1) Confirm in the pinned rc.1 tree (mcp-client transport options) and with a fixture MCP server under the shipped profile whether the terminal guard already absorbs it; cite file:line. (2) If DSH inherits stderr: no dashi workaround (no spawn patching, no idle repaint timer); README names the gap and the upstream lane posts the report. (3) Search deepseek-harness issues and discussions for an existing report first and link it instead of duplicating. Acceptance: the handoff is the cited finding and, if a gap, the ledger upstream entry with the search result; production source 0. Builder note: confirmed against pinned rc.1 commit a66e470: mcp-client omits the transport's stderr option (packages/mcp/mcp-client/src/transport.ts:31-39), while the resolved MCP SDK defaults it to inherited fd 2 (stdio.js:48-75). The shipped-profile PTY fixture proves a post-handshake stderr line appears inside the active alternate screen. No exact upstream report exists; Discussion 4465 is nearest, with related 1241 and 5129 and third-party dsh-TUI issue 17.
W-052 /clear NAME and /agents authoring — status: accepted 2026-09-04 (PR #77 squash-merged; owner roller-exec)
/clear NAME, /reset NAME, /new NAME rename the current root through
SessionController.rename, await the store flush, then create the
fresh root as before; /agents new (copy of the selected preset or
defaultId, then the W-037 editor path on the DSH-resolved
composition path), copy, and delete call agentPresets copy/remove;
one new action forwards to the existing open-file effect. DSH errors
verbatim. 34 production lines; 247 tests. Ships in alpha.11.
Last two PARITY.md doable rows. (a) /clear NAME (and /reset NAME,
/new NAME): rename the current root to NAME through the title
service, then create the fresh root as today, so the old
conversation is findable by name. (b) /agents gains new NAME,
copy SRC DEST, delete NAME over the DSH agent-presets authoring
API (read, copy, delete cited in PARITY.md); new opens the created
preset file in $EDITOR through the W-037 editor path. DSH owns the
files; dashi writes nothing itself. Acceptance: PTY tests for
/clear NAME (old session listed by name) and for copy and delete;
production source under 50 lines.
Ruling during the item: /history stays the transcript cell browser
(it already shows command and shell cells); only the Up/Down input
fold widened. One pure projection (historyInput) recovers, in log
order, human prompts, /<name><args> from command/run events that
carry args, and !<command> from dashi's durable shell notice first
line; recordInput-false commands (/login, /btw, /recap) stay absent,
README says so. The rewind picker is untouched. 19 production lines;
265 tests. Ships in alpha.13.
Owner report 2026-09-04: in Claude Code, up/down recall cycles through
everything typed, including /commands and !shell lines; in dashi they
are invisible to history, which recalls human prompts only. Both are
already durable DSH facts: every resolved command appends command/run
with its recorded input (unless the definition set recordInput false),
and ! lines are dashi's durable shell notices. Scope: the history
fold that feeds up/down and /history includes, in log order, (a)
/<name> <args> reconstructed from command/run events that carry
input, and (b) !<command> from shell notices; commands that record
no input (/login, /btw, /recap) stay absent by design and README says
so; /history search covers the same set. No new state; the fold stays
pure. Acceptance: renderer/state tests with a recorded log mixing
prompts, two commands, and a shell line proving the recall order and
that a recordInput-false command is skipped; PTY test that up recalls
the last /model line; production source under 30 lines.
W-059 Rewind picker lists the same entries as recall — status: accepted 2026-09-04 (PR #102 squash-merged)
The rewind boundary fold uses W-058's historyInput for every
sequenced event, so prompts, recorded slash commands, and shell
notices share one picker projection; each maps to the latest earlier
turn/end, session-start fallback, second screen, and composer
prefill unchanged; mid-turn marking stays for human prompts only.
11 production lines added, 12 removed; 269 tests. Ships in alpha.13.
Owner correction 2026-09-04: in Claude Code the rewind picker
("Rewind to a prompt") lists the same entries as Up/Down recall,
commands and shell lines included; dashi's picker lists human
prompts only. Scope: the rewind picker uses W-058's historyInput
projection (one list, one owner); each entry keeps its event seq;
choosing a command or shell entry rewinds to the latest completed
turn boundary before that seq exactly as a prompt entry does, and the
existing second screen (conversation, code, both) and composer
prefill apply unchanged, so a recalled /model ... or !ls can be
re-run after the rewind. Entries with no preceding boundary (before
the first turn) map to session start as today. No new state.
Acceptance: rewind tests with the W-058 mixed recorded log proving
the picker rows and the boundary each maps to; PTY test rewinding to
a ! entry; production source under 30 lines.
W-060 Completion for /plugin arguments — status: accepted 2026-09-04 (PR #100 squash-merged; owner roller-exec)
First word from the fixed pnpm verb list; after exec, the running
profile's node_modules/.bin; after remove/update/why, the profile's
direct dependencies; both reread at completion time from the profile
directory (ctx.baseUrl), no cache; later words free text. README and
the post-success notice carry the W-061 wording (bundles load next
launch; plain plugins need a patch row that DSH live-reloads). 38
production lines; 268 tests. Ships in alpha.13.
Owner request 2026-09-04. /plugin forwards to pnpm, so complete
its vocabulary through the existing live completion trigger: first
word from the fixed list add, remove, update, outdated, list, why,
exec, licenses; second word for exec from the binaries in the
running profile's node_modules/.bin, and for remove, update, why from
the direct dependencies in the profile's package.json (both read
only, from the profile directory DSH resolved in W-034, no caching,
read at completion time). Anything after those words stays free
text. Acceptance: completion tests with a fixture profile dir; PTY
test that /plugin ex<Tab> completes to exec and /plugin exec <Tab> lists a fixture binary; production source under 40 lines.
W-061 Live plugin activation after /plugin add — status: closed 2026-09-04 (lookup; production source 0)
Finding (rc.1, vendor/loader/src/config/tree.ts:97-155,
packages/boot/app-boot/src/index.ts:785-787, apps/cli/src/plugin.ts:59-91,
apps/cli/src/profile-boot.ts:279-286): the running loader exposes
create/update/remove and imports with fresh resolution from the
profile directory, so live activation is mechanically possible; but
dsh plugin add composes no row for a plain plugin package (only
bundle packages are appended to dsh.profile.bundles), so such a
package never loads until a patch row is written by hand, and DSH's
default live patch reload then applies it without restart, while
bundle additions need a restart. Ruling: no dashi mechanism
(composing bundle rows duplicates loadProfile; writing patch rows
makes dashi the writer of a DSH-owned file). README states the two
cases (W-060); two upstream entries below.
Owner question 2026-09-04: must dashi restart after /plugin add?
Bounded lookup first, reported before any code: (1) in the pinned
rc.1 tree and the vendored Cordis loader, does the running loader
(ctx.loader, the tree DSH boots from) expose a supported operation
to insert or enable an entry at runtime (cite the API, and how the
web app or the disabled hmr row uses it, if they do); (2) after
dsh plugin --profile <p> add <pkg> succeeds, can the running
process resolve the freshly installed package from the profile
node_modules (module resolution from the profile root, pnpm hoisted
linker) without restart; (3) what dsh plugin add writes
(package.json, dsh.profile.bundles, cordis.patch.yml) and whether a
runtime insert would diverge from what the next boot composes. If
all three hold: propose the smallest follow-up so /plugin add
ends with a live activation through the loader (no dashi-owned
registry, no reconciliation loop) and the next-launch notice only
when activation fails. If any fails: name the DSH gap, README
sentence, upstream queue entry; production source 0.
Cause: DSH records an injected shell notice durably first as an
agent/inbox/spliced event (rc.1 core/agent/src/inbox.ts:176-187) and
promotes it to user/message only at a later step (agent-loop
agent.ts:291-293), so the live projection never saw a message.
Fix: historyInput reads the splice event (type-level exclusion of
the promoted message, so no duplicates on cold bind); the live !
submit records through the existing prompt-recorded action and the
follower skips the splice with one condition. PTY test types
!printf hi and recalls it. 12 production lines; 270 tests. Ships in
alpha.14.
Defect, owner report 2026-09-04 on alpha.13: /command lines typed
in a running session are recalled with Up, !command lines are not,
although the recorded-log tests of W-058/W-059 pass. Find the live
cause (candidates: the reducer appends the composer text to the
recall list only on the prompt submit path and not on the ! path;
or the live shell notice event's first line differs from the
fixture's $ command form; or the live per-event projection does
not see the notice because it is injected through maintenance after
the fold ran) and fix it within the W-058 mechanism, no new state.
Acceptance: a shipped-profile PTY test that types !printf hi,
waits for the output cell, presses Up, and sees !printf hi in the
composer; fixture shapes corrected to match the live event if they
differed (the W-037 lesson); production source under 15 lines.
Ships in alpha.14.
Grew into the HUD row by owner addenda: one values-only line
<model> · <effort> · <preset> · ctx <used>/<limit> <pct>% · cache <pct>% · <total> tok · <n> agents · <n> jobs · <title> · <repo>/<branch>, effort/agents/jobs only when present or nonzero,
drop from the right in that order; model and title from DSH's
selection and title projections, tokens summed over the four
token-meter buckets (DSH has no total), repo root and branch from one
read-only shell call at launch and turn/end kept as the latest
result; the hint line keeps the model only in accessible mode. 39
production lines; 272 tests. Ships in alpha.14.
Owner report 2026-09-04 on alpha.13: the W-056 status row shows
model <provider/model> · permission <preset> above the composer
and the pre-existing hint line below it still shows idle · <model> · Enter send · cards collapsed. Remove the model from the
hint line (it keeps state, send mode, card mode, and the exit-arm
text); the status row is the single place for model and preset.
Accessible mode, where the status row is hidden, keeps the model in
the hint line so the fact stays visible there. Acceptance: renderer
tests for both modes; production source under 10 lines. Ships in
alpha.14.
W-064 Rewind fails without a durable model selection — status: accepted 2026-09-04 (PR #115 squash-merged)
One carry rule (projection next, else lastUsed, else none); the fresh-root rewind path calls selectModel only when DSH recorded a selection, otherwise the new root takes DSH's default. A session is headerless when its prompt turn ended or was interrupted before request assembly emitted request/header and it started on the default model; that was the sole throw of this kind. Regression: headerless persisted log resumed through the shipped profile, both restore choices, plus a unit test. 14 production lines; 274 tests. Ships in alpha.15. Defect, owner report 2026-09-04 on alpha.14: rewinding to the very first prompt ended with "Error · the current session has no durable model selection". The message is dashi's, raised on the rewind path that creates a fresh root (first-prompt case, D-032) when it tries to reapply the session's model selection and the log has no model/selection event because the session ran on DSH's default. Fix: carry a selection over only when the source session has one (modelSelection projection next or lastUsed); otherwise the fresh root takes DSH's default and nothing is selected; the same rule for any other place dashi requires a durable selection (audit the throw sites). Acceptance: PTY test in the shipped profile: start without --model, one turn, /rewind to the first prompt with "conversation" and with "code and conversation", both succeed; production source under 15 lines. Ships in alpha.15.
W-065 Drag-and-drop files into the composer — status: accepted 2026-09-04 (PR #123 squash-merged; owner roller-exec)
A complete bracketed paste whose shell-decoded tokens (bare,
single-quoted, backslash-escaped) all exist becomes attachments for
image paths through the existing attach effect and @ mentions for
the rest through DSH's file-reference grammar, relative inside the
bound root's cwd and absolute outside; any other paste is inserted
unchanged; one stat per token; insert at the editor cursor. Review
fix: resolve against the bound session's cwd, not the launch cwd.
39 production lines; 280 tests. Ships in alpha.16.
Owner request 2026-09-04. In a terminal a drop arrives as a paste of
one or more file paths, shell-escaped (single quotes, backslash
spaces, or bare). Claude Code turns them into references. Scope: when
a bracketed paste, after unescaping, consists only of whitespace-
separated paths that exist on disk, dashi does not insert the raw
text; image files (by extension DSH's attachment service accepts)
become attachments exactly as --image PATH does, other files become
@<path relative to cwd, or absolute when outside> references in the
composer with a trailing space, through the existing @ reference
path. Any paste that is not entirely existing paths is inserted
unchanged. Pure input handling, no new state; the existence check is
one stat per path at paste time. Acceptance: input tests for the
three escape forms and a mixed non-path paste; PTY test dropping one
image and one text file (pasted with bracketed paste) shows an
attachment row and an @ reference; production source under 40 lines.
W-066 macOS gate on hosted runners — status: accepted 2026-09-04 (PR #121 squash-merged; production source 0)
Non-blocking macos-latest host-gate job in ci.yml. First two hosted
runs: setup, typecheck, build, lint, and all unit files pass; the
runner then receives an external shutdown about 200 s into the
clean-install PTY test (profile-pty.spec.ts:1191), both times at the
same point. Audit: no test signals a process group or kills by name.
Zero dashi assertions, Linux-only assumptions, or DSH gaps observed
before the cut. Follow-up decided by the mac peer's local host-gate
result (D-038). The job stays non-blocking until green.
Owner decision 2026-09-04: macOS is a primary target and nothing has
run there. Scope: a macos-latest job in ci.yml running the host
gate (pnpm gate, not the container; brew-installed tmux and screen)
on pull requests and develop, continue-on-error: true until it is
green, so it never blocks merges; release.yml unchanged. First run:
classify every failure as (a) a dashi defect on mac (fix in a
follow-up item), (b) a test-harness assumption (Linux-only helper,
bubblewrap, xclip) to skip on mac with a one-line reason next to the
skip, never a silent pass, or (c) a DSH gap on mac (README, upstream
queue). Handoff is the job plus the classified list; when the job is
green, a second PR removes continue-on-error. Production source 0.
W-067 macOS test harness — status: accepted 2026-09-04 (PR #126 squash-merged; owner mac-dashi; production source 0)
All nine mac failures fixed in the harness: line endings normalized;
tty compare on icanon/echo/isig/opost; realpath on both sides of
path assertions; screen socket dir under /tmp everywhere with
screen >= 4.1 required and the binary printed; clipboard fixture
fakes the platform helper production invokes (pngpaste on Darwin,
wl-paste/xclip on Linux); hermetic git config in every fixture and
pane; the worker-killing stdin case fixed at its cause (macOS bash
3.2 leaves BASHPID empty, so pid 0 signalled the process group; the
fixture now uses /bin/sh $$); turn and resize waits on observed
idle markers and the synchronized-render close. No Darwin skips.
Mac gate 274/274; hosted macos-gate green for the first time.
Mac peer's first host-gate run 2026-09-04 (macOS 27 arm64, Node 22,
pnpm 11, tmux 3.7, brew screen 5.0): non-PTY suites 164/164 pass;
PTY suite 100 pass, 9 fail, all harness assumptions, no DSH gap:
CRLF in the --version assertion; Darwin's PENDIN bit breaks exact
stty -g comparison (/memory, /agents editor, Ctrl+G); /private/var
temp paths wrap /init and /skills assertions; GNU screen socket path
over the Darwin limit (2); the clipboard fixture hardcodes wl-paste
and xclip; plus fixture git commits inheriting the user's signing
config, /var vs /private/var cwd assertions, system screen 4.0
lacking -Logfile, and the stdin-close-during-decision test killing
the Vitest worker on Darwin. Scope: one PR of test and fixture
changes only (production source 0): normalize line endings; compare
the tty mode fields that matter instead of the raw stty -g string;
realpath both sides of path assertions; short screen socket dir on
every platform; pbpaste/pbcopy fixture helpers selected by platform;
hermetic git config (GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM) in
fixtures; screen >= 4.1 required with the found binary printed and a
README mac note; the worker-killing case fixed at its cause or
skipped on Darwin only with the reason beside the skip. Acceptance:
pnpm gate green on the mac and the hosted Linux gate green; the
hosted macos-latest job (W-066) reported on after the change.
One shared exitChord (arm on first press, exit on the second within the window); Ctrl+C keeps its interrupt and draft-clear steps before it, Ctrl+D enters it after the nonempty-composer check regardless of root status or attachments. Reducer tests for running root and pending attachments; PTY test arms mid-turn with the child alive, then exits on the second press. 9 production lines; 281 tests. Ships in alpha.17. Defect, owner report 2026-09-04 on alpha.16, Linux and mac: pressing Ctrl+D while a turn is running exits at once, no arm, no second press; between turns it arms correctly. Cause: state.ts:501 routes ctrl-d straight to exit when the root is not idle or attachments are present. Fix: Ctrl+D always uses the shared arm (first press arms with the hint, second press within the window exits) regardless of root status or attachments; a running turn is not interrupted by Ctrl+D (Ctrl+C keeps that role). Acceptance: reducer tests for running root and for pending attachments; PTY test pressing Ctrl+D once mid-turn and seeing the arm hint with the process still alive, then a second press exiting; production source under 10 lines. Ships in alpha.17.
From PARITY.md, to be cut into work items in this order (W-042, W-043, W-047..W-052 cut 2026-09-04): none left. Remaining parity rows are DSH gaps; see PARITY.md and the upstream report queue.
B-001 Workspace restore plugin (Claude Code style file rewind) — status: closed 2026-09-04, satisfied by @antst/roller 0.1.2 (bundled in dashi-app since D-033; rewind offers conversation, code, or both since D-032)
A separate DSH plugin, not part of the dashi TUI: e.g. @antst/dsh-checkpoint
in its own package or repo. Records the before-content of every file
touched by DSH's own file-mutation tools, keyed by the turn boundary
DSH already logs, and exposes one native DSH command to restore the
workspace to a chosen boundary plus durable session events for what was
restored. dashi's rewind picker then offers "conversation only" or
"conversation and files" by calling that command; no TUI-side storage.
Constraints: storage through DSH's storage service under its sandbox
rules; only DSH-tool edits are covered, and shell/hook/external edits
are stated as not covered; no Git reset or stash; bounded per-session
size with a plain eviction rule. Not scheduled; opens after Phase B.
Origin: owner request 2026-09-02, DESIGN.md 8.2.
- 2026-09-02 DSH Discussion: JSONL session ownership is process-local, allowing concurrent writers (D-015). deepseek-ai/deepseek-harness#5460
- 2026-09-02 pi issue: expose frameless and prompt-prefix options on Editor (D-013). earendil-works/pi#9032
- 2026-09-02 DSH Discussion (Ideas): policy-aware FileSystem delete primitive for plugins (roller D-003). deepseek-ai/deepseek-harness#5461
- Status 2026-09-02: pi issue 9032 auto-closed by the contributor gate pending a maintainer reply; DSH 5460 has one community reply, no maintainer reply yet.
- 2026-09-02 DSH Discussion (Ideas): presentCall/presentResult on CommandDefinition (D-017 release). deepseek-ai/deepseek-harness#5462
- 2026-09-02 DSH Discussion (Ideas): ignorable marker for out-of-tree session event types (D-017 release). deepseek-ai/deepseek-harness#5463
- Status 2026-09-02 (later): no maintainer replies on any thread; one community reviewer corroborated all four DSH reports. Prior art they cited: session lock 5460 duplicates issues 1452 and 1333, community fix in PR 1550 (writer lease, unmerged); ignorable events 5463 is the sixth report after 1538, 1584, 1619, 2778, 3191; delete 5461 design notes (absent target succeeds, unlink must not follow the final component) already hold in roller. pi 9032 remains auto-closed pending maintainer review.
- 2026-09-03: the pi Editor frame/prefix change is being prepared on a fork branch (antst/pi, editor-frame-prefix) by the upstream lane, tests in pi style, no PR until a maintainer replies lgtm on issue 9032.
- 2026-09-03: pi Editor frame/prefix change ready on the fork: https://github.com/antst/pi/tree/editor-frame-prefix (commit c53792b, base upstream 4e69b0c); 52 changed lines in editor.ts plus a 103-line test file; all 920 pi-tui tests and the repo check pass. PR opens only after maintainer lgtm on issue 9032.
- 2026-09-03: fork branch editor-frame-prefix independently reviewed: PASS (scope, spec, byte identity, conventions; unrelated upstream failure confirmed pre-existing). PR-ready pending lgtm.
- Queued (not yet posted): DSH Discussion on two install hazards: (1) the
@deepseek-ai/dshCLI's caret ranges on its own packages resolve a mixed prerelease graph (alpha.5 CLI with rc.1 libraries fails to load); (2) nineteen packages (dsh-attachment, dsh-fs, dsh-shell, dsh-jobs, dsh-sandbox, dsh-compaction, dsh-session-persistence, dsh-session-query, dsh-settings, dsh-code-runtime, dsh-bash-local, dsh-workflow, dsh-spill, dsh-output-retention, dsh-session-telemetry, dsh-session-title-llm, dsh-authorization, dsh-anonymous-user-id, dsh-subagent-in-process-driver) are reached only as peer dependencies, so a user upgrading with an existing lockfile keeps stale versions that pnpm treats as satisfied (proven 2026-09-03: only a node_modules and lockfile wipe refreshed them); suggest a boot-time peer-version check or making them dependencies. Release to the lane when convenient. - 2026-09-03: branch link posted on pi issue 9032: earendil-works/pi#9032 (comment)
Load the AS-owned adapter into the dashi profile so an interactive
dashi session is addressable as a peer: the adapter binds to
ctx.tuiRoot.current() and follows tui/root-changed on /new,
/resume, /fork, /rewind; decisions stay with the terminal user
(no preset override in the interactive profile). Needs the adapter to
expose the terminal-bound-root branch it already planned for; dashi
side is expected to be documentation and a fixture test of the host
contract (DESIGN.md section 11), no new mechanism. Opens after Phase C.
- Queued (not yet posted): DSH Discussion (Ideas): session-scoped model/effort selection. rc.1 selectModel always calls agentDefaultModel.saveSelection (packages/api/session-controller/src/commands.ts:119-145) and neither SessionCreateRequest, resolveAgent, nor AgentPreset carries a model or effort, so a launch flag like
--modelcannot avoid moving the deployment default (W-025). Release to the lane with the install-hazards report. - Queued (not yet posted): DSH Discussion: cold session list never shows a title for a seeded (forked) session whose inherited log exceeds coldBlankProbeMaxBytes; ApiSessionList refuses projection-cache rows for cold seeded sessions (packages/api/session-controller/src/list.ts:327-336) and probes the log only under the size bound (list.ts:164-205), so a durable session/title event is ignored in the picker until the session is opened (W-051). Suggest honoring a projection-cache title row, or probing the tail of the log for the latest title event.
- Queued (not yet posted): DSH Discussion: @deepseek-ai/dsh-mcp-client spawns stdio MCP servers with stderr inherited (transport.ts:31-39 passes no stderr option; the MCP SDK defaults to 'inherit', stdio.js:48-75), so server logs write straight to the host terminal and corrupt any TUI frame; suggest piping stderr into DSH's logger or a diagnostics channel. Nearest existing thread: Discussion 4465 (silent stdio spawn failures); related 1241, 5129; third-party reproduction ccch1mneyyy/dsh-TUI issue 17 (W-057).
- Queued (not yet posted): DSH Discussion:
dsh plugin addof a plugin package without a bundle manifest installs it and warns, but composes no loader row, so it never loads; suggest adding an insert row to the profile patch (or adsh plugin enable <pkg>verb) and making the warning say what is missing (apps/cli/src/plugin.ts:59-91) (W-061). - Queued (not yet posted): DSH Discussion: live patch reload (patchReload: live) applies cordis.patch.yml edits at runtime but not dsh.profile.bundles additions, and nothing documents the asymmetry; suggest either reloading bundle layers live or stating the restart requirement in the profile docs (apps/cli/src/profile-boot.ts:279-286, packages/boot/app-boot/src/index.ts:236-267) (W-061).
- Queued (not yet posted): DSH Discussion: with
pnpm install -g @deepseek-ai/dsh@0.1.2-rc.1under pnpm 11 on macOS, every profile fails to boot, including DSH's owndsh --profile web, with missing @deepseek-ai/dsh-llm (and others). Cause: healProfilesModuleFallback (packages/boot/app-boot/src/profile.ts:578-592) walks the CLI's dependency closure with require.resolve.paths from the CLI package's own location, but pnpm 11's global layout keeps the CLI package under store/v11/links with its dependencies hoisted into the global instance's .pnpm/node_modules, which that walk does not reach; the healed $DSH_HOME/profiles/node_modules ends up with 68 of the packages and no dsh-llm. Reproduced 2026-09-04 on macOS 27 arm64, pnpm 11.7. Suggest resolving the closure from the pnpm virtual-store instance (the realpath's .pnpm/node_modules sibling) or documenting that global pnpm installs are unsupported and npx/local installs are the path. - Queued (not yet posted): DSH Discussion: session-only model selection, restated for the Agent Sessions lane case: SessionSelectModelRequest carries sessionId but selectModel always calls agentDefaultModel.saveSelection (packages/api/session-controller/src/commands.ts:119-145), so a headless lane opened with a model moves the deployment default for every later session in that DSH home (D-039; duplicates the W-025 entry's ask with the second motivating case).