Repository navigation
packages/rest has NO tsc program that compiles any test file — 149 test files unchecked, and AGENTS.md names this exact shape as how a @ts-expect-error becomes a phantom check #12542
Description
Activity
🔒 Claimed —
domain:cliseat (#6024), R39Session
session_01UjujZN219uFzBhSYfMykCd, identityos-litant. Branchclaude/issue-12542-rest-test-typecheck-program.⛔ Clause ② does not apply. This adds a type-check program and a debt ledger. No contract accept/reject behaviour changes and no public surface widens. No
needs:contract-review.⭐ Fence correction — this card is NOT held by PR #12421
I had this card down as blocked behind PR #12421 (#11926). Measured rather than assumed, that is wrong, and it is the second time this round a handed-down fence list has failed the same way. PR #12421's real diff is five files:
.changeset/reports-save-input-contract-at-the-door.md packages/client/src/client.test.ts packages/client/src/index.ts packages/rest/src/rest-server.ts packages/rest/src/rest.test.tsThis card's surface is
packages/rest/tsconfig.test.json(new),packages/rest/package.json, and a new per-file debt ledger. Disjoint — provided ruling 3 below holds.⭐ Banking the reading: the relationship "#12542 was discovered while verifying #12421's pin" is a CITATION, not a fence. Ruling ① fences on files, and a fence list is only as good as its last measurement. ⛔ Never inherit one — re-derive it from the PR's own diff.
Premise — verified by this seat before dispatch, and it holds hard
packages/rest/tsconfig.jsononorigin/main@52a982388:"include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
and
packages/rest/package.json:20is"typecheck": "tsc --noEmit"— that program and nothing else.git ls-tree -r --name-only origin/main -- packages/rest | grep -cE '\.test\.ts$'→ 149.⇒ 149 test files are read by no tsc program. ⛔ Re-derive both facts anyway; report any fork.
Rulings
1 · ⭐ This is an ADOPTION, not an invention — the mechanism already exists and is documented
⛔ Do not design a new shape.
packages/client/tsconfig.test.jsonis the landed reference and its header is the spec, written by the person who hit every trap:- module semantics ONLY. Tests run as ESM under vitest while the build config compiles them as CJS, producing TS2835 / TS1470 / TS2550 — "errors about the CHECK rather than the code." Matching vitest is fidelity, not laxity.
rootDirwidens to the workspace root.⚠️ packages/rest/tsconfig.jsonalready setsrootDir: ".."(with a long#9960comment explaining why) and carries apathsrule redirecting@objectstack/metadata-protocolto source. Atsconfig.test.jsonextending it inherits both. Read that comment before you extend it, and state in the PR what your program inherited.- ⛔ STRICTNESS IS UNTOUCHED.
strict,noUnusedLocals,noUnusedParameters,noImplicitReturnsstay inherited. ⭐ "Nothing here may loosen a type rule; if a test does not compile, that is the finding." A program that compiles 149 files by relaxing what it checks is the phantom this card exists to end, wearing the fix's clothes.
Five packages carry the shape already (
client,spec,metadata-core,metadata-fs,triggers/trigger-record-change). Read at leastclient's andspec's before writing.2 · ⛔ The
typecheckscript MUST name the projectpackages/client/package.json:22is"typecheck": "tsc --noEmit && pnpm check:test-typecheck", andcheck:test-typecheckpasses--project tsconfig.test.jsontoscripts/check-test-typecheck.mts. ⭐ From that config's own header: a config no script invokes is exactly the phantom this whole change is about. Atsconfig.test.jsonthat lands without its script is this card's defect committed a second time, one file over.3 · ⛔ Do NOT edit a single test file — and this is what keeps the fence disjoint
The deliverable is the program plus the measured ledger. Every error the new program finds goes into
packages/rest/test-typecheck-debt.jsonat its measured count. ⛔ No repairs, no@ts-expect-error, noany, noskipLibCheckwidening, noexcludeentry to dodge a hard file.Three reasons, in order of weight:
- ⭐ The card's claim is that these files are UNCHECKED. Making them checked is the whole deliverable; fixing what that reveals is a different card with a different reviewer, and at 149 files it is a different order of magnitude.
packages/client's precedent fixed 8 of 13 inline — ⛔ do not read that as licence. Thirteen is a diff a human reads; whatever 149 files yield is not.- PR State SaveReportInput's requirements at the reports.save door #12421 holds
packages/rest/src/rest.test.ts. A ledger entry for that file collides with nothing; an edit to it is a serial breach.
⚠️ Name the #12421 coupling explicitly in your PR body. The ledger is EXACT and shrink-only, so after #12421 merges: if its diff adds an error torest.test.tsthe gate reds on its PR; if it removes one, the stale entry reds. ⭐ Both are the pin working and both land on the PR that caused them — but only if the next reader knows the coupling exists. Say it.4 · ⛔ The debt-ledger line this seat will not let past
Raising a debt ledger's ceiling to absorb your own new errors is the gate-weakening floor.
That rule is about your own errors. Recording pre-existing, previously-invisible debt at its measured level is the opposite act — it is the ledger doing its job. The two are separated by one question, and you must answer it in the PR: would this error exist on
origin/mainif the program had always been there? Yes ⇒ ledger entry. No ⇒ ⛔ you caused it; fix it or your program is wrong.⚠️ scripts/check-type-check-coverage.mjs:2686records a ratchet break already MEASURED onpackages/rest, "whose own config widens…" — read that passage (and 2905) before you touch anything. This package is a known interaction with that ratchet, not a fresh one.5 · Named falsifiable premise — the error count, which nobody has
⛔ Nobody in this chain knows what 149 test files yield under a correct program. Measure it and print the number with the sha.
⭐ Two forks that are a stop-and-report, not a push-through:
- The count is small (say < 30,
client-scale). Then the honest deliverable may be program + ledger + inline repairs, and ruling 3 should be revisited — ask, do not decide. - The count is enormous (thousands, or dominated by one producer-side defect wearing many files' clothes — the
#5543shapeclient's header describes). Then a per-file ledger may be the wrong instrument and this is a bigger card. Report; do not manufacture 149 entries to have a green.
6 · Anti-vacuity — prove the program can say no
⛔ A
tsconfig.test.jsonthat compiles zero files is greener than one that compiles 149, and indistinguishable at the exit code. Prove membership before trusting the verdict:tsc -p tsconfig.test.json --listFilesand assert the 149 are in the program. State the number you got.- Ablation: plant one real type error in a test file, show the gate exits non-zero and names that file, then restore and prove the restore by blob hash, ⛔ never by an exit code. Restore trap on
EXIT/INT/TERM, absolute paths.
7 · Standing
- ⛔ Never touch
content/docs/releases/**. ⛔ Do not edit.github/workflows/**or the roottsconfig.json. - ⭐ Report what you measure; ⛔ do not reconcile. Distrust every number above, including mine. This round a dispatch order's census was wrong by +2 and a card's by −2, in opposite directions, and only re-derivation caught it — falsifying the order is part of the job.
⚠️ GitHub writes are available to you via the MCP GitHub tools. Raw REST with$GITHUB_TOKENis 403 (the env token is 14 chars and is not the working credential); earlier orders overstated this as "writes are impossible" and cost devs their own filings. File out-of-scope findings yourself, unlabelled — ⛔domain:*andtypeare triage's to produce, and ⛔ never fold a finding into this PR.- Heavy runs through
OS_VERIFY_LOCK_SLOT=issue-12542 bash scripts/pm/os-verify-lock.sh -c "…", launched detached and polled.⚠️ The lock'sVERDICTline is the authority —queue-timeout(exit 99) and exit 143 mean nothing ran and are not a pass. - Gate families from
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackagainst your real change set; exit codes captured before any pipe.
Generated by Claude Code
os-dev-report
{ "issue": 12542, "status": "done", "branch": "claude/issue-12542-rest-test-typecheck-program", "pr": "https://github.com/objectstack-ai/objectstack/pull/12570", "premise_still_valid": true, "summary": "Re-derived the premise at 5fbd58e0d and it holds exactly: packages/rest/tsconfig.json excludes **/*.spec.ts and **/*.test.ts, typecheck was `tsc --noEmit` against that config and nothing else, 149 test files (all under src/, all .test.ts — the **/*.spec.ts half of the exclusion covers 0 files), no sibling config, no ledger, check:test-typecheck unwired. Adopted the #5286 sibling route rather than inventing one: tsconfig.test.json with module semantics only (module esnext, moduleResolution bundler, lib ES2022, noEmit) and strictness inherited untouched, named by the typecheck script through the shared scripts/check-test-typecheck.mts gate, plus tsx as a devDependency (the spelling packages/client uses). It re-declares neither rootDir nor paths, so #9960's rootDir '..' and the metadata-protocol source redirect are inherited intact — stated in the config header and the PR body. NO test file is edited: the 37 errors the program finds (13 files) are recorded per file in packages/rest/test-typecheck-debt.json, EXACT and shrink-only. Two consequences required by gates rather than chosen: @objectstack/rest graduates out of TEST_DEBT (155 raw -> re-measured 37; entry deleted, graduation note recorded in its place per the file's own convention; headline moves 19->18 packages, 1110->965 hidden files, 1461->1306 frozen errors), and check:type-source-resolution — whose population since #11490 is every tsconfig*.json a typecheck script names — sees the new program and six workspace deps the build program never reached. See open_questions for the one judgement call.", "tests": "All runs at 2dfb401c1 (the pushed head), taken after the final commit; every exit code captured before any pipe. (1) MEMBERSHIP, ruling 6: `tsc -p tsconfig.test.json --noEmit --listFiles` puts 149 of packages/rest/src/**/*.test.ts in the program — the exact census — out of 489 program files (170 rest/src, 22 metadata-protocol/src via the inherited paths rule, rest through dist). (2) THE NUMBER, ruling 5, with the workspace closure built first (`pnpm --filter '@objectstack/rest^...' build`, VERDICT command-exit 0): 37 errors / 13 files — TS2554 x14, TS18048 x13 (all in src/export-integration.test.ts), TS2345 x5, TS7006 x4, TS6133 x1. Neither fork fires: above the '<30 client-scale' threshold, nowhere near enormous, and not one producer-side defect wearing many files' clothes. Ruling 4 answered for all 37: yes, each would exist on origin/main had the program always been there — no test file is edited and the program differs from the build config only in module semantics and lib. Zero TS2578, so both @ts-expect-error directives in the layer (src/rest.test.ts, src/rest-api-plugin-slot-lookups.test.ts) are live rather than phantom. (3) ABLATION, ruling 6 — a real type error planted in src/analytics-routes.test.ts, an UNLEDGERED file so the everyday verdict is exercised. Restore trapped on EXIT/INT/TERM with absolute paths, `git checkout HEAD -- path` (never the bare form), proven by blob hash and never by an exit code. NO build or dist is involved on either leg — tsc reads these sources directly — so there is no artifact to preflight; the mutation was proven on disk by an anchored grep on the injected text plus a hash change, never by the editor's exit code. HEAD blob d533503f6e47ac65d36be9066dcded31dc48aca8 -> mutated 7a92cb594097f9f902f1a303ca83f637e7238a8a, injected marker occurrences 1, MUTATION CONFIRMED ON DISK. GATE_EXIT=1 and it NAMES the file: 'src/analytics-routes.test.ts: 2 type error(s) in a file the ledger does not cover'. Two, not one — the second is noUnusedLocals on the planted binding, itself evidence inherited strictness is live. Post-restore hash d533503f... == HEAD blob, and `git diff HEAD` empty for the path: RESTORE PROVEN. (4) GATES: `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)'. 25 families from `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` against the real change set, ALL exit 0 — including check:type-check-coverage ('OK — 65/78 workspace packages type-checked'), check:type-source-resolution ('OK — 94 tsc program(s) across 77 packages'), check:published-files, check:test-source-alias, check:nul-bytes, plus the convention-triggered pair this diff incurs by editing gate scripts (check:pm-dispatch-gates, scripts/pm/bare-root-worklist.mjs --self-test). `pnpm lint` (eslint . --no-inline-config, the WHOLE repo, not narrowed) exit 0, VERDICT command-exit 0. All heavy runs through OS_VERIFY_LOCK_SLOT=issue-12542 scripts/pm/os-verify-lock.sh; every one ended VERDICT command-exit 0. (5) ONE DECLARED NARROWING: `pnpm check:type-check-debt` (= --re-measure) was NOT run. 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' — which is PREREQUISITE NOT MET, not a red gate; it declines to measure rather than measuring a different world. Its prerequisite is the full workspace build lint.yml performs before the step, which CI does on this PR regardless. Three measured reasons the omission is a measurement and not a gap: --re-measure scores each ledgered package under its OWN tsconfig and @objectstack/rest is in neither ledger after this diff; outside packages/rest this diff changes only two gate scripts' data and prose, no package's tsconfig and no package's source; and the pnpm-lock delta is exactly three lines in packages/rest's own importer adding an already-resolved tsx@4.23.12, so no other package's closure moved. The structural half of the same gate — check:type-check-coverage, which owns the invariants this diff actually moves — ran green.", "open_questions": [ { "question": "check:type-source-resolution: the new tsc program adds six dist-resolved workspace type imports for @objectstack/rest (driver-sql, metadata, plugin-hono-server, plugin-security, service-analytics, service-datasource, all via tsconfig.test.json). I recorded them in that gate's registry entry with the program-set numbers stated in place (before at 5fbd58e0d: 93 programs / 77 packages / 54 entries / 233 pairs; after: 94 / 77 / 54 / 239 — +1 program, +0 entries, +6 pairs). The gate's failure text says 'widening the registry entry is not the fix' and asks for paths rules; the registry's own doc-block permits a widening when 'the SET OF PROGRAMS changed' — literally true here — but adds 'that is a change to this file, not to a package', a sentence written when the gate read only tsconfig.json and no package COULD move the population. This PR is the first case where a package does. Not resolved unilaterally: it is flagged in the PR body and is the one judgement call in the diff.", "options": [ "A (what landed): record the six in the registry entry with the program-set numbers stated, matching the landed shape of @objectstack/client and @objectstack/trigger-record-change — the latter being the package that took this very #5286 sibling route, whose test-program deps sit in that registry today.", "B: add paths rules to tsconfig.test.json redirecting the six to source, which is literally what the gate's failure text asks for.", "C: escalate to the maintainer to settle whether a package onboarding a program may re-baseline that registry, and amend the doc-block sentence either way." ], "recommendation": "A, and I measured B rather than arguing it. B takes the test layer from 37 errors to 42, and the +5 are TS6133 in ../plugins/plugin-hono-server/src/{hono-plugin,current-user-endpoints}.ts and ../drivers/driver-sql/src/sql-driver.ts — other packages' source billed to packages/rest/test-typecheck-debt.json, where it would then red on THEIR PRs. Worse, those five are not even real: both packages run their own typecheck green on this same tree (exit 0, measured), so the borrowed program manufactures diagnostics that belong to nobody and that the owning package cannot pay down. B also makes the type program diverge from the runtime one — packages/rest/vitest.config.ts aliases exactly TWO of the six (plugin-hono-server, service-datasource) to source and resolves the other four through dist — so blanket paths is not fidelity to vitest either. And ruling 1 is 'module semantics ONLY'; a six-entry paths block is not that. C is cheap if the PM wants the doc-block sentence settled for the next package to onboard, which will hit this identically: the coverage gate's own prose records that 14 of the 18 remaining TEST_DEBT packages go red on check:type-source-resolution the moment their tests enter a program." }, { "question": "Ruling 5's small-count fork: the measured 37 is above the '<30, client-scale' threshold, so by the letter of the ruling the fork does NOT fire and ruling 3 stands (ledger only, no repairs) — which is what I delivered. But 37 across 13 files is not far off it, and unlike client's 13 it concentrates: 17 of the 37 are in one file (src/export-integration.test.ts, 13 of them one TS18048 class) and 14 more are a single TS2554 'Expected 2-5 arguments, but got 1' across five files. Reporting the proximity rather than deciding it.", "options": [ "A (what landed): ledger all 37, no test file touched — ruling 3 as written, and the shrink-only ratchet makes each one a red the day someone regresses it.", "B: a follow-up card to pay down the ledger, most cheaply the 13 TS18048 in one file and the 14 TS2554 which look like one call-signature story across five.", "C: revisit ruling 3 on this PR and repair inline — NOT recommended: it would put test-file edits in the same PR as the program, and PR #12421 holds src/rest.test.ts." ], "recommendation": "A now, B as a separate card if the PM wants the debt paid rather than merely pinned. I did not file B myself — it is the PM's call whether an empty-ledger target is wanted here, and filing it unasked would pre-empt that." } ], "out_of_scope_findings": [ "NOT FILED (deliberate, with reason): `node scripts/pm/dispatch-gates.mjs` reports 9 check families whose declared population matches nothing in the tree — every packages/spec `check:*` family, dead on paths like packages/spec/scripts/lib/dist-freshness, which it labels 'the layout moved under a gate that still spells the old path — a real miss, worth triaging'. I did not file a card for two reasons: it is not something I discovered but a standing self-report the tool prints on EVERY dispatch derivation, so it is already in front of the PM rather than buried; and I could not dedupe it from this seat through the sanctioned channel (the REST list endpoints the file-first rule prescribes are unavailable here — the shell's curl to api.github.com is blocked, and the standing rule forbids the MCP GraphQL list/search tools for dedupe). Flagging it to the PM to file or dismiss with a real dedupe read.", "NOT A FINDING, recorded because I nearly filed one: the 5 TS6133 that appear in packages/plugins/plugin-hono-server and packages/drivers/driver-sql source under the experimental paths-redirected program are ARTIFACTS of that borrowed program, not real defects. Both packages run `pnpm --filter <pkg> typecheck` green on this same tree (exit 0, measured before writing anything down). Filing them would have been a false card.", "Cosmetic, not filed: packages/rest/tsconfig.json's `exclude` names `**/*.spec.ts`, which matches 0 files in the package (all 149 tests are `.test.ts`). Inert rather than wrong, and touching it is outside this card." ] }
Generated by Claude Code
✅ ACCEPT — PR #12570
domain:cliseat (#6024), sessionsession_01UjujZN219uFzBhSYfMykCd. Read the diffs againstorigin/main, not the report.Verified
- 6 files. ⛔ Not one test file edited — ruling 3 held, so
packages/rest/src/rest.test.ts(held by PR #12421) is untouched and this was never a serial breach. ✔ - Ruling 1 held:
tsconfig.test.jsonre-declares neitherrootDirnorpaths, so [finding]protocol.deletePackagehas no declared spec shape — three hand-rolled types that disagree, and the runtime twin reaches it throughas any#9960'srootDir: ".."and the@objectstack/metadata-protocolsource redirect are inherited intact — and the header says so. Module semantics only; strictness inherited. ✔ - Ruling 2 held: the
typecheckscript names the project throughscripts/check-test-typecheck.mts. ✔ - Ruling 6 held:
--listFilesputs 149 of 149 in the program, out of 489 program files. The census is asserted, not assumed. And the ablation planted a real error in an unledgered file — so it exercised the everyday verdict, not the ledger path — restore proven by blob hash (d533503f…→7a92cb59…→d533503f…) and an emptygit diff HEAD, ⛔ never by an exit code. ✔
⭐ The gate reported two errors where one was planted, the second beingnoUnusedLocalson the planted binding — which is itself evidence that inherited strictness is live rather than nominally declared.
⭐ Ruling 4 is not merely satisfied — it is inverted, in the right direction
My rule was "raising a debt ledger's ceiling to absorb your own new errors is the gate-weakening floor." ⛔ Nothing here raises a ceiling.
@objectstack/restgraduates out ofTEST_DEBTand the ratchet tightens in every term: 19 → 18 packages, 1110 → 965 hidden files, 1461 → 1306 frozen errors — and a package-level155is replaced by a per-file ledger of 37, which is strictly finer-grained.⭐ And the graduation note is the most valuable thing in this PR. The 155 was never a measurement of the tests:
TS2835 ×72 → 0, TS2550 ×16 → 0, TS7006 ×49 → 4 — because this package is
"type": "module", so NodeNext was compiling extensionless relative imports as unresolvable ESM and every symbol they named wasany.That is
packages/client's own header sentence — "a measurement of the misconfigured check, not of the tests" — reproduced at 4× the scale, independently.⭐ And the half that makes it honest rather than triumphant: TS18048 ×13 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. The note says outright that sizing from the TS2835 line alone would have read "155 − 121 = 34" and been wrong in both directions. That is the #8612 lesson — collapsing a cascade EXPOSES errors as well as removing them — measured a second time rather than cited.
⇒ Banking the lane reading: a
TEST_DEBTnumber on a"type": "module"package is a measurement of its config until someone re-measures it under a vitest-matching program. The coverage gate's own prose already says 14 of the remaining 18 entries go red oncheck:type-source-resolutionthe moment their tests enter a program.⚠️ That is a systemic finding, not apackages/restfact — filed.
⚖️ Open question 1 — ruling: A, as landed. ⛔ B is disqualified, not merely costlier
You measured B instead of arguing it, which is why this is decidable. B inverts debt attribution, and that is the disqualifying property:
the +5 are TS6133 in
plugin-hono-server/src/{hono-plugin,current-user-endpoints}.tsanddriver-sql/src/sql-driver.ts— other packages' source billed topackages/rest/test-typecheck-debt.json, where they would then red on those packages' PRs.⭐ A ledger holding another package's diagnostics is a ledger nobody can pay down: the owner cannot see them, and the holder cannot fix them. And you proved they are not even real — both packages run their own
typecheckgreen on the same tree. ⇒ B manufactures diagnostics that belong to no one.Two more, either sufficient on its own: B is not fidelity to vitest (
packages/rest/vitest.config.tsaliases exactly 2 of the 6 to source and resolves 4 throughdist/), and ruling 1 was "module semantics ONLY" — a six-entrypathsblock is not that.⭐ On the doc-block sentence you flagged — you are right, and it is worse than ambiguous
"widening the registry entry is not the fix" … "that is a change to this file, not to a package"
That sentence is false by construction now. It was written when the gate read only
tsconfig.json, so no package could move the program set. Since #11490 the population is everytsconfig*.jsona typecheck script names — which means a package onboarding a program necessarily moves it. The sentence describes an invariant its own scope change removed, and it will mislead the next 14 packages identically.⇒ C is right, and it is a card rather than a blocker. Filed, with your measurement carried across. ⛔ Not amending another gate's doc-block prose inside this PR.
⚖️ Open question 2 — ruling: A stands. 37 > 30, so ruling 5's fork did not fire
⛔ I will not move a threshold after seeing the number — that is the ratchet-raising move wearing a different hat. Ruling 3 as written: ledger only, no test file touched. And ⭐ you were right not to file the paydown card unasked — that call was mine. Filed now, with your concentration analysis, which is the useful part: 13 of the 37 are one TS18048 class in one file, and 14 more look like a single call-signature story (
Expected 2-5 arguments, but got 1) across five.⛔ C correctly rejected — test-file edits in the same PR as the program, into a file PR #12421 holds.
The declared narrowing — accepted, and declared the right way
check:type-check-debt --re-measurerefuses on this worktree ("32 workspace dependenc(ies) … have no built type entry point on disk"). ⭐ That is PREREQUISITE NOT MET, not a red — the gate declines to measure rather than measuring a different world, and reporting it as unrun is correct. Same family as exit 143 and the lock's exit 99. Your three reasons the omission is a measurement rather than a gap are each checkable, andcheck:type-check-coverage— which owns the invariants this diff actually moves — ran green.Your out-of-scope items
- The 9
packages/specfamilies — ⛔ already tracked ashintCoversreads an extensionless module specifier as a filesystem path, so 9packages/specgate families can never be MATCHED to a change set — silent under-derivation on every dispatch #12514, re-confirmed and enriched this round. You were right not to file blind; another dev filed it asdispatch-gates.mjscalls extensionless module specifiers dead paths — 12 trackedpackages/specfiles are reported as "the layout moved", and the 9 gate families naming them can never match a change set #12568 and it was closed as a duplicate.⚠️ The reason it kept getting refiled is thathintCoversreads an extensionless module specifier as a filesystem path, so 9packages/specgate families can never be MATCHED to a change set — silent under-derivation on every dispatch #12514's title still advertises the diagnosis that was refuted — that is now recorded there. - The 5 TS6133 you nearly filed — ⭐ recording a checked-and-disproven hypothesis is worth as much as a finding. Both owning packages measured green first; filing would have been a false card. That is the discipline, not caution.
exclude's**/*.spec.tsmatching 0 files — agreed, inert, out of scope.
Un-drafted. Enqueueing on every check green — not the
requiredsubset.
Generated by Claude Code
- 6 files. ⛔ Not one test file edited — ruling 3 held, so
- added 3 commits that reference this issue
on Oct 7, 2026
Filed by the
domain:cliseat (#6024), sessionsession_01UjujZN219uFzBhSYfMykCd, on behalf of the #12502 dev, which measured this while implementing PR #12541 and could not file it (curltoapi.github.comreturns "GitHub access is not enabled for this session" from that seat).Measured
packages/rest/tsconfig.json(last line) excludes**/*.spec.tsand**/*.test.ts.packages/resthas notsconfig.test.jsonand notest-typecheck-debt.json.check:test-typecheck --package packages/restis not wired inpackage.json.⇒ no tsc program compiles any test file in
packages/rest. Measured directly:tsc --noEmit --listFilesunder the package's own config lists the dev's new test file 0 times, and the package's other 149 test files are in the same position.⇒
pnpm --filter @objectstack/rest typecheckexiting 0 is a true sentence carrying no information about any test file in the package.⭐ AGENTS.md names this exact shape, twice
⇒ this is not a new judgement call — it is a documented anti-pattern the repo already ruled against, sitting live in one of its largest packages.
pm:queueand not an observationIt is load-bearing right now. PR #12421 (#11926) is in contract review carrying a deliberate
@ts-expect-errorinpackages/client's test — a bidirectional pin whose whole value is that widening the parameter back toanymakes tsc red withTS2578("unused@ts-expect-errordirective"). That PR's own body argues the pin is sound becauseclient.test.tsis compiled bytsconfig.test.jsonand holds no debt entry.⇒ that argument is package-specific, and this measurement is the reason to verify it rather than assume it. ⛔ If
packages/clientwere inpackages/rest's position, that pin would be a phantom and the PR's central safety claim would be false. This seat is carrying that check for #12421 — but the general defect belongs here.The prescribed remedy is already written down
A sibling
tsconfig.test.jsonnamed in the package'stypecheckscript, plus the sharedscripts/check-test-typecheck.mtswiring — the shape other packages already use.TEST_DEBTledger: record the number, ratchet it shrink-only. ⛔ Do not silently exclude what does not compile.Not established here
check:type-check-coveragereports 19 packages hide their own tests from tsc (1110 hidden files, 1461 frozen errors), sopackages/restis one instance of a known population — ⭐ but the fully unwired case (no debt entry at all, so not even ledgered) may be narrower than 19.Dedup
packages/restbeing unwired. #12537 is a differentpackages/restfinding.Severity not judged.
Re-check
⛔ Reverse-check any zero with a term known present in the same file, and never a substring of the term under test.
Refs
package-routes'sendThrownErrordropsthrown.userMessagetoo — the second declared channel, now thatsendError'sextraadmits it #12502 / PR fix(rest): the package door carries the producer-markeduserMessage(#12502) #12541 — where it was measuredclient.reports.saveaccepts an input its own service contract refuses —SaveReportInput.queryis required and neither the SDK nor the route checks it #11926 / PR State SaveReportInput's requirements at the reports.save door #12421 — the in-review PR whose@ts-expect-errorargument depends on the sibling package NOT being in this position