feat(bin): sync upstream firstmate (jev memory guard, keep-ai-trailers, Herdr and polling fixes) - #13
Conversation
…hrashing guard (kunchenguid#5903) * feat(jev): add the guard framework and the memory RSS/swap thrashing guard A Jev guard is a bounded read-only host diagnostic that turns one class of resource pressure into a machine-readable audit record and a one-line verdict. This lands the framework contract (docs/jev-guards.md) with one representative family (Pattern 46, memory RSS and swap thrashing) as the pilot: thin bash wrapper, stdlib-only python engine, and a behavioral test through the CLI. * no-mistakes(review): fix jev mem guard fail-open unknown and contract * no-mistakes(review): fix mem guard rss pairing, memfree, tests, docs * no-mistakes(review): fix inverted UNKNOWN branch in fail-forcing test * no-mistakes(review): register docs/jev-guards.md in audience inventory * no-mistakes(review): simplify wrapper, drop dead top_n, single-source thresholds * no-mistakes(review): assert exact exit code in fail-forcing test leg * no-mistakes(review): tolerate any stdout encoding in text output * no-mistakes(document): Fix guard contract dash style and output wording
…enguid#5900) * fix(bin): stop slow GitHub reads from starving and waking the contributions poll The poll's fixed budget cut the tail URL's reads short, and any read killed at the bound was recorded as a forge failure, so routine GitHub slowness produced an observation-unavailable wake. A configured FM_CONTRIBUTIONS_BUDGET now rides the generated check shim and is cut down to the watcher's per-check bound with a margin, a read killed at the bound or the deadline is budget refusal that leaves records untouched, and the poll moves to the next URL that still has a full observation reserve instead of ending. * fix(bin): report the bound when a signal death leaks through fm_run_timed fm_run_external_timeout trusted the wrapper's recorded status over the runner's own bound verdict, so a read killed by the bound's TERM could surface as 143 and callers such as the contributions poll classified a budget-bound read as a forge failure. When the runner reports the bound, a signal-death status is the bound's own TERM racing the wrapper's bookkeeping and is reported as 124; a natural exit still passes through. The replace-shell probe also moves to the repo's portable BASHPID fallback so the suite runs on the macOS system bash. * no-mistakes(review): Rotate contribution polling and verify generated budget behavior * no-mistakes(review): Stabilize contribution rotation across successful observation refreshes * no-mistakes(review): Exclude settled contributions from live observation rotation * no-mistakes(document): Document contribution poll rotation and observation reserves * no-mistakes(ci): Captain, fixed timeout handling with a one-line change preserving natural exit 137. Full session-start and contributions suites, targeted regressions, and lint passed. The timeout suite still fails on the known Bash 3.2 BASHPID issue, left unchanged as instructed. Logs retained in .no-mistakes/ci-evidence/. Remote CI was not rerun * no-mistakes(ci): Fixed the fixture’s lock-acquisition race with a one-line bounded wait. Forced contention reproduced the CI error before the fix and passed afterward; the ordinary held-lock case, lint, syntax, and diff checks passed. Local Bash 3.2 failures remain: “cleanup lock bound 08 gave up before the marker lock freed” (also reproduced without the fix) and “TERM did not stop a watcher blocked inside a poll”. Remote CI was not rerun
…railers (kunchenguid#5859) * feat: add keep AI trailers setting * no-mistakes(document): Drop duplicate keep-ai-trailers line, update Cursor attribution note * no-mistakes(document): Honor keep-ai-trailers for Devin worker attribution * no-mistakes(document): Qualify Devin attribution note with keep-ai-trailers flag * no-mistakes(review): Inherit keep-ai-trailers into secondmate homes
…sh-command popup cannot hide the composer (kunchenguid#5876) * Fix Herdr composer reads blinded by the slash-command popup Capture the full visible viewport for every Herdr composer state and content read instead of a bounded tail. Claude Code renders its slash-command popup between the composer and the pane bottom, which pushes the composer outside a tail window. The pre-Enter payload proof then read an empty composer, judged a typed command unsent, and cleared it without pressing Enter. The proof-lines value now bounds only the clear cost, not the capture size. Growing the window adds rows above the composer only, so bottom-most shape selection and prior verdicts are unchanged. The suffix refusal is kept, and the shared inbox pending-line read stays a bounded tail. Portable regressions cover the popup-below-composer layout, and the live submit-confirmation guard gains a third exit scenario. The runtime-backends verification record documents the Herdr 0.9.0 and Claude Code 2.1.283 run. Closes kunchenguid#5533 * no-mistakes(document): Clarify composer capture bound ownership * no-mistakes(review): Drop unrequired bracketed-paste Enter fallback from live guard * no-mistakes(review): Latch trust prompt, check idle composer arm first
…configurable bound (kunchenguid#5917) Each per-task endpoint read in the session-start fleet digest runs in its own crash-isolated child under the existing timeout helper with a configurable FM_SESSION_START_ENDPOINT_TIMEOUT bound (default 10s). A hung or killed read becomes that task's endpoint error while the digest continues, and the wrapper banners any abnormal child exit naming its status.
…nchenguid#5886) * Keep lab tmux sockets on short private paths * no-mistakes(review): Fix Linux stat checks and fail-closed lab tmux teardown * no-mistakes(document): Update lab helper documentation for isolated tmux sockets
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 39959a05ca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if [ "$KEEP_AI_TRAILERS" = 0 ]; then | ||
| "$FM_ROOT/bin/fm-git-strip-ai-trailers.sh" install "$GIT_HOOKS_DIR" "$WT" || { | ||
| echo "error: could not install the AI-trailer strip hooks for $ID" >&2 | ||
| exit 1 | ||
| } | ||
| fi |
There was a problem hiding this comment.
Preserve AI-trailer stripping for every worker
When config/keep-ai-trailers is present, this conditional skips the commit-msg hook, while the associated Claude and Devin changes also retain their attribution settings. Any supported runtime that emits attribution can therefore add an agent Co-Authored-By trailer to commits, directly violating this repository's unconditional prohibition on agent commit co-authors; the opt-in must not disable that invariant.
AGENTS.md reference: AGENTS.md:L45-L50
Useful? React with 👍 / 👎.
| CHECK_TIMEOUT=${FM_CHECK_TIMEOUT:-30} | ||
| case "$CHECK_TIMEOUT" in ''|*[!0-9]*|0) CHECK_TIMEOUT=30 ;; esac | ||
| BUDGET_CAP=$((CHECK_TIMEOUT - 3)) |
There was a problem hiding this comment.
Normalize the watcher timeout before arithmetic
When FM_CHECK_TIMEOUT is a zero-padded numeric value such as 08, the digit-only validation accepts it, but Bash parses it as octal in this arithmetic expansion and exits with value too great for base; because this runs before command dispatch, poll, arm, and even read-only subcommands all fail instead of enforcing the configured bound. Normalize the value to base 10 or reject padded inputs before arithmetic.
Useful? React with 👍 / 👎.
| except Exception: | ||
| return [] | ||
|
|
||
| for entry in entries: |
There was a problem hiding this comment.
Enforce a deadline while scanning process RSS
On a host with a large process table or slow procfs reads, this loop performs a separate blocking statm read for every PID and often another comm read, without checking elapsed time or applying an outer timeout. Because every invocation, including --check, runs this scan, the guard can exceed its documented sub-500ms budget and block its caller instead of degrading to UNKNOWN; stop scanning at a monotonic deadline or run the scan under an enforced bound.
Useful? React with 👍 / 👎.
| chmod 700 "$socket_dir" || { rmdir "$socket_dir" 2>/dev/null || true; exit 1; } | ||
| [ "$(fm_lab_home_mode "$socket_dir")" = 700 ] && [ "$(fm_lab_home_owner "$socket_dir")" = "$(id -u)" ] \ | ||
| || { rmdir "$socket_dir" 2>/dev/null || true; fm_lab_home_error "cannot secure tmux directory"; exit 1; } | ||
| (umask 077; printf '%s\n' "$socket_dir" > "$record") || { rmdir "$socket_dir" 2>/dev/null || true; exit 1; } |
There was a problem hiding this comment.
Serialize creation of the lab tmux directory
When two validation processes call tmux-dir for the same lab home concurrently, both can observe the record as absent, create different /tmp/fml.* directories, and overwrite this record; both callers report success, but teardown knows only the last directory. If the losing caller starts tmux, its server and socket remain untracked and cannot be cleaned up through this helper, so publish the record under a lock or atomic create and make losing callers discard their directory.
Useful? React with 👍 / 👎.
| jq -Rnr --argjson bucket "$((EPOCH / 300))" ' | ||
| [inputs] | if length == 0 then . else ($bucket % length) as $offset | .[$offset:] + .[:$offset] end | ||
| | .[]' < "$TMP/live.tsv" > "$TMP/known.tsv" |
There was a problem hiding this comment.
Use a poll-sequence rotation to prevent starvation
When checks run at a cadence whose five-minute bucket step shares a factor with the live URL count, this wall-clock offset never selects some URLs first. For example, with four URLs, a valid 600-second check cadence alternates offsets 0 and 2; if slow forge reads leave room for only the first observation, URLs 1 and 3 are never attempted, recreating the starvation this change is intended to prevent. Advance a durable cursor per completed poll, or otherwise ensure every offset is visited independently of polling cadence.
Useful? React with 👍 / 👎.
Intent
Update firstmate to the latest: the fork's own changes plus the original project's new changes.
Context: this firstmate home runs from the fork zakna/firstmate (remote origin), which already runs its latest main. The original project is kunchenguid/firstmate (remote upstream). A first sync (PR #11, fm-upstream-sync-o1) merged upstream earlier on 2026-09-27. Since then upstream's main has 6 new commits the fork lacks (kunchenguid#5886, kunchenguid#5917, kunchenguid#5876, kunchenguid#5859, kunchenguid#5900, kunchenguid#5903). A read-only test merge of upstream/main into origin/main on 2026-09-27 had no conflicts.
What Changed
kunchenguid/firstmatecommits into the fork's main. It adds the jev guard framework contract and a memory RSS/swap thrashing guard (bin/fm-jev-mem-guard.{py,sh},docs/jev-guards.md, feat(jev): add the guard framework contract and the memory RSS/swap thrashing guard kunchenguid/firstmate#5903). It also adds aconfig/keep-ai-trailersopt-out, which keeps AI co-author trailers instead of stripping them (feat(bin): add config/keep-ai-trailers opt-out to keep AI co-author trailers kunchenguid/firstmate#5859).bin/:tests/fm-jev-mem-guard.test.sh, plus additions to the session-start, contributions, Herdr backend, spawn dispatch-profile, timeout-lib and gate-refuse suites). Merged cleanly with the fork's currentorigin/main.Risk Assessment
✅ Low: The branch's delta against origin/main is exactly upstream's six commits (kunchenguid#5886, kunchenguid#5917, kunchenguid#5876, kunchenguid#5859, kunchenguid#5900, kunchenguid#5903), with every fork-only change kept and no conflict markers, so it is a clean upstream sync that matches the stated intent.
Testing
The Test agent exceeded its invocation budget before live validation completed; no evidence was gathered for this head.
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.