Distillate

Text to evidenceLesson 3openfrom namaq

A check that exists but doesn't run

Building lesson 0002's decision surfaced two bugs, at two different layers, with the same shape: something had a name that promised a check nothing actually performed.

Building lesson 0002's decision surfaced two bugs, at two different layers, with the same shape: something had a name that promised a check nothing actually performed.

The first symptom

Once SourcePage derived a paragraph anchor from the page's own text instead of an authored list, history:validate — unchanged — went from passing the whole corpus to failing dozens of batches. Not because the migration broke anything: because the check got stricter, and the corpus had been living under the old, weaker version of it the whole time.

Talhah ibn Ubaydullah's full-name claim cites anchor 4/23-p17 — paragraph seventeen of printed page 23. The store's v4/23.md has ten paragraphs.

Follow it down

The old schema paired a declared passage with a paragraph by array position:

passages: [{ anchor: "4/23-p17" }, { anchor: "4/23-p18" }, …]
                              ↓ zipped positionally, not by the number in the string ↓
paragraphs: [p[0], p[1], …]

validateBatch checked that the count of declared passages matched the count of paragraphs in the file. It never checked that an anchor's own number, p17, meant "the seventeenth paragraph" of anything. The number was a label a human wrote down, trusted without being read.

The reason it was seventeen: whoever extracted Talhah's entry correctly recorded where his page sits in the full printed page — something else, still untranscribed, occupies the paragraphs before his. The label was honest about the book. The check just never looked.

The second symptom, same shape

The schema swap itself shipped clean: lint passed, tsc passed, all 1,701 tests passed. Then /api/people/[slug] 500'd, in a running app, on a real request:

The column `citations.accountId` does not exist in the current database.

Nothing in the diff added that field. Early in the change, SourceAccount still declared citations Citation[] — copied forward out of habit, a relation to a field that no longer existed on Citation once its own accountId was removed. prisma format doesn't reject a one-sided relation; it repairs it, silently adding sourceAccountId back so both sides matched. The repair was syntactically correct and semantically wrong — a field nothing was meant to have — and every static check waved it through, because as far as the type system was concerned, it was consistent.

What both had in common

Neither gap was a missing check. history:validate ran, and reported "no issues." Lint, tsc, and the test suite all ran, and reported green. Each one was checking a real invariant — a real invariant, just not the one that mattered: paragraph count instead of paragraph correspondence; type consistency instead of "should this field exist at all." A check with the right name and the wrong question passes exactly the inputs that should fail it.

Only two things caught the real problem, and both were the same kind of thing: deriving the answer independently and comparing it to what was claimed, rather than checking that the claim was internally well-formed. Position-derived anchors compare against the store's real paragraph order. A live query against the real database compares against what the schema actually produces, not what its types say it should.

Retrieval

Why did tsc and every test pass with a phantom sourceAccountId column in place?

Retrieval

Talhah's anchor said p17 and the store held ten paragraphs. What does that mean is true about page 23?

The move worth stealing

Before trusting a check, ask what it actually compares. A count check tells you two lists are the same length, not that they correspond. A type check tells you code agrees with a schema, not that the schema is the one you meant to write. Neither failure mode looks like a missing check — both look, from the outside, exactly like "validated." The distinguishing question is: does passing this check require the value in question to be independently re-derived and compared, or only to have the right shape?

The workspace notes already had this thread open — five accounts sat in the wrong volume before anything caught it, because nothing compared a declared volume against its own pages' links (lesson 0002's aside). This is the same question asked twice more, at the paragraph layer and at the schema layer, with the same answer each time: the fix was never "add a check." It was "make the existing check compare against a derivation instead of a shape."

Primary sources: the plan's Order section, which put the store before the schema before the read path for exactly this reason — each step's output is what the next step derives against; the tracked anchor gaps, which is what "derive, don't guess" looks like when the derivation can't resolve.

Card: Source pipeline card.