Review findings applied across the group:
- storage hub: stale disposers no longer remove a successor registration;
the package now default-exports the Storage service class per the
service-package export shape.
- json backend: failed publishes roll back the authoritative memory state
(a rejected write can no longer resurface via get() or ride the next
publish); close() drains in-flight writes and blocks in-flight opens;
double-open rejects as a plain caller error instead of malformed-medium.
- sqlite backend: loadAll builds records on a null prototype (__proto__
keys round-trip instead of polluting), user_version is stamped only
after the schema is fully created, and corrupt record JSON rejects as
malformed-medium instead of a bare SyntaxError.
- domain form: writes persist before mutating authoritative memory or
emitting; DomainChanged is a put/deleted discriminated union.
- workspace: attach/detach idempotence decided on the write chain (stale
snapshots no longer short-circuit), create() requires a directory, and
startup fails loud on duplicate stored paths.
Eleven regression tests pin the fixed behaviors.
Proposed-lifecycle Agent Note (Chinese draft, English body to follow
after review) recording the storage hub / domain form / workspace
design, the deletion semantics deferred to future work, alternatives
considered, and the session-backend migration reuse audit. The dev plan
under missions/ carries the engineering breakdown.
ctx.workspace registry owns WorkspaceId-branded records: realpath-
normalized unique paths (create rejects collisions; resolveByPath shares
the normalization), ordered sessionIds as the single source of ownership
truth, attachSession gated on the session header cwd matching the
workspace path (double-booking structurally impossible), dead session
ids filtered on projection and pruned on the next mutate, status()
reporting missing directories. No delete surface this phase — deletion
ships together with the session-side primitives as future work.
ctx.storage.domain opens declared domains: zod value schemas parsed at
the durable boundary, one write chain per domain (update(fn) is the only
read-modify-write), domain/changed emitted per record after durability
(new snapshot + operation, no old value, per repo event convention).
Domain-to-backend routing is configuration (default backend + per-domain
overrides); unknown names and missing facets fail loud. Ships the
MemoryStorageBackend test helper and a runtime invariant asserting every
change event matches the in-memory state.
node:sqlite DatabaseSync with the session-persistence-sqlite open
sequence (0o700 dir, exclusive 0o600 create, foreign_keys, configurable
journal mode, user_version stamp-or-reject). STRICT tables throughout:
units/unit_globals meta tables plus one document-per-row table per
declared unit table, keeping per-key durable updates precise.
Atomic whole-file replacement (same-dir temp + fsync + rename + parent
fsync); the in-memory unit state is authoritative and the file is always
the current net state, pretty-printed. Missing files open as empty units
and materialize on first write; foreign or unparsable files reject with
malformed-medium, stored-version drift with version-mismatch.
ctx.storage is a pure registration hub: multiple named backends stay
mounted side by side, data forms (domain first) mount via the
merge-extensible StorageForms map. src/backend.ts is the normative
KV-facet contract; tests/contract.ts is the shared conformance suite
every backend runs. Backends expose data-shape facets (kv now, an
append-log facet reserved for the future session-backend migration).
Keep inherited child prompt markers bounded by the normal silence fallback. Stage web-plugin rescans atomically and retain missing watch state until a successful rebuild.
The fake terminal retains earlier autocomplete frames, so the directory follow-up assertion could pass before the scoped lookup completed. Wait for the quoted file mention to be applied before submitting.