Context
During the #13999 / #14020 Memory Core repair lane, the corruption (~10k missing neo-agent-memory vectors) had gone undetected until a canonical backup failed on 2026-06-20 and surfaced on 2026-06-25. Nobody could say when it started — the live integrity check (ai:check-chroma-integrity) reports the store's current coverage but carries no history.
This session demonstrated that the daily backups encode that history. Walking .neo-ai-data/backups/backup-*/bundle-meta.json (each records a fail-loud, #13583, MC/KB export count = rows that had retrievable vectors at that date) produced a dated timeline that pinned the vector loss to the 2026-06-18 07:42Z → 06-20 05:23Z window (MC exported cleanly at 18,835 memories on 06-18; the 06-20 backup is the first failed export) and falsified two standing theories (the May incident — clean through 06-18; the embedding caps — landed 06-23/24, after the window). That walk was manual; this ticket makes it a repeatable, read-only diagnostic.
Live latest-open sweep: checked latest 25 open issues at 2026-06-25T23:03Z — no equivalent diagnostic ticket; closest are #13999 (the incident this dates), #14010 (test-bleed), #13860 (immune-system diagnostics epic), none duplicate. A2A in-flight sweep: checked latest 12 messages — no competing [lane-claim] for this diagnostic.
The Problem
A self-healing organism (ADR-0025/0026) currently has no way to date when a data-integrity regression began. The live coverage audit answers "is it corrupt now?"; it cannot answer "since when?" — which is exactly the question every post-incident forensic needs (to bisect the cause to a commit/process window). The backups hold the answer but require hand-walking JSON manifests, which is slow, error-prone, and not reproducible across agents/sessions.
The Architectural Reality
- Backups live at
.neo-ai-data/backups/backup-<ISO>/, each with a bundle-meta.json recording per-subsystem export results (e.g. subsystems.mc.message: "Export complete. Exported 18835 memories, 1023 summaries...") plus per-collection JSONL exports under mc/, kb/, graph/. A failed export leaves the bundle incomplete (no bundle-meta.json) — itself a signal.
ai/scripts/maintenance/checkChromaIntegrity.mjs is the live-store sibling (current coverage: metadata-count vs vector-index-count). This diagnostic is its historical/forensic complement — same coverage concept, applied across the backup series instead of the live store.
- New
.mjs placement: ai/scripts/maintenance/ (sibling-pattern match to checkChromaIntegrity.mjs / defragChromaDB.mjs — structural-pre-flight fast-path; no novel directory).
The Fix
Add a read-only ai/scripts/maintenance/backupCorruptionTimeline.mjs (name TBD) that:
- Enumerates
.neo-ai-data/backups/backup-*/ sorted by timestamp.
- For each backup: reads
bundle-meta.json export counts per collection; flags backups with no manifest as export-failed; (optionally, behind a flag) computes per-collection coverage from the JSONL export rows for finer granularity.
- Emits a dated timeline (date → per-collection export-count / coverage / status) in both human and
--json form, and highlights the onset transition (last-clean → first-degraded) as the corruption window.
- Never contacts the live Chroma daemon or mutates anything — backups only.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
backupCorruptionTimeline.mjs CLI |
backup bundle-meta.json shape + checkChromaIntegrity coverage concept |
Read-only; emit dated per-collection coverage/export-status timeline + onset window |
Skip/flag malformed or manifest-less backups without aborting the whole walk |
Script JSDoc + CLI --help |
Unit test over a fixture backup tree (clean → degraded transition detected) |
Decision Record impact
none directly. Aligned-with ADR-0025 (deployment diagnostics) as the forensic/historical complement to a future live data-integrity detect-signal; it does not amend the ADR.
Acceptance Criteria
Out of Scope
- Live-store mutation or repair (that is #14020 / #13999).
- The live, scheduled data-integrity detect-signal + alerting daemon (a separate Epic #13860 sub — this is the historical/forensic one-shot complement, not the live monitor).
- The data-recovery actuator / backup-merge-vs-from-scratch strategy (separate, Ideation-bound).
- Identifying the exact culprit commit in the pinned window (a #13999 code-audit forensic step).
Avoided Traps
- Do not query the live Chroma daemon — the whole point is a safe, offline, historical view that adds zero load to a possibly-degraded live store.
- Do not conflate this with the live detect-signal. They are complementary: this dates past corruption from backups; the detect-signal alerts on current drift in real time.
Related
- #13999 — the incident this diagnostic dates (and where the empirical 06-18→20 pin is recorded).
- #14010 — Chroma test-collection bleed (sibling test-isolation regression).
- #13860 — deployment immune-system diagnostics epic (the live detect-signal sibling lives here).
- ADR-0025 / ADR-0026 — the immune-system detect/act pair this complements.
Origin Session ID: 58acd7a6-778e-4fe6-b85a-4569802ac57b
Handoff Retrieval Hints: query_raw_memories("backup bundle-meta vector coverage timeline corruption onset 06-18"); query_summaries("backup-corruption timeline diagnostic Chroma vector-coverage drift"); exact anchors: .neo-ai-data/backups/backup-*/bundle-meta.json, ai/scripts/maintenance/checkChromaIntegrity.mjs.
Authored-by: @neo-opus-vega (Vega, Claude Opus 4.8)
Context
During the #13999 / #14020 Memory Core repair lane, the corruption (~10k missing
neo-agent-memoryvectors) had gone undetected until a canonical backup failed on 2026-06-20 and surfaced on 2026-06-25. Nobody could say when it started — the live integrity check (ai:check-chroma-integrity) reports the store's current coverage but carries no history.This session demonstrated that the daily backups encode that history. Walking
.neo-ai-data/backups/backup-*/bundle-meta.json(each records a fail-loud, #13583, MC/KB export count = rows that had retrievable vectors at that date) produced a dated timeline that pinned the vector loss to the 2026-06-18 07:42Z → 06-20 05:23Z window (MC exported cleanly at 18,835 memories on 06-18; the 06-20 backup is the first failed export) and falsified two standing theories (the May incident — clean through 06-18; the embedding caps — landed 06-23/24, after the window). That walk was manual; this ticket makes it a repeatable, read-only diagnostic.Live latest-open sweep: checked latest 25 open issues at 2026-06-25T23:03Z — no equivalent diagnostic ticket; closest are #13999 (the incident this dates), #14010 (test-bleed), #13860 (immune-system diagnostics epic), none duplicate. A2A in-flight sweep: checked latest 12 messages — no competing
[lane-claim]for this diagnostic.The Problem
A self-healing organism (ADR-0025/0026) currently has no way to date when a data-integrity regression began. The live coverage audit answers "is it corrupt now?"; it cannot answer "since when?" — which is exactly the question every post-incident forensic needs (to bisect the cause to a commit/process window). The backups hold the answer but require hand-walking JSON manifests, which is slow, error-prone, and not reproducible across agents/sessions.
The Architectural Reality
.neo-ai-data/backups/backup-<ISO>/, each with abundle-meta.jsonrecording per-subsystem export results (e.g.subsystems.mc.message: "Export complete. Exported 18835 memories, 1023 summaries...") plus per-collection JSONL exports undermc/,kb/,graph/. A failed export leaves the bundle incomplete (nobundle-meta.json) — itself a signal.ai/scripts/maintenance/checkChromaIntegrity.mjsis the live-store sibling (current coverage: metadata-count vs vector-index-count). This diagnostic is its historical/forensic complement — same coverage concept, applied across the backup series instead of the live store..mjsplacement:ai/scripts/maintenance/(sibling-pattern match tocheckChromaIntegrity.mjs/defragChromaDB.mjs— structural-pre-flight fast-path; no novel directory).The Fix
Add a read-only
ai/scripts/maintenance/backupCorruptionTimeline.mjs(name TBD) that:.neo-ai-data/backups/backup-*/sorted by timestamp.bundle-meta.jsonexport counts per collection; flags backups with no manifest asexport-failed; (optionally, behind a flag) computes per-collection coverage from the JSONL export rows for finer granularity.--jsonform, and highlights the onset transition (last-clean → first-degraded) as the corruption window.Contract Ledger Matrix
backupCorruptionTimeline.mjsCLIbundle-meta.jsonshape +checkChromaIntegritycoverage concept--helpDecision Record impact
nonedirectly. Aligned-with ADR-0025 (deployment diagnostics) as the forensic/historical complement to a future live data-integrity detect-signal; it does not amend the ADR.Acceptance Criteria
.neo-ai-data/backups/backup-*/dirs sorted chronologically, read-only.bundle-meta.json) and marks manifest-less backups asexport-failed.--jsonoutput for tooling + a human-readable table.test/playwright/unit/ai/scripts/maintenance/).:8000, no persist-dir writes).Out of Scope
Avoided Traps
Related
Origin Session ID: 58acd7a6-778e-4fe6-b85a-4569802ac57b
Handoff Retrieval Hints:
query_raw_memories("backup bundle-meta vector coverage timeline corruption onset 06-18");query_summaries("backup-corruption timeline diagnostic Chroma vector-coverage drift"); exact anchors:.neo-ai-data/backups/backup-*/bundle-meta.json,ai/scripts/maintenance/checkChromaIntegrity.mjs.Authored-by: @neo-opus-vega (Vega, Claude Opus 4.8)