Skip to content

Build master @ bc50cd5bunreleased — 31 commits past v1.282.0Last reviewed Last changed Grade fresh (aging >90d, stale >180d)Source / History

Wiki Store ​

The wiki holds what BoB has learned about its own tooling and skills: one page per failure mode, strategy or workaround, each citing the traces it came from. The skill proposer reads it to decide what to change. It lives in BOB_SOURCE, is checked by make check, retires what stops being true, and never carries a client fact (#2388, part of #2387).

Why the wiki exists and how it fits the skill-evolution loop is the design in Skill evolution: traces, a wiki and skills. This page is the contract the tools enforce.

Layout ​

wiki/
├── patterns/<slug>.md   authored  — one page per pattern
├── index.md             derived   — generated by scripts/wiki/index.js
├── logs.md              appended  — one row per maintainer run
├── skill-impact.md      appended  — one row per skill proposal
└── .maintain-state.json written   — the maintainer's watermark
PathWritten byRule
patterns/<slug>.mdThe maintainer (#2389), through a PR a person mergesLowercase kebab slug. Edits are patches: merge evidence, bump count, extend last_seen. A page is never deleted.
index.mdnode scripts/wiki/index.jsNever edited by hand. make check fails when it does not match the pages.
logs.mdThe maintainer runOne row per run, appended.
skill-impact.mdThe gate harness, not a modelOne row per proposal, appended.
.maintain-state.jsonThe maintainer runThe last record consumed per project and store; see the maintainer run.

wiki/ is not deployed: deploy.sh syncs named surfaces into BOB_HOME, and the wiki is not one of them.

Page frontmatter ​

A page starts with frontmatter that schemas/wiki-page.schema.json validates, then a # Title line and the text.

markdown
---
kind: failure-mode
skills: [warp-drive, commit]
evidence:
  - paulirv/bigbrain#1856
  - loop-note:3f2a9c01b7de
first_seen: 2026-09-12
last_seen: 2026-10-01
count: 3
defect: paulirv/bigbrain#1856
status: active
---

# The docs hook does not fire when there is nothing to tick
KeyRequiredValue
kindyesfailure-mode (what goes wrong and why), strategy (what worked) or workaround (a low-level fix that helped one model in one harness — a proposer treats it as evidence, not as a step to put in a skill)
skillsyesOne or more names of skills, commands or agents in this repo (skills/, commands/, agents/ or their registry/ twins)
evidenceyesOne or more trace references, in the forms below. Never copied trace text.
first_seen, last_seenyesYYYY-MM-DD; first_seen is not after last_seen
countyesTimes seen, 1 or more. A recurrence bumps it; it never becomes a second page.
defectnoowner/repo#n: the bug the pattern describes or works around. The page retires when it closes.
statusyesactive or retired
retired_reasonnoWritten by retire.js when it retires the page

The frontmatter is a strict YAML subset: key: value, key: [a, b], or key: followed by - item lines. Anything else is a lint error, so every reader parses a page the same way.

Evidence ref forms ​

FormPoints at
#N, owner/repo#NAn issue: a lesson, a decision, a harness defect, a session report
loop-note:<12 hex>A loop note, by its signature()
ledger:<12 hex>A session-ledger record, by the first 12 hex of the SHA-1 of its line (ledger records carry no id)
session:<id>A harness session id, 8 or more of A-Z a-z 0-9 _ -
ext:<kind>:<hash>A record in another project's store, opaque on purpose: the kind of trace and a 12–64 hex hash. A client's repo name, path or issue URL never appears in a ref.

The lint checks the form. Resolving a ref is the reader's job.

The lint ​

make check-wiki (part of make check) runs node scripts/wiki/lint.js. --root DIR grades another checkout; --json prints wiki-lint/1. Exit 0 is clean, 1 is findings, 2 is a usage or config error.

RuleFails when
pageA page has no frontmatter, fails the schema, has a slug that is not lowercase kebab, has no evidence, cites a ref outside the forms above, names a skill that does not exist, or has first_seen after last_seen
indexwiki/index.md differs from what index.js renders from the pages
loglogs.md or skill-impact.md is missing, its header is not exact, or a row breaks the format below
injectA file in hooks/, templates/hooks/, commands/, registry/commands/ or a provisions/*.json manifest names wiki/
tenantA path or line under wiki/ contains a tenant literal
credentialA line under wiki/ matches the credential catalogue and is not a placeholder; the match is printed masked to four characters
allowscripts/checks/release-content-allow.json has an entry covering wiki/

The two tables ​

Both are markdown tables with a fixed header. A table with no rows is valid: that is how the repo ships them.

logs.md, one row per maintainer run:

markdown
| Date | Sources | Read | Refused | Created | Updated |
|------|---------|------|---------|---------|---------|
| 2026-10-09 | loop-notes, ledger | 12 | 1 | — | docs-hook-silent |

Sources is a comma list of trace store names. Read and Refused (the records the client-data scrub refused) are whole numbers. Created and Updated are comma lists of existing page slugs, or — for none.

skill-impact.md, one row per proposal:

markdown
| Date | Skill | PR | Pages cited | Score before | Score after | Outcome |
|------|-------|----|-------------|--------------|-------------|---------|
| 2026-10-09 | commit | #4300 | docs-hook-silent | 0.50 | 0.62 | accepted |

Skill is an existing skill, command or agent. PR is #N or owner/repo#N. Pages cited is a non-empty list of existing slugs. The scores run 0 to 1: before is the best validation score without the change, after is the score with it. Outcome is accepted or rejected, and accepted requires after to be strictly higher than before, the eval gate's rule (#2390).

The index ​

bash
node scripts/wiki/index.js            # write wiki/index.md
node scripts/wiki/index.js --check    # exit 1 when the committed index is stale

One line per active page, grouped by kind and sorted by slug: title, skills, count, last seen and defect. Retired pages are left out.

Retirement ​

bash
node scripts/wiki/retire.js                       # report: what would retire, what is stale
node scripts/wiki/retire.js --apply               # retire pages whose defect closed
node scripts/wiki/retire.js --max-age-days 90     # staleness threshold (default 180)
RuleWhat happens
Defect closedAn active page whose defect: issue is closed retires. The state comes from scripts/lib/defect-status.js, cached for 6 hours. A forge that cannot be reached gives unknown, which counts as open: a page never retires on a failed lookup. --apply sets status: retired, adds a retired_reason: line, changes nothing else, and regenerates the index.
StaleAn active page whose last_seen is older than the maximum age is listed. Nothing changes, even with --apply: an old pattern may still be true. BOB_WIKI_STALE_DAYS sets the default age.
RecurrenceThe maintainer's job: a repeat is new evidence on the existing page, not a new page, and a retired page that recurs is reopened.

A retired page stays in patterns/ and in git history. --json prints wiki-retire/1. Exit 0 means nothing to retire, or retired with --apply; 1 means pages would retire on a dry run.

The maintainer run ​

bob wiki maintain (scripts/wiki/maintain.js, #2389) writes patterns/, logs.md, index.md and the watermark. /trace-mining and bob tick call it. The how-to is Consolidate traces into the wiki.

bash
bob wiki maintain [--project DIR] [--source DIR] [--dry-run] [--max-records N] [--max-chars N] [--since YYYY-MM-DD] [--json]
StepContract
CollectOne project's stores: loop-notes, decision-log, harness-defects, ledger (no-trigger, improvised), lessons (open), session-reports. Only records after the watermark; a store with no watermark starts at --since (default 30 days back). Each record carries a ref in an evidence form: bigbrain records are cited directly, any other project's only as ext:<kind>:<hash>. A bigbrain project is any checkout or worktree sharing BOB_SOURCE's git directory.
ScrubA record from another project that contains a tenant literal is refused. A record matching the credential catalogue is refused, from any project. With no operator resolved, every record from another project is refused. Refused records are counted and consumed, never written.
BoundOldest first, at most --max-records (8) kept records, each cut to --max-chars (15,000). The rest waits for the next run.
ConsolidateOne claude -p call with tools disallowed. The prompt is registry/agents/wiki-maintainer.md, the known items, the current index and pages, and the packet, all on stdin. The answer is one JSON object {edits: [...], notes}. BOB_WIKI_MAINTAINER names another command with the same contract.
ApplyThe script owns the frontmatter. create needs a kebab slug, a kind, at least one existing skill, a title and at least one packet ref; first_seen, last_seen and count come from the cited records. update merges new refs, bumps count by each ref added or recurring, extends last_seen, appends the optional text as a dated paragraph, and reopens a retired page with a note. A ref the page already cites recurs when its record is newer than last_seen. An unknown op, an invented ref, an unknown skill or an update of a missing page is refused and listed in the PR. Nothing is deleted or rewritten.
ProposeThrough a worktree on wiki-maintain/<date>-<project key>-<hash> from the main branch: the edits, the watermark, one logs.md row and the regenerated index. The wiki lint grades the result first; a finding under wiki/ stops the run. Then one commit (through the repo's hooks), a push and one PR with the packet summary. The checkout is never touched and master never receives a commit.

The watermark is wiki/.maintain-state.json: {version, projects: {<key>: {<store>: {ts, key}}}}, where <key> is bigbrain or ext-<12 hex> (a hash of the project's origin, never its name). The run also mirrors it to $BOB_HOME/.wiki-maintain-state.json, and each store reads the later of the two positions. The mirror covers a run whose PR is still open on this machine; the committed copy covers every machine after the merge. A run over the same inputs is therefore a no-op: no agent call, no branch, no PR, no log row.

ExitMeaning
0opened (a PR), noop (nothing new) or dry-run
1the run failed (the agent, the lint, the push or the PR); nothing is consumed and the next run retries
2usage or config: no BOB_SOURCE checkout, or no wiki store in it

--json prints wiki-maintain/1: verdict, project {key, bigbrain}, per-store sources {new, read, refused, error}, read, refused, left_over, the packet (refs, never text), refusals, watermark, created, updated, reopened, rejected, pr, branch and reason.

Client data ​

The wiki is in the product repository, and every client installs from a tag of it. A client project's traces may teach the wiki about a BoB item; no client fact may reach it — no client code, path, issue text or name.

Two gates hold that line:

  • The lint, in make check, runs the release content gate's tenant and credential rules over every file under wiki/. The tenant literals come from the operator profile's tenants[]. With no operator resolved, the lint prints tenant rule: not run and grades everything else.
  • The release content gate (reference) reads every tracked file at release time, wiki/ included, and refuses a release when no operator resolves. The lint fails if its allow-list ever exempts wiki/.

Who reads the wiki ​

Only the skill proposer (#2391). No SessionStart hook injects it, no command loads it, and it is not a provisioned item. The paper behind the design found that an agent with the wiki in its context solves tasks from the wiki, so its traces stop showing what the skills lack. The lint's inject rule keeps it that way.

See also ​