Author-facing. Not referenced from SKILL.md and never loaded by the model.
A second register, opt-in. /whatif is untouched: same 550–850 words, same
offline single pass, same everything. /whatif deep adds a research pass, a
1,400–2,600 word band, movements separated by [break], and a rule that a name
may be printed only if a verifier confirmed it. The two modes share every rule in
SKILL.md; deep adds Step 0c and swaps three reference loads.
Opt-in rather than a raised default, deliberately. The evals already name length inflation across a session as the most likely failure, and a skill that can drift into long form has no reference point left to detect the drift against.
scripts/deep-workflow.js. Seven agents in two waves: four regime agents and
a cold numbers adversary together, then a name verifier and a number reconciler
over the barrier. The adversary shares the first wave on purpose — it must not
see the regime agents' output or it stops being independent and becomes a
reviewer. Shipped as a file and invoked by scriptPath, so it costs nothing
until used.
references/sourcing.md (deep only). Source tiers retargeted from
supersearch's business/tech framing to physical fact-checking, the definition of
"verified", the claims.tsv format, and the rule that the ledger is a whitelist.
The framing that made this cohere: Rule 2 is not a tax, it is a ceiling on
specificity, and research is the only thing that raises it. Nicheness is bought
with verification.
references/longform.md (deep only). Movements, escalation across them, the
one-impossibility discipline restated because it is the rule most likely to erode
per-movement, and the rhythm guidance imported from humanize.
examples/deep-answer.md (deep only). One complete 1,465-word answer with its
ledger printed after it. Written the way deep mode is meant to be run — the
figures for Lake Maracaibo, the NSSL voltage range, the CBECS hospital intensity
and the NFPA 110 classes were all fetched and checked, and the two UNVERIFIED
rows became the answer's most honest paragraph rather than a hedged sentence.
scripts/estimate.py. Q carries units through arithmetic with derived
units expanded to SI base, so J/W reduces to s and Q(0.5,"kg/m2") * 1000
comes back as kg/m² rather than quietly becoming millimetres. .to() asserts
rather than converts. Plus band() for three-point inputs and compare() against
a table of familiar magnitudes.
check.py gained --deep, --claims, a BREAKS row, and three
prose-variance rows. render.py gained the [break] asterism.
The archive has no deep mode, so the deep band set is a design decision and the code says so. The variance floors are measured, but over the exemplars rather than the corpus, and the comment says that too — the file's existing values are labelled "measured, not invented" and that distinction had to survive.
| row | observed range | floor set | source |
|---|---|---|---|
| SENT VAR | 8.27 – 11.13 | 7.0 | 3 worked answers + 2 fresh drafts |
| PARA VAR | 25.67 – 35.49 | 20.0 | same |
| OPENER REPEAT | max 2 occurrences | 4, or 5.0/1k | same |
OPENER REPEAT earned its place immediately: the first draft of the deep exemplar opened seven sentences with "It is" and nothing else in the file noticed.
Both pre-existing, both invisible until a draft had wrapped captions.
A [drawing: …] caption spanning more than one line was split: the first line
became a drawing, the rest became body prose. It inflated the word count by 75,
and it fed the caption's tail into the sentence splitter, which then read the next
sentence's first word as a proper noun. check.py and render.py now both
consume to the closing bracket. Wrapped footnotes had the same shape and got the
same fix.
render.py --outdir with a relative path reached Path.as_uri() and raised,
after doing all the layout work. Resolved at parse time now.
- No third mode. Deep always researches. A
/whatif researchthat only verified, without the extra angles or length, would have been a third band set to keep calibrated for very little. - The ledger check covers names, not numbers. Detecting "a figure presented as sourced" mechanically produces mostly noise, and PRECISION already covers the tell that matters. Eval F keeps the numbers half manual and says why.
- No per-movement footnote quota. Distribution is a judgement about where the jokes sit, and a count would have been enforced without being understood.
- Both standard drafts from this session re-checked:
Nothing flagged, word counts unmoved. - Deep exemplar:
Nothing flaggedat--deep --claims, 1,465 words, 5 drawings, 9 footnotes, 3 breaks, ledger clean. - Rendered twice to separate directories: 84,827 bytes both times.
- Structural read of the paginated output: 3 asterisms, 5 panels, 9 inline footnote spans, no orphan endnote block.
estimate.pyself-test, andnode --checkon the workflow script wrapped the way the runtime wraps it.
New references/slop.md. Loaded at the draft step alongside voice.md.
Holds the precedence rule, the contested verdicts with their measurements, and
a one-paragraph sweep of everything adopted without argument. It restates what
it needs from the humanize skill rather than depending on it being installed.
About 650 words, which is the price of not having the model rediscover these
collisions per answer.
check.py grew six slop rows — REFRAME, SNAP Q&A, FRAG RUN, TRICOLON,
ANAPHORA and a grouped SLOP row for the near-zero-rate phrases. Same reporting
style as the existing table: a count, a note, a verdict. The notes say what the
legitimate version looks like, because four of these patterns have a
corpus-attested form that must not be deleted on sight. Thresholds are set from
the archive, not from taste.
Step 3 gained two sentences saying the slop rows have carve-outs and a flag means look rather than delete. No new step and no loop; the check still runs once.
render.py now paginates properly. Paged.js is vendored into assets/,
which gets @page margin boxes, float: footnote and a page counter that
Chrome does not implement. Footnotes now sit at the foot of the page that
refers to them, under a short rule, with the number hung in the margin. Pages
after the first carry a running head and a folio. Straight quotes, apostrophes,
double hyphens and ellipses are typeset at render time, so drafts stay plain
ASCII.
Drawings stayed captions. The compositor described in the brief was built
and then removed at the author's instruction mid-session: captions are
sufficient, no generated imagery wanted. The [scene: ...] draft format was
reverted from check.py and render.py together, and the framed panel is now
the intended output rather than a placeholder.
Measured over all 162 archive articles, 113,340 words of body prose, using the
same extraction as the author's measure.py. The extraction was validated
against the numbers already in voice.md: em dash 7.79 vs 7.59 published,
semicolon 1.46 vs 1.45, exclamation 0.67 vs 0.65.
| # | Pattern | Rate | Verdict |
|---|---|---|---|
| 1 | Negative parallelism | 0.05 | Carve-out. All six instances correct a claim about the world. The cross-sentence rhetorical reframe is the banned shape. |
| 2 | False ranges | ~0 slop | Adopt. The nine hits are genuine spectra. |
| 3 | Not X. Not Y. Just Z. | 0.00 | Adopt. |
| 4 | Stakes inflation | — | Adopt; already Rule 6 and the intensifier list. |
| 5 | Filler transitions | 0.07 | Adopt; already the formal-hedge list. |
| 6 | The X? A Y. | 2.2 questions, 0.01 snaps | Carve-out. Structure authentic, snap form banned — one instance archive-wide. |
| 7 | Invented concept labels | 0.00 | Adopt; already Rule 2. |
| 8 | Tricolon abuse | 0.70, 5 adjacent pairs | Compatible. Already inside the one-per-500 ceiling. Escalation ladders are not tricolons and are explicitly protected. |
| 9 | Tapestry and landscape | 0.12 | Adopt, with a caveat: all fourteen landscape hits are literal terrain. |
| 10 | Anaphora | 2 instances | Split. Anaphora adopted; the causal clause hand-off carved out at 0.26 strict / 0.94 loose. |
| 11 | Here's the kicker | 0.00 | Adopt. |
| 12 | Short punchy fragments | runs of 1: 224, 2: 20, 3+: 2 | Carve-out. The single deflation beat is the device; three stacked is the failure. |
| 13 | Listicle in a trench coat | 0.00 | Adopt. |
| 14 | The truth is simple | 0.03 | Adopt. |
| 15 | Think of it as | 0.00 | Split. Phrase banned, flat recategorisation protected. Plain simile survives at 0.02. |
| 16 | Participial tack-ons | 0.01 | Adopt. |
| 17 | Plain ASCII | curly quotes 9.0 | Overridden. Typeset output; smart punctuation moved into the renderer. |
| 18 | Vague attributions | 0.00 | Adopt; already Rule 2. |
The reconciled audit flagged REFRAME twice in each of the three worked answers. Adjudicating each of the six against the carve-out: five were factual corrections and stayed, one was rhetorical and was cut. Two more were removed to bring each exemplar to a single instance, since two per answer in all three exemplars is a pattern the model would copy and is far off the archive's rate.
- Laptops — "So the question isn't whether it works. It's how many." became "So the only question is how many." Rhetorical repositioning, no fact, and the exact shape the corpus never uses. Exemplar edited.
- Laptops — "Two hundred is not an unimaginable number. It's four pallets." Carve-out; kept.
- Shrew — "The shrew isn't eating constantly because it's greedy. It's eating constantly because..." Carve-out; kept. This is the answer's pivot.
- Shrew — the hard-floor sentence was restructured into a single sentence so the negation stops being a cross-sentence pair. Exemplar edited for rate.
- Clouds — "It isn't. It's mostly air." shortened to "It's mostly air." Exemplar edited for rate.
- Clouds — "A cloud is not a bucket. It's the working end of a pump." Carve-out; kept. Also the protected recategorisation device.
The laptop exemplar's chained energy-flow sentences were checked against the anadiplosis carve-out and left alone. ANAPHORA reads 0 on all three, which is the carve-out working mechanically rather than by instruction.
All three now report Nothing flagged.
--print-to-pdf fires on the load event, which is partway through Paged.js's
chunking, so the PDF comes out as a near-empty 796-byte file. Five runs per
flag combination against the same document:
| flags | complete documents |
|---|---|
| bare | 0/5 |
--virtual-time-budget=15000 |
1/5 |
--virtual-time-budget=15000 --run-all-compositor-stages-before-draw |
1/5 |
--timeout=8000 |
2/5, one of them partially paginated |
--virtual-time-budget=8000 --timeout=20000 |
1/5 |
It is a race, not a timing constant, so no budget value fixes it. The page now
sets window.__pagedDone from PagedConfig.after, and render.py drives
Playwright, waits on that beacon, then calls page.pdf(prefer_css_page_size).
That is 4/4 in isolation and 9/9 byte-identical across the exemplar suite. The
command-line path is kept only for the unpaged fallback, where there is nothing
to race.
- Three exemplars and two fresh questions rendered end to end. Each exemplar rendered three times: nine PDFs, byte-identical per document.
- Visual inspection against the section D checklist passed on all five.
- Drift test across this session's three answers — pool, bicycle, hair — shows
no length inflation (699, 567, 555), stable footnote count, stable em-dash
density, no headings, no exclamation marks. The single flag was
745.7tripping the four-significant-figure rule; it is the definition of mechanical horsepower, so it was judged and kept, which is the intended use of that row.
- No body serif was vendored. Georgia was kept. It has old-style figures, which is what a trade book uses and what makes the numbers on the page read as set rather than typed, and the fallback to Times New Roman is adequate on a machine that has neither. Adding 400 KB for a marginal change would have been novelty.
- No
@pagerunning head on the opening page. It is a chapter opener and carries the mark instead.