The opus profile is the only one with no gitTokenEnv, so an opus member gets no GITEA_TOKEN — and CLAUDE.md tells every member to use that name #766

Open
opened 2026-10-05 10:57:40 +02:00 by ltms · 0 comments
Owner

Found while settling #763. Two halves, one cause: a missing config key, and an instruction that names a variable which is unset for the profile a lead reaches for most.

Measured (2026-10-05)

fleetd/fleetd.yaml declares eight profiles: local, local-direct, gx, opus, sonnet, sol, terra, xf.

Seven of them set gitTokenEnv: WORKER_GITEA_TOKEN. opus sets none. The daemon's own startup log agrees, independently of my reading of the file:

09:52:42.766 INFO  dev.ltms.fleet.Fleetd - startup secret WORKER_GITEA_TOKEN: set
  (profile 'local' gitTokenEnv, profile 'local-direct' gitTokenEnv,
   profile 'gx' gitTokenEnv, profile 'sonnet' gitTokenEnv,
   profile 'sol' gitTokenEnv, profile 'terra' gitTokenEnv, profile 'xf' gitTokenEnv)

Seven names, no opus. Two sources, two different mechanisms, same answer.

HerdrPeerLauncher injects the forge token only when the profile's gitTokenEnv resolves — workerEnv.put("GITEA_TOKEN", gitToken) sits inside that branch. With no key, an opus member's GITEA_TOKEN is never set.

Confirmed from inside a member pane

The #761 worker runs on opus. It reported, unprompted:

  • GITEA_TOKEN — unset
  • GITEA_ACCESS_TOKEN — present and empty, length 0, gives HTTP 401 token is required (that is the memberCredentials scrub, working as designed — see #763)
  • WORKER_GITEA_TOKEN — present, 40 characters, and this is what its PR went out on

So the PR exists because the worker went looking after the documented name failed. A worker that followed the instruction literally would have stopped.

Why this matters more than it looks

opus is the profile a lead picks when a unit needs the strongest worker. It is the single profile where the designed forge path is missing, so the failure is concentrated exactly on the work most likely to produce a PR worth opening.

It is also silent. The spawn succeeds, the member runs, the build passes, and the gap shows up only at the last step, as a worker that cannot open its PR — or, as happened here, as a worker that quietly uses a different credential and mentions it in a caveat the lead might not read.

Two fixes, and they are separate

1. Config. Give opus the same gitTokenEnv: WORKER_GITEA_TOKEN as the other seven, unless there is a deliberate reason it was left out. I could not find one: nothing in fleetd.yaml near that profile explains the omission, and the other claude-code profiles all set it. Treat "it was an oversight" as the hypothesis, not the conclusion, until someone who knows confirms it.

2. The instruction. CLAUDE.md's member turn contract says:

the repo-scoped GITEA_TOKEN the daemon injects into your environment does work, and using it to open your own PR is part of the job

That sentence is true for seven profiles and false for one, and the member reading it cannot tell which it is on. Fix the config and it becomes true everywhere — which is the better order, because rewording it to mention two names would teach every worker a workaround instead.

Note the two names are not interchangeable by design. GITEA_TOKEN is the repo-scoped value the daemon injects; WORKER_GITEA_TOKEN is the daemon's own source variable, reaching the pane from the login shell. A member using the source variable is routing around a missing injection, and it should not learn to do that as normal practice.

A guard worth considering

Nothing fails when a claude-code profile omits gitTokenEnv. A startup check — "every profile whose members are expected to open PRs resolves a forge token" — would have caught this at boot instead of at the last step of a worker's turn. The daemon already reports which secrets resolved and for which profiles, so the information is in hand; what is missing is a complaint when a profile is absent from that list. That is the same shape as #763's finding: the log told the truth and nobody was required to read it.

Not measured

I have not spawned an opus member and checked git push and PR-create after adding the key. The claim here is about which variable reaches the pane, not about what the credential is allowed to do. Push goes over SSH and is unaffected either way.

Found while settling #763. Two halves, one cause: a missing config key, and an instruction that names a variable which is unset for the profile a lead reaches for most. ## Measured (2026-10-05) `fleetd/fleetd.yaml` declares **eight** profiles: `local`, `local-direct`, `gx`, **`opus`**, `sonnet`, `sol`, `terra`, `xf`. Seven of them set `gitTokenEnv: WORKER_GITEA_TOKEN`. `opus` sets none. The daemon's own startup log agrees, independently of my reading of the file: ``` 09:52:42.766 INFO dev.ltms.fleet.Fleetd - startup secret WORKER_GITEA_TOKEN: set (profile 'local' gitTokenEnv, profile 'local-direct' gitTokenEnv, profile 'gx' gitTokenEnv, profile 'sonnet' gitTokenEnv, profile 'sol' gitTokenEnv, profile 'terra' gitTokenEnv, profile 'xf' gitTokenEnv) ``` Seven names, no `opus`. Two sources, two different mechanisms, same answer. `HerdrPeerLauncher` injects the forge token only when the profile's `gitTokenEnv` resolves — `workerEnv.put("GITEA_TOKEN", gitToken)` sits inside that branch. With no key, an `opus` member's `GITEA_TOKEN` is never set. ## Confirmed from inside a member pane The #761 worker runs on `opus`. It reported, unprompted: - `GITEA_TOKEN` — **unset** - `GITEA_ACCESS_TOKEN` — present and **empty**, length 0, gives `HTTP 401 token is required` (that is the `memberCredentials` scrub, working as designed — see #763) - `WORKER_GITEA_TOKEN` — present, 40 characters, **and this is what its PR went out on** So the PR exists because the worker went looking after the documented name failed. A worker that followed the instruction literally would have stopped. ## Why this matters more than it looks `opus` is the profile a lead picks when a unit needs the strongest worker. It is the single profile where the designed forge path is missing, so the failure is concentrated exactly on the work most likely to produce a PR worth opening. It is also silent. The spawn succeeds, the member runs, the build passes, and the gap shows up only at the last step, as a worker that cannot open its PR — or, as happened here, as a worker that quietly uses a different credential and mentions it in a caveat the lead might not read. ## Two fixes, and they are separate **1. Config.** Give `opus` the same `gitTokenEnv: WORKER_GITEA_TOKEN` as the other seven, unless there is a deliberate reason it was left out. I could not find one: nothing in `fleetd.yaml` near that profile explains the omission, and the other `claude-code` profiles all set it. Treat "it was an oversight" as the hypothesis, not the conclusion, until someone who knows confirms it. **2. The instruction.** `CLAUDE.md`'s member turn contract says: > the repo-scoped `GITEA_TOKEN` the daemon injects into your environment does work, and using it to open your own PR is part of the job That sentence is true for seven profiles and false for one, and the member reading it cannot tell which it is on. Fix the config and it becomes true everywhere — which is the better order, because rewording it to mention two names would teach every worker a workaround instead. Note the two names are not interchangeable by design. `GITEA_TOKEN` is the repo-scoped value the daemon injects; `WORKER_GITEA_TOKEN` is the daemon's own source variable, reaching the pane from the login shell. A member using the source variable is routing around a missing injection, and it should not learn to do that as normal practice. ## A guard worth considering Nothing fails when a `claude-code` profile omits `gitTokenEnv`. A startup check — "every profile whose members are expected to open PRs resolves a forge token" — would have caught this at boot instead of at the last step of a worker's turn. The daemon already reports which secrets resolved and for which profiles, so the information is in hand; what is missing is a complaint when a profile is absent from that list. That is the same shape as #763's finding: the log told the truth and nobody was required to read it. ## Not measured I have not spawned an `opus` member and checked `git push` and PR-create after adding the key. The claim here is about which variable reaches the pane, not about what the credential is allowed to do. Push goes over SSH and is unaffected either way.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: fleet/fleetd#766