Repository navigation
fix(bin): treat a never-asked external-imports prompt as not a decline - #4
Merged
rub-a-dub-dub merged 3 commits intoSep 19, 2026
Conversation
…line hasClaudeMdExternalIncludesApproved===false is also Claude Code's own pre-dialog default, not only the result of an explicit "No, disable" answer, so a project entry that was never asked read as a permanent decline and blocked every Claude worker for that project. Disassembly of the installed claude binary confirms hasClaudeMdExternalIncludesWarningShown is set true only when the dialog actually rendered and was answered, so that flag is what now gates the decline check.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Firstmate's own defect, found and filed by firstmate on 2026-09-16 while two of the captain's tasks were stranded by it.
WHAT BREAKS: firstmate refuses to launch any Claude worker for a project whose Claude config records the external-imports prompt as not approved - but it cannot tell "the captain declined" from "the captain was never asked". Those are different, and the difference is recorded.
WHAT IT COST, concretely: on 2026-09-17 two finished firstmate tasks could not be moved to Claude for an entire evening while their other runtime was out of allowance. The captain could not clear it by answering the prompt either, because that project's own instructions never trigger the dialog - his words, "It never ased me the second q". He eventually had to edit his own config by hand. The gate was unclearable by the very action it implicitly demanded.
What Changed
bin/fm-claude-trust.shnow requireshasClaudeMdExternalIncludesApproved===falseandhasClaudeMdExternalIncludesWarningShown===truebefore treating a project entry as an explicit "No, disable" answer.approved===falseon its own is Claude Code's pre-dialog default, so a never-asked project now falls through to the normal untouched-imports path and registers trust instead of refusing the whole registration.declinedExternalImportscomment — were rewritten to record the two-flag contract and the claude 2.1.278 disassembly evidence that every post-default write of the approved flag also setswarningShown===true.tests/fm-claude-trust.test.shadds two cases covering the never-asked shapes (warningShownexplicitlyfalse, and absent entirely): both must exit 0, land trust without import consent on the worktree and project-root entries, and preserve the project entry's unrelated settings. The existing decline test's comment was updated to state the pairing it relies on..agents/skills/harness-adapters/references/harness/claude.mdwas synced to the same rule.Risk Assessment
✅ Low: The functional change is a single two-line narrowing of one predicate, its vendor-behavior premises were independently confirmed against the installed claude binary, the consent-safety invariant is preserved by a separate
approved===truecheck, and two new behavioral regression tests cover both never-asked shapes.CI
This PR shipped without a CI gate. GitHub Actions has never been activated on this fork (
rub-a-dub-dub/firstmate), so no check run was ever registered here -gh pr viewreports "0 passed, 0 failed — this PR has no CI checks configured". Every step through Push (intent, rebase, review, test, document, lint, push) completed and passed; the pipeline's CI step was cancelled after it waited for checks that can never arrive on this fork. This is a known, already-filed gap unrelated to this change - two other PRs shipped ungated today for the same reason.Testing
Ran the targeted claude-trust suite and the harness-adapter reference test (both pass), then produced the real evidence by driving bin/fm-spawn.sh's Claude launch path over four project-config shapes against both the base commit and the fixed commit: before the fix a never-asked project entry is refused as a decline and no worker is launched (the captain's stranded-task symptom), after the fix the worker launches with its brief while the project entry's import flags stay untouched, and a genuine decline plus a prior explicit approval both still behave as before. One demo run initially failed on an unrelated leftover /tmp/fm-<id> task temp root from an earlier run; re-running with run-unique task ids was clean and reproducible. The edited harness reference doc is agent-facing prose with no rendered end-user surface, so no visual artifact applies to it. Temporary trees and task temp roots created during testing were removed and the worktree is clean.
Evidence: CLI transcript: fm-spawn claude launch before vs after the fix, four consent shapes
Source: CLI transcript: fm-spawn claude launch before vs after the fix, four consent shapes
Evidence: Reproduction script that generated the transcript
Source: Reproduction script that generated the transcript
Evidence: Key contrast (never-asked project entry)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 1 issue found → auto-fixed ✅
.agents/skills/harness-adapters/references/harness/claude.md:28- The agent-facing harness reference still states the pre-fix decline rule and now contradicts the code. Line 28 reads "When the project entry instead already carries an explicit decline (===false), the whole registration refuses - including the trust flag", and line 33 repeats it as a diagnostic ("A visible trust dialog means pre-registration did not take effect (or the project entry already carries an explicit decline)"). After bin/fm-claude-trust.sh:473-474,hasClaudeMdExternalIncludesApproved===falsealone no longer refuses; only that value paired withhasClaudeMdExternalIncludesWarningShown===truedoes. Concrete misdiagnosis path: AGENTS.md:211 mandates loadingharness-adapters"before trust handling", so an agent inspecting a store whose project entry holds{"hasClaudeMdExternalIncludesApproved":false,"hasClaudeMdExternalIncludesWarningShown":false}after a wedged pane will follow line 28 and conclude the registration refused on a decline, when the script in fact registered trust successfully and the pane is wedged on the separate external-imports dialog - the exact two states this change exists to tell apart. Remedy is a mechanical sync of those two sentences to name the approved===false + warningShown===true pairing as the decline; it corrects what the change already does rather than extending it.🔧 Fix: sync claude harness reference with never-asked decline rule
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
bash tests/fm-claude-trust.test.sh(32 checks, including the two new never-asked cases)bash tests/fm-harness-adapter-references.test.sh(routing artifact + edited harness/claude.md reference reachable)Manual end-to-end spawn demo throughbin/fm-spawn.sh(suite's standard tmux/claude fakes) over four config shapes — never-asked, never-asked with warningShown absent, genuine decline, prior approval — run against both the base tree (git archive 6f4c112) and the worktree under test:never-asked-spawn-demo.sh <tree> <label> <run-tag>✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.