Uh oh!
There was an error while loading. Please reload this page.
feat(devx): check:engine-double-contract gains the consumer-seam refusal invariant (#8194) - #8423
Merged
Merged
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
qq9340100
marked this pull request as ready for review
August 13, 2026 12:54
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#8194
check:engine-double-contractgains a second, walled-off subject: the CONSUMER SEAMS. Scope is the finding-grading ruling's option 2 only — pin the consumers, not the doubles. Option 1 and the card's own title shape stay excluded, and no ledger entry was added in either direction.The ruling's premise did not survive measurement, and the gate is narrower for it
The ruling scoped this to "every consumer performing a by-id write probes existence first — a small, decidable set". Measured on
origin/main, that predicate is neither small nor true:update/deletecarrying a scalarwhere.id), across 23 filesBecause #7867/#7989 put the gate at the funnel, not at each consumer.
ObjectQL.update/.deleteread the prior row unconditionally on their by-id branch and throwrecordNotFoundErrorwhen it is missing —engine.tssays so in as many words ("placed at the one point all of them funnel through, so it is not a fourth site"). Asql-http-outbox.ack(id)or adb-queue-adapter.purgeFailed(messageId)is already refused. Shipping the literal predicate would have reddened ~40 correct call sites — the identical 37-false-positive shape the ruling used to exclude the title's own gate, one layer out.So the scan carries three conjuncts, each measured for what it removes:
Result: 6 seams (4 functions,
callDataholding two verbs), 0 false positives, 0 exemptions, no ledger.The assertion is REFUSAL, not "probes"
The four seams do not share a mechanism, and demanding the commonest one would redden correct code:
protocol.updateDataprobes and shares the read with its OCC gate;protocol.deleteDatareads the driver'sPromise< boolean >(=== falseis the contract's own positive not-found signal);callDataprobes with afind; the MCP bridge probes withfindById. So the gate asks only that the function refuses before it answers — same contract-first shape as the slices above it: pin the decision that must be reached, not the road to it.Discovery reads three options spellings, and the second is load-bearing rather than a nicety:
protocol.updateDataanddeleteDataboth buildconst opts = { where: { id: request.id } }and pass the variable, so a scan reading only inline literals discovers neither — it would report a green tree it never looked at. That is pinned by a named self-test assertion and by a real-tree wiring assertion.What it deliberately does not claim
recordNotFoundError;packages/mcp/src/stdio-data-bridge.tsmints a local bareErrorwith neithercodenorstatus. Filed as The stdio MCP bridge mints its own not-found error — noRECORD_NOT_FOUNDcode, no 404 — where every sibling seam throws the sharedrecordNotFoundError#8422 rather than laundered through a ledger entry — that package is outside this card's declared file surface, and a gate opening RED on a defect its own PR did not introduce teaches readers that red means "someone else's problem". The verdict records and prints which envelope each seam reaches, so the divergence stays visible; tightening to shared-only is a one-line change once The stdio MCP bridge mints its own not-found error — noRECORD_NOT_FOUNDcode, no 404 — where every sibling seam throws the sharedrecordNotFoundError#8422 lands.check:engine-double-contractstill cannot see it #8194 excluded on the double side for the same reason. Stated in the header rather than left for a reader to discover.Verification
node scripts/check-engine-double-contract.mjs— OK; 193 pinned, 133 DEBT, 2 exempt (all unchanged), plusconsumer seams: 6 in 3 source file(s) — 4 refusing through recordNotFoundError, 2 through a locally minted error, 0 not refusing at all.node scripts/check-engine-double-contract.mjs --self-test— OK.$inassertion passed for the wrong reason (rootIdentifieralready rejects object literals, so the scalar test was a phantom check) — now pinned onscalarWhereIdOfdirectly, where a mutation can reach it;new Error, leaving the call arm untested — the local-factory spelling (the MCP shape) and the unresolvable-imported-factory spelling are now both fixtures.pnpm check:nul-bytes— OK (7602 files, 0 control bytes); plus a directgrep -naPself-scan of the changed file.npx eslint scripts/check-engine-double-contract.mjs— clean.node scripts/pm/dispatch-gates.mjs scripts/check-engine-double-contract.mjsre-derived against the actual changed path: no path-scoped family beyond the repo-wide set. No test file added — the coverage is embedded in--self-test, this file's own convention — socheck:type-check-coverage/check:type-check-debtare not pulled in.Scripts-only, no runtime behaviour changed ⇒
skip-changeset. The consumer seams inpackages/objectqlandpackages/metadata-protocolwere read as the gate's subject and never edited; the diff is one file.Generated by Claude Code