Files
poimen-memory/tasks/M2.7-verify-edges.md
T

91 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# M2.7 — `mem verify` — edge closure
| Field | Value |
|---|---|
| Phase | M2 — Projections |
| Size | S — under 1 day |
| Status | ⬜ Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | M2.6 |
## Goal
Assert the provenance graph is well-formed, so a memory with no traceable
evidence is caught rather than believed.
## Facts (inlined — no spec read needed)
Invariants, checked against the log and the database independently:
1. Every L1 `memory` record has at least one L0 parent. A memory with no evidence
came from somewhere the record does not explain.
2. Every `parents` sha resolves to a node that exists.
3. Every `evidence` sha appears as a parent of at least one memory. Evidence that
nothing cites was written for no reason.
4. `evidence` count equals `gate.update == true` count (also M1.6 a2, re-checked
here across the whole project rather than one run).
5. No edge is self-referential; no cycles.
6. Levels are consistent: an L1 node's parents are L0; an L2 node's are L1.
Invariant 6 is the one that catches a tier confusion, and it is the one most
likely to break when M3.1 adds the L2 pass — an L2 node accidentally parented to
L0 evidence would still look plausible in the vault.
`mem verify` is a read-only diagnostic. It never repairs; repair is `mem rebuild`.
## Steps
1. `mem verify --project P [--db] [--log]`, defaulting to both.
2. Check invariants 16, collecting **all** violations rather than failing on the
first — one run should tell you everything wrong.
3. Report per violation: invariant, level, sha, run id, and the log line number.
4. Exit non-zero on any violation.
5. `--format json` for machine consumption.
## Acceptance
- A clean project reports zero violations, exit 0.
- Each invariant has a fixture that violates it and is detected.
- All violations are reported in one run, not just the first.
## Verify
**Harness:** hand-built log fixtures, one per invariant, plus a clean one.
**Integration test**`tests/it_verify.rs`:
1. `a1_clean_passes` — the good fixture, zero violations, exit 0.
2. `a2_orphan_memory` — L1 with empty `parents`; detected as invariant 1.
3. `a3_dangling_parent` — parent sha not present; invariant 2.
4. `a4_uncited_evidence` — evidence nothing references; invariant 3.
5. `a5_evidence_gate_mismatch` — 3 update-gates but 2 evidence records;
invariant 4.
6. `a6_cycle` — A parents B, B parents A; invariant 5.
7. `a7_level_mismatch` — L2 node parented directly to an L0 node; invariant 6.
8. `a8_reports_all` — a fixture violating three invariants at once; assert all
three appear in one run's output.
9. `a9_db_and_log_agree` — introduce a violation in the database only; assert
`--db` catches it and `--log` does not, proving the two checks are independent.
**Command:** `cargo test -p mem-cli verify`
**False pass:**
- Checking the database only. The log is authoritative; a log-level violation
that rebuild happens to smooth over is still a bug in the writer, and
assertion 9 is what keeps the two checks honest.
- Failing fast on the first violation. It passes every single-violation fixture
and makes assertion 8 impossible, which in practice means three rebuild cycles
to find three problems.
## Traps
- Treating invariant 3 as fatal. Uncited evidence is a real smell, but a
legitimate case exists: the final turn updates memory and the run is cut short
before the memory record flushes. Report it, and let the gate decide severity.
- Skipping invariant 6 because L2 does not exist yet. It is cheap now and it is
precisely what M3.1 will break.
---
Background: [DESIGN.md](../DESIGN.md) — tier model, Verification