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
chore(layering): forbid process globals outside entry points #3753
The rule. A lint or layering rule forbids process.env, process.cwd(), process.on, process.once, process.exit and process.emitWarning in non-test sources outside the declared entry points.
Declare the entry points beside the rule:
the CLI, the daemon executable and the client factory;
the agent-device/host boundary;
scripts;
worker and child-process entry files: the PNG worker, Apple helpers, and the detached runner launcher.
Each worker or child entry receives only explicit, serializable configuration.
No baseline and no allowlist.
Done when
The rule runs in pnpm check.
Adding a process.env read to any non-entry module fails the gate.
#3753 cannot be landed yet: #3775's "Remaining, handed to owners" lists were never owned
I verified origin/main (112d291605) rather than trusting the closure notes. The result is narrower than a blanket gap: the platform and capture packages from #3775's list 2 are genuinely clean and its list 3 is waived by contract, so the residue sits in three places — list 1, list 4, the root half of list 2 — plus one cluster nobody has claimed.
The commandHostEnvironment() half of #3753's premise is accurate as #3798 states it: exactly the two spawn-bound whichCmd lookups (packages/platform-apple/src/core/tool-provider.ts:80, packages/platform-linux/src/tool-provider.ts:91) plus the Linux standalone display fallback (packages/platform-linux/src/tool-provider.ts:139). What is missing is the raw process.env / process.cwd() residue that #3753's rule also forbids.
The residue: 36 reads / 24 files outside the CLI
process.env / process.cwd() in production sources outside src/cli, excluding packages/test-kit (test support) and default-parameter sites. The four clusters below sum to 36: 16 daemon (unowned) + 10 + 3 + 7. Three further reads are released-boundary wrappers under the #3751 waiver, listed separately. packages/session-journal is reached as a live read but is written as a default parameter, so it counts in the waiver too.
No owner anywhere — packages/daemon, 16 reads / 9 files. This is the largest cluster and it is not on any of the four lists above. #3752's "Done when" is only "no process.exit, process.on or process.once under packages/daemon/src", which main satisfies (verified: zero hits); env and cwd reads were never in its scope.
execution/app-events.ts:103,105,106,107 — the four AGENT_DEVICE_*_APP_EVENT_URL_TEMPLATE reads
Note: packages/daemon/src/server/http/daemon-runtime.ts already receives host: HostEnvironment and threads env through ~30 configuration reads (lines 192–478), so the plumbing exists — these 16 sites sit outside it.
List 1 — named for #3772, 10 reads / 8 files (plus the session-journal default parameter below)
Released-boundary wrappers, excluded from the 40 (inside the #3751 waiver until #3759): src/io.ts:13 (createLocalArtifactAdapter) and src/sdk/remote-config.ts:13 (resolveRemoteConfigProfile), both named in #3775's summary. src/mcp/tool-input-config.ts:16 is the third such wrapper but is not named by #3775 — it waits on "the MCP caller must pass env first" in list 4.
#3750's "Done when" also requires the PNG worker to receive explicit serializable configuration rather than the inherited worker env (packages/capture-kit/src/png-worker-client.ts:113). On main the PNG worker reads no process globals and its entry is clean, so the requirement holds in effect — by absorption into the runtime (#3772's refcounted worker), not by the explicit-config handover #3750 describes. Worth one confirming word from its owner rather than a reopen.
What I have not done
No code, no PR, no baseline, no allowlist, per the plan. in-progress is on #3753. I have not touched #3757's branches.
Once 1–4 have owners and land, and 5–6 are decided, #3753 becomes mechanical: one layering rule in the existing scripts/layering/ machinery (next free id R86), the entry-point declarations beside it, the commandHostEnvironment() boundary ban, and the two self-tests.
Coordinator decision on the questions in #3753 and #3747. Thanks for the audit: it changes the plan, not the goal.
Owners.#3753 owns all 36 reads. The rule is "resolve violations at the owning module", and these reads are what keep the gate from landing, so they belong to the gate's issue. No new issues. Land them in one PR, ahead of the rule, with one commit per cluster:
packages/daemon: the 16 reads take the runtime host the daemon already holds (HostEnvironment.env/.cwd). expandSessionPath(..., process.cwd()) takes host.cwd.
replay-port, maestro, ad-script and device-selection: the 10 reads take the host from their callers.
Root src/platform-runtime-*: the 3 reads take the composition host.
src/commands/**: the 7 reads take the command's host.
The gate rule itself goes in the final commit. The PR then lands green with no baseline.
Default parameters (env = process.env).
Internal functions (not reachable from a released exports entry): make the parameter required, and have callers pass the host's env. Examples are resolveSessionIdleExpiryMs, resolveDaemonIdleReapMs and resolveSessionEventLogMaxBytes.
Released public API: keep the signature, which stays additive in 0.21.x. Take the default from host-kit's process capture rather than a raw process.env, so host-kit stays the one module that reads the process. 0.22: remove superseded plugin, client and environment APIs #3759 removes those defaults in 0.22.
There is no exemption list either way. List the public ones you keep in the PR body, so #3759 has its removal list.
"The CLI" as an entry point.src/cli/** is a declared entry point, so its 9 reads may stay. Threading them through a captured host is welcome but not required here.
test-kit.packages/test-kit is test support, so the rule excludes it the same way it excludes *.test.ts. Say so in the rule's own declaration.
commandHostEnvironment(): the boundary list from #3798 holds as you confirmed.
Activity
thymikee commentedon Oct 19, 2026
#3753 cannot be landed yet: #3775's "Remaining, handed to owners" lists were never owned
I verified
origin/main(112d291605) rather than trusting the closure notes. The result is narrower than a blanket gap: the platform and capture packages from #3775's list 2 are genuinely clean and its list 3 is waived by contract, so the residue sits in three places — list 1, list 4, the root half of list 2 — plus one cluster nobody has claimed.Verified against main, by #3775's own list
112d291605session-journalcap,device-selectionenv,replay-port/maestro/ad-scriptreplay env and cwdplatform-runtime-*files are not done, and no package owns them.provider-device-runtimes.ts,provider-credential-fingerprint.ts,cli/connection/provider-policy.ts,plugins/*update-check.ts, replay-test reporters/progress,color.ts,diff.ts,commands/replay/index.ts,doctorCliOutput,plugins.ts,cli-config.tsThe
commandHostEnvironment()half of #3753's premise is accurate as #3798 states it: exactly the two spawn-boundwhichCmdlookups (packages/platform-apple/src/core/tool-provider.ts:80,packages/platform-linux/src/tool-provider.ts:91) plus the Linux standalone display fallback (packages/platform-linux/src/tool-provider.ts:139). What is missing is the rawprocess.env/process.cwd()residue that #3753's rule also forbids.The residue: 36 reads / 24 files outside the CLI
process.env/process.cwd()in production sources outsidesrc/cli, excludingpackages/test-kit(test support) and default-parameter sites. The four clusters below sum to 36: 16 daemon (unowned) + 10 + 3 + 7. Three further reads are released-boundary wrappers under the #3751 waiver, listed separately.packages/session-journalis reached as a live read but is written as a default parameter, so it counts in the waiver too.No owner anywhere —
packages/daemon, 16 reads / 9 files. This is the largest cluster and it is not on any of the four lists above. #3752's "Done when" is only "noprocess.exit,process.onorprocess.onceunderpackages/daemon/src", which main satisfies (verified: zero hits); env and cwd reads were never in its scope.execution/app-events.ts:103,105,106,107— the fourAGENT_DEVICE_*_APP_EVENT_URL_TEMPLATEreadscore/session-script-publication-capability.ts:47,97,135,core/session-script-writer.ts:163,core/session-replay-transaction.ts:65—expandSessionPath(..., process.cwd())execution/screenshot-runtime.ts:326—req.meta?.cwd ?? process.cwd()sessions/handlers/record-runtime.ts:312,321—cwd: process.cwd()andreq.meta?.cwd ?? process.cwd()server/http/http-server.ts:100,101—options.env ?? process.env,options.cwd ?? process.cwd()resources/artifact-tracking.ts:269—{ ...process.env, COPYFILE_DISABLE: '1' }intotarsessions/handlers/session-doctor-output.ts:44—shouldColorStatusMarkers(process.env, …)Note:
packages/daemon/src/server/http/daemon-runtime.tsalready receiveshost: HostEnvironmentand threadsenvthrough ~30 configuration reads (lines 192–478), so the plumbing exists — these 16 sites sit outside it.List 1 — named for #3772, 10 reads / 8 files (plus the session-journal default parameter below)
packages/replay-port/src/session-replay-vars.ts:28,daemon-port/session-replay-runtime-session.ts:183,daemon-port/test-command.ts:409,daemon-port/session-test-shard-devices.ts:97,106packages/maestro/src/internal/engine-flow.ts:104,packages/maestro/src/daemon-port/run-script-http-child.ts:9packages/ad-script/src/internal/vars.ts:113—readReplayShellEnvSourcefalls back toprocess.envpackages/device-selection/src/dispatch-resolve.ts:218,222packages/session-journal/src/session-event-log-window.ts:66— default parameter, reached as a live read fromsession-event-log.ts:90List 2, root half — 3 reads / 3 files
src/platform-runtime-android-adb-host.ts:30—const environment = process.env;at module scopesrc/platform-runtime-app-log-android-transport.ts:31—{ ...process.env, ...adb.options.env }src/platform-runtime-app-log-process.ts:176—{ ...process.env, ...request.env }List 4 — named for #3763, 7 reads / 4 files
src/commands/output/color.ts:4,8—FORCE_COLOR,NO_COLORsrc/commands/output/diff.ts:160—process.cwd()src/commands/replay/index.ts:248,275,348—collectReplayClientShellEnv(process.env)×2,input.cwd ?? process.cwd()src/commands/schema/cli-config.ts:25—options.env ?? process.envReleased-boundary wrappers, excluded from the 40 (inside the #3751 waiver until #3759):
src/io.ts:13(createLocalArtifactAdapter) andsrc/sdk/remote-config.ts:13(resolveRemoteConfigProfile), both named in #3775's summary.src/mcp/tool-input-config.ts:16is the third such wrapper but is not named by #3775 — it waits on "the MCP caller must pass env first" in list 4.One correction to the closure note on #3750
#3750's "Done when" also requires the PNG worker to receive explicit serializable configuration rather than the inherited worker env (
packages/capture-kit/src/png-worker-client.ts:113). On main the PNG worker reads no process globals and its entry is clean, so the requirement holds in effect — by absorption into the runtime (#3772's refcounted worker), not by the explicit-config handover #3750 describes. Worth one confirming word from its owner rather than a reopen.What I have not done
No code, no PR, no baseline, no allowlist, per the plan.
in-progressis on #3753. I have not touched #3757's branches.Needed to proceed
platform-runtime-*files, so no package scope holds them.env = process.envdefault parameters. The 14 sites are waived until 0.22: remove superseded plugin, client and environment APIs #3759 by refactor: read host settings from HostEnvironment in root src and remaining packages #3751, so chore(layering): forbid process globals outside entry points #3753's rule must exempt default parameters until 0.22: remove superseded plugin, client and environment APIs #3759 — or chore(layering): forbid process globals outside entry points #3753 waits for 0.22: remove superseded plugin, client and environment APIs #3759. I cannot read that from the issue text, which says "No baseline and no allowlist." This includespackages/session-journal/src/session-event-log-window.ts:66, which is a default parameter but is reached live fromsession-event-log.ts:90.src/cliholds 9 reads / 5 files:update-check.ts:94,95,96,commands/plugins.ts:29,33,replay-test/progress.ts:242,replay-test/reporters/default.ts:106,110,replay-test/reporters/custom.ts:54. All five are named in refactor(host): read host settings from HostEnvironment in root src and remaining packages #3775's list 4 as feat(host): capture HostEnvironment once at entry points #3763's debt, so the issue and refactor(host): read host settings from HostEnvironment in root src and remaining packages #3775 disagree about whether the CLI entry point may read them.src/cli/connection/provider-policy.tsis defaults only, andsrc/cli/process-exit.tsis the exit wrapper itself.Once 1–4 have owners and land, and 5–6 are decided, #3753 becomes mechanical: one layering rule in the existing
scripts/layering/machinery (next free id R86), the entry-point declarations beside it, thecommandHostEnvironment()boundary ban, and the two self-tests.thymikee commentedon Oct 19, 2026
Coordinator decision on the questions in #3753 and #3747. Thanks for the audit: it changes the plan, not the goal.
Owners. #3753 owns all 36 reads. The rule is "resolve violations at the owning module", and these reads are what keep the gate from landing, so they belong to the gate's issue. No new issues. Land them in one PR, ahead of the rule, with one commit per cluster:
packages/daemon: the 16 reads take the runtime host the daemon already holds (HostEnvironment.env/.cwd).expandSessionPath(..., process.cwd())takeshost.cwd.src/platform-runtime-*: the 3 reads take the composition host.src/commands/**: the 7 reads take the command's host.The gate rule itself goes in the final commit. The PR then lands green with no baseline.
Default parameters (
env = process.env).exportsentry): make the parameter required, and have callers pass the host's env. Examples areresolveSessionIdleExpiryMs,resolveDaemonIdleReapMsandresolveSessionEventLogMaxBytes.process.env, so host-kit stays the one module that reads the process. 0.22: remove superseded plugin, client and environment APIs #3759 removes those defaults in 0.22.There is no exemption list either way. List the public ones you keep in the PR body, so #3759 has its removal list.
"The CLI" as an entry point.
src/cli/**is a declared entry point, so its 9 reads may stay. Threading them through a captured host is welcome but not required here.test-kit.
packages/test-kitis test support, so the rule excludes it the same way it excludes*.test.ts. Say so in the rule's own declaration.commandHostEnvironment(): the boundary list from #3798 holds as you confirmed.chore(gates): forbid process globals outside declared entry points (#…
chore(gates): forbid process globals outside declared entry points (#…
chore(gates): forbid process globals outside declared entry points (#…
refactor(daemon): resolve configuration from the owning runtime host (#…
refactor(replay): thread runtime host through replay and device selec…
refactor(runtime): use the owning host environment at process boundar…
refactor(commands): resolve command inputs and output from the caller…
chore(gates): forbid process globals outside declared entry points (#…