Skip to content

fix(devx): check:error-status-conformance derives wire producers past the REST door's translating arms (#15749) - #15762

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-15749-error-status-in-process-only-codes
Sep 5, 2026
Merged

fix(devx): check:error-status-conformance derives wire producers past the REST door's translating arms (#15749)#15762
baozhoutao merged 2 commits into
mainfrom
claude/issue-15749-error-status-in-process-only-codes

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #15749
Unblocks #15631

Gate-script only: scripts/check-error-status-conformance.mjs and its --self-test. Nothing under packages/**, content/docs/**, .github/**, and scripts/error-status-unpinned-baseline.json is untouched (see "Baseline" below). Verified at 6cdb96f58.

The defect

The deriver read DuplicateRecordError's readonly status = 409 (packages/objectql/src/duplicate-record-error.ts) as "the runtime can emit HTTP 409 for DUPLICATE_RECORD" — its only producer, verbatim from --report on origin/main:

DUPLICATE_RECORD
    409  packages/objectql/src/duplicate-record-error.ts: class DuplicateRecordError

Per the maintainer ruling on #14723 that code is the engine's in-process identity: the REST door answers UNIQUE_VIOLATION on every route (structuredCodeAnswer's arm in packages/rest/src/error-response.ts:992; toRowApiError in packages/metadata-protocol/src/protocol.ts and toFailedResult in packages/rest/src/import-runner.ts apply the identical two-part gate to a batch row and an import row). So direction A required a page to publish 409 for a code that never crosses HTTP — the gate was green because the catalog published that claim, and correcting the page is what turned it red.

What changed

deriveDoorTranslations(sources) reads the translating arms out of the door's own source — a guard testing error?.code === 'FROM' (and, when the arm names one, the throwing class's error?.name === 'Class') whose { status, body: { code: 'TO' } } terminal answers a different code. Never a hand-written list of codes: that would be the second copy of a table this file's header argues against. Braces are walked on the structural projection for the reason R5b exists — the arms build their bodies with conditional spreads.

Derived repo-wide today: exactly one translation, DUPLICATE_RECORD (class DuplicateRecordError) → UNIQUE_VIOLATION at packages/rest/src/error-response.ts:992.

Three bounds keep the exclusion narrow, each pinned in --self-test:

  • R1 only — the rule that reads a throwing CLASS, the half the arm's guard names. Any other producer of the same code (a sendError door, a { code, status } terminal, an assignment pair) keeps it a wire producer. That is what "every path from its throw to HTTP goes through a translating arm" means in the only terms a deriver can read.
  • Both halves of the guard match — a different class declaring the same registered code is a different producer and keeps its producer.
  • Reported, never dropped — a new translated census prints on every run, and says when a page still publishes an entry for the translated code. A translated code is also kept out of the unpinned census: that census exists for a doc claim nothing pins, and here the door pins the answer, under its wire spelling.

Reading 1 — the unmodified tree (this branch), before and after

Both runs node scripts/check-error-status-conformance.mjs, exit code captured before any pipe.

Line before (origin/main) after why it moves
producer sites 315 producer site(s) derived 314 producer site(s) derived the one suppressed class declaration
reconciled 26 code(s) with a derived producer, 27 (code, status) pair(s) 25 code(s) with a derived producer, 26 (code, status) pair(s) DUPLICATE_RECORD @409 was one code and one pair
unpinned unpinned: 26 … (baselined: 26) unpinned: 26 … (baselined: 26) unchanged — the translated code is kept out of that census
scope 52 codes, 2245 files scanned identical the vocabulary and the scan are untouched

New line, after:

  translated: 1 class declaration(s) a door translates away before HTTP, so the thrown code is an in-process contract and NOT a wire producer (reported, and kept out of the unpinned census — the door pins the answer under its wire spelling) —
      DUPLICATE_RECORD (declared 409 at packages/objectql/src/duplicate-record-error.ts: class DuplicateRecordError) → UNIQUE_VIOLATION at packages/rest/src/error-response.ts:992 — NOTE: a scanned page still publishes an entry for DUPLICATE_RECORD

Verdict, EXIT=0: ✓ every derivable runtime status is documented, and every documented status is reachable.

Reading 2 — the purpose: PR #15750's tree

content/docs/api/error-catalog.mdx taken from claude/issue-15631-15632-error-docs-wire-shape (447fcf93c) and applied in the worktree without committing it (git apply, doc blob dc208c30e25772cfdb, 2 hunks, proved on disk), gate run with cwd pinned to the worktree, then reverted (git checkout HEAD --; doc blob back to dc208c30e, git diff HEAD empty).

  • base deriver (origin/main's copy of the script) on that same tree — EXIT=1:
      ✗ DUPLICATE_RECORD: the runtime can emit HTTP 409, and no doc publishes that status for it (documented: nothing). Emitted at packages/objectql/src/duplicate-record-error.ts: class DuplicateRecordError. …
    ✗ check:error-status-conformance — 1 finding(s).
    
    the finding the card measured, reproduced here.
  • this branch's deriver on that same tree — EXIT=0: ✓ every derivable runtime status is documented, and every documented status is reachable. 314 producer site(s), 25 code(s) … 26 (code, status) pair(s), unpinned: 26 … (baselined: 26), and the census NOTE correctly absent — nothing on that tree documents DUPLICATE_RECORD any more.

The "bare ### DUPLICATE_RECORD heading" green was refused and is not used anywhere here.

Ablation — both halves of the change, trap-guarded, absolute paths

Each leg: mutate, prove the mutation landed on disk (marker count + blob hash differs from the HEAD blob), run, restore with git checkout HEAD -- ABSOLUTE_PATH, prove the restore (marker count 0, blob back to the HEAD blob 2696baad0, git diff HEAD empty), re-run. No build step is involved — the gate is a .mjs run directly, there is no dist/ between the edit and the run.

leg mutation --self-test which cases failed gate on the unmodified tree
1 deriveDoorTranslations returns [] (derivation neutralised) EXIT=1 26, 26b, 26g EXIT=0
2 the !translatedCodes.has(code) guard removed from reconcile EXIT=1 26c EXIT=1✗ DUPLICATE_RECORD: documented with an HTTP status, but no producer declares one …
restore (both legs) EXIT=0, 58 cases none EXIT=0

Leg 2 is also the answer to "why not just let it fall into the unpinned census": without the exclusion this branch would red main today and the only remedy the gate offers is admitting a row into scripts/error-status-unpinned-baseline.json — the MAINTAINER-ONLY weakening path — which #15750 would then immediately have to ratchet back down.

Honest note on leg 1: cases 26c, 26d, 26e, 26f, 26h stay green under it. They are not discriminators of that leg (with no exclusion the code has a producer, so it is not unpinned either) — they are the controls, and 26c discriminates leg 2.

Self-test

node scripts/check-error-status-conformance.mjs --self-test50 cases before, 58 after, EXIT=0. New battery 26 — a code the DOOR TRANSLATES away is not a wire producer: derived from (floor 8, roster floor raised 26 → 27):

  • 26 a class whose code the door translates is NOT a wire producer
  • 26b the excluded declaration is REPORTED, with the arm that translates it
  • 26c a still-catalogued translated code is kept OUT of the unpinned census (this is main's state today)
  • 26d the WIRE code the arm answers keeps its producer at the arm's status
  • 26e POSITIVE CONTROL: an untranslated code (TIMEOUT @504) keeps its producer and reconciles
  • 26f NEGATIVE CONTROL: with no translating arm the same class IS a wire producer
  • 26g the guard's CLASS is load-bearing: another class speaking the code keeps its producer
  • 26h an arm that answers code: error.code (the capability gates) translates nothing

Baseline

scripts/error-status-unpinned-baseline.json is not edited: unpinned: 26 (baselined: 26) before and after, no row added and none removed. Another seat's draft #15708 removes one row there; this branch does not touch the file, so there is nothing to merge against it.

Gates run at 6cdb96f58

Family derived with node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack (EXIT=0, change set 1 path(s) vs merge base 66e68adc6, derived from this repo's tree — not a hand list). All 29 runnable commands were run, exit code captured before any pipe: 23 exit 0, 6 NOT MEASURED.

NOT MEASURED, by the prerequisite each one names — this box has no node_modules for them (re-run with the workspace node_modules linked in, same result):

  • node packages/lint/scripts/check-reference-carrier-shape.mjs (and --self-test) — exit 3, PREREQUISITE NOT MET — the dependency typescript is not installed
  • node scripts/check-ci-filter-parity.mjs — exit 3, dependency yaml
  • node scripts/check-closing-keyword-parity.mjs (and --self-test) — exit 3, dependency yaml
  • node scripts/check-comment-mask-corpus.mjs — exit 1, Cannot find package '@typescript-eslint/parser'
  • pnpm check:driver-memory-census — exit 3, dependency typescript

Green and load-bearing here: pnpm check:error-status-conformance (self-test + gate), pnpm check:nul-bytes, pnpm check:entry-guard, pnpm check:ratchet-remedy-authority, pnpm check:parse-guard, pnpm check:pm-dispatch-gates, node scripts/check-self-test-wired.mjs, node scripts/check-declaration-mirrors.mjs, node scripts/check-whole-set-label-write.mjs, node scripts/pm/bare-root-worklist.mjs --self-test.

node scripts/pm/check-governed-merges.mjs --test scripts/check-error-status-conformance.mjs✅ NOT governed — ordinary queue landing applies to a PR with exactly this file list.

Changeset

None, skip-changeset. AGENTS.md: a patch changeset is owed by "a bug fix in a released package … ⛔ never skip-changeset: that label is for a diff that publishes nothing from any released package." This diff is one repo-internal CI gate script; no released package publishes a byte of it.

🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

`check:error-status-conformance` read `DuplicateRecordError`'s `readonly
status = 409` as "the runtime can emit HTTP 409 for DUPLICATE_RECORD", so
direction A required a scanned page to publish 409 for a code that never
crosses HTTP. Per the #14723 ruling that code is the engine's in-process
identity; the REST door answers `UNIQUE_VIOLATION` on every route.

`deriveDoorTranslations` reads those arms out of the door's own source — a
guard on `error?.code` / `error?.name` whose `{ status, body: { code } }`
terminal answers a different code — never a hand-written list. R1 skips a
class the door translates away and REPORTS it in a new `translated` census;
a translated code is kept out of the unpinned census too, since the door
pins the answer under its wire spelling.

Pinned in `--self-test` battery 26 (8 cases): the arm-less negative control
still produces, the guard's class name is load-bearing, a `code: error.code`
arm translates nothing, and an untranslated code is untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 5, 2026
@github-actions github-actions Bot added the size/m label Sep 5, 2026
@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

ACCEPT — PR #15762 (head 6cdb96f58, scripts/check-error-status-conformance.mjs +294/−12) reviewed against the GitHub three-dot diff, not the report.

The deriver change is what the ruling asked for and nothing else: deriveDoorTranslations reads the translating arms from packages/rest/src/error-response.ts's source (an if (… .code === 'X' / .name === 'Class') guard whose body answers status: NNN, body: { code: 'Y' }), deriveRuntimeStatuses drops a class-declared status when a door translates that class/code away, and reconcile keeps such a code out of the "documented but no producer" remedy via translatedCodes — no hand list of codes. Pinned in the battery (26, 26b–26h incl. the positive control TIMEOUT @504, the negative control "no arm ⇒ still a producer", and the load-bearing class guard); SELF_TEST_BATTERY_FLOOR 26 → 27. Baseline file untouched (unpinned 26 / baselined 26 before and after).

Measured by this seat (detached probe worktree at 6cdb96f58, plain node; EXIT read before any pipe):

Flip + arm when both required jobs report success on 6cdb96f58. On landing: #15749 closes; PR #15750 (#15631) merges origin/main, re-runs the gate (must read exit 0 as above), then flips.


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review September 5, 2026 06:57
@baozhoutao
baozhoutao enabled auto-merge September 5, 2026 06:57
@claude

claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Flipped ready + auto-merge enabled (06:57Z, method: MERGE). Required jobs on 6cdb96f58: Lint & Repo Gates = success, TypeScript Type Check = success; git merge-tree --write-tree origin/main <branch> → EXIT=0 against the current main. Watched; on landing #15749 closes and PR #15750 (#15631) merges main, re-runs the gate and flips.


Generated by Claude Code

@baozhoutao
baozhoutao added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit e52bb6a Sep 5, 2026
36 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-15749-error-status-in-process-only-codes branch September 5, 2026 07:38
baozhoutao pushed a commit that referenced this pull request Sep 5, 2026
Brings in #15762 (#15749), the error-status-conformance deriver fix that stops
counting a door-translated in-process class as a wire producer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants