test(plugin-security): compile the 89 test files no tsc program read - #13395
Merged
Conversation
The package's `typecheck` script ran two programs, and neither read a single test file: `tsconfig.json` names `**/*.test.ts` in `exclude`, and `tsconfig.scripts.json` selects `scripts/**`. Measured per program with `tsc --noEmit --listFiles`: 460 files / 0 tests, and 310 files / 0 tests. Add the sibling `tsconfig.test.json` (the AGENTS.md "Build & Test" pattern) and name it in `typecheck`. Module semantics match how vitest executes the layer, strictness is inherited untouched. The 9 residual errors are repaired rather than ledgered, so the layer compiles at zero and no `test-typecheck-debt.json` is needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012WkdHQwHr2KQmaX7P1BHzi
…s type-source registry
Both edits are demanded by the gates themselves once the package's test
layer enters a tsc program. check-type-check-coverage names the deletion
("it graduated; delete its entry"); check-type-source-resolution's five
new deps are all annotated `via tsconfig.test.json`, the program this
change onboarded, which is the re-baseline limb its doc-block opens to a
package. Numbers stated in place: 94 to 95 programs, 238 to 243 pairs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012WkdHQwHr2KQmaX7P1BHzi
Each of them reasons from the hole being closed: two say a `@ts-expect-error` here would evaluate never, one explains a normalization by "keeping the test-layer TEST_DEBT flat", and one records writing `__dirname` instead of `import.meta.url` to keep a ratchet from moving. All four are now false or spent; the reasons are corrected rather than deleted, because the cost of a hidden layer is the lesson. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012WkdHQwHr2KQmaX7P1BHzi
Contributor
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
os-elon
marked this pull request as ready for review
August 30, 2026 09:10
os-elon
enabled auto-merge
August 30, 2026 09:10
This was referenced Aug 30, 2026
os-elon
pushed a commit
that referenced
this pull request
Aug 30, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012WkdHQwHr2KQmaX7P1BHzi
This was referenced Aug 30, 2026
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 free
to 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 #13176
@objectstack/plugin-securityadvertised"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json"while no tsc program in the package read a single one of its 89 test files — 1636 tests, in the package whose suites pin refusal behaviour. This adds the siblingtsconfig.test.jsonthe AGENTS.md "Build & Test" section prescribes, names it intypecheck, and repairs the residue rather than ledgering it.The hole, measured per program (not in aggregate)
A single combined zero cannot tell "excluded" from "the grep was wrong", so each program was measured separately with
tsc --noEmit --listFiles, workspace closure built first, onorigin/mainataa16721b6:*.test.tsin ittsconfig.json(build)excludenames**/*.test.tstsconfig.scripts.jsonincludeisscripts/**/*tsconfig.test.json(new)The 89 is the positive control: the same pattern that answers 0 twice answers 89 once, and 89 is also what
findreports on disk.The card's "11 errors across 6 files" — re-measured, and it did not move
Measured at
1a540e82bin the card, re-measured here ataa16721b6: the same 11, in the same 6 files, at the same per-file counts. Under the vitest-matching module semantics this config adopts, 2 of the 11 are the CHECK rather than the code and disappear:TS1470(import.metain a file "which will build into CommonJS output" — a file vitest runs as ESM every day) andTS2550(Array.prototype.atagainst alibolder than es2022, on Node >= 22). That is the same subtractionpackages/specandpackages/restmade.The remaining 9 are all repaired in this PR, so the layer compiles at ZERO and there is no ledger file at all:
explain-engine.test.ts?.oncontributors/rules, which are optional in the pre-parsez.inputlayer shape — an absent member then reaches the matcher asundefinedand the expectation still fails loudlyrls-pushdown-limits.test.tsReturnType(typeof vi.spyOn), which instantiates that generic's own parameters and leavesmock.callsuntyped; named asMockInstance(typeof console.warn)instead of annotating each callbackauthz-matrix-gate.test.tsRLS_DENY_FILTERisRecord(string, unknown), so the sentinel arrived asunknown; converted once at the declarationobjects/default-permission-sets.test.tsstring[]rather than the literal union⛔ Nothing was silenced: no
strictloosened, no blanketany, no@ts-nocheck, no@ts-expect-error, no deleted assertion.pnpm --filter @objectstack/plugin-security test— 89 files, 1636 tests, all passing — after the repairs.Why no
test-typecheck-debt.jsonAt zero residue a bare
tsc --noEmit -p tsconfig.test.jsonis the strictly stronger gate: any error is red immediately, with no ledger to be added to. Wiringscripts/check-test-typecheck.mtswould cost this package atsxdependency and two more scripts to hold an empty ledger. This is the callpackages/metadata-core,packages/metadata-fsandpackages/triggers/trigger-record-changealready made in-tree, and each of their configs states it. The moment this package acquires residue that cannot be fixed in its own PR is the moment to wire the shared script — not before.Reverse verification: the new program is not as blind as the old one
Predicted before running: the new leg goes red, the old one stays green on the same tree. A deliberate
TS2322was appended tosrc/audience-anchors.test.ts— a file that carried none of the 11:Same bytes, same command, two answers — which is the whole point of the card. The restore is proven by state (blob hash back to the
HEADblob, marker count 0, emptygit diff HEAD), not by an exit code.A second, unplanned demonstration arrived for free: a comment edit in this PR put a
*/inside a block comment, andtypecheckreported 8 syntax errors indefault-report-sink.test.ts— a file that, one commit earlier, no program would have read.Two repo-level files, each demanded by its own gate
Declared on the issue before either was edited, with the measurements.
scripts/check-type-check-coverage.mjs— the TEST_DEBT entry is deleted (graduation). The gate names the remedy itself: "has a TEST_DEBT entry but has no test files -- it graduated; delete its entry". The entry stood aterrors: 11, re-measured at exactly 11 before repair, so it was retired against a measurement. The ledger moves in the tightening direction only:-1 package, -89 hidden files (this package's, exactly), -11 frozen errors (this entry's, exactly). ⛔ Neither that script's DEBT nor its TEST_DEBT ledger is raised.
scripts/check-type-source-resolution.mjs— this package's registry entry is re-baselined, +5 deps. All five are annotatedvia tsconfig.test.jsonby the gate's own failure text — the program this change onboarded — which is condition 1 of the limb that gate's doc-block opens to a package ("a package whosetypecheckscript begins naming atsconfig.test.jsonhas moved the set BY DEFINITION").pathsis barred rather than merely unused: the same doc-block records PR #12570 measuring that route onpackages/restas taking its test layer from 37 to 42 errors, the +5 being other packages' source diagnostics billed to a ledger those packages cannot pay down. Condition 2, the numbers, stated in the registry beside the entry and here:+1 program, +0 entries, +5 pairs.
--listbefore was measured in a dedicated comparison worktree atorigin/main, not assumed.Four in-tree notes this change falsifies, corrected
Each reasons from the hole being closed, and each is a receipt for what a hidden layer costs:
default-report-sink.test.tsandpermission-set-projection.test.tsboth said a@ts-expect-errorhere "evaluates never". It would now be live; the reason is corrected rather than the practice (the compile-time half stays withpnpm check:optional-error-sink).explain-engine.test.tsexplained a normalization by "keeping the test-layer TEST_DEBT flat" — that ledger entry is gone.seed-write-refusal.test.tsrecords writing__dirnameinstead ofimport.meta.urlto keep the TEST_DEBT ratchet from going 11 to 12 — a source file shaped around a program whose verdict nothing ever ran. The code is left alone (__dirnameworks under both); the note now says the reason is spent.Verification
Gate family derived at edit time with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, and re-derived after the file set grew (the two scripts pulled in 7 more families plus the gate-script convention pair). All runs below are on873284a46, the final commit, with exit codes captured before any pipe.Green (exit 0):
check:type-check-coverage·check:type-source-resolution·check:cross-package-test-inputs·check:doc-authoring·check:logger-receiver-detach·check:objectql-double-limit·check:override-consistency·check:page-declaration-shape·check:published-files·check:slot-lookup·check:test-source-alias·check:where-matcher·check:engine-double-contract·check:query-options-erasure·check:nul-bytes·check:i18n·check:i18n-stale-fill·check:agent-test-spelling·check:bash32-floor·check:cli-command-ids·check:entry-guard·check:parse-guard·check:pnpm-filter-targets·check:watch-hint-literal·check:pm-dispatch-gates·scripts/pm/bare-root-worklist.mjs --self-test·check-changeset-fixed·check-ci-filter-parity·check-comment-mask-adoption·check-keyed-text-bounds·check-osv-exemptions·check-plugin-teardown-shape·check-shard-attestation·check-undeclared-dep-imports·docs-audit/check-affected-docs·docs-audit/check-drift-comment·--filter @objectstack/spec check:llms-txt. Both edited scripts run their own--self-testfirst inside thepnpm check:*invocation, so both halves are covered by those two zeros.pnpm lint(eslint . --no-inline-config, full repo, not narrowed): exit 0.pnpm --filter @objectstack/plugin-security typecheck(all three programs): exit 0.pnpm --filter @objectstack/plugin-security test: 89 files / 1636 tests, all pass.NOT MEASURED (prerequisite not met — neither a red nor a finding):
check-test-completeness— exit 3, needs a savedturbo run testlog; the script's own text prescribes recording it this way when the family is run locally.check:dual-build-cjs-loads— exit 3, reads built output for 12 packages with nodist/; needs a fullpnpm build.check:type-check-debt— the RATCHET half, deliberately left to CI and declared here rather than skipped silently. It needs the whole workspace built plus a per-entry tsc re-measure, which does not fit one foreground run; the STRUCTURAL half that judges this PR's deletion is green above, and the deletion gives that ratchet strictly less to re-measure.check:i18n's prerequisite WAS cleared (10-package build) rather than declared, and it passes.Out of scope, filed rather than folded
#13394 — two exported declarations in this package are wider than any value they hold (
ExplainDecision.layersas thez.inputshape;RLS_DENY_FILTERasRecord(string, unknown)). Three of the nine errors repaired here are that class landing in the tests. Filed unassigned as an observation; narrowing a shipped declaration is not this card's business.skip-changesetapplies: no published behaviour changes. The onlypackage.jsonedit is thetypecheckscript.Generated by Claude Code