You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(cli): architecture rules select nothing on Windows — every path helper returns backslashes #863
cli/tests/architecture/helpers.ts builds every path it hands to a rule with relative(CLI_ROOT, full), which is platform-native. On Windows that yields src\contexts\tools\..., while the rules themselves match on forward slashes — startsWith("src/contexts/"), includes("/profiles/"), and similar.
Nothing throws. The rules simply select nothing, and 21 architecture tests fail on
Windows with messages that read like the repository drifted rather than like the
harness did: "the rule selects no file — its scope is stale", "no profile
directory found", "pattern matches nothing under src/".
The more serious half is not the noise, it is the silence on the other side: any
architecture rule a contributor adds is unverifiable on Windows, and a Windows
contributor cannot tell a real violation from a harness that selected zero files.
Affected file(s)
cli/tests/architecture/helpers.ts — sourceFiles(), and every helper returning a relative() path
15 of the 25 files under cli/tests/architecture/, as consumers
Expected behaviour
The architecture project passes on Windows, or names a real violation when one exists.
Observed behaviour
On next at 126e88da, a pristine checkout, no local modification:
normalize is imported in the file but is not applied at this point.
Additional context
Found while implementing #862, and it is the same defect class: a path or shell
assumption that holds on Linux and macOS, breaks on Windows, and reports nothing. #862 is one hook that never runs; this is 21 rules that never select. Both are
invisible to a Linux-only CI, which is what #707 exists to close.
Deliberately not fixed inside the #862 branch: normalising the separators would make
21 rules run on Windows for the first time, and some may then surface genuine
findings. That is a discovery task with an unbounded cost, and how this harness should
treat Windows is a maintainer decision.
Current next still returns relative(CLI_ROOT, full) directly from sourceFiles(). On Windows that is a backslash-separated path, while the architecture rules intentionally match /. The reported zero-selection failures therefore remain reproducible in the harness.
The possibility that normalisation reveals real architecture violations is not a reason to close the defect. It is the expected consequence of making the rules run on Windows for the first time.
Reopen this as a Bug and keep the fix narrow:
normalise every repository-relative path emitted by the architecture helpers to /;
add a regression test proving Windows-style paths are accepted by the helper and its consumers;
run the architecture project on Windows;
if the now-active rules reveal real violations, fix them or create separately scoped issues. Do not preserve a broken selector to keep the current baseline green.
Out of scope: changing the architecture rules’ intended scope or weakening their zero-selection assertions.
I can reproduce this on native Windows and report the current next
baseline, including which failures come from path formatting versus
actual rule violations. I'll keep the selectors, baselines, and
zero-selection assertions intact. I see the contribution guide asks for
maintainer validation before implementation; please confirm this issue
is ready for a focused helper-normalisation PR. I'll start with the
runtime test evidence in the meantime.
Native Windows reproduction on current next revision 83b0246e532d3f76491baabbc8b0b266f00e3590 confirms the path issue. I have not changed the source or debt baselines.
Environment: Windows 11 build 26200, Node v22.23.1, Vitest 3.2.6, Biome 2.5.11. I used pnpm 11.19.0 with the existing frozen lockfile and lifecycle scripts disabled; the package declares pnpm 12.3.4.
There are also two collection errors: docs-do-not-lie raises “no command found”; mutation-covers-source raises “Invalid or unexpected token”. I have not root-caused the latter, or attributed every failed assertion to path separators.
The actual sourceFiles() helper returns 381 paths, all containing backslashes. Comparing its output with a read-only slash-normalized view:
Prefix
Actual helper matches
Slash-normalized view
src/contexts/
0
272
src/contexts/tools/
0
60
src/presentation/commands/
0
15
src/kernel/
0
27
All eight pluginReadmes() paths also contain backslashes. The actual declaredCommands() raises the stale-scope error despite those 15 command files being present. This diagnostic can be reproduced from cli/ with Node 22:
A narrow fix needs more than sourceFiles(). Source tracing identifies these boundaries:
helpers.ts: normalize sourceFiles(), pluginReadmes(), and resolveImportTarget() outputs consistently. Otherwise relative imports will fail the canonical-key lookup after sourceFiles() changes.
catches-that-swallow.arch.test.ts: its independent testFiles() output is compared with slash-based BASELINE and SELF keys.
ports-are-called.arch.test.ts: its independent portFiles() output must match the source-body map. A partial fix would make bodies.get(port) ?? "" silently miss Windows ports. This is a static trace of the partial-fix risk, not a claim that I ran a patched suite.
Normalize paths used for diagnostics in comments and no-shared-binary too; keep filesystem roots/operations native.
There is also an independent Windows launcher failure. The direct spawnSync(node_modules/.bin/biome, ...) used by biome-guards-bite returns error.code = "ENOENT" and status = null in this environment. A control invocation of the installed package through Node returns status 0 and Version: 2.5.11:
biomeFindings() ignores result.error, so a failed launch becomes an empty diagnostic list. Its three planted guard assertions fail in the baseline. A supported cross-platform launcher plus an explicit spawn-error check is needed before judging those rules; the version control above does not establish that their lint behavior passes.
For regression coverage, I would assert canonical real helper outputs, an exact relative-import edge, and that every discovered port has a source-body entry, then run the whole architecture project on Windows with existing scopes and zero-selection assertions intact. The current hand-authored slash-path probes miss the producer boundary. Any remaining debt should be assessed separately after the harness works.
I organized the focused code relationships and findings with Brain Scanner; the behavior evidence above comes from native Node probes and Vitest. Implementation remains pending the maintainer validation requested above.
@kevin-lozada-santos Validated — go ahead, and thank you for the native Windows baseline. That is the evidence this issue was missing, and it corrects me on the size of the fix.
I had labelled this good first issue on the strength of helpers.ts:19 alone. Your trace shows that was wrong, and I am removing the label: the producer boundary is wider than sourceFiles(), and a partial fix fails silently, which is the worst outcome available here. Your reading of the boundaries matches ours — normalize sourceFiles(), pluginReadmes() and resolveImportTarget() together, or the canonical-key lookup for relative imports breaks after the first change.
On scope, three rulings so you do not have to guess:
In scope for your PR. Path normalization in tests/architecture/helpers.ts, plus the independent producers in catches-that-swallow.arch.test.ts and ports-are-called.arch.test.ts, plus the diagnostic paths in comments and no-shared-binary. Your instinct is right: normalize what is compared, keep filesystem roots and operations native. A rule matching on / against a string built by join() is the bug; changing how we touch the disk is not the fix.
In scope, and please keep it. The zero-selection assertions. A rule that selects nothing must fail, on every platform — that is the guard this whole issue exists for, and it is the one thing that would have caught this on day one.
Separate issue, please, not this PR. The Biome launcher. spawnSync on node_modules/.bin/biome returning ENOENT while node node_modules/@biomejs/biome/bin/biome returns 0 is a real defect and your control invocation proves it, but it is a different failure — a launcher that cannot start, not a path that cannot match. That biomeFindings() swallows result.error and reports an empty finding list is the more serious half of it: a guard that reports nothing looks identical to a guard that passed. Open it with the evidence you already have and I will treat it as a bug.
Out of scope entirely. The two collection errors (docs-do-not-lie, mutation-covers-source) and any debt baseline. Leave both untouched, exactly as you proposed.
Two notes on your environment, neither blocking: you ran pnpm 11.19.0 against a 12.3.4 declaration with --ignore-scripts, so if a result surprises you, re-check it under the declared version before concluding. And --experimental-strip-types on a .ts import is fine for a probe, but the regression tests should go through the normal vitest path.
For the regression coverage, your three assertions are the right ones. Add the platform dimension explicitly rather than relying on the runner's separator: a test that builds a backslash path and asserts the rule still selects it fails on Linux CI today and would have made this issue unnecessary.
The scoped Windows path fix is ready in #880, targeting next:
#880
sourceFiles, pluginReadmes, and import targets now use the same
comparison paths. The separate producers and diagnostics are covered,
with filesystem operations and zero-selection assertions preserved.
Eight ordinary Vitest regressions include explicit backslash inputs;
the focused affected suites pass 37 tests.
The full native Windows comparison improves from 79 passed/21 failed
plus two collection errors to 117 passed/5 failed plus one collection
error, with no newly failing assertions. The existing Biome, workflow,
codebase-map, and mutation failures are documented rather than hidden
or folded into this patch.
I filed the separate Biome launcher report with the
ENOENT/Node-entrypoint control here:
#881
Kevin
On Thu, 17 Sep 2026 11:26:42 -0700, Baptiste LAFOURCADE ***@***.***> wrote:
blafourcade left a comment (ai-driven-dev/framework#863)
@kevin-lozada-santos Validated — go ahead, and thank you for the native Windows baseline. That is the evidence this issue was missing, and it corrects me on the size of the fix.
I had labelled this `good first issue` on the strength of `helpers.ts:19` alone. Your trace shows that was wrong, and I am removing the label: the producer boundary is wider than `sourceFiles()`, and a partial fix fails silently, which is the worst outcome available here. Your reading of the boundaries matches ours — normalize `sourceFiles()`, `pluginReadmes()` and `resolveImportTarget()` together, or the canonical-key lookup for relative imports breaks after the first change.
On scope, three rulings so you do not have to guess:
**In scope for your PR.** Path normalization in `tests/architecture/helpers.ts`, plus the independent producers in `catches-that-swallow.arch.test.ts` and `ports-are-called.arch.test.ts`, plus the diagnostic paths in `comments` and `no-shared-binary`. Your instinct is right: normalize what is *compared*, keep filesystem roots and operations native. A rule matching on `/` against a string built by `join()` is the bug; changing how we touch the disk is not the fix.
**In scope, and please keep it.** The zero-selection assertions. A rule that selects nothing must fail, on every platform — that is the guard this whole issue exists for, and it is the one thing that would have caught this on day one.
**Separate issue, please, not this PR.** The Biome launcher. `spawnSync` on `node_modules/.bin/biome` returning `ENOENT` while `node ***@***.***/biome/bin/biome` returns 0 is a real defect and your control invocation proves it, but it is a different failure — a launcher that cannot start, not a path that cannot match. That `biomeFindings()` swallows `result.error` and reports an empty finding list is the more serious half of it: a guard that reports nothing looks identical to a guard that passed. Open it with the evidence you already have and I will treat it as a bug.
**Out of scope entirely.** The two collection errors (`docs-do-not-lie`, `mutation-covers-source`) and any debt baseline. Leave both untouched, exactly as you proposed.
Two notes on your environment, neither blocking: you ran pnpm `11.19.0` against a `12.3.4` declaration with `--ignore-scripts`, so if a result surprises you, re-check it under the declared version before concluding. And `--experimental-strip-types` on a `.ts` import is fine for a probe, but the regression tests should go through the normal vitest path.
For the regression coverage, your three assertions are the right ones. Add the platform dimension explicitly rather than relying on the runner's separator: a test that builds a backslash path and asserts the rule still selects it fails on Linux CI today and would have made this issue unnecessary.
---
_Generated by [Claude Code](https://claude.ai/code)_
--
Reply to this email directly or view it on GitHub:
#863 (comment)
You are receiving this because you were mentioned.
Message ID: ***@***.***>
The path producers and import lookup now use canonical / comparison keys while filesystem operations remain native. Explicit backslash regressions pass, the Windows CI job is green, and the current architecture suite passes 139/139 locally.
#881 separately owns the Windows Biome launcher failure. No remaining #863 scope is open.
Activity
blafourcade commentedon Sep 14, 2026
This was closed without a patch.
Current
nextstill returnsrelative(CLI_ROOT, full)directly fromsourceFiles(). On Windows that is a backslash-separated path, while the architecture rules intentionally match/. The reported zero-selection failures therefore remain reproducible in the harness.The possibility that normalisation reveals real architecture violations is not a reason to close the defect. It is the expected consequence of making the rules run on Windows for the first time.
Reopen this as a Bug and keep the fix narrow:
/;Out of scope: changing the architecture rules’ intended scope or weakening their zero-selection assertions.
kevin-lozada-santos commentedon Sep 16, 2026
I can reproduce this on native Windows and report the current
nextbaseline, including which failures come from path formatting versus
actual rule violations. I'll keep the selectors, baselines, and
zero-selection assertions intact. I see the contribution guide asks for
maintainer validation before implementation; please confirm this issue
is ready for a focused helper-normalisation PR. I'll start with the
runtime test evidence in the meantime.
kevin-lozada-santos commentedon Sep 16, 2026
Native Windows reproduction on current
nextrevision83b0246e532d3f76491baabbc8b0b266f00e3590confirms the path issue. I have not changed the source or debt baselines.Environment: Windows 11 build 26200, Node
v22.23.1, Vitest3.2.6, Biome2.5.11. I used pnpm11.19.0with the existing frozen lockfile and lifecycle scripts disabled; the package declares pnpm12.3.4.From
cli/:There are also two collection errors:
docs-do-not-lieraises “no command found”;mutation-covers-sourceraises “Invalid or unexpected token”. I have not root-caused the latter, or attributed every failed assertion to path separators.The actual
sourceFiles()helper returns 381 paths, all containing backslashes. Comparing its output with a read-only slash-normalized view:src/contexts/src/contexts/tools/src/presentation/commands/src/kernel/All eight
pluginReadmes()paths also contain backslashes. The actualdeclaredCommands()raises the stale-scope error despite those 15 command files being present. This diagnostic can be reproduced fromcli/with Node 22:A narrow fix needs more than
sourceFiles(). Source tracing identifies these boundaries:helpers.ts: normalizesourceFiles(),pluginReadmes(), andresolveImportTarget()outputs consistently. Otherwise relative imports will fail the canonical-key lookup aftersourceFiles()changes.catches-that-swallow.arch.test.ts: its independenttestFiles()output is compared with slash-basedBASELINEandSELFkeys.ports-are-called.arch.test.ts: its independentportFiles()output must match the source-body map. A partial fix would makebodies.get(port) ?? ""silently miss Windows ports. This is a static trace of the partial-fix risk, not a claim that I ran a patched suite.commentsandno-shared-binarytoo; keep filesystem roots/operations native.There is also an independent Windows launcher failure. The direct
spawnSync(node_modules/.bin/biome, ...)used bybiome-guards-bitereturnserror.code = "ENOENT"andstatus = nullin this environment. A control invocation of the installed package through Node returns status 0 andVersion: 2.5.11:biomeFindings()ignoresresult.error, so a failed launch becomes an empty diagnostic list. Its three planted guard assertions fail in the baseline. A supported cross-platform launcher plus an explicit spawn-error check is needed before judging those rules; the version control above does not establish that their lint behavior passes.For regression coverage, I would assert canonical real helper outputs, an exact relative-import edge, and that every discovered port has a source-body entry, then run the whole architecture project on Windows with existing scopes and zero-selection assertions intact. The current hand-authored slash-path probes miss the producer boundary. Any remaining debt should be assessed separately after the harness works.
I organized the focused code relationships and findings with Brain Scanner; the behavior evidence above comes from native Node probes and Vitest. Implementation remains pending the maintainer validation requested above.
blafourcade commentedon Sep 17, 2026
@kevin-lozada-santos Validated — go ahead, and thank you for the native Windows baseline. That is the evidence this issue was missing, and it corrects me on the size of the fix.
I had labelled this
good first issueon the strength ofhelpers.ts:19alone. Your trace shows that was wrong, and I am removing the label: the producer boundary is wider thansourceFiles(), and a partial fix fails silently, which is the worst outcome available here. Your reading of the boundaries matches ours — normalizesourceFiles(),pluginReadmes()andresolveImportTarget()together, or the canonical-key lookup for relative imports breaks after the first change.On scope, three rulings so you do not have to guess:
In scope for your PR. Path normalization in
tests/architecture/helpers.ts, plus the independent producers incatches-that-swallow.arch.test.tsandports-are-called.arch.test.ts, plus the diagnostic paths incommentsandno-shared-binary. Your instinct is right: normalize what is compared, keep filesystem roots and operations native. A rule matching on/against a string built byjoin()is the bug; changing how we touch the disk is not the fix.In scope, and please keep it. The zero-selection assertions. A rule that selects nothing must fail, on every platform — that is the guard this whole issue exists for, and it is the one thing that would have caught this on day one.
Separate issue, please, not this PR. The Biome launcher.
spawnSynconnode_modules/.bin/biomereturningENOENTwhilenode node_modules/@biomejs/biome/bin/biomereturns 0 is a real defect and your control invocation proves it, but it is a different failure — a launcher that cannot start, not a path that cannot match. ThatbiomeFindings()swallowsresult.errorand reports an empty finding list is the more serious half of it: a guard that reports nothing looks identical to a guard that passed. Open it with the evidence you already have and I will treat it as a bug.Out of scope entirely. The two collection errors (
docs-do-not-lie,mutation-covers-source) and any debt baseline. Leave both untouched, exactly as you proposed.Two notes on your environment, neither blocking: you ran pnpm
11.19.0against a12.3.4declaration with--ignore-scripts, so if a result surprises you, re-check it under the declared version before concluding. And--experimental-strip-typeson a.tsimport is fine for a probe, but the regression tests should go through the normal vitest path.For the regression coverage, your three assertions are the right ones. Add the platform dimension explicitly rather than relying on the runner's separator: a test that builds a backslash path and asserts the rule still selects it fails on Linux CI today and would have made this issue unnecessary.
Generated by Claude Code
kevin-lozada-santos commentedon Sep 17, 2026
blafourcade commentedon Sep 24, 2026
Resolved by #880, merged into
nextas16c7c650.The path producers and import lookup now use canonical
/comparison keys while filesystem operations remain native. Explicit backslash regressions pass, the Windows CI job is green, and the current architecture suite passes 139/139 locally.#881 separately owns the Windows Biome launcher failure. No remaining #863 scope is open.