Skip to content

feat(bin): sync fork with upstream firstmate main - #3

Merged
remicaesar merged 6 commits into
mainfrom
fm/fm-upstream-sync-s2
Oct 6, 2026
Merged

remicaesar merged 6 commits into
mainfrom
fm/fm-upstream-sync-s2

Conversation

@remicaesar

@remicaesar remicaesar commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Intent

sync my fork with upstream

Context: the fork remicaesar/firstmate (remote origin) is at 3e1ee94, 5 commits behind upstream kunchenguid/firstmate (remote upstream, main at e5b9ddd) and 6 commits ahead with fork-only fixes (a macOS Bash 3.2 fix for fm_exec_timed, a supervision-engine test link, and an armed-PR-poll readability fix). The 5 upstream commits are: e5b9ddd worker-role skill-file fallback (kunchenguid#6647), 6a7b391 start tasks from a named base branch (kunchenguid#6442), 98d80a1 opt-in no-mistakes spend recording at teardown (kunchenguid#5354), f470a01 faster remote-job sequence claim cleanup (kunchenguid#6575), 9ea0c41 prevent unnecessary remote worker turnover (kunchenguid#6431). The previous sync (#2) found an upstream change that broke on this Mac's stock /bin/bash 3.2.57, so the running fleet stays on the current main until the synced tree is proven.

What Changed

  • Merges 5 upstream commits into the fork. The fork's existing fixes stay as they were.
  • Base branch: fm-brief.sh --base-branch and fm-spawn.sh can now start a task from a named base branch. The spawn resets the pooled copy to origin/<base> and records base_branch= in the task meta. The worker opens its PR against that branch, and review diffs, cleanup and scout promotion read the recorded base. Deliveries that are local-only or go to Gerrit refuse a named base.
  • Spend recording: a new script, bin/fm-pipeline-spend.sh, records each ship task's no-mistakes pipeline spend at teardown. It is opt-in: it only runs when config/pipeline-spend exists, and it writes to data/pipeline-spend.jsonl. fm_nm_state_db is now the only place that decides where the no-mistakes state database lives.
  • Remote jobs: the remote-job worker and its LaunchAgent no longer replace a healthy worker; LaunchAgent repair is serialized, and the worker checks owners that launchd is tracking. Expired .seq-claims directories are cleaned up in a single find walk.
  • Other fixes: test fixture registries are kept out of git worktree roots. The worker role names the Firstmate skill file as a fallback.
  • Adds tests and docs for each of these changes.

🤖 Generated with Claude Code

Risk Assessment

⚠️ Medium: The merge is clean, but it brings in about 2,400 lines of upstream shell code, including changes to the LaunchAgent remote-worker lifecycle and to spawn and teardown. The intent says this code has to be proven on stock /bin/bash 3.2.57 before the fleet moves to it, and that runtime proof belongs to the later test step.

Testing

I ran every test file the upstream sync touches, plus the fork's fm_exec_timed regression test, under the Mac's stock /bin/bash 3.2.57. That shell is the one that broke the previous sync. All 13 files passed. They cover the five upstream changes: the worker-role skill fallback in brief, named-base-branch spawn and review-diff, opt-in pipeline-spend recording at teardown, faster remote-job claim cleanup, and the remote worker turnover fix. The fork's Bash 3.2 fix still holds after the merge. I also ran the new fm-pipeline-spend.sh command by hand under Bash 3.2. Its usage output and exit codes were correct, and with recording disabled it exits 0. There is no UI surface, so there are no screenshots.

Evidence: Bash 3.2 results for each changed test file (all 13 pass)

Source: Bash 3.2 results for each changed test file (all 13 pass)

== fm-brief rc=0 (13s) ok - fm-brief.sh: every crewmate scaffold forbids administering the shared worktree pool
== fm-dod-lib rc=0 (5s) all fm-dod-lib tests passed
== fm-pipeline-spend rc=0 (4s) ok - missing tasks, secondmates, and unsafe ids are refused
== fm-remote-job-claim-reap rc=0 (5s) ok - a due claim sweep over ~17460 dirs does not starve job serving
== fm-remote-job-claim-retention rc=0 (0s) ok - claim sweep preserves numeric-name eligibility and whole-second expiry
== fm-remote-job-launchagent rc=0 (90s) ALL TESTS PASSED
== fm-remote-job rc=0 (153s) ALL TESTS PASSED
== fm-review-diff rc=0 (4s) ok - fm-review-diff compares a task against its recorded base branch
== fm-spawn-dispatch-profile rc=0 (300s) # all fm-spawn-dispatch-profile tests passed
== fm-spawn-pool-base-freshen rc=0 (107s) # all fm-spawn-pool-base-freshen tests passed
== fm-teardown rc=0 (280s) ok - the run abort and the leaked-process reap both complete before the destructive worktree return
== fm-test-fixture-cleanup rc=0 (1s) ok - test registries and fixture roots stay out of a git worktree TMPDIR
== fm-timeout-lib rc=0 (11s) ok - fm_timed_out accepts 124 and 137 and nothing else
Evidence: fm-pipeline-spend.sh CLI run under /bin/bash 3.2

Source: fm-pipeline-spend.sh CLI run under /bin/bash 3.2

$ /bin/bash --version | head -1
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin25)
$ /bin/bash bin/fm-pipeline-spend.sh
fm-pipeline-spend.sh - attribute a task's no-mistakes pipeline spend to the
task and keep it in Firstmate's own records.

Usage:
  fm-pipeline-spend.sh record <task-id>

record appends the task's pipeline spend as one JSON object on one line of
data/pipeline-spend.jsonl, at most once per task incarnation (task id plus
the record's spawn_gen): repeating it for an incarnation already in the
ledger appends nothing, so a retried cleanup never counts a task twice.
Recording is disabled unless config/pipeline-spend is present; in that case
this command exits before reading task metadata, no-mistakes state, or ledger.
When enabled, bin/fm-teardown.sh calls record for every ship task whose
local copy it cleans up, before it deletes the task branch this script
attributes runs by and before it removes state/<id>.meta. The ledger is
private and gitignored with the rest of data/.
Exit status: 0 when a record was recorded or already present, its source is
unavailable, or recording is disabled; 1 when the task record is missing,
names a secondmate, or the record could not be built or written; 2 for bad usage.

Source. no-mistakes keeps each agent invocation's token usage only in its
local state database, one agent_invocations row per invocation; its
environment reference documents those fields, and `no-mistakes stats --run
<id>` renders the same rows as a human table. There is no machine-readable
export yet, so this script reads the database read-only (mode=ro), located
by bin/fm-nm-run-lib.sh's fm_nm_state_db, and bounded by 30 seconds per
no-mistakes or database call.

Attribution. A task's runs are the runs no-mistakes recorded for the task
copy's repository and current branch since that branch was created:
  - repository: the `repo:` line `no-mistakes axi` prints from the task copy,
    which is the CLI's own resolution (a pooled worker copy resolves to the
    registered primary clone), matched exactly against repos.working_path;
  - branch: the task copy's current branch, the one bin/fm-crew-state.sh
    reads;
  - since: the oldest surviving reflog entry of that branch. spawn_gen cannot
    bound the task, because a relaunch mints a new one while the same branch
    keeps validating. Teardown deletes the branch, so a later task that
    reuses the id and branch name starts a fresh reflog and never inherits an
    earlier task's runs. With no reflog, every run on the branch counts and
    since is null.
Two live tasks sharing one branch name in one repository would both count
its runs; each record lists run ids, so such an overlap stays visible.

Counting. Every invocation of those runs counts, whatever its exit status
(ok, error, cancelled), and each token field is no-mistakes' own, summed
without reinterpretation (whether input includes cache reads differs by
agent; see no-mistakes' environment reference):
  - input_tokens, output_tokens, cache_read_tokens sum the per-round
    delta_* columns, because a resumed session's raw counters are cumulative
    for some agents (codex) and summing them would count earlier review
    rounds again. A row with no delta (written before the delta columns
    existed) falls back to its raw counter only when its session_mode is not
    `resumed`, since no-mistakes defines a cold, started, or fallback row's
    delta as its raw counter.
  - cache_creation_tokens sums the raw counter, which has no per-round
    delta, so it counts only for a row whose counters are proven
    per-invocation: not resumed, or every delta equal to its raw counter.
  - reasoning is not summed: no-mistakes counts it inside output and keeps no
    per-round delta for it.
A value no-mistakes did not record is unknown, never zero: each token field
carries its known total and how many invocations were unknown, so a total
with unknown > 0 is a lower bound.

Record schema (this header is its one owner). One JSON object:
  task, spawn_gen         the task id and the incarnation recorded in its meta
  recorded_at             UTC time the record was built
  source                  "no-mistakes-state", or "unavailable" when the runs
                          could not be read; reason then says why, total
                          is null, and runs and purposes are empty
  repo, branch, since     the attribution above (since as UTC time or null)
  total                   a tally over every counted invocation
  runs[]                  {id, status, created_at} plus a tally, oldest first
  purposes[]              {purpose} plus a tally, by no-mistakes' purpose
                          name (review, review-fix, test, document, ci, ...)
A tally is {invocations, exit: {<exit status>: count}, duration_ms,
input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens}, and
each token field is {total, unknown}.
exit=2
$ /bin/bash bin/fm-pipeline-spend.sh record demo-task   # recording disabled (no config/pipeline-spend)
exit=0
$ /bin/bash bin/fm-spawn.sh t1 /tmp --relaunch --base-branch dev 2>&1 | head -3
error: no-mistakes gate agent must not drive the fleet (NO_MISTAKES_GATE set)

Full-suite results on stock macOS Bash 3.2.57

All 248 test scripts were run locally under /bin/bash 3.2.57 (the only bash on the host), sharded as CI does: portable-parallel-1, portable-parallel-2, portable-serial and real-herdr-gated.
A /bin/bash -n parse sweep of all 458 shell scripts found no failures, and bin/fm-lint.sh exits 0 once the pinned actionlint 1.7.12 is installed.
The incoming diff was also audited for Bash 4+ constructs; none needed a fix.

Nine scripts failed in the lane runs.
Three passed when re-run after the cause was removed: fm-lint-workflows needed actionlint, while fm-remote-reply and fm-spawn-compact-adviser-disable-remote were timing flakes.
Six failures are pre-existing: each fails with the same assertion on unmodified fork main 3e1ee94 and on upstream main e5b9ddd, so the merge introduced none of them.

Test Failing assertion Likely cause
fm-afk-return evidence publication failure should retain catch-up pre-existing on both baselines
fm-cursor-harness a real cursor-agent ancestor must detect cursor process-ancestry detection on this host
fm-harness-precedence codex ancestry alone resolved '' process-ancestry detection on this host
fm-muse-harness fm-harness.sh under a muse process reported '' process-ancestry detection on this host
fm-gate-refuse tmux is required for the lab socket behavioral test tmux not installed
fm-procevent could not read the draining holder's identity pre-existing on both baselines

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - medium risk

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • /bin/bash tests/fm-brief.test.sh (pass)
  • /bin/bash tests/fm-dod-lib.test.sh (pass)
  • /bin/bash tests/fm-pipeline-spend.test.sh (pass)
  • /bin/bash tests/fm-remote-job-claim-reap.test.sh (pass)
  • /bin/bash tests/fm-remote-job-claim-retention.test.sh (pass)
  • /bin/bash tests/fm-remote-job-launchagent.test.sh (pass)
  • /bin/bash tests/fm-remote-job.test.sh (pass)
  • /bin/bash tests/fm-review-diff.test.sh (pass)
  • /bin/bash tests/fm-spawn-dispatch-profile.test.sh (pass)
  • /bin/bash tests/fm-spawn-pool-base-freshen.test.sh (pass)
  • /bin/bash tests/fm-teardown.test.sh (pass)
  • /bin/bash tests/fm-test-fixture-cleanup.test.sh (pass)
  • /bin/bash tests/fm-timeout-lib.test.sh (pass; the fork's fm_exec_timed Bash 3.2 fix)
  • Ran the CLI by hand under /bin/bash 3.2: bin/fm-pipeline-spend.sh with no arguments prints its usage text and exits 2; fm-pipeline-spend.sh record demo-task with recording disabled exits 0
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 1)
✅ **Push** - passed

✅ No issues found.

kunchenguid and others added 6 commits October 4, 2026 10:47
* fix: prevent healthy remote job worker turnover

* no-mistakes(review): Serialize full LaunchAgent repair and verify launchd-tracked owners

* no-mistakes(review): Let launchd-tracked unpublished spawns start before reloading

* no-mistakes(document): Document remote worker heartbeat and serialized LaunchAgent recovery

* no-mistakes(ci): Full CI log showed the idle-worker regression exceeded its outdated command budget (82 versus 80) after independent heartbeat ownership checks were added. Raised the budget to 120 while retaining the separate busy-poll sleep limit. Remote-job and LaunchAgent executable tests passed, as did bash syntax validation and git diff --check. No production behavior changed

* no-mistakes(ci): Fixed missing readiness recovery under verified live ownership, preserving the serving PID and private file mode. Added executable regressions for deletion during a blocked sweep and stale readiness diagnostics without LaunchAgent reload. Deletion regression failed before the fix. Both remote-job test suites, bash syntax validation, and git diff --check passed

* no-mistakes(ci): Full CI log identified a flaky ownership-loss test racing an already-authorized heartbeat refresh. Replaced backdating and a fixed sleep with bounded observation of readiness expiry through the public probe. Production behavior unchanged. Remote-job and LaunchAgent executable suites passed; bash syntax validation and git diff --check passed

* fix: wait for launchd bootout cleanup

* no-mistakes(document): Document remote worker recovery and read-only turnover verification

* no-mistakes(review): Publish worker identity before lock owner records

* no-mistakes(document): Document worker identity publication safety invariant

* no-mistakes(ci): Fixed ci-1: replacement workers discard predecessor readiness before publishing identity and roll back identity if lock-owner recording fails. Added executable regressions reproducing both defects. LaunchAgent and orphan-reap suites, shell syntax checks, and git diff --check passed. No live service state was modified

* no-mistakes(ci): Restored lock-owner-before-identity publication and removed the identity rollback and reordering-only tests. Retained an executable regression proving predecessor readiness is rejected until replacement startup completes. LaunchAgent and orphan-reap suites, shell syntax checks, and git diff --check passed. Other changes remain intact; the retained-identity interrupted-repair edge remains out of scope. No live service state was modified
* fix(remote-job): reap expired seq claims with one directory walk

The hourly claim sweep forked uname+stat per .seq-claims entry and blocked
serving for ~85s at ~17k dirs. Delete expired empty claim dirs with a single
find -exec rmdir batch and cache the host uname for remaining mtime reads.

* no-mistakes(review): Restore original path mtime helper and drop uname cache

* no-mistakes(test): Restore claim retention eligibility; focused retention and serving tests pass

* no-mistakes(document): Document single-walk claim cleanup and regression entrypoints

* no-mistakes(ci): Fixed Lint 2’s reproduced SC1091 by adding the tests/lib.sh ShellCheck source directive to the retention test. Runtime behavior is unchanged. ShellCheck passed for both new claim tests; Bash syntax, the retention behavior test, and git diff --check passed

* fix(tests): keep fixture registries out of git worktree roots

A TMPDIR pointed at a repository root placed live .fm-test-* registries
beside tracked files, and a concurrent git add during the claim-walk CI
fix round committed three of them. Route registries and fixture roots
through a TMPDIR that refuses git worktree roots, remove the stray files,
and pin the escape with a behavioral cleanup test.

* no-mistakes(review): Preserve whole-second claim expiry in single-walk sweep

* no-mistakes(document): Correct temporary-directory resolution documentation

* no-mistakes(ci): Fixed ci-3 by changing only the stale, fresh, and read-only orphan fixture paths in tests/fm-test-fixture-cleanup.test.sh to use $FM_TEST_TMPDIR. All seven tests passed both normally and with TMPDIR set to the worktree root. Shell syntax and git diff --check passed
…t-in (kunchenguid#5354)

* feat(bin): record each task's no-mistakes pipeline spend at cleanup

no-mistakes keeps every pipeline agent invocation's token usage only in its
local agent_invocations records, and cold pipeline agents (review, test,
document) leave no session log, so a task's review-loop cost never reached
Firstmate's records and could not be attributed after cleanup.

bin/fm-pipeline-spend.sh attributes a task's runs by the repository
no-mistakes resolves for the task copy, the task branch, and the branch's
creation (a relaunch mints a new spawn_gen while the branch keeps
validating), then sums every invocation, failed and cancelled included.
Token fields use no-mistakes' per-round deltas so resumed review rounds are
not counted twice, and unrecorded values stay unknown rather than zero.
show prints the record read-only; record appends it once per task
incarnation to data/pipeline-spend.jsonl. Teardown records it for every
ship task it cleans up, before deleting the branch and the task record.

fm_nm_state_db becomes the one owner of where no-mistakes' state database
lives, shared with the capped run-inventory reader.

* no-mistakes(review): Drop spend show command and timeout override

* style(bin): rewrap fm-pipeline-spend header comment

* Make pipeline spend recording opt-in

* feat(bin): record each task's no-mistakes pipeline spend at cleanup

no-mistakes keeps every pipeline agent invocation's token usage only in its
local agent_invocations records, and cold pipeline agents (review, test,
document) leave no session log, so a task's review-loop cost never reached
Firstmate's records and could not be attributed after cleanup.

bin/fm-pipeline-spend.sh attributes a task's runs by the repository
no-mistakes resolves for the task copy, the task branch, and the branch's
creation (a relaunch mints a new spawn_gen while the branch keeps
validating), then sums every invocation, failed and cancelled included.
Token fields use no-mistakes' per-round deltas so resumed review rounds are
not counted twice, and unrecorded values stay unknown rather than zero.
show prints the record read-only; record appends it once per task
incarnation to data/pipeline-spend.jsonl. Teardown records it for every
ship task it cleans up, before deleting the branch and the task record.

fm_nm_state_db becomes the one owner of where no-mistakes' state database
lives, shared with the capped run-inventory reader.

* no-mistakes(review): Drop spend show command and timeout override

* style(bin): rewrap fm-pipeline-spend header comment

* Make pipeline spend recording opt-in

* no-mistakes(document): Note pipeline-spend opt-in gate in teardown comments

* no-mistakes(ci): I fixed all three Greptile findings following your decision. Both test suites pass: tests/fm-teardown.test.sh (105 ok, exit 0) and tests/fm-pipeline-spend.test.sh (8 ok). I didn't run either new test against the old code, so the claim that they fail before the fix is from reading the code, not a run. Nothing was committed or pushed. **ci-1 / ci-2 (bin/fm-teardown.sh):** The rule is that every owned ship task in a home that has opted in gets a durable spend record before its task record is deleted. The `[ -d "$WT" ]` check skipped the recorder when the worktree folder was already gone, so no record was written. I removed that check and kept the other conditions: the task must be a ship task, teardown must own its worktree, and `config/pipeline-spend` must exist. `bin/fm-pipeline-spend.sh` already writes an unavailable-source line when the worktree is missing. The new test `test_teardown_records_unavailable_spend_for_a_gone_worktree` in tests/fm-teardown.test.sh sets up an owned ship task whose worktree is missing, with recording turned on. It checks that teardown succeeds, writes a line with `source=="unavailable"`, `total==null` and a reason saying the task copy is gone, and still removes the task record. Under the old check no ledger would have been written. **ci-3 (bin/fm-nm-run-lib.sh):** When `NM_HOME` and `HOME` were both unset, `fm_nm_state_db` fell back to `${HOME:-}/.no-mistakes`, which resolves to `/.no-mistakes`. It now uses `root=~/.no-mistakes`. Bash expands `~` to the account's home directory even when `HOME` is unset, which matches the previous Python `Path.home()` lookup. The new test `test_state_db_without_nm_home_or_home_uses_the_account_home` in tests/fm-pipeline-spend.test.sh runs the function with both variables unset and compares the result to the home directory from the system's password database. The old code would have returned `/.no-mistakes/state.sqlite`. shellcheck reports nothing new on the changed files. I added one `disable=SC2016` comment in the new test, where the single-quoted `$1` is meant to expand in the child shell
* feat(bin): start tasks from a named base branch

Spawns always reset a task's pooled copy to origin's default branch,
so work that belongs on a feature, integration, or release branch
started from the wrong code and opened its PR against the default.

fm-brief.sh --base-branch records the base in the brief, fm-spawn.sh
resets the copy to origin/<base> and records base_branch= in task
meta, and the worker targets its PR at that branch. Review diffs,
the cleanup content check, and scout promotion read the recorded
base. local-only and Gerrit deliveries refuse a named base.

* no-mistakes(review): Anchor brief base-branch parse on scaffold Setup line

* no-mistakes(review): Require explicit --base-branch on spawn, matching brief lines

* no-mistakes(document): Note base-branch reset in fm-spawn freshness header

* no-mistakes(ci): I fixed all four Greptile findings the way you specified. The affected suites pass locally: fm-brief, fm-dod-lib, fm-spawn-pool-base-freshen, fm-review-diff, fm-teardown and fm-task-delivery. shellcheck on the changed files reports only info-level notes, no warnings or errors. I didn't revert the code to watch each new test fail before the fix. - **ci-1 (brief text blocked or redirected spawns):** In bin/fm-dod-lib.sh, `fm_brief_base_branches` now only accepts a `Base branch: <value>` line that comes directly after the base-variant Setup sentence fm-brief.sh writes. Any other `Base branch:` line is ignored. `--base-branch` is still the only authority, and the existing checks in fm-spawn.sh are unchanged. With the flag, the spawn is refused unless a matching pair exists and no pair names a different branch. Without the flag, it is refused only if such a pair exists. The header comment in fm-spawn.sh is updated to match. - The `brief_with_base` test helper now writes the real two-line pair. - The decoy refusal case now uses a full decoy pair in the captain's intent. - New test `test_prose_base_branch_line_is_ignored`: a brief with no base whose intent quotes `Base branch: release/1.2` spawns normally with no `base_branch=` recorded. A spawn with `--base-branch feature/hub` whose intent names release/1.2 starts from `origin/feature/hub` and records it. - **ci-2 (scout base not checked against the forge):** fm-spawn.sh now looks up the project's registered forge with `bin/fm-project-mode.sh --forge`, like the ship path does, before `fm_base_branch_valid` runs. Previously it passed `none`. New test `test_scout_base_branch_refused_on_gerrit_forge`: a scout with a base on a forge=gerrit project is refused at spawn and no meta file is written. - **ci-3 (base not shell-quoted in worker commands):** `fm_dod_block` now quotes the base with `printf %q` in the `--base` and `--base-branch` arguments, and the branch name in the prose stays readable. The test in fm-brief.test.sh uses the base `release/$HOTFIX` and checks that both direct-PR and no-mistakes briefs render `release/\$HOTFIX` in the commands. - **ci-4 (ambiguous PR sentence):** The direct-PR sentence now reads "open a PR with `gh-axi` that is ready for review, not a draft, against the base branch `X` (`--base X`), not the repository default." A brief with no base renders exactly as before. The existing assertion in fm-brief.test.sh is updated

* no-mistakes(ci): Both real failures on PR 6442 (run 37064756768) come from CI tooling and a Pi version bump, not from the base-branch feature. Fixes for both already exist on upstream main, so I applied those two commits to the working tree with `git cherry-pick --no-commit`. Nothing is committed or pushed, and no feature file changed. - **Lint 2:** ShellCheck ran out of memory on `bin/fm-teardown.sh` (rss about 8 GB, exit 251, "shellcheck: out of memory"). The rule that broke is that the lint gate must finish on any valid shell root without exceeding the runner's memory ceiling. Upstream commit d719ef3 (kunchenguid#6443) fixes this in `bin/fm-lint.sh`: a root that hits the memory ceiling is retried without `--external-sources`. It also updates `tests/fm-lint.test.sh` and `docs/fm-test-portable-shards.md`. - **Behavior portable serial 6:** `tests/fm-calm-pi-extension.test.sh` failed with "grep disappeared from /export calm.html HTML while calm mode was on". Pi 1.0.1 renamed its HTML export renderer lookup, and upstream commit fede619 (kunchenguid#6530) updates the test to accept the new name. This change is test-only. Both commits applied cleanly. The four touched files are `bin/fm-lint.sh`, `docs/fm-test-portable-shards.md`, `tests/fm-lint.test.sh` and `tests/fm-calm-pi-extension.test.sh`. None of them is in the feature diff, so the feature contract is unchanged. **Local checks:** - `tests/fm-lint.test.sh` passes. - `bin/fm-lint.sh` on `bin/fm-teardown.sh`, `bin/fm-spawn.sh` and `bin/fm-dod-lib.sh` exits 0 with full ShellCheck analysis. - `tests/fm-calm-pi-extension.test.sh` exits 0 with 7 ok and 0 not ok. One case skipped because the Pi package isn't installed locally, so the Pi 1.0.1 export path that failed in CI couldn't be reproduced here. CI needs to confirm it. - I didn't reproduce the 8 GB OOM locally. The PR has to be updated through the pipeline with a normal push: no force push, no replacement PR, no merge
…ole (kunchenguid#6647)

* fix(bin): point project workers at the Firstmate skill file

The Skill tool cannot resolve a Firstmate skill from another project's worktree, so the launch role names the readable skill file instead.

* no-mistakes(review): fix(bin): name Firstmate skill file as fallback only
@remicaesar
remicaesar merged commit e12b854 into main Oct 6, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants