Skip to content

feat(bin): add bead lifecycle closing with fm-ledger safety net - #25

Merged
trillium merged 2 commits into
mainfrom
fm/bead-lifecycle-closing
Aug 1, 2026
Merged

feat(bin): add bead lifecycle closing with fm-ledger safety net#25
trillium merged 2 commits into
mainfrom
fm/bead-lifecycle-closing

Conversation

@trillium

@trillium trillium commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Intent

Implement bead lifecycle CLOSING structure: agents close self-contained work on landing, and beads whose work landed but were never closed are surfaced as closeable. Wire the documented --beads spawn flag end to end (beads_id= meta, fm-bead-stamp.sh invocation), make fm-teardown.sh auto-close a linked bead only on confirmed landed teardown (never on --force or a refused teardown), clarify the brief hook's claim-first/close-last contract, and add bin/fm-ledger.sh to surface and close likely_dropped (claimed, unclosed, stale) beads as a safety net.

What Changed

  • Wired the --beads <id> spawn flag end-to-end: bin/fm-spawn.sh now writes beads_id= to task metadata and invokes fm-bead-stamp.sh to mark the bead dispatch=sent/lifecycle=sent.
  • bin/fm-teardown.sh now auto-closes a task's linked bead once teardown confirms the work landed, and explicitly never does so on --force or a refused teardown; clarified the claim-first/close-last contract in bin/fm-brief-hooks.d/beads.sh.
  • Added bin/fm-ledger.sh, a new fleet-wide safety net that lists and closes "likely dropped" beads (claimed, still open, gone stale), plus documentation updates in AGENTS.md and docs/scripts.md.

Reviewer Finding Resolution

The pipeline's review step flagged (finding review-1, info/ask-user) that scout and secondmate teardown skip the git-level "PR merged or content in default branch" landed check before auto-closing a linked bead, relying instead on their own pre-existing completion gates (a scout's report plus decision-hold, a secondmate having no in-flight children).
This is confirmed correct, intended, pre-existing behavior, not a gap introduced by this change.
Those completion gates are each kind's landed-equivalent condition, so bead auto-close on their teardown is right as-is; only ship tasks need the git-landed check.
No code change was made in response to this finding.

Risk Assessment

✅ Low: The change is additive, fail-open, and well-tested (landed-close, --force-skip, and refused-teardown-skip paths are each covered by new tests); I independently verified the beads query DSL semantics (label format, relative-date comparison direction) against the live task/bd CLI help and a real query run, and found no correctness issues.

Testing

Ran the existing fm-ledger and fm-teardown behavior-test suites (all passing, including the 3 new teardown cases the author added for the close-on-landed/never-on-force/never-on-refused contract), and wrote a new tests/fm-spawn-beads.test.sh to close a real coverage gap — the --beads flag's end-to-end wiring (meta beads_id= and fm-bead-stamp.sh invocation) previously had zero test coverage since it was documented but unimplemented before this change; both new cases pass, confirming the flag now works as documented.

Evidence: Bead lifecycle test run transcript
=== tests/fm-spawn-beads.test.sh (new, added by this validation run) ===
ok - a spawn with --beads <id> records beads_id= in meta and stamps the bead dispatch=sent/lifecycle=sent
ok - a spawn without --beads records no beads_id= and never invokes the bead stamp
# all fm-spawn-beads tests passed

=== tests/fm-teardown.test.sh (beads-lifecycle subset) ===
ok - a beads-linked task's bead is closed automatically on a landed (non-force) teardown
ok - a beads-linked task's bead is NOT closed on a --force teardown
ok - a beads-linked task's bead is NOT closed when teardown refuses unlanded work

=== tests/fm-ledger.test.sh (full) ===
ok - fm-ledger.sh lists claimed, unclosed, idle beads under the default 2-day threshold
ok - fm-ledger.sh --json emits only the likely_dropped beads, each flagged
ok - fm-ledger.sh --stale-days changes which beads are surfaced
ok - fm-ledger.sh --close closes exactly the given bead ids
ok - fm-ledger.sh --close with no ids errors instead of closing nothing silently
ok - fm-ledger.sh --close-all without --yes only previews what would close
ok - fm-ledger.sh --close-all --yes closes every currently-listed likely_dropped bead
ok - fm-ledger.sh --close-all reports cleanly when nothing is likely_dropped
ok - fm-ledger.sh --close warns and exits nonzero when the CLI rejects a close
ok - fm-ledger.sh errors clearly when the task CLI is not on PATH

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 1 info
  • ℹ️ bin/fm-teardown.sh:1406 - Auto-close only requires reaching this line without --force. For kind=scout/secondmate, validate_worktree_teardown_safety short-circuits to true (pre-existing line ~816: secondmate|scout) return 0), so the git-level PR-merged/content-in-default "landed" check never runs for those kinds — their own completion gates (scout: report + decision-hold; secondmate: no in-flight children) stand in for "landed" instead. That's a defensible reading consistent with how those kinds already define done, but is a looser bar than the header comment's "confirmed work landed" language implies for ship tasks. Worth confirming auto-close across all three kinds (not just ship) matches intent.
✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-ledger.test.sh — all 10 cases pass (list/--json/--stale-days/--close/--close-all/--yes/CLI-reject/CLI-missing)
  • bash tests/fm-teardown.test.sh — all 39 cases pass, incl. 3 new bead-close-on-landed, no-close-on-force, no-close-on-refused cases
  • bash tests/fm-spawn-beads.test.sh — new test added by this run; confirms --beads <id> writes beads_id= to task meta and invokes fm-bead-stamp.sh (task show / set-state dispatch=sent / set-state lifecycle=sent); also confirms no --beads flag means neither happens
  • bash tests/fm-beads-backend.test.sh and bash tests/fm-brief.test.sh — unaffected adjacent suites still pass
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

Wire the --beads <id> flag end to end (it was documented but never
implemented: no arg parsing, no beads_id= meta write, fm-bead-stamp.sh
was dead code). fm-spawn.sh now records beads_id= in task meta and
invokes fm-bead-stamp.sh after launch.

fm-teardown.sh closes a task's linked bead automatically once its work
is confirmed landed, and skips closing on a --force or refused
teardown. The close call is fail-open: a missing task CLI or a
rejected close warns and never blocks teardown.

Add bin/fm-ledger.sh as the safety net for beads that fall outside
that automatic close: claimed, unclosed beads that have gone quiet
past --stale-days are surfaced as likely_dropped and can be closed
individually or in bulk.

Clarify fm-brief-hooks.d/beads.sh's Bead Closure section so a
crewmate that cannot reach its own close step knows firstmate's
teardown will close the bead for it.

Cross-reference beads_id= in AGENTS.md's state/<id>.meta field list.
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@trillium, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 43488f55-2ad9-40e9-b967-b133f92bbd0e

📥 Commits

Reviewing files that changed from the base of the PR and between 1e11ba8 and ae8d7ef.

📒 Files selected for processing (9)
  • AGENTS.md
  • bin/fm-brief-hooks.d/beads.sh
  • bin/fm-ledger.sh
  • bin/fm-spawn.sh
  • bin/fm-teardown.sh
  • docs/scripts.md
  • tests/fm-ledger.test.sh
  • tests/fm-spawn-beads.test.sh
  • tests/fm-teardown.test.sh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@trillium
trillium merged commit cb69726 into main Aug 1, 2026
12 checks passed
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.

1 participant