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
13 changes: 13 additions & 0 deletions bin/fm-tasks-axi.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
# - tasks-axi missing from PATH;
# - a caller-supplied --file, because this command owns the addressing and
# tasks-axi would silently let the last --file win;
# - `add` (or its `create` alias) with --start, so neither spelling places a
# row In flight without the dispatch artifacts bin/fm-spawn.sh creates -
# the task record, status file, and inbox that go with the row - which such
# a row would lack, counting as live work nobody is doing that nothing
# later would notice (`start <id>` stays a documented direct transition);
# - a data directory that cannot be resolved, or whose backend configuration
# cannot be read (bin/fm-tasks-axi-lib.sh owns that diagnostic);
# - a markdown `<data>/backlog.md` that is itself a symlink, because the
Expand Down Expand Up @@ -94,6 +99,14 @@ for arg in "$@"; do
--file|--file=*)
fail "this command always addresses this home's backlog at $DATA; drop --file, or run tasks-axi directly for another backlog"
;;
--start)
case "${1:-}" in
add|create)
fail "add --start would place a row In flight with no dispatch record; add it Queued and let bin/fm-spawn.sh start it"
;;
esac
ARGS+=("$arg")
;;
--to|--*-file)
ARGS+=("$arg")
path_value_next=1
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Captain rows have no Beads due semantics, so that create path waives a Beads `du
Do not register a Beads `types.custom` `captain` type for this: captain is a hold kind, and the fleet Beads `due.required` policy for ordinary work stays in the federated beads config.
When the automatic transition gate applies, dispatch and completion are not separate operator actions: each moves its work item inside the same run that creates or removes the task's record, so the ordinary successful path cannot leave the backlog and live task set out of sync ([`bin/fm-backlog-transition-lib.sh`](../bin/fm-backlog-transition-lib.sh)).
Under that gate, dispatch accepts only an unheld, unblocked Queued or In flight item in this home; a missing, Done, held, or dependency-blocked item is refused before any endpoint or local copy is created.
[`bin/fm-tasks-axi.sh`](../bin/fm-tasks-axi.sh) refuses `add --start` and its `create --start` alias so neither spelling places a row In flight without dispatch artifacts, because such a row would have no task record, status file, or inbox and would count as live work nobody is doing; `tasks-axi start <id>` remains a documented direct transition the wrapper passes through.
Completion refuses to report success until the item is closed, and session start reconciles this home's own books after an interrupted run.
When a spawn is interrupted after launch delivery began, its exit path re-reads the paired task record and the backlog row under the same per-task lock as the commit, repairs a row the commit believed it had moved, and reports only what was verified or honestly attempted, never intent phrased as outcome ([`bin/fm-spawn.sh`](../bin/fm-spawn.sh); [`tests/fm-backlog-atomicity.test.sh`](../tests/fm-backlog-atomicity.test.sh)).
Automatic transitions run from the configured data directory's parent, letting that home's effective tasks-axi configuration address its selected adapter while keeping relative scout-report links rooted there.
Expand Down
24 changes: 24 additions & 0 deletions tests/fm-tasks-axi.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,29 @@ test_wrapper_refusals() {
pass "fm-tasks-axi.sh refuses caller --file, a symlinked home backlog, and an unresolvable home"
}

# Dispatch alone moves a row to In flight, because only bin/fm-spawn.sh
# creates the task record, status file, and inbox that go with it; a row
# hand-placed there through `add --start` would count as live work nobody runs.
test_wrapper_refuses_add_start() {
local dir out rc before
dir=$(make_split wrapper-add-start)
before=$(cat "$dir/home/data/backlog.md")
out=$(wrapper_from_code "$dir" add hs-1 "hand-started" --start 2>&1)
rc=$?
expect_code 2 "$rc" "add --start"
assert_contains "$out" "bin/fm-spawn.sh" "the add --start refusal did not name the dispatch path"
assert_equals "$before" "$(cat "$dir/home/data/backlog.md")" "a refused add --start still wrote a row"
out=$(wrapper_from_code "$dir" create hs-c "hand-started via alias" --start 2>&1)
rc=$?
expect_code 2 "$rc" "create --start"
assert_contains "$out" "bin/fm-spawn.sh" "the create --start refusal did not name the dispatch path"
assert_equals "$before" "$(cat "$dir/home/data/backlog.md")" "a refused create --start still wrote a row"
wrapper_from_code "$dir" add hs-2 "queued" >/dev/null || fail "plain add was refused"
assert_grep "hs-2" "$dir/home/data/backlog.md" "plain add did not write its row"
wrapper_from_code "$dir" start hs-2 >/dev/null || fail "start <id> was refused"
pass "fm-tasks-axi.sh refuses add --start while plain add and start <id> pass through"
}

test_wrapper_single_home() {
local dir
dir="$TMP_ROOT/single-wrapper"
Expand All @@ -224,6 +247,7 @@ if [ "$HAVE_TASKS_AXI" = 1 ]; then
test_wrapper_writes_through_to_home
test_wrapper_overrides_ambient_file
test_wrapper_refusals
test_wrapper_refuses_add_start
test_wrapper_single_home
else
echo "skip: tasks-axi not found; home-addressing cases not run"
Expand Down
Loading