Skip to content

finding: nothing stops a packages/cli test from spawning bin/run.js and a ts-path-enabling NODE_ENV at once — the pair silently cancels #11464

Description

@claude

Surfaced while implementing #11317. Filed unassigned rather than folded in: that
card's declared surface is three test files, and the guard below is a new
mechanism, not a truthfulness repair.

The self-cancelling pair

A packages/cli test that spawns bin/run.js is asking for the built CLI. A
test that puts development or test in the child's NODE_ENV is asking
oclif to resolve commands from src/. Doing both is a no-op with a false
comment attached, and nothing in the repo says a word.

@oclif/core@4.13.3 skips its TypeScript path lookup only when isProd(),
which lib/util/util.js:66 defines as a negated membership test — a
leading logical-NOT applied to
['development', 'test'].includes(process.env.NODE_ENV ?? '').

Measured directly against @oclif/core's own Config.load() on
packages/cli, with packages/cli/dist present, reading back the root
plugin's resolved commandsDir:

child NODE_ENVresolved commandsDir
unsetpackages/cli/dist/commands
developmentpackages/cli/src/commands
testpackages/cli/src/commands
productionpackages/cli/dist/commands

Note that vitest exports NODE_ENV=test on its worker, and childEnv()
deliberately does not strip it (its header says why), so a spawner that names
bin/run.js and simply says nothing about NODE_ENV lands in the rerouted leg
by inheritance — the failure does not require anyone to set the variable on
purpose.

Why a guard rather than a sweep

#11317 removed the three files that had the pair. The tree is clean today: the
only remaining bin/run.js spawners are invocation-loudness.e2e.test.ts and
serve-node-env-production-default.e2e.test.ts, and the latter reaches dist/
precisely because it leaves NODE_ENV unset. Nothing keeps the next author
from reintroducing the pair, and the reintroduced version is silent in both
directions: the suite stays green, and the comment claiming the built entry
stays plausible.

This is the same shape as #11341 one property over — a spawned child's env
quietly changing what is under test, where the sweep is easy and the mechanism
is the part that lasts. If both land, they likely want to be one walk over the
same population of spawner files rather than two.

Not obvious which way it should read

The guard's polarity depends on a ruling that is open on #11317: whether these
suites should reach src/ (spawn bin/run-dev.js) or dist/ (spawn
bin/run.js with NODE_ENV unset). Either way the guard is the same
assertion — the two properties must not contradict each other — but which
combination is the recommended one differs, and so does the failure text.

Blocked-by: #11317

Related


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions