Merge upstream main into fork (commits #4894, #4889, #4910) - #11
Merged
Merged
Conversation
…henguid#4894) * fix(bin): let a background Claude session keep owning its session lock Session-lock ownership was decided by process ancestry alone. Under an unattended Claude session the model loop runs in a transient bg-spare bridged to the front-end by a shared daemon; when that bridge is recycled the contiguous claude-named ancestry from a hook to the recorded owner breaks while the owner pid stays alive, so the Stop auto-arm stood down as a foreign live owner, the turn-end guard ended every turn with its read-only diagnostic, and fm-lock.sh refused - a self-sustaining outage until restart. Ownership is now ancestry membership OR a trusted same-session id, never id-first: - fm-session-lock-lib.sh accepts CLAUDE_CODE_SESSION_ID only when CLAUDE_PID is a Claude-shaped member of the current contiguous run, compares it against the id recorded in state/.lock-session, and requires the recorded pid to still be a live harness. No id, no sidecar, an untrusted id, a different id, or a dead recorded pid leaves the ancestry verdict unchanged. Ids are never read from ps argv. - fm-lock.sh accepts a same-session holder at both refusal sites, writes, refreshes, and clears the sidecar only under its claim lock (including the early already-mine exit, skipped only while the deferred startup sweep leases that lock), keeps it byte-identical across a same-session confirmation, records CLAUDE_PID on lock line 1 for a session with a trusted id so a shared daemon or front-end that outlives the session never keeps a dead session's lock alive, never rewrites a live line 1 on a same-session confirmation, and names the recorded id in the live-owner refusal. - The .lock line-1 format is unchanged, so every reader that takes the whole first line as the pid keeps working; the guard's foreign-owner exit is unchanged and inherits the fix through the shared predicate. Tests: the ancestry suite drives the ancestry and id signals apart in a deterministic process table (asserting the divergence) and runs a real orphaned front-end/daemon/pty-host/spare tree through six phases with the real lock, auto-arm, and guard scripts; the foreign-owner repro keeps its negative control and adds a same-id positive control. Disclosure: no live unattended Claude background session ran on the verifying machine. The topology is documented by the real process listings in kunchenguid#3902, kunchenguid#2314, kunchenguid#3398, and kunchenguid#4066; coverage is the structural predicate plus the executable fixtures, not a live pass. Residual: bin/fm-sessionstart-nudge.sh keeps its own private ancestry walk (it only decides whether to print a nudge) and may nudge on a resume in the recycled case. Out of scope, deliberately: no structured lock format, no guard budget changes, no daemon-identity rejection, no fork lineage. * no-mistakes(review): Wait for claim lock; revert failed sidecars * no-mistakes(review): Revalidate ownership after wait; restore sidecars * no-mistakes(review): Roll back sidecar by publication phase * no-mistakes(review): Restore sidecar only if lock line is unchanged * no-mistakes(review): Trust session ids without a spelling allowlist * no-mistakes(review): Disarm sidecar rollback before backup cleanup * no-mistakes(document): Updated session-lock ownership documentation
* feat: park main under the away posture on Pi While the away-posture record exists on a Pi primary, the supervision branch takes every actionable wake, no processing turn opens on main, captain rows accumulate for the return brief, and main's standing authority relocates to the branch through the existing guarded scripts. - lib/fm-branch-dispatch.ts: read the record at every routing decision; while it exists claim check, decision-owned, and heartbeat rows too, keeping the two broken-queue vetoes; expose checkSeqs so a claimed check row lifts task scoping. - fm-primary-pi-watch.ts: offer every actionable row under the record; a declined wake and every watcher-failure alarm still reach main. - fm-branch-supervision.ts: drop the legacy .afk decline; append a fixed POSTURE: AWAY tail carrying the record's read-back verbatim per wake; open no processing request while the record exists, re-checked immediately before a request would open and at every run boundary; present the accumulated rows at the first run boundary after archive. - fm-lease-lib.sh: fm_lease_forbid_branch passes the branch for opted-in actions only while fm-afk-contract.sh validate succeeds on a confirmed live record; PR merge, fresh spawn, and decision answer opt in, local landing never does. - fm-send.sh: a --resolve-key naming an open needs-decision or captain-held task is a decision answer and meets the partition; blocked: keys stay steering. - fm-spawn.sh: enforce the record's spend cap for a fresh ordinary spawn by either actor; relaunches and secondmates exempt. - fm-branch-prompt.sh: fixed Postures section and the verbatim ask-user-authority policy; the prefix stays byte-stable. - fm-afk-return.sh: count what the away session handled from the store. - docs, afk skill, AGENTS.md stub: main parked on Pi, green merge gate absolute while away. - tests: watcher and branch extension suites, fleet-record, merge, and decision-answer suites cover the relocation, the vetoes, the tail, the parked processing turn, the cancellation, the re-presentation, and the spend cap; dated live-guard evidence recorded. * no-mistakes(review): Refuse branch merge after preflight archive race * no-mistakes(review): Fix away wake, spawn, and processing races * no-mistakes(review): Suppress parked processing; narrow away-only rejection * no-mistakes(review): Abort dedicated processing; gate branch spawn once * no-mistakes(review): Stamp away-only on the dispatch offer * no-mistakes(review): Treat invalid away records as spend-cap absence * no-mistakes(review): Drop spawn test hook; abort processing-opened runs * no-mistakes(review): Bind abort to opening prompt; cap-read absence * no-mistakes(review): Limit away branch spawn to queued work only * no-mistakes(document): Correct AFK posture documentation
* ci: simplify CI job timeouts to a three-tier policy Replace the scattered per-job timeout values (10m parallel, 25m lint, 30m serial, 10m macOS) with three readable tiers, each a hang tripwire with headroom rather than a packing estimate: - fast (5m): coverage guard, repo invariants, timing aggregate - normal (30m, one shared budget): lint partitions, portable parallel shards, portable serial shards, macOS stock Bash - heavy (Herdr only): 20m step tripwire on the family run so always() cleanup still runs, under a 75m job-level last-resort backstop The workflow's header comment states the policy and points at docs/fm-test-portable-shards.md "Timeouts", which now owns it, and each job names its tier beside timeout-minutes. tests/fm-ci-workflow.test.sh asserts the policy against the parsed workflow instead of the old per-job minute values: every job joins exactly one tier, exactly three distinct job-level values exist, the fast tier stays within 5-10 minutes, the normal budget stays at least double the modeled parallel lane sum reported by fm-test-run.sh --check-coverage, and the Herdr step tripwire stays below its job backstop with an always() cleanup after it. Concurrency supersession, shard counts, lane membership, and fail-fast settings are unchanged. * no-mistakes(review): Decouple the normal timeout from packing estimates * no-mistakes(review): Assert Herdr teardown follows the family run * no-mistakes(review): Pin Herdr family-run timeout to 20 minutes * no-mistakes(review): Ignore comments when identifying Herdr steps * no-mistakes(review): Identify Herdr steps by declarative ids * no-mistakes(document): Clarify authoritative three-tier timeout policy
Bring in three commits from kunchenguid/firstmate main: - 4812db8 fix(bin): preserve Claude lock ownership after helper recycling (kunchenguid#4894) - 65a3bac feat: park main under the away posture on Pi (kunchenguid#4889) - 2bcb88c ci: standardize workflow timeouts into three tiers (kunchenguid#4910) Conflict resolution in tests/fm-send-resolve-key.test.sh: - Fork change added test_secondmate_helper_keyed_report_then_resolve_key (fm-secondmate-report.sh --key decision routing) - Upstream change added test_decision_answer_partition_relocates_under_the_record (away-posture decision answer partition) - Both are independent functions, kept both with their test registrations
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Five unresolved findings affect away-posture routing, cancellation, acknowledgement, and contract validation.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (1)
What changed in this PR
Merges upstream fixes for Claude lock ownership, Pi away-posture supervision, and CI timeout tiers.
Changes:
- Preserves trusted Claude session ownership across helper recycling.
- Parks Pi main during away posture and routes gated work to the supervision branch.
- Standardizes CI jobs into fast, normal, and heavy timeout tiers.
Review findings:
.pi/extensions/fm-branch-supervision.ts:1499— Moderate, 1 vote: Reject away-only wakes before claiming ordinary task rows..pi/extensions/fm-branch-supervision.ts:1095— Moderate, 1 vote: Track and cancel queuednextTurnprocessing when away posture appears..pi/extensions/lib/fm-branch-dispatch.ts:37— Critical, 1 vote: Validate.afk-contractrecords before routing away..pi/extensions/lib/fm-branch-dispatch.ts:307— Moderate, 1 vote: Preserve durable acknowledgements for branch-handled check rows.bin/fm-branch-prompt.sh:99— Moderate, 1 vote: Keep Relay/public check rows on main or provide complete branch handling.
| File | Reviewed change |
|---|---|
tests/fm-turnend-foreign-owner-repro.py |
Adds same-session lock regression coverage. |
tests/fm-send-resolve-key.test.sh |
Tests away decision-answer routing. |
tests/fm-pr-merge.test.sh |
Tests away merge authorization and races. |
tests/fm-pi-watch-extension.test.sh |
Tests away wake eligibility. |
tests/fm-pi-branch-extension.test.sh |
Tests parked-main lifecycle behavior. |
tests/fm-ci-workflow.test.sh |
Tests timeout-tier policy. |
tests/fm-branch-supervision.test.sh |
Tests away authority and spend caps. |
docs/watcher-continuity.md |
Documents session ownership continuity. |
docs/verification/supervision.md |
Records lock verification. |
docs/verification/runtime-backends.md |
Records away-posture verification. |
docs/turnend-guard.md |
Documents trusted lock ownership. |
docs/supervision-protocols/pi.md |
Updates Pi supervision protocol. |
docs/sessionstart-nudge.md |
Documents same-session lock handling. |
docs/scripts.md |
Updates lock-library ownership guidance. |
docs/pi-supervision-branch.md |
Defines away-posture contracts. |
docs/fm-test-portable-shards.md |
Defines timeout tiers. |
docs/configuration.md |
Documents Pi away behavior. |
docs/architecture.md |
Documents parked-main architecture. |
bin/fm-turnend-guard.sh |
Uses shared ownership verification. |
bin/fm-startup-network.sh |
Clarifies lock sweep ownership. |
bin/fm-spawn.sh |
Adds away dispatch and spend-cap gates. |
bin/fm-session-start.sh |
Updates lock ownership documentation. |
bin/fm-session-lock-lib.sh |
Implements trusted session ownership. |
bin/fm-send.sh |
Partitions decision answers by posture. |
bin/fm-pr-merge.sh |
Supports guarded away merges. |
bin/fm-merge-local.sh |
Keeps local landing main-owned. |
bin/fm-lock.sh |
Persists trusted session identity. |
bin/fm-lease-lib.sh |
Adds posture-aware authority relocation. |
bin/fm-claude-stop-autoarm.sh |
Preserves lock ownership across recycling. |
bin/fm-branch-prompt.sh |
Adds away-posture role rules. |
bin/fm-afk-return.sh |
Reports away-session outcomes. |
AGENTS.md |
Documents lock sidecars and Pi away behavior. |
.pi/extensions/lib/fm-branch-dispatch.ts |
Adds posture-aware eligibility and routing. |
.pi/extensions/fm-primary-pi-watch.ts |
Routes away-posture wake rows. |
.pi/extensions/fm-branch-supervision.ts |
Implements parked-main routing and outcome handling. |
.github/workflows/ci.yml |
Applies the three timeout tiers. |
.agents/skills/afk/SKILL.md |
Documents Pi away-posture operation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+37
to
+42
| export function afkPostureRecordPresent(state: string): boolean { | ||
| try { | ||
| return statSync(join(state, AFK_CONTRACT_FILE)).isFile(); | ||
| } catch { | ||
| return false; | ||
| } |
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.

Merge three commits from kunchenguid/firstmate main:
Conflict resolution
The only conflict was in
tests/fm-send-resolve-key.test.sh, where both sides added a test function at the same point:test_secondmate_helper_keyed_report_then_resolve_key(secondmate report decision-key routing)test_decision_answer_partition_relocates_under_the_record(away-posture decision partition)Both are independent functions - kept both with their test registrations. The fork's
REPORTvariable was also retained.Verification
tests/fm-send-resolve-key.test.shpasstests/fm-branch-supervision.test.shpasstests/fm-session-lock-ancestry.test.shpasstests/fm-pi-branch-extension.test.shpasstests/fm-pi-watch-extension.test.shpassStats
38 files changed, 2706 insertions(+), 222 deletions(-)