101 lines
4.0 KiB
Markdown
101 lines
4.0 KiB
Markdown
# 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
|