feat(beads): auto-link every spawn to a bead under the beads backlog backend - #37
Conversation
|
Warning Review limit reached
Next review available in: 22 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (11)
📝 WalkthroughWalkthroughThe beads backend now auto-resolves or creates beads for ship and scout spawns. Spawn metadata records the bead ID, generated briefs receive lifecycle hook content, and teardown documentation describes confirmed closure behavior. Explicit bead links remain supported. ChangesBeads lifecycle integration
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant fm_spawn
participant fm_tasks_axi_lib
participant task
participant fm_brief
fm_spawn->>fm_tasks_axi_lib: resolve or create bead
fm_tasks_axi_lib->>task: find or create labeled bead
task-->>fm_spawn: return bead ID
fm_spawn->>fm_brief: inject bead hook sections
fm_spawn->>fm_spawn: record metadata and lifecycle state
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
04b8671 to
20fde45
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bin/fm-spawn.sh`:
- Around line 465-466: Move the BEADS_ARG assignment invoking
fm_beads_resolve_or_create out of the early preflight block and place it after
all non-mutating checks, once project resolution succeeds and immediately before
launch. Preserve the existing conditions for resolving or creating beads, and
add a regression case that forces project resolution failure and verifies task
create is not invoked.
- Around line 461-467: Update the automatic beads-linking branch in fm-spawn.sh
to return a clear nonzero error when fm_beads_resolve_or_create fails, rather
than clearing BEADS_ARG and continuing; preserve AUTO_BEADS_LINKED only after
successful resolution or creation. In bin/fm-spawn.sh lines 103-110 and
docs/configuration.md lines 64-66, retain the beads_id guarantee and
automatic-linking statement, respectively, once failed linkage is rejected, and
add regression coverage for missing dependencies, inaccessible storage, or
bead-creation failures.
- Around line 465-467: Update the argument-precedence logic in bin/fm-spawn.sh
so a non-empty FM_HOOK_BEADS_ID sets BEADS_ARG and marks BEADS_SET before the
auto-linking condition. Ensure the existing fm_beads_resolve_or_create path only
runs when neither --beads nor the external hook bead ID provided an explicit
bead.
In `@bin/fm-tasks-axi-lib.sh`:
- Around line 100-116: The fm_beads_resolve_or_create lookup currently treats
task-list and jq failures as empty results, risking duplicate creation and
invalid null IDs. Update this function to return failure when task list fails,
JSON parsing fails, the response is not a valid array, or an existing bead lacks
a non-null ID; only invoke bead creation for a successfully parsed, valid empty
array, and add regression tests covering failed lookup, malformed JSON, and
missing bead IDs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 60c40b57-6222-4dfb-99bf-733f0bff35d0
📒 Files selected for processing (11)
AGENTS.mdbin/fm-bead-stamp.shbin/fm-brief.shbin/fm-spawn.shbin/fm-tasks-axi-lib.shbin/fm-teardown.shdocs/configuration.mddocs/scripts.mdtests/fm-beads-backend.test.shtests/fm-brief.test.shtests/fm-spawn-beads.test.sh
| # entities, not backlog work items, so they stay exempt. Fails open: a resolve | ||
| # failure (task/jq missing, store unreachable) leaves BEADS_ARG empty and spawn | ||
| # proceeds exactly as it did before this backend existed. | ||
| AUTO_BEADS_LINKED=0 | ||
| if [ "$BEADS_SET" -eq 0 ] && [ "$KIND" != secondmate ] && [ "$(fm_backlog_backend_value "$CONFIG")" = beads ]; then | ||
| BEADS_ARG=$(fm_beads_resolve_or_create "$ID") || BEADS_ARG= | ||
| [ -z "$BEADS_ARG" ] || AUTO_BEADS_LINKED=1 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Do not continue when required bead resolution fails.
With config/backlog-backend=beads, a failed task or jq dependency, store read, or bead creation clears BEADS_ARG and returns a successful spawn. That task has no beads_id=, dispatch stamp, or confirmed-close lifecycle. This conflicts with the documented automatic-linking guarantee.
bin/fm-spawn.sh#L461-L467: return a clear nonzero error when automatic resolution or creation fails. Add regression coverage for this failure path.bin/fm-spawn.sh#L103-L110: retain thebeads_id=guarantee only after the spawn path rejects failed automatic linkage.docs/configuration.md#L64-L66: retain the automatic-linking statement only after the implementation rejects failed automatic linkage.
As per PR objectives, beads-backed spawns must resolve or create a bead and record beads_id=. Based on learnings, do not silently continue after a missing dependency or inaccessible backend.
📍 Affects 2 files
bin/fm-spawn.sh#L461-L467(this comment)bin/fm-spawn.sh#L103-L110docs/configuration.md#L64-L66
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/fm-spawn.sh` around lines 461 - 467, Update the automatic beads-linking
branch in fm-spawn.sh to return a clear nonzero error when
fm_beads_resolve_or_create fails, rather than clearing BEADS_ARG and continuing;
preserve AUTO_BEADS_LINKED only after successful resolution or creation. In
bin/fm-spawn.sh lines 103-110 and docs/configuration.md lines 64-66, retain the
beads_id guarantee and automatic-linking statement, respectively, once failed
linkage is rejected, and add regression coverage for missing dependencies,
inaccessible storage, or bead-creation failures.
Source: Learnings
| if [ "$BEADS_SET" -eq 0 ] && [ "$KIND" != secondmate ] && [ "$(fm_backlog_backend_value "$CONFIG")" = beads ]; then | ||
| BEADS_ARG=$(fm_beads_resolve_or_create "$ID") || BEADS_ARG= |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Resolve the bead after all spawn preflight checks.
These lines can mint a bead before project resolution. If project resolution or a later preflight fails, the new bead has no task metadata link and no teardown closure path.
Move resolution after non-mutating preflight checks and before launch. Add a regression case that forces project resolution to fail and asserts that task create was not called.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/fm-spawn.sh` around lines 465 - 466, Move the BEADS_ARG assignment
invoking fm_beads_resolve_or_create out of the early preflight block and place
it after all non-mutating checks, once project resolution succeeds and
immediately before launch. Preserve the existing conditions for resolving or
creating beads, and add a regression case that forces project resolution failure
and verifies task create is not invoked.
| if [ "$BEADS_SET" -eq 0 ] && [ "$KIND" != secondmate ] && [ "$(fm_backlog_backend_value "$CONFIG")" = beads ]; then | ||
| BEADS_ARG=$(fm_beads_resolve_or_create "$ID") || BEADS_ARG= | ||
| [ -z "$BEADS_ARG" ] || AUTO_BEADS_LINKED=1 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# Read the script header and inline option help before inspecting option state.
sed -n '1,220p' bin/fm-spawn.sh
# Trace initialization and use of explicit bead inputs.
rg -n -C 4 'FM_HOOK_BEADS_ID|BEADS_SET|BEADS_ARG|--beads|Usage:' \
bin/fm-spawn.sh tests/fm-spawn-beads.test.shRepository: trillium/firstmate
Length of output: 34688
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "== script outline around globals/default hooks =="
sed -n '220,280p' bin/fm-spawn.sh
sed -n '860,925p' bin/fm-spawn.sh
sed -n '950,1100p' bin/fm-spawn.sh
echo
echo "== FM_HOOK_BEADS_ID occurrences in repo =="
rg -n 'FM_HOOK_BEADS_ID|hook.*beads|beads.*hook|Bead Receipt|Bead Closure|--beads' . \
-g '!tests/fm-spawn-beads.test.sh' -g '!bin/fm-spawn.sh' -g '!**/node_modules/**' -g '!**/dist/**' || true
echo
echo "== all occurrences of FM_HOOK_BEADS_ID in tracked files =="
rg -n 'FM_HOOK_BEADS_ID' bin tests lib scripts 2>/dev/null || true
echo
echo "== behavioral/static probe for external env precedence =="
python3 - <<'PY'
from pathlib import Path
p=Path('bin/fm-spawn.sh')
s=p.read_text()
checks={
'initializes BEADS_ARG empty': 'BEADS_ARG=' in s[s.index('KIND=ship'):s.index('for a in "$@"; do')],
'initializes BEADS_SET zero': 'BEADS_SET=0' in s[s.index('KIND=ship'):s.index('for a in "$@"; do')],
'opts parser reads --beads value and flag': '--beads) want_value=beads' in s and '--beads=*) BEADS_ARG=${a#--beads=}; BEADS_SET=1' in s,
'opts parser reads short -b value and flag': '-b) want_value=beads' in s and '-b=*) BEADS_ARG=${a#-b=}; BEADS_SET=1' in s,
'opts parser rejects empty --beads': '--beads requires a non-empty value' in s and 'if [ "$BEADS_SET" -eq 0 ] || [ -z "$BEADS_ARG" ]' not in s,
'auto-resolution gate depends on BEADS_SET': '[ "$BEADS_SET" -eq 0 ] && [ "$KIND" != secondmate ] && [ "$(fm_backlog_backend_value "$CONFIG")" = beads ];' in s,
'external hook id referenced in README/comments': 'FM_HOOK_BEADS_ID' in s,
'no case sets BEADS_SET from external env': 'FM_HOOK_BEADS_ID' not in s[:s.index('for a in "$@"; do')] and '\n FM_HOOK_BEADS_ID' not in s[s.index('KIND=ship'):s.index('BEADS_SET=0'):],
}
for k,v in checks.items():
print(f'{k}: {v}')
PYRepository: trillium/firstmate
Length of output: 24312
Apply the external FM_HOOK_BEADS_ID bead ID before auto-linking.
bin/fm-spawn.sh only sets BEADS_ARG and BEADS_SET from --beads; an external FM_HOOK_BEADS_ID is still used by hooks, but it does not stop auto-linking under config/backlog-backend=beads. Handle FM_HOOK_BEADS_ID at argument-precedence time so the explicit external bead is used instead of auto-resolving a different bead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bin/fm-spawn.sh` around lines 465 - 467, Update the argument-precedence logic
in bin/fm-spawn.sh so a non-empty FM_HOOK_BEADS_ID sets BEADS_ARG and marks
BEADS_SET before the auto-linking condition. Ensure the existing
fm_beads_resolve_or_create path only runs when neither --beads nor the external
hook bead ID provided an explicit bead.
20fde45 to
940cf4b
Compare
Stage 3 of the beads-authority migration (data/beads-authority-migration-scout/report.md). Under config/backlog-backend=beads, bead-linking is the backend itself, not an opt-in cross-reference: fm-spawn.sh and fm-brief.sh now resolve or mint a bead labeled task:<task-id> (fm_beads_resolve_or_create in fm-tasks-axi-lib.sh) whenever --beads/FM_HOOK_BEADS_ID isn't already set, so every ship/scout dispatch gets a beads_id= in its meta and the claim-first/close-last lifecycle applies automatically. An explicit --beads still wins, and secondmate launches stay exempt. The default tasks-axi/manual backends are unchanged: --beads remains a deliberate opt-in there. Also wires up fm-brief.sh's documented hook system (fm-brief-hooks.d/*.sh sourced, stdout prepended to the brief), which had never actually been implemented, so the Bead Receipt/Closure sections it produces can render at all.
… scaffold to spawn"}
…-linked briefs at spawn"}
…ads backlog backend
… in fm-spawn.sh and fm-brief.sh
…ve in fm-brief.sh
940cf4b to
6e363d4
Compare
…backend (#37) * feat(beads): auto-link every spawn to a bead under the beads backend Stage 3 of the beads-authority migration (data/beads-authority-migration-scout/report.md). Under config/backlog-backend=beads, bead-linking is the backend itself, not an opt-in cross-reference: fm-spawn.sh and fm-brief.sh now resolve or mint a bead labeled task:<task-id> (fm_beads_resolve_or_create in fm-tasks-axi-lib.sh) whenever --beads/FM_HOOK_BEADS_ID isn't already set, so every ship/scout dispatch gets a beads_id= in its meta and the claim-first/close-last lifecycle applies automatically. An explicit --beads still wins, and secondmate launches stay exempt. The default tasks-axi/manual backends are unchanged: --beads remains a deliberate opt-in there. Also wires up fm-brief.sh's documented hook system (fm-brief-hooks.d/*.sh sourced, stdout prepended to the brief), which had never actually been implemented, so the Bead Receipt/Closure sections it produces can render at all. * no-mistakes(review): {"summary": "Defer beads auto-minting from brief scaffold to spawn"} * no-mistakes(review): {"summary": "Inject bead hook sections into auto-linked briefs at spawn"} * no-mistakes(document): Sync docs with automatic bead-linking under beads backlog backend * no-mistakes(lint): summary: suppress SC1090 for dynamic hook sourcing in fm-spawn.sh and fm-brief.sh * no-mistakes(lint): Avoid ShellCheck crash on hook-loop source directive in fm-brief.sh
…backend (#37) * feat(beads): auto-link every spawn to a bead under the beads backend Stage 3 of the beads-authority migration (data/beads-authority-migration-scout/report.md). Under config/backlog-backend=beads, bead-linking is the backend itself, not an opt-in cross-reference: fm-spawn.sh and fm-brief.sh now resolve or mint a bead labeled task:<task-id> (fm_beads_resolve_or_create in fm-tasks-axi-lib.sh) whenever --beads/FM_HOOK_BEADS_ID isn't already set, so every ship/scout dispatch gets a beads_id= in its meta and the claim-first/close-last lifecycle applies automatically. An explicit --beads still wins, and secondmate launches stay exempt. The default tasks-axi/manual backends are unchanged: --beads remains a deliberate opt-in there. Also wires up fm-brief.sh's documented hook system (fm-brief-hooks.d/*.sh sourced, stdout prepended to the brief), which had never actually been implemented, so the Bead Receipt/Closure sections it produces can render at all. * no-mistakes(review): {"summary": "Defer beads auto-minting from brief scaffold to spawn"} * no-mistakes(review): {"summary": "Inject bead hook sections into auto-linked briefs at spawn"} * no-mistakes(document): Sync docs with automatic bead-linking under beads backlog backend * no-mistakes(lint): summary: suppress SC1090 for dynamic hook sourcing in fm-spawn.sh and fm-brief.sh * no-mistakes(lint): Avoid ShellCheck crash on hook-loop source directive in fm-brief.sh
…backend (#37) * feat(beads): auto-link every spawn to a bead under the beads backend Stage 3 of the beads-authority migration (data/beads-authority-migration-scout/report.md). Under config/backlog-backend=beads, bead-linking is the backend itself, not an opt-in cross-reference: fm-spawn.sh and fm-brief.sh now resolve or mint a bead labeled task:<task-id> (fm_beads_resolve_or_create in fm-tasks-axi-lib.sh) whenever --beads/FM_HOOK_BEADS_ID isn't already set, so every ship/scout dispatch gets a beads_id= in its meta and the claim-first/close-last lifecycle applies automatically. An explicit --beads still wins, and secondmate launches stay exempt. The default tasks-axi/manual backends are unchanged: --beads remains a deliberate opt-in there. Also wires up fm-brief.sh's documented hook system (fm-brief-hooks.d/*.sh sourced, stdout prepended to the brief), which had never actually been implemented, so the Bead Receipt/Closure sections it produces can render at all. * no-mistakes(review): {"summary": "Defer beads auto-minting from brief scaffold to spawn"} * no-mistakes(review): {"summary": "Inject bead hook sections into auto-linked briefs at spawn"} * no-mistakes(document): Sync docs with automatic bead-linking under beads backlog backend * no-mistakes(lint): summary: suppress SC1090 for dynamic hook sourcing in fm-spawn.sh and fm-brief.sh * no-mistakes(lint): Avoid ShellCheck crash on hook-loop source directive in fm-brief.sh
…backend (#37) * feat(beads): auto-link every spawn to a bead under the beads backend Stage 3 of the beads-authority migration (data/beads-authority-migration-scout/report.md). Under config/backlog-backend=beads, bead-linking is the backend itself, not an opt-in cross-reference: fm-spawn.sh and fm-brief.sh now resolve or mint a bead labeled task:<task-id> (fm_beads_resolve_or_create in fm-tasks-axi-lib.sh) whenever --beads/FM_HOOK_BEADS_ID isn't already set, so every ship/scout dispatch gets a beads_id= in its meta and the claim-first/close-last lifecycle applies automatically. An explicit --beads still wins, and secondmate launches stay exempt. The default tasks-axi/manual backends are unchanged: --beads remains a deliberate opt-in there. Also wires up fm-brief.sh's documented hook system (fm-brief-hooks.d/*.sh sourced, stdout prepended to the brief), which had never actually been implemented, so the Bead Receipt/Closure sections it produces can render at all. * no-mistakes(review): {"summary": "Defer beads auto-minting from brief scaffold to spawn"} * no-mistakes(review): {"summary": "Inject bead hook sections into auto-linked briefs at spawn"} * no-mistakes(document): Sync docs with automatic bead-linking under beads backlog backend * no-mistakes(lint): summary: suppress SC1090 for dynamic hook sourcing in fm-spawn.sh and fm-brief.sh * no-mistakes(lint): Avoid ShellCheck crash on hook-loop source directive in fm-brief.sh
Intent
Stage 3 of the beads-authority migration: make bead-linking automatic and mandatory on every spawn when config/backlog-backend=beads, so firstmate's own work is always represented in the beads store (previously beads_id= was opt-in via --beads only). fm-spawn.sh and fm-brief.sh now auto-resolve/mint a bead via fm_beads_resolve_or_create (fm-tasks-axi-lib.sh) when the beads backend is active and no explicit --beads/FM_HOOK_BEADS_ID was given, applying the existing claim-first/close-last bead lifecycle to every dispatch. Also fixes a pre-existing gap where fm-brief.sh's documented hook-sourcing system (fm-brief-hooks.d/*.sh) was never actually wired into the code, so the Bead Receipt/Closure sections could never render. Default tasks-axi/manual backend behavior is unchanged; --beads remains opt-in there.
What Changed
bin/fm-spawn.shandbin/fm-brief.shnow auto-resolve/mint a bead via a newfm_beads_resolve_or_createhelper inbin/fm-tasks-axi-lib.shwheneverconfig/backlog-backend=beadsis active and no explicit--beads/FM_HOOK_BEADS_IDwas supplied, applying the existing claim-first/close-last bead lifecycle to every dispatch instead of only opted-in ones; bead minting is deliberately deferred to spawn time (not brief scaffold) to avoid orphaned beads when a brief is never spawned.bin/fm-brief.shwires up the previously-inertfm-brief-hooks.d/*.shsourcing so injected Bead Receipt/Closure sections actually render for auto-linked briefs, andbin/fm-spawn.sh/bin/fm-teardown.sh/bin/fm-bead-stamp.shwere updated to stamp and close these auto-linked beads.tests/fm-beads-backend.test.sh,tests/fm-brief.test.sh, andtests/fm-spawn-beads.test.sh, and syncedAGENTS.md,docs/configuration.md, anddocs/scripts.mdto document the automatic bead-linking behavior; default tasks-axi/manual backend behavior is unchanged.Risk Assessment
✅ Low: Both substantive issues from earlier rounds (orphan-bead creation at scaffold time, and missing Bead Receipt/Closure sections for auto-linked spawns) have been correctly fixed with matching test coverage; the remaining diff is well-contained, fails open consistently with the rest of the codebase, and no new material bugs were found in this pass.
Testing
Ran the three new/updated automated suites (fm-beads-backend, fm-brief, fm-spawn-beads) plus adjacent spawn/ledger regression suites — all pass — and additionally drove the real fm-brief.sh and fm-spawn.sh scripts end-to-end in an isolated sandbox (bypassing only the no-mistakes gate-agent refusal the same way firstmate's own test harness does) to produce a genuine generated brief showing the auto-minted bead id, the spliced-in Bead Receipt/Closure sections positioned correctly before # Setup, the recorded beads_id= in spawn metadata, and the expected list→create→claim-lifecycle-stamp sequence of beads CLI calls, directly demonstrating the described automatic/mandatory bead-linking behavior as a real dispatcher would experience it.
Evidence: Generated brief with auto-injected Bead Receipt/Closure sections
Evidence: Spawn task metadata showing auto-recorded beads_id=
Evidence: Beads CLI call transcript (lookup/mint/claim-lifecycle stamp)
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 4 issues found → auto-fixed (2) ✅
bin/fm-brief.sh:281- fm-brief.sh now mints a bead (fm_beads_resolve_or_create) at brief-scaffold time under config/backlog-backend=beads, before the task is spawned. If the brief is generated but the task is never spawned (captain declines after review, spawn fails, task id abandoned), the bead gets created with no beads_id= ever recorded in any state/<id>.meta, so fm-teardown.sh's close_linked_bead (bin/fm-teardown.sh:347-360) never runs for it — the bead is permanently orphaned/unclosed in the shared federated store. Previously bead creation was opt-in only at spawn time, so this class of orphan didn't exist.bin/fm-tasks-axi-lib.sh:93- Comment says 'As of Stage 1, only reads (fm-fleet-snapshot.sh) use this label; no code in bin/ creates a bead with it yet.' This is now false: fm_beads_resolve_or_create, added a few lines below in this same diff (Stage 3), creates beads carrying this exact label via fm_beads_fleet_label.docs/configuration.md:62- docs/configuration.md states 'as of Stage 1 no bin/ code creates a bead with that label yet, only the reads below query by it.' This is now stale given this diff's fm_beads_resolve_or_create wiring in fm-spawn.sh and fm-brief.sh, which mints beads carrying the fleet:firstmate label.bin/fm-tasks-axi-lib.sh:110- fm_beads_resolve_or_create's lookup-then-create (task list --label ... then task create) is not atomic. fm-brief.sh calls it unlocked, fm-spawn.sh calls it under a per-task spawn lock; if brief.sh and spawn.sh (or two brief.sh invocations) ever race for the same task id, two distinct beads could both be minted with the same task:<id> label since there's no compare-and-swap. Low practical risk given the documented sequential brief-then-spawn workflow, but worth noting since the function is explicitly designed to converge callers onto one bead.🔧 Fix: {"summary": "Defer beads auto-minting from brief scaffold to spawn"}
2 issues (1 warning, 1 info) still open:
bin/fm-spawn.sh:464- Under auto-linked beads-backend spawns (no explicit --beads), the crewmate's brief.md is fully scaffolded by fm-brief.sh before fm-spawn.sh mints/resolves the bead (bead resolution was deliberately deferred to fm-spawn.sh by the fix commit to avoid orphan beads). fm-spawn.sh only reads an already-existing brief and never appends to it, so the '# Bead Receipt'/'# Bead Closure' sections (which instruct the worker to runtask set-state <id> dispatch=claimed/lifecycle=claimedand close the bead) never render for the common auto-linked case. The bead sits at dispatch=sent/lifecycle=sent until fm-teardown.sh force-closes it at landing, skipping the worker-confirmed 'claimed' step entirely. This contradicts fm-spawn.sh's own header comment (lines 103-107) claiming the claim/close lifecycle 'applies to every dispatch, not just opted-in ones.' Untested by tests/fm-spawn-beads.test.sh.bin/fm-tasks-axi-lib.sh:105- fm-spawn.sh header comment (line 107) and fm-tasks-axi-lib.sh's fm_beads_resolve_or_create docstring (line 105, 'so fm-brief.sh and fm-spawn.sh converge on the same bead regardless of call order') are now stale/inaccurate given the fix commit removed fm-brief.sh's call to this function.🔧 Fix: {"summary": "Inject bead hook sections into auto-linked briefs at spawn"}
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
bin/fm-test-run.sh tests/fm-beads-backend.test.sh tests/fm-brief.test.sh tests/fm-spawn-beads.test.sh(all pass: minting/reuse via fm_beads_resolve_or_create, brief scaffold defers bead minting to spawn, auto-link injects Bead Receipt/Closure before # Setup, explicit --beads wins and avoids duplicate sections)bin/fm-test-run.sh tests/fm-spawn-reused-worktree-hooks.test.sh tests/fm-spawn-dispatch-profile.test.sh tests/fm-ledger.test.sh(regression check on adjacent spawn/bead-lifecycle behavior, all pass)Manual end-to-end run of the realbin/fm-brief.shthenbin/fm-spawn.sh(via FM_GATE_REFUSE_BYPASS=1, the same escape hatch the test suite uses, against an isolated temp-sandbox home/project/worktree with faked tmux/treehouse/task binaries) under config/backlog-backend=beads with no --beads flag: confirmed the brief scaffolded with no Bead sections yet, then after spawn the brief.md had # Bead Receipt / # Bead Closure sections referencing the auto-minted bead id spliced in before # Setup, state/<id>.meta recorded beads_id=bd-2f91a, and the fake beads CLI log showed list (lookup) -> create (mint) -> show/set-state dispatch=sent/lifecycle=sent/assign (claim-first lifecycle stamp)✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed (2) ✅
🔧 Fix: summary: suppress SC1090 for dynamic hook sourcing in fm-spawn.sh and fm-brief.sh
1 warning still open:
🔧 Fix: Avoid ShellCheck crash on hook-loop source directive in fm-brief.sh
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation