Store the captured validation error as unknown in a wrapper object and
rethrow it directly: the instanceof-Error normalization added an
un-inducible false branch that failed harness.ts's 100% branch gate.
- proposed/testing -> implemented/testing with the lifecycle rewrite
(Proposal->Decision in present tense, Acceptance criteria + Risks
folded into Consequences); zh counterpart mirrored and both pairs
re-recorded.
- the rejected NIH-audit roll-up pair now links the implemented/ path.
- loader-smoke README: captured output is bounded by execa's default
100 MB maxBuffer, no longer unbounded.
- acp-snapshot README: harness.ts now also imports vitest (vi.waitFor),
so the vitest-run-only constraint names both modules.
- jsonrpc keyless smoke: raise the invalid-env case's subprocess
deadline to 25s (the 9s pick starved a cold tsx boot on slow NFS).
Validate replay sidecars and cross-copy failure facts, make browser console tripwires and macOS temp paths deterministic, and wait for asynchronous TUI resume details. Keep the owning docs, translations, and generated catalog aligned.
Implements the execa Agent Note's four sub-changes:
- execa (root devDep + loader-smoke dep) replaces the hand-rolled
spawn-collect-timeout choreography in loader-smoke, apps/cli and
cli-demo/acp-demo built-bin e2e, lsp-local and code-runtime-worker
built-lib e2e, the tui pty-harness outer collector, the jsonrpc
keyless smoke, and crash-recovery's child spawn. Genuinely custom
parts stay custom: cli-demo's interrupt-on-marker, jsonrpc's
line-predicate protocol driving, crash-recovery's SIGKILL-at-failpoint.
The two loader-smoke /* v8 ignore */ OS-error branches are gone.
- llm-mock-server CLI tokenizes via node:util parseArgs; numeric
coercion/bounds/cross-option constraints stay manual; pinned
error-message tests updated to the parseArgs texts.
- both loadRootEnv copies in apps/web/tests are deleted: the owning
vitest configs (web unconditionally, snapshot in record mode)
already load the repo-root .env before these files run.
- the four poll loops (acp-snapshot harness waits + crash-recovery
waitForFile) ride vi.waitFor with explicit {interval, timeout}.
Two new keyless scenarios for the functionality master gained since this
lane's base (#644 websettings, #643 workspace browser rework), both zero
model calls:
- settings-chrome: the modal shell (sidebar-foot trigger aria states,
role=dialog, aria-current section switch to the deliberately empty
Models, Escape + close-button paths, dialog aria golden); the Appearance
row as the REAL theme gesture — retiring lifecycle-chrome's
TODO(web-theme-gesture): clicking 深色 runs aria-pressed -> persisted
dsh.theme -> body[data-ds-dark-theme] -> alias-token flip, survives
reload, and 'system' follows the emulated OS scheme both ways; the
Language row switches the settings-scoped copy to English (dsh.locale
persisted, survives reload) and restores zh. Intentional reloads tear
the SSE stream, so the spec drains exactly its own reconnect warnings —
the tripwire still fails on unexpected connection loss.
- workspace-management: create-by-name twice through the region-header
dialog (host-durable via ctx.workspace.list()); rename end to end —
hover-revealed row menu (the button is display:none until the row
hovers), duplicate-name pre-check (inline role=alert + disabled primary
before any wire call), then workspace.rename through the real RPC,
row update, host durability, reload survival; the flat 'In one list'
view (section label flips, group headers drop, dsh.workspace.view
persists across reload, grouped restored); the session hover card
(dwell to open, closes on pointer leave). The one session row reuses
seeded-history's committed seed — no new recording. Deliberately not
driven: the inert menu rows and drag reorder (deferred in the note
with re-entry triggers).
Agent Note gains scenarios 8-9 and the drag-reorder deferred item in both
languages; llm-replay README's zh side catches up with the { patches }
paragraph; pairings re-recorded.
Downstream packages lost the tests that exercised their agent-loop-facing
edges when the loop was rewritten. Restore 100% per-file coverage with
behavior tests through public seams: llm-retry config validation and
cancellation races, goal replay drift/staleness/teardown edges, plan-mode
disposed-flush, workspace-context empty-change commits, api-proxy
synchronous send failures, acp-snapshot spill-path extraction and refresh
write-back, ACP injection-triggered turns, cli-demo and tui inbox
lifecycle edges, and agent-loop retry/settlement/lifecycle branches.
The only source changes are narrowly-justified v8 ignore annotations on
invariant guards and one redundant-guard removal (workspace-context).
The branch edited the English side of 17 doc pairs without their Chinese
counterparts. Bring every .zh.md up to date (loop.ts references now point
at agent.ts, send/steer/inject unification, context/message folded into
user/message, HookContext replaced by UserMessageData in tool decision
blocks, plan/skill delivery moved to agent/step) and re-record the pairing
hashes. Deduplicate type-equiv.manifest.json entries duplicated by the
master merge and drop entries for symbols the refactor removed. Complete
the missing JSDoc on extractSnapshotSpillPaths.
The ReplayConfig.overrideFile JSDoc still described only whole-script
replacement; it now names both sidecar forms and links ReplayOverrideDoc
(config catalog regenerated: source line shifted).
The override sidecar now accepts { patches: [{ at, entry }] } alongside the
legacy whole-script ReplayEntry[] replacement: the JSONL-derived script is
kept and only the named call indexes are swapped (at == length appends, for
a retry attempt following an injected transient throw). Out-of-range or
non-integer indexes fail loud with the derived length in the diagnostic.
This is the mock-LLM error capability the web e2e scenarios drive: 'call N
throws AUTH/SERVER, everything else replays as recorded'.
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).