Uh oh!
There was an error while loading. Please reload this page.
build(rest): give the package a test-layer tsc program and ledger its 37 errors - #12570
Merged
Merged
Conversation
… 37 errors `packages/rest/tsconfig.json` excluded `**/*.test.ts` and `typecheck` was `tsc --noEmit` against that config, so no tsc program compiled any of the package's 149 test files and both `@ts-expect-error` directives in that layer were phantom checks. Adopts the #5286 sibling route: `tsconfig.test.json` with vitest-matching module semantics and untouched strictness, named by the `typecheck` script through the shared `check-test-typecheck.mts` gate. No test file is edited; the 37 errors the program finds (13 files) are recorded per file in `test-typecheck-debt.json`, EXACT and shrink-only. Two mechanical consequences, both required by gates rather than chosen: `@objectstack/rest` graduates out of TEST_DEBT (155 raw, re-measured 37), and its `check:type-source-resolution` registry entry gains the six workspace deps the new program reaches, with the program-set numbers stated in place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjujZN219uFzBhSYfMykCd
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 13 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
os-litant
marked this pull request as ready for review
August 26, 2026 11:25
This was referenced Aug 26, 2026
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#12542
packages/rest/tsconfig.jsonexcludes**/*.spec.tsand**/*.test.ts, and thepackage's
typecheckscript wastsc --noEmitagainst that config and nothingelse. So no tsc program compiled a single test file in the package — the
shape AGENTS.md names twice ("never exclude
*.test.ts", "a@ts-expect-errorin a file no tsc program compiles is a phantom check"), sitting live in one of
the repo's largest packages.
Premise, re-derived rather than inherited
At
5fbd58e0d(my branch base; the card measured at52a982388):include/exclude["src/**/*"]/["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]typecheckscripttsc --noEmit, that program and nothing elsegit ls-tree -r --name-only HEAD -- packages/rest | grep -cE '\.test\.ts$').spec.tsfiles**/*.spec.tshalf of the exclusion covers nothingsrc/?includetsconfig.test.json/test-typecheck-debt.jsoncheck:test-typecheckwiredThe card's numbers hold exactly, at both shas. Only the
.spec.tsline is anaddition: the exclusion's second glob was already inert.
What landed — an adoption, not an invention
packages/rest/tsconfig.test.jsonfollowspackages/client's andpackages/spec's landed shape: module semantics only, pluslib, withstrictness inherited and untouched. It is named by the
typecheckscriptthrough the shared
scripts/check-test-typecheck.mtsgate, because a config noscript invokes is exactly the phantom this change is about:
tsxjoinsdevDependenciesfor the same reasonpackages/clientandpackages/speccarry it — the shared gate runs through it.What the program INHERITED, and did not re-declare
packages/rest/tsconfig.jsonalready carries two#9960settings, bothload-bearing, and this file deliberately re-declares neither:
rootDir: ".."— already widened topackages/there, as a consequenceof the
pathsrule rather than a preference. So the TS6059 pile that forcedpackages/client's sibling config to widenrootDirdoes not arise here; itwas paid for already.
paths: { "@objectstack/metadata-protocol": [".../src/index.ts"] }— achild that declared its own
pathswould replace this map rather thanmerge into it, silently sending that specifier back to
dist/. This filedeclares no
pathsat all, so the rule stands and 22 of the producer's sourcefiles are in the test program exactly as they are in the build one.
The number nobody had (ruling 5), measured at
5fbd58e0d37 errors across 13 files, with the workspace closure built first
(
pnpm --filter '@objectstack/rest^...' build, exit 0).src/export-integration.test.tsNeither stop-and-report fork fires: 37 is above the "small,
client-scale"threshold and nowhere near "enormous", and it is not one producer-side defect
wearing many files' clothes — five codes across 13 files, the largest single
file holding 17.
Ruling 4's question, answered for all 37: would this error exist on
origin/mainif the program had always been there? Yes, for every one. Notest file is edited by this PR; the program differs from the build config only
in module semantics and
lib, and each of the 37 sits on a line that predatesthis branch. None is caused by the change, so all 37 are ledger entries in
packages/rest/test-typecheck-debt.json— EXACT and shrink-only.Why the 155 in TEST_DEBT became 37
Not a rescale — the old entry predicted it. 121 of its 155 were TS2835 plus the
implicit-any pile TS2835 causes, and 16 more were one
Array.prototype.atmessage against a
libolder than es2022. This package is"type": "module",so NodeNext was compiling extensionless relative imports as unresolvable ESM and
every symbol they named became
any. Under vitest-matching semantics TS2835x72 goes to 0, TS2550 x16 to 0, TS7006 x49 to 4. What is left is a different
shape, and it grew in one place while collapsing in four: TS18048 x13 is a class
the 155 never contained at all — "possibly undefined" reads that only become
visible once the imports above them resolve to real types. Sizing this off the
TS2835 line alone would have said "155 − 121 = 34" and been wrong in both
directions. That is the #8612 lesson, measured a second time.
No test file is edited (ruling 3), and the #12421 coupling
Zero test files are touched: the deliverable is the program plus the measured
ledger. PR #12421 holds
packages/rest/src/rest.test.ts, which carries 4 ofthe 37 and therefore has a ledger entry — which collides with nothing, where an
edit would have been a serial breach.
ledger is EXACT and shrink-only, so once #12421 merges: an error it adds to
rest.test.tsreds the ledger on its run ("the debt GREW"); an error itremoves reds the entry as stale until re-recorded ("the debt SHRANK"). Both
are the pin working, and both land on the change that caused them.
Both
@ts-expect-errordirectives in this layer — insrc/rest.test.tsandsrc/rest-api-plugin-slot-lookups.test.ts— were phantom checks that evaluatednever. Under this program neither reports TS2578, so both are live and each is
suppressing a real error.
Anti-vacuity: the program can say no (ruling 6)
Membership.
tsc -p tsconfig.test.json --listFilesputs 149 ofpackages/rest/src/**/*.test.tsin the program — the exact census — out of 489files total.
Ablation. A real type error planted in
src/analytics-routes.test.ts, anunledgered file, so the everyday verdict is what gets exercised:
The gate exits non-zero and names the file. The restore ran from a trap on
EXIT/INT/TERMagainst absolute paths, usedgit checkout HEAD -- path(never the bare form, which restores from a polluted index), and is proven by
blob hash — never by an exit code. It reports 2 errors, not 1: the second is
noUnusedLocalson the planted binding, which is itself evidence that inheritedstrictness is live. No build or
dist/is involved on either leg — tsc readsthese sources directly — so there is no artifact staleness to preflight.
Two mechanical consequences, both required by gates rather than chosen
1.
@objectstack/restgraduates out of TEST_DEBT. Once thetypecheckscript names the sibling config,
check:type-check-coveragereportshas a TEST_DEBT entry but ... no longer hides its tests -- it graduated. Theentry is deleted and a graduation note recorded in its place, per the file's own
convention. Headline moves: 19 → 18 packages hiding tests, 1110 → 965
hidden files, 1461 → 1306 frozen errors — exactly −1 package, −149 files,
−155 errors.
2.
check:type-source-resolutionsees a new program, and this is the onejudgement call in the PR — flagged rather than buried. Since #11490 that gate's
population is every
tsconfig*.jsonatypecheckscript names, so the newprogram joins it and reports six workspace deps the build program never reached:
driver-sql,metadata,plugin-hono-server,plugin-security,service-analytics,service-datasource— all six viatsconfig.test.json.I recorded them in that package's registry entry, with the program-set numbers
stated in place as the registry's own rule requires: before, at
5fbd58e0d,--listreported 93 programs / 77 packages / 54 entries / 233 pairs; after,94 / 77 / 54 / 239. +1 program, +0 entries, +6 pairs.
The gate's failure text asks for
pathsrules instead and calls registrywidening "not the fix", so here is why I did not do that — measured both ways
on the same checkout, not argued:
pathsredirecting the six to source takes the test layer from 37 errors to42, and the +5 are TS6133 in
../plugins/plugin-hono-server/src/{hono-plugin,current-user-endpoints}.tsand../drivers/driver-sql/src/sql-driver.ts— other packages' source billed topackages/rest/test-typecheck-debt.json, where they would then go red onthose packages' PRs. Worse, those five are not even real: both packages run
pnpm --filter ... typecheckgreen on this same tree (exit 0, measured), sothe borrowed program MANUFACTURES diagnostics that belong to nobody. A ledger
seeded with those cannot be paid down by the package that owns the file.
packages/rest/vitest.config.tsaliases exactly two of the six(
plugin-hono-server,service-datasource) to source and resolves the otherfour through
dist/. Blanketpathsis not fidelity to vitest either.@objectstack/clientand@objectstack/trigger-record-changeboth carrytest-program deps there, the latter being the package that took this very
#5286sibling route.pathsblock is not that.registry's doc-block permits a widening when the set of programs changed —
which is literally true here, 93 to 94 — but adds "that is a change to this file,
not to a package", written when the gate read only
tsconfig.jsonand a packagecould not move the population. This PR is the first case where a package does.
Happy to switch to
pathsif a maintainer reads that sentence as binding.Verification
All runs below are at
2dfb401c1, the pushed head, taken after the finalcommit. Every exit code captured before any pipe (redirect, then
$?).pnpm --filter @objectstack/rest typecheck— exit 0. Gate's own verdict line:check:test-typecheck: OK — @objectstack/rest's test layer compiles under packages/rest/tsconfig.test.json; 13 file(s) / 37 error(s) held in test-typecheck-debt.json (shrink-only).node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackagainst the real change set, all exit 0 —including
check:type-check-coverage,check:type-source-resolution,check:published-files,check:test-source-alias,check:nul-bytes,check:pm-dispatch-gatesandscripts/pm/bare-root-worklist.mjs --self-test(the last two are the convention-triggered pair this diff incurs by editing
gate scripts).
pnpm lint(eslint . --no-inline-config, the whole repo, not narrowed) —exit 0,
VERDICT command-exit 0.scripts/pm/os-verify-lock.sh; every one endedVERDICT command-exit 0.One declared narrowing:
pnpm check:type-check-debtwas NOT run. It ischeck-type-check-coverage.mjs --re-measure, and it refuses on this worktree —--re-measure cannot run: 32 workspace dependenc(ies) of the ledgered packages have no built type entry point on disk. That is a PREREQUISITE NOT MET, not ared gate: it declines to measure rather than measuring a different world. Its
prerequisite is the full workspace build that
lint.ymlruns before the step,which CI does on this PR regardless. Three things make the omission a
measurement rather than a gap:
--re-measurescores each ledgered package underits own tsconfig, and
@objectstack/restis in neither ledger after thisdiff, so it is not among the projects measured; outside
packages/restthis diffchanges only two gate scripts' data and prose — no package's tsconfig and no
package's source; and the lockfile delta is exactly three lines in
packages/rest's own importer, adding an already-resolvedtsx@4.23.12, so noother package's closure moved. The structural half of the same gate —
check:type-check-coverage, which owns TESTS_COVERED, PINS_CHECKED, RECONCILEDand the composition invariants, and is what this diff actually moves — ran green
above.
Changeset: deliberately none, and the rule applied
packages/rest'sfilesis["dist", "README.md", "CHANGELOG.md"]. This PRchanges no
src/file, sodist/is byte-unaffected;tsconfig.test.jsonandtest-typecheck-debt.jsonare not published; andtsxis a devDependency noconsumer installs. Nothing is released, which is the
skip-changesetlabel'sown definition in
pr-automation.yml. The in-tree precedent for this exactchange shape is
951b025e4—build(trigger-record-change): graduate the package out of the TEST_DEBT ledger(PR #11489),
package.json+tsconfig.test.json+ the coverage gate, nochangeset.
Generated by Claude Code