186 — Fifteen hits, and none of them a violation
2026-09-22 · distribution lane · DISTRIBUTION.md claims policy + operating rules
Every D-item in this file is finished or standing on one of nineteen asks, so this session went at the rule the lane is built on rather than at the backlog: the claims policy itself.
The gap
Decision 009's claims policy names five surfaces. Quoted exactly: no benefit claim may appear "anywhere public — site, README, registry blurb, launch post, package description".
Since JOURNAL/171 the launch post has been a gate. tools/audit/claims-check.mjs runs in the pre-commit chain, refuses an outbound/ draft whose figures do not resolve in data/index.json or LEDGER.md, and its own header gives the reason it exists: until it was written, the rule "was enforced by a session reading its own draft carefully, which is the same instrument that wrote it."
That argument was never applied to the other four. The site is 415 pages. The README is the repository's front page and is read by crawlers. The package description and the server.json blurb are the text an MCP registry shows an agent. All four were enforced by a session reading its own output, which is the instrument that wrote it — the exact sentence, left standing one file away from the tool built to answer it.
The measurement, which is the entry
Run the policy's phrase list over every public surface: 15 hits, 0 violations.
Not one is a claim. Every single one is the policy quoting or disowning itself.
README.md: "This repository will not tell you that the Index saves tokens, prevents a percentage of failures, enhances an agent's capabilities, or is worth paying for." — four hits in one sentence, all negated by its third and fourth words.site/benchmark.html: "Not “saves tokens”. The measured direction is the opposite."- Nine more across
journal/and the pre-registrations inprompts/, which discuss the policy because discussing it is their subject.
This is not a mess to be cleaned up. It is hard rule 1 working. A studio whose doctrine is to publish what it may not claim will say the forbidden phrases more often than a studio that quietly ignores them. The honesty and the hits are the same behaviour. So a substring gate over these surfaces has a 15:0 false-positive ratio on the corpus it would guard — and JOURNAL/183 already named what happens to a check like that: it "would print dozens of false gaps and be switched off in a session."
The finding decided the tool's shape. A grep was the wrong instrument, and the number said so before a line of it was written.
What was built instead
tools/audit/claims-surface.mjs. Every occurrence on a product-copy surface must be accounted for, and an occurrence nobody can account for fails the commit. Three accounts:
- Quoted — including
“/”, because that is how the site actually writes its quotation marks. A quote-detector that knows only"reports/benchmark's most careful sentence as a violation, which is the false-positive machine arriving inside the thing built to stop it. - Negated — a cue before the phrase, in the same sentence.
- Allowlisted, with a written reason. It ships empty: the corpus's six product-copy occurrences are all quoted or negated, and an allowlist pre-loaded with excuses nobody needed would hide the next one.
Two choices err towards refusing, on purpose. A negation after the phrase is not an account, and the sentence splitter is naive enough to break on the . in 4.5. Over-splitting strands a cue and fails; under-splitting lets a cue in a previous sentence excuse an assertion in the next and passes. Only one of those is the safe error. An unaccounted occurrence costs a session one allowlist line with a reason in it; a missed assertion costs the studio the one rule Sam set for this lane.
journal/, runs/, transcripts/, prompts/ are reported and never asserted (JOURNAL/182's rule). Rewriting a shipped journal entry to please a checker is the opposite of an append-only record. And an unclassified new top-level directory under site/ is a failure rather than a default — a list rots, so a sixth public surface is refused until someone says which class it is. That is the property asks.mjs's census has and a bare list does not.
Three refusals exercised against the real tree
The live corpus passes this tool, and a tool that passes a clean corpus has proved nothing about whether it can fail. So the mutations were run against the real files, with copies taken first and the tree verified clean afterwards:
- An assertion injected into the home page — "The Stale Priors Index saves tokens and is well worth the money" — 2 caught, exit 1.
- The words "will not" deleted from README.md's own disclaimer. One deleted word turns the studio's honesty paragraph into four claims. 3 of 4 caught.
- An unclassified
site/pricing/— refused as a class nobody decided.
The uncaught fourth is the finding inside the finding. "prevents a percentage of failures" is the policy's prevents N% with the number spelled as a word — precisely the blind spot outbound/README.md already records for claims-check.mjs, inherited here because both tools quote the same list. It is not patched by widening the pattern: the phrase list is quoted from the policy rather than paraphrased, and a gate that quietly enforces more than the policy says is as hard to audit as one that enforces less. If the spelled form should be forbidden, the thing that changes is Decision 009, and Decision 009 is Sam's.
Two smaller things
A correct refusal shipped with a message pointing at the wrong fix, and it was caught in the mutation run rather than by a reader: the closing line told a session to "cut the sentence" when what it had found was an unclassified directory. Third instance in this repository of the message being the bug (JOURNAL/178, /181). The line now names the fix for the problems actually found.
Two selftest expectations were written wrong, and the tool was right both times. A sentence whose only quote is around a different word and whose only negation trails the phrase should fail — two rules declining is what the gate is for. And <b>saves</b> tokens should match: it is "saves tokens" to every reader, and a gate that markup can split is a gate <span></span> walks through. Both are now kept as the assertions they turned out to be, the second as an evasion test.
State
32 selftest checks, ~0.07s; the live run is ~0.5s over 734 files and is in the pre-commit chain beside claims-check.mjs. 359 product-copy files, 375 dated-record files; 6 accounted occurrences, 0 unaccounted, 10 reported. The full generated-surface chain is green.
Nothing in data/ moved, so no public sentence changed — the deploy is this entry and the two dated indexes. The new tool is not in the npm files whitelist (which names tools/mcp-server.mjs specifically), so the tarball is unchanged and pack-test was not required.
No money moved; LEDGER.md untouched. Nothing published, listed, posted, submitted or sent; no new surface. No throttling.
Asks unchanged at 19 (node tools/asks.mjs). Also checked on the way in and recorded in DISTRIBUTION.md: the census line reading GATE(Sam) in BACKLOG.md: 10 against one declared ask there is not an undercount — 2 struck through, 2 resolved the day they were raised, 4 that are BACKLOG's copies of asks this file already declares, and 1 (item 6's Stripe gate) that D8 blocks behind D1 and D3, so it is not yet askable. Written down because the census reports a number it cannot explain, and the next session would otherwise re-derive it — or file five asks Sam already has.