Skip to content

chore(typecheck): seven ledgered packages type-check their own tests through a sibling test program (#12511) - #16295

Merged
baozhoutao merged 3 commits into
mainfrom
claude/issue-12511-test-typecheck-sibling-configs
Sep 6, 2026
Merged

chore(typecheck): seven ledgered packages type-check their own tests through a sibling test program (#12511)#16295
baozhoutao merged 3 commits into
mainfrom
claude/issue-12511-test-typecheck-sibling-configs

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Part of #12511.

Seven of the card's eight residual packages now type-check their own tests through a sibling
test program named by their own typecheck script, so an error in a test file surfaces in
seconds instead of only after a full-closure build. @objectstack/http-conformance is
deliberately left out — its hole needs a config-policy judgement that is the card's to make,
stated at the bottom.

Config only: tsconfig.test.json (new, 7), package.json scripts (7), test-typecheck-debt.json
(new, 7, measured), the TEST_DEBT rows in scripts/check-type-check-coverage.mjs, the merge
dispositions in scripts/regen-artifacts.mjs, and a declared re-baseline in
scripts/check-type-source-resolution.mjs. No src/** and no test file is edited anywhere.

The census — measured, with positive controls in the same run

Limb 2 is decided by RUNNING every config each package's typecheck script NAMES under
tsc --noEmit --listFiles and counting that package's own test files in the resulting program —
never by looking for a tsconfig.test.json on disk. Taken at 6a1e38244 with the dependency
closure built, and re-confirmed on the merged tree at bb4ccec4e.

package programs typecheck named BEFORE (files / own src / own tests) own tests in program, AFTER TEST_DEBT before per-file ledger after
mcp tsconfig.json 412 / 11 / 0 of 26 26 of 26 (553 files) 53 53 over 6 files
formula tsconfig.json 181 / 15 / 0 of 29 29 of 29 (271) 17 15 over 5 files
platform-objects tsconfig.json 419 / 91 / 0; tsconfig.scripts.json 371 / 72 / 0 of 35 35 of 35 (579) 3 3 over 1 file
connector-mcp tsconfig.json 349 / 4 / 0 of 3 3 of 3 (418) 5 5 over 1 file
connector-openapi tsconfig.json 332 / 4 / 0 of 4 4 of 4 (402) 5 5 over 1 file
connector-rest tsconfig.json 340 / 4 / 0 of 4 4 of 4 (405) 1 1 over 1 file
service-sms tsconfig.json 751 / 7 / 0 of 5 5 of 5 (792) 1 1 over 1 file
http-conformance tsconfig.json 346 / 3 / 0 of 5 not wired — see below 2 unchanged

Positive controls, same binary, same run, same worktree — without them every zero above is
unfalsifiable: packages/rest tsconfig.test.json 186 test files in program;
packages/metadata-core tsconfig.test.json 15; packages/drivers/driver-memory
tsconfig.json (no test exclusion) 44. Every program in the table also holds a non-zero
count of the package's own src files, so no zero is an empty-program artifact.

The coverage gate's own summary line, before and after:

before  test layer: 8 package(s) still hide their own tests from tsc
        (111 files hidden as counted by this run, 87 frozen raw errors in TEST_DEBT).
after   test layer: 1 package(s) still hide their own tests from tsc
        (5 files hidden as counted by this run, 2 frozen raw errors in TEST_DEBT).

pnpm check:type-check-coverage exits 0 before and after; --self-test exits 0 (55 semantic +
97 observation + 45 re-measure + 28 built-closure + 19 auto-lowering + 18 exit-code cases hold).

Not a blanket edit — where the per-package judgement actually diverged

Each config states its own reading in its own header. The two axes that mattered:

package what its config changes, and why
mcp Module semantics UNTOUCHED. It is "type": "module", so the inherited NodeNext already reads its tests as ESM and is the stricter of the two readings; measured, this layer writes zero extension-less relative imports and esnext/bundler subtracts nothing. metadata-core's precedent, not rest's. lib: ["ES2022"] only.
formula module: esnext / moduleResolution: bundler — the one package of the seven where the change subtracts a measured pile (TS1470 x2, import.meta in a CJS program). Plus lib: ["ES2022"].
platform-objects Same module override; measured subtraction zero — declared for fidelity, and because this package is the card's live exemplar (PR #15874 hit a real TS2339 on Intl.supportedValuesOf here against an ES2020 lib and had to build a scratch config to see it). Its typecheck gains a third leg beside the existing tsconfig.scripts.json one.
connector-mcp, connector-openapi, connector-rest, service-sms Same module override; measured subtraction zero in each. Declared so the first test file to use import.meta or an extension-less import gets no config-tier diagnostic ledgered as if it were a defect.

rootDir is inherited untouched in all seven — every test file lives under src, and the two
imports that escape the package (scripts/js-comment-mask.mjs, from mcp and platform-objects)
land on a checked-in js-comment-mask.d.mts, so the raw programs carry no TS6059. Measured, not
reasoned. Strictness is untouched everywhere, and no config declares paths (a child's paths
replaces the parent map rather than merging into it).

lib: ["ES2022"] has a measured effect of zero on today's tree in all seven — no TS2550
either way. It is stated that way rather than claimed as a subtraction; it is fidelity to the Node
vitest actually runs on, and it is the exact trap the card was filed about (Object.hasOwn is
TS2550 against a pre-ES2022 lib while the package's advertised typecheck says nothing).

TEST_DEBT attribution — both directions, no remainder

RAW is re-measured on the way out through the coverage gate's own remeasureProject shape
(extends the package's tsconfig.json, drops ONLY the test glob) at 6a1e38244 with the closure
built. RECORDED equalled RAW for all seven, class for class and file for file.

package RECORDED RAW dissolve exposed ledger
mcp 53 53 0 0 53
formula 17 17 -2 0 15
platform-objects 3 3 0 0 3
connector-mcp 5 5 0 0 5
connector-openapi 5 5 0 0 5
connector-rest 1 1 0 0 1
service-sms 1 1 0 0 1

The single dissolution is formula's TS1470 x2 under module: esnext. Nothing was exposed
behind it
— there was no unresolved-import cascade to collapse in any of the seven, so there is
no +n term anywhere. Not one of the 83 is repaired here; this change edits no test file, and each
would have been reported on origin/main had these programs always existed.

One composition correction the deleted formula entry earned: it attributed its TS2591 x6 to
process. Measured, all six are node builtins named as bare specifiers (node:fs x2,
node:path x2, node:url x2) and the TS2339 x2 beside them read
Property 'url' does not exist on type 'ImportMeta' — one cause, this package declaring no
@types/node and naming no types. Real debt, now ledgered per file and per signature, and
deliberately not repaired here.

Two mechanical consequences, both recorded in place:

  • scripts/regen-artifacts.mjs gains seven NOT_DRIVER_MANAGED rows, one per new ledger.
    reconcileGenerators keys on (owner, script), so one row standing for the family would be a
    disposition nobody made for any of these files. pnpm check:merge-driver exits 0.
  • scripts/check-type-source-resolution.mjs takes the re-baseline its own doc-block opens for
    exactly this case, with the provenance the doc-block requires: every admitted dep is annotated
    via tsconfig.test.json by the gate itself, i.e. reached only through the program this change
    onboarded. 125 -> 132 programs, 61 -> 61 entries, 310 -> 319 package-dep pairs
    (mcp +2, platform-objects +3, the three connectors +1 each, service-sms +1).
    formula onboarded a program and admitted nothing, which is the control saying the other six
    report a real widening rather than an artifact of the population growing. paths is measured to
    be the wrong tool for the onboarding case (PR build(rest): give the package a test-layer tsc program and ledger its 37 errors #12570: 37 -> 42, billing other packages' source
    diagnostics into the onboarding package's ledger), so the two places where a vitest alias and the
    type program now disagree about which artifact they read are declared there rather than
    papered over.

Positive control — the card's own claim, reproduced

One deliberate type error planted in an unledgered test file of a wired package, on the
committed tree, restored by blob hash. Nothing from this step is committed.

HEAD blob:                 284876511f134709f854edec7bed238b1878d30c
on-disk before:            284876511f134709f854edec7bed238b1878d30c
anchor occurrences before: 1        (unique — otherwise the control did not run)
injected occurrences:      1        (proof it reached disk; an editor's exit code is not proof)
on-disk after mutation:    557714f45394567d5e6787c53c84d5df2c1a9090

--- pnpm --filter @objectstack/connector-rest typecheck (mutated tree)
elapsed: 8s   typecheck exit: 1
check:test-typecheck: 1 problem(s)
  - src/connector-rest-plugin.test.ts: 1 type error(s) in a file the ledger does not cover.

on-disk after restore:     284876511f134709f854edec7bed238b1878d30c   (== HEAD blob)
git diff HEAD  -> empty
git status --porcelain -> empty

8 seconds on a shared box, naming the file. Before this change the same error was invisible to
that package's advertised typecheck and reachable only through a full-closure build — the ~9
minutes the card measured. Restore is git checkout HEAD -- <path> (never a bare checkout, which
reads the index the mutation polluted), under a trap with absolute paths.

Verification, pinned to bb4ccec4e

  • Each wired package's own typecheck, both legs, on the merged tree with the closure built: 7 of
    7 exit 0
    , each printing check:test-typecheck: OK with its ledger held exactly
    (53 / 15 / 3 / 5 / 5 / 1 / 1 errors; 8 / 12 / 2 / 1 / 1 / 1 / 1 pinned signatures).
  • Gate union derived with node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack
    on the final tree: 70 commands, 70 run. Exit codes captured before any pipe.
    67 exit 0 on the first pass. Three returned exit 3 — PREREQUISITE NOT MET, which is NOT
    MEASURED and not a finding, never a red
    (check:dual-build-cjs-loads and check:i18n need a
    full workspace build; check:type-check-debt OOM'd under a 4096 MB ceiling the runner imposed,
    below the 6144 MB the CI job uses). Re-run with pnpm build done (exit 0) and at CI's ceiling,
    all three exit 0 — so 70 of 70 exit 0. check:type-check-debt's own verdict line:
    check-type-check-coverage --re-measure: OK — 5 ledger entr(ies) re-measured in 95.9s, 55 raw tsc error(s) total, none above its recorded number.
  • Not run locally, by design: the repo-wide pnpm lint sweep and the full test farm are CI's.

skip-changeset applied: config-only, nothing is published from any package by this diff.

What did NOT graduate, and why it is the card's call

@objectstack/http-conformance keeps its entry. Its 2 recorded errors reproduce exactly
(TS2307 x1, TS2304 x1) and its own ledger note already says what they are — both inside
node_modules .d.ts files (@better-auth/core's bun:sqlite import, @better-fetch/fetch's
Timer), so the entry moves with the lockfile rather than with this package's code.

The reason it cannot take the sibling route unchanged is a fact this repo had not recorded:
packages/qa/http-conformance/tsconfig.json is one of six package configs that do not extend
the repo root config, and it is the only one of those that also declares no skipLibCheck — which
is the sole reason those two third-party declarations are checked at all. Both available repairs
depart from precedent:

  • Ledger them per file. That keys a shrink-only, EXACT ratchet on .pnpm content-hash paths
    that move on any unrelated dependency bump — a ledger nobody can pay down, and red for reasons
    that are about no test.
  • Turn skipLibCheck on. None of the 31 sibling test configs declares it, and the alternative
    — putting it in the build config, or making that config extend the root like every other package —
    changes what the build program reports for src too, which every one of the 31 precedents
    explicitly refuses to do.

That is a judgement about this repo's config policy rather than about this package, so it is left
to the card rather than taken here.


Generated by Claude Code

…gered packages

WIP: the tsconfig.test.json files and the package.json wiring. Ledgers, the
TEST_DEBT graduations and the config headers follow in the next commits.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
…and config headers

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 7 changed file(s) yielded no anchor (packages/connectors/connector-mcp/test-typecheck-debt.json, packages/connectors/connector-openapi/test-typecheck-debt.json, packages/connectors/connector-rest/test-typecheck-debt.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 7 changed package(s)).

What this run could not see
  • 7 changed file(s) yielded no anchor (packages/connectors/connector-mcp/test-typecheck-debt.json, packages/connectors/connector-openapi/test-typecheck-debt.json, packages/connectors/connector-rest/test-typecheck-debt.json, …) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 7beaaa32ccb1295eeaec73bdca4aea36e713232dpackageMentionDocs.

@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Sep 6, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 6, 2026 13:17
@baozhoutao
baozhoutao enabled auto-merge September 6, 2026 13:17
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 8cf806f Sep 6, 2026
40 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-12511-test-typecheck-sibling-configs branch September 6, 2026 13:49
baozhoutao pushed a commit that referenced this pull request Sep 6, 2026
…vel anchors

Second round of the scripts/** unresolvable-citation migration PR #16301
started. Two of the six held files freed since that PR landed --
check-docs-section-name.mjs and docs-audit/affected-docs.mjs (PR #16300),
check-type-check-coverage.mjs (PR #16295) -- so their 7 citations migrate
now by the same method: the file named as a file-level anchor, the number
kept beside it as data, no digit repaired or repointed.

- check-docs-section-name.mjs: concept.mdx:426, doc-pages.mdx:257 and
  forms.mdx:183 each resolve to exactly one tracked file under
  content/docs/** (forms.mdx is ambiguous by basename alone -- two files
  share it -- but only content/docs/ui/forms.mdx has a line 183 carrying
  the cited "sections": [/* ... */] shape).
- check-type-check-coverage.mjs: engine.test.ts:2547/2577 resolves to
  packages/services/service-automation/src/engine.test.ts, named by the
  surrounding prose ("service-automation's note").
- docs-audit/affected-docs.mjs: three rest-server.ts citations resolve to
  packages/rest/src/rest-server.ts, the only tracked file of that name.

The corpus residual (scripts/check-scripts-symbol-anchors.mjs
--list-unresolvable) drops from 15 to 8, all 8 still held by open PRs
(#16215, #16202) -- none an ambiguity. judgeUntrackedLineAnchors stays
false per the #15809 fence (flips only at residual zero). Header prose
updated to record both dated readings.

Part of #15809.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants