Repository navigation
feat(bin): merge upstream main while preserving fork delivery and supervision deltas - #52
Merged
Merged
Conversation
…m as silence (kunchenguid#5588) * fix(bin): surface an unrecognized status prefix instead of dropping it A parked or holding declaration, and a verb whose correlation token did not parse, never became an event, so the supervisor still saw the earlier line. * no-mistakes(review): Require verb-shaped unrecognized status prefixes, add continuation tests * no-mistakes(document): Document unrecognized status prefix escalation in afk skill * no-mistakes(ci): I reproduced the "Behavior portable serial 6" failure locally and fixed it by changing the test data in one test. No product code changed. **What failed:** `tests/fm-session-start.test.sh`, in `test_orphan_status_logs_are_printed`, with "matched status log was printed 2 times". **Why:** the test writes status lines with made-up prefixes, `matched: surfaced once` and `orphan: step N`. The test only uses them as placeholder text. It checks that the session-start digest prints each task's status tail exactly once. This PR (kunchenguid#4763) deliberately makes an unrecognized one-word lowercase prefix a status event. So those lines now surface as captain-relevant events, and the wake queue's STATUS OUTCOME BACKSTOP section prints them a second time. The code under review is behaving as the issue asks. Only the test's placeholder data had become meaningful. **Rule the test depends on:** its status lines must not be captain-relevant, so the digest is the only place they are printed. Both lines in this test broke that rule. The orphan line would have failed the same count check right after the matched line did. **Fix:** in that test only, I switched both lines to the recognized, non-captain verb `working:`: `working: surfaced once` and `working: orphan step 1..6`. I updated the matching assertions and counts to use the new text. What the test checks is unchanged: orphan logs are labelled, the tail is bounded, the log path is printed, and each tail appears once. **Verification:** before the fix, the test failed locally the same way as in CI. After it, `bash tests/fm-session-start.test.sh` reports "all assertions passed * no-mistakes(review): Detect unrecognized prefixes on unstamped lines; share verb list --------- Co-authored-by: Kun's firstmate <kunchenguid+firstmate@users.noreply.github.com>
…henguid#5658) Fixes kunchenguid#5295 Session start now reports a remote inheritance failure using the push's own error line instead of the first unchanged item that happened to print before it, and the shared captain preferences header check now names the first required phrase it did not find, on both the local and remote inheritance paths.
…yloads (kunchenguid#5657) * fix(bin): stand down the Claude Stop auto-arm on pi-code-delivered payloads pi-code loads the tracked Claude settings but has no asyncRewake, so it awaits every Stop hook; without a stand-down the auto-arm runs synchronously inside Pi's turn end and holds it open for the declared multi-hour timeout. Stand down when the payload's transcript_path contains a /.pi/ path component, the same discriminator the closed-but- unmerged fix in kunchenguid#3352 used, with an explicit string-type check on the jq filter. Fixes kunchenguid#3343 * no-mistakes(document): document pi-code stand-down in harness integrations reference
…kunchenguid#5659) * fix(bin): match whole multi-word project names in the registry lookup bin/fm-project-mode.sh matched a registered project name against only the first whitespace-delimited token of a registry row, so a name containing a space never matched, silently defaulting the project to no-mistakes off instead of its declared posture. The lookup now matches the whole registered name against the raw line text, so a name is compared literally (never as a regex) and a name that is a leading prefix of another registered name still resolves to its own row. * no-mistakes(document): docs already accurate for multiword registry name match * chore: drop accidental empty err file Co-authored-by: Kun Chen <kunchenguid@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Kun Chen <kunchenguid@users.noreply.github.com>
…d#5546) * fix(bin): classify the stdin program of `bash -s` with operands in the arm policy With -s, sh/bash/zsh read the program from stdin even when operands follow; the operands are only positional parameters. The arm policy treated the first operand as a script path, so heredoc and here-string payloads were never classified and a hidden bin/fm-watch.sh execution was allowed. A protected path in the operand position still fails closed as before. Fixes kunchenguid#1489 * no-mistakes(document): Clarify stdin shell operand documentation * no-mistakes(ci): Captain, fixed `shellInvocation` so `bash -- -s` treats `-s` as a script name, and updated R21 to test the exact command. The targeted policy suite, lint, documentation check, and diff check pass. Both hosted workflows show `action_required` before any jobs ran; that external approval state remains unresolved * fix(bin): keep main's handling of words after a leading `--` Revert the pipeline CI-step change that made the first word after a leading `--` always a script. It turned forms that main denies today into allow (for example `bash -- -c 'bin/fm-watch.sh'`), which is outside kunchenguid#1489 and loosens a fail-closed policy. `--` after `-s` still ends option parsing.
…nchenguid#5695) * fix(bin): strip AI co-author trailers from fleet-launched commits Cursor and other non-Claude runtimes append the trailer after the typed message. A per-task commit-msg hook removes it and leaves human co-authors and the author identity untouched. * no-mistakes(review): Export pane hooksPath override and drop generated-with stripping * no-mistakes(ci): This PR caused all three CI failures, and the fix is test-only: 4 test files change, no product code. **Cause.** `fm-spawn.sh` now installs the AI-trailer strip hooks for every spawn, secondmates included. The installer refuses a worktree that is not a git repository, and the PR deliberately keeps that fail-closed rule because real secondmate homes are firstmate clones. Four test fixtures still gave secondmates a plain directory as their home, so each spawn failed with "not a git worktree ... could not install the AI-trailer strip hooks": - serial 5: `tests/fm-backlog-atomicity.test.sh` ("secondmate spawn failed"). - serial 8: `tests/fm-secondmate-harness.test.sh` ("split: no meta written"). - Herdr: `tests/fm-backend-herdr-launcher-workspace-e2e.test.sh` and `tests/fm-backend-herdr-workspace-per-home-e2e.test.sh`. **Rule that must hold.** Every home a test spawns as a secondmate must be a git worktree. I checked the other places in the changed area: the only secondmate spawns in these tests are the ones listed. The earlier rounds already fixed the other fixtures (`fm-secondmate-liveness`, `fm-secondmate-safety`) the same way. **Fix.** - Each of those four secondmate homes now gets the same `.gitignore` plus `git init -q -b main` that the liveness and safety tests already use. - The two Herdr tests clean up with their own plain `rm -rf "$TMP_ROOT"`, not the shared `tests/lib.sh` helper. Because the installer leaves each `state/<id>.git-hooks` directory read-only, that cleanup printed "Permission denied" and left the directories behind. Both cleanups now restore the owner's write bit on every directory before removing (`find ... -exec chmod u+rwx`), which is what `fm_test_remove_tree` in `tests/lib.sh` does. **Verification.** - `tests/fm-secondmate-harness.test.sh` passes. - `tests/fm-backlog-atomicity.test.sh` passes (99 ok, exit 0). - shellcheck is clean on all four files. - I could not run the two real-Herdr tests locally: the Herdr lab on this host refuses to start because it needs exactly one running default session, and I did not change the host's Herdr state to get around that. Instead I checked their two changed steps directly: the installer succeeds on a home set up the new way, and the new cleanup removes the read-only hooks directory completely. Those two tests will only be proven on CI
…id#5683) * fix(bin): treat Pi's dollar-first cost footer as furniture An idle Pi status row opening with $0.000 was read as a dead-shell prompt, so exit and relaunch refused on an empty composer. * test: wait for the draining holder to exec sleep before reading its identity The procevent drain fixture read fm_pid_identity immediately after backgrounding setsid sleep, racing the child's exec chain. Mid-exec the cmdline can read empty, failing the fixture on a loaded CI runner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…5534) * fix(bin): refuse a merge when a required check never reported fm-pr-merge.sh built its GitHub refusals only from checks present in statusCheckRollup, so a required check that never ran was simply absent and the merge proceeded on the subset that reported, contradicting its own "every required check green" claim. The GitHub verify now reads the base branch's required contexts from the forge itself - the classic branch protection summary on GET repos/{o}/{r}/branches/{b} and the active ruleset rules on GET repos/{o}/{r}/rules/branches/{b} - and refuses when a required context has no entry in the same rollup, at the same head, that the merge is bound to. Absence reads as unknown, never green. The required-set read joins the existing refusal list, so a draft, a red check, and an unreported required check are all reported together. Could not read vs nothing required: both endpoints need only repository read access. The admin-only GET .../branches/{b}/protection endpoint is deliberately not used: it answers a non-admin token with the same 404 an unprotected branch gets (observed live on kunchenguid/firstmate main with this token), which would read a missing permission as "nothing required". Any failed or malformed read of either source (auth, missing fine-grained permission, rate limit, network, 404, unexpected shape) refuses the merge with a line naming the unreadable source. The one exception is GitHub's plan-gated 403 on the rules endpoint ("Upgrade to GitHub Pro or make this repository public"), which already means "this repository has no branch rules" for the merge-queue reader; that check moves into one shared helper and the classic summary still decides for such a repository. Attended waiver: --allow-missing <check-name> is the twin of --allow-red and follows the same design and recording path: once, separate name argument, waives only that exact unreported required check, still requires every other required check reported and every check green, never waives an unreadable required set, refused while the away-posture record exists, and refused on GitLab. Merge-state BLOCKED policy is unchanged. How this differs from the withdrawn kunchenguid#5353 (read from its diff): - kunchenguid#5353 read the admin-only branches/{b}/protection endpoint and treated its 404 as "no required checks", so for any non-admin token the required set silently read as empty; this change reads the read-access branch summary and treats every failure as unreadable. - kunchenguid#5353 ignored rulesets; this change also reads required_status_checks rules from the effective branch rules. - kunchenguid#5353 made separate per-head REST reads of statuses and check-runs capped at per_page=100 with no pagination; this change checks presence in the same statusCheckRollup view the red-check gate already reads at the verified head. - kunchenguid#5353 stopped at the first unreadable read; this change reports it as one refusal among all the others. - kunchenguid#5353 also claimed kunchenguid#5345 (lock stealing) and changed 39 files, most unrelated; this change is kunchenguid#5344 only. Live proof, read-only (a gh wrapper refused every merge and mutating call): - cli/cli#14474 (trunk requires 3 classic build contexts, none ran): refused, naming build (macos-latest), build (ubuntu-latest), build (windows-latest); with --allow-missing "build (macos-latest)" it still refused, naming the other two. - cli/cli#13665 (ran build (ubuntu-24.04-firewall) instead): refused, naming build (ubuntu-latest). - hashicorp/terraform#39262 (ruleset-required checks absent): refused, naming Code Consistency Checks, End-to-end Tests, Race Tests, Unit Tests. - cli/cli#14485 (all required reported and green): verified; the wrapper blocked the merge call and the pull request read back open. Fixes kunchenguid#5344 * fix(review): Preserve required-check producers and aggregate independent read failures * fix(document): Clarify required-check verification and waiver documentation * fix(bin): match an app-bound required commit status by name The producer-identity check resolved an app-bound required context only against check runs, so a required context that the required app reports as a commit status could never match and always read as "has not reported". A commit status carries no app id to compare, so an app-bound requirement that arrives as a status now matches by name, as before producer binding; check runs keep requiring the configured producer app. Live, read-only: hashicorp/terraform#39262 requires license/cla from integration 865473, reported green as a commit status by the CLA app. The previous head refused it as unreported; this head no longer does, while still naming the four required check runs that never ran there. Refs kunchenguid#5344 * fix(document): Clarify accepted commit-status producer verification limitation --------- Co-authored-by: firstmate-oss <firstmate-oss@kunchenguid.local>
…uid#5696) * fix(bin): never offer a persistent secondmate for teardown The return brief's "Landed, cleanup due" scan listed every state/*.meta record carrying a pr= and a merge-notified marker without regard to kind, so a secondmate record holding a relayed child's merged PR put the mate itself up for "bin/fm-teardown.sh <mate>" cleanup. A secondmate is a persistent worker, never landed work. - bin/fm-afk-return.sh: skip kind=secondmate in the landed-cleanup scan. - bin/fm-pr-check.sh: refuse to record pr= or arm a merge watch on a kind=secondmate record before any side effect; a PR reported on its routed status channel belongs to a task in the mate's own home, which arms its own watch. - bin/fm-watch.sh: a merged result from a poll already armed on a secondmate retires the poll silently - no merge outcome, marker, or wake. * no-mistakes(document): Document secondmate merge-watch and return-brief exclusions * no-mistakes(ci): CI failed in an unchanged watcher-shutdown test whose three-second wait was sensitive to runner load. Increased the wait for both state- and home-deletion cases without changing watcher behavior. The full fm-watch-arm suite passed locally; syntax and diff checks passed
…unchenguid#5702) * fix(bin): refuse unknown dash-leading args in public-posting fm-x scripts fm-x-reply.sh collected any unrecognized argument into the positional pool and took the first one as the reply text, so an invocation like "fm-x-reply.sh <id> --followup --final <text>" posted the literal string "--final" to X and silently dropped the real text. Make argument parsing strict in every script that can post publicly: an unknown dash-leading argument, a dash-leading request_id/task id, a dash-leading option value, or a surplus positional now exits 2 with a usage error before any config load, outbox write, or network call. Reply text starting with '-' is still accepted via --text-file or stdin, and --help is honored wherever it appears instead of becoming text (a --help forwarded through fm-x-followup.sh would have counted as a posted follow-up and mutated the link). fm-x-link.sh and the fm-public-followup scripts already refuse unknown arguments; fm-x-poll.sh takes none. * no-mistakes(review): Refuse surplus follow-up text sources; drop post-ID help branches * no-mistakes(document): Clarify reply and follow-up argument usage * no-mistakes(review): Refuse dash-leading --text-file operands in fm-x-reply * no-mistakes(document): Correct follow-up argument parsing comment * no-mistakes(document): Document dismiss argument rejection in script header
…nchenguid#5701) * feat(bin): latch the supervision host after repeated engine errors Rung 3c-1 of the PR 5631 re-cut: the host copies the Pi branch's broken-session policy. Two consecutive engine errors latch the session; every away wake then reaches main with one supervision-host line for a five-minute cooldown, after which one wake probes the engine, and each failed probe doubles the cooldown up to one hour. A reported turn without an engine error clears it. The latch is kept per main session, engine, and model in state/.supervision-host-health, and the engine conversation now uses the same main-session key, which includes the lock holder's process identity so a recycled pid never shares either. Lifted from the validated 5631 tree and adapted to main's away-only host: the attended recovery line and attended cooldown pass-through are left for the attended core, so a recovery is only logged. * no-mistakes(document): Consolidate supervision-host latch documentation
…kunchenguid#5535) * fix(bin): absorb routine second-mate progress while surfacing routed replies Fixes kunchenguid#2959 A kind=secondmate task's status signal was never absorbable, so a healthy mate's routine working: and paused: appends woke the primary every time. signal_crew_provably_working now reads the mate's lines new since the watcher's classified position: a decision, blocker, terminal outcome, note:, correlation-marked line, or unknown verb still surfaces regardless of busy evidence, while unmarked working:, paused:, and resolved: fall through to the same provably-working absorb an ordinary crewmate gets. * no-mistakes(review): narrow secondmate routine absorb to working and paused
* fix(bin): use gh-axi for the ship DoD draft check * no-mistakes(review): use PR number not URL in gh-axi draft check
… only (kunchenguid#5520) * fix(bin): drop status prose from the inactive-outcome dedupe identity The inactive-outcome receipt fingerprint included the child's sanitized last status line, so a persistent child appending routine prose after one terminal outcome minted a fresh parent event per sentence. Bind the identity to incarnation, task id, terminal state, and PR only, keeping the last line in the record as status_head evidence. Fixes kunchenguid#2960 * no-mistakes(document): note structured-only inactive receipt identity in regression coverage
…unchenguid#5707) * feat(bin): record the supervision host's dialog mirror on Claude and Cursor Add bin/fm-host-mirror.sh, the one owner of the supervision host's dialog mirror file, cursor, lock, and feed, plus the main-session key it keys entries to. The tracked Claude UserPromptSubmit and Stop hooks and the Cursor beforeSubmitPrompt and afterAgentResponse hooks record the captain's prompt and main's reply, only on a home with config/supervision-host, from a genuine primary checkout, for the lock-owning session. The mirror lands inert: writers record and nothing reads it yet; attended supervision on the host is the later step that consumes the feed. Codex, Grok, OpenCode, and omp have no writer here. * no-mistakes(review): Scope mirror dedup to session, atomic appends, marker-inclusive caps * no-mistakes(document): Clarify dialog mirror scope and remove duplicate contract details * no-mistakes(document): Correct Cursor hook documentation for dialog mirror registration * no-mistakes(review): Pass mirrored dialog text to jq via stdin * no-mistakes(document): Clarify dialog mirror documentation and remove duplicate claims * no-mistakes(review): Preserve internal dialog whitespace; drop mirror check and verified modes * no-mistakes(review): Drop only identical mirror repeats; remove redundant chmod guard
… escalations are not repeated (kunchenguid#5731) * fix(bin): retire check-row receipts on branch acks and report an unchanged situation once * fix(bin): scope a branch acknowledgement's check-row receipt retirement to its granted sequences The away posture lifts the attended partition's check/decision exclusions, so a branch grant can name check-kind rows - but the branch-actor ack still assumed check rows were main-only and skipped every receipt scan. The queue row was consumed while its terminal-outcome .pending receipt stayed behind, and each inactive-reconcile cadence scan re-queued the same fingerprint. In the first real away window on the supervision host that re-escalated one unchanged held-PR situation on every cycle (~1,734 of 4,149 outcomes). A branch ack now scans inactive-outcome and inactive-reconcile receipts and commits secondmate stall receipts against exactly the sequences in its eligible-row snapshot - the same rows it consumes - instead of none. Attended grants still name no check row, so the scans find nothing. * fix(bin): store a repeated captain verdict as routine while the task's durable situation is provably unchanged fm-branch-outcome.sh append computes a mechanical situation key per captain row - metadata bytes, captured status-log endpoint and identity, live crew-state verb, worktree head - and anchors it in state/.<task>.branch-captain-key. A later captain verdict whose recomputed key matches is stored as routine with "unchanged since seq <N>:" prefixed to its summary, so one situation escalates once until something provably changes. A task with no readable status ledger is never demoted, an unreadable record fails toward reporting, and teardown removes the sidecar with the task's other branch records. The append-only store schema is unchanged. This covers both hosts: the Pi supervision branch and the supervision host both funnel reports through append. * docs: check rows are main-owned only while attended; the away posture grants them to the branch, whose ack retires their receipts exactly * test: the away-flood reproduction as a regression test (branch ack retires the receipt and later scans stay quiet), store-level dedupe coverage, and a branch-ack secondmate stall receipt case * fix(bin): restore the secondmate child devin-config cleanup path The branch-captain-key sidecar addition mistyped the sibling entry as .$child_id.devin-config.json, so a forced secondmate teardown would have stopped removing each child's real <id>.devin-config.json. Restore the original path and add a behavioral test that stops the child sweep mid-loop on a refused close, proving the cleaned child's devin config and captain anchor are both removed while the unconsumed child's records are retained. * no-mistakes(review): Key captain dedupe on the covered wake rows' fingerprint * no-mistakes(review): Drop captain-key demotion; prove one escalation on both surfaces * no-mistakes(review): Drop unrelated teardown test; cite both receipt test files * no-mistakes(document): Docs already match branch-ack check-receipt retirement
…oorbell (kunchenguid#5664) * fix(calm): deliver Claude-bound operational input as a record-backed doorbell Claude Code 2.1.280 removes U+2063 from every submitted prompt, so a typed operational envelope reaches a Claude Code primary as plain text. The away daemon now writes the envelope to a record under state/operational-inbox and types only a plain doorbell naming it; the /afk return check and the Calm mod recognize the doorbell only when that record holds a current envelope. Marker- preserving harnesses keep the typed envelope. The live Calm guard accepts the 2.1.280 module-load log line, drives the doorbell, and asserts thinking stays hidden. * no-mistakes(review): Fix operational record retention at 7 days and document prune limit * no-mistakes(document): Point Calm bounds at 2.1.280 evidence; fix afk-exit comment * no-mistakes(lint): Pick newest Calm e2e transcript without parsing ls * docs(calm): add a minimal turning-Calm-on step for Claude Code * fix(spawn): deliver the Claude launch brief as a record-backed doorbell Claude Code strips U+2063 from the launch-prompt argument too, so a worker's launch brief arrived with its operational marker removed. Publish the brief as a record in the receiving home's operational inbox - a secondmate's own state, not the primary's - and pass only the printable doorbell naming it, falling back to the typed envelope when the record cannot be published so the brief body still delivers. Unwrap doorbell-carried digests in the daemon digest tests that still read the raw send log under the claude pin, and update the documented bounds now that launch briefs hide like the other operational rows. * test(spawn): cover a secondmate's launch-brief record landing in its own home The record-backed doorbell resolves its state through the receiving pane's home, so prove a claude secondmate launch publishes into the seeded secondmate's operational inbox and never leaks a record into the primary's. * no-mistakes(review): Pass primary harness to daemon, tighten retention, refresh verdicts * no-mistakes(review): Prune operational records by exact seven-day elapsed age * no-mistakes(review): Batch record pruning so large inboxes still expire * no-mistakes(review): Refuse Claude spawn when brief record cannot publish * no-mistakes(review): Drop thinking probe from Claude Calm live test and docs * no-mistakes(review): Record dated Claude Code 2.1.282 reproduction evidence * no-mistakes(document): Clarify operational doorbell documentation and record expiry * no-mistakes(document): Correct AFK escalation carrier guidance * no-mistakes(review): Describe operational record retention as about seven days * no-mistakes(document): Clarify Calm delivery and operational record retention * no-mistakes(review): Remove out-of-scope Calm launch guide from Claude docs * no-mistakes(document): Document Claude launch-brief delivery and refusal * no-mistakes(document): Correct stale operational-input documentation * no-mistakes(ci): Fixed the stale Claude trust test to verify that worker and secondmate launches deliver readable, record-backed briefs instead of expecting brief paths in their commands. Annotated the daemon’s output variable for ShellCheck without changing behavior. The affected tests, daemon tests, ShellCheck, and diff check pass locally * no-mistakes(ci): parse rebased Claude launch after trailer hook prefix * no-mistakes(review): Trust launch-brief record and restore thinking bound doc * no-mistakes(review): Parse final Claude launch statement; drop Stop-hook docs --------- Co-authored-by: Mike Sewell <maikunari@protonmail.com> Co-authored-by: no-mistakes <no-mistakes@localhost>
kunchenguid#5583) A host-local relaunch rewrote only the far endpoint, so this home kept the old harness, model, and effort, and appending those keys after pr= broke pull-request poll authentication.
…ound (kunchenguid#5516) tests/fm-watch-triage.test.sh finishes in about 434s alone and about 698s under CI load, so the 900s bound the changed-suite runner applies produced a false timeout under ordinary concurrent validation. Raise the automatic bound to 1500s, which keeps every measured script under it while staying below the 30-minute normal CI tier so a genuinely hung script still fails here with its output before the job cap cancels the lane. Fixes kunchenguid#3869 Refs kunchenguid#3565
…kunchenguid#5728) * Fix nested watcher lock reclaim * no-mistakes(review): Elect a single steal-mutex reaper and bound arm TERM wait * no-mistakes(review): Reclaim self-held steal mutex and unify autoarm steal reaping * no-mistakes(review): Resume own interrupted steal reap from its tombstone
…nguid#5710) * test: hold the back-to-back boundary close on the host's own clock test_park_boundary_holds_under_back_to_back_closes assumed two engine turns fit in the ~16s pre-refusal window and that the stub finished a turn in 3s. Under load the stub's real drain, report, and acknowledgement take ~13s, so the turn either died at its bound (which hands the wake to main, no boundary line) or the second close landed past the window and the fixture failed while the boundary held. 3 failures in 5 runs at a load average near 11. Hold the first turn on a release file instead: once the engine is in flight, a second close is appended mid-turn and the turn is released as the refusal window opens (park bound minus turn bound and grace, read off the host's own start record). The queued close can then only wait for the boundary on any machine speed, which is what the test asserts: the boundary line ends the output, the demo.status row stays queued for main, and no second engine turn ever starts. A host too loaded to start the turn at all hands the first close to the same boundary exit. After: 12/12 at load ~15-42. * no-mistakes(review): Print boundary test deadline as a decimal integer * no-mistakes(review): Hold boundary test turn on a FIFO, require full sequence * no-mistakes(review): Remove stray before/after supervision-host test copies * test: hold the late close's render until the refusal window opens The boundary recheck test's node shim slept a fixed 10s, which assumed the first close was read before the host's refusal window opened. Under load the close arrived after the refusal check, so the host correctly refused it before the successor started and the render snapshot never appeared. Block the wake-prompt render on a FIFO released at the refusal-open instant read from the host's own start record, so the pre-turn recheck must refuse on any machine speed. * no-mistakes(review): Derive minimal park bounds and refresh supervision-host shard hint * no-mistakes(review): Drive park-boundary tests from a seam-gated host test clock
* fix(bin): stage remote home clones before publishing them A remote home provision cloned the code root directly into the public FM_HOME path while rollback() claimed rm -rf of that same path on any failure. Bash defers trapped signals past a foreground child, but any other cleanup or lifecycle path that removes the home directory races the live clone's object copy, producing the CI flake "fatal: failed to copy file to .../.git/objects/...: No such file or directory". Clone into a private staging directory beside the home and publish with an atomic rename once complete, so no cleanup can remove a directory a live clone is still writing; a home that appears mid-provision now dies cleanly instead of inheriting torn state. The regression coverage holds a real clone mid-copy, removes the public path, and requires the provision to finish and publish intact. * no-mistakes(review): Prove home ownership by sentinel and hold only a live clone * no-mistakes(review): Assert raced provision publishes a complete, intact clone * no-mistakes(document): Document remote home staging and publication safety * no-mistakes(lint): Fix ShellCheck warning in clone integrity assertion * no-mistakes(document): Clarify remote home publication and rollback guarantees
* fix(control): keep a relaunched Pi worker's herdr pane status authority alive Defect: after `bin/fm-control.sh <id> relaunch` (observed live on a herdr Pi crewmate whose pane read idle while it ran its validation pipeline), the pane froze at whatever its previous agent had last reported. Cause, measured on herdr 0.9.1 against a real Pi: a pane has one status authority, and for Pi with its integration installed that authority is the lifecycle hooks, so herdr also skips screen detection for the pane. In the crew shape the registration outlives its agent process (upstream issue kunchenguid#4115; docs/herdr-backend.md "Restart and liveness behavior"), and herdr applies only reports carrying the session identity it bound. A replacement started fresh in that pane reports a NEW session, so its state reports are ignored and the pane stays frozen. Nothing from outside repairs it: `pane report-agent-session` and `pane report-agent` for `herdr:pi` are accepted (rc=0) without being applied unless the reporter is the registered pane agent, and `pane release-agent` on the stale record changes nothing. Fix: a relaunch preserves the binding instead of fighting it. The launch owner reads the session reference the endpoint's own runtime recorded (`fm_backend_herdr_pane_agent_session_ref`) and passes it back as Pi's own `--session <path-or-id>` (`relaunch_resume_args`; `fm_control_relaunch_resume_flag` owns which adapters and which registered-agent labels qualify). That is the same reference herdr itself resumes Pi panes with after a server restart, and the resumed session's reports land again, which the live check confirmed: the pane returned to working while the replacement worked and idle when it settled, on the same session identity. Safety: relaunch-only (a fresh spawn binds nothing), herdr-only (the one adapter that records a per-pane session), Pi-family only, and only when the registration's own agent label matches - so no other adapter's conversation can be handed to a Pi launch. An unreadable, missing, or malformed reference degrades to exactly the fresh-session launch that existed before. No lifecycle, liveness, isolation, or merge guard is touched, and an empty result leaves every non-Pi launch byte-identical. `resume` remains a refused verb; docs/agent-control.md and the harness-adapters references are corrected where they claimed Pi had no verified resume form at all. * no-mistakes(document): docs: correct relaunch session-authority ownership and skill paths * no-mistakes(document): docs: correct stale control-plane ownership claim * no-mistakes(document): docs: drop unverified Herdr restart resume claim * no-mistakes(test): Added offline Herdr Pi session-authority relaunch coverage * no-mistakes(document): Document Herdr Pi relaunch session continuity * no-mistakes(ci): The failing remote relaunch test tried to arm a PR poll for a secondmate, which `fm-pr-check.sh` correctly refuses. Removed that invalid test scenario; the remaining remote relaunch tests pass, and `git diff --check` is clean
…nchenguid#5758) Main has been red since fm-pr-check.sh began refusing to arm a merge poll on a kind=secondmate record (kunchenguid#5696): the relaunch-ordering case in tests/fm-remote-secondmate-relaunch.test.sh armed its fixture through that entry point and could no longer be set up. The ordering guarantee still matters: a secondmate record armed before the refusal can legitimately carry a trailing pr=/pr_head= identity block until the watcher retires it, and fm-remote-secondmate-relaunch.sh must still keep that block last when republishing harness/model/effort. Seed the fixture the way such a record was really written - pr= appended last to the meta, then the poll artifacts published through the same fm_pr_poll_prepare/fm_pr_poll_publish_prepared pair fm-pr-check.sh uses, a pattern tests/fm-pr-check-security.test.sh already follows - and drop the now-unused fake gh fixture. The kunchenguid#5696 refusal itself stays pinned by the security suite's secondmate-record case.
…id#5748) * feat: run attended supervision on the host for Claude and Cursor On a home opted into config/supervision-host with a Claude or Cursor primary, the supervision host now takes the attended wakes the Pi branch would take: routine outcomes stay off main, and a captain outcome wakes main once with a branch-outcome line and waits in the drain's new BRANCH OUTCOMES section until main acknowledges it with mark-processed. - The offer rule moves into branchOfferForWake, shared by the Pi watcher and the host through bin/fm-branch-dispatch.mjs offer. - The host feeds the dialog mirror at the head of each attended wake and passes a close through unchanged when it is main-only, the engine or a tool is missing, the primary has no verified mirror, the main session cannot be identified, or the session is cooling down. - The drain presents captain outcomes first, one line per task, never behind older routine outcomes, and collapses routine overflow into a count that is marked read. - The return advances the store's read cursor through the away window once the brief has rendered, so the first drain does not replay it. - The branch prompt's mirror wording is host-neutral, and the rule to report what main must act on as captain, once per unchanged situation, applies only to the attended posture on the host. * docs: record the attended supervision host live check * no-mistakes(review): Present pre-window unread outcomes and contiguous captain prefix * no-mistakes(review): Return brief presents every row it marks read * no-mistakes(review): Return brief lists every unread outcome in one list * no-mistakes(review): Keep return list in store order and gate cursor failures * no-mistakes(review): Make the drain the only branch-outcome presenter after return * no-mistakes(review): Gate return on drain outcome failures; byte-count outcome budgets * no-mistakes(review): Gate drain on projection failures; UTF-8-safe byte cuts * no-mistakes(review): Fail drain without jq; hand unreadable prompt mirror to main * no-mistakes(document): Correct supervision-host return and drain documentation * no-mistakes(review): Recheck attended offer at turn start; honest failed-drain brief * no-mistakes(document): Correct supervision-host posture and drain documentation * no-mistakes(document): Documentation remains accurate for attended supervision
…unchenguid#5753) Each '# shellcheck source=' directive makes ShellCheck's external-source traversal expand that library's whole transitive graph again at the site. fm-pending-reply-lib carried three directed lazy sources of fm-wake-lib and two of fm-parent-channel-lib on identical per-call re-source sites, so one file analysis peaked above 4 GiB and every caller (fm-watch, fm-teardown) inherited the multiplier - the root cause of the PR kunchenguid#5732 Lint 1 OOM kill. Keep the runtime '.' commands byte-identical: the lazy re-source under 'local STATE FM_WAKE_QUEUE FM_WAKE_QUEUE_LOCK' is real behavior. Drop the duplicate directives so each library expands once per unit, and drop the tmux/classify directives since classify already arrives through the kept fm-wake-lib expansion and no tmux symbol is referenced here. The directive above the lib-dir assignment is kept - it binds the bin/ prefix so the undirected sites still resolve without SC1091. Measured peak RSS, ShellCheck 0.11.0 -x on Linux arm64: bin/fm-pending-reply-lib.sh 4.06 GiB -> 1.96 GiB, zero findings
kunchenguid#5773) * fix: split bash 5.2 sibling $() in recovery mint and delivery log Sibling command substitutions on one line can empty a recovery generation under bash 5.2 when a CHLD trap is set. Mint pid/epoch sequentially, refuse empty tokens before write, and clean delivery fields before printf. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: split bash 5.2 sibling $() in recovery mint and delivery log Sibling command substitutions on one line can empty a recovery generation under bash 5.2 when a CHLD trap is set. Mint pid/epoch sequentially, refuse empty tokens before write, and clean delivery fields before printf. Co-authored-by: Cursor <cursoragent@cursor.com> * fix: keep recovery mint failure semantics after sibling $() split Remove the new pid/date refusal and grammar guard so a mint miss still yields a grammar-valid token and a durable wake row, matching accepted review intent. Drop the fake-failing-date case that locked in the refuse. Co-authored-by: Cursor <cursoragent@cursor.com> * no-mistakes(document): Point recovery-mint hazard comment at its regression test --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…stop calling Escape safe there (kunchenguid#5791) Fixes kunchenguid#4520
…unchenguid#5790) Fixes kunchenguid#4756 The voice status reader in bin/fm_voice_records.py reports each worker's state from the last non-blank line of its status log. When a worker appends a status line and then a line of plain prose, the reader reported "note" with the prose line instead of the declared state, diverging from bin/fm-classify-lib.sh's shell scan. Scan back through the tail for the newest line whose prefix is a single lowercase verb-shaped word (letters and hyphens), and report that event's verb instead of always taking the last line. An unrecognised verb-shaped prefix still reports "note" rather than letting an earlier recognised line answer for it, and free text with no colon is skipped as prose. When the tail holds no such event, the last line is reported exactly as before.
…nd an opt-in flag (kunchenguid#7634) * refactor(bin): share the branch-to-PR lookup through fm-pr-lib * feat(bin): notify owned PR health changes behind an opt-in flag * no-mistakes(review): drop status-line ownership, key checks per name, remove PR wait rule * no-mistakes(review): rediscover the branch PR once the cached one is final * no-mistakes(review): refuse retired re-enrollment, require marked checks, verify owned records * no-mistakes(document): note shared branch-to-PR lookup in scripts table * fix(bin): notify reported failures when no checks are required * no-mistakes(document): inventory PR health flag and state, note new pr-forge member
* Isolate real Rex tests with private servers * no-mistakes(review): Trap Rex cleanup early and prove passive-read autostart guard * no-mistakes(document): Consolidate Rex test isolation documentation
* fix(bin): report an outdated bootstrap tool instead of a missing one detect_local_tools printed "MISSING:" for a treehouse without --lease, a no-mistakes below 1.46.0, a gh-axi below its floor, and an incompatible tasks-axi or quota-axi, so a supervisor read an installed tool as absent and took the first-install remediation again. Report those as "OUTDATED:" with the installed version, the requirement and the tool's own upgrade command: treehouse update, no-mistakes update, gh-axi update and quota-axi update. tasks-axi ships no self-updater, so it keeps its install command. "MISSING:" stays for absent tools only, and lavish-axi keeps PRESENTATION_UNAVAILABLE with its text fallback. Update the bootstrap-diagnostics handler, the AGENTS.md section 13 trigger list and the operator diagnostics in docs/configuration.md, and cover absent, below-floor and at-floor for every floored tool in tests/fm-bootstrap.test.sh. * no-mistakes(document): Documented distinct bootstrap tool diagnostics * fix(bin): reuse the tasks-axi check's own probe results Rendering the tasks-axi OUTDATED requirement text ran the two `--help` capability probes again after the compatibility check had already run them, including when session start handed bootstrap an incompatible verdict. The check now records the reason behind its verdict - the version floor, or the capability probes the build fails - and reuses it, both within the process and across the existing one-hop handoff, so naming a missing capability costs no second probe pass. A build below the floor is answered by its version alone and never pays for capability probes it cannot use. The bootstrap tests that simulate a tool as absent removed it from the fake bin directory while bootstrap kept searching the fallback base PATH, so a real host install could satisfy the lookup instead of the expected MISSING line. Those cases now hide the tool from the base path as well, and a new case pins the contrast between a bare and a curated fallback. Verified: the touched bootstrap suite passes, and its probe-count and capability assertions fail against the pre-fix scripts. Run with a fallback base PATH carrying real installs of the absent tools, the pre-fix suite fails on "absent treehouse reports a missing tool" while the fixed suite passes every case. * no-mistakes(document): Documented missing versus outdated tool diagnostics * no-mistakes(document): Refresh outdated-tool documentation * no-mistakes(review): Reuse shared version parser for outdated diagnostics * no-mistakes(document): Correct outdated-tool diagnostics documentation * no-mistakes(document): Align toolchain upgrade documentation * fix(skills): keep main's bootstrap-diagnostics description and only add OUTDATED
…e verbatim match (kunchenguid#7530) * Clarify ahoy legacy startup exclusion * Fix quoted payload code span
…unchenguid#7637) * fix(timeout): bypass shell builtins when invoking external runners * no-mistakes(review): Remove redundant slashless runner resolution from external timeout
…urable (kunchenguid#6094) * fix: count answered captain holds in the Done archive at the completion gate Done retention moves closed markdown rows into the configured Done archive, which tasks-axi show never reads, so verify, complete, and scout teardown refused an answered captain hold once retention archived it. The completion gate now reads the newest archived block for an attested entry, under its exact or legacy identity, and accepts it only through a recorded resolution. Unanswered archived closes and holds found nowhere still refuse. * no-mistakes(document): document Done-archive durability in captain-hold verification record * no-mistakes(ci): Fixed the selected Greptile findings in bin/fm-captain-hold.sh (all in the Done-archive read path added by this PR). code_change_needed=true. ci-3 (Archived reads can hang): task_show_archived() now bounds the replayed archive `tasks-axi show` with the same read bound the live backlog read uses (FM_BACKLOG_ROW_TIMEOUT_SECS via fm_run_timed, sanitising a non-positive/padded value to 10s, mirroring fm_backlog_row_show). A bound hit is treated as 124, not absence: task_show_archived returns 124 and prints a loud reason; archived_entry() propagates 124; verify_entry_durable() exits 124 on the archived-read bound (alongside its existing resolve 124 handling); verify_hold_durable() exits 124 rather than reading the row as absent. So verify can no longer hang and complete can no longer stay blocked while holding the origin metadata lock. ci-4 (User-level archive setting ignored): added captain_markdown_archive_setting(), which resolves the [markdown] archive key with tasks-axi's own source precedence — the backlog root's .tasks.toml first, then $HOME/.tasks-axi/config.toml — before the existing default of done-archive.md beside the backlog; a relative value still resolves against the backlog root, which I verified empirically matches tasks-axi (root config wins over home config; a relative home-config value resolves against the backlog root; default stays data/done-archive.md). tasks-axi exposes no command for the archive path, so re-parsing with matched precedence is the available approach. Added test_archived_answer_in_a_user_configured_archive_satisfies_the_gate, which sets the archive only in a fixture $HOME/.tasks-axi/config.toml and drives the whole hold→answer→retention→verify/complete/teardown flow with that HOME, asserting the answered call archived to the relocated location still satisfies the gate. ci-5 (repeated archive scans, P2): left unchanged per the instruction's opt-out. A clean reuse of the row archived_entry already found is not small: archived_entry runs inside a command substitution, so its TASK_SHOW_OUTPUT dies with the subshell and cannot be handed to verify_hold_durable without restructuring both to pass the loaded row through the current shell. The growth is bounded (O(archive size) per attested entry) and P2, so hardening it would add more machinery than the fix warrants. Unanswered archived closes and holds found nowhere still refuse (existing test coverage retained). Verification: `bash -n` clean; pinned ShellCheck 0.11.0 (`shellcheck -x bin/fm-captain-hold.sh`) reports 0 findings; tasks-axi archive-precedence behaviour confirmed by direct experiment. The full fm-captain-hold-lifecycle.test.sh suite (which exercises both the pre-existing archived-gate test and the new user-config test) is slow due to beads spawns and was still running at report time; recommend confirming it green before merge. ci-1/ci-2 need no code change (action_required fork workflow runs awaiting maintainer approval), per the recorded user decision * no-mistakes(document): Document Done-archive reads under backlog row timeout * docs: say only the archive row replay is bounded, not the archive scan * no-mistakes(document): Move Done-archive rule to the complete section
…kunchenguid#7627) * fix(bin): optionally read a PR with no CI checks as unverified, not checks green nm_ci_checks_state maps the ci log marker "no CI checks reported - still monitoring" to green, the same verdict as "all CI checks passed". A pull request that no check ever ran against therefore surfaced as "checks green: PR ready for review" (kunchenguid#3216). Add a no-checks verdict for that marker, returned only in a home that creates config/ci-no-checks-not-green. The run-step consumer reports working with a detail naming the real condition, and the status-log override no longer promotes a crew's own "checks green" report while that marker is the latest. Without the flag every reading is unchanged. Co-authored-by: Harlan Do <hieudtr8@gmail.com> * no-mistakes(document): document ended-monitor reading under the zero-checks flag * fix(bin): read a PR with no CI checks as unverified on every home The ci log marker "no CI checks reported - still monitoring" now always yields the no-checks verdict, so a pull request that no check ran against is reported as working instead of checks green (kunchenguid#3216). The opt-in flag, its docs section, and its skill entry are removed, the default-path test now expects working, and the usage header and architecture note describe the no-checks reading. --------- Co-authored-by: Harlan Do <hieudtr8@gmail.com>
… test root-safe (kunchenguid#7650) * no-mistakes(review): correct the counter-reset log line, the sentinel comment, and the root proof Three findings the review step recorded but never applied, all in this branch's own change. The clean-rollback branch of the counter-reset guard logged "cap state rolled back", copied from the sibling marker-write branch. That is wrong here: the cap marker was already written before this block and _wedge_cap_rollback only truncates the counter, re-arms the since-file and clears write tracking - it never clears the markers. The window therefore stays silenced for FM_CAP_HORIZON_SECS, so the one operator-facing line must say that rather than claim the cap state was rolled back. The rollback behaviour is unchanged and no marker-clear call is added: the marker is deliberately kept so the window stays quiet. The sentinel short-circuit comment opened "from either route that has to defend cap state" and then enumerated three routes. Wording only. The new regression case injected its fault with chmod 444, which does not restrict root, so under root the guard would never fire and the case would fail on its own exit-code assertion. It now skips as root the way this suite's other mode-based faults do. The assertions are untouched: as the ordinary user the case still proves the guard. * no-mistakes(review): name both ways a capped window leaves the silence in the reset log line The tier-0 message for a refused post-fire escalation-counter reset says the cap marker stands, so the window stays silent until its horizon elapses. That names one release route where there are two: wedge_episode_end drops the marker through _wedge_cap_markers_clear, so any episode-ending route ends the silence early, and docs/configuration.md records deleting the marker as the operator's way to re-engage a capped window at once. This tier writes no sentinel, so the marker is the only thing left to remove. The message now names the horizon, an episode-ending route, and the operator remedy, in one line in the same shape and position as its sibling tier-0 messages. The rollback behaviour, the marker handling and the tier are unchanged. * no-mistakes(document): correct root-skip comment's mode-bit precedent in wedge-cap test
* fix(bin): resurface wakes appended while a handling successor is parked The supervision host parks on the handling successor it starts for a wake its engine handled, and a handling successor never resurfaced recovery, so a row another script appended during the park (a captain inbox note from the Basecamp integration) sat queued until an unrelated close - four hours in the observed case. A handling successor now records the newest queued sequence at its start and closes once with the recovery wake while a newer row is still queued; a newer row already consumed only advances that mark, so the predecessor's delivered episode is still never re-announced. Follow-ups, not built here: - A row appended in the short window between a handled close and its successor's start (about a second) is below the successor's mark; if the engine handles that close without claiming the row, the row waits for the next close. Closing that window needs the host to treat rows left queued after a handled attended turn as main's, which is a separate host change. - After a machine reboot the primary session does not restart on its own, so queued wakes wait until it is resumed by hand. Either auto-resume the primary session after boot, or alert the owner when the primary session is down while wakes are queued. * no-mistakes(review): Close handling-successor startup wake race * no-mistakes(test): Stabilize parked-host inbox wake regression timeout * no-mistakes(document): Clarify handling-successor wake continuity guarantees * no-mistakes(ci): Stabilized the flaky remote secondmate lifecycle test by extending the auto-relaunch watcher deadline from ~30 to ~60 seconds. The CI runner exceeded the old deadline by about 3 seconds; the changed production branch was not reachable in this fixture. `bash -n` and `git diff --check` pass. The full test was attempted but exceeded the 600-second local execution limit before reaching this case * no-mistakes(ci): Fixed flaky concurrent Herdr recovery testing by retrying only documented session-lock contention after both competitors finish. Cleanup now handles fixture-owned read-only hooks. Targeted E2E passed; bash syntax and diff checks passed. Shellcheck reported only existing SC1091 source-following notices * no-mistakes(test): Preserve successor wake boundaries across handoffs * no-mistakes(test): Revert successor boundaries and drain teardown fixture wake * no-mistakes(test): Notify main when downtime handback publication fails * no-mistakes(test): Revert downtime handback notification changes * no-mistakes(document): Correct stale supervision wake routing documentation * fix(bin): stop the supervision host deadlocking and re-announcing handed-back closes Two defects in this branch's handling-successor changes surfaced as the "Behavior portable serial 2" shard hanging at its 30-minute limit. A TERM that lands while the host settles a handled turn's successor handoff left the host holding the wake-queue and recovery-marker locks; its exit cleanup then waited forever on the queue lock it still owned. The cleanup now releases those locks if this process owns them before releasing its grant. When an accepted close turns main-only at its turn, the host hands it back and leaves the successor running, but republished the episode as pending downtime, so that successor re-announced the same close and exited. The handback now republishes the episode as already announced; a later durable append still opens a pending episode the successor recovers. * no-mistakes(test): Park on successor after handling settlement failure * no-mistakes(test): Use tab-delimited handled-settlement failure logs --------- Co-authored-by: Caleb LeNoir <calebl@users.noreply.github.com>
* fix(bin): keep accented letters in pending-reply request summaries fm_pending_reply_summarize deleted every byte outside printable ASCII, so a Portuguese summary lost its accents: "não faça a correção" was stored as "no faa a correo". It now removes control characters only. The 120-character cap counts UTF-8 characters from lead bytes, so it never splits a letter under any caller locale, including the C locale. * no-mistakes(document): Documentar preservação de acentos nos resumos --------- Co-authored-by: Alexandre Custodio <alexandre.custodio@soraschools.com>
…id#7149) * Add explicit OpenRouter Jev dispatch resolution * no-mistakes(document): Clarify dispatch documentation and remove stale duplicates * chore: exclude local Stele binding from PR * no-mistakes(document): Correct dispatch help and never-send documentation * ci: retrigger CI after a timed-out test shard * no-mistakes(document): Correct dispatch architecture documentation ownership
…imits (kunchenguid#6983) Opt-in: fm-watch.sh publishes state/lane-capacity.json via bin/fm-capacity.sh --publish only while config/lane-capacity exists; adds beat pressure and keyed limits to fm-capacity.sh. Unconfigured fm-capacity output unchanged.
…#5359) * fix(bin): transport contribution-input JSON through slurpfile, not argjson bin/fm-fleet-snapshot.sh --contribution-input passed the full backlog and task-record JSON to jq through --argjson. A single --argjson argument is capped by the kernel's MAX_ARG_STRLEN (131072 bytes on Linux), so a large enough backlog made the kernel refuse to exec jq while the script still exited 0 with a broken result. Transport both documents through temporary files and --slurpfile, matching the pattern the default snapshot path already uses, and propagate a non-zero exit when that jq invocation fails. Fixes kunchenguid#5349 * test: narrow contribution-input regression to final assembly failure The ARG_MAX transport is already fixed on main by kunchenguid#6830, so keep only the regression proving a failing final jq assembly exits non-zero.
* Fix Rex secondmate session lifecycle * no-mistakes(review): Retire Rex secondmate sessions under the owning home identity * no-mistakes(review): Limit Rex FM_ROOT label override to secondmate launches * no-mistakes(document): Clarify Rex secondmate grouping and lifecycle documentation
…gins, and exit (kunchenguid#7336) * feat(opencode): add OpenCode 2 support with version-gated launch and plugins OpenCode 2 dropped the v1 server-plugin hook API, so the five fm.primary.* plugins now export a default definition with an id and a setup function alongside their legacy OpenCode 1 server export, backed by a new shared lib/fm-opencode2.js helper; the turn-end guard, watch-arm, cd-check, pretool-check, and sessionstart-nudge install their behavior from setup(ctx) on OpenCode 2 and keep the server entrypoint for OpenCode 1. fm-spawn probes the resolved opencode executable's --version once and branches the launch, model flag, reasoning config, exit key, and busy-state plugin off the detected major. A parsed major >= 2 takes the OpenCode 2 shape (no --model, the model pinned through OPENCODE_CONFIG_CONTENT, a raw Ctrl-C exit, the session.execution.* busy plugin); OpenCode 1.x and any undetected or unparseable version fall back to the 1.x shape (--model, 1.x config, /exit) everywhere. The worker busy-state plugin is always provisioned, but OpenCode 1 keeps the pre-port session.status/session.idle plugin so its busy-state wiring is unchanged, and only a major >= 2 gets the session.execution plugin. Adapter and supervision docs, OpenCode 2 live fixtures, and regression tests for the unknown->1.x launch path and the version-gated busy-plugin shape are included. * no-mistakes(review): gate OpenCode 2 busy plugin turn-end touch on notifyTurnEnd * feat(opencode): launch detected OpenCode 2 workers with --standalone OpenCode 2's default launch attaches to the shared background service, which hosts plugins with the service's environment and ignores the pane's OPENCODE_CONFIG_CONTENT. A private server keeps per-worker model, permissions, FM_HOME, and lock ancestry correct. 1.x and undetected versions are unchanged. Ported from kunchenguid#5143. Co-authored-by: Girik Bajaj <bajajgirik2010@gmail.com> * fix(opencode): resolve the OpenCode 2 project location and treat every terminal turn as a turn end OpenCode 2.0.x hands plugins the project only as ctx.location.project.directory and takes ctx.session.prompt({ sessionID, text }), so the V2 setup path never found its root or delivered a prompt. Interrupted and failed turns now end a turn like a succeeded one, while a server shutdown does not. Ported from kunchenguid#5143. Co-authored-by: Girik Bajaj <bajajgirik2010@gmail.com> * test(opencode): add a token-free OpenCode 2 native plugin fixture to the live e2e A private opencode serve plus a loopback provider proves the five primary plugins load and act on OpenCode 2: startup nudge, cd and watcher-redirection guards, turn-end guard, watcher arming across succeeded, interrupted, and failed turns. The fixture declares the complete model capabilities object, which OpenCode 2.0.11 requires and 2.0.22 accepts. FM_OPENCODE_LIVE_BINS runs it across several OpenCode 2 executables. The credentialed half now picks its run flags and session tables by detected major, so it keeps working on 1.x. Fixture ported from kunchenguid#5143. Co-authored-by: Girik Bajaj <bajajgirik2010@gmail.com> * feat(opencode): send one Enter when an OpenCode 2 launch leaves the brief unsent OpenCode 2.0.11 prefills the --prompt brief without submitting it, while 2.0.22 submits it. After launch, fm-spawn waits for the worker's busy-state plugin to report the turn start, sends one Enter only if it has not started and the composer is not empty, and fails the spawn if the turn never starts. * docs(opencode): document the OpenCode 2 launch, delivery gate, and standalone primary homes Records the 2.0.11 and 2.0.22 live evidence for the native plugin fixture and the post-launch brief delivery gate. * no-mistakes(review): Trim OpenCode 2 adapter shapes and harden Ctrl-C exit * no-mistakes(review): Record OpenCode 2 live seatbelt and exit evidence * no-mistakes(test): Document OpenCode 2.0.22 wide-pane sidebar exit refusal limitation * no-mistakes(document): Refresh OpenCode 2 exit-key and seatbelt payload docs * test(opencode): verify OpenCode 1.x live with the dual-export plugins Pin the live e2e TUI panes to the resolved opencode executable, and record OpenCode 1.15.7, 1.18.20, and 1.18.31 evidence for plugin loading, the turn-end guard, watcher continuity, and 1.x worker launch and exit. * no-mistakes(review): Defer OpenCode 2 Enter on unknown composer; widen turn-end docs * no-mistakes(document): Document OpenCode 2 delivery gate unknown-composer test outcomes --------- Co-authored-by: Niranjan <nniranc@gmail.com> Co-authored-by: Girik Bajaj <bajajgirik2010@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t the most recent group member (kunchenguid#7662) * fix(bin): resolve the tmux container from the caller's own session, not the most recent group member * no-mistakes(test): Fall back when the $TMUX session id is stale * no-mistakes(document): Document tmux container choice under grouped sessions
…er (kunchenguid#7647) * fix(bin): read a fully dim Cursor placeholder as an empty composer cursor-agent 2026.10.19 draws the composer glyph and its idle placeholder as one SGR 2 run with no reverse-video cell, so ghost stripping empties the row and the empty-content guard returned unknown. Relaunch and exit then refused on every idle Cursor pane on tmux. When a styled bare row strips to empty and the plain row is an agent glyph followed by an anchored idle placeholder, read it empty. Bright typed text, other dim text, and unstyled rows keep their existing verdicts. Fixes kunchenguid#7413 * docs(verification): record the fully dim Cursor placeholder verdict and refresh command
… composer as empty (kunchenguid#6163) * fix(bin): recognize a named Claude session's titled composer rule on cursorless backends `claude -n <name>` right-aligns the session name in the rule above the composer. Cursorless backends (Herdr) accepted only solid rules, so the scan saw the lower rule alone, the idle composer read `unknown`, and away-mode delivery to a named Claude primary deferred until return. A rule carrying one right-aligned title now opens a separator pair. It never closes one, the pair it opens counts only over an agent-glyph row, and it is never a valid Pi pair, so Pi's verdict is unchanged. The away-mode composer deferral now names which refusal it was: unsubmitted text, or no composer shape recognized. * no-mistakes(review): Reword composer deferral reason to cover busy harness * no-mistakes(ci): ci-3 (Greptile, user chose to fix): The rule that must always hold is that Claude's titled opening rule is accepted for any non-empty session name that doesn't start with a space and doesn't contain the rule glyph `─`, including a multiword name. The only place that check lives is the title `case` in `_fm_composer_titled_rule_row` (bin/fm-composer-lib.sh). It now reads `case "$title" in ''|' '*|*─*) return 1 ;; esac`, as the user asked, and the comment above the function mentions multiword titles. I added regression fixtures to `test_matrix_claude_named_session_titled_rule` in tests/fm-composer-lib.test.sh, next to the existing single-word `Andrew` cases. They use the title `Andrew Chen` and check that an idle composer on herdr reads `empty` and a composer with typed text reads `pending`. Something you should know: the old pattern already accepted a title with a space in the middle. It only rejected a title ending in a space. So the new multiword fixtures also pass against the old code, and the bot's failure scenario did not actually happen for "Andrew Chen". What the fix really changes is that it drops the trailing-space rejection, as instructed. Checks run: tests/fm-composer-lib.test.sh and tests/fm-composer-ghost.test.sh both pass (exit 0, no failures), and `shellcheck -x` is clean on both changed files * no-mistakes(review): Match unnamed-ghost fixture rule width to the partner rule
…process groups (kunchenguid#7666) * fix(tests): reap Pi test process groups and stop arms whose ground is removed Orphaned stub-arm pollers from tests/fm-pi-watch-extension.test.sh outlived a failed case: they waited for release/stop files the failed driver never wrote, teardown only removed the temp root, and the loops had no other exit. Run each node driver in its own recorded process group and reap those groups in teardown before the root is removed; end every blocking stub loop once its own script is gone. bin/fm-watch-arm.sh now exits with a typed ground-removed line within one poll when its script, state directory, or home is deleted, instead of polling through the successor window. * no-mistakes(document): Document arm exit when its ground is removed
…alidating (kunchenguid#5947) * fix(bin): defer wedge escalation while an attributed validation run is running or fixing While a no-mistakes run executes a round, the worker is correctly idle behind its own blocking drive call, yet the watcher's wedge timer escalated a possible wedge every FM_STALE_ESCALATE_SECS. The timer now consults the attributed run step before escalating: a run the current-state reader reports as validating (running) or validating (fixing) for a named run defers onto the FM_PAUSE_RESURFACE_SECS recheck cadence, aged from a marker bound to that run id. Parked, terminal, ci-monitoring, coarse, runless, absent and unreadable runs keep the unchanged escalation ladder. * test: count only the validating-run read in the unarmed parked-gate probe * no-mistakes(document): add .validating-* to watcher-internals marker inventory * no-mistakes(document): Update fm-watch.sh comments for validating-deferral chain * no-mistakes(document): Document validating-run deferral in guard and handoff docs * feat(watch): gate the validating-run wedge deferral behind config/wedge-defer-validating The deferral changed the default for every home, unlike the comparable config/wedge-defer-parked-gate and config/wedge-defer-worker-wait waits. Absent the new presence flag the wedge timer spends no current-state read for it and keeps the unchanged ladder.
…unchenguid#6300) * fix(session-lock): identify a harness session on Windows/Git Bash Every session start on Cygwin (Git for Windows) refused the fleet lock and dropped to read-only with "cannot locate harness process in ancestry", so spawning, steering, merging, the wake-queue drain, and supervision repair were skipped on every start. Two independent causes, both on the identity path. Cygwin's ps has no -o option at all and fails the whole invocation with "unknown option -- o", so the ancestry walk aborted on its first hop. The walk now reads comm, args, and ppid through accessors that fall back to Cygwin's fixed ps columns, leaving the procps/BSD path unchanged. That alone does not resolve the session: the parent link from a shell the harness spawns does not cross the Cygwin boundary, and Cygwin reports that shell's PPID as 1, so no walk can reach a harness that is a native Windows process. Identity is instead taken from the session pid the harness publishes and confirmed against the Windows process table before it is used - the pid must still be live and its executable must independently identify a verified harness - so an absent, stale, or non-harness value is discarded rather than bound. Walking the real Windows parent chain was implemented and then removed as unsafe. MSYS emulates exec by spawning a fresh Windows process and exiting the old one, so intermediate shells vanish and a child's recorded parent is routinely a pid that no longer exists; Windows never reparents an orphan, so that dangling id stays and can be reissued to an unrelated process. Following it can bind a home's lock to the wrong process, which is the failure this file exists to prevent. A harness that publishes nothing stays unresolved, which leaves the session read-only exactly as before. Windows pids are tagged rather than stored bare. They are a different namespace: kill -0 reports a live Windows process as dead, and the number can collide with an unrelated live Cygwin pid. The tag makes the value non-numeric, so a consumer that treats it as a local pid - including a future kill - refuses it instead of acting on the wrong process. fm-sessionstart-nudge.sh carried a private second copy of the ownership walk and so stayed wrong after the owner was fixed, nudging a session that already held the lock. It now asks the owning function. Verified on Windows 11 (Git Bash, Cygwin ps 3.4.10): the lock is acquired, reports its holder, is idempotent, and refuses a bogus, dead, or non-harness published pid. Regressions cover both platform departures behind a fake process table, so they run on Linux and macOS CI too. The pre-existing e2e failure in this suite on Windows is unchanged from main; it cannot exec its symlinked fixture. Refs kunchenguid#3396 Claude-Session: https://claude.ai/code/session_01F9T29YDqYSkQeDTC2Nfi7h (cherry picked from commit 8b281b7) * fix(session-lock): keep the nudge's own ancestry question intact Delegating the nudge to fm_session_lock_owned_by_self changed the question it asks. The nudge asks whether a process in this ancestry took the lock; the ownership predicate additionally requires a verified harness in that ancestry, so a session whose lock was written by a plain shell started being nudged to run session start again. tests/fm-sessionstart-nudge.test.sh pins the looser contract deliberately. The walk stays local, now reading ppid through the portable accessor so it also survives a ps with no -o option, and defers to the ownership predicate only for a Windows-tagged holder, which is not in this process table at all. (cherry picked from commit 0baf64f) * fix(session-lock): accept a tagged identity in every gate that reads the lock The Windows identity added in this branch introduced a second shape into state/.lock. Six gates read that field, and each one answered "is this value usable" with its own inline numeric test, so every one of them read a valid Windows holder as malformed. The visible cost was concentrated in startup completion: the record was never written, and the clear/compact check that consumes it could never match, so every clear or compact on Windows repeated the full startup sequence. The deferred network sweeps reported ownership as changed when it had not, and the Stop auto-arm treated a dead Windows session as an unreadable lock rather than a recoverable one. fm-lease.sh was the outlier: rather than refusing a value it could not use, `tr -cd '0-9'` reduced the tag to its digits and produced a number naming an unrelated process in the local table. It happened to fail closed downstream, but deriving a wrong-namespace pid is precisely what the tag exists to prevent, so it now takes the value whole and requires a local pid. fm_session_pid_valid is now the single owner of that question and every gate delegates to it, so a third identity shape cannot split them again. Verified against the shipped bytes of each gate with a tagged lock: startup completion now records and is recognized (main's gate reruns the full startup on the same input), both network-ownership gates authorize their sweeps, and a tagged lock yields no lease holder pid instead of 7204. (cherry picked from commit 9256182) * fix(session-lock): gate the tagged identity on Windows and read it in every lock reader Accept win:<digits> only where fm_win_boundary_applies, with an all-digit remainder, so a Linux/macOS reader of a win:N lock stays inert as before. The sessionstart nudge routes its tagged case through fm_win_untag_pid. fm_session_lock_foreign_owner_live and fm_session_lock_inspect now validate through fm_session_pid_valid and resolve a tagged holder through fm_harness_pid_alive, so a live win:N holder reads as a live foreign owner and as held; unverifiable tagged holders stay unknown. * no-mistakes(document): Document Windows tagged session-lock identity in nudge and scripts docs * fix(session-lock): keep task leases and stale detection working for a tagged holder A Windows session can now hold the session lock as win:<pid>, but the task lease still accepted only a local pid: the claim recorded the short-lived fm-lease.sh process instead, and fm_lease_live rejected the tagged lock, so the lease was never live and the other supervision actor could clear it and act on the same task. The lease now records a live tagged lock holder whole and treats it as live while it is still the lock holder and a verified harness in the Windows process table. The session-lock library is loaded only for a win:-shaped value, so POSIX lease verdicts are unchanged. fm_session_lock_inspect left an exited tagged holder unknown, because fm_win_command could not tell an absent pid from an unreadable table. It now returns 1 only when a readable table proves the pid absent and 2 when the table cannot be read, so inspection matches the local-pid contract: absent is stale, a live non-harness is unknown with no live harness, and an unreadable table stays unknown. * no-mistakes(review): Restrict caller-supplied lease holder pid to local numeric * no-mistakes(review): Pass tagged session-lock holder whole to task lease * no-mistakes(review): Make tagged lease-holder regression tests fail without their fixes * fix(session-lock): take a Windows CLAUDE_PID only with its session id On Windows the session identity comes from CLAUDE_PID, which every process started from a Claude pane inherits, so an unrelated process carrying a copy could take the lock as that session and both would own it. The bridge now accepts CLAUDE_PID only together with CLAUDE_CODE_SESSION_ID. The trusted session-id gate matches the tagged win:<pid> form, so that id is recorded beside the lock, and the anchor stays tagged. fm-lock.sh and the ownership readers refuse the same tagged pid under a different recorded id as another live session's. A non-Claude primary hand-started from a Claude pane inherits both values and still resolves as that session; that remains a known limitation. POSIX behavior is unchanged: every new branch applies only to a tagged pid. * fix(session-lock): let a Windows /clear re-key its own lock /clear keeps the Claude process, so CLAUDE_PID stays while the session id changes. Refusing the same tagged pid under a different recorded id locked a session out of its own lock after /clear, which POSIX handles by re-keying the sidecar. Drop that refusal: a Windows CLAUDE_PID is still taken only together with its CLAUDE_CODE_SESSION_ID, and that id is still recorded beside the lock and re-keyed on /clear as on POSIX. * no-mistakes(review): Give tagged host test a session id; clarify Windows lock error * no-mistakes(document): Point the trust-gate comment to the Windows limitation --------- Co-authored-by: Lloyd <lloydkakunguwo@gmail.com>
Merge upstream 5d718d5 with a real second parent. Preserve Azure DevOps delivery and standalone report compaction, intent and custody contracts, quiet and away authority, supervision host handback and cleanup guards, auth forwarding, fleet sync, fork skills and CI shards. Adopt upstream for historical BEHIND-ONLY paths and equivalent snapshot/skill owners. Reconcile overlapping behavior and fixtures; verify lint, source-aware ShellCheck, affected delivery and supervision suites, and live Claude/Pi guards. Ruby-dependent workflow assertions require CI on this host.
…2", failed on one test in tests/fm-supervision-host.test.sh: `test_tagged_lock_holder_reaches_the_engine_whole`, which reported "tagged: the wake was not handled". This PR's merge brought the test in from upstream (kunchenguid#6300). The test is upstream's apart from one extra `ps` stub line added during the merge; the code it exercises (the session-lock library) is identical to upstream. Root cause: the test's wait budget is too short for the fork's CI runners. - Upstream's CI runs this test in about 23s. - The fork's CI runs this suite about 2x slower than upstream's: about 2.1x slower in this run, and about 1.9x at a610a4e, the fork's last green main run. - The sibling away-posture test (`test_away_wake_is_handled_on_the_engine_and_never_reaches_main`) uses the same budget of 150 polls for the watcher plus 250 polls for the wake to be handled. It already takes 44–46s on fork CI, both at a610a4e and in this run. - The tagged test is a bit slower still, because its fake `ps` and `uname` scripts start an extra shell on every lookup. It needs roughly 48s and timed out at about 45s. The supervision-host log showed only the host start line, with no close and no error. - Locally, upstream (5d718d5) and HEAD take the same time per test (for example 4.8s/4.8s and 4.5s/4.3s), so the fork's code does not slow this suite down. Invariant: each asynchronous wait in this suite must give enough time for the fork's CI runner to finish. Among this test's waits only the handled wait ran out; the watcher wait passed. Other fork merges have already widened waits in this suite the same way (450 polls for `watcher_live`, 600 for `hook_exited`). The away sibling has the same 250-poll handled wait and still passes, so I left it unchanged. Change: in the tagged test, the handled wait goes from 250 to 450 polls, with a two-line comment explaining why. The test's logic and assertions are unchanged. Verification: - `shellcheck -x tests/fm-supervision-host.test.sh` is clean. - The tagged and away tests pass in isolation, and the full tests/fm-supervision-host.test.sh suite passes locally (exit 0, 97 passes, no failures). - A CI rerun is needed to confirm the fix on the fork's runners. Not addressed here: "Behavior portable serial 1" (ci-2) was cancelled when it hit the 30-minute job timeout, because tests/fm-watch-triage.test.sh alone ran about 27 minutes. Upstream main's serial 1 has the same cancellation (runs 39940220247 and 39968718606), so it is inherited from upstream and not caused by this PR
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.
.agents/skills/afk/SKILL.md: Combined upstream spend-at-entry/return changes with fork uber refresh, guarded cleanup and escalation evidence..agents/skills/agent-skill-trigger-index/SKILL.md: Kept upstream trigger-index shortening and restored fork opentofu-iac-management and md-conflict-fastpath entries..agents/skills/ahoy/SKILL.md: Upstream version: fork blob is an older upstream version.agents/skills/away-quiet-supervision/SKILL.md: Restored fork guarded authority, uber refresh/exit and Pi main-parking rules; upstream left this owner unchanged since b062eb9..agents/skills/bearings/SKILL.md: Upstream version: fork blob is an older upstream version.agents/skills/bootstrap-diagnostics/SKILL.md: Upstream MISSING/OUTDATED distinction and triggers with fork no-mistakes 1.48.0 floor..agents/skills/harness-adapters/references/harness/opencode.md: Upstream version: fork blob is an older upstream version.agents/skills/operational-home-layout/SKILL.md: Retained upstream owner changes and restored fork pr-poll-last-reported and conflict-radar state entries..agents/skills/process-event-sources/SKILL.md: Upstream version: fork blob is an older upstream version.agents/skills/quiet/SKILL.md: Kept Pi/host quiet-with-words entry and no-daemon behavior while incorporating upstream current skill body..agents/skills/session-start-recovery/SKILL.md: Took exact current upstream skill/trigger owner; dropped fork duplicate edits in this moved/shortened skill family as directed, retaining fork feature skills and runtime owners..agents/skills/ship-landing/SKILL.md: Retained upstream additions and restored fork own-PR merged-PR teardown instruction plus conflict-radar and md-conflict-fastpath pointers..agents/skills/validation-supervision/SKILL.md: Restored fork conflict-radar and md-conflict-fastpath routing pointers; upstream left this owner unchanged since b062eb9..github/workflows/ci.yml: Retained upstream workflow/skip controls and fork serial host shard and Pi pins; snapshot count and diagnostic expect the restored 19-result suite..pi/extensions/fm-branch-supervision.ts: Kept quiet posture read through the shell owner, immediate quiet outcomes and endpoint bindings; adopted upstream async dispatch, current SDK processing boundaries, startup-context delivery and routing revalidation..pi/extensions/fm-primary-pi-watch.ts: Kept quiet-with-words routing and parked-main only for away; adopted upstream watcher generation, startup-context and custom-message boundary fixes..pi/extensions/lib/fm-branch-dispatch.ts: Kept quiet posture/authority and recorded backend-target routing; combined upstream fail-closed ambiguous alias detection and scoped decision routing.AGENTS.md: Upstream supervisor contract; retained quiet Pi main-parking distinction, ADO waiver wording, per-run lint-skip intake and uber skill triggers; combined GitLab null-pipeline waiver.README.md: Kept fork read-only conflict-radar entry; adopted upstream installation, runtime backend and skill layout updates.bin/backends/herdr.sh: Upstream version: fork blob is an older upstream versionbin/fm-afk-contract.sh: Kept quiet authority-present owner and word-bearing quiet announcement; adopted upstream spend-at-entry record format and validation.bin/fm-afk-launch.sh: Kept no-daemon quiet entry on ready host and Pi primaries, quiet rollback and words; adopted upstream entry spend and current host readiness checks.bin/fm-afk-return.sh: Adopted upstream spend/return accounting; retained task-owned --merged-pr cleanup command in landed-worker catch-up.bin/fm-backend.sh: Upstream version: fork blob is an older upstream versionbin/fm-backlog-transition-lib.sh: Adopted upstream note-label helper and GitLab closure; added ADO label to preserve atomic Done conversion and replay.bin/fm-bootstrap.sh: Upstream MISSING/OUTDATED distinction with retained fork no-mistakes 1.48.0 floor.bin/fm-branch-dispatch.mjs: Kept --quiet wake tail and shell-owned endpoint binding diagnostics; adopted upstream scope/offer interface and fail-closed routing.bin/fm-branch-prompt.sh: Kept guarded quiet/away merge authority, own-PR cleanup and routine unsticking; retained upstream completed-scout policy delivery and GitLab null-pipeline refusal.bin/fm-branch-report.sh: Kept away-host main-action classification, durable lane/decision handbacks and already-closed guard; adopted upstream current outcome store and return relay.bin/fm-brief.sh: Combined fork lint-skip and run-versus-reattach contract with upstream forge/external, base and publication-consent contract.bin/fm-captain-hold.sh: Used upstream bounded Done-archive replay and note-label helper; ADO closure remains through the combined shared helper.bin/fm-config-inherit-lib.sh: Upstream version: fork blob is an older upstream versionbin/fm-contributions.sh: Upstream version: fork blob is an older upstream versionbin/fm-control.sh: Kept pre-stop LiteLLM gateway validation alongside upstream per-secondmate pins and Polytoken preflight.bin/fm-crew-state.sh: Kept canonical ADO completed/readback and live-run supersession; adopted upstream backend, liveness and current task-state reconciliation.bin/fm-dod-lib.sh: Kept fork intent budget, custody, compaction and logged drives within upstream DoD; the strict CI-ready matcher recognizes canonical ADO pullrequest URLs.bin/fm-fleet-snapshot.sh: Took upstream: equivalent file-based contribution JSON transport replaces fork duplicate; upstream also propagates assembly errors.bin/fm-git-strip-ai-trailers.sh: Upstream version: fork blob is an older upstream versionbin/fm-lab-home.sh: Upstream version: fork blob is an older upstream versionbin/fm-lease-lib.sh: Kept quiet-with-words guarded PR permission while quiet spawn remains attended and away spend stays bounded; adopted upstream current lease/liveness guards.bin/fm-lint.sh: Took current upstream including its memory cap and fallback, restoring fork comma-decimal clock handling within the upstream function.bin/fm-merge-authority-lib.sh: Kept explicit quiet authority records and shared authority-present read; adopted upstream exact canonical merge identity and record locks.bin/fm-nm-run-lib.sh: Upstream version: fork blob is an older upstream versionbin/fm-pr-check.sh: Kept fork ADO delivery and upstream providers; Forgejo ownership now uses the head ref from the one existing shared PR read, with foreign-ref refusal and unreadable-ref warning preserved.bin/fm-pr-lib.sh: Combined upstream provider/record APIs with fork ADO completion/readback and own-PR authority; the existing Forgejo response also supplies its optional source ref.bin/fm-pr-merge.sh: Combined ADO approval/completion, REST policy gate, +10 vote, exact-head squash, completed readback, approval actions and quiet authority with upstream Forgejo and GitLab null-pipeline guards.bin/fm-pr-poll.sh: Kept bounded ADO completion, conflict and abandonment reporting with dedup receipt; added upstream Forgejo polling.bin/fm-project-mode.sh: Combined fork nmskip/ledgers queries with upstream Forgejo/external registry bindings; retained fail-closed invalid values.bin/fm-promote.sh: Combined explicit fork lint-skip metadata and DoD arguments with upstream publication-consent, base and external-forge refusal.bin/fm-remote-secondmate-relaunch.sh: Retained fork literal STATUS heredoc repair with upstream bounded convergence changes; upstream still retains this path.bin/fm-review-diff.sh: Upstream version: fork blob is an older upstream versionbin/fm-session-start.sh: Upstream version: fork blob is an older upstream versionbin/fm-spawn.sh: Combined fork auth/gateway forwarding and lint-skip metadata with upstream harness/backend, consent, Orca environment sources and secondmate hook-preserving settings files. Expanded Claude settings once and merged gateway settings into the secondmate file.bin/fm-supervise-daemon.sh: Combined fork exact-blocker/bounded readable+hex evidence with upstream distinct pending/unproven-composer deferral reasons.bin/fm-supervision-engine-lib.sh: Upstream version: fork blob is an older upstream versionbin/fm-supervision-host.sh: Kept quiet words, away lane handback, evidence-led routine unsticking and closed-task merge snapshots alongside upstream watcher handoff/continuity fixes.bin/fm-teardown.sh: Combined upstream lifecycle checks with fork task-owned merged-PR guard; completed states are provider-bound, including Forgejo closed plus merged=true.bin/fm-test-run.sh: Retained fork host-only serial shard, 35-minute modeled host budget and report-compact coverage; combined upstream expanded inventory and timing hints.bin/fm-timeout-lib.sh: Upstream version: fork blob is an older upstream versionbin/fm-tool-update-check.sh: Upstream version: fork blob is an older upstream versionbin/fm-wake-lib.sh: Upstream version: fork blob is an older upstream versionbin/fm-watch.sh: Kept ADO conflict/abandonment state notifications and transition receipt handling; adopted upstream wedge deferrals/caps, PR health signals, daemon ownership and continuity.docs/agent-control.md: Retained gateway binding refusal before worker stop; adopted upstream per-harness control and relaunch policy.docs/architecture.md: Retained ADO delivery, quiet authority, approval/waiver and completed-head guards; adopted upstream watcher, Forgejo, external review and GitLab pipeline architecture.docs/captain-hold-lifecycle.md: Adopted upstream bounded replay/note-label ownership; retained Azure DevOps PR closure notes through the common atomic transition helper.docs/configuration.md: Kept fork auth-forward/gateway configuration, per-run lint skip and ledger schema, ADO completion/approval options and quiet records; incorporated upstream schemas and opt-ins without enabling flags.docs/documentation-audiences.json: Union of upstream new surfaces and fork uber/conflict-radar/OpenTofu/md-fastpath inventory; removed retired decision-hold pointer.docs/fm-test-portable-shards.md: Kept fork measured 1649201ms supervision-host sample and dedicated shard; incorporated other upstream timing changes.docs/jev-guards.md: Upstream version: fork blob is an older upstream versiondocs/pi-supervision-branch.md: Kept quiet outcomes/authority, own-PR cleanup and live away-prompt guard; adopted upstream SDK compatibility, processing boundaries and routing guidance.docs/scripts.md: Updated shared script inventory for ADO PR guards, compactor and conflict radar; adopted upstream new backend/review owners.docs/supervision-host.md: Kept quiet record entry, away completion/decision handback, own-PR cleanup, unsticking and closed-task sweep behavior; incorporated upstream continuity, return and engine guidance.docs/supervision-protocols/supervision-host.md: Kept away main-handback continuation under the existing record; adopted upstream protocol and watcher ownership changes.docs/turnend-guard.md: Upstream version: fork blob is an older upstream versiondocs/verification/runtime-backends.md: Kept fork quiet/Pi/gateway and away-prompt verification entry points; adopted upstream current backend/harness records. Refreshed Claude 2.1.309 and Pi 0.87.1/1.2.0 active verification records after real engine/SDK guards passed, and executable Claude gateway settings after the stable spawn/relaunch regressions passed.docs/watcher-continuity.md: Upstream version: fork blob is an older upstream versiontests/fm-afk-contract.test.sh: Kept fork quiet-word authority/record cases and upstream live-worker-count entry-spend matrix.tests/fm-afk-inject-e2e.test.sh: Kept fork guarded task-owned landed-worker cleanup in the prompt; adopted upstream current operational-input sentinel fixtures.tests/fm-afk-launch.test.sh: Kept fork quiet-with-words host entry, no-daemon persistence and archive cases; adopted upstream current launch readiness and spend accounting.tests/fm-backend-tmux-smoke.test.sh: Kept fork generated-prompt safety expectations; adopted upstream exact window/pane/session targeting and grouped-session container cases.tests/fm-backlog-atomicity.test.sh: Kept fork ADO closure/backfill cases and current upstream atomic transition and replay cases.tests/fm-bootstrap-network-parallel.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-bootstrap.test.sh: Upstream installed/absent/outdated matrix adjusted to retained 1.48.0 floor.tests/fm-branch-supervision.test.sh: Kept fork quiet/away permission and task-owned teardown prompt cases; adopted upstream completed-scout cleanup and pinned-holder re-anchor cases.tests/fm-brief.test.sh: Kept fork explicit lint-skip and run-versus-reattach matrix; adopted upstream Forgejo/external delivery, safe inbox listing and consent cases.tests/fm-captain-hold-lifecycle.test.sh: Kept fork ADO/Gerrit answer-before-cleanup replay; adopted upstream configured Done-archive answer lookup cases.tests/fm-claude-trust.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-composer-lib.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-contributions.test.sh: Union of fork quiet-word authority case and upstream retry/review-thread cases.tests/fm-control-relaunch.test.sh: Kept fork gateway reconstruction and refusal-before-stop cases; adopted upstream per-mate pins, immutable personal settings and away relaunch cases. Gateway parsing uses the shared launch-prefix helper.tests/fm-daemon.test.sh: Union of fork exact-blocker evidence cases and upstream pending-versus-unrecognized deferral cases.tests/fm-dispatch-resolve.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-dod-lib.test.sh: Merged upstream and fork cases, adding behavioral ADO CI-ready done gating coverage.tests/fm-git-strip-ai-trailers.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-kimi-harness.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-lint.test.sh: Retained upstream suite, restored comma-locale regression with epoch-timestamp assertions, and corrected the empty assignment literal for ShellCheck.tests/fm-live-lab.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-pending-reply.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-pi-branch-extension.test.sh: Union of quiet/endpoint fork cases and upstream SDK/alias regressions; copied the shared shell endpoint owner into isolated fixtures, declared Orca backend for terminal targets, and gave quiet tasks distinct endpoints.tests/fm-pi-branch-live-e2e.test.sh: Retained fork quiet-outcome assertions and upstream real-SDK startup-context and retry probes.tests/fm-pi-watch-extension.test.sh: Kept fork quiet-word/empty-quiet routing; adopted upstream current SDK generation, process-group cleanup and OpenCode 2 plugin boundary cases.tests/fm-pipeline-spend.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-pr-check-security.test.sh: Retained upstream/fork provider checks and added Forgejo ownership tests for initial and merge-time registration; kept the merge-watch comment with its test.tests/fm-pr-merge.test.sh: Kept fork bounded ADO REST/policy/vote/options/exact-head completion, quiet authority and GitHub reviewer cases; adopted upstream Forgejo complete/readback matrix and GitLab null-pipeline waiver cases.tests/fm-procevent.test.sh: Kept fork fixture isolation while adopting upstream list-form nested targets, attachments, multiline prompts and malformed-item refusal cases.tests/fm-project-capacity.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-review-diff.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-secondmate-harness.test.sh: Kept fork explicit lint-skip spawn metadata expectations; adopted upstream per-mate pin and malformed/legacy pin matrices.tests/fm-spawn-dispatch-profile.test.sh: Kept fork auth forwarding, gateway secret isolation/rotation and lint-skip cases; adopted upstream resolved OpenCode 1/2 executable and delivery matrix, inherited Git config and hook-preserving secondmate settings. Gateway coverage now includes secondmates and preserved personal permissions.tests/fm-spawn-orca-worktree.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-spawn-pool-base-freshen.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-supervision-host-attended-live-e2e.test.sh: Kept fork task-owned teardown command assertions; adopted upstream current attended engine and watcher-continuity guard.tests/fm-supervision-host.test.sh: Combined fork quiet, lane handback, routine unsticking and closed-sweep cases with upstream continuity tests. The tagged Windows fixture models severed POSIX ancestry; CI repair 562ed22 widens its handled wait from 250 to 450 polls for loaded fork runners, preserving every assertion.tests/fm-teardown.test.sh: Combined provider fixtures and fork own-PR guards, adding Forgejo branch-actor completed-state coverage and correcting its quoted done literal.tests/fm-test-fixtures.test.sh: Union of fork gateway environment-isolation and upstream inherited Git-location isolation checks.tests/fm-test-run.test.sh: Combined upstream runner regressions with fork dedicated-host assertions; isolated ordinary boundary fixture weights from growing upstream inventory.tests/fm-timeout-lib.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-watch-arm.test.sh: Upstream version: fork blob is an older upstream versiontests/fm-watcher-lock.test.sh: Upstream version: fork blob is an older upstream versionFork deltas after this sync
Resolution method: BEHIND-ONLY means the fork blob exactly matched an upstream historical blob; those paths take current upstream. Fork-owned overlaps were reconciled from last-synced upstream
b062eb94, preserving both deltas. The complete blob manifest records every path.Upstream
5d718d5d775780c5449313dc4a6f55f7fb3b9b01is merged into forka610a4e1a6d1099192f5a1243fea6c2951370806with two parents in commitb820ccfa1215a9487396dc45e464b183a59de074.The merge-base after the branch merge is
5d718d5d775780c5449313dc4a6f55f7fb3b9b01(previously1a814e4943b69db555cc93ce0f42ef6db685c567); landing this PR with a merge commit preserves it.Kept:
Azure DevOps PR delivery and task-owned landed cleanup:
bin/fm-pr-{lib,check,merge,poll}.sh, bin/fm-teardown.sh, bin/fm-backlog-transition-lib.sh, bin/fm-captain-hold.sh, bin/fm-crew-state.sh, bin/fm-check-register.sh, bin/fm-watch.sh, PR/teardown fixtures and tests; docs/{configuration,architecture,scripts,captain-hold-lifecycle,gitlab-merge-watch}.md; AGENTS.md.Standalone 4000-character ADO PR compaction, verified step board and closed findings comment:
bin/fm-pr-report-compact (single self-contained file, unchanged), tests/fm-pr-report-compact.test.sh, tests/fixtures/fm-pr-report-compact/az.Captain-only intent, 900-character budget, CI-ready compaction, custody and foreground logged no-mistakes drives:
bin/fm-dod-lib.sh, bin/fm-brief.sh, tests/fm-{dod-lib,brief}.test.sh, docs/{configuration,gerrit-forge-integration}.md.Quiet-with-words and away guarded live-green PR merge authority on Claude and Pi:
bin/fm-{afk-contract,afk-launch,merge-authority-lib,merge-outcome-lib,lease-lib,branch-prompt,branch-report,supervision-host}.sh, bin/fm-branch-dispatch.mjs, bin/fm-contributions.jq, .pi/extensions/{fm-branch-supervision,fm-primary-pi-watch,lib/fm-branch-dispatch}.ts; .agents/skills/{afk,quiet,uberafk,uberquiet}/SKILL.md; quiet/afk/Pi/branch tests and supervision docs.Supervision-host lane completion/worker decision handback, task-owned teardown, routine unsticking and closed-task sweep suppression:
bin/fm-{supervision-host,branch-report,branch-prompt,wake-drain,pr-lib,teardown,afk-return}.sh, tests/fm-supervision-host.test.sh, tests/fm-teardown-endpoint-safety.test.sh; docs/{supervision-host,pi-supervision-branch,supervision-protocols/supervision-host}.md.Recorded backend targets for stale supervision routing:
bin/fm-endpoint-records.sh, bin/fm-branch-dispatch.mjs, .pi/extensions/lib/fm-branch-dispatch.ts and branch/Pi/host tests.Worker auth forwarding and LiteLLM gateway binding, including pre-stop relaunch refusal:
bin/fm-{spawn,control,claude-gateway-lib}.sh (gateway settings retain worker attribution and secondmate hooks/personal permissions), docs/{configuration,agent-control}.md, .agents/skills/harness-adapters/references/harness/gemini.md, spawn/control/Muse fixture tests.Fleet-sync origin upstream selection for main/master clones:
bin/fm-fleet-sync.sh, tests/fm-fleet-sync.test.sh.Per-run lint-skip intake and promotion/relaunch contract:
bin/fm-{project-mode,brief,spawn,promote,dod-lib}.sh, AGENTS.md, .agents/skills/project-management/SKILL.md, related brief/spawn/DoD/report tests.Documentation/mechanical conflict fast path, conflict radar and OpenTofu release guidance:
.agents/skills/{md-conflict-fastpath,conflict-radar,opentofu-iac-management}/, bin/fm-conflict-radar.sh, tests/{md-conflict-fastpath,fm-conflict-radar}.test.sh, docs/verification/conflict-radar.md, documentation-audiences.json.Fork CI shard layout and coherent Pi package tree:
.github/workflows/ci.yml, bin/fm-test-run.sh, tests/fm-{test-run,ci-workflow}.test.sh, docs/fm-test-portable-shards.md.Fork bootstrap floor, bounded escalation diagnostics and literal remote status marker:
bin/fm-bootstrap.sh (1.48.0), CONTRIBUTING.md, .agents/skills/bootstrap-diagnostics/SKILL.md, bin/fm-supervise-daemon.sh, bin/fm-remote-secondmate-relaunch.sh, tests/fm-{bootstrap,daemon}.test.sh; docs/{verification/supervision,wedge-alarm,herdr-backend}.md.Locale-safe lint clock:
bin/fm-lint.sh,tests/fm-lint.test.sh; dot/comma decimal separators preserve epoch timestamps, durations and fallback deadlines, within upstream structure and memory cap.Fork instruction owners, triggers and runtime paths:
.agents/skills/{away-quiet-supervision,validation-supervision,ship-landing,agent-skill-trigger-index,operational-home-layout}/SKILL.md; guarded recorded authority, uber refresh/exit, own-PR teardown, conflict routing, OpenTofu/markdown triggers, and persisted state entries restored. The 55-path untouched-upstream audit identified the two dropped unchanged skill owners; other differences were decided lint changes, retained runner fixture adjustments, and restored snapshot regression coverage.Large-input snapshot behavior coverage:
tests/fm-fleet-snapshot-view.test.shkeeps the bounded-argv regression with fixture-derived record assertions;.github/workflows/ci.ymlexpects 19 results. The upstream implementation replaces the fork transport duplicate.Integration fixes preserving fork gates across upstream providers:
bin/fm-dod-lib.sh,tests/fm-dod-lib.test.sh,tests/fm-crew-state.test.sh,tests/fm-pr-check-security.test.shrecognize canonical ADO CI-ready notes;bin/fm-pr-lib.sh,bin/fm-pr-check.shprovide Forgejo ownership from the existing single PR read;bin/fm-teardown.sh,tests/fm-teardown.test.shaccept Forgejo closed+merged only for Forgejo. Matching/foreign/unreadable refs and pending/other-PR notifications are covered.Retained shared delivery template and enforcement (workflow and verifier test are already byte-identical to upstream):
.github/workflows/no-mistakes-required.yml,tests/fm-no-mistakes-required.test.sh,bin/fm-pr-lib.sh,bin/fm-pr-merge.sh; the pinned require-no-mistakes action checks the signature and head-bound pipeline attestation on opened, edited, synchronized and reopened submissions to main. The shared action validates the attestation; merge guards separately require head-bound CI/policy evidence. Intent, What Changed, Risk Assessment and verification material remain visible in this description.Dropped or replaced:
bin/fm-captain-hold.sh): replaced by the upstream shared note-label helper and bounded replay; the combined helper retains ADO support inbin/fm-backlog-transition-lib.sh.bin/fm-fleet-snapshot.sh): upstream1a868710,072a7da3and25bb43bbpreserve file transport and failure propagation. The fork bounded-argv behavior test is retained with fixture-derived record assertions; upstream failure-propagation tests remain alongside it.2ce57d00,9d76aa8e) remain; currentsession-start-recoveryandscout-completionare adopted. The five fork feature-owner hunks initially dropped by mistake are restored and listed under Kept; no moved/retired skill is resurrected.decision-hold-lifecyclepointer: upstreamcaptain-hold-lifecycleremains the owner; no resurrected pointer.bin/fm-lint.sh,tests/fm-lint.test.sh): upstream cap and bounded source-analysis fallback adopted as directed. The comma-locale clock handling and regression are retained; the fallback now receives correct epoch timestamps in comma-decimal locales.Upstream supervision fixes
05a68cb2(kunchenguid#6588),ac8a767f(kunchenguid#7466),11990a20(kunchenguid#7444),f05b6284(kunchenguid#5939),5db719de(kunchenguid#7361),bd866530(kunchenguid#7552),0fc96515(kunchenguid#5947),8eb9be51(kunchenguid#7437),8818a804(kunchenguid#6163), and9cd4a3be(kunchenguid#7308) are retained.They strengthen watcher continuity, wait evidence, caps, composer diagnostics and entry spend; they do not replace the fork's lane handback, own-PR guard, foreground drives, quiet authority or unsticking/sweep behavior.
Upstream PR changes
56074d03(kunchenguid#7450),db03cb45(kunchenguid#7627),45dd50a5(kunchenguid#7462),6eb8b7f8(kunchenguid#7630),c79cd403(kunchenguid#7634), and6df15481(kunchenguid#7364) are retained alongside ADO delivery; none supplies ADO completion or the standalone report compactor.What Changed
Merge upstream
5d718d5dinto the fork through the two-parent mergeb820ccfa, retaining fork behavior and upstream improvements. Validation commits correct locale timing, ADO CI-ready recognition, Forgejo ownership and teardown, retained skill owners, large-input snapshot coverage, and their coupled tests/CI checks. CI repair 562ed22 also gives the Windows tagged-lock fixture a bounded 450-poll handled wait; its focused tests and the full 97-case host suite pass locally. The path-by-path resolutions and kept/replaced inventory above record the final integration choices.🤖 Generated with Claude Code
Risk Assessment
The update spans supervision, watcher continuity, worker launches and multiple forges. Named-head and policy checks, guarded recorded authority, and task-owned teardown remain required. Behavioral and live-harness evidence is described below. Local Ruby/GNU-time limitations and the approved inconclusive pipeline Test verdict are disclosed; required GitHub CI must pass before landing.
Validation
Pinned comparison heads: ours
a610a4e1a6d1099192f5a1243fea6c2951370806; theirs5d718d5d775780c5449313dc4a6f55f7fb3b9b01; merged is commitb820ccfa1215a9487396dc45e464b183a59de074, the two-parent merge of those exact commits.At the submitted merge head b820ccf, tree
b90bee95c1d5d10e3f80089079d5b508b0ffdee6preserves all 24 fork-added files byte-for-byte; all 186 BEHIND-ONLY paths match upstream exactly and 139 fork differences remain. The validation fixes described above are descendants of that two-parent merge. The following three-head table records the pre-pipeline runs; current-head pipeline evidence and its approved limitations are reported separately below.Baseline copies are Git archives with private Git metadata bound to their exact original commits, not managed worktrees. Their tracked content remains unchanged.
Commands:
bash bin/fm-lint.sh; source-aware ShellCheck on all 94 changed bin scripts, plus final touched scripts/tests;bash bin/fm-doc-audience-check.sh;bash bin/fm-test-run.sh --check-coverage; andbash bin/fm-test-run.sh --json <task-proof>.json <suite paths>on each head.tests/fm-pr-merge.test.shtests/fm-pr-check-security.test.shtests/fm-teardown.test.shtests/fm-teardown-endpoint-safety.test.shtests/fm-dod-lib.test.shtests/fm-brief.test.shtests/fm-afk-contract.test.shtests/fm-afk-launch.test.shtests/fm-afk-return.test.shtests/fm-afk-inject-e2e.test.shtests/fm-supervision-host.test.shtests/fm-branch-supervision.test.shtests/fm-spawn-dispatch-profile.test.shtests/fm-spawn-compact-adviser-disable.test.shtests/fm-spawn-pool-base-freshen.test.shtests/fm-pr-report-compact.test.shtests/fm-fleet-sync.test.shtests/fm-pi-branch-extension.test.shtests/fm-pi-watch-extension.test.shtests/fm-contributions.test.shtests/fm-daemon.test.shtests/fm-control-relaunch.test.shtests/fm-backlog-atomicity.test.shtests/fm-captain-hold-lifecycle.test.shtests/fm-test-fixtures.test.shAdditional merged checks: strict TypeScript 5.9.3 production-consumer typechecks against Pi 1.1.0/1.2.0; bootstrap and self-repo consent; PR attestation enforcement; conflict radar and md-conflict fastpath; snapshot view and contribution input; documentation audience inventory; CI shard coverage.
Lint, source-aware ShellCheck, actionlint 1.7.12 and documentation audience checks pass; coverage reports 278 suites with nine serial shards and the dedicated host shard.
Real harness guards: Claude Code 2.1.309 passes guarded away merge interpretation (green covered PR merges synchronously; red, unreported and other-lane hold) and supervision-host continuity/resume. Pi SDK 0.87.1 and 1.2.0 pass quiet outcomes, startup-context and retry behavior with intercepted provider streams; 1.2.0 additionally passes prompt-bearing user/no-prompt custom events and cancellation boundaries. Pi 0.87.1 explicitly cannot exercise those newer custom-opening subprobes.
Host limitation: Ruby is absent.
fm-ci-workflow.test.shcannot execute its Ruby YAML parse;fm-test-run.test.shpasses the corrected packing-boundary cases then cannot execute its Ruby workflow assertions. The actual workflows pass pinned actionlint. These two suites remain incomplete here and must run in CI.Superseded merge validation failures and corrections:
Reproduced upstream baseline failure: the untouched upstream supervision-host suite times out at the at-turn downtime-publication failure/Stop-hook case, and that exact case fails in isolation (
upstream-host-timeout-focused.log). The merged complete host suite passes. No baseline source was modified.Pre-existing baseline failure: the original fork Pi watcher suite rejects an external healthy OpenCode arm as owned. The merged watcher suite passes with upstream fixes. Original baseline failures remain in
comparison-ours.log; corrective reruns never rewrite the original logs.Evidence:
targeted.log/.json,comparison-{ours,theirs}.log/.json,comparison-ours-fixture-retest.log/.json,final-host.log/.json,final-spawn.log/.json,control-final.log/.json,final-pi.log/.json,snapshot-equivalent.log/.json,live-host-retest.log/.json,live-away-prompt.log,pi-live-1.2-retest.log/.json, and lint/ShellCheck logs in this task proof directory.Intent
Merge upstream/main into jredgard/firstmate with a real merge commit so merge-base advances. Preserve fork deltas: ADO REST completion, policies, vote, squash/head binding, completed readback, polling, landed-work/draft guards; standalone fm-pr-report-compact with tests/fixtures, 4000-char body and closed-findings comment; no-mistakes 900-char intent, done compaction and custody; Claude/Pi quiet words and away guarded merge authority; uberafk/uberquiet; host lane/decision handback, own-PR teardown, foreground logged validation, worker unsticking and closed-task sweep skips; launch auth forwarding; fleet-sync origin upstream fix; md-conflict-fastpath; fork CI shards. Pass lint and affected suites. PR must list each conflict resolution, every kept fork change and each duplicate dropped for upstream equivalence. Land with a merge commit, never squash.
Pipeline verification
Published head:
562ed22c10ff20955ba18f80732e7dc73d9272e6, a descendant of the real upstream merge. Document has no unresolved findings; canonical lint passes. The no-mistakes drive returned checks-passed for this published head; required GitHub CI has been read back green.Firstmate approved the local Test exception after eight directly driven scenarios passed. The timed batch stopped before results for pr-check-security, pr-report-compact, afk-contract, afk-launch, fleet-sync and md-conflict-fastpath; their pipeline-batch outcomes are unknown. Pre-pipeline merged-head evidence is recorded above, and required CI must complete these checks before landing. GNU
/usr/bin/timeis absent here, so the inherited lint RSS case is unavailable; all other lint cases pass. Ruby-dependent workflow assertions are also unavailable locally. These are limitations, not local clean passes. The stored exception reads: "live validation verdict: inconclusive; Test exception approved: no operator reason supplied"; firstmate supplied the approval rationale in steering message 007, based on independent evidence and mandatory CI.The old skill-restoration review finding was retained after its fixes; firstmate verified the five restorations at 5ef5d0c and explicitly approved that stale review evidence. CI repair 562ed22 widens the tagged-lock handled wait from 250 to 450 polls after the fork runner timed out; ShellCheck, the focused tagged/away cases and the full host suite (97 passes) pass locally. The first CI attempt recorded all 20 serial-1 scripts passing but its job was cancelled at the 30-minute limit; its fm-watch-triage runtime was 1603470 ms, and the same cancellation was reproduced on upstream runs. The fresh CI run passed all nineteen checks, including both serial lanes; its current-head results supersede the initial cancellation and tagged-lock fixture failure. Earlier corrections are preserved: locale-safe clock and its test; provider-bound Forgejo cleanup; ADO CI-ready recognition; Forgejo ownership from one existing read; restored skill owners and bounded-argv test; coupled CI count/comment alignment; ShellCheck-clean test literals.
Published pipeline evidence (full logs and scenarios remain in the linked evidence branch):
Pipeline
Updates from git push no-mistakes
Upstream opt-ins worth enabling
config/wedge-defer-worker-wait,config/wedge-defer-parked-gate,config/wedge-defer-validating,config/wedge-evidence-guards, andFM_WEDGE_MAX_ESCALATIONS: bound repeat alarms while quiet/away workers wait or validate.config/ready-after-green: withhold child PR-ready lines until checks are green.config/pr-health-monitor: notify owned PR conflicts and failing required checks; optional stale review-thread re-raises also warrant evaluation.FM_CI_SKIP_PUSHandFM_CI_SKIP_MACOSrepository variables: optional fork CI cost controls.These are follow-up candidates only; this sync enables none of them.
Land with a merge commit, never squash