Repository navigation
feat(bin): sync fork with upstream firstmate main - #3
Merged
Merged
Conversation
* 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
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
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
fm-brief.sh --base-branchandfm-spawn.shcan now start a task from a named base branch. The spawn resets the pooled copy toorigin/<base>and recordsbase_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.bin/fm-pipeline-spend.sh, records each ship task's no-mistakes pipeline spend at teardown. It is opt-in: it only runs whenconfig/pipeline-spendexists, and it writes todata/pipeline-spend.jsonl.fm_nm_state_dbis now the only place that decides where the no-mistakes state database lives..seq-claimsdirectories are cleaned up in a singlefindwalk.🤖 Generated with Claude Code
Risk Assessment
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)
Evidence: fm-pipeline-spend.sh CLI run under /bin/bash 3.2
Source: fm-pipeline-spend.sh CLI run under /bin/bash 3.2
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.
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ 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.shwith no arguments prints its usage text and exits 2;fm-pipeline-spend.sh record demo-taskwith recording disabled exits 0✅ **Document** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.