Restoration Runbook
This document provides consistent recovery procedures for all persistent subsystems within the Neo.mjs AI substrate, leveraging the atomic bundles generated by the Daily Snapshot Pipeline (npm run ai:backup).
Bundle Layout Overview
Backups are stored in .neo-ai-data/backups/backup-<timestamp>/ and contain the following atomic directories:
kb/: Knowledge Base ChromaDB as JSONLmc/: Memory Core memories and summaries as JSONLgraph/: Memory Core SQLite graph as JSONLconcepts/: Concept Ontology JSONLtrajectories/: RLAIF training trajectories JSONL
Prerequisites
Before initiating any restoration, ensure that all AI MCP servers and daemon processes are stopped (terminate any running npm run ai:server processes).
Atomic-Bundle Restore CLI (ai:restore)
npm run ai:restore -- <bundle-path> (entrypoint ai/scripts/maintenance/restore.mjs) inverts the Daily Snapshot Pipeline: it reads a bundle, validates structure + JSONL parseability + topology compatibility, then restores each subsystem (KB, MC memories/summaries, graph, concepts, RLAIF trajectories) through the canonical Zod-validated SDK boundary. Use this for a full-bundle restore; the per-subsystem procedures below are the manual fallback when you need to recover a single store.
Flags
| Flag | Effect |
|---|---|
--mode merge (default) |
Idempotent. Embedded substrates upsert (graph SQLite uses INSERT OR IGNORE); flat substrates (concepts/, trajectories.jsonl) skip-if-target-exists, preserving operator additions. No --force required. |
--mode replace |
Gated + destructive. Each embedded subsystem fires assertDestructiveTargetAllowed() before truncating + restoring; refuses if any target is non-empty without --force. |
--force |
Required when --mode replace AND any target is populated (acknowledges overwrite). Also overrides the flat-file skip-if-non-empty rule under --mode merge. |
--force-topology-mismatch |
Bypasses the topology-compatibility refusal when restoring a legacy federated-topology bundle into a unified deployment (collection IDs may diverge across topologies). |
Pre-flight validation
Before any write touches a service, the orchestrator validates the bundle: the required subdirectories exist, each .jsonl is parseable (a torn write / corruption fails fast), and bundle-meta.json (when present) parses and passes the topology check. A torn or partial bundle aborts with a clear error and zero side effects on the live substrate.
Production-target safeguard
When --mode replace targets canonical .neo-ai-data/ paths, the destructive-operation guard requires both an environment opt-in AND an explicit confirmation token before the truncate fires; otherwise the restore aborts. Disposable targets (under tmp/, the OS temp dir, or :memory: SQLite) bypass the requirement so tests can exercise replace mode safely.
Programmatic use
The orchestrator is also exported as runRestore({ bundleRoot, mode, force, forceTopologyMismatch }) from ai/scripts/maintenance/restore.mjs, for embedding in higher-level recovery substrate (e.g. the daily snapshot pipeline or cold-restore harnesses). It returns per-subsystem result blocks, the parsed bundle-meta.json (or null for legacy bundles), and the topology-check verdict. Companion exports validateBundle(...) and checkTopology(...) expose the pre-flight checks for callers that want to gate before restoring.
Restoration Procedures
1. Knowledge Base (KB)
The Knowledge Base is the neo-knowledge-base collection inside the one flat unified ChromaDB store (ADR 0017) — a cache, not a store. Recover it by deterministic rebuild from source, at collection scope. Never delete the store folder: chroma/unified also holds the irreplaceable Memory Core collections.
Procedure:
- Re-synchronize the KB from the source files (deterministic rebuild — clears and repopulates only the
neo-knowledge-basecollection):npm run ai:sync-kb
(Note: Direct JSONL import of thekb/bundle is deferred to #10871. Do notrm -rfthechroma/unifiedfolder — it is shared with Memory Core; KB recovery is collection-scoped, handled by the rebuild above.)
2. Memory Core (MC) - Memories & Summaries
Memory Core memories and session summaries live as the neo-agent-memory and neo-agent-sessions collections inside the same flat unified ChromaDB store (ADR 0017). Unlike the KB, MC is the irreplaceable store — recover it from the backup bundle, at collection scope via the SDK. The pre-unification chroma/memory-core/ folder is retired.
Procedure:
- Re-import the MC JSONL from the backup bundle via the SDK (
mode: 'replace'clears and repopulates the MC collections at collection scope — no folder deletion):node -e "import('./ai/services.mjs').then(s => s.default.memory.manageDatabaseBackup({action: 'import', file: '.neo-ai-data/backups/backup-<timestamp>/mc/memory-backup-<timestamp>.jsonl', mode: 'replace'}))"(Note: For full-bundle restores, prefer the Atomic-Bundle Restore CLI above. The direct SDK import remains the manual per-subsystem fallback. Do notrm -rfthechroma/unifiedfolder — it is shared with the Knowledge Base; MC restore is collection-scoped via the SDK above.)
3. Chroma FTS5 Integrity Repair
The unified Chroma store is a shared physical SQLite database. pragma quick_check
or pragma integrity_check can report malformed inverted index for FTS5 table main.embedding_fulltext_search while vector collections still answer normal
queries. Treat this as a shared-store integrity incident: diagnose copy-first,
stop all writers before touching the live database, and do not use Chroma defrag
as a substitute for SQLite FTS5 repair.
Procedure:
- Run the on-demand diagnostic and keep its copied SQLite snapshot:
npm run ai:check-chroma-integrity -- --json --keep-snapshot
- Validate the repair on the reported snapshot path, not on the live file:
sqlite3 <snapshot>/chroma.sqlite3 "insert into embedding_fulltext_search(embedding_fulltext_search) values('rebuild'); pragma quick_check; pragma integrity_check;"Continue only if both pragmas returnok. If the copied snapshot remains malformed, stop here and recover from backup or rebuild the affected collection rather than experimenting on the live store. - Stop every process that can reach the Chroma daemon or the unified Chroma directory: Orchestrator, Memory Core, Knowledge Base, wake daemons, harness MCP server instances, and the Chroma daemon itself.
- Capture a fresh backup bundle and a physical copy of the unified Chroma
directory:
npm run ai:backup cp -R .neo-ai-data/chroma/unified .neo-ai-data/chroma/unified.pre-fts5-rebuild-<timestamp> - Rebuild the live FTS5 table only after the writers are stopped and the
backups exist:
sqlite3 .neo-ai-data/chroma/unified/chroma.sqlite3 "insert into embedding_fulltext_search(embedding_fulltext_search) values('rebuild'); pragma quick_check; pragma integrity_check;" - Restart Chroma and the dependent AI services, then verify both SQLite
integrity and API-level reachability:
npm run ai:check-chroma-integrity -- --json node ai/scripts/maintenance/probeCollectionQueryHealth.mjs
Boundaries:
ai/scripts/maintenance/defragChromaDB.mjscompacts collection storage; it is not an FTS5 integrity repair tool.- KB rebuild (
npm run ai:sync-kb) repairs the cache collection, not the shared SQLite full-text index. - MC backup import restores collection rows, but it is not required when the copied FTS5 rebuild validates cleanly.
- API embedding-export failures such as
Error finding idare a separate Chroma read-path issue (see §7 for its repair); do not conflate them with FTS5 index repair.
4. Memory Core - Native Edge Graph
The Memory Core Edge Graph is persisted in SQLite.
Procedure:
- Move the corrupted SQLite database aside:
mv .neo-ai-data/sqlite/memory-core-graph.sqlite .neo-ai-data/sqlite/memory-core-graph.sqlite.bak - Re-import the Graph JSONL from the backup bundle via the SDK:
node -e "import('./ai/services.mjs').then(s => s.default.memory.manageDatabaseBackup({action: 'import', file: '.neo-ai-data/backups/backup-<timestamp>/graph/graph-backup-<timestamp>.jsonl', mode: 'replace'}))"
5. Concept Ontology
The Concept Ontology consists of nodes and edges defined in JSONL.
Procedure:
- Clear the active concepts directory:
rm -rf .neo-ai-data/concepts/* - Copy the concepts from the backup bundle:
cp -r .neo-ai-data/backups/backup-<timestamp>/concepts/* .neo-ai-data/concepts/
6. RLAIF Trajectories
The RLAIF trajectories capture interaction feedback and metadata for offline RL alignment.
Procedure:
- Replace the active trajectories file:
cp .neo-ai-data/backups/backup-<timestamp>/trajectories/trajectories.jsonl .neo-ai-data/datasets/rlaif/trajectories.jsonl
7. Memory Core Stored-Embedding Export Repair
A distinct failure from §3 (FTS5) and from a full restore (§2): npm run ai:check-chroma-integrity reports get embedding by id: Error finding id (stored-embedding export fails) for neo-agent-memory / neo-agent-sessions / neo-native-graph while the query canary stays healthy — Chroma metadata rows are present but many ids are missing from the persisted HNSW vector index (#13496 / #13467). Backup/export silently skips the missing-vector ids, so it is not a safe substitute. The repair re-embeds the missing vectors into a shadow collection and promotes copy-first (defragChromaDB.mjs repairMemoryCoreCollectionsViaFullEnumeration, #13635), behind the --allow-memory-core opt-in (default fails closed).
Procedure:
- Confirm the diagnosis (read-only — a healthy query canary plus a failing exportability sample is the signature):
node ai/scripts/maintenance/probeCollectionQueryHealth.mjs npm run ai:check-chroma-integrity -- --exportability-sample-size 2 --json
- Quiesce every competing writer — but leave the Chroma server running (both the backup and the repair use its API; unlike §3's file-level FTS5 repair, this does not stop Chroma). Stop the Orchestrator, Memory Core, Knowledge Base, wake daemons, and harness MCP server instances (the
npm run ai:serverprocesses) so nothing else mutates the collections during the repair. - With the writers stopped (store quiescent, Chroma still up), capture the canonical SDK backup and a coarse physical rollback copy:
npm run ai:backup cp -R .neo-ai-data/chroma/unified .neo-ai-data/chroma/unified.pre-mc-repair-<timestamp>(ai:backupreads through Chroma's API, so Chroma must be up; the physical copy is a coarse rollback taken while no writers are active.defragChromaDBadditionally takes its own private pre-promote snapshot.) - Run the repair with Chroma running and no competing writers (the repair is the exclusive collection writer; opt-in — it shadow-extracts intact vectors, re-embeds the missing ids, validates the shadow collection, then promotes copy-first):
node ai/scripts/maintenance/defragChromaDB.mjs --target memory-core --allow-memory-core
A clean run clears its repair state-marker; a partial run rewrites an explicitmemory-core-repair-abortedmarker and exits non-zero — investigate before re-running. - Verify export and query health are both green, then restart the AI services (see Verification below):
npm run ai:check-chroma-integrity -- --json node ai/scripts/maintenance/probeCollectionQueryHealth.mjs
Boundaries:
- This repairs stored-embedding export (re-embed missing vectors); it is not the §3 FTS5 SQLite repair and not the §2 backup-restore — use those for their respective failures.
- The repair is gated behind
--allow-memory-core(default fails closed) and promotes copy-first; it never deletes/recreates the live collection in place. - The heavier exportability probe (
get embedding by id) stays on-demand (the maintenance scripts above); the routine bounded healthcheck remains query-path only.
Verification
After restoration, restart the subsystem and verify health.
npm run ai:server
Validate recovery by ensuring healthcheck.backup.lastSuccessful surfaces your desired timestamp and no subsystem connection errors are reported.
Important: Never perform destructive restores on shared multi-tenant databases without confirming coordinate isolation. Only restore into intended environments.