Skip to content
Skip to main content
Novel Systems home
Decision log
D-037August 4, 2026

The placeholder check stays a warning; the residue check is fatal

Decided

scripts/check-proof-copy.mjs now makes two checks with deliberately unequal severity. A testimonial still at status: "placeholder" warns and exits 0, and PROOF_STRICT=1 is not set in CI. A testimonial marked approved that still carries PLACEHOLDER_ATTRIBUTION or a DRAFT — prefix fails the build unconditionally, with no flag to turn it off.

This reverses advice the script itself gave. Its header used to say PROOF_STRICT=1 was what to set in CI "once the approved copy is in and the intent becomes never regress". The five quotes being approved is the moment that advice came due, and looking at it directly showed it was wrong. placeholder is not a defect. It is the correct designed state of a study whose quote nobody has confirmed, and lib/customer-proof.ts says as much: the next study added starts there and stays invisible until someone confirms it. Failing CI on that state means whoever writes study six meets a red pipeline whose cheapest fix is to type approved — putting the full weight of the build system behind the one act this whole mechanism exists to prevent. A control that makes the unsafe state the path of least resistance is worse than no control, because it also carries the appearance of rigour.

Why the other direction is fatal instead. Draft text under an approved status has no innocent reading: either a status was flipped without replacing the words, or draft wording was pasted into a live slot. Both put unconfirmed sentences in a named person's mouth, and unlike a placeholder, that state *renders*. There is no project state in which shipping it is correct, so there is no flag to disable it. The strictness went where the irreversible harm is, not where the incomplete work is.

The check is verified against its own blindness. A regex that stops matching finds nothing, and finding nothing is indistinguishable from finding no problems. So the block count is compared against the number of status fields — catching a testimonial the regex missed entirely — and every matched block is asserted to contain all four fields of CustomerTestimonial, which catches the subtler case where a nested object ends the non-greedy match early and the quote falls outside the captured body. That second guard exists because the count-only version of it was written first and then failed a test of exactly that case.

What would make this wrong: evidence that placeholders are being ignored rather than worked through — a quote sitting at placeholder for months while the warning scrolls past in CI logs. The remedy then is not PROOF_STRICT=1, it is an expiry: fail the build on a placeholder older than N days, which pressures the confirmation rather than the status field.