Skip to content

[finding] check-optional-error-sink-contract.mjs's own header says "both narrowings" while it documents and pins three #12291

Description

@yinlianghui

Observation filed while landing #11551 (PR #12290), which de-numbered the workflow comment describing this gate. Not a claim on anyone's time; unassigned, no pm:queue.

Not repaired in that PR: #11551's declared file surface is .github/workflows/lint.yml and its dispatch ruled the gate script explicitly out of scope. So this is recorded rather than fixed.

What is stale

scripts/check-optional-error-sink-contract.mjs documents three narrowings and pins all three, but two lines of its own prose still say there are two.

:60 — in the paragraph introducing the population:

Structural costs false positives instead, so both narrowings below are measured and their cost is printed on every run rather than argued in prose.

:789 — the self-test docblock:

Every limb observed FAILING and observed SILENT, plus the two narrowings pinned as positive counts.

Against that, the file itself carries three narrowing sections — :90"Narrowing 1: a shape narrowed by an as CAST", :102"Narrowing 2: purity", and :114"Narrowing 3: a member type this SYNTACTIC matcher cannot resolve", added by #11069 as the reject side of reading bare Function as a channel.

All three are pinned, not merely documented: expectUnreadable cases at :915, :923 and :934, asserted at :968.

And the gate says so out loud on every run:

✓ optional-error-sink-contract self-test: 19 case(s), both directions, all three narrowings pinned as counts, prefilter pinned over 9 channel(s) × 4 spellings plus its reject side.
 narrowings: 3 shape(s) skipped as `as`-cast narrowings … ; 2 skipped as not-a-pure-sink … ; 3 skipped as UNREADABLE …

So the runtime string was updated to "all three" when #11069 landed narrowing 3, and these two prose lines were not.

Why it is worth a line rather than nothing

Low severity — no behaviour depends on it, and the printed census is correct. What makes it worth recording is that the header is the stated authority for this gate (AGENTS.md: "the script's own header is the authority on detail"), and a reader auditing whether the narrowings are all accounted for is told by the authority that there are two of them. :60's sentence is specifically a promise that the cost of every narrowing is measured and printed, so undercounting the narrowings undercuts the one claim that paragraph exists to make.

⚠️One deliberate non-instance, so a repair does not over-reach: :985 also says "both narrowings", but in the past tense about #11069's own history — "an unsound prefilter survived a self-test that pinned both narrowings as counts (#11069)". That one is a correct statement about the state of the world before narrowing 3 existed and should be left alone.

Same family as the card that surfaced it

This is #11551's shape one level down — prose asserting a count with nothing that re-checks it, next to a runtime that prints the true number. #11551 was the workflow-comment instance; this is the gate-script instance of the same defect, in the same gate.

That is a data point for the open question #11551 raises (whether this shape wants a gate rather than repeated repairs): the drifted count and the correct count are both present in the same repository, one in a comment and one in a string literal a few hundred lines away.

Suggested shape, if triage takes it

Two words at :60 and :789 ("both"/"the two" → "all three", or better, a spelling with no integer in it at all, as PR #12290 did for the workflow comment). Leave :985.

Refs

#9754 (the gate) · #11069 (added narrowing 3 and the "all three" runtime string) · #11551 / PR #12290 (the workflow-comment instance, where this was measured)

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions