Knowledge Workflow
Build the vault, then repair retrieval in measured rounds until it stops improving.
Standing Mandates
- The loop is the deliverable, not the edit. Every round is: measure → diagnose → change one thing → measure against the saved run. A round with two changes cannot be attributed or reverted, so it does not count as a round.
- Split before the first repair. Record
--split holdoutonce, up front. A holdout measured after repairs proves nothing, and tuning against it destroys the only unbiased number in the loop. - Fix the largest lever first, not the easiest edit. The order below is measured, not intuited. Vocabulary work feels productive and has repeatedly moved nothing while the bottleneck sat in the provider or the ranker.
- A stop condition is part of the run. Say which one applies before the round starts. A loop with no declared exit becomes editing that looks like progress.
- Never cite the round count as progress. Report the holdout number, or say it did not move.
Workflow Overview
[A] Build once
catalog → graph → RAG → index
↓
[B] Split + baseline ← record holdout, save the run
↓
┌──> [C] Diagnose (repair_targets, kind breakdown)
│ ↓
│ [D] One change (lever order below)
│ ↓
│ [E] Re-measure vs baseline → keep or revert
│ ↓
└─── stop condition met? ──no──┘
│ yes
↓
[F] Report + hand off to knowledge-query
Phase A — Build (once)
| Step | Skill | Skip if |
|---|---|---|
| A1 Catalog | knowledge:knowledge-base-builder |
_knowledge/catalog.jsonl exists and covers the sources |
| A2 Graph | knowledge:knowledge-graph-builder |
No relationship questions in scope |
| A3 RAG chunks | knowledge:rag-corpus-builder |
Notes are short enough to retrieve whole |
| A4 Index | knowledge:sqlite-index-builder |
— |
Competency questions in _knowledge/questions.jsonl are a precondition for Phase B, not an output of it. Without them there is nothing to measure and the loop cannot start.
Phase B — Split and baseline (once)
node --no-warnings "${CLAUDE_PLUGIN_ROOT}/scripts/sqlite-knowledge.mjs" eval \
--root <vault> --k 10 --split holdout # record this number, then leave it alone
node --no-warnings "${CLAUDE_PLUGIN_ROOT}/scripts/sqlite-knowledge.mjs" eval \
--root <vault> --k 10 --split dev > /tmp/round-0.json
Record the reranker ceiling once too: eval --k 50 minus eval --k 10. Required notes missing at both depths can never be recovered by reordering.
Phase C — Diagnose
Read, in this order: repair_targets (each gap and how many questions the note blocks), the per-kind hit breakdown, then relation_participant_evicted_ids on any comparison question that lost a sibling note.
| Signal | Lever | Route to |
|---|---|---|
embedding_quality: lexical-baseline, paraphrased or spoken-style questions miss |
Attach a semantic provider | knowledge:sqlite-index-builder |
embedding_prompt: none under embeddinggemma |
Rebuild with the asymmetric prompts | knowledge:sqlite-index-builder |
documents_windowed high |
Chunk the corpus finer | knowledge:rag-corpus-builder |
| Exact screen-label questions lose to paraphrases | Sweep the fusion split | eval --sweep 0.3,0.4,0.5 |
recall@50 ≫ recall@10 |
Attach a reranker | knowledge:knowledge-query → references/local-sqlite.md |
gap: missing-note |
Extract the note | knowledge:knowledge-base-builder |
gap: no-lookup-vocabulary |
Add bridge fields, grounded in the repo | knowledge:knowledge-base-builder |
| Comparison question retrieves the contrast but none of its sides | Declare participants |
knowledge:knowledge-graph-builder |
Re-indexing between rounds is cheap: an unchanged document reuses its stored embedding, so a round that edits two notes re-embeds two documents. embeddings_computed far above the number of edited notes means the cache was rejected — usually because the provider or model changed, which makes the round a provider change, not a corpus one.
Phase D — One change
Apply exactly one. Ground every added term in the source, never in the question set — copying a question's words into a note is test-set leakage that scores well and retrieves nothing new.
Phase E — Re-measure
node --no-warnings "${CLAUDE_PLUGIN_ROOT}/scripts/sqlite-knowledge.mjs" eval \
--root <vault> --k 10 --split dev --baseline /tmp/round-N.json
Corpus and vocabulary edits: revert on any regression, even when recall_at_k rose — competency sets are small enough that an aggregate gain routinely hides a question that stopped working. Engine, provider, and reranker changes are judged differently: they move every question at once, so weigh net movement and holdout together and name the regressions instead of reverting on their existence.
Stop conditions
- Two consecutive rounds with no dev improvement — the remaining gaps are not the kind this loop fixes.
- Every remaining
repair_targetismissing-notewith no source material — an authoring problem; the honest answer is that the vault does not contain the answer. - The target recall is met — score the holdout once and report both numbers.
- The holdout has not moved while dev keeps rising — stop and say so. That gap is overfitting, and more rounds widen it.
Output Template
## Retrieval round N
**Change:** <the one thing> **Lever:** <provider|ranking|corpus|vocabulary>
**Dev:** hits A → B, recall@10 X → Y, MRR P → Q
**Verdict:** improved | regressed | unchanged
**Regressions:** <question ids, or none>
**Decision:** kept | reverted — <why>
**Holdout (recorded at round 0):** <number, unchanged since>
**Stop condition:** <which one, and whether it is met>
What Claude Does / What You Do
| Claude | You |
|---|---|
Runs eval, reads repair_targets, names the lever |
Attaches Ollama or a reranker if you want one |
| Proposes exactly one change per round | Confirms the change is true of the system, not just of the docs |
| Reverts regressions without being asked | Sets the recall target and calls the loop done |
| Reports the holdout, or that it did not move | Supplies the competency questions |
Related Skills
- Steps:
knowledge:knowledge-base-builder,knowledge:knowledge-graph-builder,knowledge:rag-corpus-builder,knowledge:sqlite-index-builder - After:
knowledge:knowledge-query— answer from the index the loop stabilized - Also:
knowledge:render-graph-view,knowledge:ontology-builder - Measured lever sizes and what is deliberately not being done: ROADMAP.md