Every spec now commits at least one golden and the interactive ones one
per distinct end-state (nine new .expected.md):
- live-interactions: cancel.expected.md (frozen partial + 已停止 marker),
error-auth.expected.md (the prompt bubble alone — the committed artifact
of the web-error-surface gap, the diff that flips when error rendering
lands), retry.expected.md (indistinguishable from a clean completion —
retries are deliberately invisible in the transcript).
- question-composer: answered.expected.md (the question resolved into its
tool round trip plus the final reply, takeover gone) beside the existing
waiting-state golden.
- steering: mid-steer.expected.md pins the accepted-but-INVISIBLE state
(the loop drains steering only at the step boundary, so no interjection
bubble exists while the question still blocks — if the client ever
renders pending steers eagerly, this golden flips first) and
settled.expected.md the badged bubble plus obeying reply.
- navigation-panes: waterfall.expected.md and details-open.expected.md
(tool-name header, Input args, Output result) beside the trajectory one.
- lifecycle-chrome: reloaded.expected.md — rendering the same settled
transcript from persistence alone IS the recovery claim.
Fixture inventories extended to the new closed sets; the Agent Note's
expected-outputs policy updated in both languages (per-end-state goldens
for interactive scenarios), pairing re-recorded.
One tiny recorded text turn drives three whole-page concerns:
- workspace flow over the real wire: the empty-state hero's first send
materializes a real Workspace + Session (the jsdom workspace-flow suite
pins this state machine over the fixture client; this scenario pins it
through HTTP RPC + SSE + the gateway). Durable proof: the session
header's cwd is the create-by-name target <workspaceRoot>/workspace.
Adds the hero waiting-state aria golden.
- reload recovery: collapse the sidebar (persisted dsh.layout.panels),
page.reload, and the surface comes back whole from persistence alone —
layout collapsed, selection restored (dsh.sessions.current), the
recorded turn re-rendered from session.history with zero model calls
(the drained replay cursor makes any stray request fail loud at close).
- dark mode: no product control flips the theme yet, so the scenario
drives the ThemeService's entire DOM contract — body[data-ds-dark-theme]
— and pins the shipped cascade: the alias token flips, a painted surface
repaints, and removing the attribute restores the light sample exactly.
TODO(web-theme-gesture) upgrades to a real settings control; no theme
golden per the lane's scope ruling (aria is color-blind).
Agent Note scenario list extended in both languages; pairing re-recorded.
One two-turn seed (turn 1: bash + two parallel reads in a single assistant
message; turn 2: a markdown-heavy reply) rendered cold through the
seeded-history pattern — zero model calls — serving four surfaces:
- sidebar search: client-side title filter; asserted only after the durable
title lands with the attach baseline (a cold SessionSummary carries no
title — search matches the displayTitle the user sees). Negative query
empties the tree, positive narrows to the match + its force-expanded
group, clear restores.
- Trajectory tab: turn sections, the step group's tool mix ('bash read×2'),
and a view-area aria golden.
- Waterfall tab: span stats header + one lane per span. The P-I fold counts
a turn-0 prologue span (only assistant/steering nodes carry a turn
number) — pinned as-is; real spans are P-III per the view's ledger.
- details column: the bash toolview row routes click to openDetails;
open/closed is asserted on the frame's data-details-collapsed attribute
because close collapses the grid column to width 0 without unmounting
the subtree (hidden, not absent, is the contract).
Agent Note scenario list extended in both languages; pairing re-recorded.
Both languages: the three new scenarios (live-interactions overrides,
question-composer takeover, wire-level steering), the product-delta list
({ patches } override form, the carried-failure fix, the llm-retry row),
two new Deferred items (web error surface, composer steering gesture),
and de-hardcoded scenario counts; pairing re-recorded.
- rename same-title no-op moves inside the serialized creation chain
- insertSessionBefore maps only the typed WorkspaceMoveInvalidError to
workspace-move-invalid; storage failures stay internal
- workspace upsert rejects snapshots older than the installed projection
- flat-mode empty state shows when the query hides the intent row
- intent row no longer forces group expansion; header twist stays live
- group-by menu rides a portal; menu clicks stop propagating to the row
- intent row uses the same single-slot indent in both list modes
- regenerate cordis api/catalog + doc graphs
Integrate the Commander adapter with master's `dsh web --workspace-root`
(workspace-aware session flow).
- args.ts: add `--workspace-root <path>` to the web subcommand; WebInvocation
carries workspaceRoot.
- web.ts: keep the adapter-parsed signature, take (host, port, dev,
workspaceRoot) and pass workspaceRoot through to AppCLIEntry (drop master's
re-added parseArgs and CLI host/port validation — the schema owns those).
- bin.ts forwards invocation.workspaceRoot; args.spec + the Agent Note pair note
the flag.
Integrate the Commander adapter with master's headless rework (dsh -p now boots
through AppCLIEntry — the same cordis.yml composition as dsh web, web-observable
while it runs — via toFetchHandler(ctx.apiProxy)).
- headless.ts: keep master's AppCLIEntry-based body but take the adapter's
pre-parsed `task` (drop the re-added parseArgs). Old startHost path and the
now-deleted packages/host/runtime dependency are gone.
- remove the stale packages/host/runtime dir left on disk by master's deletion
(mirrors the earlier packages/ui/acp cleanup).
ds-review-bot: `dsh web -p task`, `dsh web --resume s`, and `dsh --config c.yml
web` reached the web action with those values in program.opts() but the action
ignored them and served — silently dropping mode-specific inputs. The web
action now reads the parent opts and fails loud (exit 1) on a leaked
--config/-p/--resume, matching the root mode's mixing guard. Covered in
args.spec.ts.
Also (ds-review-bot): tui-demo/README documented the removed `dsh
[path-to-cordis.yml]` positional form; corrected to bare `dsh` / `dsh --config
<path>`. Agent Note + Chinese pair note the web-leak guard.
Rewrite the Agent Note's Decision/Resume/front-door/Consequences sections and
its Chinese pair in present tense, dropping changelog phrasing ("X replaces an
earlier Y", "retired the env var", "which the merge brought in", "Anyone who
ran X now uses Y", "an earlier revision dispatched..."). The note now introduces
the current grammar directly; Problem and Alternatives keep the motivation and
rejected designs the format requires.
The adapter no longer validates --host/--port or declares the allowed set:
LOOPBACK_HOST/ALL_INTERFACES_HOST leave args.ts. --host/--port are now
unvalidated pass-through overrides — the adapter only Number-coerces the port
string (the dsh-host-webserver schema wants a number). That schema
(host a 127.0.0.1/0.0.0.0 literal union, port a natural <= 65535) is the single
source of both the default (the shipped cordis.yml webserver row) and validity;
AppCLIEntry patches an explicit flag into that row, so a bad host/port fails
loud at the schema on boot (verified: `dsh web --host 9.9.9.9` and `--port abc`
both exit 1 with the schema's ValidationError).
web.ts keeps two display-only literals (the printed loopback URL, the
all-interfaces LAN-detection check), commented as mirrors of the schema, not a
source of truth. Agent Note + Chinese pair and README updated; the args spec
drops the host/port exit-code cases (now the schema's job, covered by the web
smoke on boot).
Drop the bare `dsh <config>` positional in favor of a `--config <path>` flag.
Without a root positional, `web` can be a real Commander subcommand in one
program instead of the reserved-first-token dispatch to a second parser, so
`dsh --help` lists every mode natively (no hand-pasted command text) and the
second parser + reserved-token machinery are gone.
Grammar:
dsh TUI (shipped tree + ~/.dsh overlay)
dsh --config <path> TUI, alternate tree (demos/tests only)
dsh --resume <id> TUI, resume a session
dsh -p "task" headless one-shot
dsh web [--host --port --dev]
`dsh` is the product front door with no positional; `--config` exists only so
demo:cordis, demo:code-mode, and the keyless PTY smokes can point the shipped
bin at an example tree. Those three sites and the /resume re-exec argv move to
`--config <path>`. The `-p` + `--config`/`--resume` mode-mixing guard and the
cordis.yml-owns-host/port-default fix are preserved.
Agent Note + Chinese pair, README, tui.ts docs updated. All 13 PTY smokes
(including code-mode via --config and the exec-replace resume handoff) green.
The merge's "always pass adapter-resolved host/port to AppCLIEntry" made the
adapter's 127.0.0.1/3080 shadow apps/cli/cordis.yml's webserver row — editing
the yml port would have had no effect, a duplicated default.
The adapter now assigns no host/port default: an absent --host/--port leaves the
field undefined (WebInvocation.host?/port?), runWeb forwards each to AppCLIEntry
only when present, and AppCLIEntry patches the webserver row only for an
explicit flag. cordis.yml is the single source of the host/port default; the
adapter still validates a flag when given. Removes the now-unused
DEFAULT_WEB_PORT; LOOPBACK_HOST/ALL_INTERFACES_HOST stay as the allowed-value
vocabulary (validation + the printed URL/LAN line).