diff --git a/skills/auto-tmux/references/iteration-roadmap.md b/skills/auto-tmux/references/iteration-roadmap.md index d780b08..02f7f32 100644 --- a/skills/auto-tmux/references/iteration-roadmap.md +++ b/skills/auto-tmux/references/iteration-roadmap.md @@ -25,7 +25,8 @@ | 17 | `dc59ee2` | 任务依赖 | `task-depend`、`task-ready` | | 18 | `42bca78` | 结果归档 | `swarm-archive.sh` | | 19 | `07ac877` | 安全策略 | `safety-check.sh`、`safety-policy.md` | -| 20 | `本轮` | 状态可视化 | `swarm-board.sh` | +| 20 | `6d46748` | 状态可视化 | `swarm-board.sh` | +| 21 | `本轮` | 依赖领取一致性 | `task-next` 尊重 `blocked-by` | ## 后续候选方向 diff --git a/skills/auto-tmux/references/swarm-state.md b/skills/auto-tmux/references/swarm-state.md index b783d13..50f5cac 100644 --- a/skills/auto-tmux/references/swarm-state.md +++ b/skills/auto-tmux/references/swarm-state.md @@ -105,6 +105,8 @@ skills/auto-tmux/scripts/swarm-state.sh task-claim --id task-001 --owner "ai-hub skills/auto-tmux/scripts/swarm-state.sh task-next --owner "ai-hub:2.1" ``` +`task-next` 会跳过依赖尚未完成的 `TODO` 任务;它与 `task-ready` 使用同一套 blocked-by 判断。 + 完成任务: ```bash diff --git a/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh b/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh index fde14fb..67a21a9 100755 --- a/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh +++ b/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh @@ -14,6 +14,7 @@ SAFETY_CHECK="$SCRIPT_DIR/safety-check.sh" SWARM_DISPATCH="$SCRIPT_DIR/swarm-dispatch.sh" SESSION="auto-tmux-smoke-$$" SWARM_DIR="/tmp/auto-tmux-smoke-swarm-$$" +DEP_SWARM_DIR="/tmp/auto-tmux-smoke-dep-swarm-$$" SNAPSHOT_DIR="/tmp/auto-tmux-smoke-snapshot-$$" RECORD_DIR="/tmp/auto-tmux-smoke-record-$$" BRIEF_DIR="/tmp/auto-tmux-smoke-brief-$$" @@ -26,7 +27,7 @@ PASTE_FILE="/tmp/auto-tmux-smoke-paste-$$.txt" cleanup() { tmux kill-session -t "$SESSION" 2>/dev/null || true - rm -rf "$SWARM_DIR" "$SNAPSHOT_DIR" "$RECORD_DIR" "$BRIEF_DIR" "$WATCH_DIR" "$ARCHIVE_FILE" "$BOARD_FILE" "$DISPATCH_PROMPT" "$TASK_IMPORT_FILE" "$PASTE_FILE" + rm -rf "$SWARM_DIR" "$DEP_SWARM_DIR" "$SNAPSHOT_DIR" "$RECORD_DIR" "$BRIEF_DIR" "$WATCH_DIR" "$ARCHIVE_FILE" "$BOARD_FILE" "$DISPATCH_PROMPT" "$TASK_IMPORT_FILE" "$PASTE_FILE" } trap cleanup EXIT @@ -82,6 +83,16 @@ printf '%s\n' "- import task one" "- import task two" > "$TASK_IMPORT_FILE" "$SWARM_STATE" task-depend --dir "$SWARM_DIR" --id dep-b --blocked-by dep-a >/tmp/auto-tmux-smoke-depend.txt "$SWARM_STATE" task-ready --dir "$SWARM_DIR" >/tmp/auto-tmux-smoke-ready-before.txt ! grep -q 'dep-b' /tmp/auto-tmux-smoke-ready-before.txt +"$SWARM_STATE" init --dir "$DEP_SWARM_DIR" >/tmp/auto-tmux-smoke-dep-only-init.txt +"$SWARM_STATE" task-add --dir "$DEP_SWARM_DIR" --id dep-only-a --text "dependency root" >/tmp/auto-tmux-smoke-dep-only-a.txt +"$SWARM_STATE" task-add --dir "$DEP_SWARM_DIR" --id dep-only-b --text "dependency child" >/tmp/auto-tmux-smoke-dep-only-b.txt +"$SWARM_STATE" task-depend --dir "$DEP_SWARM_DIR" --id dep-only-b --blocked-by dep-only-a >/tmp/auto-tmux-smoke-dep-only-link.txt +"$SWARM_STATE" task-next --dir "$DEP_SWARM_DIR" --owner "$worker_target" >/tmp/auto-tmux-smoke-dep-only-next-a.txt +grep -q 'dep-only-a' /tmp/auto-tmux-smoke-dep-only-next-a.txt +! grep -q 'dep-only-b' /tmp/auto-tmux-smoke-dep-only-next-a.txt +"$SWARM_STATE" task-done --dir "$DEP_SWARM_DIR" --id dep-only-a --owner "$worker_target" --result "dep root done" +"$SWARM_STATE" task-next --dir "$DEP_SWARM_DIR" --owner "$worker_target" >/tmp/auto-tmux-smoke-dep-only-next-b.txt +grep -q 'dep-only-b' /tmp/auto-tmux-smoke-dep-only-next-b.txt "$SWARM_STATE" task-done --dir "$SWARM_DIR" --id dep-a --owner "$worker_target" --result "dep ok" "$SWARM_STATE" task-ready --dir "$SWARM_DIR" >/tmp/auto-tmux-smoke-ready-after.txt grep -q 'dep-b' /tmp/auto-tmux-smoke-ready-after.txt diff --git a/skills/auto-tmux/scripts/swarm-state.sh b/skills/auto-tmux/scripts/swarm-state.sh index f407b7e..a7d2361 100755 --- a/skills/auto-tmux/scripts/swarm-state.sh +++ b/skills/auto-tmux/scripts/swarm-state.sh @@ -324,7 +324,28 @@ cmd_task_next() { acquire_state_lock local id tmp - id="$(awk -F '\t' 'NR > 1 && $2 == "TODO" {print $1; exit}' "$TASKS_TSV")" + id="$(awk -F '\t' ' + FILENAME == ARGV[1] { + if (FNR == 1) next + status[$1] = $2 + order[++n] = $1 + next + } + FILENAME == ARGV[2] { + if (FNR == 1) next + if (status[$2] != "DONE") blocked[$1] = 1 + next + } + END { + for (i = 1; i <= n; i++) { + id = order[i] + if (status[id] == "TODO" && !blocked[id]) { + print id + exit + } + } + } + ' "$TASKS_TSV" "$DEPS_TSV")" if [[ -z "$id" ]]; then release_state_lock printf 'no TODO task available\n'