Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions bin/fm-session-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -257,16 +257,30 @@ print_backlog_tasks_axi_compact() {
fi
}

# print_backlog_beads_compact - beads-authority migration Stage 2 (see
# data/beads-authority-migration-scout/report.md section 4). Mirrors
# data/backlog.md's `## In flight`/`## Queued` structure instead of listing
# only bd's native --ready set, which silently drops in_progress/blocked
# work from the digest. Both sections are scoped by fm_beads_fleet_label so
# this stays firstmate's fleet view, not the shared federated store's full
# cross-project set (same label fm-fleet-snapshot.sh's Stage 1 beads read
# uses). Any read failure falls back to the whole title-line rendering, same
# as before Stage 2.
print_backlog_beads_compact() {
local path=$1 out rc
printf 'compact backlog listing (beads task store; max %s item(s))\n' "$BACKLOG_LIMIT"
out=$(task list --ready --limit "$BACKLOG_LIMIT" 2>&1)
rc=$?
if [ "$rc" -eq 0 ]; then
printf '%s\n' "$out"
local path=$1 label out_inflight rc_inflight out_queued rc_queued
label=$(fm_beads_fleet_label)
printf 'compact backlog listing (beads task store; label %s; max %s item(s) per section)\n' "$label" "$BACKLOG_LIMIT"
out_inflight=$(task list --label "$label" --status in_progress,blocked --limit "$BACKLOG_LIMIT" 2>&1)
rc_inflight=$?
out_queued=$(task list --label "$label" --ready --limit "$BACKLOG_LIMIT" 2>&1)
rc_queued=$?
if [ "$rc_inflight" -eq 0 ] && [ "$rc_queued" -eq 0 ]; then
printf '## In flight\n%s\n' "$out_inflight"
printf '## Queued\n%s\n' "$out_queued"
else
printf 'beads task listing failed; falling back to title-line rendering.\n'
printf '%s\n' "$out"
printf '%s\n' "$out_inflight"
printf '%s\n' "$out_queued"
Comment on lines 281 to +283

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the fallback output all-or-nothing.

If the In flight query fails and the Queued query succeeds, Line 283 prints the successful Queued result before the markdown fallback. The output then contains a partial Beads listing and the fallback listing.

  • bin/fm-session-start.sh#L281-L283: Do not print a successful query capture after either query fails.
  • tests/fm-session-start.test.sh#L1392-L1398: Assert that ready-task-1 is absent from fallback output.
Proposed fix
   else
     printf 'beads task listing failed; falling back to title-line rendering.\n'
-    printf '%s\n' "$out_inflight"
-    printf '%s\n' "$out_queued"
     if [ -f "$path" ]; then
       print_backlog_manual_compact "$path" "fallback"
   assert_contains "$out" "- [ ] blocked-followup - Follow compact startup" \
     "beads read failure fallback omitted the Queued backlog title line - a partial digest was printed instead of the full title-line fallback"
+  assert_not_contains "$out" "ready-task-1" \
+    "beads read failure fallback printed successful Queued-query output"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
printf 'beads task listing failed; falling back to title-line rendering.\n'
printf '%s\n' "$out"
printf '%s\n' "$out_inflight"
printf '%s\n' "$out_queued"
printf 'beads task listing failed; falling back to title-line rendering.\n'
Suggested change
printf 'beads task listing failed; falling back to title-line rendering.\n'
printf '%s\n' "$out"
printf '%s\n' "$out_inflight"
printf '%s\n' "$out_queued"
assert_contains "$out" "- [ ] blocked-followup - Follow compact startup" \
"beads read failure fallback omitted the Queued backlog title line - a partial digest was printed instead of the full title-line fallback"
assert_not_contains "$out" "ready-task-1" \
"beads read failure fallback printed successful Queued-query output"
📍 Affects 2 files
  • bin/fm-session-start.sh#L281-L283 (this comment)
  • tests/fm-session-start.test.sh#L1392-L1398
🤖 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-session-start.sh` around lines 281 - 283, Make the fallback output
all-or-nothing in bin/fm-session-start.sh around the query-failure handling:
when either Beads query fails, do not print either captured result before the
markdown fallback, including a successful Queued result when the In flight query
fails. Update tests/fm-session-start.test.sh in the affected fallback case to
assert that ready-task-1 is absent from the output.

if [ -f "$path" ]; then
print_backlog_manual_compact "$path" "fallback"
fi
Expand Down
8 changes: 5 additions & 3 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,18 @@ Because bootstrap requires `tasks-axi` on `PATH` on every profile, that delegati
Compatible means the shared bootstrap probe accepts `tasks-axi --version` as 0.1.1 or newer, `tasks-axi update --help` exposes `--archive-body`, and `tasks-axi mv --help` exposes `[<id>...]` for the atomic multi-ID move introduced in 0.2.2 and required by handoff delegation.
That sentence is the single owner of the tasks-axi compatibility definition; every other document points here instead of restating the version gates.
Bootstrap requires compatible `tasks-axi` on every profile; see "Toolchain" below for missing-tool reporting and silent default-backend behavior.
Set the local, gitignored `config/backlog-backend` file to `beads` to use the beads federated `task` store as the queue source; session-start's digest will list the store's native ready set (via `task list --ready`, bd's dependency-derived readiness with no manual tagging) instead of `data/backlog.md`.
Set the local, gitignored `config/backlog-backend` file to `beads` to use the beads federated `task` store as the queue source instead of `data/backlog.md`.
Session-start's digest mirrors `data/backlog.md`'s `## In flight`/`## Queued` split with two beads-sourced sections, both scoped by the firstmate-fleet label below: **In flight** is `task list --label <label> --status in_progress,blocked`, and **Queued** is `task list --label <label> --ready` (bd's dependency-derived readiness with no manual tagging).
If either read fails, the whole listing falls back to the title-line rendering of `data/backlog.md` rather than printing a partial digest.
Beads requires the `task` CLI on `PATH` and access to the active beads store.
Bootstrap validates the beads backend and reports a `MISSING:` line if the CLI is absent or the store is unreachable.
Set the local, gitignored `config/backlog-backend` file to `manual` to force manual backlog editing and suppress the verbose `BOOTSTRAP_INFO: tasks-axi available` fact, not missing-tool reporting.
Absent or `tasks-axi` selects the default tasks-axi backend.
The file format is unchanged in tasks-axi and manual modes; both produce the same `## In flight`, `## Queued`, and `## Done` sections in `data/backlog.md`.
The beads backend does not use `data/backlog.md`; all backlog state lives in the beads store and is queried dynamically at session start.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the fallback documentation.

Line 63 conflicts with Line 57 and bin/fm-session-start.sh. When a Beads read fails, session-start reads data/backlog.md for the title-line fallback.

Proposed fix
-The beads backend does not use `data/backlog.md`; all backlog state lives in the beads store and is queried dynamically at session start.
+When Beads reads succeed, backlog state is queried dynamically from the beads store. If either read fails, session-start uses `data/backlog.md` for its title-line fallback.

As per coding guidelines, “Report failures and outcomes faithfully, with concrete evidence rather than masking or minimizing failures.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The beads backend does not use `data/backlog.md`; all backlog state lives in the beads store and is queried dynamically at session start.
When Beads reads succeed, backlog state is queried dynamically from the beads store. If either read fails, session-start uses `data/backlog.md` for its title-line fallback.
🤖 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 `@docs/configuration.md` at line 63, Update the fallback documentation in the
Beads backend section to state that session start reads data/backlog.md for the
title-line fallback when a Beads read fails, aligning it with the behavior
described near the existing configuration guidance and implemented by
bin/fm-session-start.sh.

Source: Coding guidelines

Under the beads backend, firstmate's own dispatched-work beads are meant to carry a firstmate-fleet label, `fleet:firstmate` by default (overridable only for test fixtures via `FM_BEADS_FLEET_LABEL`), once bead creation is wired to it, so a `task list --label <that label>` call scopes to firstmate's fleet instead of surfacing the shared federated store's full cross-project set; as of Stage 1 no `bin/` code creates a bead with that label yet, only the reads below query by it.
Under the beads backend, firstmate's own dispatched-work beads are meant to carry a firstmate-fleet label, `fleet:firstmate` by default (overridable only for test fixtures via `FM_BEADS_FLEET_LABEL`), once bead creation is wired to it, so a `task list --label <that label>` call scopes to firstmate's fleet instead of surfacing the shared federated store's full cross-project set; as of Stage 2 no `bin/` code creates a bead with that label yet, only the reads below query by it.
`bin/fm-tasks-axi-lib.sh`'s `fm_beads_fleet_label` is the single owner of that label; read it from there rather than hardcoding it.
The structured fleet snapshot (`bin/fm-fleet-snapshot.sh --json`) and Bearings (`bin/fm-bearings-snapshot.sh`) also read this fleet's in-flight/queued beads, scoped by that label, when the beads backend is selected; with any other backend their output is unchanged.
The structured fleet snapshot (`bin/fm-fleet-snapshot.sh --json`), Bearings (`bin/fm-bearings-snapshot.sh`), and session-start's compact digest above all read this fleet's in-flight/queued beads, scoped by that label, when the beads backend is selected; with any other backend their output is unchanged.
That beads-sourced view currently covers only status open/in_progress/blocked beads mapped to `records[]` state `queued`/`in_flight`; per-bead dependency graphs and correlation with local `state/*.meta` are not yet wired through, so `blocked_by_ids` is always empty and `requires_child_metadata`/`captain_actionable` are always false for a beads-sourced record.

## Runtime backend (config/backend / FM_BACKEND)
Expand Down
97 changes: 80 additions & 17 deletions tests/fm-session-start.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,10 @@ SH
}

# make_fake_task_beads_compact <fakebin>: a fake `task` (beads) CLI whose
# `list --limit 1` (availability probe) and `list --ready` (compact listing)
# both succeed, so a regression back to the empty `--label "status:ready"`
# query is caught by asserting the exact --ready invocation and its output.
# `list --limit 1` (availability probe), `list --label fleet:firstmate
# --status in_progress,blocked` (In flight section), and `list --label
# fleet:firstmate --ready` (Queued section) all succeed, each scoped by the
# firstmate-fleet label so unscoped or wrong-status regressions are caught.
make_fake_task_beads_compact() {
local fakebin=$1
cat > "$fakebin/task" <<'SH'
Expand All @@ -161,11 +162,13 @@ case "$*" in
printf '%s\n' 'fake-ready-1'
exit 0
;;
*'--label '*'status:ready'*)
printf '%s\n' 'No issues found'
*'--label fleet:firstmate'*'--status in_progress,blocked'*)
case "$*" in *'--limit 80'*) : ;; *) printf '%s\n' 'missing compact limit' >&2; exit 9 ;; esac
printf '%s\n' 'inflight-task-1'
printf '%s\n' 'inflight-task-2'
exit 0
;;
*'--ready'*)
*'--label fleet:firstmate'*'--ready'*)
case "$*" in *'--limit 80'*) : ;; *) printf '%s\n' 'missing compact limit' >&2; exit 9 ;; esac
printf '%s\n' 'ready-task-1'
printf '%s\n' 'ready-task-2'
Expand All @@ -177,6 +180,37 @@ SH
chmod +x "$fakebin/task"
}

# make_fake_task_beads_inflight_read_fails <fakebin>: same availability probe
# as make_fake_task_beads_compact, but the In flight `--status
# in_progress,blocked` call fails while the Queued `--ready` call would
# otherwise succeed, so the whole beads listing must fall back to title-line
# rendering rather than printing a partial digest.
make_fake_task_beads_inflight_read_fails() {
local fakebin=$1
cat > "$fakebin/task" <<'SH'
#!/usr/bin/env bash
set -u
log=${FM_FAKE_TASK_LOG:-}
[ -n "$log" ] && printf '%s\n' "$*" >> "$log"
case "$*" in
'list --limit 1')
printf '%s\n' 'fake-ready-1'
exit 0
;;
*'--status in_progress,blocked'*)
printf '%s\n' 'store timeout' >&2
exit 1
;;
*'--ready'*)
printf '%s\n' 'ready-task-1'
exit 0
;;
esac
exit 1
SH
chmod +x "$fakebin/task"
}

# make_fake_ps_claude <fakebin>: harness_pid()/holder_alive() (fm-lock.sh) walk
# `ps` output looking for a harness command name; this fake reports EVERY
# queried pid as a live `claude` harness, so the very first ancestry check
Expand Down Expand Up @@ -1311,7 +1345,7 @@ EOF
pass "unavailable or incompatible tasks-axi falls back to compact manual backlog rendering"
}

test_backlog_compact_beads_uses_ready_filter_not_empty_label() {
test_backlog_compact_beads_shows_inflight_and_queued_sections() {
local rec root home fakebin out log
rec=$(new_world backlog-compact-beads)
IFS='|' read -r root home fakebin <<EOF
Expand All @@ -1325,16 +1359,44 @@ EOF

out=$(FM_FAKE_TASK_LOG="$log" run_session_start "$home" "$root" "$fakebin:$BASE_PATH")

assert_contains "$out" "compact backlog listing (beads task store; max 80 item(s))" \
"beads backend did not render the compact backlog listing"
assert_contains "$out" "ready-task-1" "beads compact listing omitted a ready item from bd's native --ready filter"
assert_contains "$out" "ready-task-2" "beads compact listing omitted a second ready item"
assert_not_contains "$out" "No issues found" \
"beads backend queried the empty status:ready label instead of bd's native --ready filter"
assert_grep "list --ready --limit 80" "$log" \
"session start did not ask beads for its native --ready set with the bounded limit"
assert_contains "$out" "compact backlog listing (beads task store; label fleet:firstmate; max 80 item(s) per section)" \
"beads backend did not render the labeled compact backlog listing"
assert_contains "$out" "## In flight" "beads backend digest is missing the In flight section heading"
assert_contains "$out" "inflight-task-1" "beads compact listing omitted an in_progress/blocked item from the In flight section"
assert_contains "$out" "inflight-task-2" "beads compact listing omitted a second In flight item"
assert_contains "$out" "## Queued" "beads backend digest is missing the Queued section heading"
assert_contains "$out" "ready-task-1" "beads compact listing omitted a ready item from the Queued section"
assert_contains "$out" "ready-task-2" "beads compact listing omitted a second Queued item"
assert_grep "list --label fleet:firstmate --status in_progress,blocked --limit 80" "$log" \
"session start did not query beads for its own fleet's in_progress/blocked set with the bounded limit"
assert_grep "list --label fleet:firstmate --ready --limit 80" "$log" \
"session start did not query beads for its own fleet's ready set with the bounded limit"

pass "beads backend digest shows In flight (in_progress/blocked) and Queued (ready) sections, both scoped by the fleet label"
}

test_backlog_compact_beads_partial_failure_falls_back_to_manual() {
local rec root home fakebin out
rec=$(new_world backlog-compact-beads-partial-failure)
IFS='|' read -r root home fakebin <<EOF
$rec
EOF
make_fake_toolchain "$fakebin"
make_fake_ps_claude "$fakebin"
make_fake_task_beads_inflight_read_fails "$fakebin"
printf '%s\n' beads > "$home/config/backlog-backend"
write_long_body_backlog "$home/data/backlog.md"

out=$(run_session_start "$home" "$root" "$fakebin:$BASE_PATH")

assert_contains "$out" "beads task listing failed; falling back to title-line rendering." \
"a failed In flight beads read did not trigger the whole-listing fallback"
assert_contains "$out" "- [ ] compact-startup - Compact startup digest" \
"beads read failure fallback omitted the In flight backlog title line"
assert_contains "$out" "- [ ] blocked-followup - Follow compact startup" \
"beads read failure fallback omitted the Queued backlog title line - a partial digest was printed instead of the full title-line fallback"

pass "beads backend lists bd's native ready set, not the empty status:ready label query"
pass "a failed In flight beads read falls back to the whole title-line rendering rather than a partial digest"
}

# --- fleet-state digest: no in-flight tasks ----------------------------------
Expand Down Expand Up @@ -1574,7 +1636,8 @@ test_composition_invokes_real_scripts
test_backlog_compact_tasks_axi_omits_bodies_and_keeps_metadata
test_backlog_compact_manual_backend_skips_indented_bodies
test_backlog_compact_tasks_axi_unavailable_uses_manual_fallback
test_backlog_compact_beads_uses_ready_filter_not_empty_label
test_backlog_compact_beads_shows_inflight_and_queued_sections
test_backlog_compact_beads_partial_failure_falls_back_to_manual
test_fleet_digest_empty_fleet
test_next_step_sources_x_mode_cadence
test_next_step_afk_delegates_to_daemon
Expand Down
Loading