Skip to content
Closed
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
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,16 +428,16 @@ Mention cost as a courtesy when unusually much work is running, but never block

## 10. Backlog contract

`data/backlog.md` is the durable queue.
`data/backlog.md` is the durable queue (for tasks-axi and manual backends); when `config/backlog-backend=beads` is set, the beads federated task store is the queue source instead.
It tracks work items only, never agents; persistent secondmates never appear as backlog items.
Work routed to a secondmate is recorded in that secondmate home's own backlog, not the main backlog.
When a main-side thread such as a pending captain decision or relay reminder is worth durable tracking, file it as its own work item; use `tasks-axi hold <id> --reason "<reason>" --kind captain` for a captain-gated thread.
When a main-side thread such as a pending captain decision or relay reminder is worth durable tracking, file it as its own work item; use `tasks-axi hold <id> --reason "<reason>" --kind captain` for a captain-gated thread (or the equivalent beads API when using the beads backend).
Unresolved decisions discovered by investigations or visual reviews follow `decision-hold-lifecycle`, which owns their mandatory backlog lifecycle.
Update the backlog on every dispatch, completion, and decision for a work item.
Re-evaluate queued work after every teardown and heartbeat, dispatching items only when dependencies and time gates have cleared.

`.tasks.toml`, `docs/configuration.md`, and current `tasks-axi --help` own the backlog schema, compatibility, retention, and routine command syntax.
Use compatible `tasks-axi` when the configured backend selects it and the documented manual path otherwise; keep only the configured recent Done entries.
`.tasks.toml`, `docs/configuration.md`, and current `tasks-axi --help` own the backlog schema, compatibility, retention, and routine command syntax for the default and manual backends; `docs/configuration.md` also documents the beads backend option and its mechanics.
Use compatible `tasks-axi` when the configured backend selects it, the beads store when beads is configured, and the documented manual path otherwise; keep only the configured recent Done entries.
`secondmate-provisioning` and `bin/fm-backlog-handoff.sh` own cross-home handoff safety.

Keep free-form notes free of temporary paths, moving versions, ephemeral identifiers, and copied state that will rot.
Expand Down
25 changes: 21 additions & 4 deletions bin/fm-bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ install_cmd() {
no-mistakes) echo "curl -fsSL https://raw.githubusercontent.com/kunchenguid/no-mistakes/main/docs/install.sh | sh" ;;
gh-axi|chrome-devtools-axi|lavish-axi) echo "npm install -g $1 && $1 setup hooks" ;;
tasks-axi|quota-axi) echo "npm install -g $1" ;;
task) echo "go install github.com/steveyegge/beads/cmd/bd@latest # task is the beads CLI" ;;
*) return 1 ;;
esac
}
Expand Down Expand Up @@ -864,10 +865,26 @@ if [ "${FM_BOOTSTRAP_VERBOSE_FACTS:-0}" = 1 ] && [ -n "$crew" ] && [ "$crew" !=
echo "BOOTSTRAP_INFO: crew harness override active: $crew"
fi
crew_dispatch_validate
if [ "${FM_BOOTSTRAP_VERBOSE_FACTS:-0}" = 1 ] \
&& ! fm_backlog_backend_manual "$CONFIG" && fm_tasks_axi_compatible; then
echo "BOOTSTRAP_INFO: tasks-axi available"
fi
backlog_backend=$(fm_backlog_backend_value "$CONFIG")
case "$backlog_backend" in
beads)
if ! command -v task >/dev/null 2>&1; then
echo "MISSING: task CLI (beads store; install: $(install_cmd task))"
elif ! task list --limit 1 >/dev/null 2>&1; then
echo "MISSING: task store is unreachable or broken (beads backend configured, cannot run 'task list')"
elif [ "${FM_BOOTSTRAP_VERBOSE_FACTS:-0}" = 1 ]; then
echo "BOOTSTRAP_INFO: beads task store available"
fi
;;
manual)
: # manual backend requires no validation
;;
*)
if [ "${FM_BOOTSTRAP_VERBOSE_FACTS:-0}" = 1 ] && fm_tasks_axi_compatible; then
echo "BOOTSTRAP_INFO: tasks-axi available"
fi
;;
esac
if [ "${FM_BOOTSTRAP_DETECT_ONLY:-0}" != 1 ]; then
secondmate_liveness_sweep
secondmate_sync
Expand Down
31 changes: 29 additions & 2 deletions bin/fm-session-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,19 @@ print_file_or_absent() {
}

print_backlog_pointer() {
printf 'Full task bodies remain available on demand: tasks-axi show <id> --full when compatible tasks-axi is available, or data/backlog.md.\n'
local backend
backend=$(fm_backlog_backend_value "$CONFIG")
case "$backend" in
beads)
printf 'Full task bodies remain available on demand: task show <id> (beads task store).\n'
;;
manual)
printf 'Full task bodies remain available on demand: inspect data/backlog.md.\n'
;;
*)
printf 'Full task bodies remain available on demand: tasks-axi show <id> --full when compatible tasks-axi is available, or data/backlog.md.\n'
;;
esac
}

print_backlog_manual_compact() {
Expand Down Expand Up @@ -192,10 +204,25 @@ print_backlog_tasks_axi_compact() {
fi
}

print_backlog_beads_compact() {
local out rc
printf 'compact backlog listing (beads task store; max %s item(s))\n' "$BACKLOG_LIMIT"
out=$(task list --label "status:ready" --limit "$BACKLOG_LIMIT" 2>&1)
rc=$?
if [ "$rc" -eq 0 ]; then
printf '%s\n' "$out"
else
printf 'beads task listing failed: %s\n' "$out"
fi
}

print_backlog_compact() {
local path=$1 label=$2
subsection "$label"
if [ -f "$path" ]; then
if fm_beads_backend_available "$CONFIG"; then
print_backlog_beads_compact
print_backlog_pointer
elif [ -f "$path" ]; then
if [ -s "$path" ]; then
if fm_tasks_axi_backend_available "$CONFIG"; then
print_backlog_tasks_axi_compact "$path"
Expand Down
8 changes: 8 additions & 0 deletions bin/fm-tasks-axi-lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,13 @@ fm_backlog_backend_manual() {
fm_tasks_axi_backend_available() {
local config_dir=$1
fm_backlog_backend_manual "$config_dir" && return 1
[ "$(fm_backlog_backend_value "$config_dir")" = beads ] && return 1
fm_tasks_axi_compatible
}

fm_beads_backend_available() {
local config_dir=$1
[ "$(fm_backlog_backend_value "$config_dir")" = beads ] || return 1
command -v task >/dev/null 2>&1 || return 1
task list --limit 1 >/dev/null 2>&1
}
6 changes: 5 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ 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 items with `status:ready` label from the beads store instead of `data/backlog.md`.
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 both modes; tasks-axi and manual edits produce the same `## In flight`, `## Queued`, and `## Done` sections.
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.

## Runtime backend (config/backend / FM_BACKEND)

Expand Down
92 changes: 92 additions & 0 deletions tests/fm-beads-backend.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env bash
# tests/fm-beads-backend.test.sh - beads as third backlog backend option.
# Tests backend selection and availability checks.
set -u

# shellcheck source=tests/lib.sh disable=SC1091
. "$(dirname "${BASH_SOURCE[0]}")/lib.sh"

# shellcheck source=bin/fm-tasks-axi-lib.sh disable=SC1091
. "$(dirname "${BASH_SOURCE[0]}")/../bin/fm-tasks-axi-lib.sh"

TMP_ROOT=$(fm_test_tmproot fm-beads-backend)

# Test: fm_backlog_backend_value() returns beads when configured
test_beads_backend_value() {
local config="$TMP_ROOT/config"
mkdir -p "$config"

# Test default (tasks-axi)
value=$(fm_backlog_backend_value "$config")
[ "$value" = "tasks-axi" ] || fail "default backend should be tasks-axi, got: $value"

# Test explicit beads
printf '%s' 'beads' > "$config/backlog-backend"
value=$(fm_backlog_backend_value "$config")
[ "$value" = "beads" ] || fail "beads backend when configured should return beads, got: $value"

# Test manual
printf '%s' 'manual' > "$config/backlog-backend"
value=$(fm_backlog_backend_value "$config")
[ "$value" = "manual" ] || fail "manual backend when configured should return manual, got: $value"
}

# Test: fm_beads_backend_available() checks config and task CLI
test_beads_backend_available() {
local config="$TMP_ROOT/config-beads"
mkdir -p "$config"

# Test: beads not configured - should return false
printf '%s' 'tasks-axi' > "$config/backlog-backend"
if fm_beads_backend_available "$config"; then
fail "fm_beads_backend_available should return false when beads not configured"
fi

# Test: beads configured but task CLI not found - should return false
printf '%s' 'beads' > "$config/backlog-backend"
if ! command -v task >/dev/null 2>&1; then
if fm_beads_backend_available "$config"; then
fail "fm_beads_backend_available should return false when task CLI not found"
fi
return 0
fi

# Test: beads configured and task CLI found - should return true if store is reachable
if ! task list --limit 1 >/dev/null 2>&1; then
# Store not reachable in test environment - that's OK
return 0
fi
if ! fm_beads_backend_available "$config"; then
fail "fm_beads_backend_available should return true when beads configured and task CLI works"
fi
}

# Test: fm_tasks_axi_backend_available() returns false when beads is configured
test_tasks_axi_backend_false_for_beads() {
local config="$TMP_ROOT/config-axi"
mkdir -p "$config"

printf '%s' 'beads' > "$config/backlog-backend"

if fm_tasks_axi_backend_available "$config"; then
fail "fm_tasks_axi_backend_available should return false when beads is configured"
fi
}

# Test: backend value handles whitespace
test_backend_value_whitespace() {
local config="$TMP_ROOT/config-ws"
mkdir -p "$config"

printf '%s' ' beads ' > "$config/backlog-backend"
value=$(fm_backlog_backend_value "$config")
[ "$value" = "beads" ] || fail "backend value should strip whitespace, got: $value"
}

# Run all tests
test_beads_backend_value
test_beads_backend_available
test_tasks_axi_backend_false_for_beads
test_backend_value_whitespace

echo "ok - all beads backend tests passed"
Loading