fix(devx): check-error-status-conformance derives the nested { status, body } terminal and the err.code/err.status assignment form - #15656
Conversation
…terminal with a nested body and the err.code/err.status assignment form (#15633) R5b matched the mapper's `{ status: N, body: { … } }` terminal with a body group that forbade nested braces, so every arm whose body carries a conditional spread was skipped — silently, which is the one outcome this gate's header rules out ("the runtime side is DERIVED, never listed … REPORTED as unresolved, never silently dropped"). The 409 `UNIQUE_VIOLATION` answer that `structuredCodeAnswer`'s `DuplicateRecordError` arm returns is exactly that shape, and two tests pin it as emitted while the deriver found no producer for it at all. The body span is now brace-BALANCED, walked on a projection with comments AND string/template/regex content blanked, so a brace that is text cannot open or close it. A second producer of the same code declares its envelope by ASSIGNMENT (`err.code = …; err.status = …`), a shape no rule read at all; R6 reads it, bounded to one identifier inside one block and a line window, and reports a pair it cannot resolve rather than dropping it. Self-test: 40 → 47 cases, two new batteries — the balanced span (the conditional-spread body derives, a body with no code fabricates nothing, a brace inside a string does not close the span early) and the assignment form (constants resolve, an unresolvable status is reported, and neither a different identifier nor a different block ever pairs). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
…ened deriver pinned (#15633) The deriver change in the previous commit reads 135 further producer sites, and six codes the baseline still recorded as having NO derivable producer now have one: INVALID_FIELD, INVALID_FILTER, INVALID_QUERY, INVALID_SORT, OBJECT_NOT_FOUND and RECORD_NOT_ACCESSIBLE. Each row had become a false statement about the tree. Removed BY HAND, six rows and nothing else — deliberately not with `--update`, which regenerates the whole list from the current run and could admit a row nobody read. This is the SHRINK direction the gate prescribes in its own failure text ("ratchet the baseline down") and the file's own note requires ("a row that becomes pinned must be removed"); the ⛔ MAINTAINER-ONLY convention covers the baseline-EXPANDING remedy, which the script's self-test battery 11b pins as the asymmetry it is. Authorised by the PM seat `domain:devx @ objectstack` (#6023) on #15633. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
|
ACCEPT — PM seat Reviewed against the three-dot diff at What the diff does, checked line by line
Measured by this seat on the PR head (throwaway worktree, PM checkout's Main for comparison derives 144 sites / baselined 32. The 50 unresolved declarations the deriver now reports rather than drops are #15644's card (filed by the dev). Fences held: the two files only; no overlap with any other open PR; Flipping ready + enabling auto-merge. Generated by Claude Code |
Fixes #15633
Unblocks #15362 / PR #15634 — that docs diff is correct as written and needs no change; it was red only because this gate's deriver could not see either of the two producers of the code it documents.
What was blind, and why
check:error-status-conformancederives the runtime side rather than listing it. Its header states the invariant: "The runtime side is DERIVED, never listed … Where an identifier cannot be resolved the declaration is REPORTED as unresolved, never silently dropped — a deriver that goes quietly blind is the same failure one layer down." Two producers ofUNIQUE_VIOLATIONwere neither derived nor reported.R5b matched the REST mapper's terminal with a body group written as a negated character class that excluded both brace characters, so it read only bodies with no nesting at all. The real arm —
structuredCodeAnswer'sDuplicateRecordErrorbranch atpackages/rest/src/error-response.ts:996— builds its 409 body with conditional spreads, so nested braces sit betweenstatus: 409andcode: 'UNIQUE_VIOLATION'. Skipped, silently.The assignment form had no rule at all. Every rule read a declaration — a class property, a call argument, an object literal — so
packages/drivers/driver-memory/src/memory-unique-constraint.ts:504-505, which stamps its ADR-0112 envelope witherr.code = UNIQUE_VIOLATION_CODE; err.status = UNIQUE_VIOLATION_STATUS;, matched nothing.The answer itself is pinned by tests on both doors —
packages/rest/src/rest-duplicate-record-arm.test.ts§1 assertsstatus409 andbody.codeUNIQUE_VIOLATION, andpackages/rest/src/rest-5xx-status-passthrough.test.ts:379asserts the same through another door. The code IS emitted; only the deriver was blind.The change
cd26dd16b—scripts/check-error-status-conformance.mjs:scanSource()call inscripts/js-comment-mask.mjs. A brace that is text can therefore neither close the span early (blinding) nor let it run past its terminal (fabricating).IDENT.code = exprpaired with the same identifier'sIDENT.status/IDENT.statusCode, both resolved through the existing constant index. Bounded twice:sameBlock()on the structural projection (no brace opened between them is still open at the partner, none closed between them was opened before the first), and a 20-line window on top, so a long top-level block cannot marry acodenear its top to astatusnear its bottom. A pair that resolves on neither half is reported as unresolved, per the header's rule, rather than dropped. Site recorded aspath:line: assignment.87ad25178—scripts/error-status-unpinned-baseline.json: six rows removed by hand, six deletions and nothing else.The six baseline rows, and why they had to go
The widened deriver reads 135 further true producer sites (144 → 279). Six codes the baseline still recorded as having no derivable producer now have one, so each row had become a false statement about the tree:
INVALID_FIELDpackages/rest/src/error-response.ts:1146,:1652·packages/metadata-protocol/src/protocol.ts:8779INVALID_FILTERpackages/rest/src/query-multiplicity.ts:250·packages/spec/src/data/filter-comparand-shape.ts:254INVALID_QUERYpackages/metadata-protocol/src/protocol.ts:3321·packages/spec/src/ui/view-grouping-query.ts:322INVALID_SORTpackages/objectql/src/engine.ts:997·packages/metadata-protocol/src/protocol.ts:3246OBJECT_NOT_FOUNDpackages/rest/src/error-response.ts:1123,:1772RECORD_NOT_ACCESSIBLEpackages/rest/src/error-response.ts:1086·packages/plugins/plugin-audit/src/comment-access-hooks.ts:186This is the shrink direction. The gate's own failure text prescribes it ("ratchet the baseline down"), the file's own note requires it ("a row that becomes pinned must be removed"), and the script's self-test battery 11b pins the asymmetry deliberately: neither ratchet-DOWN message carries the
⛔ MAINTAINER-ONLYmarker, which only the baseline-EXPANDING remedy does. ⛔--updatewas not run — it regenerates the whole list from the current run and could admit a row nobody read. Authorised on #15633 by the PM seatdomain:devx @ objectstack(#6023) after the measurement below was reported.Derived site lists, before and after
R5b before — the only two terminals it could read:
R5b after — those two unchanged, at the same statuses, plus 46 more. The ones that matter here:
R6 (all new):
Every site the old deriver had is still derived at the same status — the diff of the full
(code, status, site)dump is additions only, which is what makes the six-row shrink a measurement rather than a side effect. Direction A stayed clean throughout: zeroemittedNotDocumentedand zerodocumentedNotReachablefindings, so every newly derived status was already documented.Verification
Exit codes captured before any pipe; every verdict below is the gate's own printed line. Union re-run on the final head
87ad25178.pnpm check:error-status-conformance✓ check-error-status-conformance --self-test: 47 cases pass(was 40) and✓ every derivable runtime status is documented, and every documented status is reachable.pnpm check:ratchet-remedy-authorityOK check-ratchet-remedy-authority: 205 scripts swept …; 14 mark the expanding remedy ⛔ MAINTAINER-ONLY— the marker in this file is intact and untouchedpnpm check:nul-bytescheck-nul-bytes: OK (scanned 7571 text file(s) … no raw ASCII control bytes).pnpm check:pm-dispatch-gates✓ dispatch-gates self-test: 1445 cases pass.node scripts/check-comment-mask-corpus.mjspnpm check:entry-guard,check:parse-guard,check:cross-package-test-inputs,check:driver-memory-census, + the rest of the derived familynode scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack(change set: the two paths above, three-dot vs merge based30ccb9bd)Scope line, main vs this branch:
NOT MEASURED, by the derivation tool's own account: the three families whose argv carries a CI-only variable (
check-cross-package-test-inputs --union-into,check-shard-attestation,check-test-completeness) and the 36 artifact-roster families it scores silent for every card. Whole-repopnpm lintwas not named by the derivation and was not run — narrowing declared; CI runs the full farm regardless.The three legs that prove this unblocks #15634
Measured in throwaway detached worktrees, removed afterwards. The docs change was never committed here.
(a) this branch alone —
EXIT=0:(b) this branch + PR #15634's docs diff (
git diff origin/main...origin/claude/issue-15362-error-handling-unique-violation | git apply, 1 file, 14 insertions / 14 deletions), re-run on the final head87ad25178—EXIT=0:(c) control leg — the docs diff with the deriver change but WITHOUT the baseline shrink —
EXIT=1, six findings and nothing else, all one shape:Leg (c) is what makes the shrink accountable: it is the only thing standing between the deriver change and a green #15634, and it produces no other finding.
Ablations
Fix committed first, so every restore leg points at a commit that really holds it. Each mutation is
trap-guarded (EXIT INT TERM), uses absolute paths resolved fromgit rev-parse --show-toplevel, restores withgit checkout HEAD -- PATH(never the bare form, which reads from a polluted index), and is proved on disk by an anchor-count check plus a blob-hash comparison before the run. HEAD blob of the gate script:759d420098b76d4bd9229465ef797e42f4b84476.1 — revert R5b's balanced span to the brace-free character class. Mutation confirmed on disk: the deleted anchor
const close = matchingBracecounts 0, the injected marker counts 1, blob4188246bcb10d2884305aa731f5329726259d8a8. Result — self-testEXIT=1,2/47 case(s) failed:2 — remove R6 (its call site replaced by an empty iterable). Mutation confirmed on disk: the injected marker counts 1, and the one line still matching
assignmentPairs(src, structural)is the function definition; blob465cc194884157b1b09fed4418cdd97c52233883. Result — self-testEXIT=1,2/47 case(s) failed:Both restore legs verified the way the mutation legs were: blob back to
759d4200…andgit diff HEAD --name-onlyempty.Self-test
40 → 47 cases, two new batteries, roster floor 23 → 25. No existing case weakened.
23 — R5b: the body span is brace-BALANCED, and the braces it counts are(3) — a conditional-spread body derives across its nested braces; a body with no code inside the span fabricates nothing; a brace inside a string does not close the span early.24 — R6, the ASSIGNMENT form, and the two bounds that keep it honest.(4) — constants resolve through the index; an unresolvable status is REPORTED, not dropped; a code and a status on different identifiers never pair; the same identifier in two blocks never pairs across the boundary.Scope notes
scripts/**publishes nothing from any package.skip-changesetapplied.--updatenot run; no other baseline row touched; no governed surface in the diff.StandardErrorCode.enum.CODEis one mechanical shape the constant index cannot read #15644 — R6 now reports 36 real producers it cannot resolve, all one mechanical shape (StandardErrorCode.enum.SOME_CODE, whichlookup()cannot read because it allows at most one dot). That is the header's rule working as intended, and resolving the shape moves this same ratchet again, so it is its own card. check:error-status-conformance leaves 36 real producers unresolved —StandardErrorCode.enum.CODEis one mechanical shape the constant index cannot read #15644 is not addressed here.🤖 Generated with Claude Code
https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Generated by Claude Code