-
Notifications
You must be signed in to change notification settings - Fork 0
agentm memory index
title: memory index — design status: launched kind: design scope: feature area: agentm/memory-index governs: [] parent: agentm-hld.md seeded: 2026-06-26 approved: 2026-06-26
The memory index (V6-11) is the SQLite metadata table that lets recall filter by
kind,project,tag, andstatusin one query alongside the vector search — replacing the grep-over-frontmatter pass. It lifts the queued V6 plan into a tracked design, reconciled to the built index and extended with the fingerprint column + two new memory kinds; parent agentm HLD, sibling to memory system.
Recall today runs a vector search plus a grep over each entry's frontmatter — there is no structured way to ask for "the security entries in project: sherwood, updated this month." V6-11 adds that: a SQLite metadata table beside the existing vector index, joined by row id, so a recall is one SQL WHERE over kind / project / tag / status AND a vector MATCH in the same query. It is the substrate the diagnostics recall ladder and token-audit's session-cost capture both wait on.
The contract for this lived only in the queued V6 plan in the vault, which no tracked design referenced — so four dependents read as buildable while resting on an unwritten substrate. This design lifts that plan into the governed tree, reconciles it to what the index actually is today (a four-column table awaiting additive columns), and folds in the three extensions the AG track added: the fingerprint column, and the session-cost and failure-incident memory kinds.
Markdown stays the source of truth. The index is a derived, device-local cache the recall loop reads; this slice widens its metadata so a query can filter and rank in one pass.
Markdown is the source of truth; the drain walk hydrates the two row-id-joined SQLite tables; hybrid recall is one SQL WHERE + a vector MATCH. Grep stays the graceful fallback; the fingerprint column feeds diagnostics.
The index is real and device-local (the memory system's local-index tier, never synced):
-
vec_index.py— aentriesvec0 virtual table (FLOAT[1024]) plus a metadata tableentry_meta(rowid, path, updated_at, indexed_at)— four columns, joined to the vectors byrowid. It has a JSONL embedding queue, an async drain, drift detection (the source ofindexed_at), and a dimension-mismatchrebuild_index. -
recall.py— the recall loop: tokenize → vector search → drift-check-and-drop → grep over slug, tags, title, and the first 500 chars → merge (sim × 0.85 + keyword × 0.05) → top-5. It graceful-degrades to grep-only when sqlite-vec or embeddings are absent.
V6-11 extends this; it does not replace it.
Add columns to the built entry_meta by additive, idempotent migration (an ALTER TABLE ADD COLUMN guarded by a column check, mirroring the existing _migrate_pre_v37 step):
| Column | Source |
|---|---|
kind · status · slug · project · created
|
the entry's frontmatter |
tags (JSON array) |
frontmatter |
group_name |
frontmatter group (renamed — group is a SQL keyword) |
fingerprint |
the AG-added join key for the diagnostics recall ladder |
Row id stays the join to entries. The rebuild_index contract extends to repopulate these columns from frontmatter on a full-sync, alongside the embeddings; its CREATE TABLE entry_meta must carry the new columns too, or a rebuild would drop them. The design declares which columns are indexed (kind, project, status carry a CREATE INDEX for the filter path). The built table starts at four columns, so the migration is purely additive.
The index is device-local and never synced, so a fresh machine has the synced markdown but no index — it builds one from the vault. The markdown is the source of truth; the index is a derived cache rebuilt locally from it, the same way on a new device, after a wipe, or after a corruption.
The cold build reuses the two built operations. full_sync --rebuild walks the vault (private/ · projects/ · incubator/) and enqueues every entry; drain then embeds each one and populates both tables — the entries vectors and the entry_meta row, with the V6-11 metadata columns read from each entry's frontmatter in the same pass. The build is resumable: the enqueue is a plain JSONL append and the drain is cursor-backed, so an interrupted first sync continues where it left off and a re-run is idempotent.
The two halves split by dependency: enqueueing needs no sqlite-vec or embedding model (it is an append), so the walk runs anywhere; only the drain needs the local embedding model. Embedding every entry is a one-time per-device cost, and recall graceful-degrades to grep-only until the build finishes — so a fresh agent keeps recalling (without the vector half) while the drain catches up. The fresh index is created with the extended schema, so a cold build produces the full metadata table directly. A dedicated reindex convenience subcommand is anticipated; full_sync --rebuild is that path today.
Add a --filter path to recall.py query: an expression like tag=security AND project=sherwood compiles to one SQL WHERE over the metadata, joined with the vector MATCH, in a single query. This replaces the grep-over-frontmatter pass for the filtered case, while grep stays the graceful fallback when sqlite-vec is absent. The merge weighting is unchanged (sim × 0.85 + keyword × 0.05 — the live constants; the 0.7 / 0.3 in the comments is stale). Operator surfaces follow: /memory search --tag --project, /memory list --kind --updated-since. Out of scope (from the source plan): materializing entry bodies into SQLite, and LLM-extracted metadata.
kind is already a free-form kebab-case taxonomy (a path segment + a display header), so the two new kinds are reserved values of the existing kind taxonomy:
-
session-cost— a recall-eligible kind that token-audit's session-cost capture writes and the dreaming session-cost review reads. -
failure-incident— the kind the diagnostics recall ladder writes. Its write carries a mandatory privacy scrub, because failure context is untrusted and PII-bearing — a persistence-boundary guard the write cannot skip.
The fingerprint is a real column — a join/lookup key the diagnostics ladder matches on.
The memory system reserves a DerivedMaintenance extension point for exactly this kind of derived index (it points the reader at the storage seam, though the seam text does not yet carry the reservation), and nothing implements it yet — the built vec_index.py is a parallel working implementation. This slice builds on vec_index.py (its drain, drift detection, and rebuild already work) and names DerivedMaintenance as the contract it eventually satisfies, with a re-audit trigger so the reconciliation stays on record.
-
vs memory system — the memory system owns the recall loop, the capture path, the tiers, the local-index tier, and the
DerivedMaintenanceextension point; this design is the indexed-recall slice it marks designed-for (the V6 milestone), specifying the metadata table + the hybrid query, and a futureDerivedMaintenanceimplementer. -
vs storage seam — the seam owns the read/write verbs, the
Locatorguards, and the T1/T2/T3 ownership tiers; this index is a derived device-local cache the recall path reads, beneath that contract. - vs the rest of V6 — V6-11 is the metadata table only. The hybrid-rank engine (V6-3, RRF over BM25 + vector), the typed-entity graph (V6-2), and chunking (V6-10) stay deferred; this slice is pulled forward on its own because its dependents need structured filtering; ranking stays deferred.
-
extends the built index —
vec_index.py(the table + drain + rebuild) andrecall.py(the query path). -
rides the memory system's local-index tier (device-local, never synced); a future
DerivedMaintenanceimplementer (the extension point memory-system reserves). -
feeds diagnostics (the
failure-incidentkind + thefingerprintrecall ladder) and token-audit (thesession-costkind). -
composes privacy — the mandatory scrub on a
failure-incidentwrite. - Points up at the agentm HLD §Memory; the recall loop + tiers are the memory system.
-
This is a lift. The canonical V6-11 contract lived only in the queued V6 plan in the vault (
_harness/ROADMAP-AgentMemoryV6.md). This design migrates it into the governed tree, reconciled to current truth. Re-read the live source at fold in case it drifted since it was queued — the schema here is reconciled from the queued spec plus the AG-added axes. -
Additive migration only. The built
entry_metais four columns; the migration isALTER TABLE ADD COLUMN(idempotent, guarded), back-filled on the nextfull-sync. A clean-slateCREATE TABLEwould mis-describe current truth and risk a drop-and-recreate. -
At lift (docs): add the
agentm/memory-indexarea to the area-taxonomy; have memory system point to this slice as the V6-11 specification; downgrade the dependents' status language from "designed" to "blocked on V6-11" where they don't already say so (the runner already does).
- Buildability illusion. Until this lands, the diagnostics first-slice deterministic layer, token-audit session-cost capture, the runner health-check report, and the dreaming session-cost review read as independently buildable while resting on an unspecified substrate. The lift closes the spec gap and re-points those dependents.
-
The parallel-implementation tension.
vec_index.pysidesteps the seam's reservedDerivedMaintenance. Building onvec_index.pyis the pragmatic call, but the seam's never-implemented contract rots silently if this is left unsaid — hence the named re-audit trigger. -
failure-incidentis a leak surface. Its write carries untrusted, PII-bearing context; the mandatory privacy scrub is a persistence-boundary guard the write cannot skip. - Source-of-truth drift. The lifted spec was queued 2026-05-27; re-read the live vault plan before folding in case it changed.
-
Re-audit triggers: re-read the live V6 plan at fold; reconcile onto
DerivedMaintenanceif/when the seam path is implemented; flip[PENDING-IMPL]as the table + hybrid query land; revisit when V6-3 (RRF) arrives to consume the table.
- A standalone Wave-B slice. Ship the metadata table + SQL filtering + the fingerprint on their own to unblock diagnostics and token-audit; the source plan's pairing with V6-3 (RRF) is deferred — V6-3 consumes this table later.
-
Additive migration only. Extend the built four-column
entry_metaby guardedALTER TABLE ADD COLUMN, mirroring_migrate_pre_v37; the rebuild path'sCREATE TABLEcarries the new columns too. -
group_namefor the frontmattergroupkey. The baregroupis a SQL keyword. -
The two new kinds are reserved
kindvalues.session-costandfailure-incidentride the existing free-formkind; thefingerprintis the one real new column. -
Build on
vec_index.pynow;DerivedMaintenanceis the eventual contract (re-audit trigger recorded). - Markdown stays the source of truth. The index is a derived, device-local cache; nothing here moves authority into SQLite.
-
Lifted from: the queued V6 plan (
_harness/ROADMAP-AgentMemoryV6.md— the V6-11 metadata-table spec) -
Extends (built):
harness/skills/memory/scripts/vec_index.py(the four-columnentry_meta+entries+ drain + rebuild) ·recall.py(the five-step loop + thesim × 0.85 + keyword × 0.05merge) -
Composes: memory system (the recall loop + tiers + the local-index tier + the
DerivedMaintenancereservation) · storage seam (the read/write verbs + ownership tiers) · privacy (the failure-incident scrub) -
Consumers: diagnostics (the
failure-incident+fingerprintrecall ladder) · token-audit (thesession-costkind) · the runner health-check report - Up: agentm HLD §Memory · memory system (the V6 indexed-recall trajectory)
Newest first. Collapses to one ≤2-paragraph entry at finalization; git holds the granular history.
-
2026-06-26 — lifted the V6-11 metadata-table plan into the governed tree; approved + launched. The last Bucket-A substrate piece, and a lift: the canonical V6-11 contract lived only in the queued V6 plan in the vault, which no tracked design referenced, so its dependents rested on an unwritten substrate. This design migrates it, reconciled to the built index (the four-column
entry_metainvec_index.py, extended additively), and folds in the three AG-added axes: thefingerprintcolumn, and thesession-cost+failure-incidentreservedkindvalues. It specifies the extended metadata table, the hybrid--filterrecall path (one SQLWHERE+ a vectorMATCH, replacing the grep-over-frontmatter pass, grep kept as the graceful fallback), the build-from-source path (a fresh machine bootstraps the device-local index from the synced markdown viafull_sync --rebuild→drain, resumable, recall grep-degrades until built), and the mandatory privacy scrub on afailure-incidentwrite. Locked: a standalone Wave-B slice (V6-3 RRF deferred); additive idempotent migration;group_nameover the SQL keyword; the two kinds are reservedkindvalues; build onvec_index.pywithDerivedMaintenancenamed as the eventual contract; markdown stays the source of truth. Re-audit: re-read the live V6 plan at fold; reconcile ontoDerivedMaintenancewhen the seam path is implemented; flip[PENDING-IMPL]as the table + hybrid query land.