Skip to content

ci(lint): de-number the optional-error sink-contract step comment — the gate prints its own census - #12290

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-11551-lint-yml-sink-contract-counts
Aug 25, 2026
Merged

ci(lint): de-number the optional-error sink-contract step comment — the gate prints its own census#12290
yinlianghui merged 1 commit into
mainfrom
claude/issue-11551-lint-yml-sink-contract-counts

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes#11551

.github/workflows/lint.yml's "Optional-error sink contract" step carried a census of a moving tree pinned in prose that nothing re-measures:

# Runs its own --self-test FIRST (13 cases, both directions, both# narrowings pinned as counts), then the scan — 36 sink types, 15# baselined shrink-only. AST over packages/**, no spawns; ~3.5 s.

This drops the three counts and keeps the facts that do not drift.

The card's preferred option rests on a claim; I verified it directly

The card's option 1 ("drop the counts") rests on "the gate prints its live census on every run; the comment does not need a stale copy." That was the load-bearing assumption, so it was measured rather than inherited — pnpm check:optional-error-sink at bc5ab78d04, verbatim:

✓ 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.
SINK CENSUS [optional-error-sink-contract] (#9754): 41 sink type(s) declaring `error` in packages/** — 12 declare it REQUIRED (nothing to guarantee), 28 declare it optional beside a REQUIRED `warn`, 1 permit silence (1 optional-fallback, 0 no-fallback).
✓ optional-error sink contract: every sink declaring an optional `error` guarantees a `warn` channel (1 baselined, shrink-only).

The assumption holds, and precisely. All three numbers the comment restated are printed live on every run — the self-test case count (19 case(s)), the sink-type population (41 sink type(s)) and the baselined total (1 baselined, shrink-only). Deleting them from the comment removes a stale copy, not the only copy. Option 1 is correct as stated; option 2 (point at the census line) is unnecessary, though the replacement text says where the live numbers come from.

Re-measured on this base — and the card's own figures had already drifted again

The card's "reality" column was measured 2026-08-24. Re-measured at bc5ab78d04:

the comment saidcard's reality (2026-08-24)measured here (2026-08-25)
13 cases1919
36 sink types4141
15 baselined51

The baselined count moved 5 → 1 in the single day between filing and repair. The ledger's own history block records why: the maintainer's 2026-08-24 ruling on #10556 settled four of the five rows, leaving one recorded deliberate exception (SettingsDiagnosticsLogger). Corroborated independently of the gate — scripts/optional-error-sink-contract.baseline.json contains exactly one entry.

That is the card's thesis demonstrating itself twice over. A repair that had merely corrected the three numbers would have shipped a comment that was already wrong about the sharpest of them on the day it landed.

The same is true of the file position: the card says "around line 1315" and the step now sits at 1723 — the file moved ~400 lines in one day, confirmed on this base.

A fourth stale count, inside the sentence being removed

both narrowings was also wrong. The gate now pins three narrowings (as-cast, purity, and the UNREADABLE narrowing added by #11069), and its self-test says so in the line quoted above: "all three narrowings pinned as counts". The replacement says "every narrowing pinned as a count" — removing the count rather than restating it, so this one cannot drift a fourth time.

What is kept, deliberately

Per the filer, the parts that earn their place and do not drift: runs its own --self-test first, both directions, AST over packages/**, no spawns, ~3.5 s, and the rationale for having no paths: filter (a filter on packages/** would go dormant on the PR that edits the baseline).

~3.5 s was checked rather than assumed, since it is also a measured number: 0.494s self-test + 2.659s scan ≈ 3.15 s of node time at this commit. It has not drifted, which is the filer's point about it.

On the "does this shape want a gate" question — not answered here

The card asks whether four workflow-comment-drift cards "want a gate rather than four separate repairs". Per the dispatch that is a triage question, so nothing is implemented for it. One measurement from this repair that bears on it, offered as input only: the three counts here were re-derivable mechanically — the gate prints all of them in a single stable line — so a gate for this instance would have been cheap. That is not obviously true of the sibling cards, whose claims are prose assertions about workflow behaviour (#9828's keying, #10894's fence scope, #10572's ordering contract) rather than integers a script already prints. A gate for "comments that restate a number a gate prints" would cover this card and plausibly none of the other three.

The three sibling cards are untouched here, as ruled.

Verification

Gate union derived at the final commit with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (gate list derived from the tree of 'objectstack-ai/objectstack' at commit bc5ab78d04; change set 1 path(s) vs merge base 0b478e1ef). Editing lint.yml pulls in workflow-shaped families a scripts/** diff would not, so the derived list was read rather than assumed: 18 families, all run, all green, exit codes captured before any pipe.

gateverdict line
check:agent-test-spelling✓ check-agent-test-spelling: 0 violations — 380 file(s) · 3867 bare -- token(s) …
check:node-versioncheck-node-version: OK (34 setup-node step(s) across 28 workflows, all on Node 22).
check:pnpm-acquisitioncheck-pnpm-acquisition: OK -- every job that runs pnpm acquires it first.
check:pnpm-filter-targets✓ check:pnpm-filter-targets: 136/173 --filter occurrence(s) across 28 file(s) resolve …
check:required-contexts✓ check-required-contexts: 6 required context name(s) pinned across 2 workflow(s) …
check:shard-attestation✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) …
check:type-check-coveragecheck-type-check-coverage: OK — 65/78 workspace packages type-checked …
check:type-check-debtcheck-type-check-coverage --re-measure: OK — 32 ledger entr(ies) re-measured in 277.7s, 1843 raw tsc error(s) total, none above its recorded number.
check:workflow-status-functionscheck-workflow-status-functions: OK (scanned 28 workflow file(s), 51 job(s) …)
check-aggregator-roster.mjs✓ check-aggregator-roster: 3 aggregator(s) across 2 workflow(s); roster == needs: in both directions …
check-required-contexts.mjs✓ check-required-contexts: 6 required context name(s) pinned across 2 workflow(s) …
check-self-test-wired.mjs✓ check-self-test-wired: every one of the 134 script(s) CI runs that ship a --self-test has that self-test run by CI.
check-self-test-workflow-commands.mjs✓ check-self-test-workflow-commands: no self-test CI runs prints a line the Actions runner would parse as a workflow command.
check-shard-attestation.mjs✓ check-shard-attestation: 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).
check-step-collectors.mjs✓ check-step-collectors: 352 run: steps across 28 workflow(s) …
check-whole-set-label-write.mjs✓ check-whole-set-label-write: 0 violations — 211 file(s) over 3 root(s) …
check-drift-comment.mjs✓ check-drift-comment: 56 cases pass across 5 fixture diff(s).
pm/ci-failure.mjs --self-testOK self-test: supersession keeps the newest per name and reports the drops …

⚠️check:type-check-debt was red on first run and is worth recording rather than hiding: it refused to run, it did not fail. --re-measure requires the built dependency closure on disk and stops rather than "silently measure a DIFFERENT WORLD". Settled the way lint.yml itself does — turbo run build --filter='./packages/*' --filter='./packages/*/*' (70 successful, 70 total) — then re-run to the green verdict quoted above. No narrowing is claimed anywhere in this table.

Base freshness: the derivation reported "At least 1 commit(s) behind origin/main, but nothing this answer derives from changed across that range" — no STALE TREE warning, so no merge was needed to trust these numbers. git log HEAD..origin/main -- .github/workflows/lint.yml is empty: PR #12266 also edits this file and is still queued, which is ordinary concurrency here and was not worked around. If it lands first and conflicts, the resolution is a merge of origin/main, never a rebase or force-push.

Changeset: none — a workflow comment publishes nothing. skip-changeset applied.

Not restated as fact

The card's per-number attribution table (which PR moved which count) is the filer's account and was not re-derived here, so it is not repeated as established. The one attribution above — the 5 → 1 move — is read from the ledger file's own history block and its surviving entry, not from the card.


Generated by Claude Code

The step's comment pinned a census of a moving tree in prose that nothing
re-measures: "13 cases ... 36 sink types, 15 baselined". Every number was
wrong, and the baselined figure had been wrong since the ledger paydown --
a reader trusting it believed there were fifteen outstanding sink repairs.
The gate prints all three figures live on every run, so the comment does not
need a copy. Drop the counts and keep the facts that do not drift: runs its
own --self-test first, AST over packages/**, no spawns, ~3.5 s, and the
no-`paths:`-filter rationale.
Also drops "both narrowings", which had itself gone stale -- the gate now
pins three and its self-test prints "all three narrowings".
Part of #11551
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
@yinlianghuiyinlianghui added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 25, 2026 — with Claude
@yinlianghui
yinlianghui marked this pull request as ready for review August 25, 2026 16:44
@yinlianghui
yinlianghui added this pull request to the merge queueAug 25, 2026
Merged via the queue into main with commit 5ce5f8cAug 25, 2026
29 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-11551-lint-yml-sink-contract-counts branch August 25, 2026 17:06
yinlianghui pushed a commit that referenced this pull request Aug 25, 2026
…out its narrowing count
:60 and :789 said "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". Repointed both sentences
to an integer-free spelling ("every narrowing below" / "the narrowings")
so they cannot go stale again when a fourth narrowing arrives, matching
the fix PR #12290 made for the sibling workflow-comment instance.
:985 is left exactly as written — it is a correct past-tense statement
about the state of the world before narrowing 3 existed (#11069's own
history), not a present-tense claim.
No behaviour, count, pin, or runtime string changed.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cdsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@yinlianghui@claude