docs: correct VARLOCK_ENV test-detection caveat - #1088
Conversation
The caveat claimed test runners set NODE_ENV=test after varlock has already resolved. Measured across vitest 4.1.5 and jest 30.5.1: in every position where auto-load is normally imported (test config, setup file, app code under test) NODE_ENV=test is already set. Rewrite it around the two cases that do fail, and add the derived-flag recipe for keeping your own env flag name.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
varlock-website | 90538ae | Commit Preview URL Branch Preview URL |
Sep 15 2026, 08:18 PM |
There was a problem hiding this comment.
Important
The revised caveat still classifies Jest globalSetup as a safe setup-file import even though it can run without a test signal.
Reviewed changes Reviewed the updated VARLOCK_ENV guidance and checked its claims against Varlock's builtin detection and injected-graph behavior, plus the documented Jest and Vitest startup contracts.
- Existing environment flags: Documents deriving
APP_ENVfrom$VARLOCK_ENVwhile preserving explicit process overrides. - Test detection timing: Replaces the general warning with specific preload and inherited-
NODE_ENVcases, and explains how an early resolution propagates through__VARLOCK_ENV.
azure/gpt-5.6-sol | 𝕏
Replace the prose caveat with a table of what each runner sets and where. node --test and Mocha set no signal varlock can read, so test is never detected under them. Jest's parent-scoped code (config, globalSetup) has only NODE_ENV, unlike its worker-scoped code.
There was a problem hiding this comment.
ℹ️ No critical issues; two minor runner-matrix corrections are noted inline.
Reviewed changes Reviewed the documentation updates since the prior Pullfrog review, including the revised runner signal matrix and generalized failure caveats.
- Added a runner signal matrix: Distinguished Vitest and Jest parent and worker scopes, plus the signals exposed by Bun, AVA, Node's test runner, and Mocha.
- Clarified detection failures: Applied inherited
NODE_ENVbehavior across signal-only rows and retained the preload and__VARLOCK_ENVpropagation warnings.
azure/gpt-5.6-sol | 𝕏
Vitest workers also get VITEST_POOL_ID. Jest's globalTeardown runs after the runner has assigned JEST_WORKER_ID=1 on the parent, so it does not belong in the parent-scoped row alongside config loading and globalSetup.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes Reviewed the runner-matrix corrections added since the prior Pullfrog review.
- Corrected Vitest worker signals: Added the worker-only
VITEST_POOL_IDsignal while retaining the parent and workerVITESTandNODE_ENV=testsignals. - Narrowed Jest parent scope: Removed
globalTeardownfrom theNODE_ENV=testonly row because in-band execution can retainJEST_WORKER_IDthrough teardown.
azure/gpt-5.6-sol | 𝕏
Deriving a flag from $VARLOCK_ENV requires the enum to cover every value it can produce. remap() passes unmatched values through unchanged, so only the differing ones need listing.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes Reviewed the environment-name remapping guidance added since the prior Pullfrog review.
- Added remapping guidance: Demonstrated translating
VARLOCK_ENV=previewtoAPP_ENV=stagingwhile preserving unmatched values and keeping the resulting enum complete.
azure/gpt-5.6-sol | 𝕏

The
VARLOCK_ENVtest-detection caveat told users that test runners setNODE_ENV=testafter varlock has already loaded and resolved, so detection "may not work in all setups". That is not accurate, and it pushes people toward hand-rolled workarounds (callingexecSyncVarlock+initVarlockEnv()themselves) that they do not need.Both Vitest and Jest set their test signals during their own startup, before config files are loaded. Measured across Vitest 4.1.5 and Jest 30.5.1,
NODE_ENV=testis already set in every position wherevarlock/auto-loadis normally imported: the test config file, setup files, and app code under test. Only anode --import/NODE_OPTIONSpreload resolves before the runner sets anything.The caveat is replaced with a table of what each runner actually sets, and where:
VITESTandNODE_ENV=test(workers also getVITEST_POOL_ID)setupFiles,setupFilesAfterEnv, test files, app code under test)JEST_WORKER_IDandNODE_ENV=testjest.config.*,globalSetup)NODE_ENV=testonlybun test, AVANODE_ENV=testonlynode --test, MochaThree cases where detection does not fire, none of which were documented:
node --testand Mocha set nothing varlock reads, soVARLOCK_ENVnever resolves totestunder them.NODE_ENV. Runners setNODE_ENV=testonly when unset, so a shell or CI value survives and everyNODE_ENVonly row loses its last signal.Whenever varlock resolves in a parent process, its values are injected as
__VARLOCK_ENVand reused by the workers, so a wrong result propagates even to workers that would have detectedtestthemselves. That propagation is now called out, since it fails silently.Also promotes a recipe out of the caution box into the
@currentEnvsection: if you already have your own flag, derive it (APP_ENV=$VARLOCK_ENV) instead of assigningprocess.envat runtime. Explicit overrides still win, and it avoids the ordering trap where a bundled config (vite/vitest) hoistsimport 'varlock/auto-load'above any inlined sibling module that was meant to set the flag first. Where your own environment names do not line up with$VARLOCK_ENV, the section now showsremap()as the translation step, which passes unmatched values through unchanged so only the differing ones need listing.Docs only, no behavior change.