Uh oh!
There was an error while loading. Please reload this page.
fix(scripts): ADR-0087 runtime-interface-only reads CODE, not docblock prose (#12881) - #12997
Merged
Merged
Conversation
…k prose (#12881) Step 4 of the `runtime-interface-only` predicate scanned whole metadata-surface files for the claimed symbol, so a JSDoc sentence naming a runtime type counted as an unresolvable reference and refused the disposition. Measured: the only `.zod.ts` in the tree naming `ObjectStackClient` is contract.zod.ts, which explains what `unwrapResponse` keys on in one docblock line. Mask comment spans with the shared scripts/js-comment-mask.mjs before every read step 4 performs. Strings stay unmasked on purpose. Self-test gains both directions: RIO-G2/G3/G4 (prose, line comment, commented-out import) and RIO-R10/R11/R12 (prose beside a code reference, beside a real import, and a string literal), plus RIO-R3 converted to a code-level mention.
…r0087-comment-blind-spot
) The digest self-test stages an executable COPY of check-adr-0087-registration.mjs into a throwaway repo and runs it. That gate gained a ./js-comment-mask.mjs import, which the sandbox manifest did not copy, so both #6494 ROUND TRIP cases died on ERR_MODULE_NOT_FOUND and read as a broken round trip. Fix the manifest, not the import: the single hard-coded invoked-as.mjs line becomes a list, matching how the gate's own I1/I2 fixture spells it.
…r0087-comment-blind-spot
hotlong
marked this pull request as ready for review
August 28, 2026 13:49
hotlong
enabled auto-merge
August 28, 2026 13:49
hotlong
commented
Aug 28, 2026
ContributorAuthor
Re-enqueued by the epic PM: this PR's own merge-queue batch runs (13:52Z) were ALL SUCCESS (CI, Lint & Type Check, Spec Liveness Check, Governed Surface Guard); the entry was dropped when the shared batch was rebuilt around a sibling PR's failure — batch-victim shape, resubmitted as-is per the landing playbook. |
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#12881
Step 4 of the
runtime-interface-onlypredicate ("NO metadata surface REFERENCES it") is a text scan over whole metadata-surface files, so it counted a symbol named in a JSDoc prose sentence as a reference it could not resolve, and refused the disposition.The measurement
packages/spec/src/api/contract.zod.tsline 164 explains, insideBaseResponseSchema's docblock, whatObjectStackClient.unwrapResponsekeys on.ObjectStackClientis not imported there, is in no schema, andobjectstack migrate metahas nothing to reach through a sentence. It was nevertheless the only.zod.tsin the tree naming it, so a steps-1-to-3-clean claim onpackages/client/src/index.tswas refused.The cost is not one refused claim. The remaining categories are
unpublished,already-registeredandno-migration-prescription; for a published rename carrying a real before/after table the first two are false and the third is a self-contradiction. The blind spot's effect is to push an honest disposition out and a dishonest one in — the #8299 anti-pattern this block exists to close, arriving through the door #8299 built.Live incidence is 2 of the 3
runtime-interface-onlyclaims in the current stock, not the 1 the card measured.packages/services/service-datasource/.../datasource-driver-factory.ts#DatasourceDriverHandlewas refused by the same shape — a docblock inpackages/spec/src/contracts/data-driver.ts:373naming it while explaining a #11381 typing decision.The change
Mask comment spans through the shared
scripts/js-comment-mask.mjsbefore every read step 4 performs — the mention scan,declaresLocally,importSpecifiersFor, and the evidence line it quotes back.maskCommentsrather thanstripComments, per that module's own rule: this branch reports a line, and blanking is the projection that keeps line numbers aligned with the file on disk. A private stripper here would be the driftcheck:comment-mask-adoptionexists to refuse.String literals are deliberately not masked. A name inside a
.describe()still refuses: the conservative direction here is to refuse (#4690), and a string is where an authorable value lives. That boundary is pinned in the self-test so a future widening has to be argued rather than slipped in.The teeth are unchanged, and both directions are pinned
Self-test 212 assertions to 228. RIO-R3 was converted from a comment mention to a code-level mention — it had been a fixture for two rules at once, and keeping the comment text there would have meant deleting the rule it was written for.
*.zod.tsis not a referenceAblation on
e44d54a59: reverting only the two masking lines (mutation confirmed on disk — 0 occurrences of the deleted text, 1 of the injected text — and the tree restored byte-identically to the HEAD blob,git diff HEADclean) turns RIO-G2, G3, G4 red and moves RIO-R10's evidence line to the docblock, 4 failures. RIO-R3/R11/R12 stay red on their own reasons, as they should.Verdicts elsewhere are unchanged, measured rather than argued
--listand--audit-stockover the live stock: byte-identical before and after.verified: packages/client/src/index.ts#ObjectStackClient (class).One thing the reviewer should know
#12885's changeset also carries a
## Migrationsection with rewrite tables, andruntime-interface-onlyinherits the prescription refusal by design. That arm did not fire —findMigrationPrescriptionreturnsnullon that body, because a framed region closes at the next heading of any level and the tables sit under###sub-headings. So #12885 now passes this gate, but the prescription arm was never consulted. That is a separate blind spot with its own stock-wide blast radius; filed as #12996 rather than fixed here.Gates
Union run at
e44d54a59, exit codes captured before any pipe. All derived families green, plus both convention-triggered obligations for editing a gate script (bare-root-worklist --self-test,check:pm-dispatch-gates) and the mask module's own gates. Verdict lines quoted from the gates themselves:check:bash32-floorfails here and is not caused by this diff: it fails identically (8 of 153 cases, at the simulated-3.2 harness leg) on a pristineorigin/maincheckout with this change absent. This diff contains no shell.Scripts-only, publishes nothing, so
skip-changesetrather than a changeset.Patch round — one CI red the local union missed
Lint & Repo Gatesfailed incheck:objectui-changeset.objectui-changeset-digest.mjs's self-test stages an executable copy ofcheck-adr-0087-registration.mjsinto a throwaway repo atfw-gate/and runs it for real, so the new./js-comment-mask.mjsimport died onERR_MODULE_NOT_FOUNDthere and took both#6494 ROUND TRIPcases (red leg and green leg) down with it — reading as a broken round trip when nothing about the round trip had moved.Fixed the manifest, not the import: the harness's single hard-coded
invoked-as.mjsline becomes a list, the same spelling this gate's own I1/I2 fixture already uses. Two staging sites of one gate, now one convention.scripts/check-empty-changeset.mjsalso names this gate, but it reads the file's TEXT for a parser-parity assertion and never executes a copy, so it needed nothing.Reproduced red locally before the fix and green after:
Full union re-run at
8a7db35d2after merging currentmain; every family above still green, pluscheck:objectui-changeset.Why the derivation missed it (input for the gate-mapping card, measured not guessed):
check:objectui-changesetdeclares exactly one population —.changeset— sodispatch-gates --residuescores itsilentfor a diff touching onlyscripts/check-adr-0087-registration.mjs. It matched only once I edited the digest itself, by gate-script identity. The dependency it really has is a runtime staging dependency — one gate copying another gate's source into a sandbox and executing it — which no path glob declares and no existing convention-trigger names. The "adds or edits a GATE SCRIPT" trigger fires on the edited gate's own families, not on the families of gates that stage a copy of it.Generated by Claude Code