Members cannot run shell commands: the classifier refuses every one, so they cannot build, test or open a PR (3 members, 2 profiles) #381
Closed
opened 2026-09-09 02:29:11 +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
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: fleet/fleetd#381
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?
Measured 2026-09-08 UTC across one fan-out of six members on the Mac.
What happened
Two
terramembers reported that their shell runner refused every command they tried.The #376 member, verbatim:
The #352 member, verbatim:
Both were briefed as implementers. Both wrote correct-looking source changes into their worktrees. Neither could compile, test, stage, commit, push, or open a pull request.
Why this matters more than a lost turn
A member in this state is not obviously broken. It reads files, edits them, and writes a fluent report — it simply cannot execute anything. The #376 member produced a real change and an articulate summary of tests it had "added", and none of it had ever been compiled or run.
I only found out because its report said so plainly. A less careful member would have reported success, and the implementer brief asks for a PR URL, which would have been missing — but a lead skimming a good-looking report could easily miss that. The failure is quiet in exactly the way that costs a merge.
It also silently converts a delegation into lead work. I had to read the change, build it, test it, find its defect, rewrite it and commit it myself. That is the opposite of what delegating is for.
What I have and have not established
Measured: two
terramembers, same fan-out, same refusal. Both briefed the same way as threesonnetmembers and onegxmember spawned minutes apart from the same lead.Not established:
terraprofile, to the OpenAI-backed adapter generally, or to something about that fan-out. A thirdterramember on #340 did read-only work and never needed a command, so it is not evidence either way.sonnetmembers hit it. They were still running when I filed this.fleetdcomponent at all.I am filing the observation, not a diagnosis. Do not fix anything from this text alone.
Suggested first step — measure, do not fix
Spawn one
terramember and onesonnetmember with the same trivial brief: rungit statusandecho ok, and report the exact output or the exact refusal. That settles profile-specificity in one turn and costs almost nothing.If it reproduces, the next question is where the refusal is raised, because that decides whose bug it is. Nothing here yet shows it is
fleetd's.Consequence for how the fleet is used, until this is understood
terrashould not be given implementer work — it cannot complete the hand-off the implementer skill requires. It appears fine for read-only analysis: the #340terramember produced the best report of the whole fan-out, and needed no commands to do it.Related
The title is wrong. This is not a terra problem.
A gx member (#377,
worker/t377-7f587b-7) hit the identical refusal about 20 minutes after I filed this. Its own words:So the count is now three members across two profiles: terra ×2, gx ×1. My "suggested first step" above — spawn one terra and one sonnet and compare — was built on a theory this disproves. Do not run that experiment as written.
What the gx data point adds
It rules out two things the terra pair could not:
pwdanddatewere refused. There is nothing to get wrong aboutpwd. The refusal is upstream of whatever the member typed.It also tells us the failure is total, not selective — every command, including a subagent's shell.
Still not established
mvn clean installsuccessfully earlier in the same fan-out, on the same host. So it is not "the classifier is down for everyone, always". Either it broke partway through the evening, or it does not affect every member. I did not capture timestamps precisely enough to separate those, and that is the single most useful thing the next person could measure.fleetd's bug rather than the host command classifier's.Better first step, replacing the one above
Do not compare profiles. Compare time. Spawn two members on any profile, a few minutes apart, brief each to run
pwdand report the exact output or exact refusal with a timestamp. If the first succeeds and the second is refused, this is a service that degrades, and the question becomes what recovers it.If a member is refused, have it report immediately rather than retry — the gx member spent a long stretch probing, which bought nothing.
What it cost, and what stopped it costing more
The gx member had the whole change written and could not build, commit, push or open a PR. I told it to stop probing and hand over, then did the build myself.
Its code failed on the first real build — 7 of its 9 tests failed. Not because the production code was wrong (it was correct), but because
logback-test.xmlsetsdev.ltms.fleettoWARNand the new lines log atINFO, so the test's appender captured an empty list. The member had copied theattach()helper fromMemberTrustModelReportTestwithout thesetLevel(INFO)its siblings do at each call site.That is the real cost of this bug. A member that cannot run anything reports "code-complete" in good faith, and it is not — there is no way for it to know. The three sonnet members tonight all found and fixed real problems during their own build runs. This one could not, and its work needed a fix before it could merge.
Merged in
2830735after I fixed the harness and proved the guard by mutation.Consequence, updated
Not "terra should not be given implementer work". Any member that reports this refusal should be told to stop and hand over immediately, and its work must be built by the lead before it is trusted. Read-only analysis is unaffected.
terra members cannot run shell commands: the classifier refuses every one, so they cannot build, test or open a PRto Members cannot run shell commands: the classifier refuses every one, so they cannot build, test or open a PR (3 members, 2 profiles)Root cause found, fixed and verified live on the Mac, 2026-09-10.
The axis was never time, and it was never the profile
My earlier comment said "compare time, not profile", and told the next person not to run the profile comparison. That was wrong, and the reason it was wrong is worth naming: I concluded "not profile-specific" from two profiles,
terraandgx. Both arekind: opencode. Two profile names looked like two independent data points and were one.The right experiment holds time constant and varies the backend kind. I spawned one
terraand onesonnetin the same minute with the same three-command brief:terra(kind: opencode) →classifier produced no valid verdict after 3 attempt(s):sonnet(kind: claude-code) → all three ran;datereturnedThu Sep 10 06:33:34 +07 2026Same host, same minute, opposite results. Every refusal on record — 2 terra, 1 gx, plus a fourth terra earlier today — is an opencode member. Every success is a Claude Code member.
Where the refusal is raised
Not in
fleetd. It is an opencode plugin:The plugin asks a model to approve every bash command. Claude Code members never load it, which is the whole asymmetry.
Why the model call failed
classifier.jssendsbody.modelfromOPENCODE_AUTOMODE_MODELwhen that variable is set. It is set on this host toopenai/gpt-5.4-mini. That model is refused outright by the account:So all three attempts failed and the plugin threw. Every command in every opencode member was refused, including
pwdanddate— which is exactly what the gx data point showed and what I could not explain at the time.Note the reported error was empty:
after 3 attempt(s):with nothing after the colon. The plugin catches the failure and keeps onlytext.slice(0, 200)of an empty body, so the one line that identifies this in five seconds is thrown away before anyone sees it. That reporting gap is why this took two days instead of ten minutes. It is the plugin's bug, not ours, and I am not filing it here.The fix, and why it is in our config
fleetd.yamlwas passing the broken pin straight into every member.memberCredentials.allowlistedOPENCODE_AUTOMODE_MODEL, so the env scrub kept it. Removed. The name is already on theknown:block list, so with the allow entry gone the scrub blanks it for members and the operator's own shell is untouched.With the variable absent, the plugin's own fallback resolves the model from the caller session — the member's working model — and the classifier answers normally.
This keeps the command gate on. It does not disable automode, and it does not weaken what a member is allowed to run. It only stops pointing the gate at a model the account will not serve.
fleetd.yamlis gitignored, so there is no commit. The key is hot-read per spawn, so no restart was needed.Verification
Same profile that failed, four minutes later, after the change:
The worktree path matches what
fleet_spawnassigned, so this is real output and not a member describing what it thinks would happen.grep -ac 'produced no valid verdict'against the daemon log since the change: 0.For fleet01
fleet01 runs its own
fleetd.yaml. If it also allowsOPENCODE_AUTOMODE_MODELthrough to members, and the host has that variable set to a model its account refuses, its opencode members are in the same state. Cheap check: spawn one opencode member, brief it to runpwd, and see whether it comes back with output or with the same refusal string. I have not measured fleet01.What this cost, restated
Four members across two profiles wrote correct-looking code they could never compile. Two of them said so plainly; the implementer brief's missing PR URL was the other signal. The consequence line from my earlier comment stands and is now explained rather than just observed: a member that reports this refusal should stop and hand over immediately, and its work must be built by the lead before it is trusted.
Confirmed on a full implementer job, not just a probe. When I closed this I had verified the fix with a short spawn that ran real shell commands. That left the real question open: can a
terramember now do a whole unit of work?It can. A
terramember took fleetd #384 end to end on 2026-09-10 and reported:It also ran a mutation on its own change, read the failure text, restored the code, committed, and pushed. I merged that work after checking it myself. Every one of those steps is a bash command that this ticket's defect refused for two days.
And the classifier is now doing its job rather than erroring. The same member hit a real denial:
That is the difference worth recording. Before the fix, the classifier could not produce a verdict at all and refused
pwdanddatealong with everything else. Now it allows ordinary commands and denies one that would put a token on the wire. The gate was never the problem — the model name was.The member stopped and said so instead of working around it, which is the correct behaviour. It pushed its branch and put its report in the reply instead of a PR body, so nothing was lost.
Nothing to reopen here. Recording it because "the refusal stopped" and "the member can do a day's work" are two different claims, and I had only measured the first.