Task — engineering-spec@1

Memory hardening - schema single-source, INTEROP.md, walker + doctor

doneTASK-MEMORY-303
module memory · class product · priority p1 · created 2026-07-23 · shipped null
depends on none · blocks TASK-IMP-140

TASK-MEMORY-303: Memory hardening - schema single-source, INTEROP.md, walker + doctor wiring

Summary

Five memory-protocol promises are broken in ways that compound: the schema has forked (the root + vendored copies lack the StoreAcl definitions §14.4.7 makes normative, while the package-data copy has them) and the drift test that should have caught it points at a nonexistent path and silently skips; the §14.1-mandated INTEROP.md does not exist; using the protocol's own §7.7 dream or §18 session features would create dreams//sessions/ dirs the doctor's canonical-layout allowlist rejects, and the walker invariants AGENTS.md names for those features are undeclared; the live store is already FROZEN_RECOVERABLE from stray adrs/ + impl-plans/ dirs; and §18.7's extra.session_id stamping is unwired in the canonical writer. This task restores schema single-sourcing with a real drift test, authors INTEROP.md, teaches the walker the protocol's own features, repairs the live store under operator gate, wires cyberos doctor into the machine-gate floor where memory is installed, and stamps extra.session_id.

Problem

Audit finding H10 plus the memory-row medium items, all verified first-hand 2026-07-23:

  1. Schema fork, guarded by a skipping test. modules/memory/cyberos/data/memory.schema.json (the copy the Python package loads) carries StoreAcl/StoreAclEntry/StoreAclMode; modules/memory/memory.schema.json (the copy build.sh:161 vendors into every payload) does not. Consumers therefore validate STORE.yaml ACLs against a schema that has never heard of them. test_schema_drift.py exists to catch exactly this and catches nothing: _COMMITTED points at modules/memory/docs/memory.schema.json, which does not exist, so every test in the module pytest.skips - a green that means "did not look".
  2. INTEROP.md is a dangling MUST. AGENTS.md §14.1 binds non-ledger consumers to a <= 6,000-char INTEROP.md; no such file exists anywhere. Every cross-agent consumer is currently bound to a document nobody can read.
  3. The walker rejects the protocol's own features. _CANONICAL_TOP_LEVEL_DIRS omits sessions (§18.2 bodies) and dreams (§7.7.4 artefacts): a store that exercises dreaming or transcripts goes doctor-RED. The invariants AGENTS.md explicitly names for these features (dream-applied-row-has-provenance §7.7.2, store-yaml-acl-valid §14.4.7, the §18.8 session lifecycle set) are absent from memory.invariants.yaml.
  4. The live BRAIN is frozen. .cyberos/memory/store/ carries stray top-level adrs/ and impl-plans/ (measured: two dirs today; TASK-MEMORY-261's earlier context listed five, three since cleaned). layout-root-canonical fails, so §12 forces protocol-compliant agents to refuse writes - the audit trail is silently absent while the repo's own doctrine (AGENT-ENTRY.md #4) tells agents to record decisions into it.
  5. §18.7 unwired. The canonical writer never stamps extra.session_id, so even with an active session, put/move/delete rows carry no session linkage and the §18.8 walker checks would have nothing to verify.

Proposed Solution

Schema: regenerate modules/memory/memory.schema.json from the generator (tools/cyberos_generate_schema.py, whose Struct source already emits StoreAcl per cyberos/core/store_acl.py); fix test_schema_drift.py's _COMMITTED to the real root-copy path and add modules/memory/tests/test_schema_single_source.py asserting (a) the generator's --check passes against the root copy, (b) root and package-data copies are byte-identical, (c) build.sh vendors from the root copy path (source-grep), and (d) the drift test can never silently skip - a missing committed schema is a FAIL, not a skip. INTEROP.md: author modules/memory/INTEROP.md (<= 6,000 chars) covering the §14.1 consumer subset - read paths, the no-write rule for audit//HEAD/.lock, canonical-writer routing, STORE.yaml ACL honor-for-writes (§14.4.6), sync_class export semantics (§14.3) - and vendor it via build.sh next to the schema. Walker: add sessions and dreams to _CANONICAL_TOP_LEVEL_DIRS; declare and implement the missing invariants (dream-applied-row-has-provenance, store-yaml-acl-valid, session-lifecycle covering §18.8's four checks) in memory.invariants.yaml + invariants.py. Store repair: an operator-gated move of the stray dirs' contents into their canonical homes per the ADR disposition TASK-MEMORY-261 specifies (decision first if 261 is unshipped), leaving the audit chain intact, ending with cyberos doctor OK on the live store. Doctor gate: run-gates.sh gains a doctor gate that runs when .cyberos/memory/store/ exists AND the memory CLI is importable (python3 -m cyberos doctor), SKIPs (with provenance line) when either is absent, and fails RED on doctor FAIL. Session stamping: writer.py stamps extra.session_id on every put/move/delete row while sessions/.active names an active session (§18.7), covered by test_session_id_stamping.py.

Alternatives Considered

Success Metrics

Scope

In scope: schema regeneration + drift-test fix + single-source test, INTEROP.md authoring + vendoring, walker allowlist + three new invariant families, the operator-gated live-store repair, the presence-gated doctor gate in run-gates.sh, §18.7 stamping in writer.py, CHANGELOG.

Out of scope / Non-Goals

Dependencies

Blocks TASK-IMP-140 (its §13 BRAIN-recording step needs the store un-frozen by this task's repair). Related: TASK-MEMORY-261 (draft - specifies the layout ADR + single-sourcing this task's repair executes under; runs first inside this task's HITL flow if still unshipped), TASK-MEMORY-302 (draft bug - the applier root cause), TASK-MEMORY-117 (done - shipped the StoreAcl enforcement the stale schema copies contradict), TASK-MEMORY-119 (done - shipped the transcript ledger whose §18.7 stamping this task completes).

AI Authorship Disclosure

1. Description (normative)

2. Acceptance criteria

3. Edge cases

Audit

§1 — Verdict summary

Eight §1 clauses, eight ACs, seven edge cases including a security-class row. The largest task in the batch; the audit pressure fell on overlap discipline with the two existing draft memory tasks (261, 302), the direction of schema unification, ordering hazards (doctor gate vs frozen store), and keeping the BRAIN-write prohibition intact through authoring. One measured-truth correction (two stray dirs, not five) and one plan-shape correction (copy census) are recorded in source_decisions.

§2 — Findings (all resolved)

ISS-001 — scope collision with TASK-MEMORY-261 and TASK-MEMORY-302

The plan's T6 bullet includes "repair live store layout"; TASK-MEMORY-261 (draft) already specifies the canonical-set single-sourcing + five-dir ADR, and TASK-MEMORY-302 (draft bug) owns the applier root cause. A spec that re-claimed either would put two tasks on one deliverable. Resolved: Non-Goals carves both out explicitly; clause 1.5 executes the repair under 261's ADR procedure (running its decision step first if unshipped, inside this task's HITL flow); the relationship is expressed via related_tasks + prose because adding depends_on would require editing an existing task's frontmatter for reciprocity, which is outside this authoring wave's write scope.

ISS-002 — unification direction was assertable both ways

"Unify the copies" permits rolling StoreAcl BACK out of the package data. Resolved: source_decisions + Alternatives pin package-data-forward with the normative justification (§14.4.7 + shipped TASK-MEMORY-117 enforcement); clause 1.1 requires the regenerated root copy to carry the three ACL definitions, so the direction is testable, not just stated.

ISS-003 — the drift test could keep its skip-on-missing behavior after the path fix

Fixing _COMMITTED while leaving pytest.skip on absence rebuilds today's failure mode one deletion away. Resolved: clause 1.2 makes missing-schema a FAIL; AC 2 asserts it by monkeypatching the path to a missing file.

ISS-004 — live-store ACs were unverifiable without violating the no-BRAIN-writes rule

AC 5's first draft demonstrated the repair ON the live store, but authoring-time and CI-time verification must not mutate .cyberos/memory/store/. Resolved: AC 5 splits verification - the mechanical demonstration runs on a fixture store cloned from the live layout; the live-store result (doctor OK, verify OK, rows present, operator approval recorded) is verified at the human review gate, which is where an operator-gated mutation belongs.

ISS-005 — doctor-gate ordering hazard (the gate would RED the repo before the repair lands)

Wiring 1.6 before 1.5 completes makes this repo's own gates RED mid-task. Resolved: the edge case makes repair-before-gate-wiring normative implementation order and instructs the review gate to verify it was honored.

ISS-006 — sessions allowlist addition vs the /sessions/ sandbox fragment

_SANDBOX_FRAGMENTS contains /sessions/; a careless reader (or implementer) could conflate the store-path check with the child-dir check and either break sandbox rejection or re-reject the new dir. Resolved: edge case pins the non-interference semantics and requires the walker test to cover it.

ISS-007 — new invariants needed constructed-violation coverage, not just clean-store passes

Declaring dream-applied-row-has-provenance, store-yaml-acl-valid, and session-lifecycle with only happy-path tests would repeat the declared-but-unverified pattern this task exists to close. Resolved: clause 1.4 requires each to fail on a constructed violating fixture; AC 4 enumerates the three fixtures.

ISS-008 — CLI-name collision in the doctor probe

Gating on a binary named cyberos reintroduces the TASK-IMP-130 PATH-collision class (an unrelated tool answering the name). Resolved: edge case requires probing module importability (python3 -c "import cyberos.core"), not binary presence; clause 1.6's wiring is python3 -m cyberos doctor accordingly.

§3 — TRACE-006 semantic sufficiency (per clause)

ClauseVerb demandCited test assertsVerdict
1.1 MUST regenerate; copies byte-identical + ACL-bearing--check green + three-way hash equality + definition keys presentAC 1: asserts all threesufficient
1.2 MUST point at real path; missing MUST fail not skipruns with 0 skips + forced-missing FAILsAC 2: asserts both halvessufficient after revision (ISS-003)
1.3 MUST exist <= 6000 chars, five anchors, vendoredlength bound + content anchors + payload presenceAC 3: asserts all threesufficient
1.4 MUST allow sessions/dreams + three invariants pass/fail correctlylayout pass + three violating fixtures each fail their own invariantAC 4: asserts positive and all three negativessufficient after revision (ISS-007)
1.5 MUST relocate via ledger moves under operator approval; doctor OK + chain intactfixture relocation preserves chain; live-store outcome verified at HITL reviewAC 5: split verification per ISS-004sufficient
1.6 MUST run doctor when present, RED on FAIL, SKIP with provenance when absentthree-state behavior on scratch reposAC 6: asserts all three statessufficient
1.7 MUST stamp iff active sessionpresence with active + absence without, across all three opsAC 7: asserts both directionssufficient
1.8 MUST record in CHANGELOGfour groups named in top entryAC 8: asserts the four substringssufficient

§4 — Resolution

Eight findings - two scope-discipline, one measured-truth, five material contract/ordering gaps - all resolved in the audited revision. Score = 10/10.

Status transition draft -> ready_to_implement is authorised by this verdict per STATUS-REFERENCE.md §1.1. The two human-acceptance gates in /ship-tasks are unchanged - this audit clears the spec-correctness gate only. The live-store repair inside this task additionally carries its own explicit operator approval per §1.5.


End of TASK-MEMORY-303 audit.