A jsdom slot test runtime for feature specs: a real Cordis Context, the
production SlotsService and web-react renderer, and typed session/workspace
doubles (TestSessions implements ISessions with FixtureSession sessions;
TestWorkspaces implements IWorkspaces), so the compiler flags fixture drift
when a production face changes. Fixtures feed plain data: list rows,
conversation snapshots, and ISession-typed behavior stubs; provide-bundle
materialization runs the shared SessionProvideChannel.
DOM snapshot support: declare()/renderSlot() mount a single slot inside a
data-slot wrapper for local .snap capture, and a snapshot serializer folds
CSS-module class hashes to their semantic locals and collapses svg internals
to a content fingerprint. The typed provide() constrains declared-service
fakes to Partial of the service's outward face.
Static: the cache package.json files array matches the workspace
constraint shape, the unused dsh-storage-json devDependency is dropped
(tests run on the memory backend), and docs/module-graph.md is
regenerated for the new package edge.
Coverage: two unreachable branches deleted rather than tested —
coldSnapshot's floor-0 tail reuse (a baseSeq-0 restore never throws and
an unrelated record still carries a usable watermark) and flushSoft's
non-mandatory clean-skip (throttle triggers only fire dirty). New tests
close the real gaps: write() on a never-dirty session and the non-JSON
unit-state rejection, plugin disposal clearing armed interval timers,
cachedSnapshot's all-version-mismatched and cwd-identity arms, the
zero-units empty-log cut, the coordinator seek-hook ladder (suffix /
not-found / plain failure / abort-reason relay), and the superseded-
retirement race proving forget()'s exact-entry guard.
SessionSummary grows an optional projections column (whole value per key,
same passthrough posture as the history-tail block): attached rows cut the
live registry watermark cache; cold rows view the persisted projection
cache's stored rows via the new registry viewCheckpoint face (version-
matching keys only, zero I/O) — the RFC's motivating scenario, every
session's title across a listing without loading one event log. The column
is fail-soft and absence-coded: no registry, no cache row, or a throwing
read serve the row without the column, never breaking the listing.
New package on the domain data form: one session_projcache record per
session (key → {stateVersion, observedSeq, state}), landing beside
workspace.json under the shipped json backend. Write policy: two mandatory
points (turn/end + session disposal) with count/interval throttling between
them (both Config fields required — flush cadence is a deployment choice);
every background write is fail-soft (log + stay stale, self-heal on the
next write or cold read). coldSnapshot(id) runs the read ladder — cached
rows + persistence readFrom from the registry's anchored restore floor +
registry restore + fail-soft write-back — detecting crash-repair-shrunk
logs via the one-below anchor and degrading to a single full re-read.
Mounted in apps/cli/cordis.yml (writeEveryEvents 200 / writeIntervalMs
5000).
The master merge left the generated catalogs/module graph stale, the
python runtime closure missing dsh-session-projection (now reached
through session-title and tool-todo), and apiproxy holding a dead
session-title dependency (the bespoke title frame is retired). The four
files the merge pushed under the per-file coverage floor (commands
executor + invariant, projection registry drive tails, TUI) join the
existing TODO(gui) deferral block per the GUI-lane policy; the remaining
coverage-run failures reproduce identically on pure origin/master
(environment-bound suites: sdk process exit, TUI PTY timing, workflow
worker timing, title loader slow-boot) and are not this branch's debt.
commandId crosses three boundaries (session log, wire admission response,
client flow pairing), so per the branded-id rule it becomes
Branded<'CommandId'>, declared in a new pure @deepseek-ai/dsh-commands/brand
outlet (the dsh-llm/brand shape: type + constructor, no Context merges, so
wire and client programs can name it without loading the host plugin). The
event payloads, CommandExecution, and the executor mint carry the brand; the
wire schema gains commandIdSchema as the domain's single brand-cast point
(the approvals precedent); CommandNode and the fixture's fabrication cast
follow type-only.
The group's convention is package suffix == provider default
(subagent-acp/'acp', subagent-spawn/'spawn', subagent-fork/'fork'), and the
provider default became dsh-sdk in the last review round — so the package
follows: @deepseek-ai/dsh-subagent-dsh-sdk at
packages/subagent/subagent-dsh-sdk, plugin name subagent-dsh-sdk,
diagnostics prefixed subagent-dsh-sdk:. The dsh echo has precedent
(dsh-llm-deepseek). Directory, fixture path, knip/tsconfig/examples
registrations, catalogs, READMEs (en+zh), and the Agent Note follow; the
sdk-client dispose ladder moves to its own module (src/dispose.ts) with the
deterministic FakeChild tier tests restored alongside it.
Master's #660 replaced dsh-subagent-subprocess with the dsh-subprocess
capability seam (ctx.subprocess + scrubbedParentEnv, tree-scoped teardown)
and moved subagent-acp onto it. Convergence for this branch's packages:
- The shared out-of-process provider vocabulary this branch had grown in
the deleted library (NO_START_CAPABILITIES, assertPositiveFinite, cwd
resolution, settleRunResult, subprocessRunHandle) moves into the subagent
seam package as out-of-process.ts — it enforces subagent-seam contracts,
not process mechanics, and both out-of-process backends now import it
from there (subagent-acp keeps master's shape otherwise).
- subagent-sdk spawns THROUGH the SDK client (the subprocess README's
documented exception for SDK-managed transports) and now applies the
seam's scrubbedParentEnv() + explicit-env merge in place of the deleted
buildChildEnv.
- sdk-client inlines the EOF→SIGTERM→SIGKILL ladder as private helpers (it
runs outside any harness context, so it cannot ride ctx.subprocess).
- The child harness fixture gains the now-required dsh-subprocess-local
entry for bash-local; the fixture cordis.yml keeps exercising the
shipped provider default.
Resolutions: regenerate the conflicted generated docs (cordis services
catalog, event-producer-consumer, module-graph); take master's
packages/README pair and re-insert the telemetry row on both sides;
re-record the README and session-doc translation pairs.
Consolidates the personal dsh-tui customizations (module split into
components/session/extension, prompt template + running-glyph indicator,
copyable transcript, tool-card headers, timing placement, XML tool output,
status/footer rework) and ports upstream's model reasoning-effort selector
(Shift+Tab effort cycling, effort-aware /model, footer, and /status) onto
the personal module layout.
Keep restore-only and self-hosted stores outside pnpm/action-setup's replaceable install directory, resolving the configured path for every paired cache. Pin Yarn 4 for the generated-project e2e so runner images without Corepack do not fall back to Yarn Classic, and update the bilingual decision record.