# m3 Wiki — an auto-generated knowledge base from your memories `m3 wiki generate` compiles your **canonical memories** and your **indexed files** into a browsable, interlinked Markdown vault. It is a *projection*, not a new store: it reads `agent_memory.db` and `files_database.db` and renders pages — your memory model is untouched, and re-running only refreshes the output. The result is a folder of Markdown files that opens as an [Obsidian](https://obsidian.md) vault, renders on GitHub, or browses offline via a self-contained HTML viewer. By default it uses **standard Markdown links** (`[text](page.md)`) so it's clickable in every renderer; add `--obsidian` to emit `[[wikilinks]]` when you want Obsidian's **graph view and backlinks** to populate (see [Using it in Obsidian](#using-it-in-obsidian)). --- ## Quick start ```bash m3 wiki generate # writes a vault to /wiki m3 wiki status # where it is, how many pages, last build ``` By default the vault lands in your engine root (e.g. `~/.m3/engine/wiki`) — it is **private and per-machine**, and is never committed anywhere. Point it elsewhere with `--out`: ```bash m3 wiki generate --out ~/notes/m3-vault ``` Then open that folder in Obsidian (**Open folder as vault**). --- ## What gets included A memory becomes a wiki entry when it is **canonical** — m3's three overlapping signals for "this matters": - **pinned** — explicitly marked as canon (never aged out) - **high importance** — at or above the `--importance-threshold` (default `0.55`) - **a consolidated type** — `belief`, `procedure`, or `reference` (these are already distillations, so they belong in the wiki regardless of importance) Raise the bar for a tighter, higher-signal vault: ```bash m3 wiki generate --importance-threshold 0.8 ``` Your **files corpus** contributes a second layer: each indexed document becomes a `sources/` page (from its summary), and — via m3's promotion bridge — a memory can link *down* to the exact file a fact came from, while a source page links *up* to every memory it fed. Skip the files layer with `--no-files`. > **Backend note.** The wiki's *memory* layer works on both m3 backends (SQLite and > PostgreSQL) — it reads through m3's core database seam. The files corpus > is currently a local **SQLite** sidecar (`files_database.db`) on every backend, so > the `sources/` pages are read from SQLite even on a PostgreSQL deployment. > PostgreSQL support for the files corpus is planned (see the CHANGELOG). If you run > PostgreSQL without a local files DB, use `--no-files` for a memory-only vault. --- ## How pages are organized - **Topics** (`topics/*.md`) — related memories are clustered into one page per topic. Clustering uses m3's relationship graph *and* shared extracted entities, so memories that talk about the same thing land together even without a hand-authored link. Each page carries real frontmatter (`confidence`, `valid_from`, the source `memory_ids`), a member list, an **Evidence** section (links to source files), and **Backlinks**. - **Sources** (`sources/*.md`) — one page per indexed file, with its summary and notable extracted facts. - **index.md** — a reader-facing table of contents: a **⭐ Start here** shortlist of your most prominent topics, then sections grouped by kind (Knowledge, Runbooks, Decisions, References). - **overview.md** — counts and your largest topics at a glance. - **lint.md** — housekeeping: orphaned memories, and **contradictions** (memories that disagree are kept on one page and reported here, never silently dropped). Superseded and contradicted memories are shown as history, not hidden — the wiki reflects what m3 actually knows, including where it changed its mind. --- ## Prose summaries (optional) By default a topic page lists its member memories. With `--synthesize`, m3 asks a **local chat model** to write a short prose lede at the top of each topic: ```bash m3 wiki generate --synthesize ``` This talks to an OpenAI-compatible `/v1/chat/completions` endpoint. Point it at your model with environment variables (defaults shown): | Variable | Default | Purpose | |---|---|---| | `M3_WIKI_SYNTH_URL` | `http://127.0.0.1:1234/v1/chat/completions` | Chat endpoint (LM Studio, llama-server, Ollama, vLLM, …) | | `M3_WIKI_SYNTH_MODEL` | *(server's loaded model)* | Model id to request | | `M3_WIKI_SYNTH_TIMEOUT` | `30` | Per-request timeout (seconds) | Ledes are **cached on disk** by a content-hash of each topic, so an unchanged topic is never re-summarized and repeat runs are cheap. If no model is reachable, synthesis degrades gracefully — the page keeps its member list and generation never fails. --- ## Compiled syntheses (`m3 wiki compile`) `--synthesize` writes a prose lede into the *rendered vault*. `m3 wiki compile` goes further: it writes each topic's synthesis back into your store as a durable `synthesis` memory — a first-class, searchable, supersede-tracked row with provenance edges to its sources. This is the "compile-at-ingest" model: knowledge is distilled once and stored, not re-derived on every read. ```bash m3 wiki compile # compile the whole corpus m3 wiki compile --dry-run # show what would compile — no model call, no write ``` Compilation is **idempotent**: an unchanged topic is recognized by a content hash and skipped (no model call), so re-running only recompiles topics that actually changed. Each synthesis records its source `member_ids` and writes `consolidates` provenance edges, so blast-radius, citation-drift, and the Knowledge Anchor Report can all reason about how a page was derived. ### `--check-drift` — has a synthesis drifted from its sources? ```bash m3 wiki generate --check-drift # opt-in; report-only; mutually exclusive with --check ``` An **opt-in, report-only** pass that asks an LLM judge whether each compiled synthesis still faithfully reflects the sources it cites, flagging any that have **drifted**. It never rewrites or withholds a page — it only reports — and it is **fail-open**: if the judge is unreachable or errors, the page is treated as un-drifted so a model outage never blocks a build. It cannot be combined with `--check` (the byte-identity determinism check), because the judge is non-deterministic by design. Endpoint/model/timeout come from the `M3_WIKI_DRIFT_*` env vars (falling back to `M3_WIKI_SYNTH_*`) — see [ENVIRONMENT_VARIABLES.md](ENVIRONMENT_VARIABLES.md#wiki--synthesis--compilation). ### The admission gate — which topics earn a synthesis Not every cluster deserves a compiled page. A cluster fused only by incidental co-mention (two memories that happen to name the same file or host) is a grab-bag, not a topic. The **admission gate** demotes such clusters back to the orphan list *before* the model is called — so compilation spends effort only on genuinely anchored topics, and the vault isn't padded with incoherent pages. The gate scores each cluster on its **member-to-member link structure** and admits it if any of these clears its floor: | Signal | Meaning | Default floor | |---|---|---| | `backbone_ratio` | share of real (non-co-mention) edges — the primary discriminator | 0.6 | | `provenance` | share of edges that are authored lineage (supersedes / extends / …) | 0.5 | | `kas` | overall structural Knowledge Anchor Score | 0.5 | Two properties are guaranteed by design: - **Deterministic** — the same store produces a byte-identical result. - **State-independent** — the decision reads only *authored* structure, never the `consolidates` edges that a prior compile wrote. A topic is admitted or demoted identically on the first compile and the thousandth; compiling never changes what the gate will do next time. Tune the floors (or turn the gate off for an audit build) with an optional config file at `$M3_CONFIG_ROOT/.wiki_admission.json`: ```json { "min_backbone_ratio": 0.6, "min_provenance": 0.5, "min_kas": 0.5, "enabled": true } ``` Any field may be omitted (it keeps the default). A `--dry-run` reports how many clusters the gate would demote, so you can calibrate before a real run. > Compiled syntheses derived from a memory that is later erased are handled under > GDPR Art. 17 — see [The Wiki & the Right to Erasure](WIKI_GDPR.md). --- ## Keeping it fresh The generator is **deterministic**: the same memories produce a byte-identical vault, so a diff always reflects a real change in what m3 knows. Check whether the on-disk vault is stale (useful in a scheduled job): ```bash m3 wiki generate --check # exit 0 if fresh, non-zero (and lists drift) if stale ``` `--check` runs on the deterministic vault only; it is not combined with `--synthesize` (LLM prose isn't bit-reproducible). --- ## Clustering The wiki is a **core feature — it ships in the base `pip install m3-memory`** and needs no extra to run. `m3 wiki generate` works out of the box. Topic clustering uses [`networkx`](https://networkx.org) greedy-modularity community detection, which is a **base dependency** of m3-memory — it installs with the core package, nothing extra to enable. Clustering is deterministic run-to-run, so `m3 wiki generate --check` stays byte-reproducible. > The old `[wiki]` optional extra is gone: networkx is now always present, so the > `--no-networkx` flag (still accepted on `generate` and `compile`) only forces the > pure-Python clustering fallback — it is no longer needed to avoid a missing dep. `pip install "m3-memory[wiki]"` still works as a > no-op back-compat alias so existing scripts don't break, but it installs nothing > beyond the base package. --- ## Using it in Obsidian The vault opens directly in Obsidian: **Open folder as vault**, point it at the output dir. Every page is clickable straight away. For Obsidian's **graph view** and **backlinks pane** to populate, generate with `--obsidian`: ```bash m3 wiki generate --obsidian ``` This emits `[[wikilinks]]` instead of standard Markdown links — Obsidian builds its graph and backlinks from wikilinks, not from `[text](page.md)` links. The tradeoff: wikilinks render as literal text outside Obsidian (GitHub, the HTML viewer), so `--obsidian` is opt-in. Use the default (standard links) for a portable vault; use `--obsidian` when Obsidian is your primary reader. --- ## Command reference ``` m3 wiki generate [options] --out DIR Output vault dir (default /wiki) --importance-threshold F Min importance to count as "core" (default 0.55) --no-files Memory-only vault (skip the files corpus) --synthesize Add an LLM prose lede per topic (opt-in, cached) --obsidian Emit [[wikilinks]] so Obsidian's graph view + backlinks work (opt-in; literal text elsewhere) --exclude REGEX Drop memories whose title/content matches REGEX --html Also write a self-contained wiki.html viewer --check Exit non-zero if the on-disk vault is stale m3 wiki compile [options] Compile topics into durable synthesis memories --importance-threshold F Min importance to count as "core" (default 0.55) --dry-run Report what would compile — no model call, no write (also shows how many clusters the admission gate demotes) m3 wiki status [--out DIR] Vault location, page count, last build time ``` Everything runs locally. No account, no API key, no network egress is required for core generation (only `--synthesize` talks to a model, and that model is yours).