One decision, one writing desk, three loose ends. The work that remains is three different activities, so it is planned as three tracks rather than one list.
2026-08-22 · corpus guides/peregrine/ — 327 entries · app ~/Developer/Peregrine · follows 2026-08-15-peregrine-machine-target.md
Seven outputs project from one guide, and all three game guides verify clean.
| File | Rows | Owner |
|---|---|---|
vigilance-bank.json | 69 | corpus |
cons.json | 19 + Innocent | corpus |
undercover.json | 6 + 4 | corpus |
playbook.json | 17 | corpus |
heckler.json | 150 | corpus |
strategy.json | 10 | corpus |
bestiary.json | 22 + boss | corpus |
generated-*.json | 281 | to_app_bank.py |
PeregrineCopy.swift stays in Swift on purpose: centralised UI chrome with a Placeholder enum a test asserts against, and the guide's own rule is that entries carry content and never behaviour.
The only task carried over, and it is not a migration. The con count is identical at nine. What the corpus adds is twenty-nine more clean claims.
| clean | con | ratio | |
|---|---|---|---|
| app, shipped | 7 | 9 | 0.8 : 1 |
| corpus | 36 | 9 | 4 : 1 |
The contract requires 4:1 and the shipped bank breaks it. Below one in five, firing at everything is the winning strategy, so the mini game currently trains the opposite of the restraint it exists to teach.
It also nearly triples the bank and changes how a run feels, which is why it sits here rather than in Track C. Sarah decides; nothing else in this plan depends on it. Once decided, the implementation follows the pattern already used five times.
Authored content the corpus can now count, and could not while it was a Swift literal. None of it needs a developer.
The bank deals five rounds a run and holds six, so a single sitting nearly exhausts it. The honest target is seventeen, not nineteen — Fumbler and Doubler are keystroke slips with no reasoning to voice, the same reason they carry no Playbook entry.
Eleven lineup-eligible cons have no round:
reverse · distort · scope · irrel · footnote · early
wrench · invert · splice · dangle · agree
Each round needs a claim, a con line, two honest critiques and a counter. The two-critique rule is load-bearing: with one, the true critique is a lone odd one out and the round reads itself.
Worth doing first. Teach the exporter the denominator is seventeen. It currently reports "13 cons have no undercover round", counting two that never should, so it overstates the gap every time it prints.
recentQueueLimit is 25 and persists across sessions, so a pool smaller than that can sit entirely inside the ledger. fire returns nil rather than erroring, so the symptom is the heckler simply going quiet.
| pool | lines | when it is heard |
|---|---|---|
payoff-exit | 3 | the payoff, §6 |
huh | 5 | first correct after 4+ misses, §3.2 |
post-commit | 6 | after the commit |
respect | 7 | earned by a hot streak |
hesitation | 8 | 12s without a commit |
The spec's own note says these are seed content at roughly half target size. Doubling the five smallest is the shortest path out. Two constraints any new line must pass, both now enforced at the boundary: the Ceiling Rule (§2.2) — mock the play, never the player — and no duplicate line across pools, since the anti-repeat ledger keys on the line itself and one line in two pools mutes in both.
102 distractors now carry an authored mechanism that renders at the reveal, beneath the con line. The corpus text was written analytically ("A student picks this because…") and the game is 1940s noir with a banned vocabulary. The lines have never been read in place.
This needs to be seen before it is edited, which makes it Sarah's first, and it is the only item in this plan with a hard ordering claim.
Seventeen Lineup distractors carry mechanism / drill_rx / error_mode and are discarded at the border, printed on every export. The Wager had an obvious home — the moment the player picks a distractor. The Lineup's reveal narrates the suspect's reasoning instead and never names a chosen option, so there is no equivalent moment.
A design question before it is a code change. Options: attach the mechanism to the segment at breakIdx, where the tell lands; surface it only in the post-round debrief; or decide the Lineup does not want it and drop the fields rather than carry a permanent export warning.
The exporter blocks a rename it cannot see art for and prints the five surfaces that do not follow. Two are reachable only from inside the app: VigilanceResult.conID and ConProgress are SwiftData rows on the learner's device, and ProfileArchive carries conID in four row types, so exported archives stop resolving on import.
A migration keyed on the corpus's previous_ids would close it. Do this only when a rename is actually wanted — building it now is speculative, and the guard already prevents the silent failure. Recorded so the gap is not rediscovered.
Three contract changes a reader would now get wrong: an output can span several families and classes into one file; a target can be an SPM package's Resources and the app bundle rather than Data/; and one directory's contents are owned by two generators, split by file.
Run /cr-blueprint machine-export.
One hard dependency exists in this plan: B3 before any editing of the mechanism text, because the register question cannot be answered from the JSON.
Otherwise A is Sarah's alone, B is writing, C is code. Inside C: C3 first (cheap, and the blueprint actively misleads), then C1 (a standing export warning), then C2 only on demand.
Eleven undercover rounds and roughly thirty heckler lines are authoring tasks. Guessing at hours would turn a writing desk into a burndown.
I have not read LineupGameView's reveal closely enough to say whether the breakIdx segment can carry a second line without crowding the narration. That reading is the first step, not the implementation.
The 45 claims were migrated and authored, and nobody has fired at a 4:1 stream. The ratio is right on paper. Whether 36 clean claims is tedious rather than tense is a thing only playing will answer, which is exactly why it is parked.
High on Tracks A and C, which are well-scoped and rest on facts the exporter now prints. Lower on B, where the work is real and the finish line is editorial: "enough heckler lines" has no test, and doubling the five smallest pools is drawn from the spec's own note rather than from anything measured in play.