Module 50 · 45 min

A Budget, Not a Finish Line

You can run a maintenance pass that stops at a number you chose, and explain why "keep going until the wiki is clean" is the wrong termination rule for a job a model does.

Surface
commands/dream.md · lint --review-limit
Ships to your plugin
commands/dream.md
Claude Code
v2.1.251
Docs checked
2026-09-02

A wiki decays whether or not anyone touches it. Sources get superseded, links break when a page is renamed, two pages drift into disagreeing, and pages nobody has looked at in a year quietly stop being true.

So you want a maintenance pass. The obvious way to write one is to have the model work through the findings until there are none left, and I want to show you why that instruction has a failure built into it. Fixing a page can produce new findings. Rewrite a page and its links change. Archive a page and every page that linked to it now has a broken link. “Until clean” is a loop whose exit condition the loop itself keeps moving.

The plugin’s answer is the last line of commands/dream.md, verbatim: “Termination is defined by the page-read budget, not by ‘until clean’ and not by elapsed time.”

One unit is one page opened

/dream --max 10

--max 10 is not a count of fixes and not a time limit. It is a hard ceiling on how many wiki knowledge pages may be opened for review in the whole pass. That is the only thing it counts.

The accounting, from SKILL.md:

ActionUnits
Open a wiki knowledge page to review it1
Open another wiki page to compare against it1
Read wiki/index.md0
Read wiki/log.md0
Read a raw source0
Run any wikictl command0

Look at row two, because that rule is the one doing the real work. Opening a second page to compare costs another unit. Without that, a pass reviews ten pages and reads forty more to check them against, and your budget of ten meant nothing. The unit is a page read, whatever the reason for reading it.

The free reads are free for a reason. index.md is a map, raw sources are evidence, and wikictl output is bookkeeping. None of those are the expensive thing, and charging for them would push a pass toward working blind.

The queue is built before anything is opened

Next up is the order, and there is one non-obvious rule in it.

Step 4 of the command builds an ordered list of pages without reading them, then truncates it to the budget. Building first and reading second is what makes the budget mean something. Read as you go and you have already spent the budget by the time you discover which pages mattered most.

Order, highest priority first:

  1. Pages stale named, meaning they cite an explicitly superseded source
  2. Pages named by a deterministic lint finding: broken links, missing frontmatter or fields, unknown source IDs, source hash mismatches
  3. Archive candidates
  4. semantic_review_candidates from lint, oldest last_linted first

The first three are pages where something is known to be wrong. The fourth is the sampling tier, and it is where --review-limit comes in.

llm-wiki/
└─ commands/
commands/dream.md

Twelve steps. Step 1 stops the whole pass on an integrity violation before any page is opened.

---
description: Run bounded LLM Wiki maintenance without ingesting sources.
argument-hint: "[--max N]"
disable-model-invocation: true
---

Run bounded maintenance. Never ingest or register a new raw source.
Parse arguments for `--max N`; default to 10.

1. Run `wikictl inventory`.
 - If `changed` or `deleted` is non-empty, stop: append-only integrity is violated.
 - If `new` contains unregistered files, note them but do not register or ingest them.
2. Run `wikictl stale`.
3. Run `wikictl lint --review-limit N`.
4. Build one ordered, deduplicated page queue without reading those pages yet:
 1. pages from `stale.pages`;
 2. pages named by broken links, missing fields, unknown sources, hash mismatches;
 3. archive candidates;
 4. `semantic_review_candidates` in the order emitted (oldest `last_linted` first).
5. Truncate the queue to N unique wiki knowledge pages.
6. Process the queue. One opened page consumes one unit. Opening another page to
 compare also consumes one unit and must fit in the remaining budget.
 `index.md`, `log.md`, and raw evidence reads do not consume units.
7. For each queued page, apply only relevant work:
 - stale evidence: read the superseding source and reconcile;
 - broken references or provenance: repair what is deterministically supported;
 - contradiction review: use judgment, but do not scan the entire wiki;
 - archive candidate: archive only if justified, then repair inbound links.
8. Set `last_linted` to today on every page actually reviewed, even if unchanged.
9. Append one maintenance entry to `wiki/log.md` with the budget, pages reviewed,
 fixes, and archives. Do not log or imply that unreviewed pages were checked.
10. Run `wikictl stale` and `wikictl lint --review-limit 0` again to validate the edits.
11. If plugin-owned files changed, run `wikictl commit`.
12. Report pages reviewed versus budget, work completed, remaining findings, and any
  unregistered raw sources waiting for `/wiki-ingest`.

Termination is defined by the page-read budget, not by "until clean" and not by elapsed time.
The maintenance command. Steps 4 to 6 are the budget; step 8 is the bookkeeping that makes the next pass pick different pages.

Sampling, and why the date field earns its place

lint --review-limit N asks for at most N pages worth looking at even though nothing deterministic is wrong with them. Its selection rule is emitted in the output as review_policy: "oldest_last_linted_first", so the caller never has to guess how the sample was drawn.

That is where last_linted from module 47 pays off. Step 8 sets it to today on every page a pass actually opened, whether or not anything was edited, and that single write is what makes the sampler fair. A page reviewed today goes to the back of the queue. A page nobody has opened in eight months rises to the front. Run /dream --max 10 once a week and the wiki gets swept in rotation without you tracking anything.

Skip step 8 when no edit was needed, and the same untouched pages come back to the top of the queue every single run, while the pages nobody has read stay unreviewed forever. Reviewed and found correct has to be recorded, or the rotation collapses onto whichever pages happen to be broken.

That is it. One number, one unit, one ordered queue, and a date field that keeps the rotation turning.

Two stopping rules that look reasonable and are not

Elapsed time. A ten-minute pass does an unpredictable amount of work, because how far ten minutes goes depends on page size, model speed, and how much repair each page needs. Two runs of the same command cover different ground and you cannot tell which. Until clean. Every fix can create a finding, so the target moves while you approach it. A page count is the only one of the three you can state before the run and check after it.

Ships to your plugin

Write commands/dream.md with disable-model-invocation: true, then add —review-limit N to lint in scripts/wikictl.py: return at most N pages under semantic_review_candidates, sorted by last_linted ascending, and emit review_policy in the JSON so the caller can see the rule it is relying on. Then check the rotation over two runs. Note which pages the first pass reviews, run it again, and confirm the second pass picks different ones. If it picks the same pages, step 8 is not writing last_linted, and your sampler is stuck.

The mistake most people make first

Letting maintenance ingest. There is an unregistered file sitting in raw/, inventory reports it, and registering it while you are in there looks tidy. What you get is a pass that cannot be budgeted, because ingestion is unbounded work: one source can touch six pages, each of which now wants review, and the number you passed to —max has stopped describing anything. What you see afterwards is a maintenance log entry claiming a ten-page pass beside a commit touching twenty. Step 1 handles this by noting the unregistered files and leaving them alone, and step 12 reports them so they are visible without being acted on. Maintenance and ingestion are separate commands because they have separate termination rules.

Does this travel?

The idea is general and the need for it grows with every agent you run: work a model does needs a termination rule it cannot move, and a countable unit is the only kind that holds. Pages here, files in a review, rows in a batch, tool calls in a loop; the shape is the same and so is the failure when it is missing. What does not travel is the specific accounting, since one unit equals one page read only makes sense for a wiki. Pick the unit that matches your work, charge for the reads that are actually expensive, and make the free list explicit so nobody has to guess.

Check yourself

  1. --max 10 is spent after reviewing six pages. Explain how, using the accounting table.
  2. Step 8 writes last_linted even when a page needed no edit. What stops working over repeated passes if you skip that write for unchanged pages?
  3. Step 1 stops the whole pass when inventory reports a changed raw file, before any page is opened. Why is that the correct order rather than reviewing pages first and reporting the violation at the end?