Peregrine · build report

Season record, writing rules,
and the case one draft

The season is recorded from v4 and invents nothing. Seven authoring locks now run in the exporter and every one is proved against a fixture. Halloran's Books is drafted, lints clean, and is held back from export.

2026-09-16 · source of truth: Solved Case v4 and the floor plan
Peregrine bf7aaeb · PressRaven-Corpus master · PressRaven master — all three level with upstream at start
The Calder Cipher stays the live CASE001. Nothing was exported. No proof document was written.

▸ Case one's three proposed traces — for your ruling

Case 1 carries traces only: no beat, no method memo, no canon document. These three are the whole of what recurs. Each is a proposal, and none is in the corpus as canon.

The name M. Corrigan Holder of a cash house account on Halloran's books. He is a witness, never a suspect — he confirms paying more than the ledger recorded, and that confirmation is what exposes the counter. It is also the reason Peregrine is in his front room at 10:30.

Recurs: Act III, when Halloran's fills a dead man's script.
The date 31 March The quarter-end the confirmations are due against, and the night the visit happens. v4 gives no calendar dates, so this is the one genuinely invented value of the three.

Recurs: v4 says Corrigan "picked the date off her case 1 assignment." This is that date.
The figure Account no. 2214 Corrigan's cash account at Halloran's. A number the player reads, uses and remembers as arithmetic, with no story attached to it.

Recurs: carried onto the Act III pharmacy log line, which makes that log checkable against a case the player already closed.
What to check before any of the three is real

31 March is invented. v4 fixes no calendar anywhere, and burn night's hour-by-hour timeline is itself marked assumed. If the season wants a different month the date moves and nothing else does.

2214 must not collide with SP-14 or the credential format. The corpus already carries "Standing Procedure 14" and a credential written P-[n]. An account number ending in 14 would rhyme with both, which is why this one does not.

Corrigan appearing in case 1 is the point and also the risk. A player who meets him as a helpful account holder will remember him. That is what a trace is for. It also means his name is spent — he cannot arrive fresh later.

1 The clash list — season cast against the corpus

Screened against all eight suspect casts (40 names), all 29 con aliases, the rejected map, the safe pool, and a word-level sweep of all 822 entries.

Headline

No name clashes with either cast. Not one of the seven reservations collides with a suspect name or a con alias. No existing name was changed.

Every reservation, screened
NameRoleCast clashWord sweep over 822 entries
MilesCorrigan, givennoneExact word in 18 entries — it is a unit of distance, and Prime Suspect case 1 measures a route in them ("the café is 1.2 miles from the museum"). Also inside smiles.substring hazard
EvanTobin, givennoneInside relevance, relevant, irrelevant, grievance — 41 entries, and relevance is a literal field name on every Prime Suspect clue.substring hazard
Wrenthe runnernoneInside wrench — 47 entries, and The Wrench is a con whose id is literally wrench.substring hazard
LANTERNdesignationnoneInside lanternfish, in one reading passage.minor
Corriganthe dead mannonecleanclear
Starlingagent namenonecleanclear
Tobinthe bodynonecleanclear
Nico / Varelaher brothernonecleanclear
Francescathe handler, givennoneclean. Already in the safe suspect pool, so reserving it takes it out of that pool.clear
Falconerthe handler, surnamenonecleanclear
Halloranthe pharmacynonecleanclear
Narcotizanthe sedativenonecleanclear
What the three hazards actually mean

None of them is a reason to rename anybody. All three names are safe in prose, because nobody reading "1.2 miles" thinks of Miles Corrigan.

They are a reason for every check that reads these names to match on word boundaries and never as a substring. A naive substring check for Miles reports a hit on arithmetic in 18 entries; for Evan, on a field name in 41; for Wren, on a con id in 47. The writing-rule checks below all match on word boundaries, and the reason is written into the code so the next author cannot loosen it by accident.

2 The check list — seven writing rules in the exporter

All seven live in export-machine.py and run from build(), so they fire on a dry run, on --write, and on the --verify hook. Each skips while the documents it reads are unwritten, and each failure names the entries involved and exits 1.

Every rule, and the fixture that proved it
RuleWhat it enforcesProved by breaking
One sedative, named in three places Narcotizan names the same sedative in the pharmacy log, the tox supplement and Wren's memo. The rapid intake says ethanol and must not name the sedative — only the confirmatory supplement does. caught a log that drops the name; caught a rapid intake that names it
The two death files The original and the operational copy differ in IDENTIFICATION and in nothing else. The SP-14 stamp is excluded from the comparison. caught a second differing field; caught the two agreeing on IDENTIFICATION; allowed a stamp-only difference
Swelling beside no injury bruising and trauma never appear. swelling never appears without no injury noted beside it. caught "facial bruising"; caught swelling standing alone
Attended, never entered The copy says attended the residence. Peregrine and entered never share a line. caught "Agent Peregrine entered the study"
The clavicle plate, two documents It appears on the intake sheet and the identity page, and on no other document. The match is immutable because the Agency never read those two against each other. caught a third document naming it
The sling, shared then named It appears in the neighbour's statement and the clerk's note, and neither names Corrigan beside it. Only the roster card makes the sling his. caught the neighbour naming Corrigan
No bird before the roster card Starling appears in no document at or before the Act III roster card. caught the Act I memo using the bird

What a run says today

$ python3 tools/export-machine.py peregrine
  season locks: 0 checked, 7 waiting on unwritten documents
    waiting: one sedative, named in three places — pharmacy-log, tox-supplement, wrens-memo, intake-sheet
    waiting: the two death files — death-file-original, death-file-copy
    …

Against the clean fixture with every document written, the same code reports 7 checked, 0 waiting and exits 0. Silent today because the season record is staged rather than committed; a guide with no season key produces no output at all, which is why stellar and every other guide are untouched.

One rule degrades rather than lying

The death-file comparison needs a field map, not prose. If the two documents are written as prose with no fields, the check reports waiting and names both entries, instead of comparing two blobs and reporting a pass it did not earn. I got this wrong on the first pass — it read the map off the season record rather than off the written entry, which made two deliberately broken fixtures come back green. That is fixed and both now fail as they should.

3 The season record

A guide-level record for guide.json, beside casting_pool. Not an entry. Staged as a note; the full JSON to paste is at docs/Peregrine-Plot/season-record.proposed.json.

Transcribed from v4
SectionContents
cast + naming_ruleNine roles. Agents are birds, the handler is the falconer, sources are designations, civilians are civilians.
in_world_rulesAll five, verbatim, including SP-14 and "Corrigan wrote rules 3 and 4. That is the hole he lives in."
nightAll thirteen timeline rows, each with what happens and what the paper says. Marked assumed: true as a whole, as v4 marks it.
death_filesThe two identification lines, the nine identical-and-unread details, the Docket verb, and the stamp recorded as chrome.
act_turnsAll five, each with its plot and its SAT reading move.
proof_documents#1–#8 plus the Docket line. Each carries act, delivery, reading move and status: "unwritten". Sixteen documents are addressable once #6's four enclosures and #8's three parts are counted.
credential_threadOne use in the hour, two after the burn, the Docket's verb, and the badge going into the sequel.
authoring_locksAll ten.

Everything v4 marks "(assumed)" carries assumed: true

The agent name Starling · the body's name and designation · Nico Varela · the handler going face-on at the Act III split · Halloran's · the whole night timeline · hints and routing passing to the ally after the burn · and the sequel thread behind why the Agency wanted Tobin.

Three fields added that v4 does not contain

Each document and part gets a key, an entry_id (null until written) and a status. They carry no story; they exist so a check has something to resolve. Without them the seven rules above have no way to name a document.

Two things to decide

The original death file is numbered twice. v4 lists it as #7 and also describes it arriving inside #6. The record indexes it inside #6, where the checks read it, and keeps a #7 row so v4's numbering survives. Delete the #7 row if the duplication is unwanted; nothing reads it.

The floor plan still says Vale. Panel 5's route legend reads "Vale's route (in, ~8:00)". v4 renamed the body to Evan Tobin specifically to clear the Theo Vale / Idris Vale collision, so the image is one revision behind its own source.

4 The rename, finished

Before She's Back. The desk is Francesca Falconer's, and v4 states it directly. The staged note supersedes the "Before He's Back" note in the earlier batch; if that one has not been committed, decline its rename and take this one.

Unchanged as instructed: the flat 100 points per file stands, and it still matches Casing the Room's flat 100 per mark. The verb row is untouched — allocate remains, with your ruling on open still outstanding. The record id stays casing-the-room.

One word in the approved line now has no referent

The reskin line you approved reads "Ten files on his desk, three minutes." The desk is hers. I staged the line verbatim and flagged it rather than rewriting an approved line unasked. Recommendation: "Ten files on her desk, three minutes. Read what you can and get out."

5 Case one — Halloran's Books, drafted

Halloran's Pharmacy closes its March quarter $1,464 short and nothing is missing from the shelves. The money left through house-account postings, $122 a week for twelve weeks. Five counters; one took it. The ordinary step that turns the case is confirming a cash account's entries with its holder — and that step is the visit.

Five threads, all numbers
ThreadModeWhat it establishesClears
E01 · Takings Against Depositsnumeric_multiple_choice$47,180 recorded against $45,716 banked over twelve weeks — the gap has a rate, $122 a week.— sustains
E02 · The Closing Rosternumeric_multiple_choiceThe gap lands in all twelve weeks; only three counters closed in all twelve.S01, S05
E03 · A Cash Account, Confirmedclaim_choiceThe visit. The holder of account 2214 confirms four payments of $86; the ledger posts four receipts of $65. Cash reached the counter that the ledger never recorded — and posting takes the ledger.S02
E04 · The Posting Windownumeric_multiple_choiceEvery disputed posting is timestamped 6:40–6:55 p.m.; one counter is off the floor at six.S04
E05 · The Reconciliationsnumeric_multiple_choiceTwelve weekly reconciliations, signed by three different counters; two signed none at all.S01, S04

Lint

Two things the lint caught that are worth knowing

A single-digit answer is dangerous. account-privacy substring-matches the correct choice text against the clue's own prose, so an answer of "4" collided with the "4" inside "6:40". E04's account now spells the window out and the digits live only in the prompt.

interval_comparison is the vehicle track-width mode. It has a model behind it and will not accept a question that supplies no measured interval. E04 became numeric_multiple_choice.

The draft is a second file beside the shipped case, deliberately

It is ps-case101-hallorans-books.json, not an edit to ps-case001.json. The corpus's own mechanism for this is validation_status: draft, which the exporter checks and holds back, and the exporter's comment says so in as many words: promotion is a corpus edit.

case_id is CASE101 rather than CASE001 so an accidental publication sorts last instead of colliding with The Calder Cipher — CaseLadder reads the digits out of the id. Promoting it means taking CASE001 and retiring or renumbering the Calder Cipher, which is a decision rather than an edit.

What the draft deliberately does not contain

No bird, no LANTERN, no Tobin, no death, no drink. Nobody dies and nothing is stolen — it is an audit. Corrigan is a witness and never appears on the board. There is no till-variance thread, because a variance band wide enough to hide $24 a night would clear nobody and would teach the wrong read.

6 Repo state

All three repos were on their default branch and level with upstream before any change. Unrelated dirty files were left alone: the modified .claude/context/blueprints/*.md, the deleted session signals, and the earlier untracked meeting notes.

Your source of truth is untracked

docs/Peregrine-Plot/ — the v4 solved case and the floor plan — is untracked in git. It is the single source for everything above and exists in one place on one machine. I did not commit files I did not author, but this one is worth committing.