Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/verify/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"build": "tsup --config ../../tsup.config.ts && node ../../scripts/check-dts-emitted.mjs",
"dev": "tsc -w",
"test": "vitest run",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit && pnpm check:test-typecheck",
"check:test-typecheck": "tsx ../../scripts/check-test-typecheck.mts --self-test && tsx ../../scripts/check-test-typecheck.mts --package packages/verify --project tsconfig.test.json"
},
"dependencies": {
"@objectstack/core": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/verify/src/harness.host-resolution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { bootStack } from './harness';
import { bootStack } from './harness.js';

/**
* Stand-in for `@objectstack/organizations`. Mirrors the real plugin's
Expand Down
2 changes: 1 addition & 1 deletion packages/verify/src/harness.posture-only.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// claim untestable: green whether or not the option actually stands alone.

import { describe, it, expect, afterEach } from 'vitest';
import { bootStack } from './harness';
import { bootStack } from './harness.js';

const app = {
manifest: {
Expand Down
2 changes: 1 addition & 1 deletion packages/verify/src/harness.posture.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// integration test.

import { describe, it, expect, vi, afterEach } from 'vitest';
import { bootStack } from './harness';
import { bootStack } from './harness.js';

class FakeOrganizationsPlugin {
readonly name = 'fake-organizations';
Expand Down
73 changes: 73 additions & 0 deletions packages/verify/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// The TEST-layer type-check program (#15145), adopting the #5286 mechanism that
// #5449 generalised and that 30 sibling configs now use. `tsconfig.json` beside
// this file is unchanged: it is the BUILD config, its `exclude` of
// `**/*.test.ts` stays, and this sibling puts the excluded layer in front of
// tsc. `package.json`'s `typecheck` NAMES this file (via
// `check:test-typecheck --project`), because a config no script invokes is
// exactly the phantom this whole mechanism is about.
//
// BEFORE THIS FILE, NO tsc PROGRAM COMPILED A SINGLE TEST FILE HERE, and that
// is measured rather than read off the config. On the tree at 1d67130585 with
// the workspace closure built first, `tsc --noEmit --listFiles -p tsconfig.json`
// puts 0 of this package's 10 `src/*.test.ts` files in the program while
// holding all 9 of its non-test `src/**` files -- so the zero is the `exclude`
// line and not a probe that sees nothing. The same probe against THIS config,
// on that same tree, names 10 of 10 (981 program files -> 990).
//
// ⚠️ ROUTE (b) WAS AVAILABLE HERE AND WAS NOT TAKEN, which is worth recording
// because `check-type-check-coverage.mjs` tells the next reader to assume it is
// not: dropping `**/*.test.ts` from `tsconfig.json`'s `exclude` leaves
// `pnpm check:type-source-resolution` GREEN for this package (measured under a
// trap-restored mutation at 1d67130585 -- exit 0, 124 programs across 78
// packages), unlike the 14 of 18 entries that gate reports as red. It was
// declined on module semantics, not on availability: vitest executes these
// files through vite, which resolves extension-less relative specifiers, while
// `tsconfig.json` inherits `NodeNext` from the repo root and demands `.js` on
// every one of them. Route (b) would hold the test layer to a resolver that
// never runs it -- the same config-tier noise the shared gate's header
// attributes 108 of spec's 842 raw errors to. Matching vitest is fidelity, not
// laxity, and it is why 28 of the 30 sibling configs override these two keys.
//
// ⛔ STRICTNESS IS UNTOUCHED. `strict`, `noUnusedLocals`, `noUnusedParameters`,
// `noImplicitReturns`, `esModuleInterop` and the rest are inherited from
// `tsconfig.json` (and through it from the repo root). `rootDir: ".."` and the
// `@objectstack/core` source `paths` rule are inherited too, deliberately: this
// program resolves core's types from source for the same #15229 reason the
// build config does. Nothing here may loosen a type rule; if a test does not
// compile, that is the finding.
//
// MEASURED on the tree at 1d67130585, workspace closure built first (an error
// count taken against an unbuilt closure is not a reading -- unresolved-import
// cascades inflate it): this program reports 0 errors over 10 test files, so
// there is no `test-typecheck-debt.json` beside it and every error any of those
// 10 files ever gains is red on arrival. 14 of the 30 sibling configs are in
// that same state. This layer also holds ZERO `@ts-expect-error` directives
// (grepped with a positive control -- the same grep hits `packages/spec/src`),
// so no pin was silently dead here; what the gap cost was the other half.
//
// ⛔ AND THEREFORE NO `gen:test-typecheck-debt` SCRIPT EITHER. That pairing is
// 1:1 across all 30 sibling configs -- 16 with a ledger declare the generator,
// 14 without one declare nothing -- and it is enforced rather than stylistic:
// `check:merge-driver` requires every manifest generator to carry a merge
// disposition in `scripts/regen-artifacts.mjs`, and #14062's note on that file
// refuses to invent one for a ledger that does not exist. Measured here: adding
// the script alone reds that gate ("generator(s) with NO recorded merge
// disposition: gen:test-typecheck-debt [@objectstack/verify]"). If this layer
// ever measures non-zero, the ledger, the generator and its regen-artifacts row
// arrive together, in that one PR. The `@objectstack/verify` TEST_DEBT entry in
// `scripts/check-type-check-coverage.mjs` graduates in this same PR: it recorded
// TS2835 x3 for three extension-less `./harness` imports, and with those three
// given their `.js` the gate re-measured the entry at 0 ("TEST_DEBT records 3,
// and tsc now reports 0 -- graduation candidate").
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["ES2022"],
"types": ["node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
63 changes: 53 additions & 10 deletions scripts/check-type-check-coverage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,59 @@ const EXEMPT = {
// So the shrink-only guarantee did not loosen here; it moved to a strictly
// sharper instrument, one that also reddens on a wholesale substitution of
// error IDENTITY at a constant total, which a per-package integer cannot see.
//
// ── #15145: `@objectstack/verify` GRADUATED — and this one WAS paid down ───
//
// `verify` (3) left this ledger on 2026-09-04. ⛔ Do NOT read it through the
// blocks above it: those all warn that a deleted entry left its population
// undiminished, and this one is the other case (`plugin-security` is the
// nearest precedent, and even it ledgered nothing only because its 9 residual
// errors were repaired by hand). Here the entry recorded
// TS2835 x3 for three extension-less `./harness` imports, all three were given
// their `.js`, and the re-measure then read the entry at ZERO before anything
// was wired -- this gate's own line, quoted: "@objectstack/verify: TEST_DEBT
// records 3, and tsc now reports 0 -- graduation candidate". So there is no
// `packages/verify/test-typecheck-debt.json`: at zero residue a bare
// `tsc --noEmit -p tsconfig.test.json` is the stronger gate, the call
// `plugin-security`, `metadata-core`, `metadata-fs` and `trigger-record-change`
// made before it, and every error any of the package's 10 test files ever gains
// is red on arrival with no ledger to be added to.
//
// ⚠️ MEASURED IN THAT ORDER ON PURPOSE, because the entry's own note predicted
// the outcome ("the same one-line fix graduates this entry") and a prediction is
// not a licence. Collapsing a TS2835 cascade EXPOSES errors as well as removing
// them -- the #8612 lesson this ledger carries twice above -- and the card that
// filed this had itself watched the entry drift 3 -> 5 (+2 TS7006) the moment
// two `.then` callbacks were added behind that same unresolved specifier. The
// honest move on an upward count is to RE-TALLY, not to delete. Here it went
// down, and only the measurement could say which.
//
// THE WIRING IS THE OTHER HALF, and it is what the entry's disappearance now
// rests on: `packages/verify/tsconfig.test.json` compiles `src/**/*` with
// vitest-matching module semantics and `package.json`'s `typecheck` NAMES it,
// so `hidesTests` is false. Measured with the closure built: the build config
// puts 0 of the package's 10 `src/*.test.ts` in its program (and all 9 non-test
// `src/**` files); the test config puts 10 of 10. The package's own advertised
// `typecheck` was green over a layer it had never read -- which is the defect
// the card was about, and which deleting a ledger entry alone would have left
// exactly where it was.
//
// ⚠️ The pin half reported nothing here in either direction and still does not:
// this package's test layer holds ZERO `@ts-expect-error` directives (grepped
// with a positive control -- the same grep hits `packages/spec/src`), so
// PINS_CHECKED had no subject. The card's sharpest line -- that a
// `ts-expect-error` in those files is a phantom check -- is a statement about
// what WOULD happen, not about an existing dead pin, exactly as `cli`'s
// graduation recorded for its own 115 files.
//
// ⚠️ ROUTE (b) WAS AVAILABLE HERE and was still not taken. The #11491 note
// above names `verify` as one of the 4 entries whose exclusion could be dropped
// with `check:type-source-resolution` staying green, and that split was
// re-measured on 2026-09-04 under a trap-restored mutation and still holds for
// this package (exit 0; 124 programs across 78 packages). It was declined on
// module semantics: `tsconfig.json` inherits NodeNext from the repo root and
// would hold the test layer to a resolver vitest never runs it under. Onboard
// by WIRING, not by widening the build config.
const TEST_DEBT = {
// ── #14710: `@objectstack/cli` GRADUATED, and it was not paid down ─────────
//
Expand Down Expand Up @@ -1154,16 +1207,6 @@ const TEST_DEBT = {
+ 'module-level `VARS` and a partial context cannot satisfy it; the same file already carries a '
+ 'hand-widened copy of that helper (`filterOf`) written for exactly that reason.',
},
'@objectstack/verify': {
errors: 3,
note: 'TS2835 x3 -- `harness.host-resolution`, `harness.posture-only` and `harness.posture` each '
+ 'import `./harness` without the `.js` extension. Re-tallied from the 8 measured at 5ab08428 '
+ '(TS2835 x4, TS7006 x4) when `derive.test.ts` gained its own extension: that ONE unresolved '
+ 'import was carrying 1 x TS2835 plus every TS7006 in the file, because a specifier that does '
+ 'not resolve under NodeNext makes every symbol it names `any` and so every callback parameter '
+ 'implicitly any. The remainder is the same NodeNext pair from the top-of-ledger note, and the '
+ 'same one-line fix graduates this entry.',
},
'@objectstack/connector-mcp': { errors: 5, note: 'TS2339 x5. Re-measured 5 at 5ab08428, exact.' },
'@objectstack/connector-openapi': { errors: 5, note: 'TS2339 x5. Re-measured 5 at 5ab08428, exact.' },
'@objectstack/http-conformance': {
Expand Down
Loading