Compile — logbook → dossiers (the canon)
Read the append-only logbook and distil its claims into dossiers: the canonical,
consistency-checked SSOT a human inspects. Each cabinet claim carries a provenance link back
to the minute that introduced it, so the canon is traceable and regenerable.
The logbook is faithful but low-authority and chronological; the cabinets are authoritative
and topical. Compile is the bridge — it consolidates many sessions into one current truth.
Inputs
- The workspace (discover a
bureau.json; default canon).
- The set of minutes to process: every entry under
logbook/ whose session id is not
yet recorded in <workspace>/_compile-state.json, narrowed by --since <YYYY-MM-DD> when
given. _compile-state.json is the watermark: { "compiled": ["<session-id>", …] }. It is
underscore-prefixed, so the press never renders it.
Cabinet page schema
A dossier is one markdown file in a topic drawer (decisions/, architecture/,
characters/, …) holding one claim (see step 4 — one claim per page keeps a page's trust
tier unambiguous). Frontmatter uses the press's simple parser: the title is unique and
unquoted and a scalar value contains no ", newline, or [ ] |. The only values that carry
[[ ]] are relation edges like contradicts: [[Other page]]. Frontmatter takes flat
key: value lines, inline lists (tags: [a, b]), and multi-line lists of scalars; values are
always strings (no YAML type coercion), and nested maps or block scalars are rejected — the
build fails. Provenance is a [[wiki-link]] to a minute: an edge the press indexes, so the minute
gets a backlink showing which dossiers it produced. A frontmatter sources: list of wiki-links
would index too, but compile writes it in the body, as a Sources line — one shape, easy to
review. gazette health reports any tiered page with no provenance link as unsourced:
---
id: 01J9Z8QKQ7ULIDEXAMPLE
title: SSOT model
updated: 2026-06-10
status: proposed
rests_on:
- { page: "[[Logbook model]]", span: "^authority", because: "the SSOT split assumes the logbook is low-authority" }
---
# SSOT model
The wiki is authoritative for current truth; the logbook is low-authority provenance. ^ssot-claim
See [[Logbook model]].
**Sources.** [[session a1b2c3d4 · 2026-06-10]]
id is an opaque, immutable identifier — stamp one on every dossier (a ULID, or any
unique token; e.g. pg-<slug>-NNNN). It is the page's identity, so a later rename never breaks a
dependency that points at it. A page with no id falls back to a title-derived shim that does
break on rename — so always author one.
- The claim carries a
^span — a ^anchor at the end of the claim line (^ssot-claim above).
This is what a dependent page points at. Anchor exactly the sentence(s) another page could depend
on; keep it stable so cosmetic edits elsewhere don't churn it.
rests_on declares a dependency: when this dossier's claim relies on another dossier's
claim, add an object edge naming the target [[page]], its ^span, and a because. The
recursion engine then flags this page needs-review whenever that upstream span changes. These
edges are proposed by compile and confirmed by the human at review — declare them
generously (under-scoping is the silent killer; over-scoping only annoys). Omit rests_on for a
standalone claim.
status is the trust tier (defined in the review skill). Compile writes only proposed
(an AI claim, unchecked) or verified (a fact it confirmed against the repo). It never
writes canonical — that tier is reached only through bureau:review, the human gate. A
conflict yields contested (see the conflict policy).
- The body
**Sources.** line wiki-links the minutes that justify this page, each by
its title (session <session-id> · <date>). This is the provenance — the press renders it as a
backlink, so each session shows which dossiers it produced, and the page lists the
sessions that justify it. A claim that disagrees keeps its own inline [[session …]] link.
Steps
- Locate the workspace (
bureau.json; default canon). If none, tell the user to run
bureau:init first and stop.
- Select entries. List minutes not in
<workspace>/_compile-state.json (apply
--since). If none remain, report "cabinets already current" and stop.
- Extract claims. For each selected entry, read its Decisions and Changes. Each yields a
claim and the dossier it belongs to (the entry names the target page).
- Place each claim — one claim per page. Derive the page title; enforce the title
rules: NFC-trim, strip any
[ ] | and quotes, and if the title collides with an existing
page on a different claim, disambiguate (append a qualifier) rather than overwrite. Find
the page by title; if absent, create it in the drawer matching its topic (use the
bureau.json profiles). Keep distinct claims on distinct pages so each page has a single,
unambiguous trust tier.
- Stamp identity + anchor the claim (the recursion engine). Ensure every page you touch —
created OR updated — carries an opaque
id: (mint one if absent; never change an existing one),
and anchor its claim sentence with a ^span (e.g. ^ssot-claim). This makes the page
rename-safe and gives dependents something stable to point at. A page you update that predates
the engine (no id/^span) is retrofitted here — add both.
- Declare dependencies — propose
rests_on. For each page, ask: does its claim rest on
another dossier's claim (it assumes it, builds on it, cites it as its basis)? If so, add a
rests_on object edge naming that [[page]], its ^span, and a one-line because. Declare
generously — a missing edge is silent staleness the gate can never catch; a spurious one only
costs a review click. These are proposals; the human confirms them at bureau:review.
- Write provenance. Add the source minute to the page's body
**Sources.** line
(a [[session …]] link). Never drop an existing source. Set updated: to today.
- Set the trust tier. A claim about a checkable artifact (a path, a build command, a
function signature, a config value, a dependency version, a commit) is confirmed against the
live repo. Before reading any path from a claim, resolve it and confirm it stays inside
the repo/workspace — reject absolute paths,
.. escapes, and symlinks that point outside;
read only contained paths. If the claim holds, set status: verified, add a body
**Verified.** line naming the artifact and date, and record the fingerprint by running the
bundled press:
node "${CLAUDE_PLUGIN_ROOT}/press/bin/gazette.mjs" ledger verify --dir <workspace> --page "<title>" --artifact <repo-relative-path> --claim "<what>"
(it writes <workspace>/_verify.json in code — never hand-edit it; the schema below is for
reference). Everything else — judgments, rationale, anything not mechanically checkable —
stays status: proposed. Never write canonical (that is bureau:review).
- Apply the conflict policy (below) whenever a new claim disagrees with a page's current
claim.
- Scan + structural check. First record the new/changed claim spans into the decision log so
the gate can flag downstream drift:
node "${CLAUDE_PLUGIN_ROOT}/press/bin/gazette.mjs" scan --dir <workspace>. Then run
bureau:inspect (press build + health) and
node "${CLAUDE_PLUGIN_ROOT}/press/bin/gazette.mjs" gate --dir <workspace>. Report the dossier
count, any dangling/orphan/contradiction findings, AND any pages the gate now marks
needs-review (they rested on a claim this compile changed) — those go to bureau:review.
- Mark compiled — only on success. ONLY after the writes and the structural check succeed,
record each processed session id by running
node "${CLAUDE_PLUGIN_ROOT}/press/bin/gazette.mjs" ledger mark-compiled <session-id> … --dir <workspace>
(it writes <workspace>/_compile-state.json in code, idempotently — do not hand-edit it). A
failed inspect must leave the session un-compiled so the next run retries it, not skips broken output.
- Report. List pages created, pages updated,
rests_on edges proposed, pages left proposed
(awaiting bureau:review), any set to contested, and any newly needs-review, with the
command to inspect them.
Retrofitting an existing canon (one-time)
A canon created before the engine has dossiers with no id, no ^span, and no rests_on. To adopt
the engine, do a one-time sweep the first time you compile it (in addition to the per-minute work
above): for every existing dossier, stamp an opaque id: and anchor its claim with a ^span;
then read the prose for dependencies the author already implied (a page that links [[Other]] and
builds on its claim rests_on it) and propose those edges with a because. Finish with a scan.
This is incremental and safe — nothing is promoted; the human confirms the proposed edges at
bureau:review. Declare edges generously: the sweep is your one chance to capture the dependency
structure the prose already encodes.
_verify.json schema (code-owned)
Written by gazette ledger verify (press engine/ledgers.mjs), never by hand. Keyed by page
title, so bureau:review can map a fingerprint back to the page and re-check it
(gazette ledger recheck --dir <workspace> --page "<title>"):
{
"<page title>": {
"verifiedAt": "<YYYY-MM-DD>",
"checks": [
{ "artifact": "<repo-relative path>", "hash": "<sha256>", "claim": "<what was confirmed>" }
]
}
}
Conflict policy
A new claim that disagrees with a page's existing claim is never silently overwritten.
Instead:
- set the page
status: contested;
- keep both claims in the body, each with its own
[[session …]] provenance;
- add a typed
contradicts: edge naming the other page — a single line:
contradicts: [[Other page]] (for 2+, one comma list contradicts: [[A]], [[B]],
deduped — keep it single-line so the edge reads as one diff). Add the reciprocal edge
on the other page. the press's health lane then renders the contradiction;
- name the conflict in the report so the human resolves it.
Resolution is a human act: once the user picks the true claim, the losing claim is removed
(its provenance stays in the logbook) and the page goes back through bureau:review to reach
canonical.
Rules
- Cabinets only. Compile writes dossiers and records the ledgers via the bundled press's
gazette ledger command (the code owns _compile-state.json and _verify.json — do not
hand-write them). It never edits minutes — the logbook is append-only history.
- Provenance is mandatory. Every claim added to a cabinet traces to a
[[session …]]
source in the page body. No orphan claims.
- No silent overwrite. Disagreement triggers the conflict policy, never a quiet replace.
- Idempotent. Re-running skips already-compiled sessions; the same logbook yields the
same cabinets.
Examples
Scope note
This skill covers ONLY distillation: minutes → dossiers with provenance. It does
not capture sessions (that is the capture skill / bureau:file-session), does not
render the gazette (that is bureau:inspect), and does not run the semantic consistency
sweep (that is bureau:lint). It is invoked by the bureau:compile command.
1---2name: compile3description: Distil minutes into consistency-checked dossiers — the canonical SSOT — writing provenance links back to the sessions that introduced each claim. Use when running bureau:compile, or when the user asks to turn the logbook into canon / update the cabinets / build the knowledge base from sessions.4---56# Compile — logbook → dossiers (the canon)78Read the append-only logbook and distil its claims into **dossiers**: the canonical,9consistency-checked SSOT a human inspects. Each cabinet claim carries a provenance link back10to the minute that introduced it, so the canon is traceable and regenerable.1112The logbook is faithful but low-authority and chronological; the cabinets are authoritative13and topical. Compile is the bridge — it consolidates many sessions into one current truth.1415## Inputs1617- The workspace (discover a `bureau.json`; default `canon`).18- The set of minutes to process: every entry under `logbook/` whose session id is not19 yet recorded in `<workspace>/_compile-state.json`, narrowed by `--since <YYYY-MM-DD>` when20 given. `_compile-state.json` is the watermark: `{ "compiled": ["<session-id>", …] }`. It is21 underscore-prefixed, so the press never renders it.2223## Cabinet page schema2425A dossier is one markdown file in a topic drawer (`decisions/`, `architecture/`,26`characters/`, …) holding **one claim** (see step 4 — one claim per page keeps a page's trust27tier unambiguous). Frontmatter uses the press's simple parser: the **title is unique and28unquoted** and a scalar value contains no `"`, newline, or `[ ] |`. The only values that carry29`[[ ]]` are **relation edges** like `contradicts: [[Other page]]`. Frontmatter takes flat30`key: value` lines, inline lists (`tags: [a, b]`), and multi-line lists of scalars; values are31always strings (no YAML type coercion), and nested maps or block scalars are **rejected** — the32build fails. Provenance is a `[[wiki-link]]` to a minute: an edge the press indexes, so the minute33gets a backlink showing which dossiers it produced. A frontmatter `sources:` list of wiki-links34would index too, but **compile writes it in the body**, as a `Sources` line — one shape, easy to35review. `gazette health` reports any tiered page with no provenance link as **unsourced**:3637```markdown38---39id: 01J9Z8QKQ7ULIDEXAMPLE40title: SSOT model41updated: 2026-06-1042status: proposed43rests_on:44 - { page: "[[Logbook model]]", span: "^authority", because: "the SSOT split assumes the logbook is low-authority" }45---4647# SSOT model4849The wiki is authoritative for current truth; the logbook is low-authority provenance. ^ssot-claim50See [[Logbook model]].5152**Sources.** [[session a1b2c3d4 · 2026-06-10]]53```5455- **`id`** is an opaque, immutable identifier — stamp one on **every** dossier (a ULID, or any56 unique token; e.g. `pg-<slug>-NNNN`). It is the page's identity, so a later rename never breaks a57 dependency that points at it. A page with no `id` falls back to a title-derived shim that *does*58 break on rename — so always author one.59- **The claim carries a `^span`** — a `^anchor` at the end of the claim line (`^ssot-claim` above).60 This is what a dependent page points at. Anchor exactly the sentence(s) another page could depend61 on; keep it stable so cosmetic edits elsewhere don't churn it.62- **`rests_on`** declares a dependency: when *this* dossier's claim relies on *another* dossier's63 claim, add an object edge naming the target `[[page]]`, its `^span`, and a `because`. The64 recursion engine then flags this page `needs-review` whenever that upstream span changes. These65 edges are **proposed** by compile and **confirmed by the human at review** — declare them66 generously (under-scoping is the silent killer; over-scoping only annoys). Omit `rests_on` for a67 standalone claim.68- `status` is the trust tier (defined in the `review` skill). Compile writes only `proposed`69 (an AI claim, unchecked) or `verified` (a fact it confirmed against the repo). It **never**70 writes `canonical` — that tier is reached only through `bureau:review`, the human gate. A71 conflict yields `contested` (see the conflict policy).72- The body `**Sources.**` line wiki-links the minutes that justify this page, each by73 its title (`session <session-id> · <date>`). This is the provenance — the press renders it as a74 backlink, so each session shows which dossiers it produced, and the page lists the75 sessions that justify it. A claim that disagrees keeps its own inline `[[session …]]` link.7677## Steps78791. **Locate the workspace** (`bureau.json`; default `canon`). If none, tell the user to run80 `bureau:init` first and stop.812. **Select entries.** List minutes not in `<workspace>/_compile-state.json` (apply82 `--since`). If none remain, report "cabinets already current" and stop.833. **Extract claims.** For each selected entry, read its Decisions and Changes. Each yields a84 claim and the dossier it belongs to (the entry names the target page).854. **Place each claim — one claim per page.** Derive the page title; **enforce the title86 rules**: NFC-trim, strip any `[ ] |` and quotes, and if the title collides with an existing87 page on a *different* claim, disambiguate (append a qualifier) rather than overwrite. Find88 the page by title; if absent, create it in the drawer matching its topic (use the89 `bureau.json` profiles). Keep distinct claims on distinct pages so each page has a single,90 unambiguous trust tier.915. **Stamp identity + anchor the claim (the recursion engine).** Ensure every page you touch —92 created OR updated — carries an opaque `id:` (mint one if absent; never change an existing one),93 and anchor its claim sentence with a `^span` (e.g. `^ssot-claim`). This makes the page94 rename-safe and gives dependents something stable to point at. A page you update that predates95 the engine (no `id`/`^span`) is **retrofitted here** — add both.966. **Declare dependencies — propose `rests_on`.** For each page, ask: does its claim *rest on*97 another dossier's claim (it assumes it, builds on it, cites it as its basis)? If so, add a98 `rests_on` object edge naming that `[[page]]`, its `^span`, and a one-line `because`. Declare99 generously — a missing edge is silent staleness the gate can never catch; a spurious one only100 costs a review click. These are **proposals**; the human confirms them at `bureau:review`.1017. **Write provenance.** Add the source minute to the page's body `**Sources.**` line102 (a `[[session …]]` link). Never drop an existing source. Set `updated:` to today.1038. **Set the trust tier.** A claim about a checkable artifact (a path, a build command, a104 function signature, a config value, a dependency version, a commit) is confirmed against the105 live repo. **Before reading any path from a claim, resolve it and confirm it stays inside106 the repo/workspace** — reject absolute paths, `..` escapes, and symlinks that point outside;107 read only contained paths. If the claim holds, set `status: verified`, add a body108 `**Verified.**` line naming the artifact and date, and record the fingerprint by running the109 bundled press:110 `node "${CLAUDE_PLUGIN_ROOT}/press/bin/gazette.mjs" ledger verify --dir <workspace> --page "<title>" --artifact <repo-relative-path> --claim "<what>"`111 (it writes `<workspace>/_verify.json` in code — **never hand-edit it**; the schema below is for112 reference). Everything else — judgments, rationale, anything not mechanically checkable —113 stays `status: proposed`. Never write `canonical` (that is `bureau:review`).1149. **Apply the conflict policy** (below) whenever a new claim disagrees with a page's current115 claim.11610. **Scan + structural check.** First record the new/changed claim spans into the decision log so117 the gate can flag downstream drift:118 `node "${CLAUDE_PLUGIN_ROOT}/press/bin/gazette.mjs" scan --dir <workspace>`. Then run119 `bureau:inspect` (press build + health) and120 `node "${CLAUDE_PLUGIN_ROOT}/press/bin/gazette.mjs" gate --dir <workspace>`. Report the dossier121 count, any dangling/orphan/contradiction findings, AND any pages the gate now marks122 `needs-review` (they rested on a claim this compile changed) — those go to `bureau:review`.12311. **Mark compiled — only on success.** ONLY after the writes and the structural check succeed,124 record each processed session id by running125 `node "${CLAUDE_PLUGIN_ROOT}/press/bin/gazette.mjs" ledger mark-compiled <session-id> … --dir <workspace>`126 (it writes `<workspace>/_compile-state.json` in code, idempotently — **do not hand-edit it**). A127 failed inspect must leave the session un-compiled so the next run retries it, not skips broken output.12812. **Report.** List pages created, pages updated, `rests_on` edges proposed, pages left `proposed`129 (awaiting `bureau:review`), any set to `contested`, and any newly `needs-review`, with the130 command to inspect them.131132### Retrofitting an existing canon (one-time)133134A canon created before the engine has dossiers with no `id`, no `^span`, and no `rests_on`. To adopt135the engine, do a **one-time sweep** the first time you compile it (in addition to the per-minute work136above): for **every** existing dossier, stamp an opaque `id:` and anchor its claim with a `^span`;137then read the prose for dependencies the author already implied (a page that links `[[Other]]` and138*builds on* its claim `rests_on` it) and propose those edges with a `because`. Finish with a `scan`.139This is incremental and safe — nothing is promoted; the human confirms the proposed edges at140`bureau:review`. Declare edges generously: the sweep is your one chance to capture the dependency141structure the prose already encodes.142143### `_verify.json` schema (code-owned)144145Written by `gazette ledger verify` (press `engine/ledgers.mjs`), never by hand. Keyed by page146title, so `bureau:review` can map a fingerprint back to the page and re-check it147(`gazette ledger recheck --dir <workspace> --page "<title>"`):148149```json150{151 "<page title>": {152 "verifiedAt": "<YYYY-MM-DD>",153 "checks": [154 { "artifact": "<repo-relative path>", "hash": "<sha256>", "claim": "<what was confirmed>" }155 ]156 }157}158```159160## Conflict policy161162A new claim that disagrees with a page's existing claim is **never silently overwritten**.163Instead:164165- set the page `status: contested`;166- keep both claims in the body, each with its own `[[session …]]` provenance;167- add a typed `contradicts:` edge naming the other page — a **single line**:168 `contradicts: [[Other page]]` (for 2+, one comma list `contradicts: [[A]], [[B]]`,169 deduped — keep it single-line so the edge reads as one diff). Add the reciprocal edge170 on the other page. the press's health lane then renders the contradiction;171- name the conflict in the report so the human resolves it.172173Resolution is a human act: once the user picks the true claim, the losing claim is removed174(its provenance stays in the logbook) and the page goes back through `bureau:review` to reach175`canonical`.176177## Rules1781791. **Cabinets only.** Compile writes dossiers and records the ledgers via the bundled press's180 `gazette ledger` command (the code owns `_compile-state.json` and `_verify.json` — do not181 hand-write them). It never edits minutes — the logbook is append-only history.1822. **Provenance is mandatory.** Every claim added to a cabinet traces to a `[[session …]]`183 source in the page body. No orphan claims.1843. **No silent overwrite.** Disagreement triggers the conflict policy, never a quiet replace.1854. **Idempotent.** Re-running skips already-compiled sessions; the same logbook yields the186 same cabinets.187188## Examples189190<example>191Context: Three sessions have been filed but the cabinets are out of date.192user: "bureau:compile"193assistant: "3 uncompiled sessions. I distilled their decisions into dossiers — created **Capture design** and **SSOT model** under decisions/, updated **Logbook model**, each with a body **Sources.** line linking the session that introduced it. The build command claim verified against package.json (status: verified); the design rationale stays proposed for bureau:review."194<commentary>Facts checkable against the repo become verified; judgments stay proposed for the human gate. Provenance is a body link so it shows as a backlink.</commentary>195</example>196197<example>198Context: A new session asserts a token TTL that conflicts with an existing page.199user: "Update the cabinets from the latest session."200assistant: "The new claim (tokens last 1h) disagrees with **Token TTL** (24h). Per the conflict policy I did not overwrite — I set the page status: contested, kept both claims with their provenance, and added a contradicts: edge so the press's health flags it. Resolve it in a session, then recompile."201<commentary>Disagreement never silently overwrites — it surfaces as a contested page with a contradicts edge for the human.</commentary>202</example>203204## Scope note205206This skill covers ONLY distillation: minutes → dossiers with provenance. It does207**not** capture sessions (that is the `capture` skill / `bureau:file-session`), does **not**208render the gazette (that is `bureau:inspect`), and does **not** run the semantic consistency209sweep (that is `bureau:lint`). It is invoked by the `bureau:compile` command.