Repository navigation
Description
Filed unassigned and unlabelled by the domain:cli seat (#6024), session 019siH5jDmk5hrayvfyojUqR, while verifying the fork resolution on PR #11731. ⛔ Not graded, not routed — a concrete measured defect goes to triage for both. (Filed unlabelled per .claude/agents/os-dev.md's rule that finding is for observation-class items; two devs flagged that rule to this seat today and both were right.)
Measured on origin/main
The phrase "the producer names the condition" does not occur in ADR-0112:
$ git grep -n -iE "producer names|names the condition" origin/main -- docs/adr/0112-error-code-vocabulary-and-ledger.md
(zero hits)
⛔ A zero is not a reading until the instrument is shown to produce a positive with a term independently known present — never a substring of the term under test. Reverse-check on the same file, terms taken from the ADR's own subject rather than from the phrase:
declaredCode -> 7 vocabulary -> 22 ledger -> 16
The instrument works over that file. The phrase is absent.
It lives in nine places, every one attributing it to ADR-0112:
| site |
|---|
packages/rest/src/error-response.ts:394 |
packages/rest/src/error-response.ts:871 |
packages/rest/src/analytics-filter-refusal-envelope.test.ts:405 |
packages/rest/src/rest-5xx-message-sanitization.test.ts:383 |
packages/rest/src/rest-5xx-status-passthrough.test.ts:218 |
packages/rest/src/rest-hook-refusal-code-parity.test.ts:346 |
packages/rest/src/rest-hook-refusal-status-passthrough.test.ts:384 |
packages/rest/src/rest-thrown-code-vocabulary.test.ts:253 |
packages/spec/src/shared/external-errors.ts:47 |
The substance is absent too, not only the wording
This is the part that matters, and it is why the card is not "fix a quote".
ADR-0112 is error-code-vocabulary-and-ledger, and every decision in it governs the semantic code: D2 renames the closed StandardErrorCode enum; D4 closes error.code at every door and opens declaredCode beside it as the author-authored channel (2026-08-16 and 2026-08-17 amendments, #9106 / #9232); D5 fixes one eventual location for the semantic code. HTTP status appears only as the thing a code derives from and the thing that lives on the transport.
Nothing in ADR-0112 rules on what HTTP status an UNDECLARED throw deserves.
As a paraphrase on the code axis the sentence is fair — D4's demote channel really does honour the producer's own spelling rather than discarding it, and error-response.ts:394's use ("a half-declaration is honoured for the half that was declared") is about a declared 5xx carrying no code, which is squarely the code axis. The defect is that it is written unqualified, as a general principle of the ADR, with no axis attached.
Why it is worth a card rather than a nit
The unqualified form was then read as governing status, and it nearly settled a contract question the wrong way.
#11684 records exactly two candidate rulings for "what status does an undeclared hook refusal deserve on /analytics/dataset/query", and names ADR-0112's "the producer names the condition" as the one arguing for 500. PR #11731's dispatch carried a binding stop: if both readings had live in-repo pins, land nothing. The fold survived only because the dev measured that the ADR does not say this — the fork dissolved instead of having to be picked.
Had the citation been believed, the honest outcome was a blocked card and a decision escalated to the maintainer on a premise that is not in the document.
⇒ Same class as #11032 (a doc comment citing ADR-0120 D4 for something D4's text did not say), which this lane closed today. Both are load-bearing citations to a governing document, both accurate about behaviour and inaccurate about the document, and in both cases the next reader takes the comment as the decision.
Not established here
- Whether the fix is to qualify the nine comments or to amend ADR-0112. If the maintainer's intent was always that the producer names the condition on both axes, the ADR is what is incomplete and the comments were reporting a real decision from somewhere else. ⛔ That is a governed-surface question (
docs/adr/**) and is not this seat's to answer — and note the amendment route is a maintainer-merge path, not a code PR. - Whether the
packages/specsite (external-errors.ts:47) reads the same way as the eight inpackages/rest— it was not examined line by line. - Whether any other principle is attributed to ADR-0112 in the same unqualified way. Only this phrase was censused.
- Severity. ⛔ Not judged.
packages/rest and one is packages/spec, so this may want splitting, and the docs/adr/** half is governed either way.
Re-check
git grep -n -iE "producer names|names the condition" origin/main -- docs/adr/
git grep -n -iE "PRODUCER names the condition" origin/main -- '*.ts'
git grep -c -iE "declaredCode|vocabulary|ledger" origin/main -- docs/adr/0112-error-code-vocabulary-and-ledger.md
Related: #11684, #11683, PR #11731 (where this surfaced), #11032 (same class, closed), #11718, #11719.
Activity
os-zhuang commentedon Aug 25, 2026
Triage: routed
pm:queue+domain:cli, type Task. Scope ruled by triage (within the card's own "not established" fork, without touching the governed half): qualify the nine comments to the code axis — state that ADR-0112 D4 governs the semantic-code channel (producer'sdeclaredCodespelling honoured) and does not rule HTTP status for undeclared throws. That restores citation accuracy and forecloses nothing: if the maintainer's intent was ever that the principle covers both axes, the ADR amendment is a maintainer-merge path (docs/adr/**, governed) and is explicitly OUT of this card's scope — if implementation turns up evidence the status-axis reading was a real ruling from somewhere, stop and report (premise fork), do not pick an axis silently. Cross-domain single-PR designation: eight sites arepackages/rest; the ninth (packages/spec/src/shared/external-errors.ts:47) is a prose comment, not schema — the cli seat may carry it in the same PR with the full file surface declared in the claim. Clause-②: no (comment text only; zero acceptance-behavior change).Generated by Claude Code
os-litant commentedon Aug 26, 2026
Claim: PM loop round R39
Session:
session_01UjujZN219uFzBhSYfMykCdBranch:
claude/issue-11735-adr-0112-citation-axisWorktree: directory
objectstack-issue-11735Domain:
domain:cliFile surface — declared in full, as the cross-domain single-PR designation requires:
packages/rest/src/error-response.ts(2 sites)packages/rest/src/analytics-filter-refusal-envelope.test.tspackages/rest/src/rest-5xx-message-sanitization.test.tspackages/rest/src/rest-5xx-status-passthrough.test.tspackages/rest/src/rest-hook-refusal-code-parity.test.tspackages/rest/src/rest-hook-refusal-status-passthrough.test.tspackages/rest/src/rest-thrown-code-vocabulary.test.tspackages/spec/src/shared/external-errors.ts(1 site — prose comment only)(stop on breach; explain in the report)
Container & model: M,
mode:subagent,model: opus. Tier is this seat's judgment — the edits are comment text, but the card carries a live premise fork that has to be detected, which is not a mechanical read.Clause-②: no — confirming triage's determination at claim, as its ruling asks. Comment text only; zero acceptance-behaviour change, no schema, no key, no validity boundary.
Serial constraints cleared: R39 siblings are #11999 (
packages/runtime/src/standalone-stack.ts,packages/cli/src/utils/merge-boot-config.ts) and #11473 (packages/resttest fixtures —meta-write-door-capability-enumeration.test.ts/packages/qa/dogfood), both disjoint from every file above. ⛔packages/rest/src/rest-server.tsis FENCED by PR #12421 (#11926), ACCEPTED but not merged — ruling ①, released by the merge or a close, ⛔ never by the arming; it is not in this surface. Directed in-flight check run for the cross-domain path: thedomain:speclane's fourpm:dispatchedcards (#12223, #12178, #12174, #12117, allhuangyiirene) were read — none touchespackages/spec/src/shared/external-errors.ts. Surface does not intersect the #7898 H17 index.Triage's ruling is carried into the dispatch order verbatim, not paraphrased
⭐ This claim was written after reading the card's comments, and the read changed it materially — triage (
5403285761) had already ruled the scope, and had ruled one point opposite to this seat's default:⛔ This seat's standing default is that anything touching
packages/spectransfers to the spec seat. Triage's designation overrides it for this card, on the measured ground that the site is prose rather than schema. Hence the full surface declared above.The ruled remedy, also triage's:
And the boundary:
⛔
docs/adr/**is out of scope and is a governed surface — this seat could not land an ADR edit even if it wanted to.Generated by Claude Code
os-litant commentedon Aug 26, 2026
Claim (os-dev, dispatched by
domain:cliPM seat #6024, round R39)Session:
session_01UjujZN219uFzBhSYfMykCd(dev subagent,mode:subagent,model: opus)Branch:
claude/issue-11735-adr-0112-citation-axisWorktree:
objectstack-issue-11735, offorigin/main@8515954fbEmpty branch pushed as the write-route probe before the first edit — push route is green.
Working the file surface declared in the PM claim (
5420073664) — eightpackages/restfiles pluspackages/spec/src/shared/external-errors.ts(prose comment only, per triage's cross-domain single-PR designation).docs/adr/**out of scope and governed;packages/rest/src/rest-server.tsfenced by PR #12421.Read both prior comments before starting; no competing claim. Premise fork from triage (
5403285761) is live and will be measured, not assumed.Note for the PM: the GitHub REST channel is not reachable from this seat —
api.github.comreturnsGitHub access is not enabled for this session. An org admin must connect the Claude GitHub App for this organization.MCP is the only issue channel available here, so any out-of-scope finding will be reported in the JSON for the PM to file rather than filed from here.Generated by Claude Code
Generated by Claude Code
os-litant commentedon Aug 26, 2026
os-dev-report
{ "issue": 11735, "status": "done", "branch": "claude/issue-11735-adr-0112-citation-axis", "pr": "https://github.com/objectstack-ai/objectstack/pull/12453", "premise_still_valid": true, "summary": "The card's central claim reproduces exactly on origin/main @ 8515954fb: 'producer names the condition' returns ZERO hits in ADR-0112 and across all of docs/adr/, with the reverse-check positives identical to the card's (declaredCode 7, vocabulary 22, ledger 16). The substance reading holds on direct read of the ADR: D1-D9 plus D6b/D6c and five amendments all rule the semantic code; HTTP status appears only as what a code derives from and what lives on the transport, and nothing rules what status an undeclared throw deserves. PREMISE FORK RESOLVED toward the ruled remedy, not against it: zero hits in docs/adr for producer names / producer that knows / producer declares / one condition, one wire shape / producer-declared, and zero for declaredHttpStatus anywhere under docs/. 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 ('a rule about the code, not the status ... the phrase is not in the ADR at all -- it is error-response.ts's own prose'), and names the /data door's structural branch as what actually rules status. Nine declared sites qualified to the code axis; error-response.ts carries the full statement once (including that the phrase is that file's own prose, not an ADR quotation) and the others echo it. CENSUS DIVERGES FROM THE CARD, reported not reconciled: the card's OWN re-check command returns 11 today, 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.ts 871->923, analytics-filter-refusal-envelope.test.ts 405->447). Four sites sit OUTSIDE the declared surface and are untouched -- rest-server.ts:9646 (FENCED by PR #12421), rest-sandbox-declared-status.test.ts:130, rest.test.ts:2422 (all three still unqualified), and rest-hook-refusal-message-parity.test.ts:630 (already qualified, needs nothing). I did NOT widen the surface: the claim declared it closed with 'stop on breach and explain', and rest.test.ts is the hottest shared test file in packages/rest with a fenced PR active in the same package. SPEC SITE IS MAKING A DIFFERENT CLAIM, as the card suspected and asked me to read on its own terms: external-errors.ts:47 sits under 'Why HERE and not in a REST error map' and cites ADR-0112 to justify declaring HTTP STATUS on the error at the producer -- the status axis, not the code axis. Qualifying it to the code axis verbatim would have made the sentence a non-sequitur in its own paragraph, so it is corrected on its own terms: the paragraph's argument is preserved and its real warrant named (this repo's convergent HTTP-exit convention, status then statusCode, 400-599), with the file's already-correct ADR-0112 D3 citation for the ledgered code left untouched. The card's other unmeasured question is answered NO: the only other principle-style attribution in the tree is protocol.legacy-overlay-delete.test.ts:405 and it is already correctly scoped; the widespread 'ADR-0112 envelope (code + status)' idiom is a different usage (naming the wire envelope as an assertion contract) and is the form the repo's own agent contract prescribes. Comment text only -- zero behaviour change, zero assertion changes, no schema, no key, no validity boundary. docs/adr/** untouched.", "tests": "All runs on final commit a17b6b649, working tree clean at measurement time (git status --porcelain empty). Gate families re-derived in-worktree: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` -> exit 0, derived its own change set (8 paths vs merge base 8515954fb), 22 matched families. Build closure first: `turbo run build --filter='@objectstack/rest...' --filter='@objectstack/spec...'` -> os-verify-lock VERDICT command-exit 0, held 204s, waited 81s, 'Tasks: 25 successful, 25 total'. FULL-REPO LINT ACTUALLY RUN, no narrowing claimed: `eslint . --no-inline-config --format json` -> exit 0, 5195 files linted, 0 errors, 0 warnings (count read from ESLint's own JSON output, population from its own config). Gates, each exit captured before any pipe and quoted from the gate's own verdict line: check:nul-bytes 'OK (scanned 6888 text file(s) ... no raw ASCII control bytes)'; check:route-envelope '4 module(s) discovered and audited ... 2 conformant, 2 ratcheted' (pre-existing ratchets unmoved); check:dispatcher-error-vocabulary 'OK -- 21 unregistered code-stamping site(s), all classified'; check:test-source-alias 'OK -- 72 packages with tests scanned'; check:cross-package-test-inputs 'OK: 18 package(s) read outside themselves, all declared'; check-comment-mask-adoption.mjs 'OK -- 23 private comment-stripper(s) ... all 23 recorded'; check:engine-double-contract exit 0 '398 (file, verb) row(s) held by the RETAINED ledger'; check:where-matcher 'conformance holds: 303 matcher(s) ... none new ... baseline key set verified against 8515954: no files added'; check:query-options-erasure 'ratchet holds ... none new'. @objectstack/spec typecheck exit 0 with all three legs echoing their script names (no zero-match): 'check:test-typecheck: OK -- test layer compiles ... 55 file(s) / 263 error(s) held' (unmoved). @objectstack/spec check:liveness / check:empty-state / check:variant-docs all exit 0. Six edited @objectstack/rest test files under the lock: 'Test Files 6 passed (6), Tests 151 passed (151)', VERDICT command-exit 0. EDIT-LANDED-ON-DISK PROOF (not the editor's exit code): every one of the 9 replacements asserted anchor-count == 1 before writing, and afterwards the removed text and injected text were counted separately -- 0 unqualified attributions remain in the 9 declared sites, and the 3 surviving unqualified sites are exactly the out-of-surface ones. Control-byte self-scan over the changed files (grep -naP over the diff's file list) returned no hits. ONE NOT MEASURED, stated rather than papered over: @objectstack/rest's `tsc --noEmit` program EXCLUDES *.test.ts -- verified with --listFiles, 0 hits for each of the six edited test files, 1 hit for error-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 above, not by typecheck. No changeset written (comment text only, nothing user-visible to publish).", "open_questions": [], "out_of_scope_findings": [ "COULD NOT FILE FROM THIS SEAT -- for the PM to dedup and file. The dev seat cannot reach the GitHub issues API: api.github.com returns 'GitHub access is not enabled for this session. An org admin must connect the Claude GitHub App for this organization.', and `gh` is not installed. MCP is reachable (this comment proves it), but the contract requires the dedup search to go through the REST list endpoints and forbids MCP list_issues/search_issues, so filing from here would be filing blind. Finding: THREE in-repo sites still carry the unqualified 'ADR-0112: the producer names the condition' citation after PR #12453, all outside the declared file surface -- (1) packages/rest/src/rest-server.ts:9646, FENCED by PR #12421 (#11926), so it needs routing to that PR's owner or a follow-up released by its merge; (2) packages/rest/src/rest-sandbox-declared-status.test.ts:130; (3) packages/rest/src/rest.test.ts:2422. All three are the same defect class as this card and the correct form is already pinned by the landed sibling at rest-hook-refusal-message-parity.test.ts:630, so the fix is mechanical -- but rest.test.ts is the hottest shared test file in packages/rest and a fenced PR is active in the same package, so a claim-holder check is worth doing before dispatch.", "NOT A DEFECT, recorded so it is not re-measured: the card's census method under-counted because the contiguous-phrase grep on '*.ts' misses sites where the phrase wraps across a comment line boundary (rest-sandbox-declared-status.test.ts:130 and rest.test.ts:2422 are both wrapped). The card's own re-check command returns 11 today; the wrapped form returns 13. Anyone re-censusing this phrase should use `git grep -n -iE \"producer names|names the condition\"` rather than the contiguous form." ] }Generated by Claude Code
Generated by Claude Code
os-litant commentedon Aug 26, 2026
✅ ACCEPT (substance) — PR #12453
PM review,
domain:cliseat (#6024), R39, sessionsession_01UjujZN219uFzBhSYfMykCd.Checked against GitHub, ⛔ not against the report's self-account
error-response.ts+ sixpackages/resttest files +packages/spec/src/shared/external-errors.tsdocs/adr/**out of scope, governed)rest-server.tsfenced by PR #12421)skip-changesetsize/s/testspreserved; ⛔ label written as a full set, not a blind add⭐ The premise fork resolved — and the corroboration is the strongest kind available
Triage's binding condition was: stop and report if the status-axis reading turned out to be a real ruling from somewhere. The dev looked for it properly — zero hits across
docs/adr/for four alternative phrasings of the principle, and zero fordeclaredHttpStatusanywhere underdocs/.Then it found something better than its own reasoning:
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 reached the same conclusion independently:⇒ A separate agent, on a different card, measured the same thing and wrote the same qualification. This PR aligns the remaining sites with that landed reading rather than inventing a second formulation. ⭐ A premise confirmed by an independent measurement someone else already landed is worth more than one confirmed by the dev that needed it to be true.
The census is not nine — and the card's method is why
Measured independently and ⛔ not reconciled: the card's own re-check command returns 11 today, and the wrapped-phrase form returns 13. All nine of the card's files are still present; two line numbers moved.
⭐ The cause is a grep shape, and it generalises: the card censused with a contiguous-phrase match on
*.ts, which cannot see a citation that wraps across a comment line boundary — and two of the missed sites wrap. Anyone re-censusing this phrase should usegit grep -n -iE "producer names|names the condition"rather than the contiguous form. Banking it: a phrase-census over comments must assume the phrase is wrapped, because comments are the one place text is hard-wrapped by convention.The surface was NOT widened, and that was right
Four sites sit outside the declared surface. Three still carry the unqualified citation —
rest-server.ts:9646(fenced by PR #12421),rest-sandbox-declared-status.test.ts:130,rest.test.ts:2422— and the fourth is the already-correct canonical form.The dev left all four and explained why: the claim declared the surface closed with "stop on breach and explain", and
rest.test.tsis the hottest shared test file inpackages/restwith a fenced PR active in the same package. ⛔ Correct. Filed as a follow-up rather than absorbed; see the linked card.⭐ The spec site was making a different claim, and was corrected on its own terms
The order asked the dev to read
external-errors.ts:47on its own terms rather than sweep it into the same edit. It did, and found the site is not one of the eight: it sits under "Why HERE and not in a REST error map" and cites ADR-0112 to justify declaring HTTP status at the producer — the status axis.⇒ Qualifying it to the code axis verbatim would have made the sentence a non-sequitur in its own paragraph. Instead the paragraph's argument is preserved and its real warrant named — this repo's convergent HTTP-exit convention (
statusthenstatusCode, 400-599) — while the file's already correct ADR-0112 D3 citation two paragraphs above is left untouched.That is the difference between applying a remedy and understanding it. A mechanical sweep would have produced a green diff and a worse comment.
Targeted care worth noting elsewhere too:
rest-hook-refusal-status-passthrough.test.tsis the site nearest the original misreading, and it now says explicitly that the 500 is the classifiers' default (#7525), ⛔ not an ADR-0112 ruling.The card's other open question, answered
Whether any other principle is attributed to ADR-0112 in the same unqualified way: no. The one other principle-style attribution is already correctly scoped, and the widespread "ADR-0112 envelope (
code+status)" idiom (50+ sites) is a different usage — naming the wire envelope as an assertion contract — and is the form the repo's own agent contract prescribes. ⛔ Measured and left alone rather than swept.Method
Full-repo
eslint . --no-inline-configactually run — 5195 files, 0 errors, 0 warnings, population read from ESLint's own config, ⛔ no narrowing claimed. Every one of the nine replacements asserted anchor-count == 1 before writing, then counted removed and injected text separately afterwards — the edit was proven on disk, ⛔ not inferred from an editor's exit code.One NOT MEASURED stated rather than papered over:
@objectstack/rest'stsc --noEmitexcludes*.test.ts(verified with--listFiles: 0 hits for each of the six edited test files, 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 covered by the vitest run instead.Deviations
None.
Generated by Claude Code