Module 35 · 50 min

Skills That Keep the Wiki Honest

You can give the agents a capture skill that routes a new fact to the right page, checks the one-place rule, shows a diff and waits for a yes, and a questions file that stops a confident wrong answer.

Surface
SKILL.md · the plugin's skills surface
Workbench tag
module-35
Claude Code
v2.1.251 (bundled)
claude-agent-sdk
v0.3.251
Docs checked
2026-08-30

Module 34 built the wiki and its rules by hand. Now you hand the agents the pen, with the tool the Learn track taught in module 05: a skill, a reusable workflow the model loads when its moment arrives.

Two skills ship. One rule governs both, and it is the module’s whole idea in a sentence: a skill names the file to read; it never carries the fact. A fact copied into a skill body looks helpful and goes stale silently, because a skill body is text the model reads and nothing checks it. The schema has a lint. The skill has nothing. So the skill points, and the schema stays the one place the rules live.

flowchart LR
fact["a fact arrives<br/>in chat"] --> cap["capture-fact:<br/>read the schema,<br/>find the owning page"] --> diff["diff shown,<br/>wait for yes"] --> wiki["the page,<br/>plus a log line"]
lint["npm run kb<br/>findings"] --> audit["kb-freshness-audit"] --> q["_questions.md<br/>one owner per question"]
Two lanes. A new fact goes through capture-fact into the wiki with a person's yes; the lint's findings go through the audit into the questions file, and never back into pages.

Four files ship, and all four are below in full. Read capture-fact/SKILL.md first, read it the way a model does, top to bottom, obeying as it goes. Then check it against the claim this page is about to make: there is no fact in it, and no digit either.

chat-workbench/
├─ .claude/
│ └─ skills/
│ ├─ capture-fact/
│ └─ kb-freshness-audit/
├─ knowledge-base/
└─ .claude-plugin/
.claude/skills/capture-fact/SKILL.md

The capture workflow. Ordered imperative steps, no rule of the schema restated.

---
name: capture-fact
description: Use when somebody states a fact about this application that is not written down yet, how a part of it behaves, what a setting does, what a run of it produced, and it should become or extend a page in the knowledge base. Intake, one-place check, diff to approve, log line.
---

# Capture a fact into the knowledge base

Do these in order. Do not skip ahead to writing; every step before the write
exists to stop a page being minted that should have been an edit.

## Read the contract first

Open `knowledge-base/_schema.md` and read it before anything else. It states
the layers, the frontmatter, the provenance values and what each of them
obliges the body to carry.

Open it by that repo-root path, typed out. Not by a wikilink: a wikilink is a
convention between pages and resolves to nothing when a tool goes looking for a
file. Not from memory of what the schema says, either, including any memory
formed while reading this file.

Nothing in this skill restates a rule from the schema, and that is the point.
A rule copied here would be a second copy with no one checking it against the
first, and it would keep reading as true for as long as it took somebody to
change the schema and not know this file existed. This file holds the order of
the steps. The schema holds the rules.

## Decide the layer

Ask whether the thing said is a wiki fact at all.

- A fact about how this application behaves, that a reader would want and
cannot get quickly from the source: a page.
- A choice that was made, and the reasons it was made over the alternatives: a
decision record, not a page. The wiki cites it.
- A number or a line that the source already states plainly: leave it in the
source. A page that restates a source is a copy that will drift away from it.
- Something nobody has checked and cannot check right now: a question, filed as
one, not a page wearing a confident frontmatter.

## Find the page that already owns it

Search the wiki before writing. If a page already owns the concept, extend that
page. Do not mint a second page whose title is a synonym of the first.

If two pages could plausibly own it, the one whose concept it actually is takes
the fact, and the other one links to it. If the fact contradicts what a page
already says, stop and follow the schema's procedure for that; the disagreement
is the finding, and resolving it quietly destroys the only thing worth knowing.

## Write it in the owning page's register

Match the page you are editing: its sentence length, its heading shapes, how
much it explains before it asserts. A paragraph in a different voice reads as
an insertion, and a reader who notices the seam stops trusting the page either
side of it.

State the fact first and the evidence under the heading the schema names for
it.

## Set the frontmatter honestly

Copy the block from `knowledge-base/_template.md` rather than typing it from
memory. Fill every required field.

Pick the provenance tier for what actually happened, and not for what would
look better. If a command was run and its output read, say so and quote it. If
a file was read, cite it. If nothing was checked against a source, the schema
has a value that says exactly that, and it is the correct one. Claiming a tier
whose evidence is not in the body is the failure the field was added to catch.

## Run the lint

Run `npm run kb`. Read every finding it prints against the page you touched,
and fix what is yours. The lint checks a subset of the schema; a clean run is
not a claim that the page obeys the rest of it.

## Show the diff and wait

Show the person the exact diff you intend to write and the lint's output, then
stop and wait for them to say yes. Do not write first and offer to revert.

The person who stated the fact is the only one who can tell you that you wrote
down something subtly different from what they meant, and the moment they can
still tell you cheaply is before it is a file. This step is a habit in the
procedure rather than a permission rule because a permission rule can only ask
whether a file may be written; it cannot show anybody what is about to go in
it, and an approval prompt on an unseen edit trains a person to say yes.

## Append the log line

After the write lands, append one line to `knowledge-base/_log.md` in the form
that file states at its top, including its Why. The Why is the half that is
worth something later: what changed is visible in the history, and why it
changed is not.

If the fact was one somebody had already filed as an open question, move that
question to answered in `knowledge-base/_questions.md` in the same round.
Everything module 35 adds, at tag module-35, whole. The capture skill is first because it is the module's subject; the sections below argue about lines you can read here.

The capture skill, step by step

.claude/skills/capture-fact/SKILL.md has a one-line description written around its trigger, somebody states a fact about the app that is not written down yet, and a body of ordered imperative steps: read knowledge-base/_schema.md first, by repo-root path; decide whether this is a wiki fact at all; find the page that already owns it, because extending an owning page beats minting a duplicate every time; write in that page’s register; take the frontmatter from _template.md with honest provenance; run the lint; show the person the diff and the lint output and wait for a yes; append the _log.md line with its Why.

Two of those steps carry the module’s design arguments. The path-not-fact rule is enforced, not requested: the skill body contains no digit at all, and the repo’s own check greps for one. A digit is a deliberately blunt proxy, defended in record 0107, every fact tempting to copy into a skill, a port, a TTL, a ceiling, an exit code, is or contains a number, and a blunt check nobody can argue with beats a precise one with an exception list. The check caught a real draft during the build, which is what a check is for.

The diff-and-wait step lives in the skill, not in permissions, which sounds backwards until you name the failure it guards. A permission prompt asks “may this path be written”. The danger here is a faithful, well-formed write of a subtly wrong fact, which every permission system in this course would happily allow. Only a person reading the diff catches it, so the skill makes showing the diff a step of the workflow itself. Record 0108 states the trade plainly: a body step is discretionary where a permission rule is not, and the skill takes that trade with open eyes.

The audit, and the file that stops confident wrong answers

kb-freshness-audit runs the lint and turns each finding into a one-line question in knowledge-base/_questions.md, grouped by who can answer it. It edits no wiki page, and its own body says so with the reason: the one-field edit that would silence a STALE finding, bumping last-verified without checking anything, is exactly the edit that turns provenance into bookkeeping (record 0110).

_questions.md is machinery, not a page, riding the schema’s underscore exemption on purpose: a question is a fact without a provenance yet, so page frontmatter is unfillable for it (record 0111). Three sections, blocking, open, answered, one owner and one date per line, and answered lines keep their answers instead of vanishing. It ships seeded with one real open question from the app’s own build, whether two SDK subprocesses can share the one pinned config directory, owned by the maintainer, because a questions file that starts empty teaches that it is decoration. The blocking section is where a confident wrong answer dies: an agent asked about something a blocking question covers has, in its context, a file saying the answer is not yet known and who owes it.

Certified without a key, by the harness itself

This round surfaced a fact the whole course benefits from: the SDK ships the real Claude Code binary, and its validator runs offline. claude plugin validate --strict passes against the repo and against the skills tree, no API key involved, which is a structural check by the same program that will load these skills for real. The manifest from module 30 now registers the skills surface, so the plugin carries them; that also namespaces their invocation names, which is why the exact /skill-name spelling stays out of this page and in your own session. What no keyless machine can certify is listed in record 0113 rather than glossed: whether a live session actually opens the schema when told to, stops at the diff, and keeps the audit out of the pages. Those are the first things to watch when you run this track with a key.

git checkout module-35
npm run check      # includes the wiki lint and the no-digits skill check
node_modules/@anthropic-ai/claude-agent-sdk-win32-x64/claude.exe plugin validate --strict .
Build

The ladder:

  1. Run it. The three commands above, then read the two skill bodies. Neither contains a number; now you know why.
  2. Read one file. capture-fact/SKILL.md beside record 0108, and find the step a permission system could not replace.
  3. Change one line and see it. Add a port number to the capture skill’s body “as a helpful example” and run npm run check: the skills check fails and names the rule. Take it back out.
  4. Build. With a real key: state a fact about the app in chat, watch the skill fire, refuse the first diff, and confirm nothing was written and no log line appeared. The refusal path is the half of diff-and-wait that fake certification cannot reach.
The mistake most people make first

You put the port number in the skill body, “so it does not have to look it up every time”. It works, and it keeps working: the skill answers fast, the number is right, everyone is pleased. Then the port changes in config.js, and the skill keeps saying the old number, every session, confidently, forever, because a skill body is text the model reads and nothing checks it. There is no error to see. The wiki’s page updated, the lint is silent, and the one copy of the fact that nothing lints is the one the model reads first.

The fix is the module’s rule: the skill carries the path to _schema.md and the path to the owning page, and pays one extra file read per invocation for the guarantee that what the model states was read from the linted place. That cost is real and small; the silent-stale cost is real and unbounded.

Does this travel?

Skills travel further than anything else on this site, and the one-place rule travels with them: any harness with reusable prompt workflows has this exact staleness trap, and points-not-facts is the cure in all of them. The questions-file pattern travels to any team. What is this SDK’s own: the SKILL.md format, the plugin skills surface, and the offline validator in the bundled binary.

Check yourself

  1. Why does the capture skill’s body contain no digit, what does the check that enforces it deliberately over-fire on, and why was that cost accepted?
  2. A permission rule could require approval before any write to knowledge-base/. Name the failure that rule cannot catch and the diff-and-wait step can.
  3. The audit skill finds twelve STALE pages. What does it write, where, and what is the one edit it must never make?