charter: separate the blocked forge MCP server from the working GITEA_TOKEN #531

Merged
ltms merged 1 commits from charter/forge-mcp-vs-token into main 2026-09-12 07:41:11 +02:00
Owner

Why

Two workers reported having working forge access. That looked like it contradicted the charter:

any forge tools it appears to have hold a blocked credential and fail

I measured it. The charter is right and the workers were right. They are talking about two different credentials, and the wording did not separate them.

Route Credential Works?
curl to $GITEA_HOST/api/v1/repos/fleet/fleetd/pulls repo-scoped GITEA_TOKEN, injected per spawn yes — this is how every worker PR this week was opened
mcp__gitea__* tools that leak in from the operator's user-scope ~/.claude.json a deliberately blocked credential no, fails every call by design

.claude/skills/implementer/SKILL.md step 5 (lines 96-117) tells the worker to use the first route, and says the token "can create a PR but cannot merge". So opening its own PR is part of a worker's job and it really can do it.

The cost of leaving it

This is an ambiguity with a consequence, not a stale line. A worker that reads "any forge tools it appears to have hold a blocked credential and fail" can reasonably conclude it cannot reach the forge at all, and skip step 5 — which is the one step the lead depends on, because the PR body is where a report survives a lost reply. The failure would be quiet: a pushed branch, no PR, and a worker that believes it followed the charter.

The same reading error is available to a lead: seeing "forge tools fail", a lead could disbelieve a worker that correctly reports its own PR URL.

The change

Two sentences, both in the canonical block. Each now names the MCP server specifically and says the injected token is a separate, working route.

Primary, step 6 — Verify yourself:

any forge MCP server it appears to have holds a blocked credential and fails every call … Its injected repo-scoped GITEA_TOKEN is a different credential and does work, so a worker reporting that it opened its own PR is reporting something it really can do.

Member, step 5 — Report honestly:

the forge MCP server you may find there holds a deliberately blocked credential and fails every call, by design. That is not your only forge route, and the two must not be confused: 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. A blocked MCP tool is never a reason to skip that step.

No behaviour changes. No code changes. 9 insertions, 4 deletions, one file.

Propagation

CLAUDE.md's own rule requires the canonical block and the wiki template to stay byte-identical. Done and verified with the script from CLAUDE.md:

in sync: True

The wiki is a submodule with its own remote, so its commit is separate: d02a55d on wiki's main, pushed and verified by ref (git ls-remote --heads origin main → d02a55da4478, matching local HEAD) rather than by exit code — a wiki push to the wrong branch name is a silent no-op here.

The submodule pointer stays unstaged in this PR, per the repo rule that wiki/ is never committed. Only CLAUDE.md is staged.

What this does not claim

I have not re-tested the blocked MCP credential myself this session. The claim that it fails every call is the existing charter's, unchanged by this PR — I am only narrowing which thing it refers to. If someone wants that half re-measured, it needs its own probe with a request that cannot succeed on its merits, so a rejection can only mean the block.

## Why Two workers reported having working forge access. That looked like it contradicted the charter: > any forge tools it appears to have hold a blocked credential and fail I measured it. **The charter is right and the workers were right.** They are talking about two different credentials, and the wording did not separate them. | Route | Credential | Works? | |---|---|---| | `curl` to `$GITEA_HOST/api/v1/repos/fleet/fleetd/pulls` | repo-scoped `GITEA_TOKEN`, injected per spawn | **yes** — this is how every worker PR this week was opened | | `mcp__gitea__*` tools that leak in from the operator's user-scope `~/.claude.json` | a deliberately blocked credential | no, fails every call by design | `.claude/skills/implementer/SKILL.md` step 5 (lines 96-117) tells the worker to use the first route, and says the token "can create a PR but **cannot** merge". So opening its own PR is part of a worker's job and it really can do it. ## The cost of leaving it This is an ambiguity with a consequence, not a stale line. A worker that reads "any forge tools it appears to have hold a blocked credential and fail" can reasonably conclude it cannot reach the forge at all, and skip step 5 — which is the one step the lead depends on, because the PR body is where a report survives a lost reply. The failure would be quiet: a pushed branch, no PR, and a worker that believes it followed the charter. The same reading error is available to a lead: seeing "forge tools fail", a lead could disbelieve a worker that correctly reports its own PR URL. ## The change Two sentences, both in the canonical block. Each now names the **MCP server** specifically and says the injected token is a separate, working route. Primary, step 6 — *Verify yourself*: > any forge **MCP server** it appears to have **holds** a blocked credential and **fails every call** … Its injected repo-scoped `GITEA_TOKEN` is a different credential and does work, so a worker reporting that it opened its own PR is reporting something it really can do. Member, step 5 — *Report honestly*: > the forge **MCP server** you may find there holds a deliberately blocked credential and fails every call, by design. **That is not your only forge route, and the two must not be confused:** 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. A blocked MCP tool is never a reason to skip that step. No behaviour changes. No code changes. 9 insertions, 4 deletions, one file. ## Propagation `CLAUDE.md`'s own rule requires the canonical block and the wiki template to stay byte-identical. Done and verified with the script from `CLAUDE.md`: ``` in sync: True ``` The wiki is a submodule with its own remote, so its commit is separate: `d02a55d` on `wiki`'s `main`, pushed and **verified by ref** (`git ls-remote --heads origin main` → `d02a55da4478`, matching local `HEAD`) rather than by exit code — a wiki push to the wrong branch name is a silent no-op here. The submodule pointer stays **unstaged** in this PR, per the repo rule that `wiki/` is never committed. Only `CLAUDE.md` is staged. ## What this does not claim I have not re-tested the blocked MCP credential myself this session. The claim that it fails every call is the existing charter's, unchanged by this PR — I am only narrowing *which* thing it refers to. If someone wants that half re-measured, it needs its own probe with a request that cannot succeed on its merits, so a rejection can only mean the block.
ltms added 1 commit 2026-09-12 07:38:36 +02:00
charter: separate the blocked forge MCP server from the working GITEA_TOKEN
CI / contract (pull_request) Successful in 54s
CI / build (pull_request) Successful in 2m3s
be07ed2033
Two worker reports said they had working forge access, which looked like it
contradicted the charter's "any forge tools it appears to have hold a blocked
credential and fail". Measured: the charter is correct and the reports are
correct. They are about two different credentials.

A worker opens its own PR with curl and a repo-scoped GITEA_TOKEN that the
daemon injects (.claude/skills/implementer/SKILL.md step 5, lines 96-117).
That route works — it is how every worker PR this week was opened. The blocked
credential belongs to the forge MCP server that leaks in from the operator's
user-scope ~/.claude.json, which is a separate thing and does fail every call.

The wording did not distinguish them. A worker reading "any forge tools it
appears to have hold a blocked credential and fail" could reasonably conclude
it cannot reach the forge at all, and skip opening its PR — the one step the
lead depends on. So this is an ambiguity with a cost, not a stale line.

Both sentences now name the MCP server specifically and say plainly that the
injected token is a different, working route.

The canonical block and the wiki template must stay byte-identical. The wiki
submodule is updated in its own tree and the official sync check reports
"in sync: True". The submodule pointer stays unstaged, per the repo rules.
ltms merged commit d25c863118 into main 2026-09-12 07:41:11 +02:00
Sign in to join this conversation.