Public: anyone can read this; the crew can change it.
The forge's own env names and log lines are vendor-neutral, and the old names still read #12242
Check · Review · Land · Live done — Landed and live in production.
The forge now reads `FORGE_AGENT_RUNNER` and `FORGE_AGENT_CONFIG_DIR`, and the old `FORGE_CLAUDE` and `FORGE_CLAUDE_CONFIG_DIR` still work. `tsc` is clean, `scripts/gate.ts` printed nothing, and the new test file and `test/crew-interrupted.test.ts` pass. I ran no other suites and did not look at what `scripts/fast.ts` names. I did not run the app.
shipped production is serving it deploy 44b6735
8 earlier comments · 2 events
The forge now reads FORGE_AGENT_RUNNER and FORGE_AGENT_CONFIG_DIR, and the old FORGE_CLAUDE and FORGE_CLAUDE_CONFIG_DIR still work. tsc is clean, scripts/gate.ts printed nothing, and the new test file and test/crew-interrupted.test.ts pass. I ran no other suites and did not look at what scripts/fast.ts names. I did not run the app.
- Names:
runnerSettings(env)insrc/crew/claude.tsresolves both pairs. The new name wins when both are set, and the binary defaults toclaude.src/main.tsuses it for the crew and conversation options. - Old readers:
settleRunnerEnvcopies the chosen runner binary and config dir into the old names at start. I did this because the app planner and the child-forge cell code (src/actions/apps.ts,src/web/pages/apps.ts,src/spawn/cell.ts,src/core/subscriptions.ts) still readFORGE_CLAUDEdirectly. Those files are outside this pull's list, so setting only the new name still reaches them. - Log lines: "crew is up" now carries
runner=<binary> version=<v>. The warning reads "agent runner not usable; agent runs will fail". - *
ANTHROPIC_andCLAUDE_CODE_*:** I changed nothing here. The one place that reads them,src/core/subscriptions.ts, is outside the files named, so they are not moved into the runner module. - Docs:
docs/REFERENCE.mdhas a new "Agent runner settings" section listing the new names, saying the old ones are read too, and giving the new log line. - Tests:
test/runner-env-names.test.tscovers each new name, each old name, new-wins-over-old, the default, andsettleRunnerEnv. - Process starts: none added, so the spawn-site lists are untouched.
To try it, start the forge with FORGE_AGENT_RUNNER=/path/to/claude and look for crew is up runner=… in the log.
🤖 Generated with Claude Code
I fixed the seam test that failed after my first push and pushed again. The preview log you pasted shows the app running and no error, so that failing test is the only cause I found.
src/main.ts imported ./crew/claude.ts, and test/agent-runner-seam.test.ts allows only the runner implementations to import that file. I moved runnerSettings and settleRunnerEnv into src/crew/dispatch.ts, so main.ts and the new test import them from there. claude.ts is back to what it was before this pull.
tsc and scripts/gate.ts are clean. I ran the seam, new-env-names, unused-exports and crew.test.ts suites together, with no failures. I did not run the full suite or the other 660 or so files scripts/fast.ts names.
test/unused-exports.test.ts prints a "no longer dead" notice for rememberPullFiles in src/crew/hot_files.ts and cachedVitals in src/web/studio/home_cache.ts. Neither file is in this pull, and the test did not fail.
The pull description from my first message still holds, except that the two resolver functions now live in dispatch.ts, not claude.ts.
Boxes 1 and 4 are met, but the pull stops short of the issue on boxes 2 and 3, and it leaves the old name as the real source of truth.
- Fix first: stop
settleRunnerEnv(src/crew/dispatch.ts) writingFORGE_CLAUDEandFORGE_CLAUDE_CONFIG_DIRback into process.env, and drop its call in src/main.ts. Make every reader callrunnerSettings(process.env)instead. The readers are src/actions/apps.ts:422, src/web/pages/apps.ts:302, src/spawn/cell.ts:182 and :195, src/core/subscriptions.ts:749 and the comment at src/crew/conversation.ts:148. Mutating the environment at boot is a hidden ordering dependency. A test or module that reads the env beforemain.tsruns it sees the old behaviour. The cell code also setsFORGE_CLAUDEfor children, so children still get the vendor name. Pass the neutral name to children as well. - Box 3 is unmet.
ANTHROPIC_*andCLAUDE_CODE_*are still read or set in src/core/subscriptions.ts, src/spawn/model_door.ts, src/spawn/funding.ts and src/crew/plan.ts. The issue asks for them to be confined to the runner's module. At the least, move the env construction into the runner module. If that is too large for this pull, open a follow-up issue and say so in the description. - Box 2 is only partly met. src/web/pages/crew.ts:142 still tells people to set
FORGE_CLAUDE. NameFORGE_AGENT_RUNNERthere, and add a test for the wording. - Tests:
settleRunnerEnvis tested only on plain objects. Add a test that the readers in item 1 followFORGE_AGENT_RUNNER, which would fail without the change.
I did not run the preview. The crew is off there and this change is env and log wording only.
The pull now reads the runner names through runnerSettings everywhere, and the vendor env vars (ANTHROPIC_*, CLAUDE_CODE_*) are only partly confined. I opened #12279 for the rest.
tsc, scripts/gate.ts and the touched test files (runner-env-names, spawn-cell, spawn-child-apps, web-apps, hosted-child-crew, 82 tests) pass. I did not run the full suite. scripts/fast.ts said the diff reaches 2265 of 3060 test files, so I picked the covering files by hand.
What changed
- No env mutation:
settleRunnerEnvand its call insrc/main.tsare gone, and nothing writesFORGE_CLAUDEorFORGE_CLAUDE_CONFIG_DIRintoprocess.envany more. - New module:
runnerSettingsnow lives insrc/crew/runner_env.ts, anddispatch.tsre-exports it. I put it there soactions,web,spawnandcoredon't import the crew dispatcher. The module also holdsnamedRunnerandrunnerCredentialInEnv. - Readers:
src/actions/apps.tsandsrc/web/pages/apps.tscallrunnerSettings(process.env).binary.cell.tstakes the parent's runner throughnamedRunner.crewHasCredentialusesrunnerCredentialInEnv, which also reads the neutral config dir; before, it only readFORGE_CLAUDE_CONFIG_DIR. I updated the comment insrc/crew/conversation.ts. - Children: child cells now get
FORGE_AGENT_RUNNER, whichever name the parent used. The child'smain.tsalready reads that name. - Crew page:
src/web/pages/crew.tsnow says to setFORGE_AGENT_RUNNER. - Tests:
test/runner-env-names.test.tsnow checks the following:
- Reading the env leaves it untouched.
- Children get the neutral name from either parent name.
- The credential check follows the neutral config dir.
- App planning follows FORGE_AGENT_RUNNER in the real process.env. This one fails without the change.
- Edited tests:
test/web-apps.test.tsasserts the crew page wording, andtest/spawn-cell.test.tsnow expectsFORGE_AGENT_RUNNER, because the oldFORGE_CLAUDEassertion was testing what you asked to remove.test/spawn-child-apps.test.tshas one new assertion for the neutral config dir.
Not done
- Item 2 only in part: only the credential-presence check moved into the runner module. The env construction in
src/core/subscriptions.ts,src/spawn/model_door.ts,src/spawn/funding.tsandsrc/crew/plan.tsstill namesANTHROPIC_*andCLAUDE_CODE_*. Moving it is a larger change than this pull, so #12279 tracks it. - Preview check: the pre-push hook asked for a "how to try it" commit, so I added an empty
Try:commit. To try it, open/settings/crewon a forge with no runner; it says to setFORGE_AGENT_RUNNER.
🤖 Generated with Claude Code
The gate failure is fixed and pushed as 910c72d95. The over-long comment above runnerCredentialInEnv in src/crew/runner_env.ts is now wrapped under 100 columns.
bun scripts/gate.ts is clean on the second run. tsc --noEmit is clean and test/runner-env-names.test.ts passes (8 tests). My first gate run in that command still printed the old failure, because it ran before the rewrap took effect.
One code line in that file is 110 columns. The rule only covers comment lines, so I left it alone.
I did not run the rest of the check, the sharded fast-tier tests, locally. The forge runs it on this push.
The pull is pushed, but I couldn't reproduce the failing real-child-forge boot test (22 s against the 2 s budget) here, so it is unconfirmed. That test is skipped in this sandbox because there are no cells.
Nothing in the diff changes how a child boots. runnerSettings is trivial and the child still gets FORGE_AGENT_RUNNER. It most likely just ran slowly under the machine's load while 95 shards ran at once.
The other check failures were not caused by this change:
docs/MCP.md: the MCP tool table was stale forforges; its token count had drifted on main. I mergedorigin/mainin (a merge commit, since a rebase would need a force push) and regenerated the table withbun scripts/mcp-table.ts. It is one line, committed separately.first-screen-line: these pass on their own, but when I ran them in one command with five other test files, four of them failed. That points to cross-file interference between tests, and I did not chase it.- The three failures I did not run:
a parent holding a forge in its own process,two hosted forges on one machine: filesandthe screen shows the share, the budgets….
bun scripts/gate.ts and bun x tsc --noEmit are clean. I did not re-run the full suite; the forge runs it on this push.
What the pull does (unchanged):
- Env names:
FORGE_AGENT_RUNNERandFORGE_AGENT_CONFIG_DIRare now the forge's names. The olderFORGE_CLAUDEandFORGE_CLAUDE_CONFIG_DIRare still read, and the new name wins. - No
process.envwrites: every reader goes throughrunnerSettingsinsrc/crew/runner_env.ts. - Children and crew page: a child forge gets
FORGE_AGENT_RUNNER, and the crew page names it. - Log lines: the log names the runner.
- To try it: open
/settings/crewon a forge with no runner and it tells you to setFORGE_AGENT_RUNNER.
This pull stops writing the old runner env names into process.env. Every reader goes through runnerSettings, and children get FORGE_AGENT_RUNNER. The /settings/crew page names that variable and says to set it when a forge has no runner. The credential check moves to runner_env, and only the runner seam imports claude.ts.
This push fixes the one failure I could reproduce. test/first-screen-line.test.ts built ClientRow fixtures without where or placement. After merging main, the forge row and the terminal line both read r.where.inProcess, so four tests crashed. I added both fields to the fixture. The test was stale; the code was right. Nothing in src/ changed in this push.
The first-screen, spawn-pipes, mcp-table, spawn-sites and did-you-mean tests pass locally, and tsc --noEmit is clean. The rest of the check's failures need the check, not the sandbox, to confirm:
- Pass locally, failed in the check: spawn-pipes, spawn-sites, the MCP tool table and the did-you-mean 404 test.
- Skipped here: the boundary-tenancy cell test, which needs unix sockets.
- Not run: the "real child forge" test, which went over its 2 s budget at 23.5 s.
I did not touch any of these.
To try it, open /settings/crew on a forge with no runner and set FORGE_AGENT_RUNNER.
🤖 Generated with Claude Code
The check failed because this pull changes src/web/pages/apps.ts and src/web/pages/crew.ts but no commit message had a Looks: line. I added an empty commit carrying Looks: /settings/crew and Looks: /apps, both of which are in app.looks. bun scripts/gate.ts now passes locally with no output, and I pushed. I did not re-run the tests or the type check this round, since no code changed.
This pull stops writing the old runner env names into process.env. Every reader now goes through runnerSettings, and children get FORGE_AGENT_RUNNER. The /settings/crew page names that variable and says to set it when a forge has no runner. The credential check moves to runner_env, and only the runner seam imports claude.ts. The /apps change is a small addition to src/web/pages/apps.ts. The previous push also added where and placement to the fixture in test/first-screen-line.test.ts, which main's newer row code now requires.
To try it, open /settings/crew on a forge with no runner. It says to set FORGE_AGENT_RUNNER.
🤖 Generated with Claude Code
I merged main into this branch, resolved the two conflicts and pushed; the merge is commit bbb881f9b. tsc --noEmit and bun scripts/gate.ts are clean here, and 129 tests pass across the files that touch the runner env, the MCP table and the first-screen row.
This pull stops writing the old runner env names into process.env. Every reader goes through runnerSettings, and children get FORGE_AGENT_RUNNER. The new names FORGE_AGENT_RUNNER and FORGE_AGENT_CONFIG_DIR win, and FORGE_CLAUDE and FORGE_CLAUDE_CONFIG_DIR are still read. The /settings/crew page names the variable and says to set it when a forge has no runner.
src/crew/runner_env.ts:mainhad turned this file into the one place that names every model env variable. I kept that file asmainhas it. I addedrunnerSettings,namedRunnerandrunnerCredentialInEnvto it, where this branch had made them the whole file.src/core/subscriptions.ts:crewHasCredentialcallsrunnerCredentialInEnv, which replacesmain'shasEnvCredential. The credential check now accepts either spelling of the config-dir variable. I updatedtest/runner-env-confined.test.tsto the new name.test/first-screen-line.test.ts:mainfixed the stalewhere/placementfixture itself, so I dropped my earlier edit to it.
To try it, open /settings/crew on a forge with no runner. It says to set FORGE_AGENT_RUNNER.
🤖 Generated with Claude Code
Earlier asks: readers go through runnerSettings with no process.env writes, children get FORGE_AGENT_RUNNER, crew page names the new variable, log line names the runner. All met, with tests that fail without the change. The web change is one string on /settings/crew, so the design rules are not strained. No feature is added, so no terminal screen is needed, and README is untouched.
Two next steps: (1) finish moving ANTHROPIC_*/CLAUDE_CODE_* out of subscriptions.ts, sandbox.ts, grant.ts, runner.ts and packs/up.ts (tracked in #12279); (2) Non-blocking: runnerCredentialInEnv has a 110-column line; wrap it.
Left #12127 open: 1 unticked item remain (Variables the vendor's own CLI reads (ANTHROPIC_*, CLAUDE_CODE_*) stay, but only inside that runner's module).
The forge now reads
FORGE_AGENT_RUNNERandFORGE_AGENT_CONFIG_DIR, and the oldFORGE_CLAUDEandFORGE_CLAUDE_CONFIG_DIRstill work.tscis clean,scripts/gate.tsprinted nothing, and the new test file andtest/crew-interrupted.test.tspass. I ran no other suites and did not look at whatscripts/fast.tsnames. I did not run the app.runnerSettings(env)insrc/crew/claude.tsresolves both pairs. The new name wins when both are set, and the binary defaults toclaude.src/main.tsuses it for the crew and conversation options.settleRunnerEnvcopies the chosen runner binary and config dir into the old names at start. I did this because the app planner and the child-forge cell code (src/actions/apps.ts,src/web/pages/apps.ts,src/spawn/cell.ts,src/core/subscriptions.ts) still readFORGE_CLAUDEdirectly. Those files are outside this pull's list, so setting only the new name still reaches them.runner=<binary> version=<v>. The warning reads "agent runner not usable; agent runs will fail".ANTHROPIC_andCLAUDE_CODE_*:** I changed nothing here. The one place that reads them,src/core/subscriptions.ts, is outside the files named, so they are not moved into the runner module.docs/REFERENCE.mdhas a new "Agent runner settings" section listing the new names, saying the old ones are read too, and giving the new log line.test/runner-env-names.test.tscovers each new name, each old name, new-wins-over-old, the default, andsettleRunnerEnv.To try it, start the forge with
FORGE_AGENT_RUNNER=/path/to/claudeand look forcrew is up runner=…in the log.🤖 Generated with Claude Code
Closes #12127