Repository navigation
[finding] lint.yml's sink-contract step comment carries measured counts that drift silently — "13 cases, 36 sink types, 15 baselined" against a tree with 19/41/5 #11551
Description
Observation filed while landing the #11069 gate fix (PR #11549). Not a claim; unassigned. Not repaired there — editing .github/workflows/lint.yml would add a hot conflict-magnet file and new gate families to a scripts/**-only PR, which is scope this card should own instead.
.github/workflows/lint.yml's "Optional-error sink contract" step (around line 1315) carries a comment stating measured counts:
# 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.Every number in it is now wrong, and each drifted for a different reason:
| the comment says | reality on cd932772 |
reality after PR #11549 | what moved it |
|---|---|---|---|
13 cases |
13 | 19 | #11549 added six self-test cases |
36 sink types |
38 | 41 | ordinary tree growth, then #11549's population widening |
15 baselined |
2 | 5 | #10556's paydown (15 → 3 → 2), then #11549's three ledger rows |
The 15 baselined figure is the sharpest: it has been wrong since #10556 paid the ledger down, long before #11069 existed. A reader who trusts it believes there are fifteen outstanding sink repairs when there are five, and the ledger's whole point is to be the count of remaining debt.
Why this is worth a card rather than a one-line fix on the next PR that passes by
The numbers are a census of a moving tree pinned in prose in a file nothing re-measures. That is a strictly worse home for them than the gate's own header, which at least sits next to the code that produces them — and #11069 was filed about exactly this species one level down (a gate's own printed counts bounded by a filter nobody re-checked).
So the fix is probably not "correct the three numbers" — that just resets a clock that will drift again on the next PR that touches any sink in packages/**. Options worth weighing:
- Drop the counts from the workflow comment entirely, keeping only the invariant facts (runs its own
--self-testfirst, AST overpackages/**, no spawns, nopaths:filter and why). The gate prints its live census on every run; the comment does not need a stale copy. - Keep a count but make it re-measurable — e.g. state it as "see the gate's own census line" rather than a literal.
- Leave it and accept the drift, explicitly, with a note saying the numbers are illustrative.
I lean to the first: the timing (~3.5 s) and the "no paths: filter, because a filter on packages/** would go dormant on the PR that edits the baseline" rationale are the parts that earn their place, and neither drifts.
Same family, already open
- [finding] cut-rc.yml's hotcrm smoke warning still tells the reader
changeset pre exitre-arms that gate — release.yml records the keying was changed away from pre-exit #9828 — cut-rc.yml's hotcrm smoke warning describes a keying that release.yml changed away from - [finding] lint.yml states the #9465 fence as covering root
package.json— fifteen times, and the over-broad reading is acquirable by copying #10894 — lint.yml states the Migrate the release toolchain to @changesets/cli v3 — one atomic PR carrying the bump, the pre-mode restructure, and the gates that model v2's semantics #9465 fence as covering rootpackage.json, fifteen times - finding: lint.yml's "must stay immediately above the re-measure" ordering contract is prose-only, and violating it is currently SILENT #10572 — lint.yml's "must stay immediately above the re-measure" ordering contract is prose-only and violating it is silent
All four are the same shape: a workflow comment asserting a fact about the world, with nothing that re-checks it. Worth someone deciding whether that shape wants a gate rather than four separate repairs.
Refs
#9754 (the gate) · #10556 (the ledger paydown that stranded 15 baselined) · #11069 / PR #11549 (where this was measured)
Activity
claude commentedon Aug 25, 2026
Concentrated triage round:
finding→pm:queue+domain:devx, Task. Premise re-verified onorigin/main:lint.yml:1655still carries "13 cases…" against a tree at 19/41/5. Fix ruled at grading: de-number the comment (describe what the step does; the live numbers already live in the self-test and ledger) rather than wiring self-updating counts — drift class removed, zero machinery added. Size: S mechanical;lint.ymlis a hot file — same-day churn check before PR.Generated by Claude Code
yinlianghui commentedon Aug 25, 2026
Claim —
domain:devx @ objectstackseatClaim:sessionaf22b339-91b5-5814-8b9b-2453fc5b3f68· branchclaude/issue-11551-lint-yml-sink-contract-counts· worktree../objectstack-11551· model tier opus.Declared file surface:
.github/workflows/lint.yml, the "Optional-errorsink contract" step's comment only. Nothing else.Clause ②: no. A workflow comment; no gate behaviour, no public surface.
Released from this lane's serial queue
.github/workflows/lint.ymlwas held behind PR #12225, which merged as1eea507968. ⛔ Note the hold was never a gate-level one:No other open PR may claim the same single-writer pathguards exactly one path,.objectui-sha, and its self-test explicitly assertspackage.jsonmust never be listed (measured at 21 colliding pairs per 300 PRs — a deliberate false-positive exclusion). This seat had that wrong and corrected it publicly on #11576 and #12243. PR #12266 is still queued and also touches this file; that is ordinary concurrency this repo accepts, not something to sequence around.Premise re-verified on today's
origin/main, not inheritedThe stale comment is still present:
⭐ Note the card says "around line 1315" and it now sits at 1723. The file has moved ~400 lines since filing — which is itself evidence for the card's thesis that this is a bad home for a census.
Generated by Claude Code
ci(lint): de-number the optional-error sink-contract step comment
yinlianghui commentedon Aug 25, 2026
ACCEPT — PR #12290. Landing held until CI is green.
domain:devx @ objectstackseat (#6023, sessionaf22b339-91b5-5814-8b9b-2453fc5b3f68). Final commitbc5ab78d04, one file, +5/−3.The load-bearing assumption was verified, not inherited
Option 1 rests entirely on "the gate prints its live census on every run." The dispatch flagged that as the one thing that had to be checked before deleting anything, and it was —
pnpm check:optional-error-sink, verbatim:All three restated numbers are printed live. Deleting them removes a stale copy, not the only copy. ⭐ Option 2 (point at the census line) is therefore unnecessary, and the dev said so rather than hedging by doing both.
⭐ The card's own figures had drifted again in one day — verified independently
13 cases36 sink types15 baselinedI checked the sharpest one against the ledger file rather than the gate, so the two readings are independent:
scripts/optional-error-sink-contract.baseline.jsonholds exactly one entry.And it is sharper than either the card or the PR states. That surviving row is annotated:
So the comment does not merely overstate the debt fifteen-fold. It presents as outstanding debt the one row a maintainer has ruled a permanent exception. The true count of outstanding sink repairs is zero. The card said a reader "believes there are fifteen outstanding repairs when there are five"; on today's tree the honest version is fifteen when there are none.
This is the card's thesis demonstrating itself twice: a repair that had merely corrected the three numbers would have shipped a comment already wrong about the sharpest of them on the day it landed.
⭐ A fourth stale count, found inside the sentence being deleted
both narrowingswas also wrong — the gate now pins three (as-cast, purity, and theUNREADABLEnarrowing from #11069), and its own self-test line says so. The replacement reads "every narrowing pinned as a count", which removes the count rather than restating it, so it cannot drift a fourth time. Confirmed onorigin/main: the live comment still reads13 cases, both directions, both narrowings.~3.5 s— the one number the filer said earns its place — was checked rather than assumed (0.494 s + 2.659 s ≈ 3.15 s) and has not drifted, which is precisely the filer's argument for keeping it.On the "does this shape want a gate" question
⛔ Correctly not implemented — that is triage's call, and the dev offered one measurement as input instead:
⭐ That is a genuinely useful narrowing for whoever grades the class: "comments that restate a number a gate prints" would cover this card and plausibly none of the other three. The three siblings are untouched, as ruled.
Process
lint.ymlpulled incheck:node-version,check:pnpm-acquisition,check:required-contexts,check:shard-attestation,check:workflow-status-functions,check-aggregator-roster,check-step-collectors,check-whole-set-label-writeand more, none of which ascripts/**diff names.check:type-check-debtwas red on first run and is reported rather than hidden: it refused, it did not fail —--re-measurestops rather than "silently measure a DIFFERENT WORLD" without the built closure. Settled the waylint.ymlitself does (70/70 build), then green.STALE TREE, so no merge was needed — and that was checked againstgit log HEAD..origin/main -- .github/workflows/lint.ymlbeing empty rather than assumed.Landing condition
Draft, not armed, until every named gate job reports⚠️ PR #12266 also edits this file and is still queued; that is ordinary concurrency and ⛔ was correctly not worked around. If it lands first and conflicts, resolve by merging
completed+success, read by name.origin/main— ⛔ never a rebase or force-push.Generated by Claude Code
yinlianghui commentedon Aug 25, 2026
{ "issue": 11551, "status": "done", "branch": "claude/issue-11551-lint-yml-sink-contract-counts", "pr": "https://github.com/objectstack-ai/objectstack/pull/12290", "premise_still_valid": true, "summary": "De-numbered the \"Optional-`error` sink contract\" step comment in .github/workflows/lint.yml — the card's option 1, taken after verifying its load-bearing assumption rather than inheriting it. Dropped the three counts (13 cases / 36 sink types / 15 baselined) and kept every invariant the filer named: runs its own --self-test first, both directions, AST over packages/**, no spawns, ~3.5 s, and the no-`paths:`-filter rationale. Also removed a FOURTH stale count inside the same sentence — `both narrowings`, when the gate now pins three (`as`-cast, purity, and #11069's UNREADABLE) and its own self-test prints \"all three narrowings pinned as counts\"; the replacement says \"every narrowing pinned as a count\", so it carries no integer to drift. Diff is 1 file, +5/-3, exactly the declared surface — no gate script, no package.json, no other step, and the three sibling cards (#9828/#10894/#10572) untouched. No gate was built for the class; the measurement bearing on that question is in the PR body and in open_questions below.", "tests": "ALL AT FINAL COMMIT bc5ab78d04 (git rev-parse --short HEAD), base 0b478e1eff. Gate union derived AT that commit: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` — stderr confirms \"gate list derived from the tree of 'objectstack-ai/objectstack' at commit bc5ab78d04\", \"--repo ... checked against this checkout's 'origin' remote — it holds\", change set \"1 path(s) vs merge base 0b478e1ef\". NO `STALE TREE` warning: \"At least 1 commit(s) behind origin/main, but nothing this answer derives from changed across that range\", so no merge was needed. Editing lint.yml pulled in workflow-shaped families a scripts/ diff would not — the derived list was READ, not assumed: 18 families, all run, ALL GREEN, each exit code captured before any pipe (runner redirects to a file, then `EXIT=$?`, never `| tail`). Verbatim verdict lines (each printed by the gate itself, not `$?`): check:agent-test-spelling `✓ ... 0 violations — 380 file(s) · 3867 bare -- token(s)`; check:node-version `check-node-version: OK (34 setup-node step(s) across 28 workflows, all on Node 22).`; check:pnpm-acquisition `OK -- every job that runs pnpm acquires it first.`; check:pnpm-filter-targets `✓ 136/173 --filter occurrence(s) across 28 file(s) resolve`; check:required-contexts `✓ 6 required context name(s) pinned across 2 workflow(s)`; check:shard-attestation `✓ 2 aggregate gate(s) count 3 declared leg(s)`; check:type-check-coverage `OK — 65/78 workspace packages type-checked`; check:type-check-debt `check-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-functions `OK (scanned 28 workflow file(s), 51 job(s), 25 job-level if:)`; check-aggregator-roster `✓ 3 aggregator(s) across 2 workflow(s); roster == needs: in both directions`; check-required-contexts.mjs `✓ 6 required context name(s) pinned`; 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 `✓ no self-test CI runs prints a line the Actions runner would parse as a workflow command.`; check-shard-attestation.mjs `✓ 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).`; check-step-collectors `✓ 352 run: steps across 28 workflow(s)`; check-whole-set-label-write `✓ 0 violations — 211 file(s) over 3 root(s)`; check-drift-comment `✓ 56 cases pass across 5 fixture diff(s).`; pm/ci-failure.mjs --self-test `OK self-test: supersession keeps the newest per name and reports the drops`. ⚠️ ONE FIRST-RUN RED, recorded not hidden: check:type-check-debt exited 1 — it REFUSED to run, it did not fail. `--re-measure` requires the built dependency closure on disk (56 workspace deps had no built type entry point) and stops rather than \"silently measure a DIFFERENT WORLD\". Settled the way lint.yml itself does: `turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2` → `Tasks: 70 successful, 70 total`, then re-ran to the green verdict quoted above. NO NARROWING IS CLAIMED anywhere: all 18 derived families were run in full. All heavy work went through `bash scripts/pm/os-verify-lock.sh -c` (VERDICT lines: command-exit 0 · held 37s; 451s build; 279s re-measure; 4s sink gate). No ablation was performed — nothing in this card needs one; the diff is a YAML comment and cannot change any gate's behaviour, which is also why check:type-check-debt's first-run red was a precondition and not attributable to the change. Changeset: none (workflow comment publishes nothing); `skip-changeset` applied and READ BACK — PR labels now `[\"size/xs\",\"skip-changeset\"]`, so it survived the size-labeler's own write. ⚠️ Declared channel fallback: the additive labels endpoint (`curl -X POST .../issues/12290/labels`) is UNAVAILABLE in this session — it returns `{\"message\":\"GitHub access is not enabled for this session. An org admin must connect the Claude GitHub App for this organization.\"}`, as does every REST call, and `gh` is not installed. So the label went through the read→union→whole-set fallback via MCP issue_write (prior set was empty), declared here per contract; the read-back above is what closes the step. The same outage forced the out-of-scope dedup search onto MCP search_issues instead of the REST list endpoints — also declared.", "measurements": { "zone_2_1_what_the_gate_actually_prints": "VERIFIED — the card's option-1 assumption HOLDS, and precisely. `pnpm check:optional-error-sink` prints all three figures live on every run. 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).` ⇒ self-test case count, sink-type population AND baselined count are all printed. Deleting them from the comment removes a stale copy, not the only copy. Option 1 is correct as stated; option 2 (the Zone 3 fallback) was NOT needed.", "zone_2_2_recount_on_base_0b478e1eff": { "self_test_cases": "19 (card's 2026-08-24 reality: 19 — unchanged)", "sink_types": "41 (card's 2026-08-24 reality: 41 — unchanged)", "baselined": "1 (card's 2026-08-24 reality: 5 — DRIFTED AGAIN, 5 → 1, in the single day between filing and repair)", "drift_note": "⭐ The card's thesis demonstrating itself. The baselined figure moved 5 → 1 overnight; corroborated INDEPENDENTLY of the gate — scripts/optional-error-sink-contract.baseline.json contains exactly one entry (SettingsDiagnosticsLogger). The ledger's own history block attributes it to the maintainer's 2026-08-24 ruling on #10556 settling four of the five rows. A repair that merely CORRECTED the three numbers would have shipped a comment already wrong about the sharpest of them on the day it landed.", "timing_also_rechecked": "`~3.5 s` was NOT assumed — it is also a measured number. 0.494s self-test + 2.659s scan ≈ 3.15 s node time at this commit. It has not drifted, which is exactly the filer's point about it, so it was kept." }, "zone_2_3_line_number": "CONFIRMED on my own base 0b478e1eff: the stale comment sits at lint.yml:1723-1725, not the card's \"around line 1315\". The file moved ~400 lines since filing — itself further evidence for the card's thesis that a workflow comment is a bad home for a census.", "zone_2_4_attribution_table": "NOT re-derived, and therefore NOT restated as fact in the PR body — treated as the filer's account per the dispatch. The single attribution the PR body does make (the 5 → 1 move) is read from the ledger file's own history block and its surviving entry, not from the card." }, "open_questions": [ { "question": "Zone 1.3 reported, NOT implemented: does the four-card family (#11551, #9828, #10894, #10572) want a gate rather than four separate repairs? One measurement from this repair bears on it.", "options": [ "A — no gate; keep repairing individually. The three counts here were mechanically re-derivable because the gate prints all of them in ONE stable line, so a gate for this instance would have been cheap; but that is NOT true of the siblings, whose claims are prose assertions about workflow BEHAVIOUR (#9828's keying, #10894's fence scope, #10572's ordering contract), not integers a script already prints. A gate for 'comments restating a number a gate prints' would cover this card and plausibly none of the other three.", "B — build the narrow gate anyway, accepting it covers ~1 of 4 cards, on the grounds that this instance drifted four numbers and recurred within a day.", "C — treat the class as a docs-drift problem and fold it into an existing prose-drift sweep rather than a new gate family." ], "recommendation": "A, because the measurement collected here argues AGAINST the family framing that motivates a gate: the four cards share a symptom (stale prose) but not a mechanism, and only this one has a machine-readable oracle. Startup scope discipline also says a new gate family for a 1-of-4 hit rate is unearned. Maintainer/triage call, not mine — nothing was implemented either way." } ], "out_of_scope_findings": [ "filed as #12291: scripts/check-optional-error-sink-contract.mjs's OWN header says \"both narrowings\" (:60) and \"the two narrowings\" (:789) while the file documents three (Narrowing 1/2/3 at :90/:102/:114) and pins all three (expectUnreadable at :915/:923/:934) — and its runtime prints \"all three narrowings pinned as counts\". Same defect class as this card, one level down in the same gate; NOT repaired here because Zone 1.1 declares the gate script out of surface. Filed unassigned with the `finding` label, no `pm:queue`. The finding explicitly EXCLUDES :985, whose \"both narrowings\" is a correct past-tense statement about the world before narrowing 3 existed and must be left alone. Dedup search done first (MCP fallback, see tests): no existing card covers it." ] }Generated by Claude Code
yinlianghui commentedon Aug 25, 2026
os-dev-report
\n\n```jsonwith theos-dev-reportmarker stripped, so it is invisible to a scan keying on it. Re-posted here led by the literal text instead, per the os-dev contract's fallback. This comment is the authoritative report; the earlier one is a duplicate, not a second result.{ "issue": 11551, "status": "done", "branch": "claude/issue-11551-lint-yml-sink-contract-counts", "pr": "https://github.com/objectstack-ai/objectstack/pull/12290", "premise_still_valid": true, "summary": "De-numbered the \"Optional-`error` sink contract\" step comment in .github/workflows/lint.yml — the card's option 1, taken after verifying its load-bearing assumption rather than inheriting it. Dropped the three counts (13 cases / 36 sink types / 15 baselined) and kept every invariant the filer named: runs its own --self-test first, both directions, AST over packages/**, no spawns, ~3.5 s, and the no-`paths:`-filter rationale. Also removed a FOURTH stale count inside the same sentence — `both narrowings`, when the gate now pins three (`as`-cast, purity, and #11069's UNREADABLE) and its own self-test prints \"all three narrowings pinned as counts\"; the replacement says \"every narrowing pinned as a count\", so it carries no integer to drift. Diff is 1 file, +5/-3, exactly the declared surface — no gate script, no package.json, no other step, and the three sibling cards (#9828/#10894/#10572) untouched. No gate was built for the class; the measurement bearing on that question is in the PR body and in open_questions below.", "tests": "ALL AT FINAL COMMIT bc5ab78d04 (git rev-parse --short HEAD), base 0b478e1eff. Gate union derived AT that commit: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` — stderr confirms \"gate list derived from the tree of 'objectstack-ai/objectstack' at commit bc5ab78d04\", \"--repo ... checked against this checkout's 'origin' remote — it holds\", change set \"1 path(s) vs merge base 0b478e1ef\". NO `STALE TREE` warning: \"At least 1 commit(s) behind origin/main, but nothing this answer derives from changed across that range\", so no merge was needed. Editing lint.yml pulled in workflow-shaped families a scripts/ diff would not — the derived list was READ, not assumed: 18 families, all run, ALL GREEN, each exit code captured before any pipe (runner redirects to a file, then `EXIT=$?`, never `| tail`). Verbatim verdict lines (each printed by the gate itself, not `$?`): check:agent-test-spelling `✓ ... 0 violations — 380 file(s) · 3867 bare -- token(s)`; check:node-version `check-node-version: OK (34 setup-node step(s) across 28 workflows, all on Node 22).`; check:pnpm-acquisition `OK -- every job that runs pnpm acquires it first.`; check:pnpm-filter-targets `✓ 136/173 --filter occurrence(s) across 28 file(s) resolve`; check:required-contexts `✓ 6 required context name(s) pinned across 2 workflow(s)`; check:shard-attestation `✓ 2 aggregate gate(s) count 3 declared leg(s)`; check:type-check-coverage `OK — 65/78 workspace packages type-checked`; check:type-check-debt `check-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-functions `OK (scanned 28 workflow file(s), 51 job(s), 25 job-level if:)`; check-aggregator-roster `✓ 3 aggregator(s) across 2 workflow(s); roster == needs: in both directions`; check-required-contexts.mjs `✓ 6 required context name(s) pinned`; 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 `✓ no self-test CI runs prints a line the Actions runner would parse as a workflow command.`; check-shard-attestation.mjs `✓ 2 aggregate gate(s) count 3 declared leg(s) across 3 attesting job(s).`; check-step-collectors `✓ 352 run: steps across 28 workflow(s)`; check-whole-set-label-write `✓ 0 violations — 211 file(s) over 3 root(s)`; check-drift-comment `✓ 56 cases pass across 5 fixture diff(s).`; pm/ci-failure.mjs --self-test `OK self-test: supersession keeps the newest per name and reports the drops`. ⚠️ ONE FIRST-RUN RED, recorded not hidden: check:type-check-debt exited 1 — it REFUSED to run, it did not fail. `--re-measure` requires the built dependency closure on disk (56 workspace deps had no built type entry point) and stops rather than \"silently measure a DIFFERENT WORLD\". Settled the way lint.yml itself does: `turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2` → `Tasks: 70 successful, 70 total`, then re-ran to the green verdict quoted above. NO NARROWING IS CLAIMED anywhere: all 18 derived families were run in full. All heavy work went through `bash scripts/pm/os-verify-lock.sh -c` (VERDICT lines: command-exit 0 · held 37s; 451s build; 279s re-measure; 4s sink gate). No ablation was performed — nothing in this card needs one; the diff is a YAML comment and cannot change any gate's behaviour, which is also why check:type-check-debt's first-run red was a precondition and not attributable to the change. Changeset: none (workflow comment publishes nothing); `skip-changeset` applied and READ BACK — PR labels now `[\"size/xs\",\"skip-changeset\"]`, so it survived the size-labeler's own write. ⚠️ Declared channel fallback: the additive labels endpoint (`curl -X POST .../issues/12290/labels`) is UNAVAILABLE in this session — it returns `{\"message\":\"GitHub access is not enabled for this session. An org admin must connect the Claude GitHub App for this organization.\"}`, as does every REST call, and `gh` is not installed. So the label went through the read→union→whole-set fallback via MCP issue_write (prior set was empty), declared here per contract; the read-back above is what closes the step. The same outage forced the out-of-scope dedup search onto MCP search_issues instead of the REST list endpoints — also declared.", "measurements": { "zone_2_1_what_the_gate_actually_prints": "VERIFIED — the card's option-1 assumption HOLDS, and precisely. `pnpm check:optional-error-sink` prints all three figures live on every run. 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).` ⇒ self-test case count, sink-type population AND baselined count are all printed. Deleting them from the comment removes a stale copy, not the only copy. Option 1 is correct as stated; option 2 (the Zone 3 fallback) was NOT needed.", "zone_2_2_recount_on_base_0b478e1eff": { "self_test_cases": "19 (card's 2026-08-24 reality: 19 — unchanged)", "sink_types": "41 (card's 2026-08-24 reality: 41 — unchanged)", "baselined": "1 (card's 2026-08-24 reality: 5 — DRIFTED AGAIN, 5 → 1, in the single day between filing and repair)", "drift_note": "⭐ The card's thesis demonstrating itself. The baselined figure moved 5 → 1 overnight; corroborated INDEPENDENTLY of the gate — scripts/optional-error-sink-contract.baseline.json contains exactly one entry (SettingsDiagnosticsLogger). The ledger's own history block attributes it to the maintainer's 2026-08-24 ruling on #10556 settling four of the five rows. A repair that merely CORRECTED the three numbers would have shipped a comment already wrong about the sharpest of them on the day it landed.", "timing_also_rechecked": "`~3.5 s` was NOT assumed — it is also a measured number. 0.494s self-test + 2.659s scan ≈ 3.15 s node time at this commit. It has not drifted, which is exactly the filer's point about it, so it was kept." }, "zone_2_3_line_number": "CONFIRMED on my own base 0b478e1eff: the stale comment sits at lint.yml:1723-1725, not the card's \"around line 1315\". The file moved ~400 lines since filing — itself further evidence for the card's thesis that a workflow comment is a bad home for a census.", "zone_2_4_attribution_table": "NOT re-derived, and therefore NOT restated as fact in the PR body — treated as the filer's account per the dispatch. The single attribution the PR body does make (the 5 → 1 move) is read from the ledger file's own history block and its surviving entry, not from the card." }, "open_questions": [ { "question": "Zone 1.3 reported, NOT implemented: does the four-card family (#11551, #9828, #10894, #10572) want a gate rather than four separate repairs? One measurement from this repair bears on it.", "options": [ "A — no gate; keep repairing individually. The three counts here were mechanically re-derivable because the gate prints all of them in ONE stable line, so a gate for this instance would have been cheap; but that is NOT true of the siblings, whose claims are prose assertions about workflow BEHAVIOUR (#9828's keying, #10894's fence scope, #10572's ordering contract), not integers a script already prints. A gate for 'comments restating a number a gate prints' would cover this card and plausibly none of the other three.", "B — build the narrow gate anyway, accepting it covers ~1 of 4 cards, on the grounds that this instance drifted four numbers and recurred within a day.", "C — treat the class as a docs-drift problem and fold it into an existing prose-drift sweep rather than a new gate family." ], "recommendation": "A, because the measurement collected here argues AGAINST the family framing that motivates a gate: the four cards share a symptom (stale prose) but not a mechanism, and only this one has a machine-readable oracle. Startup scope discipline also says a new gate family for a 1-of-4 hit rate is unearned. Maintainer/triage call, not mine — nothing was implemented either way." } ], "out_of_scope_findings": [ "filed as #12291: scripts/check-optional-error-sink-contract.mjs's OWN header says \"both narrowings\" (:60) and \"the two narrowings\" (:789) while the file documents three (Narrowing 1/2/3 at :90/:102/:114) and pins all three (expectUnreadable at :915/:923/:934) — and its runtime prints \"all three narrowings pinned as counts\". Same defect class as this card, one level down in the same gate; NOT repaired here because Zone 1.1 declares the gate script out of surface. Filed unassigned with the `finding` label, no `pm:queue`. The finding explicitly EXCLUDES :985, whose \"both narrowings\" is a correct past-tense statement about the world before narrowing 3 existed and must be left alone. Dedup search done first (MCP fallback, see tests): no existing card covers it.", "NOT filed, recorded for PM only: an ACCEPT review of PR #12290 (comment 5413581137) was posted under the shared identity ~1 minute after the PR opened and before this report existed. It is not this seat's write. Per the shared-identity rule it was left exactly as-is and nothing was 'corrected'; flagged here for PM to reconcile rather than acted on. It does not conflict with anything in this report." ] }Generated by Claude Code
ci(lint): de-number the optional-error sink-contract step comment (ob…