fix(scripts): make check-test-typecheck signatures path-independent - #18173
Merged
claude[bot] merged 2 commits intoSep 14, 2026
Merged
Conversation
A diagnostic signature must change only when the error changes. TS6059 reads `File 'ABS' is not under 'rootDir' 'ABS'`, and an absolute path carries none of the structural punctuation the collapse rule keys on, so its LENGTH was the only thing deciding whether it survived verbatim. A 25-character checkout produced one signature and a 52-character checkout another, for the identical error. Measured on pristine origin/main @ 7e58d17, same command, same commit: `pnpm --filter @objectstack/lint typecheck` exits 0 from /home/user/objectstack-issue-17739 (52 chars to packages/lint/src) and 1 from /tmp/w9 (25 chars), where it reports the same six ledgered TS6059 errors as both ARRIVED and VANISHED at once and tells the reader to go repair errors that are not new. A quoted span that is an absolute filesystem path (POSIX, Windows drive, UNC) now collapses at every length, which is exactly what a long-path checkout already did. Relativising against the repo root was the alternative and is rejected: it re-spells the signature every ledger already holds collapsed, churning a maintainer-gated shrink-only ratchet for discrimination no ledger records. Measured across all 19 test-typecheck-debt.json ledgers before choosing: quoted spans beginning `/` = 0, drive-letter spans = 0, against 89 capital-initial spans as the firing control. Self-test gains a seventh-case battery pinning the property (one signature from two checkout paths), the control that the fixture straddles the 32-character threshold, the zero-churn pin against the ledgered spelling, the Windows spellings, and an over-masking control proving short non-path spans still survive. Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk Co-authored-by: Claude <noreply@anthropic.com>
…pecheck-signature-abs-path
This was referenced Sep 14, 2026
claude
Bot
deleted the
claude/issue-17739-typecheck-signature-abs-path
branch
September 14, 2026 11:34
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 #17739
The defect
normalizeMessageinscripts/check-test-typecheck.mtskeeps a quoted span verbatim when it is short and free of the structural punctuation that makes tsc print a whole shape. A TS6059 message readsFile 'ABS' is not under 'rootDir' 'ABS', and an absolute filesystem path carries none of that punctuation — so its LENGTH was the only thing deciding whether it survived, and the identical diagnostic produced one signature in a short-path checkout and a different one in a long-path checkout.The ledgers were recorded from a long path, and CI's checkout path is long too, which is why this survived: it reds only locally and in short-path agent worktrees.
The two readings — pristine
main, same commit, same commandBoth worktrees at
origin/main7e58d177e, both afterpnpm installand the samepnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' builddependency-closure build. Nothing but the checkout path differs.packages/lint/srcpnpm --filter @objectstack/lint typecheck/home/user/objectstack-issue-17739OK, 2 file(s) / 6 error(s) / 2 pinned signature(s)/tmp/w94 problem(s)The short-path run reports the same six already-ledgered TS6059 errors as both ARRIVED (
... 'rootDir' '/tmp/w9/packages/lint/src'— a signature the ledger does not record) and VANISHED (... 'rootDir' '...'— the recorded one absent), and its ARRIVED text instructs the reader to go repair errors that are not new. The card's headline is therefore confirmed, and confirmed on today'smain, not only on the431c757120it was filed against.It also contradicted the function's own docblock: an absolute checkout path is not "a name, a dotted path, or a small literal type", and it changes when the checkout moves rather than when the error does.
The repair
A quoted span that is a filesystem-absolute path (POSIX, Windows drive letter with either separator, UNC) now collapses at every length — which is exactly what a long-path checkout already did. The change is one added condition plus its detector,
isAbsolutePathSpan, exported so the self-test can prove it still reaches its subject.Rejected alternative: relativising the path against the repo root. It buys a sharper signature, but it re-spells the signature every existing ledger already holds in its collapsed form — churning a maintainer-gated shrink-only ratchet for discrimination no ledger has ever recorded. Measured across all 19
test-typecheck-debt.jsonledgers before choosing: quoted spans beginning with/= 0, drive-letter spans = 0, against 89 capital-initial spans as the firing control.Effect on the existing ledger: none, measured two ways.
pnpm -r run check:test-typecheckover every package that wires this gate — 36 packages, 36OKlines, 0 problems, andgit status --porcelainempty afterwards (no ledger rewritten). Same lint verdict before and after the fix on the long path:2 file(s) / 6 error(s) / 2 pinned signature(s).This does not weaken the gate: it collapses only what a long path already collapsed, so no discrimination any ledger or CI has ever relied on is given up. An over-masking control in the self-test pins that short non-path spans (a relative specifier, a named type) still survive verbatim.
Tests
A sixth self-test battery,
Path-independence: a signature must not move with the CHECKOUT (#17739), floor 7, registered inSELF_TEST_BATTERIESwithSELF_TEST_BATTERY_FLOORraised 5 to 6. It pins the property (one signature from two checkout paths), a control that the fixture really straddles the 32-character threshold the defect lived on, a zero-churn pin against the spelling the ledgers hold today, the Windows spellings, an over-masking control, and a detector-discrimination control.Ablation (one-shot, on the throwaway short-path worktree whose copy was proved byte-identical to this branch's blob
76c65e7e41f1230139d7cbdbdf48ce09a3f0b462before the mutation, and restored to that same hash after):&& !isAbsolutePathSpan(inner)76c65e7etod568fd90#17739failures — the property, the zero-churn pin, the Windows pin;pnpm --filter @objectstack/lint typecheckexit 1git hash-objectback to76c65e7e41f1230139d7cbdbdf48ce09a3f0b462The ablated property failure prints the defect verbatim:
got "TS6059: File '...' is not under 'rootDir' '/tmp/w9/packages/lint/src'. ..." vs "TS6059: File '...' is not under 'rootDir' '...'. ...".Gates
node scripts/pm/dispatch-gates.mjs --commandsderived 27 families for this one-path diff; all 27 run at the final headf7c116ae2, all exit 0, reconciled with--rancarrying exit codes: 27 derived, 27 run, 0 NOT-MEASURED (a DERIVED zero).check:type-check-debtfirst returned exit 3 (PREREQUISITE NOT MET) twice — first for an unbuilt workspace closure, then for an OOM under the caller's own--max-old-space-size=4096, which is tighter than the CI-shaped 6144 ceiling that gate pins. Neither exit 3 is recorded as a measurement; the closure was built (turbo run build, 72/72) and the gate re-run at 8192 to a real exit 0.Also run: the self-test through
tsxdirectly,eslinton the changed file (0 errors / 0 warnings over 1 file, read from--format json;eslint.config.mjsenables no type-aware linting, so a one-file diff inscripts/cannot move any untouched file's verdict), and a manual control-character sweep over the changed file (0 hits) besidecheck:nul-bytes.Changeset
skip-changeset. Measured on the publish surface rather than asserted: all 70 non-private packages declarefiles[], the distinct entries areCHANGELOG.md README.md api-surface dist json-schema liveness llms.txt prompts spec-changes.json src/**/*.zod.ts, 0 of them escape their own package directory, and grepping the 69 builtdisttrees for the changed symbols givesisAbsolutePathSpan0 andSTABLE_QUOTED_SPAN0 against a positive control ofObjectKernel106. The repo-rootscripts/directory publishes nothing.Acceptance notes
noted, not filed:the card's declared scope limit still stands — only TS6059 was measured to carry an absolute path in a quoted span, and no sweep of other diagnostic codes was done. The repair does not need one (it is keyed on the span's shape, not on the TS code), so this is an observation rather than a finding. Carrier: whoever next readsnormalizeMessage.🤖 Generated with Claude Code
https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Generated by Claude Code