diff --git a/skills/auto-tmux/SKILL.md b/skills/auto-tmux/SKILL.md index cc16341..3d751d7 100644 --- a/skills/auto-tmux/SKILL.md +++ b/skills/auto-tmux/SKILL.md @@ -149,6 +149,7 @@ skills/auto-tmux/scripts/swarm-state.sh init --dir /tmp/ai_swarm skills/auto-tmux/scripts/swarm-state.sh task-add --id task-001 --text "检查 README 链接" skills/auto-tmux/scripts/swarm-state.sh task-next --owner :. skills/auto-tmux/scripts/swarm-state.sh lock-acquire --name README.md --owner :. +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 :. --reason "等待输入" skills/auto-tmux/scripts/swarm-state.sh task-fail --id task-001 --owner :. --reason "测试失败" skills/auto-tmux/scripts/swarm-state.sh report --dir /tmp/ai_swarm diff --git a/skills/auto-tmux/references/automation.md b/skills/auto-tmux/references/automation.md index 7e4c93d..5ca4ece 100644 --- a/skills/auto-tmux/references/automation.md +++ b/skills/auto-tmux/references/automation.md @@ -206,6 +206,12 @@ skills/auto-tmux/scripts/swarm-state.sh task-fail --id task-001 --owner "ai-hub: skills/auto-tmux/scripts/swarm-state.sh lock-acquire --name README.md --owner "ai-hub:2.1" ``` +回收过期锁前先 dry-run: + +```bash +skills/auto-tmux/scripts/swarm-state.sh lock-prune --older-than 3600 --dry-run +``` + 完成任务并生成报告: ```bash diff --git a/skills/auto-tmux/references/swarm-state.md b/skills/auto-tmux/references/swarm-state.md index 40986bf..94f1c8e 100644 --- a/skills/auto-tmux/references/swarm-state.md +++ b/skills/auto-tmux/references/swarm-state.md @@ -141,7 +141,15 @@ skills/auto-tmux/scripts/swarm-state.sh lock-release \ skills/auto-tmux/scripts/swarm-state.sh lock-list ``` +清理过期锁前先演练: + +```bash +skills/auto-tmux/scripts/swarm-state.sh lock-prune --older-than 3600 --dry-run +skills/auto-tmux/scripts/swarm-state.sh lock-prune --older-than 3600 +``` + 锁使用 `mkdir` 创建目录,具备基本原子性。若锁归属不匹配,默认拒绝释放;确需人工回收时才使用 `--force`。 +`lock-prune` 用于 worker 崩溃或会话中断后的过期锁回收,必须先 `--dry-run` 确认目标。 ## 报告 diff --git a/skills/auto-tmux/scripts/README.md b/skills/auto-tmux/scripts/README.md index 436761a..7f36a71 100644 --- a/skills/auto-tmux/scripts/README.md +++ b/skills/auto-tmux/scripts/README.md @@ -52,6 +52,7 @@ skills/auto-tmux/scripts/swarm-watch.sh --session ai-hub --swarm-dir /tmp/ai_swa skills/auto-tmux/scripts/swarm-state.sh init --dir /tmp/ai_swarm skills/auto-tmux/scripts/swarm-state.sh task-add --id task-001 --text "检查 README 链接" skills/auto-tmux/scripts/swarm-state.sh task-next --owner "$target" --dir /tmp/ai_swarm +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 report --dir /tmp/ai_swarm diff --git a/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh b/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh index fd60701..ac56ff4 100755 --- a/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh +++ b/skills/auto-tmux/scripts/auto-tmux-smoke-test.sh @@ -57,6 +57,10 @@ test -s "$SNAPSHOT_DIR/topology.txt" "$SWARM_STATE" task-claim --dir "$SWARM_DIR" --id smoke-task --owner "$worker_target" "$SWARM_STATE" lock-acquire --dir "$SWARM_DIR" --name smoke-file --owner "$worker_target" >/tmp/auto-tmux-smoke-lock.txt "$SWARM_STATE" lock-release --dir "$SWARM_DIR" --name smoke-file --owner "$worker_target" >/tmp/auto-tmux-smoke-unlock.txt +"$SWARM_STATE" lock-acquire --dir "$SWARM_DIR" --name stale-file --owner "$worker_target" >/tmp/auto-tmux-smoke-stale-lock.txt +"$SWARM_STATE" lock-prune --dir "$SWARM_DIR" --older-than 0 --dry-run >/tmp/auto-tmux-smoke-lock-prune-dry-run.txt +"$SWARM_STATE" lock-prune --dir "$SWARM_DIR" --older-than 0 >/tmp/auto-tmux-smoke-lock-prune.txt +grep -q 'stale-file' /tmp/auto-tmux-smoke-lock-prune.txt "$SWARM_STATE" task-done --dir "$SWARM_DIR" --id smoke-task --owner "$worker_target" --result "ok" "$SWARM_STATE" task-add --dir "$SWARM_DIR" --id smoke-next --text "claim next task" >/tmp/auto-tmux-smoke-task-next-add.txt "$SWARM_STATE" task-next --dir "$SWARM_DIR" --owner "$worker_target" >/tmp/auto-tmux-smoke-task-next.txt diff --git a/skills/auto-tmux/scripts/swarm-state.sh b/skills/auto-tmux/scripts/swarm-state.sh index f675270..bdd72a1 100755 --- a/skills/auto-tmux/scripts/swarm-state.sh +++ b/skills/auto-tmux/scripts/swarm-state.sh @@ -27,6 +27,7 @@ Usage: swarm-state.sh lock-acquire --name NAME --owner TARGET [--dir DIR] swarm-state.sh lock-release --name NAME --owner TARGET [--dir DIR] [--force] 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] Environment: @@ -343,6 +344,40 @@ cmd_lock_list() { done } +cmd_lock_prune() { + local older_than="" dry_run="0" + while [[ $# -gt 0 ]]; do + case "$1" in + --dir) set_dir "${2:-}"; shift 2 ;; + --older-than) older_than="${2:-}"; shift 2 ;; + --dry-run) dry_run="1"; shift ;; + *) die "unknown lock-prune option: $1" ;; + esac + done + [[ -n "$older_than" ]] || die "missing --older-than SEC" + [[ "$older_than" =~ ^[0-9]+$ ]] || die "--older-than must be seconds" + ensure_init + + local now path created_at created_epoch age name + now="$(date +%s)" + find "$LOCKS_DIR" -mindepth 1 -maxdepth 1 -type d -name '*.lock.d' -print | sort | while read -r path; do + created_at="$(cat "$path/created_at" 2>/dev/null || true)" + created_epoch="$(date -d "$created_at" +%s 2>/dev/null || printf '0')" + [[ "$created_epoch" != "0" ]] || continue + age=$((now - created_epoch)) + name="$(basename "$path" .lock.d)" + if (( age >= older_than )); then + if [[ "$dry_run" == "1" ]]; then + printf '[dry-run] would prune lock: %s age=%ss\n' "$name" "$age" + else + rm -rf "$path" + cmd_log --target "swarm-state" --status "UNLOCK" --message "pruned stale lock $name age=${age}s" + printf 'lock pruned: %s age=%ss\n' "$name" "$age" + fi + fi + done +} + cmd_report() { while [[ $# -gt 0 ]]; do case "$1" in @@ -379,6 +414,7 @@ main() { lock-acquire) cmd_lock_acquire "$@" ;; lock-release) cmd_lock_release "$@" ;; lock-list) cmd_lock_list "$@" ;; + lock-prune) cmd_lock_prune "$@" ;; report) cmd_report "$@" ;; *) die "unknown command: $cmd" ;; esac