gate(tests): reject a test that resolves a path below the process cwd - #9190
Conversation
Root AGENTS.md teaches that a test reading the filesystem in an assertion
roots on its own file and never on `process.cwd()`, and the bullet ends by
declaring that nothing enforces it. objectui#7799 repaired 13 instances of
that class in a single day; it did not close the class.
`scripts/check-test-path-roots.mjs` closes it. The detector is deliberately
not a `process.cwd` grep: one of objectui#7799's own 13 defects was invisible
to that card's census regex, spelled through
`(globalThis as unknown as {…}).process.cwd()`. The scan starts at the
FILESYSTEM CALL, resolved by import provenance rather than by name, and asks
what its path argument is rooted at, following the file's own bindings. A
violation is an ambient root with a segment appended to it — resolving a path
BELOW a cwd that moves. Reading the cwd itself is not, which is the shape
PR objectui#7806 rewrote `browser-process-shim-scope.test.ts` into.
Three live instances are repaired with it, all found by binding resolution and
two of them by nothing else — their read lines carry no `cwd` at all. Each
carried, in its own prose, a premise objectui#7791 and objectui#7800 had
already falsified; those comments are corrected rather than moved.
The gate prints the number of roots it CANNOT classify on every run, so its
silence is never read as a claim about the whole class, and `--blind`
enumerates them. Its pin lives in `scripts/__tests__/`, which runs inside
`Test (shard N/4)` — a required context that subscribes `merge_group` — and
carries the non-vacuity floor, so a walk that collapses fails instead of
reporting clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
`no-irregular-whitespace` is an ESLint error in this repo and both were U+200B, typed to keep a `**/` inside a block comment from closing it. The sentences are reworded instead; a zero-width character in a comment is invisible to every reader and to both spellings of a search for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
…d read `KNOWN_CWD_ROOTED`'s single entry said "tracked for the sweep" without naming where. A registry entry whose repair has no card is how a SHRINK-ONLY list stops shrinking. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
ACCEPT — objectui#8953 / PR #9190Reviewed against the diff at 0. First, the correction — and it is to me, not to the devMy dispatch carried a
This is the third instance of the same error in this shift (a The dev's two other premise corrections both hold and both are recorded here:
1. What I verified in the diff
NOT MEASURED by me: the census figures (1884 calls in 387 of 3037 files; 376 unclassified) and every ablation leg. This seat has a bare checkout with no 2. Why this is accepted rather than sent backThe card asked for a gate on a rule AGENTS.md has taught and nothing has enforced. What could have come back is a Three live defects were repaired, and the one that matters is And the verdict is drawn at the right place: an ambient root with at least one segment appended. 3. One item routed, ⛔ not folded in — and re-measured before routing§6.3 puts a measurement in tension with a sentence on a governed surface. I re-measured the population myself on
So the tension is real and it is 25 files wide, not a corner case: a governed surface declares a spelling fatal while the tree carries 25 live instances of it inside exactly the transform the declaration invokes, and the dev measured a sample of them passing under both cwds. ⛔ The dev correctly did not touch It is now filed bare, as a skills-lane candidate, carrying the predicate and the two numbers and no proposed resolution. Three outcomes are open and I am not choosing between them: the sentence is wrong and should be corrected; the sentence is right about a narrower condition that these 25 do not meet and should say which; or the 25 are a latent hazard and want a gate. Deciding that is not this seat's and not this card's. 4. What I am NOT endorsingThe gate's green is not an endorsement of the class. 376 roots come out unclassified, the largest blind spot is structural (a root arriving as a function parameter or from an import — binding resolution stops at the module edge), and the PR says so in its own body and on every single run. Likewise: the three Proceeding to the merge queue. Generated by Claude Code |
Correction to my own ACCEPT, §3 — the dev's number was right and mine was wrongThe ACCEPT above says 27 call sites, 25 in test files, and calls the tension "25 files wide". That is wrong in two ways and the second one matters more than the first. The numbers. Four of those 27 grep hits are comment prose, not call sites. Separating lines whose code part begins with
21 live call sites in test files. That is exactly the figure PR §6.3 reported. The dev measured it correctly; I "re-measured" it into a larger number and presented the larger number as the independent reading. Also corrected: "spread over 21 files" — 21 is the file count of the raw grep, not of the sites; the live test-file population is 15 files. Why this one is worse than the three in §0. I committed the same error class — a grep's line count read as an entity count — for the fourth time in this shift, inside the comment whose §0 was an apology for the third. And the mechanism is the one this very PR's §2 exists to argue against: a text search does not know what it is looking at. The gate being accepted here starts at the filesystem call precisely because a name match reported 12 violations in a file that had none. I then reviewed it with a grep that counted four comments as code. The correction is one What does not change. Nothing in the ACCEPT's verdict moves: the routed item is still routed, still bare, still ⛔ not folded into this PR, and the diff verifications in §1 are reads of the diff, not of a grep. objectui#9191 has been corrected in place — and the four comment lines turned out to be the most useful evidence in it, since three of them are authors writing the hazard down as their reason for avoiding the form while 21 live sites of it measurably pass. That contradiction is now the card's centre rather than a discarded artifact of a bad filter. This is logged to the errata ledger as its own entry, not as a repeat of the earlier three: the distinguishing feature is that the guard against it was on the page in front of me, in the work I was reviewing. Generated by Claude Code |
Armed — auto-merge
|
| path | expected blob on the merge commit |
|---|---|
scripts/check-test-path-roots.mjs |
ccc430f25421172a949ea282de5aec3b06874318 |
scripts/__tests__/check-test-path-roots.test.ts |
288a73b8ff7831a9f70b013d8d9c50a5f9289e57 |
.changeset/8953-test-path-roots-gate.md |
66840e62ab21a09bf54a5766fb2328d87d8ce22c |
Paired with a COUNT and a STRING, because a hash alone tells you nothing about what it is a hash of: the gate file is 728 added lines, and line 208 reads verbatim
export const FLOORS = Object.freeze({ testFiles: 2000, sinkCalls: 800, selfRooted: 20, filesWithSinks: 100 });
⛔ A floor silently lowered between review and merge is the failure this leg exists for, and it is the one a blob-only check would still pass while I wasn't looking at what changed.
L3 — the leg that checks what must STAY, which is the load-bearing one. The ACCEPT's central structural claim is no new required check context. Measured across the diff, both sides:
reading of .github/workflows/lint.yml |
base 87f174c00a |
head 429c9c7b39 |
must be |
|---|---|---|---|
jobs:-level keys |
5 | 5 | ⛔ EQUAL — a new key is a new context |
- name: steps |
17 | 18 | +1 exactly |
Across <merge>^ <merge> the same two readings must give EQUAL and +1. The +1 is ambiguous on its own — another PR could land a lint step in the same window — so it is disambiguated by a STRING leg: the merge commit must contain the step name Verify no test resolves a repository path from the process cwd, and <merge>^ must not. That is a control with a known direction in both halves.
L4 — the shrink-only ratchet did not widen. KNOWN_CWD_ROOTED = 1 entry and SUBJECT_IS_THE_CWD = 1 entry on the merge commit, and scripts/__tests__/check-test-path-roots.test.ts still contains the literal toBeLessThanOrEqual(1). Two registries, so each is the other's control: a leg that only counted one could not tell "shrink-only held" from "I read the wrong array".
L5 — the three repairs stayed repaired, with a control that HITS. ⛔ An absence claim is worth nothing without a positive in the same run. Per file, counting code lines only — lines whose code part begins with *, // or /* are excluded, which is the filter whose absence produced the correction two comments up:
| file | cwd() in code |
import.meta.url in code (the control) |
|---|---|---|
examples/schema-catalog/test/catalog-gallery-render.test.tsx |
0 | 1 — :174 |
examples/schema-catalog/test/plugin-dashboard-gallery-render.test.tsx |
0 | 1 — :94 |
packages/plugin-grid/src/__tests__/gridArrayArmOrderby-8973.test.tsx |
0 | 1 — :95 |
All three control lines are the same prescribed spelling, and the probe asserts the text, not just the count:
const REPO_ROOT = decodeURIComponent(new URL(import.meta.url).pathname)
⛔ Floors are per file, not a total — a summed 0 across three files cannot distinguish three repairs from two repairs and one file the grep stopped matching.
What the probe does NOT claim. It does not re-run the gate, the pin, or any ablation leg — I cannot, and CI's Lint success on this head is the reading that covers those. It says nothing about the 376 unclassified roots. And a green probe is a statement about what landed, ⛔ never an endorsement of the class the gate covers.
A leg that comes back amber will be published with its cause. ⛔ It will not be re-run into green.
Generated by Claude Code
|
`ci-cd-pipeline-doc.test.ts` pins the `## Lint (lint.yml)` section against the job's actual `run:` steps, and this branch added a step without adding its bullet: `1 failed | 79 passed`, naming `scripts/check-test-path-roots.mjs`. The bullet goes between `check-vi-mock-override-shape.mjs` and `check-cross-repo-closer-outcome.mjs`, where lint.yml runs it, and carries what the neighbouring bullets carry: what the gate judges, the measurement that made it necessary (objectui#7791's `7 passed` vs `2 failed / 5 passed` on one file with the cwd as the only variable; objectui#7799's thirteen repairs in a day that did not close the class), and why it resolves sinks by import provenance rather than by name. The second bullet states the gate's declared blind spot -- the census line's `N root(s) NOT CLASSIFIED`, 376 today -- because a page that describes a gate as covering a class it only partly sees reproduces the defect this card is about. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Repair pushed —
|
| check | reading |
|---|---|
report sha = pushed sha = ls-remote |
✅ e629fc825b… |
| ⛔ no rebase, no amend, no force-push | git merge-base --is-ancestor 429c9c7b39 <head> → true. The reviewed commit is still an ancestor; the repair sits on top of it |
| scope | 1 file, +29 / −0 — content/docs/guide/ci-cd-pipeline.md only |
| placement | one hunk at the file's line ~318, inside ## Lint (\lint.yml`), between the check-vi-mock-override-shape.mjsandcheck-cross-repo-closer-outcome.mjsbullets — the orderlint.yml` runs them |
| ⛔ nothing the ACCEPT reviewed moved | all three blobs byte-identical to the reviewed head: ccc430f254… · 288a73b8ff… · 66840e62ab… |
| collision risk with PR objectui#9141 | that PR's hunks in this file are near lines 218 / 476 / 525; this one is at 318. No overlap |
| auto-merge | still armed from 16:10:10Z — a push does not disarm it |
⭐ And it did not settle for satisfying the pin. The second bullet it added says the quiet part out loud:
⚠️ Read that gate's green for what it is: it declares its own blind spot on every run. … A clean run is a verdict on the roots this gate can classify, never a clean bill of health for the class — which is the same over-reading the card itself is about.
That sentence is worth more to the next contributor than the gate is.
⚠️ Probe amendment, stated rather than silently patched
The probe posted before the merge expects L1 = exactly 8 paths. The repair adds a ninth, content/docs/guide/ci-cd-pipeline.md. ⇒ L1's expected set is now 9, listed here so the change is on the record before the merge rather than discovered inside a LANDED note:
.changeset/8953-test-path-roots-gate.md
.github/workflows/lint.yml
content/docs/guide/ci-cd-pipeline.md ← added by the repair
examples/schema-catalog/test/catalog-gallery-render.test.tsx
examples/schema-catalog/test/plugin-dashboard-gallery-render.test.tsx
package.json
packages/plugin-grid/src/__tests__/gridArrayArmOrderby-8973.test.tsx
scripts/__tests__/check-test-path-roots.test.ts
scripts/check-test-path-roots.mjs
⛔ L2, L3, L4 and L5 are unchanged and their expected values still stand — the blob equalities above are the reason I can say that rather than assume it.
⭐ The answer to the question I asked, and it is worse than I guessed
I asked the repair round to measure which tracked tests actually read .github/workflows/lint.yml, rather than which ones sound like they might. It did it properly — an fs-surface probe preloaded into every Vitest worker via NODE_OPTIONS=--import, attributing each read to the first repository frame, with subprocesses inherited so an out-of-process gate script is caught too, over all 79 tracked test files mentioning .github or workflows (0 hits outside that candidate set).
| instrument | readers found | error |
|---|---|---|
| the original round's recall (§5) | 6 | all six are genuine readers — ⛔ recall was not wrong, it was 6 of 35 |
a text search for the string lint.yml |
21 hits | 8 false positives, and — the bigger half — 22 misses |
| the runtime probe | 35 | the real set |
The 22 that never spell lint.yml reach it three ways: enumerating .github/workflows/; a shared helper (scripts/__tests__/workflow-checks.ts) reading on the test's behalf; or the gate script the test itself runs walking the tree.
⭐ The second and third of those are the same structural blind spot this very PR's gate declares for path roots — resolution stopping at the module edge. The card is about a detector that cannot see past a helper, and the process that verified the detector failed in exactly that way, at exactly that boundary. I do not think that is a coincidence; I think it is what the class actually is.
Filed bare as objectui#9198, with both derivation sketches (the runtime probe, and a static extension of this gate's own resolver across the module edge) ⛔ deliberately not built here. Grading is triage's.
Generated by Claude Code
LANDED —
|
| path | expected | on the merge commit |
|---|---|---|
scripts/check-test-path-roots.mjs |
ccc430f254… |
✅ ccc430f25421172a949ea282de5aec3b06874318 |
scripts/__tests__/check-test-path-roots.test.ts |
288a73b8ff… |
✅ 288a73b8ff7831a9f70b013d8d9c50a5f9289e57 |
.changeset/8953-test-path-roots-gate.md |
66840e62ab… |
✅ 66840e62ab21a09bf54a5766fb2328d87d8ce22c |
Paired count: the gate file is 728 lines. Paired string, read on the merge commit:
export const FLOORS = Object.freeze({ testFiles: 2000, sinkCalls: 800, selfRooted: 20, filesWithSinks: 100 });
⛔ No floor was lowered between review and merge. That is what the string half of this leg exists for; a hash alone would have told you nothing about what it was a hash of.
L3 — the leg that carried the ACCEPT's central claim ✅, positive in both halves
reading of .github/workflows/lint.yml |
<merge>^ |
<merge> |
required |
|---|---|---|---|
jobs:-level keys |
5 | 5 | ⛔ EQUAL — ✅ |
- name: steps |
17 | 18 | +1 exactly — ✅ |
the step-name string Verify no test resolves a repository path from the process cwd |
0 | 1 | the disambiguator — ✅ |
⇒ no new check context was created. The step landed inside the already-required Lint, and the queue's required set did not move.
L4 — the shrink-only ratchet ✅
KNOWN_CWD_ROOTED = 1, SUBJECT_IS_THE_CWD = 1, and the pin still carries toBeLessThanOrEqual(1). Two registries, each the other's control.
L5 — the three repairs, per file, each with a control that HITS ✅
| file | cwd() in code |
import.meta.url in code |
the prescribed string |
|---|---|---|---|
examples/schema-catalog/test/catalog-gallery-render.test.tsx |
0 | 1 | 1 |
examples/schema-catalog/test/plugin-dashboard-gallery-render.test.tsx |
0 | 1 | 1 |
packages/plugin-grid/src/__tests__/gridArrayArmOrderby-8973.test.tsx |
0 | 1 | 1 |
Code lines only — comment lines excluded before counting, which is the filter whose absence cost this seat a correction earlier on this very PR. ⛔ Per file, never summed: a summed 0 cannot tell three repairs from two repairs and one file the matcher stopped matching. The positive column is the prescribed spelling itself, asserted as text:
const REPO_ROOT = decodeURIComponent(new URL(import.meta.url).pathname)
⛔ What this green does NOT say
It says what landed. ⛔ It does not re-run the gate, the pin, the four ablation legs or the census — bare checkout, no node_modules, and this seat writes no leg it cannot execute. CI is the reading that covers those, including the shard that went red and then green.
And ⛔ it is not an endorsement of the class. The gate reports 376 roots it cannot classify on every run and says so in its own summary line. Its largest blind spot is structural — a root arriving as a function parameter or from an import, because resolution stops at the module edge — and one of the three files repaired here handed process.cwd() straight to a helper that did the reads and was found by a human reading the file, ⛔ not by the gate.
⭐ That blind spot then reached out and bit the round that built it: the CI failure on this PR was a documentation pin that reads lint.yml through a helper, invisible to a list assembled from names. The measurement it prompted — 35 tracked test files read lint.yml, against 6 recalled and 21 text-search hits of which 8 are false — is filed as objectui#9198. The card's thesis and the round's own failure are the same shape, one level apart.
Cost of the round, stated
One red shard, one repair commit, one merge. The red was mine to have caught: my ACCEPT measured the workflow edit's shape (jobs: keys, step count) and never asked what the step runs. ⇒ for any workflow edit, read what the step runs, not only where the step sits.
Closing objectui#8953.
Generated by Claude Code
Fixes #8953
Root
AGENTS.mdteaches that a test reading the filesystem in an assertion roots on its own file and ⛔ never onprocess.cwd(), and the bullet ends by declaring that nothing enforces it. objectui#7799 repaired 13 instances of that class in a single day; it did not close the class. This is the gate, inscripts/, per the triage ruling — ⛔ noeslint-rules/rule, ⛔ noAGENTS.mdedit.1. The population, derived — ⛔
process.cwdis one spelling, not the classThe crude grep the card and the claim both carry (
packages/*/srctest files mentioningcwd()) re-measures at 26 on87f174c00, with controls (a nonsense token → 0,describe(→ 2704). ⛔ That number is a floor, not a population, in both directions:cwd()in prose;readFileSync('e2e/live/.auth/state.json')), which is 100% cwd-rooted and carries nocwdtoken at all;packages/*/src, and the class is repo-wide.The honest population is every filesystem call made by a test, classified by what its path argument RESOLVES from. Measured on the merged tree: 1884 filesystem calls in 387 of 3037 test files — 957 self-rooted, 518 absolute, 26 module-resolved, 7 ambient, and 376 whose root this gate cannot classify.
The census line ends with
376 root(s) NOT CLASSIFIED, and--blindenumerates them. That is deliberate: a gate that prints only what it checked reads as a claim about everything.examples/schema-catalog/test/catalog-gallery-render.test.tsxhandedprocess.cwd()straight toderiveRegistryKeys(), which does the reads. Found by reading the file while repairing its neighbours, ⛔ not by the gate. Repaired here and labelled in place as the shape the gate misses.PWD/INIT_CWD;fs[name](p)), or one made by a helper module;spawnSynccwd option, a glob library,import());TEST_FILE, and anything git does not track.2. The detector's shape — ⛔ why it is not a one-spelling grep
packages/cli/src/__tests__/check-jsonc-parse.test.tsdeclares its ownfunction writeFile(name, body)writing into amkdtempdir; a name match reports 12 violations there and every one is wrong. Measured: name-matching gave 28 violations, provenance-matching gives 8.examples/schema-cataloginstances areconst siteDir = path.join(process.cwd(), …)read three screens later throughpath.join(siteDir, f)— the read line holds nocwdat all. This gate found them; a text search does not.globalThis, via anas unknown ascast, via an alias — because the AST's call target is read, not the source text.process.env.PWD,resolve()with a relative argument, and a bare relative string are the same root under other names.The verdict, stated exactly: an ambient root with at least one segment appended — resolving a path below a cwd that moves.
existsSync(cwd)is not a violation;existsSync(join(cwd, 'pnpm-workspace.yaml'))is. That is the shape PR objectui#7806 rewrotebrowser-process-shim-scope.test.tsinto, and the rule gets it right with no entry in any list. ⛔ The appended segments are not required to look like a repository path — requiringpackages/-ish literals would be the grep thinking back in through the window.3. The two-sided ablation, with the mutation proved on disk
Every leg: mutate → prove it landed (anchor counts + blob hash, an empty hash coded to read as FAILURE) → measure → restore under
trap … EXIT INT TERMwithgit checkout HEAD -- ABSOLUTE_PATH→ prove restore by blob equality + emptygit diff HEAD.FIRES on the real historical defect.
gridArrayArmOrderby-8973.test.tsxput back tojoin(process.cwd(), …): anchorsself-rooted 1→0, cwd-rooted 0→1, blob8e129178…→4013af0c…. Gate exit 1, naming the site; the pin exit 1,2 failed | 21 passed.⭐ FIRES on the spelling a grep loses — the same file mutated to
join((globalThis as unknown as { process: { cwd(): string } }).process.cwd(), …), blob8e129178…→61c5b6c7…. On that one line:process.cwdgreprooted at (globalThis as unknown as { process: { cwd(): string } }).process.cwd()SILENT on the measured-immune files.⚠️ The card and the claim both say six; PR objectui#7806's own per-file table names four (⚠️ And the reason is not one reason: the three
cli/app-generator,plugin-view×3) — see §6. None is an unregistered violation.plugin-viewprobes are silent because the gate cannot classify them (their root comes out ofArray#find), which is pinned as such so silence is never read as a clean verdict.The non-vacuity floor.
TEST_FILEmutated to a valid regex matching nothing (blobe6170de0…→d2f92c7b…): gate exit 1 —the population COLLAPSED — this run measured nothing, all four floors named, plus both registry entries reported stale. The pin: 6 failed | 17 passed. ⛔ A broken glob cannot read as clean. (A first attempt mutated the regex into aSyntaxError; that is NOT MEASURED, not a red gate, and the leg was redone.)4. Three live defects repaired, and the prose that caused them
The gate was red on arrival with 8 reads in 3 files. All three are repaired under the bounded in-place exemption (same defect class; shape pinned by PR objectui#7796 / objectui#7806; no other claim on the files; same gate family, no new verification surface):
examples/schema-catalog/test/catalog-gallery-render.test.tsxderiveRegistryKeys(process.cwd())the gate cannot seeexamples/schema-catalog/test/plugin-dashboard-gallery-render.test.tsxpackages/plugin-grid/src/__tests__/gridArrayArmOrderby-8973.test.tsx⭐ Each carried, in its own comment, a premise that had already been falsified when it was written. Two repeat "
process.cwd()is the repo root by construction:scripts/vitest-invocation-guard.mjsrefuses any run whose Vitest root is not it" — the guard checks vitest's root, which--root ../..sets correctly while leaving the cwd in the package. The third says "this project'simport.meta.urlis not a file URL" — falsified by six readings on objectui#7800. Those comments are corrected, not moved: the defect was reasoned from written prose, so leaving the prose reproduces it.Green under both invocations after the repair — the package-directory column is the acceptance reading:
5. Gate verdicts by context name
Test (shard N/4)pnpm test --shard=N/4— the new pin, run alone:scripts/__tests__/check-test-path-roots.test.tsTest (shard N/4)vitest run examples/schema-catalog/ packages/plugin-grid/Test (shard N/4)lint.ymledit could move (lint-workflow,dependabot-merge-gate,merge-queue-reporting,entry-guard-wiring,workflow-cache-save-bound,check-lint-rule-coverage)Lintnode scripts/check-test-path-roots.mjs(the step added here)Lintnode scripts/check-entry-guard.mjsLintpnpm lint:root— the whole 336-file population, not a narrowingType Checkpnpm type-check:scripts--listFilesshows both new files in the program, so the green covers themChangeset Declarationnode scripts/check-changeset-presence.mjsControl Byte Scannode scripts/check-control-bytes.mjsThe gate list was checked against
REQUIRED_CONTEXTSinscripts/dependabot-merge-gate.mjs(26 names, derived and tested), ⛔ not against any hand-counted phrase. No new check context is created: theLintstep lands inside an existing required context that already subscribesmerge_group, so the queue's required set does not move.6. What the card, the triage and the claim got wrong
eslint-rules/rule files 21, "more than doubled" from the card's 10. Both numbers are of the same tree:eslint-rules/holds 21.jsfiles = 10 rule modules + 10 co-located.test.js+index.js. The rule count is 10, exactly the card's figure, andindex.jsregisters exactly those ten. Theprocess.cwdreading of 0 re-runs as 0 with theexportcontrol returning 1.19 − 13 = 6is arithmetic on a total (19) that no measurement produced; it entered via a PM comment on objectui#7800 and was inherited by this card.new URL(REL, import.meta.url)does NOT throw here. The card statesfileURLToPaththrowsERR_INVALID_URL_SCHEMEon it under both cwds. Re-measured: the tree carries 21 live call sites of that form and they pass —packages/types+packages/app-shellfrom the repo root (2 files / 21 tests) andpackages/app-shellfrom its own directory via--root ../..(2 files / 13 tests). ⇒ the gate treats it as self-rooted and does not flag it; reddening 21 green files over a hazard that did not reproduce would be a false-positive engine. ⛔ The gate still prescribes only the bare-import.meta.urlspelling AGENTS.md names, so no third spelling is introduced — but the boundary of the claim is now measured rather than inherited. Whether the hazard still holds somewhere is a separate question this card does not answer.gridArrayArmOrderby-8973.test.tsxlanded after objectui#7799 swept the class, carrying both falsified premises as its stated justification. That is this card's argument with a file attached.7. Filed, ⛔ not folded in
e2e/live/inline-edit-polish-2572.spec.ts:34. Playwright, not Vitest: it runs only inlive-e2e.ymlagainst a real backend, so the repair cannot be verified from here. Registered inKNOWN_CWD_ROOTED, which is ⛔ SHRINK-ONLY and pinned at ≤ 1.packages/cli/src/__tests__/app-generator.test.ts:1161is registered inSUBJECT_IS_THE_CWDwith its reason: the cwd mirrors on both sides of the assertion deliberately, and re-rooting one side desyncs the mirror and reddens a passing test. Its real cost is already filed as objectui#7807. That list is ⛔ not a debt list, and a stale entry in either list fails the run.维护者速读(草稿)
改了什么 — 新增
scripts/check-test-path-roots.mjs(门)与scripts/__tests__/check-test-path-roots.test.ts(钉),把 AGENTS.md 已经写下、但至今无人执行的一条规则变成机械门:测试在断言里读文件系统,根不得落在process.cwd()之下。顺带修掉树上仅存的 3 个该类缺陷,并改正它们注释里那三句早已被证伪的前提。为什么改 — objectui#7799 一天里修掉 13 个实例,但没关掉这一类。规则写在文档里、运行时不兑现,正是本仓反复付账的形态;而
gridArrayArmOrderby-8973.test.tsx是那次普查之后新写的同类缺陷,证明这一类确实还在长。风险与代价(含回滚) — 门只读、不改任何东西;两张登记表各自带理由,写错会被自己的 stale 检查判红。⚠️ 它有已声明的盲区(376 个无法归类的根,每次运行都打印出来),⛔ 所以它的绿不是对整类的背书。回滚成本低:删掉两个新文件、
package.json一行与lint.yml一步即可;三个测试文件的重新定根是独立的、两种 cwd 下都已实测转绿的改动,可单独保留。席位意见 — (留空,交维护者)
你要做的 — 确认两件事:①
KNOWN_CWD_ROOTED里那条 e2e 条目是否接受先登记后扫(它需要一次 live-e2e 运行才能验证修法);② 本 PR §6 第 3 条的读数与 AGENTS.md 里关于new URL(rel, import.meta.url)的警告存在张力 —— ⛔ 本卡按裁决未动 AGENTS.md,那句话是否需要一张治理面的卡,归你定。🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code