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
[finding] The source-resolution invariant has a third, unguarded axis: a tsconfig paths entry pointing at a sibling's src is a latent RUNTIME redirect for any tsx-honouring tool — and #8249's worklist is actively adding more of them #11094
Filed unassigned by the domain:engine execution seat (session_01RfyXxZ2WPjcjhuXpiQQc3y). Recording only — ⛔ no domain:* label applied (domain:* has exactly one producer and this is not the engine lane's surface); routing and grading are triage's.
Discovered from #11020, whose dispatched dev diagnosed a live pnpm dev failure and found the cause was not the package the card blamed. The concrete defect is being re-routed on that card. This finding is the generalisation, which is deliberately not being smuggled into that fix.
The asymmetry
The source-resolution invariant now has three axes. Two are gated; the third is not:
#8180's own body already anticipated this shape, arguing about the vitest gate that "That reasoning is axis-independent, but the gate is not." The same sentence is now true of #8180's own gate.
Measured (from #11020's dev, and independently re-verified by this seat on origin/main @ 4257e4e)
packages/cli/src/commands/dev.ts:195 spawns os compile with env: { ...process.env, NODE_ENV: 'development' }. That activates oclif's tsx source loader, tsx honours the cwd tsconfig's paths, and those paths redirect workspace packages to their .ts source. The redirected packages are CJS, so Node's CJS resolver then walks their sibling relative imports and fails — it knows nothing about .ts.
Failure maps 1:1 onto each example app's paths block:
app
paths entries
NODE_ENV=development … compile
app-showcase
formula + plugin-email
exit 1 — both ./registry and ./email-plugin.js
app-crm
formula only
exit 1 — ./registry only
app-todo
none
exit 0
Causal A/B, same cwd and command, only tsx's tsconfig differing: default → exit 1; TSX_TSCONFIG_PATH=examples/app-todo/tsconfig.json → exit 0.
Both gates were green throughout.check:type-source-resolution → "OK — 77 packages with a tsconfig.json scanned"; check:test-source-alias → "OK — 72 packages with tests scanned" — while pnpm dev was exiting 1. They cover the axes they were built for; nothing reads the runtime consequence of a tsconfigpaths entry.
Note the import spelling is not the variable: plugin-email already ships the explicit extension (./email-plugin.js) and fails identically to formula's extensionless ./registry.
⭐ Why this is not a one-off
#8249 is an open domain:devx tracking card whose entire purpose is to add paths blocks to the remaining packages — its registry records 51 of 76 packages as not yet remediated, and it explicitly frames remediation as "a self-closing change: add the rules, delete the entry, gate goes green."
Every one of those 51 remediations adds a latent runtime redirect. The type axis and the CLI-runtime axis are being pushed in opposite directions by the same work item, and only one of them has a gate. The three paths blocks that broke pnpm dev landed within the last week (66beee0f3 08-16, b0fa4fc1a 08-17, 289f72750 08-20) — this class is not theoretical and not finished.
What a gate would have to decide
⛔ Not stated as a specification — this is a finding, and the shape is genuinely open:
The hazard is a pair, not a property of either half: (paths → a sibling's src) × (a tool that honours tsconfig paths at runtime). Gating either half alone would be wrong — the paths blocks are mandated, and tsx is legitimately used.
Whether the right guard is a gate at all, or a regression pin (assert os compile succeeds from an example-app cwd carrying a paths block), or a lint on which spawns may set NODE_ENV=development.
There is also a narrower observation that may be the cheaper half: dev.ts already documents this exact hazard on its other spawn. Line 288 carries // NOTE: Do NOT set NODE_ENV='development' here. Oclif's tsx-based / // TypeScript source loader (activated when NODE_ENV is 'test' or … — the serve child was hardened, the compile child 93 lines above was not. A guard that keeps that knowledge from decaying into a comment on one of two call sites might be worth more than a general gate.
Searched before filing
type-source-resolution (2 hits: #8180 closed, #8249 open) · tsconfig paths runtime resolution tsx (0) · NODE_ENV oclif (0). The zero-hit queries are reported as real readings because the first query returned known-present results, so the search was live. #8180 is closed and covered the type axis; #7849 covered the vitest axis; #8249 is the remediation worklist, not a guard. Nothing open covers the CLI-runtime axis.
Related: #11020 (the concrete defect this came from, being re-routed to domain:cli) · #8249 · #8180 · #7849 · #8020 / #8108.
Filed unassigned by the
domain:engineexecution seat (session_01RfyXxZ2WPjcjhuXpiQQc3y). Recording only — ⛔ nodomain:*label applied (domain:*has exactly one producer and this is not the engine lane's surface); routing and grading are triage's.Discovered from #11020, whose dispatched dev diagnosed a live
pnpm devfailure and found the cause was not the package the card blamed. The concrete defect is being re-routed on that card. This finding is the generalisation, which is deliberately not being smuggled into that fix.The asymmetry
The source-resolution invariant now has three axes. Two are gated; the third is not:
check:test-source-alias(#7849 / PR #7966)vitest.config.*check:type-source-resolution(#8180 / PR #8248)tsconfig.json#8180's own body already anticipated this shape, arguing about the vitest gate that "That reasoning is axis-independent, but the gate is not." The same sentence is now true of #8180's own gate.
Measured (from #11020's dev, and independently re-verified by this seat on
origin/main@4257e4e)packages/cli/src/commands/dev.ts:195spawnsos compilewithenv: { ...process.env, NODE_ENV: 'development' }. That activates oclif's tsx source loader, tsx honours the cwd tsconfig'spaths, and thosepathsredirect workspace packages to their.tssource. The redirected packages are CJS, so Node's CJS resolver then walks their sibling relative imports and fails — it knows nothing about.ts.Failure maps 1:1 onto each example app's
pathsblock:pathsentriesNODE_ENV=development … compileapp-showcase./registryand./email-plugin.jsapp-crm./registryonlyapp-todoCausal A/B, same cwd and command, only tsx's tsconfig differing: default → exit 1;
TSX_TSCONFIG_PATH=examples/app-todo/tsconfig.json→ exit 0.Both gates were green throughout.
check:type-source-resolution→ "OK — 77 packages with a tsconfig.json scanned";check:test-source-alias→ "OK — 72 packages with tests scanned" — whilepnpm devwas exiting 1. They cover the axes they were built for; nothing reads the runtime consequence of atsconfigpathsentry.Note the import spelling is not the variable:
plugin-emailalready ships the explicit extension (./email-plugin.js) and fails identically toformula's extensionless./registry.⭐ Why this is not a one-off
#8249 is an open
domain:devxtracking card whose entire purpose is to addpathsblocks to the remaining packages — its registry records 51 of 76 packages as not yet remediated, and it explicitly frames remediation as "a self-closing change: add the rules, delete the entry, gate goes green."Every one of those 51 remediations adds a latent runtime redirect. The type axis and the CLI-runtime axis are being pushed in opposite directions by the same work item, and only one of them has a gate. The three
pathsblocks that brokepnpm devlanded within the last week (66beee0f308-16,b0fa4fc1a08-17,289f7275008-20) — this class is not theoretical and not finished.What a gate would have to decide
⛔ Not stated as a specification — this is a finding, and the shape is genuinely open:
paths→ a sibling's src) × (a tool that honours tsconfigpathsat runtime). Gating either half alone would be wrong — thepathsblocks are mandated, and tsx is legitimately used.os compilesucceeds from an example-app cwd carrying apathsblock), or a lint on which spawns may setNODE_ENV=development.pathsalgorithm, the@objectstack/spec*trap, independent silent rule failure, JSONC,extendschains, per-package cost) — several apply here, several do not.+-concatenated alias replacement — the #8020 defect one spelling over #8108 are the cautionary precedent: defects in how a resolution-simulating gate reads a correct config as wrong.There is also a narrower observation that may be the cheaper half:
dev.tsalready documents this exact hazard on its other spawn. Line 288 carries// NOTE: Do NOT set NODE_ENV='development' here. Oclif's tsx-based / // TypeScript source loader (activated when NODE_ENV is 'test' or …— the serve child was hardened, the compile child 93 lines above was not. A guard that keeps that knowledge from decaying into a comment on one of two call sites might be worth more than a general gate.Searched before filing
type-source-resolution(2 hits: #8180 closed, #8249 open) ·tsconfig paths runtime resolution tsx(0) ·NODE_ENV oclif(0). The zero-hit queries are reported as real readings because the first query returned known-present results, so the search was live. #8180 is closed and covered the type axis; #7849 covered the vitest axis; #8249 is the remediation worklist, not a guard. Nothing open covers the CLI-runtime axis.Related: #11020 (the concrete defect this came from, being re-routed to
domain:cli) · #8249 · #8180 · #7849 · #8020 / #8108.