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)

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 25, 2026
  2. self-assigned this
    on Aug 25, 2026
  3. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    ContributorAuthor

    Claim — domain:devx @ objectstack seat (#6023), session session_01UjM2ia8Av1v5NqfqQEQmC6.

    • Branch: claude/issue-12291-narrowing-count-prose
    • Worktree: ../objectstack-12291 (dedicated)
    • Model tier: sonnet
    • Clause ②: falsify, don't build on, this seat's assumptions.

    Declared file surface: scripts/check-optional-error-sink-contract.mjs — that file only.

    Premise re-verified on origin/main (0d91eaa6ea) before dispatch, by content and by line:

     60: * costs false positives instead, so both narrowings below are measured and
    789: * Every limb observed FAILING and observed SILENT, plus the two narrowings
    985:    // an unsound prefilter survived a self-test that pinned both narrowings as
    1073:            `all three narrowings pinned as counts, prefilter pinned over …
     90: * ### Narrowing 1: a shape narrowed by an `as` CAST is not a contract
    102: * ### Narrowing 2: purity — every member from the log vocabulary
    114: * ### Narrowing 3: a member type this SYNTACTIC matcher cannot resolve
    

    Three sections, a runtime string that says three, and two prose lines that say two. The line numbers on the card are still exact — nothing has drifted since it was filed.

    ⛔ :985 stays as written. It is past tense about #11069's own history and is true of the world it describes; "fixing" it would make the file lie about when narrowing 3 arrived.

    No serial constraint — no open PR claims this file.


    Generated by Claude Code

  4. removed their assignment
    on Aug 25, 2026
  5. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    ContributorAuthor
    {
      "issue": 12291,
      "status": "done",
      "branch": "claude/issue-12291-narrowing-count-prose",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12323",
      "premise_still_valid": true,
      "summary": "Repaired the two stale prose lines in scripts/check-optional-error-sink-contract.mjs (:60, :789) that said the gate measures \"both\"/\"the two narrowings\" while the file documents and pins three (Narrowing 1: as-cast, Narrowing 2: purity, Narrowing 3: unreadable member type, added by #11069) and its runtime string already prints \"all three narrowings pinned as counts\". Chose an integer-free spelling (\"every narrowing below\" / \"the narrowings\") per clause 2c, matching PR #12290's fix for the sibling workflow-comment instance, so the sentences cannot go stale again when narrowing 4 arrives. :985 left exactly as written (correct past tense about #11069's own history). Swept the whole file for every count-asserting spelling near \"narrowing\" per clause 2b; found no third stale site (the \"two blind spots\" at :132-156 and \"the two apart\" at :795 are unrelated concepts, not narrowing-count claims, and are left alone). No behaviour, count, pin, or runtime string changed.",
      "tests": "node scripts/check-optional-error-sink-contract.mjs --self-test => exit 0, '✓ 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.' node scripts/check-optional-error-sink-contract.mjs => exit 0, '✓ optional-error sink contract: every sink declaring an optional `error` guarantees a `warn` channel (1 baselined, shrink-only).' Both verdict lines unchanged from origin/main, quoted in full in the PR body so the printed number and the repaired prose can be compared directly. This is a comment-only diff, so the gate passing is NOT evidence the prose is correct (the gate never reads its own header) -- the actual evidence is a manual read of the three '### Narrowing N' sections (:90, :102, :114) against the two repaired sentences, stated explicitly in the PR body rather than dressed up as verified by the green run. Local gate union derived via node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at final commit 9097996c94 (no STALE TREE): check:agent-test-spelling, check:cli-command-ids, check:cross-package-test-inputs, check:entry-guard, check:optional-error-sink, check:parse-guard, check:pnpm-filter-targets, check-ci-filter-parity.mjs -- all run as one chained command through os-verify-lock.sh, verdict 'command-exit 0 · held the lock 30s · waited 0s', exit code captured before any pipe.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  6. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    ContributorAuthor

    os-dev-report (the HTML-comment marker above was stripped by GitHub's sanitizer; this comment starts with the literal marker text instead, per AGENTS.md)

    {
      "issue": 12291,
      "status": "done",
      "branch": "claude/issue-12291-narrowing-count-prose",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12323",
      "premise_still_valid": true,
      "summary": "Repaired the two stale prose lines in scripts/check-optional-error-sink-contract.mjs (:60, :789) that said the gate measures \"both\"/\"the two narrowings\" while the file documents and pins three (Narrowing 1: as-cast, Narrowing 2: purity, Narrowing 3: unreadable member type, added by #11069) and its runtime string already prints \"all three narrowings pinned as counts\". Chose an integer-free spelling (\"every narrowing below\" / \"the narrowings\") per clause 2c, matching PR #12290's fix for the sibling workflow-comment instance, so the sentences cannot go stale again when narrowing 4 arrives. :985 left exactly as written (correct past tense about #11069's own history). Swept the whole file for every count-asserting spelling near \"narrowing\" per clause 2b; found no third stale site (the \"two blind spots\" at :132-156 and \"the two apart\" at :795 are unrelated concepts, not narrowing-count claims, and are left alone). No behaviour, count, pin, or runtime string changed.",
      "tests": "node scripts/check-optional-error-sink-contract.mjs --self-test => exit 0, '✓ 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.' node scripts/check-optional-error-sink-contract.mjs => exit 0, '✓ optional-error sink contract: every sink declaring an optional `error` guarantees a `warn` channel (1 baselined, shrink-only).' Both verdict lines unchanged from origin/main, quoted in full in the PR body so the printed number and the repaired prose can be compared directly. This is a comment-only diff, so the gate passing is NOT evidence the prose is correct (the gate never reads its own header) -- the actual evidence is a manual read of the three '### Narrowing N' sections (:90, :102, :114) against the two repaired sentences, stated explicitly in the PR body rather than dressed up as verified by the green run. Local gate union derived via node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack at final commit 9097996c94 (no STALE TREE): check:agent-test-spelling, check:cli-command-ids, check:cross-package-test-inputs, check:entry-guard, check:optional-error-sink, check:parse-guard, check:pnpm-filter-targets, check-ci-filter-parity.mjs -- all run as one chained command through os-verify-lock.sh, verdict 'command-exit 0 · held the lock 30s · waited 0s', exit code captured before any pipe.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxDomain lane — seat card indexed by label:pm:seat

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions