Skip to content

fix(bin): make teardown exit non-zero after a fatal source failure - #3568

Open
kuan0808 wants to merge 2 commits into
kunchenguid:mainfrom
kuan0808:fm/up-10-teardown-source-failure
Open

fix(bin): make teardown exit non-zero after a fatal source failure#3568
kuan0808 wants to merge 2 commits into
kunchenguid:mainfrom
kuan0808:fm/up-10-teardown-source-failure

Conversation

@kuan0808

@kuan0808 kuan0808 commented Sep 2, 2026

Copy link
Copy Markdown

bin/fm-teardown.sh ends through an EXIT trap that returns $status, and Bash reports $? as 0 inside that trap after a fatal shell error such as a failed source of a missing sibling. A missing sibling in bin/ therefore made teardown exit 0 silently while the task's meta, tasktmp, endpoint, and state were all left in place, and every caller read that as success.

  • In the EXIT trap, treat exit 0 with the task record still present as an aborted teardown: report it on the stderr saved before the trap (the abort may happen inside a call whose own stderr is discarded) and exit 1. Every legitimate exit-0 path has already removed the record, so no refusal path or message changes.
  • In fm_backend_source, check the adapter file is readable before sourcing it, so a missing adapter is the ordinary failed return its callers already handle instead of a Bash 3.2 fatal error that skips their own refusal. This restores teardown's designed "nothing was changed" herdr preflight refusal on stock macOS Bash.
  • Regression in tests/fm-gotmp.test.sh: remove one sourced sibling from the fixture and assert non-zero exit, the stderr report, and that the task record and tasktmp survive.

The first commit is the fixture this fix's regression needs: tests/fm-gotmp.test.sh builds a fake FM_HOME by symlinking only the bin/ siblings teardown needs, and that hand-curated list never gained fm-session-lock-lib.sh, which bin/backends/tmux.sh has sourced since cf95112. Without it the regression cannot reach the failure it asserts. It is carried here rather than in the test-only pull request so both stand alone.

…k sibling

tests/fm-gotmp.test.sh has failed since cf95112 (kunchenguid#1577), which made
bin/backends/tmux.sh source fm-session-lock-lib.sh at load time. The test
builds a fake FM_HOME by symlinking only the bin/ siblings teardown needs,
and that hand-curated list never gained the new sibling. The teardown kill
step then dies on the failed source; the message is hidden behind
`2>/dev/null || true`, the EXIT trap returns Bash's post-fatal `$?` of 0,
and the test's "did not remove the tasktmp dir" assertion is the first one
that notices.

Production teardown is unaffected: a real bin/ always has the sibling, and
tasktmp removal works. This is a fixture fix only.

- Symlink fm-session-lock-lib.sh into the fixture next to fm-tmux-lib.sh.
- Stub fm-remote-job-reap-orphans.sh like fm-guard.sh and fm-fleet-sync.sh;
  teardown calls it best-effort, and the real sweep signals processes, so
  it must never be symlinked into a fixture.
- Let make_fake_root omit the tasktmp= line when called with one argument,
  and drop the second, duplicated copy of the fixture so the sibling list
  lives in one place.

bin/fm-teardown.sh and bin/fm-spawn.sh are untouched.
bin/fm-teardown.sh ends through an EXIT trap that returns `$status`, and
Bash reports `$?` as 0 inside that trap after a fatal shell error such as
a failed `source` of a missing sibling. A missing sibling in bin/ therefore
made teardown exit 0 silently while the task's meta, tasktmp, endpoint, and
state were all left in place, and every caller read that as success.

- In the EXIT trap, treat exit 0 with the task record still present as an
  aborted teardown: report it on the stderr saved before the trap (the
  abort may happen inside a call whose own stderr is discarded) and exit 1.
  Every legitimate exit-0 path has already removed the record, so no
  refusal path or message changes.
- In fm_backend_source, check the adapter file is readable before sourcing
  it, so a missing adapter is the ordinary failed return its callers
  already handle instead of a Bash 3.2 fatal error that skips their own
  refusal. This restores teardown's designed "nothing was changed" herdr
  preflight refusal on stock macOS Bash.
- Regression in tests/fm-gotmp.test.sh: remove one sourced sibling from the
  fixture and assert non-zero exit, the stderr report, and that the task
  record and tasktmp survive.
@greptile-apps

greptile-apps Bot commented Sep 2, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge, with no concrete correctness, security, or repository-rule violations identified.

The new trap condition distinguishes successful teardown from fatal aborts using metadata that all successful paths remove, while the adapter precheck routes missing files through existing failure handling.

Reviews (1): Last reviewed commit: "fix(bin): make teardown exit non-zero af..." | Re-trigger Greptile

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate:

HEAD 273aa5891d87bf40a6799fbc14585c64e4d6500d — MERGEABLE/UNSTABLE. Fork PR (kuan0808, maintainerCanModify). Author not blocked. Created today; not 14-day stale. Security: clean (bin + test only; no workflow files).

Attestation: MISSING — no head-bound no-mistakes ## Pipeline section in body or comments. NM 33687571845 FAILURE ("This PR was not raised through no-mistakes"). CI 33687571873 approved this pass and is in progress.

Contract-class: restore — teardown already promised a loud non-zero abort that leaves durable records in place (nothing was changed herdr preflight; exit 1 on every refusal; the only exit 0 is the remote-secondmate path after fm_backlog_atomic_transition remove). On main (d22318ea) the EXIT trap still return "$status" after a fatal source, and Bash reports $? as 0 in that trap, so callers saw success while meta/tasktmp stayed. fm_backend_source on main sources the adapter without a readability check; on Bash 3.2 a missing adapter is a fatal skip of teardown_herdr_require_prerequisites's existing refusal. The trap heuristic (exit 0 + record still present → stderr on the pre-saved fd + exit 1) and the readable-adapter check restore those already-specified paths. Not a new default.

VISION (per rule, evidence = bin/fm-teardown.sh + bin/fm-backend.sh + tests/fm-gotmp.test.sh vs main d22318ea):

  1. One captain, one interface — aligns (callers no longer read a silent exit 0 as success when teardown aborted).
  2. Authority is explicit and never inferred — aligns (no autonomy widening; restores the designed refusal / nothing-changed path).
  3. Scripts own the mechanics, agents own the judgment — aligns (trap + readable-check are exact script mechanics).
  4. A restart is a non-event — aligns (aborted teardown retains every durable record).
  5. Delegation with a spine — aligns (unlanded work is not torn down; abort is a finding).
  6. The fleet outlives any vendor — aligns (Bash 3.2 fatal source is no longer a silent success).
  7. Scope — aligns (field incident → regression coverage). Closing: aligns.

Overlap with #3556: this PR's first commit is the gotmp fixture sibling list (fm-session-lock-lib.sh symlink + stub fm-remote-job-reap-orphans.sh + one make_fake_root) that #3556's body describes but that #3556's actual diff does not contain. Neither has landed. Both still needed: #3556 is the composer UTF-8 / relaunch-wait / macos-stock-bash coverage; this PR is the product abort fix plus the fixture the regression requires. Do not close either.

This is waiting on you (the author), not a captain decision. Please git push no-mistakes so the PR body gets a head-bound attestation for the current HEAD (or the tip that push creates). Fork CI/NM workflows for this HEAD were approved after diff review (run ids 33687571873, 33687571845). Once attestation MATCH + NM green + CI green, this restore can auto-merge.

workflow-approvals this pass: 33687571873, 33687571845

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