CB-593: finish CB-592's two documentation criteria — inherited tokens decision, and the "only the bridge MCP" claim #79
Closed
opened 2026-08-15 18:49:32 +02:00 by ltms
·
3 comments
No Branch/Tag Specified
main
worker/fleetd-612-unita-87807e-1
worker/612-b3-mcpwirings-da2b58-3
worker/612-b2-cb185-176d3a-2
worker/612-b1-completion-457459-1
worker/612-agaps-73a926-2
worker/608-sleeps-3a64ff-3
worker/621-b4520b-1
worker/618-b83894-2
worker/fleetd-615-e05481-5
worker/lead-autocompact-5f1ab2-3
worker/fleetd-613-f85deb-3
worker/fleetd-608-flaky-nudge-test-d0c2d1-3
worker/lead-context-gauge-ad404f-1
worker/gauge-wiring-9158c1-4
worker/redeploy-slowstart-ead0e5-5
worker/charter-bytes-13668c-6
worker/rollover-outcome-291483-2
worker/589-f64303-2
worker/593-1a8025-5
worker/589-fcd2aa-1
worker/568-9fdaa2-3
worker/571-attempted-outcome-5739f7-2
worker/581-completionresolver-cas-sites-0542b7-6
worker/562-loop-health-wiring-test-99611c-5
worker/562-surface-loop-health-7df5cc-4
worker/575-waiter-cleanup-sites-62ad80-1
worker/572-answer-lock-release-46a9ae-5
worker/567-probe-channel-leak-a38fc5-6
worker/551-record-before-send-7cbf56-1
worker/561-listener-fanout-survives-a-throw-61d538-2
worker/555-redeploy-main-flow-seam-65c2f5-2
worker/556-injector-owns-registration-e027a5-1
worker/552-post-restart-mktemp-abort-bc2672-4
worker/553-onstatus-completion-leak-0da881-2
worker/550-shasum-linux-196132-1
worker/538-loop-dies-on-error-4a5eeb-6
worker/426-health-coverage-ef1fd4-4
worker/504-failed-reported-clean-3cfd66-3
worker/537-capturedlog-close-e4c437-2
worker/459-broken-link-targets-cadc17-5
worker/535-appender-leak-fe74c1-1
worker/512-part2-shutdown-detection-434701-9
worker/529-logger-level-sweep-2a5533-8
worker/528-drain-gate-call-site-5de83d-7
charter/forge-mcp-vs-token
worker/521-swap-guard-unpinned-28e931-5
worker/519-probe-test-harness-d25ab8-4
worker/525-logger-level-leak-1b4eb0-6
worker/518-fleetmcp-resolver-wiring-8ef96c-1
worker/512-drain-complete-line-7edd71-3
worker/517-abort-branch-and-jar-id-41b641-2
worker/500-9e52c9-3
worker/509-4912f4-2
worker/511-9a4b23-1
worker/493-479f45-2
worker/505-03f8b2-1
worker/492-followup-detect-unclear
worker/501-a31fa0-7
worker/498-451d1c-5
worker/494-1015ce-2
worker/492-209647-1
worker/489-001902-2
worker/480-relative-handover-path-906323-1
worker/480-b-handover-skill-45bf1f-5
worker/474-followup-source-pin-f54a55-17
worker/474-charter-check-on-reload-f54a55-17
worker/466-quarantine-repeatcount-report
worker/393-opencode-skill-seeding-71854b-13
worker/469-canonical-tool-names-2a472a-16
worker/466-quarantine-escalation-5ae9c1-15
worker/446-hot-exhausted-pattern-0af580-6
worker/464-charter-tool-name-guard-a85635-12
worker/463-listfleet-default-fails-open-f1c76c-11
worker/458-invariant-5-by-purpose-862f9a-10
worker/439-coordinator-row-gate-bc032a-8
worker/449-herdr-protocol-576015-4
worker/450-abstract-spawn-599e1c-5
worker/437-ack-refuses-177d91-1
worker/444-placement-window-feb56a-2
worker/440-helddurable-derived-d462d7-13
worker/425-rework-placement-resolve-c58ba1-9
worker/421-lead-peek-held-msgs-cdbad2-10
worker/435-fixed-policy-cap-fe11de-12
worker/422-gate-state-observability-9e79d6-11
worker/431-memberregistry-live-readers-cdbad2-10
worker/424-architect-slot-hot-038b41-7
worker/422-model-gate-spawn-c29f48-6
worker/425-default-profile-live-f55534-8
worker/415-coverage-wording-2cbf9c-5
worker/416-3ad1da-1
worker/418-588283-3
worker/deterministic-stamp-race-409-3cb7b6-10
worker/armed-reads-live-config-404-ed931f-9
worker/reply-peer-refusal-391-5a34bd-7
worker/models-allowlist-aa9e9b-3
worker/ttl-stamp-race-399-f1122f-8
worker/scrub-receipt-400-316b3e-5
worker/exhaustion-detection-395-105105-6
worker/scrub-abort-394-316b3e-5
fix/scrub-uid-abort
worker/task-scrub-517574-2
worker/t386-clock-bd5b78-4
worker/t384-scrub-813790-5
worker/t381-cc-748314-2
worker/t373-336973-2
worker/t365-3920c5-3
worker/t358-6e989b-1
worker/t355-8b321c-1
worker/fleetd-369-hermetic-git-tests-e8b19a-3
worker/fleetd-368-stale-lead-binding-f5682e-2
worker/fleetd-360-deploy-units-0d3793-1
worker/359-dead-lead-tabs-f1253b-4
worker/362-worktree-skills-c03e51-3
worker/361-coord-visibility-655144-1
362-plugin-visibility-and-drift
worker/errscan-bed2ca-2
worker/amqp-log-identity-bed2ca-2
worker/withdefaults-guard-561704
worker/sleepguard-82076d-1
worker/fd334-9ee1b6-5
worker/fd348-f1ab27-4
worker/fd335-a71c35-1
worker/fd342-174a17-2
worker/fd345-490d0f-3
worker/fleetd-337-5ec7d4-21
worker/fleetd-341-af5a6b-24
worker/fleetd-339-5ca0a2-23
worker/fleetd-338-83a4a1-22
worker/fleetd-333-281f46-18
worker/fleetd-329-11bdbb-16
worker/fleetd-330-2770fb-17
worker/fix-326-50506e-15
worker/fix-324-3e9bbf-14
worker/fix-323-b8287d-13
worker/fix-316b-bd0860-11
worker/fix-318-76ca36-9
worker/fix-317-486aec-8
worker/fix-315-ce47c5-6
worker/fix-307-275890-6
worker/fix-308-b4f664-7
worker/fix-309-ec3939-8
worker/fix-310-7a3974-9
worker/fix-302-52ad0e-9
worker/fix-298-ce1acb-8
worker/fix-297-66bd11-7
worker/fix-296-104622-6
worker/fix-293-bare-closetab-eb22b5-3
worker/fix-280-gone-ask-lapse-bca98e-2
worker/fix-290-reapidle-guard-coverage-9b0dd1-1
worker/fix-285-trust-seed-8f3565-10
worker/fix-284-backend-error-seat-85912c-11
worker/fix-282-chained-ask-e6d0bb-8
worker/fix-283-teardown-leaks-f40dfa-9
worker/fix-281-pin-handler-actions-4921ac-7
worker/audit-rendezvous-lifecycle-d072ae-2
worker/audit-health-placement-1a2476-6
worker/audit-teardown-exits-e207a5-3
worker/audit-launcher-asymmetry-27e370-4
worker/audit-rest-authz-6ca53c-5
worker/investigate-275-abandon-asking-fdef52-8
worker/fix-274-worktree-leak-b0095d-7
worker/fix-273-exhausted-pattern-9665b5-6
worker/fleetd-267-model-check-bd8068-1
worker/fleetd-131-archunit-18b834-7
worker/fleetd-266-sshagent-rename-a014ff-6
worker/fleetd-184-uid-claim-8e1f31-4
worker/fleetd-184-warn-b381ee-10
worker/fleetd-184-docs-be1d12-9
worker/fleetd-257-9bf010-7
worker/fleetd-103-23a113-6
worker/fleetd-247-342356-5
worker/fleetd-116-04dea8-4
worker/fleetd-252-a830e0-3
worker/fleetd-111-7e8673-9
worker/fleetd-155c-f8ef4b-8
worker/fleetd-176-b928ca-3
worker/fleetd-249-7a7878-2
worker/cb248-composition-root-b-9acdf7-15
worker/cb148-envrc-default-fa6c82-12
worker/cb201-unit5-wiring-6c12e6-8
worker/cb241-fallback-echo-1175e9-11
worker/cb149-trust-dialog-2392a5-9
worker/cb134-148-overlay-visible-c9b986-10
worker/cb234-session-id-keyed-04e1fc-1
worker/cb201-unit3-nudge-abdf5c-6
worker/cb201-unit2-policy-c1102c-5
worker/cb201-unit4-outcome-a13bfa-7
worker/cb201-unit1-classifier-91b9b1-4
worker/cb201-227-refine-831980-3
worker/cb175-model-readback-0f085f-1
worker/cb222-charter-tmpdir-17f013-1
worker/cb226-architect-slot-race-cd3aa8-3
worker/cb224-worktree-root-group-024523-2
worker/cb-123-role-demotion-c600f7-2
worker/cb-219-opencode-roots-1f677e-1
worker/cb214-claude-session-id-b9eab4-4
worker/cb213-zdotdir-wrong-process-dd6de4-3
worker/cb211-exhaustion-classification-9546e0-2
worker/cb137-ambiguous-task-4df3d8-4
worker/cb209-agentsessionid-4dfdb6-2
worker/cb185-hostenvnames-2692b5-3
worker/cb206-opencode-sqlite-128718-2
worker/cb185-worktree-group-fc0c99-1
worker/cb-137-ask-ticket-e7760c-2
worker/cb-172-broker-uri-d36ae4-4
worker/cb-175-model-readback-76ead6-3
worker/cb-161-pane-ancestry-293510-1
worker/cb-164-rebase-885863-8
worker/cb-164-empty-scrape-false-success-1a80af-3
fix/cb-197-ticket-ttl-from-completion
worker/cb-189-remote-url-coverage-4692f3-1
worker/cb-185-blockers-027756-4
worker/cb-192-gap-log-11b631-2
worker/cb-633-fix-5f4396-3
worker/cb185-router-d6436d-3
worker/cb185-router-routing-gaps-9e9d33-3
worker/cb185-paneids-992586-2
worker/cb-633-allow-list-union-ed374b-1
worker/cb-157-credential-in-remote-url-496e44-2
worker/cb-641-health-herdr-evidence-8f1f54-6
worker/cb-640-health-msg-evidence-99c9cd-1
worker/cb-642-fleets-status-skill-bbbc40-5
cb-634-ide-mcp
worker/lead-comms-wiring-c014b9-7
worker/lead-mailbox-c19577-6
worker/autocompact-window-82bc2f-5
worker/cb-634-probe-18056f-4
worker/cb635-broker-urienv
worker/cb-632-config-retry-8e0efa-7
lead/cb-622e-claude-md
lead/cb-622-followup
worker/cb-622a-165dff-1
lead/cb-622d-opencode-mount
worker/cb-622b-717c67-2
worker/cb-622c-ab7759-3
worker/cb-617b2-20ca4b-3
worker/cb-617a-5c2f4a-1
worker/cb596-4e49ef-3
worker/cb586-10500c-1
worker/cb-606-b9343a-25
worker/cb604-1445f8-24
worker/cb582-477374-21
worker/cb584-8c2281-22
worker/cb600-e6b9a9-20
worker/cb602-ce257f-19
worker/cb601-b42837-18
worker/cb598-6c7ba7-17
worker/cb599-740fe4-16
worker/cb597-282224-15
worker/cb590fix-185e9a-10
worker/cb528-recovery-race
worker/cb594-96bead-8
worker/cb590-916766-2
worker/cb527-997d99-3
worker/cb592-env-leak-3cbf9c-1
worker/cb588-async-ticket-nudge-3218f7-5
worker/cb578b-9dcb13-6
worker/cb581-d24826-5
worker/m2-u5-ef8c42-15
worker/cb578a-516499-2
worker/cb576-01a04b-17
worker/cb579-lead-tab-acba06-20
worker/cb580-terminal-health-ed6058-21
worker/cb577-f36fdc-18
worker/cb573b-3db06f-16
worker/cb568c-f36fdc-18
worker/cb568-drop-cause-c3ac1c
worker/cb575-cancelled-notification-c3ac1c
worker/m4-sol-a2cbec-3
worker/cb574-async-ask-c3ac1c
worker/cb573-health-model-8ca857-14
worker/cb572-unknown-target-7f2e35-13
worker/u4-700706-9
worker/u3-b9fcb6-6
worker/u2-ef5b68-4
worker/u1-469dce-1-clean
worker/u1-469dce-1
worker/cb-564-health-events-70cf7e-2
worker/cb-565-recycle-drops-role-98e58f-3
worker/cb-563-missing-reply-df2866-1
worker/cb-562-readiness-gate-silent-6c23c9-3
worker/cb-560-architect-presence-da8155-1
worker/cb-561-architect-silent-off-a71cab-2
worker/cb-548-bind-architect-slot-fe1b8c-1
worker/parity-overlay-settings-5fb711-1
secrets-central-store
cb-559-hot-key-correction
cb-557-fleet-role-pools
worker/cb-553-maxload-explicit-spawn-305ee3-6
worker/cb-551-idle-lead-heartbeat-f1633c-1
worker/cb-544-drain-preserves-worktree-925fad-3
worker/cb-552-docs-sync-1cb9cf-4
worker/cb-548-rendezvous-guard-rebased
worker/cb-548-rendezvous-guard-116b53-10
worker/cb-548-authz-v2-586df6-8
worker/cb-548-authz-264363-5
salvage/cb-528b-codex-home
salvage/cb-528a-codex-launcher
CB-518-primary-flow
feature/peer-launcher-spi
cb-103-injector
v1.1.0
v1.0.0
Labels
Clear labels
blocked
needs-live-proof
ready-to-delegate
silent-default
Cannot start until something else lands. The body says what.
Merged and green, but never shown working on the running daemon. Not the same as done.
Scope, files and acceptance criteria are written. A worker can be briefed from the body alone.
A feature that compiles, passes tests, and ships turned off. Nine recurrences and counting.
No Label
Milestone
No items
No Milestone
1.1 — single-host close-out
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: fleet/fleetd#79
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to #77, which is closed because the leak itself is closed and verified on a live pane.
Two of its seven acceptance criteria were documentation and decision work, not code, and are not
done. Splitting them out so closing #77 does not bury them.
1. Decide what happens to the other inherited credentials (#77 criterion 5)
Every member still inherits
CONTEXT7_TOKENandAI_GATEWAY_TOKENfrom herdr's environment, by theexact mechanism CB-592 fixed for the admin forge token.
They are far less dangerous than an admin forge token.
AI_GATEWAY_TOKENis the key members aremeant to use once CB-591 lands, and
CONTEXT7_TOKENbacks an MCP mount members are meant to have.So "leave them" is very likely the right answer.
The point is that "we chose to allow it" and "we never noticed" must not look the same. #77 was
found by accident; the same accident should not have to happen again for these two.
Now that
BRIDGED_MEMBERexists, splitting either one per-role is a one-line guard insecrets.sh— the option is cheap and available, which is why the decision should be explicit.Done when: a short recorded decision says, for each of
CONTEXT7_TOKENandAI_GATEWAY_TOKEN,whether a member may hold it and why. Wiki Features is the right home — the
why line is the part that stops this being re-litigated in a month.
2. Correct
CLAUDE.md's "only the bridge MCP" claim (#77 criterion 7)CLAUDE.mdtells members:That is already false for opencode members. The tracked
opencode.jsonships in every worktreeand mounts three servers:
bridged,context7, andgitea.Two problems, and the second is the one that matters:
never claim the result of a check it had no way to run. The fix is to make the sentence describe
the real mount set per peer kind, without weakening that rule.
Note the claim is in the canonical block, so the edit must be propagated byte-identically to the
wiki template (Use Cases → The portable
CLAUDE.mdblock) and to other projects carrying it. Use the sync check at the end ofCLAUDE.mdto verify rather than trust.
Done when: the block describes the real mount set, keeps the "never claim an unrun check" rule,
and the sync check prints
in sync: True.Not in scope
The leak, the launcher sentinel, the
BRIDGED_MEMBERmarker and thesecrets.shguard are all doneand verified — see #77.
Criterion 2 is DONE — and the premise in the issue body was backwards
Correcting my own text above before recording the fix. The issue says the claim is "already false
for opencode members", reasoning from the tracked
opencode.json. That was an assumption and itis wrong. I measured it instead of reasoning about it: one member spawned per backend, each asked
what MCP tools it actually holds.
gx(opencode)local(claude-code)mcp__gitea__*+ 2mcp__context7__*So it was false for exactly the backend the issue said was fine, and true for the one it said was
broken. The lesson is the ordinary one: the config file is not the mount set.
Why the tracked
opencode.jsonwas a red herringThe worktree parity overlay does its job. Both members reported their worktree copies neutralised —
.mcp.jsonis{"mcpServers": {}}andopencode.jsonis{}. Nothing the repo ships reaches amember.
The real source is outside anything this repo controls:
~/.claude.jsondeclares user-scopemcpServers(gitea,context7,ccs-image-analysis,ccs-websearch), and Claude Code's--mcp-configadds to that scope rather than replacing it. The overlay cannot see it, so nochange on our side would have caught this. opencode is unaffected only because its home config
declares no MCP block.
The forge tools are mounted but cannot authenticate — CB-592 is holding
This was the part worth checking, since 45 gitea tools include
delete_branch,delete_file,create_repoandwiki_write. Measured on the live member:Both calls fail at client creation, not at scope. The
giteaentry in~/.claude.jsonreadsGITEA_ACCESS_TOKEN, CB-592 shadows that name, so the server starts and every call fails. This isdefence in depth working in a path it was never designed for. Worth stating plainly, because it is
also the reason nobody noticed: the tools are present and useless, which looks like absence.
Residual risk, not fixed here
The tools fail only because the credential is blocked. If the user-scope
giteaentry were everpointed at
WORKER_GITEA_TOKEN— which is real and valid — a member would gain 45 working forgetools including destructive ones. Nothing in this repo prevents that, because the config is outside
it. Recording it rather than fixing it: the fix is a launcher change (
--strict-mcp-config), whichis a behaviour change for every Claude Code member and deserves its own ticket and decision.
What changed
CLAUDE.md— member rule 5 and lead step 6 both rewritten. The block now says what a member reallymounts per backend, keeps the "never claim the result of a check you had no way to run" rule
intact, and adds the thing this investigation actually taught: a mounted tool is not a working
tool.
in sync: True.2124e04, wiki1d95e3f.Criterion 2 done. Criterion 1 (the recorded decision on
CONTEXT7_TOKEN/AI_GATEWAY_TOKEN, andits Features entry) is still open, so this issue stays open.
Criterion 1 turned out to be a corner of something larger, now filed as #82 (CB-596).
Working the "may a member hold
CONTEXT7_TOKENandAI_GATEWAY_TOKEN" question meant asking how they get there. The answer is the member's pane login shell sourcing${SHARED_ENV}/tools/secrets.sh— the same mechanism CB-592 had to fight with theBRIDGED_MEMBERmarker. That file exports about thirty names. CB-592 blocks one.So the two tokens in this ticket are not a special case; they are two rows in a table nobody has filled in.
GITLAB_PERSONAL_ACCESS_TOKEN— a second forge — sits in that same table with nothing blocking it.One of the two answers is already clear and I have recorded it on #82:
AI_GATEWAY_TOKENis required, not leaked.bridged.yamlnames it intokenEnv:for thelocalandgxprofiles, so a member reaching the gateway is by design. That is criterion 1's first half, decided.CONTEXT7_TOKENI have deliberately not decided. It should be answered together with the other twenty-eight rather than on its own, which is what #82 is for.Also worth recording here: I attempted to measure the rest by briefing a member to report which names are set — names, lengths and 6-character prefixes only, never values — and the command classifier refused it. That refusal is right, and I did not route around it. Authorising that measurement is step 1 of #82.
This issue stays open for criterion 1's remaining half and for the
--strict-mcp-configdecision. Both are now in the 1.1 — single-host close-out milestone.Both criteria are now met.
1. The inherited-credential decision — recorded
New Features entry, Which inherited credentials a member may keep (wiki commit
b1d13d7). It states, per credential, whether a member may hold it and why:AI_GATEWAY_TOKENllm.ltms.devand that token is the single front-door key. Blocking it stops those members working at all.CONTEXT7_TOKENcontext7docs MCP, a read-only lookup members are meant to have. Worst case is documentation reads on the operator quota.GITEA_ACCESS_TOKENWORKER_GITEA_TOKEN.The entry says why it is written down at all:
BRIDGED_MEMBERnow exists, so splitting either one per-role is a one-line guard. The option being cheap is exactly why the decision has to be explicit rather than implied by nobody having done it.Its gotcha line keeps the scope honest — this decides two names out of about thirty. It is not a statement that the rest were checked. #82 still tracks enumerating them and still needs the operator.
2. The "only the bridge MCP" claim — already corrected, now verified
This was fixed in the canonical block before this ticket was picked up. The block now says what a member actually mounts, per backend:
The rule the old sentence carried survived the correction, which was the part that mattered: a member must never claim the result of a check it had no way to run. It is still there, and the follow-on point — a mounted tool is not a working tool, because the forge server holds a deliberately blocked credential — is stated with it.
I ran the sync check from
CLAUDE.mdrather than trusting it:Also fixed while in there
The backfill note still described the reply-push nudge budget as per source. CB-598 merged today and moved it to per pending item, so the note was already stale. Corrected, including the consequence worth writing up later: the cap bounds nudges about one item, so a lead with a steady arrival of new work keeps being nudged. That is correct behaviour but it is not what the knob name suggests.