Skip to content

ADR-0075: Corrections a cross-model grill found in the 2026-09-20 rulings

Status: Accepted — amends ADR-0057, ADR-0060, ADR-0062, ADR-0068 and ADR-0069, all of which stand Date: 2026-09-20

Decisions

D-153 A field made required by the value beside it is a rule and its refusal carries a code; the no-code case is an absent required input

ADR-0062 rules that "a blank required field remains a 400 carrying no code". That rule is about input that is simply missing. A field that is nullable in itself and required by the value beside it — a caveat required because the outcome is CARRIED_AS_CAVEAT — is a rule of the domain, not a blank field, and its refusal names itself like every other rule.

CAVEAT_REQUIRED_ON_CARRIED is therefore correct and is not an exception to ADR-0062.

What each amendment touches

ADR-0069 — the display prohibition is withdrawn

Its Consequences say the count "must not display; it exists to gate a sentence, not to be shown." That prohibition is withdrawn. It is in neither the owner's ruling nor D-145's Decision; it was the author's addition, and the frontend displays the count because it was asked to. D-145 itself is untouched: the count is carried so the every-pair-judged wording is used only where there were pairs. Whether the count is shown is not ruled and is the page's business.

ADR-0068 and ADR-0069 — two miscitations

Both cite D-49 for "a Reconciliation names the curator who made it". D-49 (ADR-0022) is the cross-section detector ruling and says no such thing. The named curator is a property of the Reconciliation record itself, asserted by the Corpus gate. Read both Coverage rows as citing the record and the gate, not D-49.

ADR-0068 also presents D-124 as "the rule the snapshot seam already states" and applies it to a curator-side read. D-124 is about the payload a device receives. The principle ADR-0068 drew from it — the server decides, the reader derives nothing — is sound and is what ADR-0071 later fixed as a contract; the citation overreached in calling a device rule a statement about this read.

ADR-0057 — a re-decision and a false cost

Its D-129 presents the two-read tear and the payload-sentinel rule as found by reading the pull. ADR-0053 had already recorded both, on 2026-09-11, in its "What remains open" section: "A version read and a snapshot pull are still two reads… The client takes its sentinel from the payload's own version, which keeps the tear harmless." ADR-0057 does not cite it. D-129 ratifies ADR-0053's open note rather than discovering it, and ADR-0053 stands.

Its stated cost — "a client must handle a 409 from two routes before it has anything to show" — is false as built: CorpusSync.ts calls the snapshot endpoint only and never /version. The 409 on /version is a contract for a caller that does not currently exist.

ADR-0060 — the reader half, and an uncited claim

ADR-0060 does not point at ADR-0072, which supplies the payload shape its reader half needed. Read ADR-0060 with ADR-0072; D-133 is unchanged and D-149 is what makes its reader half buildable.

Its Context asserts what the GHS format fixes with no R-nn behind it. See the research gap below.

ADR-0062 — the delegated list, and what the delegation does not cover

The API landed seventeen codes against this ADR's twelve. That is not a defect: ADR-0062 says in terms that "the API owns this list and may correct it against the code; whatever it lands on is what the fixture carries." The five additions — CAVEAT_REQUIRED_ON_CARRIED and four 404 codes — are within that delegation and are recorded here so the ADR's twelve are not read as the whole set.

The delegation covers the membership of the list. It does not cover the blank-field rule, which ADR-0062 states independently, and CAVEAT_REQUIRED_ON_CARRIED sits on that line. D-153 above is what settles it, in the API's favour and for the API's reason.

The rejected option that describes what then happened. ADR-0062 rejects "reviewing pages 39 and 40 against the generic state first", and says those pages wait for the codes. The review landed in frontend 2e383f9 at 01:06:21 and the codes in API 56f1dd3 at 01:10:36 — the review went first, by four minutes, and ADR-0067 and ADR-0068 both came out of it. The ruling was not followed and the outcome was good; the ADR should not be read as describing what happened. Tickets 39 and 40 still carrying paused 2026-09-11 is a separate bookkeeping error, corrected in the tickets.

Context

A cross-model grill ran on 2026-09-20 over ADR-0057..0063, 0067, 0068 and 0069 — two Grillers, neither a fork, after three fork rounds had found none of this. Its findings are in .scratch/chemical-safety-assistant/grill-findings-2026-09-20.md.

Two of its findings were reversals and are superseded elsewhere (ADR-0073, ADR-0074). The rest are wrong sentences inside rulings that are otherwise right, which is what this document is for. The house already has the pattern: ADR-0070 amended two Accepted rulings a grill found wrong, in one document.

Decision

The corrections above hold. Each amended ADR stands; none is edited.

Rejected options

  • Editing the Accepted ADRs in place — rejected. The never-edit rule is what makes an ADR a record rather than a draft, and D-83's Coverage-Note exception is explicitly for traceability bookkeeping and not for fixing a wrong sentence.
  • One ADR per correction — rejected. Five documents for five sentences, each needing its own Context to explain the same grill, and a reader would have to find all five to know what ADR-0062 now means.
  • Leaving the corrections in the grill findings file — rejected, on this repository's own evidence. id_status_check.py exists because ADR-0018 recorded a correction in prose and two new ADRs cited the dead finding anyway six days later. Its docstring draws the moral: "Prose corrections do not get re-read. Gates do." A findings file is prose.

Attribution: rejected options that were the author's reasoning, not the owner's

The grill found eleven rejected options across eight ADRs that appear nowhere in rulings-2026-09-20.md. AGENTS.md asks for the ruling in the decider's own words including what lost and why, so one presented flatly reads as the owner's reasoning. None is withdrawn for being wrong — each was checked and each still holds as an argument. What was wrong was the silence about whose argument it is.

ADRRejected optionWhose
0058leaving file/ unmounted and saying nothingauthor; the ruling gives the unmount as the enforcement, not as an option weighed
0059taking the page count from the server by parsing the PDF thereauthor; superseded with ADR-0059 in any case
0060leaving the duplicate and adding a commentauthor, from ticket 39's record of the duplicate
0061keeping a version history of explanations in the live storeauthor; superseded with ADR-0061
0062wording the refusal on the server and showing messageauthor; the ruling states the rule, not this alternative
0062letting the frontend hold its own copy of the codesauthor, by analogy from D-134
0063leaving each repository to resolve the workspace however it doesauthor; the owner ruled only against relocation
0063encoding the path in an environment variableauthor
0067leaving the reasoning in the docblock and the testauthor; the ruling names the docblock as the defect, not as an option
0068asserting the judged case and accepting the false sentence as rareauthor; drawn from the ruling's own reasoning
0069ordering NOTHING_SELECTED above NO_RESTRICTIONauthor; the ruling states the precedence, not the alternative

All eleven are kept and marked here as the author's. The rule this establishes: a rejected option the decider did not weigh is recorded as reasoning, not as a ruling, and where an ADR cannot say so itself a correcting document says it.

A research gap, recorded and not filled

The sixteen Thai Section Headings have no cited source anywhere in this workspace. GHS Annex 4 fixes the English. The Thai comes from a Thai ministerial notification that no artifact here names, and this is wider than the three headings already flagged as uncertain — it is all sixteen.

This is a stage-0 gap: an R-nn that does not exist. It is not invented here. What would close it is a /research pass citing the notification that publishes the Thai GHS Section names, by instrument and clause, with the sixteen strings checked against it one by one — after which ADR-0060's Context and CONTEXT.md's Section Heading entry both gain a citation. Until then, the Thai strings in the constant are unsourced, and the golden fixture pins them without vouching for them.

This ruling may not be re-decided

If a change contradicts this ADR: stop and raise it. Do not implement over it.

Specifically: do not re-add the display prohibition to the pair count, do not cite D-49 for the named curator, do not read ADR-0062's twelve codes as the closed set, and do not treat the eleven rejected options above as the owner's rulings.

Consequences

What becomes true. Five ADRs can be read without acting on a sentence known to be wrong. The eleven rejected options are attributed, so a later reader can weigh them as arguments rather than obey them as rulings. The Thai-heading gap is written down where a reader of the heading ruling will meet it.

What this costs. Five ADRs now require this document to be read alongside them, which is the price of never editing an Accepted ADR and is paid every time that rule holds. The research gap stays open and the constant stays unsourced until someone runs the pass.

What is closed off. Editing the amended ADRs, and reading any of the eleven rejected options as the decider's own words.

Coverage

UpstreamLanded inEvidenceNote
D-136D-153ADR-0062's blank-field rule is the sentence D-153 draws the boundary of, in the API's favourADR-0062, amended here and standing
D-44D-153the reconciliation outcomes are what make a caveat required by the value beside it rather than by the schemaADR-0022
D-145Dropped: the pair-count ruling is untouched — only ADR-0069's added display prohibition is withdrawn, and that sentence was never part of D-145 (D-104)