Move the shared module-reload policy into dsh-base by inserting its HMR row disabled, then remove the redundant disabled overrides from Web, headless, SDK, and ACP. No shipped profile enables server module reload; live profile patch watching continues through the launcher-owned config-only fallback, and browser client HMR remains a separate mechanism.
A later profile layer can opt into source-module reload explicitly with disabled: false while retaining the base root configuration. Composition tests cover every shipped mode and the explicit enable path, and the bundle references plus launcher Agent Notes document the resulting ownership and safety rationale.
Two private experimental packages run the whole harness tree inside one
dedicated Web Worker. dsh-experimental-webworker-runtime owns the in-memory
VFS (BigInt stats with per-path identity and strictly increasing mtimes),
the CommonJS wrapper loader over a lazily-evaluated builtin table whose
shims typecheck against Node's own module types, the postMessage tunnel
speaking plain HTTP, the AsyncLocalStorage runtime, and the worker
assembly. dsh-experimental-webworker-packer lowers every module body at
pack time against the shared wrapper contract, sweeps the profile closure
by static reachability, and writes a deterministically gzip-compressed tar
the worker inflates through the browser's native DecompressionStream while
it downloads.
Review asked why the launcher special-cases one plugin's row. It no
longer does: the four shipped compositions move into the package
(presets/, in files), dsh-agent-presets resolves its own shipped root
and prepends it before configured roots (includeShippedRoot, default
true, opt-out for bare-machinery embedders), and the per-composition
derived patch, its spec, and the dump layer are deleted — profile-boot
and dump-config return to plain layer stacking. The always-load
guarantee now rides the schema default instead of patch ordering, so a
whole-config replacement keeps the shipped set and the squash, reload
freeze, and dump divergence stop being possible.
Gate globs, the web scaffold, and both preset browser lanes drop their
hand-fed shipped roots; the roster e2e keeps asserting configured roots
beside the shipped four against the built lib.
Fixes#2863.
The boot-time agent-presets overlay replaced the composed roots with the
shipped root alone, so roots configured in a profile's cordis.patch.yml
vanished from the roster (externally reported in
deepseek-ai/deepseek-harness#3636). The overlay also froze the row's
boot-time config above every live reload and never reached the config
dump, which therefore showed roots the boot dropped.
Derive the roster patch from the current layers instead: prepend the
shipped root (system trust, wins duplicate ids) to configured roots,
share one builder across boot, live user-layer reloads, and
--dump-config, and fail loud on a roots value the launcher cannot
statically rewrite.
Fixes#2863.
Replace per-plugin tapIndex regex edits with pure-data IndexInjection rows
collected fresh per render over one webserver/index-inject event. One table,
two renderers: the served form renders rows into index.html; a static worker
form ships the same rows over its boot payload. tapIndex survives as the
raw-HTML escape hatch, applied after row rendering; client-modules and
ui-theme move to the event, and the manifest global renders as
globalThis["__DSH_BOOT__"].
The client boot chain gains the seams a pre-injected transport needs: the
module loader takes loadBundle from the transport global by default, HTTP
prefetch stands down when a transport owns bundle bytes, the web-app bundle
can decline frontend serving, the gateway client installs a namespace's
whole method group inside its fiber apply so a parked dependent never
observes the service without its methods, and the dynamic-code precheck
gates through new Function so hosts without a real node:vm keep the
define-time parse gate.
Cache README (EN/ZH): the medium is the session_projcache domain in
per-record layout (one version-stamped document per session under the
json backend root), reads are synchronous from the domain's in-memory
tables, and the storage stack rides in base. storage-json README
documents both layouts and their contracts; web-app README notes that
storage and the projection cache live in the shared base. Regenerated:
session-projection subsystem catalog (sync cachedSnapshot, domain
medium), config-catalog (Config.root gone), module-graph (cache now
depends on storage-domain, not session-persistence), cli composition
(storage rows in base), and the projection-cache Agent Note — which now
records the file-root revision and its revert as rejected alternatives.
The storage hub, json backend, and domain form are general infrastructure,
and the projection cache is a session-layer service that depends on them —
both belong in the shared base, not in the web overlay. Base now provides
storage / storage-json / storage-domain / session-projection-cache; the
web-app overlay keeps its surface consumers (workspace, message-feedback),
which inherit storageDomain from base (a child layer sees parent services).
This reverts the storage stack's historical web-app-only placement and the
file-root design's base mount of the cache.
The cache now opens its domain through ctx.storageDomain, which the
web-app overlay provides (storage-json + storage-domain, backend json).
A parent layer cannot see a child layer's services, so the base-layer
mount from the file-root design is reverted: the cache mount moves back
to web-app next to its storage dependencies, and Config.root is gone.
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.