fix(bin): sync upstream batch 20, restore Claude worker launches - #54
Merged
Merged
Conversation
…kunchenguid#4424) * fix(pr-merge): treat plan-gated 403 on branch rules as no merge queue (#42) * fix(pr-merge): read a plan-gated 403 on branch rules as no merge queue github_read_queue_method left status=unreadable for every failed rules read, including a 403 whose body is GitHub's own "Upgrade to GitHub Pro or make this repository public" message. A repository whose plan cannot expose branch rules cannot have a merge_queue rule either, so that specific 403 now resolves to status=none instead of unreadable - unblocking the away-merge grant on private repos without GitHub Pro. Any other failure (auth, rate limit, network, 404, unrelated 403) still reads as unreadable. * no-mistakes(document): Update stale away-merge queue-grant comment for plan-gated 403 --------- Co-authored-by: NewAiCoder <claude@theinbtw.com> * no-mistakes(review): Fix misleading away-queue-grant comment in fm-pr-merge and its test * no-mistakes(document): Update architecture.md for plan-gated-403 merge queue exception --------- Co-authored-by: NewAiCoder <claude@theinbtw.com>
…unchenguid#4246) * fix(tests): select readers of a changed top-level test fixture bin/fm-test-run.sh --changed recognised shared test helpers by an explicit list, tests/lib.sh|tests/*-helpers.sh|tests/fixtures.sh. A top-level tests/*-fixture.sh matched none of those, fell through to the tests/* catch-all, and was marked unmapped, so selection aborted with "no changed-test mapping for source path" and the run selected nothing at all. tests/herdr-client-pair-fixture.sh and tests/remote-herdr-fixture.sh are real shared fixtures with real consumers, so any branch touching one of them left a validation pipeline driving --changed with a hard abort rather than a narrowed selection. Extend the helper arm to tests/*-fixture.sh rather than routing it through the tests/fixtures/*/* arm. Both arms resolve consumers with the same reference scan, and that scan is what selects the right suites here: it finds exactly the tests that read the fixture. The fixtures/ arm adds only a directory-keying step, which has nothing to key on for a top-level file, so the helper arm is the same behaviour with no extra machinery. A tests/ path nothing reads still reaches the catch-all and still refuses loudly. Refs kunchenguid#4100 * no-mistakes(test): order nested fixtures arm before top-level fixture glob * no-mistakes(document): document tests/ shared-file mapping contract and arm order * no-mistakes(review): drop vacuous test phase, correct header claim, restore comment
… asked, not declined (kunchenguid#4387) * fix(bin): read Claude Code's default external-imports flags as never asked, not declined (kunchenguid#4378) fm-claude-trust.sh refused the whole trust registration whenever the project-root entry carried hasClaudeMdExternalIncludesApproved === false, on the premise that Claude Code writes that value only on an explicit "No, disable". Claude Code's default project entry carries Approved and WarningShown both false before the dialog is ever shown, so every such project refused every spawn. Only Approved === false with WarningShown === true — the pair the dialog writes on a decline — now counts as a decline. false/false behaves like an absent flag: trust is registered and no import consent is manufactured. New case test_project_root_entry_default_import_flags_are_not_a_decline fails on b182d0f with the refusal and passes with the fix; tests/fm-claude-trust.test.sh 31/31, bin/fm-lint.sh clean with pinned ShellCheck 0.11.0 and actionlint 1.7.12. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * no-mistakes(review): Correct harness doc's external-imports decline predicate --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…chenguid#4445) * fix(brief): keep operator address out of composed intent Teach raw-word authoring for intent sections and mid-task relays, with a neutral [captain] provenance marker for legacy mixed tasks. Keep headings and contract prose outside the serialized intent body. The legacy selector already excluded the old speaker labels from its output; preserve that read compatibility. The reproduced leak comes from adding labels inside a modern intent body, not from the legacy selector. Do not scrub actual request content. Add exact serialized-input and generated-contract regressions, retaining refusal of unmarked legacy tasks and coverage of scout promotion. Fixes kunchenguid#3882 * no-mistakes(review): Refuse operator-address lines in Captain's intent body * no-mistakes(document): Document operator-address refusal in intent contract comments
…as a proven empty composer (kunchenguid#4455) * fix(composer): accept Grok title overhang * no-mistakes(review): summary: named Grok overhang constant, doc caveat, restored tmux typed-title coverage
…ailure (kunchenguid#4474) * fix(bin): recover Claude auto-arm after timeout * no-mistakes(document): Add host-timeout signal coverage to autoarm test-coverage list
* fix(spawn): establish Claude task channel authority * no-mistakes(document): Document Claude task-worker control-channel trust in harness-adapters reference
…or pending text (kunchenguid#4458) * fix: guard relaunch exit against pending input * no-mistakes(review): Verifying test run in progress * no-mistakes(document): docs(agent-control): document exit's composer-empty fail-safe guard * no-mistakes(ci): fixed 2 tests broken by approved do_exit fail-safe change (empty-only composer gate). herdr-smoke test's sleep-stand-in never renders a real composer -> updated assertion to expect "not proven empty" refusal instead of stale "did not stop" msg. secondmate-restart fake tmux capture-pane returned bare '> ' glyph (never valid empty proof) -> changed to bordered empty box matching fm-control-relaunch fixture. all 4 related suites pass locally now
…unchenguid#4460) * fix: reconcile diverged secondmate updates * no-mistakes(document): Fix stale fm-update.sh/fm-ff-lib.sh purpose lines in docs/scripts.md * no-mistakes(document): docs: reflect secondmate divergence reconcile in README/SKILL.md
…d#4497) * fix(dispatch): support Codex Luna max effort * no-mistakes(review): use portable CODEX_HOME path in codex effort reference
kunchenguid#4498) * feat(calm): render smooth Unicode swell * feat(calm): make sails asymmetric * feat(calm): use quarter sail glyph * no-mistakes(review): docs: sync calm feasibility sprite passage with approved renderer * no-mistakes(document): docs: sync calm wave phase doc comment * no-mistakes(ci): CI の Lint 失敗は tests/fm-calm-pi-extension.test.sh の test_interactive_terminal_e2e 関数で `boat_narrow_sails` が local 宣言に残っていたことによる ShellCheck SC2034 でした。関数内での参照を確認したところ、狭幅端末の検査は boat_narrow_previous / boat_narrow_direction / boat_narrow_reversed に移行済みで、boat_narrow_sails は代入も参照も一切ありませんでした。そのため local 宣言からこの 1 語のみを削除しました(3315 行目)。Calm の描画実装、他のテストアサーション、ドキュメントは変更していません。検証: bin/fm-lint.sh(ローカル変更ファイルモード)exit 0、CI 相当の `shellcheck --norc --external-sources tests/fm-calm-pi-extension.test.sh` exit 0(SC2034 解消)、`bash -n` 構文チェック通過、actionlint 1.7.12 でワークフロー 3 件 valid。
kunchenguid#4491) * fix: supersede scout delivery brief on promotion * fix: preserve ship safety contract after promotion * no-mistakes(document): Document fm-promote.sh now supersedes brief.md on relaunch
Brings the fork up to date with 14 upstream commits (46 files, +1439/-369) from batch 19's waypoint b182d0f to aa92177 (fix(bin): supersede stale scout delivery text in brief.md on promotion, kunchenguid#4491). Includes upstream kunchenguid#4387 (treat Claude Code's default external-imports flags as never asked, not declined), the fix for Claude worker launches on this Mac. Note: upstream/main actually moved one commit further, to b85e28b (kunchenguid#4471), by the time this batch merged. Per captain decision, the batch scope stays fixed at the originally prepped waypoint aa92177; kunchenguid#4471 is deferred to batch 21, not included here. Four files conflicted; each resolved per the captain's stated conflict rule (take upstream unless the fork's approach is better, in which case keep the fork and file a PR - not needed here since every fork-only line is additive, not a competing fix for the same problem upstream also fixed). 1) bin/fm-brief.sh - UNION. Upstream moved RULE1 construction into the new fm_ship_rule_one() helper and simplified the DOD call site. The fork's fm_dod_block() still takes 4 args (mode, id, data, fm_root); took upstream's call shape but kept passing the fork's extra two args since the merged fm-dod-lib.sh (auto-merged, no conflict) still declares and uses them: RULE1=$(fm_ship_rule_one "$MODE" "$ID") || exit 1 DOD=$(fm_dod_block "$MODE" "$ID" "$DATA" "$FM_ROOT") || exit 1 Also kept the fork's per-mode DONE_SIGNAL assignments (untouched by upstream's diff, still consumed later in the same file at the "is never any other event" status-line text). 2) bin/fm-promote.sh - TAKE UPSTREAM, with the same DOD-arg threading as (1). Took upstream's promote_delivery_contract() call in place of the fork's inline fm_dod_block call: promote_delivery_contract and updated promote_delivery_contract()'s own internal call so it still passes the fork's extra args through to fm_dod_block: fm_dod_block "$MODE" "$ID" "$DATA" "$FM_ROOT" 3) docs/configuration.md - TAKE UPSTREAM, then re-append a fork-only sentence. Took upstream's two sentences (Codex `max` support for gpt-5.6-luna; the plain omitted-axis-default sentence), then re-appended the fork's SPAWN-command-line sentence because bin/fm-spawn.sh (merged tree) still requires the literal `default` for --model/--effort when config/crew-dispatch.json exists (confirmed: bin/fm-spawn.sh:1192,1196,1484,1488 error text and the `!= default` checks at lines 1531/1563/1891/2065/2075): Codex `max` is valid when the profile selects `gpt-5.6-luna`, whose installed catalog entry supports that reasoning level. An omitted model or effort means the selected harness uses its own default for that axis. The SPAWN command line may not: once `config/crew-dispatch.json` exists, firstmate must still pass `--model` and `--effort` explicitly, using the literal value `default` to carry a profile's own omission forward, so the decision to skip an axis is always a deliberate, visible choice rather than a silently inherited one. 4) docs/scripts.md - UNION. Took upstream's rewritten fm-promote.sh and fm-teardown.sh table rows (matching the fm-promote.sh code change in (2) and upstream's own independent teardown wording), and kept the fork-only fm-static-guard-lib.sh and fm-main-guard.sh rows since those two scripts exist only in the fork (confirmed: `git show aa92177:bin/fm-static-guard-lib.sh` and `bin/fm-main-guard.sh` both fail to resolve upstream): | `fm-static-guard-lib.sh` | Discover a project's own pinned static check and run it against one git tree, for both merge-time and post-merge guards | | `fm-main-guard.sh` | Arm, poll, and retire the registered check that reports a red default-branch tip | | `fm-promote.sh` | ...supersede the task's brief so a later relaunch cannot revive stale scout delivery text | | `fm-teardown.sh` | ...require completed scout deliverables, retire secondmate homes | Verification performed on the merged tree before this commit: - `git merge-tree --write-tree origin/main aa92177` predicted these same 4 conflicts before branching; no other file conflicted. - Silent-splice check: every non-blank line upstream added to every file the fork also touched since b182d0f is present in the merged tree, with exactly one deliberate exception (bin/fm-promote.sh's bare `fm_dod_block "$MODE" "$ID"` call, superseded by the 4-arg call documented in (2) above). - `git diff aa92177 -- .github/workflows/` against the staged tree is empty. - Fork non-merge commits ahead of the fixed waypoint aa92177: 97 before this merge (origin/main) and unchanged after (this branch, excluding the merge commit itself). - No new or renamed tests/*.test.sh files exist in this upstream range (all 14 touched test files are modifications), so the source-guard `|| exit 1` sweep found nothing new to check. - `bash -n` on all touched bin/ scripts: OK. Upstream PR candidates: none.
…pdate wording drift
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
Captain's standing policy for this fork (2026-09-05, verbatim): "the main goal for firstmate is to become sync with upstream. once we reach that state, we avoid introducing more changes and stay aligned with the upstream. until then you can keep adding work that helps aligning with the upstream easier and reaches the intended state quickly."
Captain's conflict rule (2026-09-05, verbatim): "if there is something upstream and our code both worked on and fixed but approaches differ, take the upstream change, however, if our change is better than upstream, file a PR. for other items, take the upstream changes."
Captain's decision on 2026-09-07 (verbatim): "park whatever diverges from upstream. our goal was to sync with upstream for firstmate. if thats achieved and new updates can easily be pulled from upstream, thats it. no further work on firstmate." Sync batches are the only firstmate work; no fork-side improvements, no new upstream PR candidates unless a measured defect forces one, and even then only recorded, never filed.
Captain's cadence rule: a sync batch every fortnight or every ten accumulated upstream commits, whichever comes first. Batch 19 landed 2026-09-15 as #53 at waypoint b182d0f; 14 upstream commits have accumulated since. Captain 2026-09-15: this batch runs on the cheaper tier with a bounded test plan and serialized lint.
The ask this task serves: upstream sync batch 20 - bring the fork's origin/main (2722456, batch 19 landed) up to upstream/main at waypoint aa92177 (14 commits, 46 files, +1439/-369) as ONE merge commit that keeps upstream's commit identities in ancestry. Among them kunchenguid#4387 "treat Claude Code's default external-imports flags as never asked, not declined" is the fix for the fork's current inability to launch Claude workers on this Mac.
Bounded test plan for this run's test step (host memory is the constraint): run only these targeted suites through bin/fm-test-run.sh, one at a time, never the full suite, and never run bin/fm-lint.sh inside the test step (lint already ran serialized on the host outside this pipeline): tests/fm-brief.test.sh, tests/fm-promote.test.sh, tests/fm-dod-lib.test.sh (if present), tests/fm-spawn.test.sh, tests/fm-claude-trust.test.sh (or the suite covering bin/fm-claude-trust.sh), tests/fm-control.test.sh, tests/fm-composer-lib.test.sh, tests/fm-claude-stop-autoarm.test.sh, tests/fm-turnend-guard.test.sh, tests/fm-pr-merge.test.sh, tests/fm-static-guard.test.sh, tests/fm-merge-waypoint-guard.test.sh, tests/fm-update.test.sh, tests/fm-secondmate-*.test.sh touched by kunchenguid#4460, tests/fm-test-run.test.sh, tests/fm-test-fixture-cleanup.test.sh. CI on ubuntu is the full oracle for broad regression coverage.
Known host-only failures to report and not edit (do not fix these; they are environment artifacts, not regressions from this batch): tests/fm-remote-doctor.test.sh and tests/fm-afk-return.test.sh (stock bash 3.2), tests/fm-secondmate-liveness.test.sh (herdr installed on host), the fm-cursor-harness and fm-harness-precedence copies-of-platform-binaries cases.
What Changed
bin/fm-claude-stop-autoarm.sh(HUP/TERM/INT signal handling during autoarm) with its test suite, and pulled in upstream fixes acrossfm-spawn.sh,fm-control.sh,fm-composer-lib.sh,fm-promote.sh,fm-pr-merge.sh,fm-update.sh, andfm-test-run.sh(crewmate identity ordering, secondmate divergence reconciliation, plan-gated 403 handling, stale scout-brief text supersession, Grok/OpenCode composer classification, shared fixture selection)AGENTS.md,docs/, harness-adapter and provisioning skills) and expanded/added coverage across ~20 test files to match the merged behavior, plus a follow-up wording fix inupdatefirstmate/SKILL.mdcorrecting "fast-forward" to "update" terminology driftRisk Assessment
✅ Low: This is a large (14-commit) upstream sync merge, but the merge commit's own detailed verification claims (single merge commit preserving upstream identities, exactly 4 documented conflicts each resolved per the captain's stated rule, a "silent-splice" no-dropped-lines proof) all checked out byte-for-byte against direct diffs I ran myself; independent deep review of all 14 non-merge commits (one pass by me directly on the highest-risk paths - Claude trust-flag classification, fm-control.sh exit guard, fm-ff-lib.sh's reset --keep reconciliation safety proof, fm-pr-merge.sh's 403 classification, Grok composer-border detection - plus two parallel subagent passes covering every remaining commit) found no functional bugs, no security issues, and no intent contradictions; the only substantiated issue is a single minor test-quality nit in a newly-added test assertion.
Testing
Ran the full bounded test plan (fm-test-run.sh invocations one at a time) against the sync-batch-20 merge commit bb4f9c0; every suite passed with zero failures, and the specific tests that exercise this batch's headline fixes (kunchenguid#4387 Claude Code external-imports flags, kunchenguid#4491 brief supersession, kunchenguid#4460 secondmate divergence reconciliation, kunchenguid#4424 plan-gated 403, kunchenguid#4474 auto-arm signal handling, kunchenguid#4246 shared-fixture selection) all pass, plus the merge-waypoint guard's own ancestry checks pass and the merge commit's git history itself confirms proper two-parent ancestry preserving upstream commit identities. tests/fm-promote.test.sh and tests/fm-spawn.test.sh do not exist under those literal filenames in this repo; I substituted the actually-existing suites that cover bin/fm-promote.sh and bin/fm-spawn.sh (fm-control-relaunch.test.sh, fm-spawn-dispatch-profile.test.sh, fm-task-delivery.test.sh), all passing. No UI/visual surface exists for this backend CLI/shell-script merge, so no screenshot/GIF artifacts apply; behavioral shell-test pass/fail against the real executables (fm-spawn.sh, fm-promote.sh, fm-update.sh, fm-pr-merge.sh, fm-claude-trust.sh, fm-claude-stop-autoarm.sh) is the correct evidence surface here. Working tree is clean with no transient test artifacts left behind. The round-1-declined AGENTS.md-line-ordering assertion in fm-task-delivery.test.sh was left untouched per the recorded decision and still passes.
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
⏭️ **Rebase** - skipped
Step was skipped.
tests/fm-task-delivery.test.sh:875- tests/fm-task-delivery.test.sh:875-878 (added by upstream commit c806c6a, part of this merge) asserts purely by grepping the real source AGENTS.md file for a specific sentence and comparing its line number to another sentence's line number:role_line=$(grep -n 'A ship or scout worker launched by Firstmate into a worktree of this repository' "$ROOT/AGENTS.md" | cut -d: -f1)/supervisor_line=$(grep -n '^You are the first mate\.$' "$ROOT/AGENTS.md" | ...)/[ -n "$role_line" ] && [ "$role_line" -lt "$supervisor_line" ] || fail .... This is a natural-language prompt document (AGENTS.md is read by an LLM, not by any of the shell scripts under test), and the assertion's only evidence is text position in that source file - it does not exercise fm-spawn.sh, fm-promote.sh, or any other executable path, and does not prove any agent actually behaves differently. This is distinct from the rest of the same test (and sibling tests in tests/fm-spawn-dispatch-profile.test.sh and tests/fm-control-relaunch.test.sh added by the same commit), which correctly assert against generated launch-brief/prompt artifacts produced by actually running fm-spawn.sh/fm-promote.sh - those are legitimate. Per the test-quality rule, this specific added assertion should be removed or refined to check the generated worker-facing artifact instead of the source doc.✅ **Test** - passed
✅ No issues found.
git log --merges -1 bb4f9c0 --format="%H %P" (confirms 2-parent merge: 272245619ad9...+aa921774fb13...)bin/fm-test-run.sh tests/fm-brief.test.sh (26/26 pass)bin/fm-test-run.sh tests/fm-control-relaunch.test.sh (36/36 pass, incl. 'fm-promote/fm-spawn --relaunch: the current ship contract supersedes stale scout delivery text' exercising #4491) - closest existing equivalent to the plan's tests/fm-promote.test.sh, which does not exist under that literal filenamebin/fm-test-run.sh tests/fm-spawn-dispatch-profile.test.sh (pass) - closest existing equivalent to the plan's tests/fm-spawn.test.sh, which does not exist under that literal filenamebin/fm-test-run.sh tests/fm-task-delivery.test.sh (12/12 pass, incl. the AGENTS.md worker-role-ordering assertion covered by the round-1 declined finding - still passes, no regression)bin/fm-test-run.sh tests/fm-claude-trust.test.sh (31/31 pass, incl. 'a never-asked default external-imports pair is not treated as a decline' exercising #4387)bin/fm-test-run.sh tests/fm-control.test.sh (34/34 pass)bin/fm-test-run.sh tests/fm-composer-lib.test.sh (14/14 pass)bin/fm-test-run.sh tests/fm-claude-stop-autoarm.test.sh (25/25 pass, incl. 'TERM mid-arm commits a durable failure and exits 2 for rewake' exercising #4474)bin/fm-test-run.sh tests/fm-turnend-guard.test.sh (26/26 pass)bin/fm-test-run.sh tests/fm-pr-merge.test.sh (33/33 pass, incl. 'away merge proceeds on a plan-gated 403' exercising #4424)bin/fm-test-run.sh tests/fm-static-guard.test.sh (17/17 pass)bin/fm-test-run.sh tests/fm-merge-waypoint-guard.test.sh (20/20 pass, directly guards this merge's ancestry shape)bin/fm-test-run.sh tests/fm-update.test.sh (16/16 pass, incl. 'T5 diverged secondmate is preserved and durably actionable' and 'T5b squash-merged divergence heals' exercising #4460)bin/fm-test-run.sh tests/fm-test-run.test.sh (34/34 pass, incl. 'a changed shared test fixture selects its readers' exercising #4246)bin/fm-test-run.sh tests/fm-test-fixture-cleanup.test.sh (13/13 pass)git show d499323 --stat to confirm #4460 touched only tests/fm-update.test.sh among test files (no other fm-secondmate-*.test.sh needed)git status --porcelain -uall (clean, no transient artifacts left behind)✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.