# M2.8 — M2 composition gate | Field | Value | |---|---| | Phase | M2 — Projections | | Size | M — 1–3 days | | Status | ⬜ Not started | | Flags | gate | | Spec | inlined below | | Blocks | all of M2 | ## Goal Prove the authority model: the log is sufficient, and both projections are genuinely derived. ## Facts (inlined — no spec read needed) The claim under test — poimen's own principle, applied here: > Nothing derived is authoritative. If it cannot be dropped and rebuilt > byte-identically, it has hidden inputs and that is a bug. The gate is destructive by design: it **deletes** the vault and truncates the database, rebuilds from the log alone, and diffs. Anything that survives only because it was already there is a hidden input, and this is the only test that finds it. ```sh rm -rf vault/poimen psql -c "delete from memory_node where project = 'poimen'" mem rebuild --from-log --project poimen git -C vault diff --exit-code # empty diff is the only pass ``` `git diff --exit-code` on a tracked vault is the assertion. It compares against what was committed, so it also catches a projector change that was not intended. Run it twice: once from empty (sufficiency) and once on top of itself (idempotence). Both must produce the same bytes. ## Steps 1. `verify/m2.8.sh` performing the destructive rebuild above. 2. Assert the vault diff is empty and the database node counts match the log. 3. Run `mem verify` and assert zero violations. 4. Second rebuild without clearing; assert still empty diff and unchanged row count. 5. Assert no controller model calls (embeddings are allowed and expected). 6. Commit `expected/m2.8.txt` with the count summary; diff against it. ## Acceptance - Vault rebuilt from nothing is byte-identical to the committed vault. - Database node/edge counts match the log's records exactly. - `mem verify` reports zero violations. - Second rebuild changes nothing. ## Verify **Harness:** disposable database, git-tracked vault, real log. Long-running; a nightly or on-demand job. **Integration test** — `verify/m2.8.sh`, output diffed against `expected/m2.8.txt`: 1. `a1_vault_from_empty` — delete vault, rebuild, `git diff --exit-code` empty. 2. `a2_db_from_empty` — truncate, rebuild, counts per level equal the log's. 3. `a3_verify_clean` — `mem verify` exits 0. 4. `a4_rebuild_idempotent` — rebuild again, diff still empty, row count unchanged. 5. `a5_no_controller_calls` — assert zero calls to the chat route during rebuild (count via the record dir from M1.1, or a proxy). 6. `a6_projection_independence` — `--vault-only` then `--db-only` produces the same end state as a combined rebuild. 7. `a7_log_alone_suffices` — move the log to a fresh checkout with no vault and no database, rebuild, diff against the committed vault. The strongest form of the claim. **Command:** `bash verify/m2.8.sh | diff - expected/m2.8.txt` **False pass:** - Running the gate without deleting the vault first. A projector that only writes changed files produces an empty diff trivially, and the hidden input survives. - Diffing an untracked vault. `git diff` on untracked files reports nothing, so the assertion passes vacuously. The vault must be committed, or the script must compare against a committed golden tree explicitly. - Allowing controller calls "because it is easier". Rebuild then produces new memory text each run and the gate can never pass — at which point the usual fix is to weaken the gate. ## Traps - Treating a non-empty diff as a projector bug by default. It is equally likely to be a *hash* bug: if `sha256` includes a timestamp (M0.2), every rebuild produces new nodes and the vault churns. Check identity before blaming rendering. - Running against production data with the destructive script and no backup. The log is the record; if that is intact, everything is recoverable — which is exactly why the log must be tracked in git before this gate is first run. --- Background: [DESIGN.md](../DESIGN.md) — Authority model, Verification