feat: skills - validate auto-tmux swarm state

This commit is contained in:
tukuaiai
2026-05-19 03:12:40 +08:00
parent 63d2ca0e32
commit 863d7a0e99
6 changed files with 87 additions and 0 deletions
+1
View File
@@ -152,6 +152,7 @@ skills/auto-tmux/scripts/swarm-state.sh lock-acquire --name README.md --owner <s
skills/auto-tmux/scripts/swarm-state.sh lock-prune --older-than 3600 --dry-run
skills/auto-tmux/scripts/swarm-state.sh task-block --id task-001 --owner <session>:<window>.<pane> --reason "等待输入"
skills/auto-tmux/scripts/swarm-state.sh task-fail --id task-001 --owner <session>:<window>.<pane> --reason "测试失败"
skills/auto-tmux/scripts/swarm-state.sh validate --dir /tmp/ai_swarm
skills/auto-tmux/scripts/swarm-state.sh report --dir /tmp/ai_swarm
```
@@ -216,6 +216,7 @@ skills/auto-tmux/scripts/swarm-state.sh lock-prune --older-than 3600 --dry-run
```bash
skills/auto-tmux/scripts/swarm-state.sh task-done --id task-001 --owner "ai-hub:2.1" --result "make test passed"
skills/auto-tmux/scripts/swarm-state.sh validate
skills/auto-tmux/scripts/swarm-state.sh report
```
@@ -115,6 +115,14 @@ skills/auto-tmux/scripts/swarm-state.sh task-fail \
skills/auto-tmux/scripts/swarm-state.sh task-list
```
校验状态一致性:
```bash
skills/auto-tmux/scripts/swarm-state.sh validate
```
`validate` 会检查 `tasks.tsv` 表头、字段数量、重复任务 ID、非法状态、终态任务结果文件,以及锁目录中的 `owner` / `created_at`
## 锁管理
锁用于避免多个 worker 同时修改同一文件、目录或服务。
@@ -165,6 +173,8 @@ skills/auto-tmux/scripts/swarm-state.sh report
- 当前锁。
- 最近状态日志。
报告前建议先运行 `validate`,避免把坏状态继续传给 commander 或 reviewer。
## 推荐协作流
1. commander 初始化状态目录。
+1
View File
@@ -55,6 +55,7 @@ skills/auto-tmux/scripts/swarm-state.sh task-next --owner "$target" --dir /tmp/a
skills/auto-tmux/scripts/swarm-state.sh lock-prune --older-than 3600 --dry-run --dir /tmp/ai_swarm
skills/auto-tmux/scripts/swarm-state.sh task-block --id task-001 --owner "$target" --reason "等待输入" --dir /tmp/ai_swarm
skills/auto-tmux/scripts/swarm-state.sh task-fail --id task-001 --owner "$target" --reason "测试失败" --dir /tmp/ai_swarm
skills/auto-tmux/scripts/swarm-state.sh validate --dir /tmp/ai_swarm
skills/auto-tmux/scripts/swarm-state.sh report --dir /tmp/ai_swarm
# 端到端自测
@@ -69,6 +69,7 @@ grep -q 'smoke-next' /tmp/auto-tmux-smoke-task-next.txt
"$SWARM_STATE" task-add --dir "$SWARM_DIR" --id smoke-fail --text "fail task" >/tmp/auto-tmux-smoke-task-fail-add.txt
"$SWARM_STATE" task-fail --dir "$SWARM_DIR" --id smoke-fail --owner "$worker_target" --reason "expected failure"
"$SWARM_STATE" report --dir "$SWARM_DIR" >/tmp/auto-tmux-smoke-report.txt
"$SWARM_STATE" validate --dir "$SWARM_DIR" >/tmp/auto-tmux-smoke-state-validate.txt
grep -q 'smoke-task' /tmp/auto-tmux-smoke-report.txt
grep -q 'BLOCKED' /tmp/auto-tmux-smoke-report.txt
grep -q 'FAIL' /tmp/auto-tmux-smoke-report.txt
+73
View File
@@ -29,6 +29,7 @@ Usage:
swarm-state.sh lock-list [--dir DIR]
swarm-state.sh lock-prune --older-than SEC [--dir DIR] [--dry-run]
swarm-state.sh report [--dir DIR]
swarm-state.sh validate [--dir DIR]
Environment:
AUTO_TMUX_SWARM_DIR overrides the default /tmp/ai_swarm.
@@ -396,6 +397,77 @@ cmd_report() {
cmd_status -n 20
}
cmd_validate() {
while [[ $# -gt 0 ]]; do
case "$1" in
--dir) set_dir "${2:-}"; shift 2 ;;
*) die "unknown validate option: $1" ;;
esac
done
ensure_init
local failures=0
local expected_header=$'id\tstatus\towner\ttext\tresult'
local actual_header
actual_header="$(head -n 1 "$TASKS_TSV")"
if [[ "$actual_header" == "$expected_header" ]]; then
printf '[OK] tasks header\n'
else
printf '[FAIL] invalid tasks header: %s\n' "$actual_header" >&2
failures=$((failures + 1))
fi
local awk_output
if awk_output="$(awk -F '\t' '
NR == 1 {next}
NF != 5 {print "[FAIL] invalid field count at line " NR ": " NF; bad = 1; next}
$1 == "" {print "[FAIL] empty task id at line " NR; bad = 1}
seen[$1]++ {print "[FAIL] duplicate task id: " $1; bad = 1}
$2 !~ /^(TODO|DOING|DONE|BLOCKED|FAIL)$/ {print "[FAIL] invalid status for " $1 ": " $2; bad = 1}
END {exit bad ? 1 : 0}
' "$TASKS_TSV" 2>&1)"; then
printf '[OK] tasks rows\n'
else
printf '%s\n' "$awk_output" >&2
failures=$((failures + 1))
fi
local id status owner text result
while IFS=$'\t' read -r id status owner text result; do
[[ -n "$id" ]] || continue
case "$status" in
DONE|BLOCKED|FAIL)
if [[ -s "$RESULTS_DIR/$id.txt" ]]; then
printf '[OK] result exists: %s\n' "$id"
else
printf '[FAIL] missing result file: %s\n' "$id" >&2
failures=$((failures + 1))
fi
;;
esac
done < <(tail -n +2 "$TASKS_TSV")
local path name
while IFS= read -r path; do
[[ -n "$path" ]] || continue
name="$(basename "$path" .lock.d)"
if [[ ! -s "$path/owner" ]]; then
printf '[FAIL] lock missing owner: %s\n' "$name" >&2
failures=$((failures + 1))
fi
if [[ ! -s "$path/created_at" ]]; then
printf '[FAIL] lock missing created_at: %s\n' "$name" >&2
failures=$((failures + 1))
fi
done < <(find "$LOCKS_DIR" -mindepth 1 -maxdepth 1 -type d -name '*.lock.d' -print | sort)
if (( failures > 0 )); then
printf 'swarm state invalid: %s issue(s)\n' "$failures" >&2
return 1
fi
printf 'swarm state valid: %s\n' "$SWARM_DIR"
}
main() {
local cmd="${1:-help}"
shift || true
@@ -416,6 +488,7 @@ main() {
lock-list) cmd_lock_list "$@" ;;
lock-prune) cmd_lock_prune "$@" ;;
report) cmd_report "$@" ;;
validate) cmd_validate "$@" ;;
*) die "unknown command: $cmd" ;;
esac
}