The cache now owns its storage root (dshHomePath('projections')), so the
mount moves from the web-app overlay to base with that root declared, next
to the other base session layers. web-app inherits it; its overlay mount is
removed.
Store each session's projection_cache.json under the cache's own root tree
(<root>/<session-id>/projection_cache.json, wired to dshHomePath('projections')
in the base bundle) instead of beside the session log via
sessionPersistence.locate(). The cache owns its directory layout, keys
directories by the code-generated session id, and never consults the
persistence layer; the service now injects only sessionProjections and
sessions.
Drop the coldSnapshot method and its readFrom-tail fold ladder: every cold
consumer refolds from the log itself, so the cache only serves the listing
read (cachedSnapshot, one async file read per session) and the write side.
Fail-soft durability, per-path write serialization, in-flight drain, and
atomic 0600 writes are unchanged; the chain cleanup now observes its own
rejection so a failed write cannot surface as an unhandled error.
dsh-session-persistence leaves peer/dev dependencies and the tsconfig
reference; dsh-atomic-write moves to peerDependencies. Config gains a
required root.
An assistant turn that answered without calling a tool serialized no
reasoning_content, so a gateway re-encoding the conversation for another
vendor had no chain of thought to hash and lost that turn's upstream
thinking signature.
- Write through @deepseek-ai/dsh-atomic-write with { mode: 0o600,
dirMode: 0o700 } instead of exporting a second atomic-write primitive
from dsh-storage-json; the session tree stays owner-only like the jsonl
backend's own directories.
- Serialize atomic replacements per cache path so an older cut can never
overwrite a newer one; track in-flight writes and drain them on
disposal so a late flush cannot land after teardown.
- Detect the absent per-session directory before the checkpoint cut and
durability flush: sqlite-style backends no-op the write entirely.
- Cold-read write-back path and identity both come from the stored log
header (tail.meta), so a stale caller header cannot mint an orphan
cache file.
- Add no-path coverage (write no-op, cachedSnapshot undefined, cold
fallback to the full-log rung) and a concurrent-write ordering test;
the package now holds 100% statement/branch/function/line coverage.
- Sync README.md/zh (inject list, coldSnapshot signature, per-session
file read wording), package description, the Agent Note alternatives,
and the superseded proposed/implemented notes (EN/ZH); add the
concurrent-checkpoint Known Limitation.
history and session.list baselines built through restore/snapshot without
wireOnly leaked host-only unit state onto the wire; pass { wireOnly: true }
on the detached history fold, the attached history snapshot, and the
attached listing snapshot.
Replace the single global session_projcache domain with a per-session
cache file inside the session's own persistence directory, resolved
through sessionPersistence.locate(meta) — the persistence backend owns
the session-directory layout, the cache service keeps every checkpoint
and cold-read responsibility.
- cachedSnapshot(meta) becomes async (one file read per session);
coldSnapshot takes the session header so it can locate the file, with
the stored log header remaining the identity witness.
- Backends without a per-session directory (sqlite) disable the durable
cache: writes no-op and cold reads fall to the full-log rung. An
obsolete global cache is never read — derived data refolds on first
cold read (no migration).
- writeAtomic is exported from dsh-storage-json as the shared atomic
whole-file replace primitive; api-proxy listing and subagent cold
reads await the now-async cachedSnapshot.
- READMEs and a new Agent Note document the per-session medium.
The product model has no cross-process writer exclusion (the coordinator
serializes per-session operations in-process; the README documents one
live writer per session), so the wx-created .lock sibling only guarded
byte-level races while adding two failure modes: a crash leaves a stale
lock that permanently wedges that log's appends/repairs/replacements, and
a post-commit lock cleanup failure makes a committed append look failed,
so the retained write-behind batch retries into duplicate seqs.
Remove withLogLock and keep replaceStored's revision compare-and-swap at
the commit boundary (recheck immediately before the atomic rename).
buildStepIndex rejected the whole decoder at initialization whenever any
registered step could not reach the current version, so one retired old
upgrader blocked every session, including later versions whose path to
the current version is complete. planSteps already refuses a specific
stored version when a needed step is missing; initialization now checks
only step legality and duplicates.
Markdown tables were always rendered at natural width, so anything wider
than the 748px message column could only be read through horizontal
scrolling. Following the deepsuite chat TableWrapper treatment: tables
under four columns (and any table inside a blockquote) now fill the column
and wrap cell text down to the cells' minimum readable width, while
four-or-more-column tables keep their natural width behind the wrapper's
horizontal scroll and carry the stable md-table-wide hook. The chat
transcript widens hooked tables past the message column with a pure-CSS
container-query breakout (100cqw against ChatView's scroll container
standing in for deepsuite's JS-measured list width), keeping the table
content aligned with the message column and clamping to neutral when the
transcript is narrower than the column.
- user-approval: name the absent agent loop when the turnBoundary
projection is not registered (instead of the misleading 'outside an
open turn') and declare dsh-agent-loop as a peer dependency; add a
regression test for the missing-unit composition (v4p).
- session-title / time-context / agent-instructions: converge the
self-registered-key-absent handling to a loud throw with the same
v8-ignored comment style (v5 suggestion).
The exact-event-set assertion called Object.keys on a Record<string, unknown>'s
on field without narrowing, failing typecheck (TS2769). Guard with isRecord
before asserting the full event sets.
Update in place the implemented notes whose mechanisms this stack changed:
the subagent list identity note drops the deleted
SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE error contract and hostile-unit
probe for the required-injection seam; the durable-subagent-catalog note
drops the same stale error-code reference; the sandbox pair replaces the
effectiveSandboxMode/effectiveApprovalPolicy trio with the sandboxMode
projection unit on the required registry; the plan collaboration note
replaces foldPlanMode with the plan projection unit. EN/ZH in lock-step;
i18n pairing re-recorded.
Address the 7 remaining review threads on PR #2768:
- spec: assert the exact full event sets for both workflows (ci-master =
[push, workflow_dispatch], ci.yml = [pull_request]) instead of only the
negated checks, so losing/adding a wrong event fails.
- 2026-07-21 note line 29: 'The ci-master and Sandbox workflows keep their
cross-platform references on master pushes' (en+zh) — ci.yml is PR-only.
- failover-runbook line 19 (en+zh): the push-reachable classification now names
ci-master.yml for the drills/wine cache and ci.yml for the PR jobs, dropping
the stale pull-request-gated phrasing.
i18n hashes re-recorded for both notes; spec 13/13, pairs consistent,
note-format 575.
Per the imccyu review, the pre-existing goal, permissions, and plan units
go back to registering through the ctx.inject(['sessionProjections'], …)
child form instead of the required-inject direct register; goal returns to
zero diff (its service never reads projections). The mandatory-seam rework
for these three sites moves to a follow-up PR. New projection units keep
the required-inject direct-register form.
Regenerated catalog and subsystem docs follow the reverted service
signatures.
Address the second review pass on PR #2768:
- Restore the serial-linux-selfhosted drill comment in ci-master.yml (it was
lost in the split boundary, leaving serial-linux-selfhosted as the only
comment-less job); adapted to the current-state fact that this workflow never
listens to pull_request while keeping the no-cache-steps reason, the
DSH_CI_FAILOVER_LINUX switch guidance, and push-triggered semantics.
- Update 2026-07-29-pnpm-setup-runner-isolation (en/zh + i18n): the regression
test and setup steps now span ci.yml and ci-master.yml.
- failover-runbook line 19: the push-reachable classification now names
ci-master.yml for the drills/wine cache and ci.yml for the pull-request jobs,
dropping the stale pull-request-gated phrasing.
- all-checks-passed comment in ci.yml notes that needs cannot reach across
workflow files (ci-master job are deliberately outside this PR verdict).
Verification: ci-workflow.spec.ts 13/13, both workflows YAML-parse with correct
concurrency, 3 translation pairs consistent, note-format 575.
Reconcile the base's persist removal and later master content with the
migration branch: keep 2742's wireOnly read options, the host-inclusive
snapshot/viewCheckpoint/restore defaults, and the drive's late-event
replay (applyToCell) that bare-session reads rely on.
- The pnpm-caching note placed node-compat in ci-master.yml, but it stays a PR
job in ci.yml; only the two runner benchmarks moved. Correct en/zh line 15 and
re-record the pair.
- The pnpm/action-setup destination test only iterated ci.yml; extend it to also
cover ci-master.yml so its five pnpm setups stay regress-tested.
Per review on PR #2768:
- Restore a concurrency block in ci.yml (cancel-in-progress: true) so a fresh
PR push cancels the superseded run; GitHub has no default PR auto-cancel,
so removing it would stack a second full 9-job run on every push.
Update the ci-workflow.spec assertion accordingly.
- Fix comment misplacements from the split boundary: ci.yml's all-checks-passed
now carries its own branch-protection comment (the drill comment that was
orphaned above it is gone), and ci-master.yml no longer ends with an orphaned
all-checks-passed comment.
- Restore the DeepSeek e2e workflow describe-block in ci-workflow.spec.ts that
the rewrite had silently deleted (e2e.yml is unchanged).
- serial-windows comment no longer claims non-blocking-for-PR/absent-from-needs
since ci-master never listens to pull_request.
- DSH_TELEMETRY_DISABLED safe-use comment restored in ci-master.yml env; split
rationale (mi gray segments in PR check) documented in .github/AGENTS.md.
Verification: ci-workflow.spec.ts 13/13, both workflows YAML-parse with correct
concurrency, git diff --check clean.
Split the single ci.yml into two workflows so the PR check panel stops listing
master-only obs jobs that skip (gray) and block the aggregate green-check:
- ci.yml is now pull_request-only, holding only the 9 PR jobs (node-24,
node-24-coverage, node-24-consumers, node-compat, python-sdk, python-runtime,
windows, windows-native, all-checks-passed). It drops the workflow-level
concurrency block since master carve-outs no longer apply.
- ci-master.yml (new) carries the six master/dispatch jobs (wine-apt-cache,
serial-linux-selfhosted, serial-macos, serial-windows, and both runner
benchmarks) with the push-exempt cancel-in-progress block and suite input.
It does not listen to pull_request, so its jobs never appear in PR checks.
ci.yml keeps the wine apt cache seed/restore (repo-scoped by key), so moving the
producer to ci-master.yml does not break the PR windows job's restore.
Update ci-workflow.spec.ts to assert the two-workflow split: ci-master owns the
drills/benchmarks and the push-exemption concurrency, ci.yml is PR-only without
concurrency. Update the serial-reference, portable-required, failover-runbook,
and pnpm-caching notes (en/zh + i18n) and .github/AGENTS.md to reflect ci-master.
Verification: scripts/ci-workflow.spec.ts 12/12, both workflows YAML-parse,
verify-translation-pairing consistent, verify-agent-note-format passes.
The note was committed on 2026-08-19 but filed under 2026-08-07 while
building on the 08-19 state-and-client-views note; rename the triplet to
align the filename with the actual date and update the inbound
architecture links.
Adapts this branch to the base's removal of the persist opt-in: the
registry folds and checkpoints every registered unit, the host-only
subagent identity drops its explicit persist: true, and the
state-and-client-views note records the uniform rule. The cordis API
catalog and session-projection subsystem signatures are regenerated.
The titleInput unit no longer retains the full eligible message history
in bounded reverse-linked chunks. It folds only {first, last, count} —
the values scheduling and fallback reads need — and the full eligible
prefix for one provider generation is scanned from the session log at
execution time. The projection state is O(1) per session instead of
growing with every user message.
The README description updates accordingly and drops the inaccurate
"latest request route" claim; the projection test now asserts the
bounded aggregate and its checkpoint row.