Review feedback (tianyicui, PR #657 inline): the missing-service message
should mention dsh-tasks, which defines ctx.tasks, rather than promoting a
specific backend. The seam's own surfaces (README, the direct-mount fence)
keep pointing at implementations, so the pointer chain still lands on
dsh-tasks-local without the producer strings going stale when another
backend becomes the recommended default. Agent Note updated accordingly
(en+zh, 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.
Prompt admission runs before any turn opens, so the agent/prompt-submit
flush listener could only ever append plan/mode outside an open turn —
invariant-vetoed and retried at agent/step when the session invariant is
mounted, silently out-of-turn in the durable log when it is not. agent/step
fires inside the open turn before every request derivation (including
turn 1 step 1), so it is the sole flush point and no behavior is lost: a
pending idle switch lands as the first step's in-turn plan/mode. Tests pin
the new contract: admission never appends; the first step boundary flushes.
concludesTurn is terminal: a completed step now carries the concluded fact
to the driver, which ends the turn immediately instead of letting steering
that arrived during the tool batch (or from an agent/stopping listener)
reopen it. The steering is not lost — it drained into the log as
steering/message before the decision point, so it feeds the next turn's
request. Also annotates the driver's unreachable invariant guards
(exhaustiveness default, abort-slot ownership) for the coverage gate.
The static CI gates run per-branch on the merged tree: regen the cordis
catalog/api, config, persistence, and doc-graph outputs that PR3/PR4's
source changes shifted, and add the CodeDispatchLog manifest entries for
the tools.md pair's new type-equiv block.
Trajectory: the layout fold interleaves one subtool cell per sub-dispatch
after its parent Tool cell (assistant-block calls, orphan results, and
running calls alike), indexes sequential across the interleave; settled
durations come from the start/settle pair, running sub-calls show the
em dash. New Sub tag (business tint) + 28px indent.
Waterfall: deriveSubSpans folds the dispatch index into per-turn lanes
with REAL wall time — each parent's window is first start → last settle
and every lane's offset/width is its fraction of it, so parallel
sub-calls visibly overlap; running lanes extend to the window end at
reduced opacity. Lanes draw under the owning turn row.
Both views read codeDispatches through the standard snapshot hook; no
new wire data, replay renders identically to live. Specs pin interleave
order, durations, the running arms, window fractions, and the rendered
lane.
ds-review-bot finding: a growing fence retokenized on every chunk
(quadratic main-thread work). MarkdownText gains a streaming flag —
the streaming partial renders fences through the plain arm and the
finalize swap highlights once; AssistantMarkdown threads its existing
flag. (The zh Agent Note pair the review also flagged landed earlier
on this branch.) New spec pins plain-while-streaming and
highlighted-after-finalize.
ds-review-bot finding: awaiting shapeDispatchLog before resolving the
binding let a slow spill backend delay the program and occupy a
dispatch slot. The settle now resolves the program immediately; the
shaped append runs as tracked side work (logWork) drained at run
settlement, so every tool/code-dispatch event still lands inside the
open turn. New spec pins the contract: with a hung spill backend the
second dispatch starts and the program completes both calls, and both
settle events land once released.
ds-review-bot findings: the browser e2e's error-state check now requires
at least one error sub-row (was >= 0); the runtime README documents the
codeDispatches snapshot contract and the ui-conversation README the code
variant + nested sub-row semantics.
ds-review-bot findings on the parallel bridge: sub-dispatches now run
through the registry's staged scheduler view (the loop's own seam) —
ordered prepare (pre-execute/guards) at submission-ordered start time,
only the dispatch/body stage overlapping, and a head-of-line commit
cursor running post-execute, context deferral, and the settle event in
submission order (new spec pins post order + context order under
out-of-order completion). Queued dispatches reclassify via
executionMode() immediately before starting, so a registry mutation
while queued flips them exclusive (native lazy-reclassification
semantics). Config and tool catalogs regenerated; the tool-catalog
metadata now names the start/settle pair and the scheduling contract.
ds-review-bot findings: the packaged Python runtime smoke's scripted
run_code call gains the required description; the ToolDefinition JSDoc
and the Code Mode foundation note (both languages, pair re-recorded) now
state both required parameters; the cordis-dynamic-toolchain fixture's
request/header line is re-compacted so the header-scrub hygiene guard
passes (my earlier patch had re-spaced it). The TUI terminal fixture was
already regenerated from keyless replay in the previous commit.
One highlighter for the client: a synchronous fine-grained shiki core
(JS regex engine, no WASM) in ui-primitives with an explicit grammar
allowlist (typescript, shellscript, json — aliases resolve, unknown
languages take a geometry-identical plain arm). The shared CodeBlock
component owns both arms; markdown fences, the run_code expanded
program body (typescript), and the details panel Input (json) all
route through it. Token colors live in a new ui-theme shiki.css sheet
as --shiki-* custom properties (light/dark blocks), wired through the
shell's base.css chain — tokens-only styling holds; shiki's generated
span tree is the sanctioned innerHTML path (static output, no user
HTML). jsdom specs pin token spans, aliases, both fallbacks, and the
fence route; the built-bundle snapshot asserts the highlighted program
under the code row.
New tools/code-dispatch-log waterfall (run via registry.shapeDispatchLog,
contained — a throwing listener falls back to the unshaped content) lets
listeners reshape the tool/code-dispatch event's content before the
bridge appends it. dsh-spill-policy registers a second arm sharing the
model-facing arm's exact replacement pipeline (same maxInlineBytes cap,
preview + locator, within-cap invariant, best-effort fallbacks), with
artifacts labeled dispatch under the sub-call id. The program's value is
untouched; read sub-calls ARE bounded (a log copy is not model context,
and read produces the biggest logs). Resolves the tools README's
uncapped-dispatch-log Known Limitation.
- The acp-demo and cli-demo built-bin e2e consumers link a pruned package
closure; add process/process and process/process-local so the built bins
boot now that bash-local injects ctx.processes.
- verify-package-paths scans backticked packages/* references, so the
timeout-note's historical mention of the deleted run.ts path is reworded
(en+zh, both sides now carry the same historical-vs-current split; the
earlier zh edit had silently missed its match); pair re-recorded.
- Mount LocalProcessManager in the sandbox e2e compositions (bwrap/landlock/
seatbelt + the spine multi-project e2e) and add the spine demo's
dsh-process-local devDependency, so SandboxBashExecutor's new inject
resolves when those suites are enabled.
- Extend the Windows test/coverage skip to packages/process/* — the POSIX
process-group suite moved there from packages/bash.
- Update the stale disposal contract: the bash seam JSDoc, BashProcess JSDoc,
and core bash doc (en+zh) now state that composition teardown (the process
manager's disposal) owns kill-and-await, and an executor-only reload leaves
background processes running.
- Record ctx.processes in the architecture capability table and extension map
(en+zh) and the root AGENTS.md layout tree; reword the timeout-library note
so it describes where the plumbing and classification live today.
Review finding (Codex round 1): abstract erases at runtime and
@deepseek-ai/dsh-tasks used to be the mountable registry, so a stale
composition row would register a ctx.tasks with no method implementations
and fail far from the misconfiguration. The seam constructor now rejects
direct mounts with a load-time pointer at dsh-tasks-local; the seam suite
pins the fence, the Agent Note cost paragraph records the actual behavior,
and the stale tool-pty README requirement line names the implementation
package.
Review finding (Codex round 1): abstract erases at runtime and
@deepseek-ai/dsh-tasks used to be the mountable registry, so a stale
composition row would register a ctx.tasks with no method implementations
and fail far from the misconfiguration. The seam constructor now rejects
direct mounts with a load-time pointer at dsh-tasks-local; the seam suite
pins the fence, the Agent Note cost paragraph records the actual behavior,
and the stale tool-pty README requirement line names the implementation
package.
New process/ capability family: @deepseek-ai/dsh-process owns ctx.processes —
abstract ProcessManager.spawn(spec) over a fully-explicit ProcessSpawnSpec —
plus the shared DSH_* managed-environment and CollectedOutput vocabulary;
@deepseek-ai/dsh-process-local carries the former bash-local run.ts plumbing
(detached groups, tail-keep spill-backed output, credential scrub, kill
escalation, kill-and-join disposal) with no config of its own.
dsh-bash-local becomes a consumer: it keeps command defaulting, the fused
deadline timedOut/aborted classification, the model-friendly terminal env
(now merged through the ordinary env channel), and the [stderr]-marked
background read merge, and spawns through ctx.processes. Background-process
lifetime moves to the manager, so an executor reload no longer kills live
background work; a background spawn failure is injected once into the read
path instead of being buffered as fake stderr. dsh-bash re-exports the moved
vocabulary so bash consumers keep one import root; dsh-bash-sandbox only
redeclares the inherited inject.
Every composition loading a bash executor now loads dsh-process-local (CLI,
examples, python bundled runtime, create-sdk bash feature, inline test
configs).
The bridge replaces its serialization queue with a pool that reuses the
native concurrency contract: submissions classify through
registry.executionMode (fail-closed isConcurrencySafe), start strictly in
submission order, overlap up to the validated maxParallelSubCalls config
(default 10; 1 restores serial), and exclusive calls drain the pool, run
alone, and bar later calls. Each started sub-call logs a
tool/code-dispatch-start event at pool entry; the existing
tool/code-dispatch settles the pair (started ⇔ settles exactly once;
abandoned queued calls log neither). SDK prompt guidance now states the
true Promise.all contract — re-recorded across every code/both-mode
snapshot (plus the stale cordis-dynamic-toolchain fixture gaining the
required description arg).
Client: CodeSubCall widens to RunningToolCall | ToolResultNode — starts
land the running shape (rows wear the native running ring), settles
replace in place preserving start order, callTime pairs to the start
time. Fixture emits start/settle pairs; jsdom pins the running sub-row;
runtime specs pin in-place settlement and out-of-order completion.
The tasks/ family now matches the capability-seam shape: @deepseek-ai/dsh-tasks
keeps the abstract TaskService (ctx.tasks contract, vocabulary types, snapshot
invariant companion) and the new @deepseek-ai/dsh-tasks-local carries the
process-local registry (LocalTaskService: in-memory store, settlement,
owner-cleanup effects, teardown, TASK_WAIT_TIMEOUT). Compositions and test
harnesses now load dsh-tasks-local; producers, TaskKindMap merges, and
dsh-tool-tasks keep importing the seam only.
Producer misconfiguration diagnostics name dsh-tasks-local because loading the
implementation is the fix. The registry behavior suite moves to tasks-local;
the seam keeps a stub-subclass registration test and the probe-based invariant
suite.
The origin/master merge introduced a required workspaceRoot on
ApiProxyDefaults and deleted the ui-conversation test hook.ts helper.
Add workspaceRoot to the new todo-projection api-proxy test and bind
the todo-panel spec's selector hook via bindSnapshotSelector directly,
matching the sibling specs.
The five settings-surface registrants carried near-identical
spec-check/ledger-judge/subscribe scaffolding (three jscpd clones);
ui-slots now owns deferRegistration() — ledger-judged presence, refresh
for registrant-localized labels, one-call disposal — and every
registrant shrinks to its registration body.
The client indexes tool/code-dispatch events into
ConversationSnapshot.codeDispatches (parent callId -> ToolResultNode-shaped
sub-calls; live mux and history replay build the identical index). ChatView
renders each run_code parent as the new code variant (description summary,
program as the expanded monospace body) with its sub-dispatches as
always-visible indented rows — every sub-row dispatches through the SAME
keyed conversation.chat.toolview hole with the same GenericToolCard
fallback, so custom registrations (bash sample) take over sub-rows exactly
as top-level rows. The details panel resolves sub-callIds to full logged
args and complete output through the native path.
Evidence: fixture turn 64 + built-bundle jsdom snapshot, real-machinery
jsdom suites (nesting, error state, details, running parent, reference
stability), and a recorded code-mode browser e2e round (keyless replay +
aria golden). Scaffold gains a toolsMode patch knob.
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).
markLlmAdapterFailure gated the own-`failure` data property on instanceof
HarnessError, which drops the validated facts exactly when class identity is
lost — two copies of this package in one process (e.g. a source-plane replay
harness throwing into a lib-plane boot) make the replay-thrown LlmError's
SERVER/AUTH code arrive as UNKNOWN and defeat llm-retry's retryable-code
match. The snapshot is already validated field-by-field and cross-checked
against the error's own code, so honor it on any Error.
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'.
The ui-layout apply bench provides a real LocaleService before the
theme plugin boots (ui-theme now injects slots/locale to register its
Appearance row); drop locale's unused clsx dependency.
Locale and ui-theme apply coverage on a real Context + SlotCore:
dictionary assembly, declaration-aware registration both ways,
inject-time getter re-sync, service write-back through the event flow,
HMR collapse recovery, and teardown reclamation. Four settings-surface
packages sit at full per-file coverage.
Generated docs follow the ui-models rename and the removed
ui-settings-general package; the English note side picks up the
feature-owner self-registration doctrine.
Settings collaboration direction (recorded in the note): the shell only
provides composition faces — feature plugins register themselves. The
General section moves into the ui-settings shell (order 0, skeleton
rows) and declares the settings.general.item list slot; locale registers
the Language row and ui-theme the Appearance row (each with its own
store mirror, dictionaries, and ledger-judged deferral); the
ui-settings-general package is gone. ui-settings-models becomes
ui-models — a feature package that contributes its Settings section
rather than a settings-owned satellite. The item-slot SlotMap entry is
authored in the ui-settings contract and repeated verbatim in
locale/ui-theme (reference-cycle avoidance; declaration merging keeps
the copies identical).
run_code gains a required bash-style description parameter: presentCall
titles the card with it and moves the program to rawInput, so every
surface gets a readable label. tool/code-dispatch now logs each
sub-call's complete content/isError (the tool/result vocabulary),
replacing the bounded resultSummary and deleting the summarize/cwd
machinery — a UI renders sub-calls through the identical path as native
results. The dsh config tree mounts the worker code runtime and reads
DSH_TOOLS_MODE (temporary seam until per-session mode selection lands).
Session format stays v0 (pre-release churn). Code-mode ACP/TUI fixtures
re-recorded; TUI presenter pin refreshed; catalogs regenerated. Keyless
web smoke pins the code-mode wire contract (tools=[run_code] + SDK
prompt section).