Architecture
The parts of memidex, how knowledge flows from a finished run to the next one, and which steps use a model.
memidex has one rule at its core: records are the source of truth, and everything else is derived from them. Records are text files you commit. The search index, the run counters and the curation queue are rebuilt or recreated on each machine.
Three layers
| Layer | Where | Committed | Role |
|---|---|---|---|
| Records and policies | agent-qa-knowledge/memory/ and policies/ | Yes | What agent-qa believes, with the evidence for it, and the rules you wrote. |
| Search index | agent-qa-knowledge/memory/index/ | No | A SQLite database built from the records: full-text search, anchors, a graph of relations, vectors and run outcomes. |
| Local state | .agent-qa/ and agent-qa-knowledge/memory/queue/ | No | Run counters, short IDs, the curation queue and lock files. |
Deleting the index is always safe: agent-qa knowledge rebuild-index builds it again from the records.
From a run to the next run
test run ──► evidence for every step ──► verdict (final)
│
▼
analysis (root causes)
│
▼
candidates ──► curator ──► checks ──► record, as candidate
│
promotion gate (local runs only)
│
▼
record, as active
│
index: keywords, anchors, graph
│
▼
next run: one brief for each test- The run records evidence. Every step keeps what it saw, what it did, how it was checked, and which records it was served.
- The verdict is frozen. Nothing that happens afterwards can change it or the exit code.
- Analysis gives each failing test a root cause, such as an app bug, a test bug, a flaky failure or an environment problem.
- Candidates are projected from the evidence by code, not by a model: a fact for each verified step, a caution for each flaky or environmental failure, a procedure for each fully passing test.
- The curator turns each candidate into one change: add a record, confirm one, supersede one, deprecate one, raise an unknown, or do nothing.
- Checks catch changes that cite missing records, contradict their own anchors or try to rewrite too much at once, and turn them into proposals instead.
- The promotion gate decides whether a candidate becomes active. On CI it never does.
- The index is updated, and the next run builds its briefs from it.
How knowledge is learned and How knowledge reaches a run cover each half in detail.
The parts
| Part | Package | What it does |
|---|---|---|
| Contracts | @vostride/agent-qa-schema | Record format, trust arithmetic, the promotion gate and search levels. |
| Knowledge store | @vostride/agent-qa-knowledge-local | Records, the curation queue, the curator, the index, search, serving, quarantine, rollback and purge. |
| Runner | @vostride/agent-qa-runner | Reads briefs, offers memory tools to the model and replays procedures. It never writes knowledge. |
| Functions | @vostride/agent-qa-agent | The knowledge.* and runs.* functions and the post-run jobs. |
| Surfaces | agent-qa, @vostride/agent-qa-mcp, the dashboard | The same functions as commands, MCP tools and views. |
One writer at a time
Only the knowledge store writes records, and only while it holds the writer lease for the knowledge folder. The lease is three local files: .lease (a heartbeat), .epoch (a fencing number, so a stalled writer cannot write after losing the lease) and .writer-authority.sqlite. All three are ignored by git.
While a daemon holds the lease, maintenance commands such as knowledge rebuild-index refuse with writer lease is held by pid …, and corrections are handed to the daemon.
Writes are crash-safe. A record is assembled in a staging folder and moved into place in one step. Each piece of curation is journaled before it runs, and a finished model answer is saved before it is applied, so a crash never calls the model twice for the same work.
Same fact, same record
A record's ID comes from its kind, its main anchor and its scope, never from the model's wording. For example:
fact-el-button-place-order-36-00-dd75ab371443d7b6When a later run sees the same element, the curator finds the existing record and confirms it instead of adding a near-duplicate. Two runs that learn the same thing produce the same ID.
What uses a model
| Uses a model | Pure code |
|---|---|
| Root-cause analysis, only for failures the rules cannot classify | Projecting candidates from evidence |
| Writing each change and its summary | IDs, checks and supersession |
| Turning a passing test into a procedure | Trust, status and validity |
| Optional replay checks with an eval corpus | The promotion gate |
| Ranking and serving the brief, and serving policies | |
| Quarantine, rollback and purge |
memidex works without a model. The curator then falls back to fixed templates, and records it wrote that way are rewritten in better words the next time a model handles the same candidate. Procedures need a model.
Search levels
Each run records how much of the search stack was available. agent-qa doctor and the dashboard report it.
| Level | When | What search uses |
|---|---|---|
memidex | The index opens and the embedding model is ready. | Keywords, anchors and semantic vectors. |
lexical-only | The embedding model is not available. This is the default in v0.2. | Keywords and anchors. |
static-floor | The native model runtime cannot load, so a small bundled model is used. | Keywords, anchors and coarser vectors. |
bundle-scan | The index has not been built yet, is locked or is damaged. | Records read directly. |
The level describes the machine, not one search. Two things are worth knowing:
- The per-test brief never uses vectors. It is ranked by trust and anchor overlap, so the level changes curation and memory tool searches, not the brief.
- Serving needs the index. When it is missing, the brief carries your policies but no learned records until the index is built again, which happens after the next run.
The full embedding model is Xenova/bge-small-en-v1.5, a 384-dimension model that runs locally on the CPU. agent-qa v0.2 never downloads it. It is used only when it is already in the model cache, ~/.cache/agent-qa/models by default or AGENT_QA_MODEL_CACHE_DIR. The bundled fallback is minishlab/potion-retrieval-32M.
Run counters
memidex counts runs per product in .agent-qa/build-ordinals.json. Each record remembers the count at which it was created and, once replaced, the count at which it stopped being true. A run is served a record only when its own count falls inside that window.
--build-tag <tag>, AGENT_QA_BUILD_TAG or use.buildTag name a build of your app. agent-qa maps each tag to the first count that used it, so agent-qa knowledge diff <tag> --product <product> can show what changed since that build.
Safety
- Secrets are masked before anything is stored. Values in your secrets file are replaced with
[secret:<name>]in records, evidence and summaries. Nothing else is masked, so keep personal data out of test steps or put it in the secrets file. - App text is data. What the app showed is handed to the curator inside an untrusted-data block, and the brief tells the model that records are hints to weigh, not instructions to follow.
- Screenshots stay local. Records point to screenshots and page snapshots in an
artifacts/folder that git ignores.
Working memory
During a run, the model can keep short notes for itself, such as a generated order number or how far it got. These live for one run only, are masked like everything else, and are never turned into records.