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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

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

opened this

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) in src/crew/claude.ts resolves both pairs. The new name wins when both are set, and the binary defaults to claude. src/main.ts uses it for the crew and conversation options.
  • Old readers: settleRunnerEnv copies 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 read FORGE_CLAUDE directly. 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_ and CLAUDE_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.md has 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.ts covers each new name, each old name, new-wins-over-old, the default, and settleRunnerEnv.
  • 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

Closes #12127

8 earlier comments · 2 events
commented

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) in src/crew/claude.ts resolves both pairs. The new name wins when both are set, and the binary defaults to claude. src/main.ts uses it for the crew and conversation options.
  • Old readers: settleRunnerEnv copies 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 read FORGE_CLAUDE directly. 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_ and CLAUDE_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.md has 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.ts covers each new name, each old name, new-wins-over-old, the default, and settleRunnerEnv.
  • 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

commented

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.

Changes requested — before the latest push

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.

  1. Fix first: stop settleRunnerEnv (src/crew/dispatch.ts) writing FORGE_CLAUDE and FORGE_CLAUDE_CONFIG_DIR back into process.env, and drop its call in src/main.ts. Make every reader call runnerSettings(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 before main.ts runs it sees the old behaviour. The cell code also sets FORGE_CLAUDE for children, so children still get the vendor name. Pass the neutral name to children as well.
  2. Box 3 is unmet. ANTHROPIC_* and CLAUDE_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.
  3. Box 2 is only partly met. src/web/pages/crew.ts:142 still tells people to set FORGE_CLAUDE. Name FORGE_AGENT_RUNNER there, and add a test for the wording.
  4. Tests: settleRunnerEnv is tested only on plain objects. Add a test that the readers in item 1 follow FORGE_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.

commented

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: settleRunnerEnv and its call in src/main.ts are gone, and nothing writes FORGE_CLAUDE or FORGE_CLAUDE_CONFIG_DIR into process.env any more.
  • New module: runnerSettings now lives in src/crew/runner_env.ts, and dispatch.ts re-exports it. I put it there so actions, web, spawn and core don't import the crew dispatcher. The module also holds namedRunner and runnerCredentialInEnv.
  • Readers: src/actions/apps.ts and src/web/pages/apps.ts call runnerSettings(process.env).binary. cell.ts takes the parent's runner through namedRunner. crewHasCredential uses runnerCredentialInEnv, which also reads the neutral config dir; before, it only read FORGE_CLAUDE_CONFIG_DIR. I updated the comment in src/crew/conversation.ts.
  • Children: child cells now get FORGE_AGENT_RUNNER, whichever name the parent used. The child's main.ts already reads that name.
  • Crew page: src/web/pages/crew.ts now says to set FORGE_AGENT_RUNNER.
  • Tests: test/runner-env-names.test.ts now 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.ts asserts the crew page wording, and test/spawn-cell.test.ts now expects FORGE_AGENT_RUNNER, because the old FORGE_CLAUDE assertion was testing what you asked to remove. test/spawn-child-apps.test.ts has 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.ts and src/crew/plan.ts still names ANTHROPIC_* and CLAUDE_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/crew on a forge with no runner; it says to set FORGE_AGENT_RUNNER.

🤖 Generated with Claude Code

commented

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.

commented

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 for forges; its token count had drifted on main. I merged origin/main in (a merge commit, since a rebase would need a force push) and regenerated the table with bun 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: files and the 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_RUNNER and FORGE_AGENT_CONFIG_DIR are now the forge's names. The older FORGE_CLAUDE and FORGE_CLAUDE_CONFIG_DIR are still read, and the new name wins.
  • No process.env writes: every reader goes through runnerSettings in src/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/crew on a forge with no runner and it tells you to set FORGE_AGENT_RUNNER.
commented

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

commented

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

commented

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: main had turned this file into the one place that names every model env variable. I kept that file as main has it. I added runnerSettings, namedRunner and runnerCredentialInEnv to it, where this branch had made them the whole file.
  • src/core/subscriptions.ts: crewHasCredential calls runnerCredentialInEnv, which replaces main's hasEnvCredential. The credential check now accepts either spelling of the config-dir variable. I updated test/runner-env-confined.test.ts to the new name.
  • test/first-screen-line.test.ts: main fixed the stale where/placement fixture 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

Approved

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.

commented

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).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs