Skip to content

feat(bin): recover relaunch through a rebuilt pane for gone Herdr endpoints - #2770

Open
khaira777 wants to merge 5 commits into
kunchenguid:mainfrom
khaira777:fm/fm-recover-missing
Open

feat(bin): recover relaunch through a rebuilt pane for gone Herdr endpoints#2770
khaira777 wants to merge 5 commits into
kunchenguid:mainfrom
khaira777:fm/fm-recover-missing

Conversation

@khaira777

Copy link
Copy Markdown

What

A recorded Herdr endpoint that is authoritatively gone (app restart) used to strand bin/fm-control.sh <id> relaunch: the ordinary path requires a positively agent-free endpoint to adopt. Relaunch now recovers it instead.

  • Control plane (bin/fm-control.sh): detects a missing endpoint, or an agent-free one with a failed:launching journal from a prior recovery attempt - scoped to Herdr records only, so an ordinary tmux relaunch failure retry keeps its working same-endpoint path unchanged. It preserves the recovery reason before the journal rewrite, skips the ordinary stopping/exit sequence (nothing left to stop), delegates to fm-spawn --relaunch --recover-missing, refuses endpoints that turn live or ambiguous between checks, and adopts the rebuilt pane from the just-published record before its liveness wait and completion journal.
  • Launch owner (bin/fm-spawn.sh): --recover-missing applies to --relaunch only and to ship/scout kinds (refused after record resolution for secondmates); accepts only recovery-grade missing-or-dead endpoint states; rebuilds exactly one replacement pane in the recorded session/workspace - or the home's flat workspace when that workspace is gone too - while reusing the task's existing local copy and records; never a fresh spawn, never a second worktree. The rebuilt shell must prove it sits in the recorded worktree before launch.
  • Docs: both script headers plus docs/agent-control.md (verb table row, transactional-relaunch steps 4-5, fail-closed boundary) updated minimally.

The implementation adapts an ~80%-complete preserved commit (903a4ec on fm/local-recover-missing) across later Cursor-harness upstream changes to both target files, preserving both intents; every referenced helper was verified against current bin/backends/herdr.sh / bin/fm-backend.sh contracts.

Validation

  • tests/fm-control-relaunch.test.sh: new hermetic coverage without a live Herdr daemon - flag refusals (--recover-missing without --relaunch, secondmate kind after record resolution), successful missing-endpoint recovery end-to-end on a stateful fake CLI (label reuse, recorded-worktree cwd, record rotation onto the rebuilt pane), ambiguous-workspace refusal keeping the prior durable record, tmux failed-launching retry staying on the ordinary path, live-endpoint refusal. Full suite green across three consecutive runs.
  • tests/fm-control.test.sh, tests/fm-trace-context-spawn.test.sh, tests/fm-spawn-batch.test.sh: green.
  • Pinned repo lint clean (bin/fm-lint.sh: ShellCheck 0.11.0, actionlint 1.7.12).
  • The two real-herdr-gated suites require a live Herdr daemon and skip identically at base commit in this environment.

Delivery note

Shipped direct-PR per captain approval: the local no-mistakes gate refuses its configured pipeline agents in this checkout because neither has a verified AGENTS.md-neutralization knob (error preserved in the task record); GitHub-hosted CI validates this PR instead.

@greptile-apps

greptile-apps Bot commented Aug 22, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains in the eligible follow-up review scope.

No blocking failure remains.

Reviews (2): Last reviewed commit: "no-mistakes(document): Document recover-..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 35d351725e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread bin/fm-spawn.sh
Comment on lines +1021 to +1025
# A previous missing-endpoint recovery can leave its replacement pane
# structurally present but agent-free after the launch fails. The control
# plane authorizes this path only from that recorded recovery journal;
# accepting the recovery-grade dead state here is therefore safe and
# avoids strand­ing the same task behind an empty Herdr pane.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require control-plane authorization for recovery launches

Captain, when fm-spawn.sh <id> --relaunch --recover-missing is invoked directly for any stopped Herdr task, this branch accepts dead solely because the flag was supplied; it never verifies SPAWN_CONTROL_PARENT, a matching relaunch transaction, or the claimed failed-recovery journal. The caller can therefore create and publish a new pane while bypassing fm-control's checkpoint, required progress note, and transactional rollback, even though the comment says the control plane authorized this path. Refuse --recover-missing unless it comes from the lifecycle-control parent with matching recovery provenance.

AGENTS.md reference: AGENTS.md:L302-L305

Useful? React with 👍 / 👎.

Comment thread bin/fm-control.sh Outdated
Comment on lines +813 to +817
if [ "$BACKEND" = herdr ] \
&& { [ "$state" = missing ] \
|| { [ "$state" = dead ] \
&& [ -f "$JOURNAL" ] \
&& grep -Fqx 'phase=failed:launching' "$JOURNAL" 2>/dev/null; }; }; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record recovery provenance before rebuilding a dead endpoint

When an ordinary same-endpoint Herdr relaunch publishes its replacement record but the new agent never becomes alive, rollback also leaves a dead pane and phase=failed:launching. This predicate cannot distinguish that ordinary failure from a failed missing-endpoint recovery, so the next fm-control ... relaunch incorrectly passes --recover-missing, creates another pane, and abandons the still-valid agent-free endpoint instead of retrying it. Persist and check an explicit recovery-attempt marker rather than inferring provenance from backend, state, and phase alone.

AGENTS.md reference: AGENTS.md:L302-L305

Useful? React with 👍 / 👎.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

VISION: aligned as a failure-mode recovery. A restart is a non-event; a gone Herdr endpoint should not strand relaunch. The path is fail-closed on live/ambiguous endpoints, scoped to Herdr, and leaves the ordinary tmux same-endpoint retry alone. Class: corrective (default relaunch recovery for that Herdr-missing shape; no enable flag).

Defect, waiting on the author: the recovery cwd wait is:

[ -z "$relaunch_seen" ] || [ "$(real_path_or_raw "$relaunch_seen")" = "$relaunch_wt_real" ] && break

bash &&/|| are left-associative, so an empty first spawn_current_path read breaks the loop immediately, then the post-loop check fails the whole recovery. That should be a positive settled-path conjunction before break. The hermetic Herdr stub always returns a cwd, so the suite does not catch it.

Security: none.

Overlap / do not land together:

CI: first-time fork workflows approved after diff review. Greptile is green. no-mistakes is pending and blocking. Ahead 1, behind 0, mergeable.

Waiting on the author for the wait-loop defect, and on CI — not waiting on the captain.

Merge-eligible: NO. Captain-flag NOW: NO.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

Corrective Herdr-only relaunch recovery for an authoritatively gone endpoint (--relaunch --recover-missing). Ordinary tmux same-endpoint relaunch stays unchanged. Aligns (restart is a non-event; refuse live/ambiguous).

Not merge-eligible. Require no-mistakes FAILED (body compliance). CI in progress. Overlap with #2760 on fm-spawn.sh.

Waiting on the author for no-mistakes — not waiting on the captain.

…points

A recorded Herdr endpoint that is authoritatively gone (app restart)
stranded relaunch: the ordinary path requires a positively agent-free
endpoint to adopt. fm-control relaunch now detects a missing endpoint,
or an agent-free one with a failed:launching journal from a prior
attempt (Herdr records only, so an ordinary tmux retry keeps its
same-endpoint path), skips the stopping step, and delegates to
fm-spawn --relaunch --recover-missing. The launch rebuilds exactly one
replacement pane in the recorded session/workspace - or the home's flat
workspace when that workspace is gone too - reuses the task's existing
local copy and records, adopts the rebuilt pane into the durable record,
and refuses live or ambiguous endpoints instead of risking duplicate
recovery. Ship/scout kinds only; secondmates keep their own recovery.
…rable attempt marker

fm-spawn now refuses --recover-missing unless the caller is a live
fm-control relaunch child (control lock naming our parent plus the
relaunch transaction id) or fm-control's recovery-attempt marker
authorizes finishing a failed attempt, so an endpoint rebuild can no
longer be requested outside the control plane.

fm-control persists that marker before delegating a recovery launch and
removes it once the replacement is confirmed alive; the dead-endpoint
recovery predicate now requires the marker instead of inferring from
the failed:launching journal shape, so an ordinary failed relaunch retry
stays on the ordinary same-endpoint path.

Also fixes the recovery cwd wait's left-associative ||/|| chain that
broke on an empty first path read; it now breaks only on a positive
settled-path conjunction.
…ndpoint recovery routing on the recovery-attempt marker
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants