CLAUDE.md — LLM-Wiki v2 Schema¶
Read by Claude Code at the start of every session. Defines the wiki architecture, memory tiers, conventions, and workflows. Update this file when conventions change. Companion
AGENTS.mdis the agent-facing tool catalogue.
Models¶
| Role | Model | Strengths |
|---|---|---|
| Summarise, extract | gemma4:e4b |
Fast, strong instruction-following |
| Reason, route, lint, claims | qwen3:14b |
Deep reasoning, thinking mode |
| Quantitative reasoning specialist | vibethinker:3b |
AIME-class maths / STEM / code; routed to ADAPTIVELY for quantitative questions |
| Embeddings | nomic-embed-text:latest |
274 MB, MTEB-strong |
| STEM embeddings (optional) | bge-m3 |
Domain-routed embedder for maths/science/econ/eng; enable with EMBED_STEM_ENABLED |
| Vision (image captions) | llava:7b |
Optional — used when ingest_caption_images=true |
All models served via Ollama at OLLAMA_HOST (default http://localhost:11434).
MODEL_FAST = MODEL_REASON is intentional — disables a missing-llama3.2 fallback.
Multi-provider LLM fleet (v4, llm_wiki/providers.py)¶
Each role can be routed to a hosted, OpenAI-compatible provider instead of
Ollama via PROVIDER_<ROLE> (summary / reason / fast / solver / embed /
vision), default ollama. Vision routes the llava image-caption role to a
multimodal provider via the OpenAI image_url content schema. Providers (v6 roster,
PROVIDER_DEFS registry): groq, github (GitHub Models), gemini, openai,
anthropic (Claude via api.anthropic.com/v1 OpenAI-compat; force_max_tokens),
xai (Grok), openrouter (100+ OSS models, one key), and custom — any
OpenAI-compatible gateway (vLLM / LM Studio / llama.cpp / Together / DeepSeek …)
via CUSTOM_BASE_URL/CUSTOM_MODEL (key optional for local gateways).
Embeddings: ollama | gemini | openai | custom only.
resolve_chat_provider() returns None (→ Ollama) when the role is ollama or its
key/model is unset, so routing is opt-in and self-healing. A provider HTTP error is
re-raised as OllamaError so the existing role fallbacks (e.g. reason→fast) still fire.
Keys are read from env ONLY — never committed. .env.example documents every knob.
Adaptive model routing (VibeThinker)¶
model_solver (default vibethinker:3b, WeiboAI/VibeThinker)
is a tiny reasoning specialist: world-class on competition maths / STEM / code, but
weak on broad knowledge (the authors say so). So it is NOT a general synthesizer
replacement — it is routed to ONLY for quantitative questions via a reason→format
two-stage:
llm_wiki/search/domain.pydetects the cognition required (general / math / science / economics / engineering) by heuristic regex over the question + retrieved context.- If quantitative (
needs_solver()True) androute_solver_enabled, VibeThinker does the step-by-step derivation (OllamaClient.solver(), temp 0.6, top_p 0.95). Its<think>…</think>trace is stripped (strip_think()). qwen3:14bthen formats + cites that verified reasoning into the standard JSON/citation schema — so grounding, per-claim confidence and save-back are unchanged.
Any solver failure (model not installed, timeout) silently falls back to reason-only
synthesis. QueryResult.reasoner records "reason" or "solver".
Serving: VibeThinker ships for vLLM / SGLang / transformers. To use it in this
Ollama stack, either pull a GGUF quant (ollama create vibethinker:3b -f Modelfile
with temperature 0.6, top_p 0.95, num_ctx 40960) or run a vLLM sidecar. Set
model_solver="" to disable routing entirely.
Domain-specialized STEM embeddings (optional)¶
model_embed_stem (default bge-m3, off unless EMBED_STEM_ENABLED=true) is a
stronger embedder for notation-heavy content. When enabled, DomainRoutedDenseIndex
(llm_wiki/search/dense_router.py) keeps a separate STEM dense collection
(chroma_stem) — two embedders mean two incompatible vector spaces, so they cannot
share one collection. Routing: quantitative pages (domain ∈ {math, science, economics,
engineering}) are indexed into BOTH general and STEM collections; quantitative queries
are served by the STEM collection (each index embeds the query with its own model, so
spaces stay consistent). hybrid_search calls route_search() when present; a plain
DenseIndex is unchanged. Disabled = transparent passthrough to the general index.
Enabling requires ollama pull bge-m3 and re-ingesting (or rebuilding) to populate
the STEM collection.
Best-of-best RAG package (v5)¶
Five techniques layered onto the existing pipeline (each flag-gated, on by default):
| Technique | Where | Flag |
|---|---|---|
Small-to-big retrieval — rerank/synthesise the matched 1500-char sub-chunks (±neighbours) instead of page[:4000]; llm_wiki/search/chunks.py re-derives the exact index-time chunks |
hybrid.py |
QUERY_CHUNK_CONTEXT |
Doc2Query (Nogueira & Lin) — index the questions each doc answers as <pid>#hq so question-phrased queries match declarative text |
ingest.py |
INGEST_DOC2QUERY |
| Lost-in-the-middle reorder (Liu et al. 2023) — ends-load synthesis context: best page first, runner-up last | query.py |
QUERY_LITM_REORDER |
| NLI-lite claim verification — one batched summary-role call judges each cited claim against its cited snippet; unsupported ×0.35 confidence | synth/verify.py |
QUERY_CLAIM_VERIFY |
| Machine-page down-weight — synthesis/promoted/crystallized pages score ×0.85 at rerank so save-backs never outrank primary sources (anti-feedback-loop) | hybrid.py |
RETRIEVAL_SYNTH_DOWNWEIGHT |
RAPTOR-lite topics (Sarthi et al. 2024 / GraphRAG communities) — weekly greedy-cosine clustering of live pages → topic-*.md overview pages (kind topic) answering corpus-level questions |
wiki/topics.py |
JOB_BUILD_TOPICS_ENABLED, TOPICS_MIN_CLUSTER, TOPICS_MAX, TOPICS_SIM_THRESHOLD |
Supporting tooling:
- scripts/backfill_v5.py — one-off #hq + topic backfill for pre-v5 pages
(--summary-model/--reason-model override when gemma4/qwen3 aren't pulled).
- Eval harness (llm_wiki/eval_harness.py): scripts/gen_golden.py builds
eval/golden.jsonl from the live wiki; scripts/run_eval.py [--ablate] [--answers]
measures recall@k / MRR / latency per one-flag-off variant and answer quality.
Run it before adding or removing retrieval techniques.
- OKF bundles: scripts/export_okf.py <out> ships sources/entities/procedures as
a validated standalone bundle; scripts/import_okf.py <bundle> imports external
bundles as curated pages (no LLM pass; links → RELATES_TO edges);
llm_wiki/loaders/okf_loader.py::validate_bundle checks conformance.
- rerank() degrades to RRF-order passthrough when flashrank isn't installed.
Three-layer architecture¶
- Raw sources (
wiki/raw/) — immutable input documents. - The wiki (
wiki/) — LLM-generated Markdown across four memory tiers. - The schema (this file +
AGENTS.md) — what the LLM is allowed/required to do.
Memory tiers (v2)¶
| Tier | Where | Lifetime | What goes here |
|---|---|---|---|
| Working | in-process state | a single request | retrieved candidates, draft answer |
| Episodic | wiki/episodic/<date>.md |
14 days (configurable) | every ingest / query / lint event with correlation IDs |
| Semantic | wiki/sources/, wiki/entities/ |
indefinite, decays | consolidated knowledge, auto-generated entity pages |
| Procedural | wiki/procedures/ + data/procedures.db |
indefinite | repeated query patterns crystallised into reusable procedures |
Promotion rules:
- A query becomes part of episodic on every successful answer.
- A topic that recurs ≥ 3 times across ≥ 14 days of episodic gets auto-promoted to semantic via promote_episodic_to_semantic (scheduled daily 04:00 UTC).
- A query pattern that recurs ≥ 5 times with similar retrieval set becomes a procedure via detect_procedures (scheduled weekly Sun 06:00 UTC).
- Save-back at query time still produces wiki/sources/synthesis-<slug>.md for high-confidence (≥ 0.80) answers with ≥ 2 citations.
Directory layout¶
LLM_Wiki/
├── CLAUDE.md ← This file (human-facing)
├── AGENTS.md ← Agent-facing tool/resource catalogue
├── llm_wiki/
│ ├── api.py ← FastAPI endpoints
│ ├── ingest.py ← Ingest pipeline (loaders → summarise → claims → graph)
│ ├── query.py ← Hybrid retrieval + reflective synthesis + save-back
│ ├── lint.py ← Health check + auto-fix
│ ├── graph.py ← KnowledgeGraph (entities, relations, facts) — bi-temporal
│ ├── llm.py ← Async Ollama client (cached embeddings)
│ ├── config.py ← pydantic-settings — all knobs in one place
│ ├── logging_config.py ← SOC-style JSON logs + audit channel
│ ├── scheduler.py ← Phase D: APScheduler + JOB_REGISTRY
│ ├── mcp_server.py ← Phase F3: agent-facing MCP wrapper
│ ├── search/ ← BM25 + dense + RRF + rerank + MMR + multi-query + intent
│ ├── synth/ ← Answer blocks + per-claim confidence + reflect + followups
│ ├── loaders/ ← Multi-format ingest (PDF/DOCX/HTML/PPTX/XLSX/CSV/MD/TXT)
│ └── wiki/
│ ├── pages.py ← Page read/write + PageStore
│ ├── synth_page.py ← Standalone util (used by save-back / promote / crystallize)
│ ├── episodic.py ← Episodic log + read_episodes + prune
│ ├── promote.py ← Episodic → semantic auto-promotion
│ ├── procedures.py ← Procedural memory tier (SQLite + Markdown)
│ ├── reconciler.py ← Memory-evolution edits to existing pages
│ ├── lifecycle.py ← Ebbinghaus decay + access reinforcement
│ ├── contradiction_resolver.py ← Composite-score auto-resolution
│ ├── entity_pages.py ← Auto-generated per-entity pages
│ ├── index_md.py ← Deterministic regen of wiki/index.md
│ └── log_md.py ← Append-only operation log
├── wiki/
│ ├── index.md
│ ├── log.md
│ ├── sources/ ← Semantic tier — primary citable content
│ ├── entities/ ← Semantic tier — auto-generated entity pages
│ ├── procedures/ ← Procedural tier — recurring patterns
│ ├── episodic/<date>.md ← Episodic tier — append-only daily logs
│ ├── archive/ ← Stale low-conf pages moved here by lint auto-fix
│ ├── review/ ← Confidence-gated drafts awaiting human accept
│ │ └── edits/ ← Reconciler-staged edit proposals
│ └── raw/ ← Immutable source documents
├── data/
│ ├── graph.db ← SQLite: entities, relations, facts (bi-temporal), page_access
│ ├── procedures.db ← SQLite: recurring query patterns
│ ├── bm25.pkl
│ └── chroma/ ← ChromaDB persistence (or numpy fallback)
└── logs/
├── app.log ← All events (rotating JSON)
└── audit.log ← Filtered audit channel
Page conventions (frontmatter)¶
The wiki conforms to Google's Open Knowledge Format (OKF) v0.1
(spec):
every concept page carries type (OKF's one required field), plus the recommended
description, resource, tags, and timestamp. write_page() stamps type
(mapped from kind via OKF_TYPE_BY_KIND), description (first prose sentence of
the body when absent) and timestamp (ISO 8601 last-modification) centrally, so every
writer conforms automatically. index.md / log.md are OKF reserved filenames —
never concept pages (PageStore.iter_pages skips them). The root index.md declares
okf_version: "0.1" and lists pages as - [Title](path) - description. log.md uses
date-grouped, newest-first entries with bold action keywords. Body cross-links are
bundle-relative markdown links ([Title](/sources/foo.md)) — NOT Obsidian wikilinks —
so OKF consumers can treat them as graph edges. scripts/migrate_okf.py re-stamps
pre-OKF pages (idempotent).
---
title: "Page Title"
kind: source | entity | synthesis | promoted | crystallized | procedure
type: "Source Document" | "Person|Organization|Concept|Place|Event" | "Synthesis" | ... # OKF required
description: "One-sentence summary (first prose sentence of body if not supplied)" # OKF recommended
resource: "wiki/raw/file.pdf" # OKF URI of the underlying asset (mirrors source)
timestamp: "2026-07-15T16:51:36+00:00" # OKF last-modification; auto-stamped on write
source: "wiki/raw/file.pdf" | "query-save-back" | "episodic-promotion" | "session-crystallize"
ingested: 2026-05-01
confidence: 0.87
confidence_reason: "..."
domain: general | math | science | economics | engineering # stamped at ingest; drives adaptive routing
chunk_strategy: dense | narrative | balanced | fixed # agentic-ingestion chunk plan used
chunk_count: 14 # sub-chunks indexed for this page — stamped at write so re-index / review-promotion can purge exactly (no residue, no 50-unit ceiling)
tags: [concept, person, org]
entity_refs: ["Entity A", "Entity B"]
context_preamble: "..." # Anthropic Contextual Retrieval — short doc context
has_tables: true
has_images: false
element_counts: {text: 14, heading: 6, table: 3, image: 0, code: 0}
evolved_by: # populated by reconciler when this page is edited by a later source
- {source: "Foo Doc", action: "refine", date: 2026-05-15}
correlation_ids: [COR-...] # only on crystallized / promoted
---
Profile / schema contract (wiki/profile.py, PROFILE_ENFORCEMENT = off|warn|strict,
default warn): write_page validates frontmatter against a declarative contract
(allowed kinds/domains, required fields, confidence bounds, entity/relation
vocabularies) — warn logs + audits PROFILE_VIOLATION, strict raises. The built-in
DEFAULT_PROFILE matches this schema; PROFILE_PATH overrides any subset. GET /profile
shows it; POST /admin/profile/validate audits the live corpus.
Feedback curator (wiki/feedback.py, data/feedback.db, FEEDBACK_ENABLED): captures
high-signal user feedback (POST /feedback) — a heuristic drops generic acks, else the
reason role classifies into correction/preference/approval/rejection. Promotion
(POST /feedback/{id}/promote, or FEEDBACK_AUTO_PROMOTE) applies it: correction →
curated sources/feedback-*.md page (indexed); preference → active preference injected
into synthesis prompts (feedback_inject_preferences); approval → page reinforcement.
Confidence gate: >= confidence_threshold (default 0.60) → wiki/sources/, else wiki/review/.
Staged pages then pass through the Review Autopilot (wiki/review_autopilot.py):
an evidence-grounded judge compares the page against its original source, blended
with a deterministic entity-grounding check (composite = 0.7×judge + 0.3×grounding;
borderline cases get a second judge vote from the reason role). ≥0.70 auto-accepts
(indexed + WIKI_REVIEW_ACCEPT by=auto), ≤0.30 auto-archives to wiki/archive/
(reversible — nothing is deleted), and the gray zone stays in review with an
auto_review frontmatter annotation (scores + judge reasons) surfaced by
GET /review. Pages whose source can't be re-read are always left for the human.
Accepted pages also keep an auto_review block (verdict: auto-accepted) so the
confidence provenance survives on the page.
Promotion (review → sources) — both accept paths (autopilot and the human
POST /review/{id}/accept) funnel through wiki/review_promote.py::promote_review_page:
it deletes the stale review/* retrieval units, re-indexes the page under its new
sources/* id via the SAME small-to-big chunker used at ingest (contextual preamble,
dense metadata, Doc2Query #hq), and calls KnowledgeGraph.reassign_page_id to
re-point facts/entities/relations/media from the old id to the new one — so a promoted
page is a first-class retrieval citizen, not a monolithic blob with orphaned graph rows.
Human POST /review/{id}/reject archives to wiki/archive/ (reversible), never
hard-deletes.
Bi-temporal facts (separate from page confidence): every claim in the facts
table carries ingested_at, optional valid_from, optional valid_to,
superseded_by, last_reinforced, and access_count. Effective confidence
is the stored value × Ebbinghaus decay; computed on read.
Workflows¶
Ingest¶
load_elements (multi-format)
→ privacy redaction (strip API keys / JWTs / private keys / passwords) [PRIVACY_REDACT]
→ agentic plan: adaptive chunk size/overlap from structure + density [agentic ingestion]
→ layout_aware_chunks (atomic tables/images, plan-driven target/overlap)
→ summary-role summarise per chunk + extract entities/relations
→ reason-role merge (3-tier fallback) + score confidence
→ extraction-signal floor (rich → bumps confidence)
→ contextual preamble (Anthropic) for embedding text
→ Doc2Query: summary-role generates the questions the doc answers → indexed as <pid>#hq [v5]
→ write to sources/ or review/
→ Review Autopilot on staged pages: evidence-grounded judge re-reads the
page AGAINST the source (+ entity-grounding cross-check + second-opinion
vote near boundaries) → auto-accept ≥0.70 / auto-archive ≤0.30 /
annotate the gray zone for humans [REVIEW_AUTOPILOT_ENABLED]
→ upsert entities + relations
→ extract S-P-O claims (reason role) → add_fact() [v2]
→ contradiction detection vs. related pages
→ on flag: supersede_fact() on older page [v2]
→ on flag: composite-score auto-resolver [v2 — Phase E2]
→ reconciler: edit affected pre-existing pages
→ on apply: supersede_fact() if action ∈ {refine, contradict} [v2]
→ rebuild_index + rebuild_entity_pages
→ episodic_log_entry
Query¶
POST /query now enters through the agentic orchestrator (agentic_rag/orchestrator.py):
it assesses question complexity and sends factual/simple queries to the fast single-pass
path (zero overhead) while multi_hop/synthesis/exhaustive go through the iterative agentic
loop (plan → fanout → sufficient-context check → gap rewriter → repeat). Request flag
agentic: omit = auto-decide (default), true = force loop, false = force single-pass.
Disable globally with AGENTIC_ENABLED=false. Both paths share the synthesis below.
semantic answer cache (opt-in) — embed the question; on cosine ≥ threshold vs a [QUERY_ANSWER_CACHE, default off]
recently-answered question, return the stored answer (cached=True), skipping
retrieval + synthesis. Complements the procedural store's EXACT pattern hash.
Staleness guard (ANSWER_CACHE_VERIFY_PAGES): a hit is rejected (re-answered) if
any page it cited no longer resolves — safe even with a loose threshold.
→ intent classifier (factual / multi_hop / synthesis / exhaustive)
→ decompose (compound)
→ multi-query paraphrase (RAG-Fusion)
→ HyDE seed for dense
→ hybrid retrieval (BM25 + dense → RRF → FlashRank → graph 2-hop → MMR)
→ small-to-big: rerank/synthesise the MATCHED sub-chunks (±neighbours), [v5]
not page[:4000] — llm_wiki/search/chunks.py re-derives index-time chunks
→ machine-page down-weight (synthesis/promoted/crystallized ×0.85) [v5]
→ mark_accessed() on retrieved pages [v2 — Phase B3]
→ CRAG relevance filter (drop off-topic)
→ adaptive model routing: if quantitative (maths/econ/science/eng), [VibeThinker]
VibeThinker reasons step-by-step → reason-role formats + cites the result
→ multimodal expansion: surface media nodes linked to retrieved [GRAPH_MULTIMODAL_NODES]
entities (tables/figures/code) into context + related_media
→ lost-in-the-middle reorder: ends-load context (best first, runner-up last) [v5]
→ synthesis (numbered citations, [Page]^conf markers, blocks)
→ grounding check + CRAG ceiling
→ NLI-lite claim verification: ONE batched summary-role call judges each cited claim [v5]
against its cited snippet (supported/partial/unsupported) → recalibrates
per-claim + overall confidence (catches "right page, wrong claim")
→ reflection critique → optional refinement
→ record_query_pattern() in procedural store [v2 — Phase C4]
→ save-back if conf ≥ 0.80 ∧ ≥ 2 cits (runs AFTER NLI-lite verification recalibrates
confidence, so a page is never persisted with an inflated pre-verification score)
→ populate semantic answer cache (grounded ∧ conf ≥ min) [QUERY_ANSWER_CACHE]
→ episodic_log_entry
Lint¶
reason-role scans first 30 pages
→ JSON report (orphans, stale, missing_entity_pages, contradictions)
→ if auto_fix=True (Phase E1):
backlink orphans from index.md
rebuild_entity_pages()
archive stale low-conf old pages → wiki/archive/
comment broken cross-references
Scheduled jobs (APScheduler, in-process)¶
| time UTC | job | toggle |
|---|---|---|
| daily 03:00 | decay_sweep |
JOB_DECAY_SWEEP_ENABLED |
| daily 03:30 | episodic_prune |
JOB_EPISODIC_PRUNE_ENABLED |
| daily 04:00 | promote_episodic |
JOB_PROMOTE_EPISODIC_ENABLED |
| daily 04:30 | review_autopilot (backlog sweep) |
JOB_REVIEW_AUTOPILOT_ENABLED |
| weekly Sun 05:00 | lint_autofix |
JOB_LINT_AUTOFIX_ENABLED |
| weekly Sun 06:00 | detect_procedures |
JOB_DETECT_PROCEDURES_ENABLED |
| weekly Sun 07:30 | build_topics (RAPTOR-lite topic overviews) |
JOB_BUILD_TOPICS_ENABLED |
Manual: POST /admin/run/{job_name} runs any registered job once.
Confidence policies¶
- Stored confidence is what the LLM assigned at ingest. Don't mutate it on read.
- Effective confidence = stored ×
exp(-Δdays / half_life_days), floored at 0.05. Half-life default = 90 days. - Reinforcement triggers when a page is accessed ≥ 3 times within a 14-day window. The reinforcement timestamp resets the decay clock.
- Decay sweep (scheduled daily) DOES rewrite stored confidence based on
last_reinforced. Day-to-day reads still compute effective on the fly.
Supersession lifecycle¶
- A new source NEVER deletes an old fact. It can only mark it superseded:
valid_to = today,superseded_by = <new fact id>. - Three triggers:
- Reconciler auto-apply: when
action ∈ {refine, contradict}lands andold_textis matched on the target page. - Contradiction detector: when
_detect_contradictionsreturns a concrete claim excerpt. - Auto-resolver (Phase E2): when a contradiction is detected with composite score margin ≥ 0.2.
- Below the margin → leave both active, surface in
GET /admin/contradictionsfor human review.
Agentic ingestion (implemented — llm_wiki/agentic_ingest.py)¶
plan_ingest() inspects each document's structure (element kinds/counts, structural
density, text length) and a content sample, then picks an adaptive chunk plan instead
of the fixed 6000-char target:
- dense (STEM domain, formulas, or ≥4 tables/code blocks) → ~3000 chars + ~10%
overlap, so notation/tables stay with their explanation.
- narrative (long prose, low density) → ~7500 chars + ~2% overlap.
- balanced → existing defaults.
Heuristic-first (on by default, zero LLM cost). Optional summary-role refinement via
INGEST_PLANNING_LLM (one extra call per doc). All sizes clamped to [1500, 9000] /
[80, 600]. The chosen strategy is recorded in frontmatter as chunk_strategy.
Privacy filtering (implemented — llm_wiki/privacy.py)¶
Implemented in llm_wiki/privacy.py. redact_text() is applied to raw element text in
ingest_file() BEFORE it reaches the summariser / claims / graph / embeddings /
on-disk page. Each secret becomes a typed [REDACTED:<cat>] placeholder so prose
stays coherent.
- Strips API keys (sk-..., ghp_..., xox[baprs]-..., AKIA..., AIza..., GitLab PATs).
- Strips JWTs (eyJ…) and PEM private-key blocks.
- Strips plaintext passwords in password: … / pwd=… form (field name preserved).
- Emails are PII but public author emails are legitimate content → opt-in via
INGEST_REDACT_EMAILS (default off).
- Audit-logs every redaction with a PRIVACY_REDACT event carrying per-category counts.
Toggles: INGEST_REDACT_SECRETS (default on), INGEST_REDACT_EMAILS (default off).
Conservative by design — high-precision patterns only, to avoid corrupting prose.
Knowledge graph¶
- Entity types:
PERSON,ORG,CONCEPT,PLACE,EVENT - Relation types:
RELATES_TO,PART_OF,CONTRADICTS,SUPPORTS,AUTHORED_BY,OCCURRED_IN - Media nodes (multimodal graph Phase 1,
GRAPH_MULTIMODAL_NODES, default off):media_nodes(kindstable|image|code|formula) +media_entitiesedges (DEPICTS|MEASURES|DEFINES|REFERENCES). Populated at ingest, each embedded as its own dense unit (<pid>#media#<n>), linked to entities by name presence. Data-only in Phase 1 — retrieval through media nodes is a later phase. Seedocs/design/multimodal-graph.md. - Fuzzy canonicalization at threshold 95 (raised from 90 for cross-domain safety).
- Reconciler requires ≥ 2 entity overlaps before considering a page affected (single-entity coincidences ignored).
- 2-hop expansion at retrieval time.
Logging conventions¶
JSON. Standard fields:
Audit events (always written to logs/audit.log):
- WIKI_WRITE, WIKI_WRITE_STAGED, WIKI_REVIEW_ACCEPT, WIKI_REVIEW_REJECT
- CONFIDENCE_LOW, CONTRADICTION_DETECTED
- STALE_PAGE_DETECTED, ORPHAN_PAGE_DETECTED
- FACT_SUPERSEDED (new in v2)
Session checklist (Claude Code at session start)¶
- Read this file (
CLAUDE.md) andAGENTS.md. GET /context/start?days=7— recent episodic + top pages + open contradictions.- Check
wiki/log.mdlast 5 entries. - Check
wiki/review/andwiki/review/edits/for staged work awaiting human review. GET /admin/contradictionsfor unresolved contradictions.
After significant changes:
- Update this file.
- Run make test (if present) and fix failures.
- POST /lint {auto_fix: true} to self-heal.
v2 last updated: 2026-05-01 — bi-temporal facts, lifecycle, scheduler, auto-fix, crystallize.