Uh oh!
There was an error while loading. Please reload this page.
test(core): resolve the __ operation-private-key pin's scan surface from git, not a readdirSync denylist (#7706) - #7855
Conversation
… from git, not a readdirSync denylist (#7706) The #7284 one-owner pin was failing only inside merge-queue builds and dequeuing whatever unrelated PR shared the batch. The issue was filed against the reading that a fourth `__` declaration was riding in an open PR; the filer retracted that within the hour (PR #7660 merged unchanged after failing twice), and the full queue log for build 31519403681 shows the real signature: `Error: Test timed out in 5000ms`, with NO offender list — the assertion never ran. `main` is clean of any fourth declaration, re-verified here at b54aaab. Two independent defects shared one fix site. Cost (directly evidenced by the queue log). `sourceFiles()` crawled `packages/` with readdirSync/statSync, then readFileSync + regex on all ~3,600 .ts files, under vitest's DEFAULT 5s timeout — ~355ms on an idle machine, i.e. 13x headroom against a runner concurrently doing a full monorepo build. The two sibling tests in the same file passed at 0ms and 102ms; only the full-tree walk timed out. Nondeterminism (mechanism demonstrated, not proven to have fired). The denylist (node_modules, dist, build, .turbo, coverage, .next) was never exhaustive against what a build deposits. Measured: running `check:skill-examples` extracts 209 prose code blocks verbatim into packages/spec/.examples-build/*.ts — under packages/, matching .ts, in none of the six skipped names — so the old walk sees 3,876 files mid-gate versus 3,667 on a quiet tree. Same commit, different candidate set, decided purely by shard interleaving. None of today's 209 extracts happens to mention a guarded symbol, so this is an open hole rather than a confirmed cause. Both close by resolving the candidate set from git instead of the filesystem. `git ls-files --cached --others --exclude-standard` is the enumerated surface; `git grep --untracked --fixed-strings` is a prefilter over that same surface. The existing JS regex remains the SOLE authority on what counts as a declaration, so no judgement moves into git's regex dialect — a fixed-string search for the two identifiers is a provably exact superset of what that regex can match. Scan drops from ~355ms to ~42ms, and an explicit SCAN_TIMEOUT_MS = 60_000 replaces the implicit 5s. The guarantee did not shrink, which is the only thing that made this worth doing. The surface is tracked files PLUS untracked ones with ignored paths excluded — deliberately not tracked-only. The docblock promises a fourth author goes red "the first time they run the suite", and a file written but not yet `git add`ed is untracked; tracked-only would defer that to commit time, and since PR CI runs only the affected subset, a new consumer outside packages/core would not go red until the merge queue. A planted fourth declaration was verified red before and after, both untracked and tracked, while the same declaration planted in build output is now correctly ignored. Three anti-vacuity guards added or strengthened, because a hardened scan that can no longer fail is worse than the flake it replaced: the prefilter must reach the home file; the prefilter's output must be a subset of the enumerated surface; and this pin — which quotes both spellings in prose and passes both to git grep as string literals — must appear in the prefilter yet never in the offender list, making the "statement-anchored regex does not self-trigger" claim executable rather than a comment. Also fixes a latent order-dependence found while verifying the above: the declaration regex is /g, and sharing one instance let lastIndex carry between files, so a later matchAll on the home file could resume mid-text and report one declaration instead of two. It is now constructed fresh per use, with a regression pin that runs a full scan first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015oe9MbWXhHmppcqxQv9Hh5
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
huangyiirene
commented
Aug 11, 2026
PM review — accepted on the merits; landing blocked by a Dogfood red that is not this PR's, and is a NEW signatureGate-family jobs at head The remaining red, diagnosed rather than re-run
Zero tests failed. Both failed suites ( This PR cannot be the cause. It changes exactly one file — ⇒ Recording The work itselfThe planted-declaration matrix is exactly what the fence asked for, and it settles the thing that mattered: the guarantee did not shrink — untracked and tracked plants both still go red, and the only behaviour removed is the false red on gitignored build output. You rejected my Also right: grading the two mechanisms differently. Cost is CONFIRMED against the 18:05Z queue log; scan-surface nondeterminism is demonstrated but not proven to have fired (3,876 vs 3,667 files, but none of today's 209 The latent Timings: 355ms → 43ms, headroom ~13x → ~1300x. The margin is the point, and you reported it as the point. Your two open questions
Your out-of-scope finding (the pin runs PR-side only where Generated by Claude Code |
huangyiirene
commented
Aug 12, 2026
Green — flipped ready, going to the queue. The Dogfood red was transient, and it is now characterized rather than merely gone.Re-run of the failed jobs on run
Gate-family jobs unchanged and green: ESLint success (20:52:54Z), TypeScript Type Check success (21:04:26Z). The new signature, resolved — for the flake ledgerVerdict: transient. Same commit, same content, red once and green on a single re-run. Distinguishing marks worth keeping, because it reads nothing like the documented Corepack flake:
⇒ Recording it as a second known transient with a distinct signature. ⛔ Note for whoever meets it next: it is not covered by the "a red inside the test step is a different verdict" rule that correctly stopped me from re-queuing on sight — that rule sorts by where the failure happens, and this one happens inside the test step while still being infrastructure. The sharper discriminator is failed-test count: One re-run, receipted beforehand. ⛔ A second red on this signature would have been a shared-infrastructure card, not another re-run. On the work itselfNothing to add to the review above — the planted-declaration matrix, the measured rejection of my Generated by Claude Code |
Fixes#7706
Flake-hardening of one test file:
packages/core/src/security/operation-private-keys.pin.test.ts. The guarded home fileoperation-private-keys.tsand its three importing consumers are untouched.__declaration riding in an open PR — was retracted by its own author 85 minutes later. There is no offending PR. I re-verifiedorigin/main@b54aaabindependently: replicating the pin's own regex over all 3,667 tracked.ts/.tsxfiles underpackages/yields exactly one hit, the home file.The two mechanisms
Cost — directly evidenced.
sourceFiles()crawledpackages/withreaddirSync/statSync, thenreadFileSync+ regex on every file, under vitest's default 5s timeout. The queue log for build 31519403681 showsError: Test timed out in 5000mswith no offender list — the assertion never ran, so the pin reported nothing about the property it guards while taking unrelated PRs out of the batch.Measured breakdown of the old ~355ms scan:
readdirSync/statSyncwalkreadFileSync× 3,667The dominant term is 3,667 sequential file reads — the part that degrades non-linearly on an I/O-saturated runner.
Nondeterminism — mechanism demonstrated, not proven to have fired. The denylist (
node_modules,dist,build,.turbo,coverage,.next) was never exhaustive against what a build deposits.check:skill-examplesextracts prose code blocks verbatim intopackages/spec/.examples-build/*.ts— underpackages/, matching.ts, in none of the six skipped names. Running it:Same commit, different candidate set, decided purely by how the vitest shard interleaves with that gate. Honest limit: none of today's 209 extracts happens to mention a guarded symbol, so this is an open hole rather than a confirmed cause of the two observed dequeues. Planting one reproduces a false red on the old scan.
I also checked the weaker claim:
turbo run buildalone deposits nothing outside the denylist (both surfaces = 3,667). The divergence comes from the lint-gate layer, not the build.The fix
Resolve the candidate set from git instead of the filesystem:
git ls-files --cached --others --exclude-standard— the enumerated surfacegit grep --untracked --fixed-strings— a prefilter over that same surfaceThe existing JS regex remains the sole authority on what counts as a declaration. Fixed-string search for the two identifiers is a provably exact superset of what that regex can match (the regex cannot match text lacking the literal identifier), so no judgement moves into git's regex dialect.
SCAN_TIMEOUT_MS = 60_000replaces the implicit 5s.⛔ The guarantee did not shrink
This was the fence on the card, so it is measured rather than asserted. A fourth declaration was planted and the pin run, before and after:
.examples-build/)The surface is tracked files plus untracked ones, ignored paths excluded — deliberately not
git ls-filestracked-only as the issue proposed. The docblock promises a fourth author goes red "the first time they run the suite", and a file they have written but not yetgit added is untracked. Tracked-only would defer that to commit time — and since PR CI runs only the affected subset, a new consumer outsidepackages/corewould not go red until the merge queue, the most expensive place to find out.--untrackedkeeps the local-loop promise intact and excludes build output, so the caveat is dissolved rather than accepted. This is the same surface conventioncheck-nul-bytesalready uses.Three anti-vacuity guards added or strengthened, because a hardened scan that can no longer fail is worse than the flake it replaced:
git grepas string literals — must appear in the prefilter yet never in the offender list, making the "statement-anchored regex does not self-trigger" claim executable rather than a comment.Verified these fire: narrowing the surface to
packages/coreturns the suite red rather than silently green.Latent bug found and fixed while verifying. The declaration regex is
/g, and sharing one instance letlastIndexcarry between files — a latermatchAllon the home file could resume mid-text and report one declaration instead of two. Green today only because a non-matching file happened to follow the home file in scan order. It is now constructed fresh per use, with a regression pin that runs a full scan first.Timings
The point is the margin, not green/red: the runner that failed was >13x slower than idle while doing a full monorepo build.
Gates
packages/coresuiteeslint(changed file)tsc --noEmit -p packages/corepnpm check:query-options-erasureb54aaab, no files addedpnpm check:type-check-debtpnpm check:init-service-contractpnpm check:kernel-hook-pairspnpm check:nul-bytespnpm check:empty-changesetGate list derived with
node scripts/pm/dispatch-gates.mjs, not by enumerating lint.yml.No changeset — needs the
skip-changesetlabel. This is a test-only change and releases nothing. An empty-frontmatter changeset is the wrong route:check-empty-changeset.mjsrejects newly-added ones (#5471), and the existing empty stock is grandfathered, not precedent. lint.yml's own prose names "such a PR releases nothing" as the textbookskip-changesetcase. I have not applied the label myself — flagging it for the PM along with landing.Out of scope
Whether this pin should also run PR-side for every PR rather than only where
packages/coreis affected. It is a real gap — a cross-package uniqueness scan is precisely the class affected-subset CI structurally cannot see — but it is CI-config work in another lane. Recorded for routing, not folded in here.Generated by Claude Code