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:
| Action | Units |
|---|---|
| Open a wiki knowledge page to review it | 1 |
| Open another wiki page to compare against it | 1 |
Read wiki/index.md | 0 |
Read wiki/log.md | 0 |
| Read a raw source | 0 |
Run any wikictl command | 0 |
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:
- Pages
stalenamed, meaning they cite an explicitly superseded source - Pages named by a deterministic
lintfinding: broken links, missing frontmatter or fields, unknown source IDs, source hash mismatches - Archive candidates
semantic_review_candidatesfromlint, oldestlast_lintedfirst
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.
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. 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.
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.
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.
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.
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
--max 10is spent after reviewing six pages. Explain how, using the accounting table.- Step 8 writes
last_lintedeven when a page needed no edit. What stops working over repeated passes if you skip that write for unchanged pages? - Step 1 stops the whole pass when
inventoryreports achangedraw file, before any page is opened. Why is that the correct order rather than reviewing pages first and reporting the violation at the end?