Uh oh!
There was an error while loading. Please reload this page.
docs(rest,spec): qualify the ADR-0112 "producer names the condition" citations to the code axis - #12453
Conversation
…citations to the code axis The sentence is cited at nine in-repo sites as an unqualified principle of ADR-0112. The ADR does not contain it, and every decision it does contain (D1-D9 plus five amendments) rules the semantic CODE vocabulary; HTTP status appears only as what a code derives from and what lives on the transport. Read unqualified, the citation was taken for a status ruling and argued for a 500 on a contract question the ADR is silent on. Each site now names the axis: ADR-0112 D4 governs the semantic-code channel, and rules no HTTP status for an undeclared throw. error-response.ts carries the full statement once, including that the phrase is that file's own prose rather than an ADR quotation, and the other sites echo it. Comment text only -- no behaviour, no schema, no assertion changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjujZN219uFzBhSYfMykCd
📓 Docs Drift Check1 anchor(s) derived from 2 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 128 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4bb5f339e8e6a2cabeecdb695c93fa3f126a907d && git checkout 4bb5f339e8e6a2cabeecdb695c93fa3f126a907d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8515954fb6dd4102e120b59ce6410f94600d5710 a17b6b649e9fad4ffba228fe1ec02142b284b237 && git checkout -B drift-repro 8515954fb6dd4102e120b59ce6410f94600d5710 && git merge --no-ff a17b6b649e9fad4ffba228fe1ec02142b284b237
node scripts/docs-audit/affected-docs.mjs --json 8515954fb6dd4102e120b59ce6410f94600d5710 |
Uh oh!
There was an error while loading. Please reload this page.
… axis PR #12453 qualified nine sites; three carried the unqualified attribution still — one because it was fenced by an in-flight PR at the time, two because they sat outside that card's declared file surface. Each now echoes the form `error-response.ts`'s docblock states in full: ADR-0112 D4 governs the semantic-CODE channel, the producer names the condition on the CODE axis, and the ADR rules no HTTP status for an undeclared throw. Comment text only — the emitted JS is byte-identical under a `removeComments` transpile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjujZN219uFzBhSYfMykCd
Fixes#11735
Nine in-repo sites cite the sentence "the producer names the condition" as an unqualified principle of ADR-0112. The ADR does not contain the sentence, and does not rule the axis the unqualified form was read on. Each site now names the axis.
What was measured, on
origin/mainat8515954fbThe card's zero-hit reproduces exactly, both halves. Re-run rather than inherited:
Reverse-check on the same file, terms taken from the ADR's own subject rather than from the phrase under test:
declaredCode7,vocabulary22,ledger16. The instrument produces positives over that file; the phrase is absent.The substance reading holds, checked directly against the ADR. D1-D9 (plus D6b, D6c and five amendments) every one rule the semantic code: D2 renames the closed
StandardErrorCodecatalog, D3 registers service codes in the ledger, D4 closesApiErrorSchema.codeand opensdeclaredCodebeside it, D5 fixes one eventual location for the semantic code. HTTP status appears only as what a code derives from ("error.codecarries the member the HTTP status derives", maintainer rulings 2026-08-16 / 2026-08-17) and what lives on the transport (D5). Nothing in ADR-0112 rules what HTTP status an undeclared throw deserves.The premise fork resolved toward the ruled remedy, and there is a landed sibling that already says so
Triage's binding condition was to stop and report if the status-axis reading turned out to be a real ruling from somewhere. It is not:
docs/adr/forproducer names/producer that knows/producer declares/one condition, one wire shape/producer-declared.declaredHttpStatusanywhere underdocs/.More decisively,
packages/rest/src/rest-hook-refusal-message-parity.test.ts:630— landed 2026-08-25 in #12280, after this card was filed — already carries the ruled qualification and reaches the same conclusion independently:It also names what does rule the status: the
/datadoor's structural branch (declaredHttpStatus(error) ?? 400) with two end-to-end pins behind it. This PR aligns the remaining sites with that already-landed reading rather than inventing a second formulation.The census is not nine
Measured independently and not reconciled with the card's table. The card's own re-check command returns 11 on today's
origin/main, and the wrapped-phrase form returns 13 citation sites. All nine of the card's files are still present; two line numbers moved (error-response.ts871 to 923,analytics-filter-refusal-envelope.test.ts405 to 447).Four sites are outside the declared file surface and are therefore untouched here:
packages/rest/src/rest-server.ts:9646packages/rest/src/rest-sandbox-declared-status.test.ts:130packages/rest/src/rest.test.ts:2422packages/rest/src/rest-hook-refusal-message-parity.test.ts:630So two sites plus the fenced one still carry the unqualified citation after this PR. Reported for the PM to route rather than absorbed silently — the surface was declared closed and "stop on breach and explain" applies.
packages/spec/src/shared/external-errors.ts:47is making a DIFFERENT claimThe card flagged this site as unexamined. Read on its own terms, it is not one of the eight: the other eight all say "no code was declared, so none is invented" — squarely the code axis. This one sits under the heading "Why HERE and not in a REST error map" and cites ADR-0112 to justify declaring the HTTP status on the error at the producer. That is the status axis, and it is the one place where the ruled remedy could not be applied verbatim: qualifying it to the code axis would have made the sentence a non-sequitur in its own paragraph.
It is corrected on its own terms instead. The paragraph's argument is preserved and its real warrant named — this repo's convergent HTTP-exit convention (
statusthenstatusCode, 400-599, the exits the docblock already enumerates) rather than an ADR-0112 ruling. Note the same file already cites ADR-0112 D3 correctly, two paragraphs above, for the ledgeredcode; that citation is untouched.Nothing else in
packages/specis touched: no schema, no key, no describe-text, no shape.Why this is worth more than a comment tidy
The unqualified form was read as governing status and nearly settled a contract question the wrong way: #11684 named it as the argument for
500on/analytics/dataset/query, and PR #11731 carried a binding stop that would have landed nothing had both readings had live pins. It survived only because a dev measured that the ADR does not say it. Same class as #11032.rest-hook-refusal-status-passthrough.test.tsis the site nearest that misreading — a status-passthrough test whose comment could be read as ADR-0112 ruling the 500 — so it now says explicitly that the 500 is the classifiers' default (#7525), not an ADR-0112 ruling.The card's other unmeasured question, answered
Whether any other principle is attributed to ADR-0112 in the same unqualified way: no. The only other principle-style attribution in the tree is
packages/metadata-protocol/src/protocol.legacy-overlay-delete.test.ts:405, and it is already correctly scoped ("the catalog governserror.code"). The widespread "ADR-0112 envelope (code+status)" idiom (50+ sites) is a different usage — it names the wire envelope as an assertion contract, not a principle about who names what — and is the form the repo's own agent contract prescribes for rejection tests. Not a defect; measured and left alone.Scope
Comment text only. Zero behaviour change, zero assertion changes, no schema, no key, no validity boundary.
docs/adr/**untouched — the ADR amendment remains a maintainer-merge path and is explicitly out of scope; nothing here is written as though the ADR were about to change, and the correction holds under either outcome of that open question, which is why it forecloses nothing.Verification
All runs below are on the final commit
a17b6b649, working tree clean at measurement time.Gate families re-derived in this worktree from the actual changed files —
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which derives its own change set from the merge base (8 paths vs8515954fb) and reported 22 matched families.pnpm lint(full repo,eslint . --no-inline-config)pnpm check:nul-bytesOK (scanned 6888 text file(s) ... no raw ASCII control bytes)pnpm check:route-envelope4 module(s) discovered and audited ... 2 conformant, 2 ratcheted(pre-existing ratchets, unmoved)pnpm check:dispatcher-error-vocabularyOK — 21 unregistered code-stamping site(s), all classifiedpnpm check:test-source-aliasOK — 72 packages with tests scannedpnpm check:cross-package-test-inputsOK: 18 package(s) read outside themselves, all declaredcheck-comment-mask-adoption.mjsOK — 23 private comment-stripper(s) ... all 23 recordedpnpm check:engine-double-contract398 (file, verb) row(s) held by the RETAINED ledgerpnpm check:where-matcherconformance holds: 303 matcher(s) ... none newpnpm check:query-options-erasureratchet holds ... none new@objectstack/spectypechecktest layer compiles ... 263 error(s) held(unmoved)@objectstack/speccheck:liveness/check:empty-state/check:variant-docs@objectstack/resttest filesBuild closure first, as a new worktree requires:
turbo run build --filter='@objectstack/rest...' --filter='@objectstack/spec...'— 25/25 tasks successful. Every heavy run went throughscripts/pm/os-verify-lock.sh; exit codes were captured before any pipe, and each row above quotes the gate's own verdict line rather than a shell$?.One NOT MEASURED, stated rather than papered over:
@objectstack/rest'stsc --noEmitprogram excludes*.test.ts— verified with--listFiles, which returns 0 hits for each of the six edited test files and 1 forerror-response.ts. So the rest typecheck is a real green over the one edited source file and says nothing about the six test files; those are measured by the vitest run instead, not by typecheck.No changeset: comment text only, nothing user-visible to publish.
skip-changesetcould not be applied from the dev seat —api.github.comreturnsGitHub access is not enabled for this session, andghis not installed, so the label needs applying by the PM.Generated by Claude Code