Skip to content

fix(scripts): SOURCES_COVERED asks every package, ledgered or not - #15482

Merged
baozhoutao merged 3 commits into
mainfrom
claude/issue-14918-type-check-coverage-sources-clause
Sep 4, 2026
Merged

fix(scripts): SOURCES_COVERED asks every package, ledgered or not#15482
baozhoutao merged 3 commits into
mainfrom
claude/issue-14918-type-check-coverage-sources-clause

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #14918

check:type-check-coverage evaluated its SOURCES_COVERED clause only for a package that DECLARES a typecheck script. The scope was deliberate and its argument was written beside it — "a DEBT package's answer is already 'nothing reads this package', which its own ledger owns; asking again here would bill the same hole to two ledgers". That argument does not survive contact with what a DEBT row is: the row records an error count, taken through tsc -p tsconfig.json, so a source directory outside that program is in neither the count nor any other reading anybody performs.

This is the third axis of the same gate, and the card is explicit that a fix must claim only one: #14630 (needs a pre-existing ledger row to notice a package at all) and #14386 (package-ROOT source, depth 0) both landed already and are not touched here.

The repair

SOURCES_COVERED is now asked of every workspace package, over accountedPrograms() — the same set TESTS_COVERED has been decided against since #7353: the configs the typecheck script invokes, or, for a package with no such script, tsconfig.json, which is what measureDebt runs. The clause's file-level predicate, its depth > 0 scope and ROOT_SOURCE_FILES are untouched.

The finding now names the claim it falsifies instead of assuming that claim is a typecheck script:

basis what the message says is contradicted
declares typecheck unchanged — the script that makes the package count as COVERED
no script, in DEBT its DEBT entry records the ERROR COUNT tsc reports through tsconfig.json
no script, in EXEMPT its EXEMPT entry says type-checking does not apply at all — these files say otherwise
no script, no tsconfig.json NOTHING reads a line of this directory

⛔ No ledger row was added and no exclusion was widened. UNCHECKED_SOURCE_DEBT is still empty and still closed.

The gate's report on its own scope

The card's own observation is that each of the three axes was found by a person tripping over it, never by an instrument — and the shape they share is not "the gate was wrong": a clause was skipped for a population, deliberately, and the run said so nowhere. A green line naming 79 packages reads as 79 packages checked.

SCOPED_CLAUSES now declares every per-package clause that is asked of a subset, beside its predicate and the argument for why that subset is legitimate, and every green run prints who was left out of what. Today exactly one clause is scoped:

  clause scope: GENERATED_COVERED was NOT asked of 4 of them (@objectstack/cloud-connection,
  @objectstack/console, @objectstack/hono, @objectstack/observability) -- they declare no
  `typecheck` script, so they invoke no config and no `include` of theirs can promise a
  generated directory -- the population behind this scope is EMPTY, not hidden. Every other
  per-package invariant was asked of all 79. ⭐ This line exists because SOURCES_COVERED was
  scoped exactly like that and said so nowhere (#14918).

The line names the population it cannot speak for

clauseScopeLine is computed over the enumerated workspace packages, and the workspace root is not one of them: observed() builds the root from its manifest alone, with none of the four observation fields, so TESTS_COVERED, SOURCES_COVERED, PINS_CHECKED and GENERATED_COVERED are never asked of it — while this file's header says the root is included "like any other package's". Found while writing the line; filed as #15483 (latent today: the root tsconfig declares no include, and git ls-files finds 0 root-level test files), and not repaired here — it is a fourth axis and this card is explicit that a fix must claim only one. The line says so rather than inheriting the boundary silently, which would be the defect it reports on:

  ⚠️ Counted over the enumerated workspace packages: the ROOT package is not one of them,
  and four per-package clauses are never asked of it (#15483).

Measured readings

Re-derived at claim time — triage's last reading (70/79, 9 ledgered, after PR #14916) is stale: the ledger is down to 4.

before (origin/main @ 0db29473c) after
headline 75/79 covered, 4 in DEBT, 1 exempt identical
source layer 0 directories, 0 files identical
packages SOURCES_COVERED was asked of 75 of 79 79 of 79
exit code 0 0

⇒ The gate stays green on this tree: the 3 non-root DEBT packages and the 1 EXEMPT package have no unread source directory today. Nothing had to be papered over, and there is no needs_decision hiding behind this green.

Two legs, so the green above is not read as "the clause does nothing"

A .ts file planted in packages/cloud-connection/scripts/ (a DEBT package, no typecheck script), same tree, same commit:

  • fixed gateEXIT=1, packages/cloud-connection/scripts: 1 non-test source file(s) here sit outside every tsc program that accounts for @objectstack/cloud-connection -- it declares no typecheck script, so tsconfig.json is all that reads it, and its DEBT entry records the ERROR COUNT tsc reports through that program
  • observation half mutated back to the old scope, same planted file → EXIT=0, check-type-check-coverage: OK — 75/79 workspace packages type-checked

Both mutations were confirmed on disk by grepping for the injected and the removed text (mutation_present=1 original_gone=0), and every restore was verified against the HEAD blob hash (780118dd1f5638c6a996233300bc47e240935c37) with git diff HEAD empty.

Two more ablations, on the self-test rather than the tree:

  • skippedClauses() neutered to return [] → self-test EXIT=1, 2 failures (skippedClauses — the packages a scoped clause was not asked of are named: expected [...], got []).
  • the finding's message forced back to the script-only wording → self-test EXIT=1, 3 failures, one per new ledger-basis case.

Verification

Exit codes captured before any pipe; each verdict line is the gate's own.

node scripts/check-type-check-coverage.mjs                     EXIT=0
  check-type-check-coverage: OK — 75/79 workspace packages type-checked (plus the root),
  4 in the DEBT ledger (53 frozen raw errors, #4311), 1 exempt.
node scripts/check-type-check-coverage.mjs --self-test         EXIT=0
  ✓ check:type-check-coverage --self-test — 50 semantic case(s) + 74 observation case(s)
  + 45 re-measure case(s) + 28 built-closure case(s) + 19 auto-lowering case(s)
  + 18 exit-code case(s) hold.
pnpm -s check:pm-dispatch-gates                                EXIT=0
  ✓ dispatch-gates self-test: 1402 cases pass.
pnpm check:nul-bytes                                           EXIT=0
  check-nul-bytes: OK (scanned 7489 text file(s) -- 7489 tracked, 0 untracked-not-ignored;
  skipped 7 binary; no raw ASCII control bytes).
pnpm check:ratchet-remedy-authority                            EXIT=0
  OK  check-ratchet-remedy-authority: 202 scripts swept; 14 mark the expanding remedy
  ⛔ MAINTAINER-ONLY

The self-test grew by 3 semantic cases and 6 observation cases; SELF_TEST_BATTERIES was re-floored (observation cases 57 → 63) and the three prose figures that quote it were moved with it. The row this change inverts — a DEBT package is not also billed for unread source — was replaced, not deleted: it asserted the behaviour this card calls the defect.

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 26 commands from the real change set (scripts/check-type-check-coverage.mjs, three-dot against the merge base). All 26 were run: 25 EXIT=0.

⚠️ The 26th, pnpm check:type-check-debt (the --re-measure variant), is NOT MEASURED, not a pass and not a red: EXIT=3, check-type-check-coverage: PREREQUISITE NOT MET — --re-measure cannot run: 33 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. That is a property of a fresh worktree, not of this diff — origin/main's own copy of the script, run on this same tree, refuses identically with EXIT=3. CI builds the closure (turbo run build --filter='./packages/*') before that step.

Every command above ran on this branch's final commit a103ce24f (git rev-parse --short HEAD, tree clean), and pnpm lint — the repo-wide eslint . --no-inline-config — was run in full rather than narrowed: EXIT=0.

All figures above were measured on a shared box under the repo's verify lock, which excludes other locked runs and nothing else.


🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

`check:type-check-coverage` evaluated its SOURCES_COVERED clause only for a
package that DECLARES a `typecheck` script. The argument written beside that
scope was that a DEBT package's answer is already "nothing reads this package",
which its ledger owns. It is not: a DEBT row records an ERROR COUNT taken
through `tsc -p tsconfig.json`, so a source directory OUTSIDE that program is
in neither the count nor any other reading. `packages/core/examples` held 2
non-test source files in no tsc program at all, neither of which had ever
compiled, and they surfaced only when an unrelated PR added a `typecheck`
script — repairing them moved the package 12 -> 29 errors, all of it drift
accumulated while nothing could check the file.

The clause now runs over `accountedPrograms()`, the same set TESTS_COVERED is
decided against: the configs the `typecheck` script invokes, or `tsconfig.json`
for a package with no such script (what `measureDebt` runs). The finding names
the claim it falsifies — a `typecheck` script, a DEBT row, an EXEMPT row, or
nothing accounting for the package at all — rather than assuming a script the
package may not have.

Measured on this tree: the gate stays green (75/79 covered, 4 in DEBT, 1
exempt), so no ledger row and no exclusion was widened to keep it green. Two
legs: with a source file planted in a DEBT package's unread directory the old
gate exits 0 and the new one reports it.

Also adds the gate's report on its own scope. Three axes of this gate's
blindness have each been found by a person tripping over one, and the shape
they share is a clause skipped for a population with the run saying so nowhere.
`SCOPED_CLAUSES` declares every such scope beside its predicate and its reason,
and every green run prints who was left out of what — today that is
GENERATED_COVERED over the 4 packages declaring no `typecheck` script.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@github-actions github-actions Bot added the size/m label Sep 4, 2026
`clauseScopeLine` is computed over the enumerated workspace packages, and the
workspace ROOT is not one of them: `observed()` builds the root from its
manifest alone, with none of the four observation fields, so TESTS_COVERED,
SOURCES_COVERED, PINS_CHECKED and GENERATED_COVERED are never asked of it while
this file's header says the root is included "like any other package's". Filed
as #15483 (latent today: the root tsconfig declares no `include`, and the tree
has 0 root-level test files).

A self-report that inherited that boundary silently would be the defect it
reports on, so the line says it. Pinned in both branches of the existing
self-test cases rather than as new ones, so the battery floor is unmoved.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@baozhoutao baozhoutao added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 4, 2026 — with Claude
@claude

claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

ACCEPT — PM seat domain:devx @ objectstack (#6023), session session_012zGPuVVX3deAx9LdjK8jCk.

Reviewed against the three-dot diff origin/main...claude/issue-14918-type-check-coverage-sources-clause (merge base e8c7956c4, head a103ce24f): one file, scripts/check-type-check-coverage.mjs, +323/−53, matching the PR file list.

Fixes #14918 closes the card on merge. #15483 is graded on the card. Flipping ready and arming auto-merge now.


Generated by Claude Code

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

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants