From 74eb5f7d132f0b6cf77171c0d182832132ac0e9e Mon Sep 17 00:00:00 2001 From: AriaNaraghi Date: Sun, 27 Sep 2026 10:00:38 +0300 Subject: [PATCH] Fix dashboard post-process restart Dashboard p treated any existing post container as running, so an exited post-process container could not be restarted from the TUI. Gate restart blocking on Docker state running, not container existence, and tell operators when an exited container will be replaced. Preserve the dashboard's active SWARM_CONFIG when invoking launch.sh post-process. Fixes #110. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 6 ++++++ USAGE.md | 4 ++-- dashboard.sh | 16 +++++++++++++--- tests/test_dashboard.sh | 42 ++++++++++++++++++++++++++++++++++++++--- 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d14799..477b12d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ and submodules without requiring directory changes. Commands, configuration, drivers, dashboard behavior, runtime resource naming, and `Tools: swarm` commit trailers are unchanged. +- **Fix: dashboard `p` restarts an exited post-process.** The + dashboard blocked `p` whenever a post-process container existed, + so an exited one stranded the restart path even after `s`. It now + blocks only a running container and notes that an exited one will + be replaced. `p` also passes the dashboard's `SWARM_CONFIG` to + `launch.sh post-process`, so non-default configs are honoured. ## 0.22.0 — 2026-06-08 diff --git a/USAGE.md b/USAGE.md index b3400f5..db4246d 100644 --- a/USAGE.md +++ b/USAGE.md @@ -356,7 +356,7 @@ do not count toward numbered-agent completion. | `P` | Post-process logs (the `P` row), if it exists. | | `h` | Harvest results. | | `s` | Stop numbered agents and post-process. | -| `p` | Start post-process after confirmation. | +| `p` | Start post-process after confirmation; replaces an exited run. | The Model column appends the agent's reasoning effort as a parenthesised letter: `(h)` high, `(m)` medium, `(l)` low, @@ -472,7 +472,7 @@ Add to `swarm.json`: } ``` -Trigger via `[P]` in the dashboard, `./launch.sh post-process`, +Trigger via `[p]` in the dashboard, `./launch.sh post-process`, or automatically via `./launch.sh wait` after numbered agents have already been started. `./launch.sh wait` does not launch numbered agents. diff --git a/dashboard.sh b/dashboard.sh index 69f106a..989a407 100755 --- a/dashboard.sh +++ b/dashboard.sh @@ -350,6 +350,12 @@ post_process_container_exists() { [ "$_pp_state" != "not found" ] && [ "$_pp_state" != "none" ] } +post_process_container_running() { + local _pp_state + _pp_state=$(container_state "${IMAGE_NAME}-post") + [ "$_pp_state" = "running" ] +} + interactive_container_names() { docker ps -a --format '{{.Names}}' 2>/dev/null \ | grep -E "^${IMAGE_NAME}-interactive-" \ @@ -770,7 +776,7 @@ draw() { printf " ${DIM}[h]${RESET} harvest" # shellcheck disable=SC2059 printf " ${DIM}[s]${RESET} stop all" - if post_process_configured && ! post_process_container_exists; then + if post_process_configured && ! post_process_container_running; then # shellcheck disable=SC2059 printf " ${DIM}[p]${RESET} post-process" fi @@ -838,7 +844,7 @@ while true; do continue fi _pp_name="${IMAGE_NAME}-post" - if post_process_container_exists; then + if post_process_container_running; then echo "(post-processing is already running -- press P for logs)" echo "Stop it first with s before starting a new run." echo "" @@ -846,6 +852,9 @@ while true; do enter_alt_screen continue fi + if post_process_container_exists; then + echo "(previous post-process container will be replaced)" + fi _pp_prompt="Start post-processing now? [y/N] " read -r -p "$_pp_prompt" _pp_confirm || _pp_confirm="" case "$_pp_confirm" in @@ -863,7 +872,8 @@ while true; do docker stop "${IMAGE_NAME}-${i}" 2>/dev/null || true done echo "--- Starting post-processing ---" - "$SWARM_DIR/launch.sh" post-process || \ + SWARM_CONFIG="$CONFIG_FILE" \ + "$SWARM_DIR/launch.sh" post-process || \ echo "(post-processing failed)" echo "" read -rp "Press Enter to return to dashboard..." _ diff --git a/tests/test_dashboard.sh b/tests/test_dashboard.sh index b618e30..e2b2286 100755 --- a/tests/test_dashboard.sh +++ b/tests/test_dashboard.sh @@ -692,9 +692,31 @@ assert_eq "leading blank inspect fallback becomes not found" "not found" \ "$(normalize_docker_state $'\nnot found')" assert_eq "leading blank inspect state keeps real state" "running" \ "$(normalize_docker_state $'\nrunning')" -assert_eq "dashboard uses normalized container state" "4" \ +assert_eq "dashboard uses normalized container state" "5" \ "$(grep -cF 'container_state "' "$DASHBOARD_FILE")" +# Post-process container predicates, loaded from dashboard.sh with +# container_state stubbed. Only a running container blocks p. +eval "$(sed -n '/^post_process_container_exists()/,/^}/p; + /^post_process_container_running()/,/^}/p' "$DASHBOARD_FILE")" +# Invoked by the eval'd predicates above. +# shellcheck disable=SC2329 +container_state() { printf '%s' "$_stub_state"; } +IMAGE_NAME="test-swarm" +for _stub_state in running exited created paused "not found" none; do + _want_exists=true + case "$_stub_state" in + "not found"|none) _want_exists=false ;; + esac + _want_running=false + [ "$_stub_state" = "running" ] && _want_running=true + assert_eq "post-process exists when ${_stub_state}" "$_want_exists" \ + "$(post_process_container_exists && echo true || echo false)" + assert_eq "post-process running when ${_stub_state}" "$_want_running" \ + "$(post_process_container_running && echo true || echo false)" +done +unset -f container_state + pp_lower_case=$(awk ' /^[[:space:]]*p\)/ { p = 1 } p { print } @@ -733,12 +755,22 @@ assert_eq "lowercase p asks for confirmation" "true" \ assert_eq "lowercase p can launch post-process" "1" \ "$(printf '%s\n' "$pp_lower_case" \ | grep -cF '"$SWARM_DIR/launch.sh" post-process' || true)" +assert_eq "lowercase p preserves dashboard config" "1" \ + "$(printf '%s\n' "$pp_lower_case" \ + | grep -cF 'SWARM_CONFIG="$CONFIG_FILE"' || true)" assert_eq "lowercase p has cancellation path" "1" \ "$(printf '%s\n' "$pp_lower_case" \ | grep -cF 'post-processing not started' || true)" assert_eq "lowercase p refuses to replace running post-process" "1" \ "$(printf '%s\n' "$pp_lower_case" \ | grep -cF 'post-processing is already running' || true)" +assert_eq "lowercase p blocks only running post-process" "1" \ + "$(printf '%s\n' "$pp_lower_case" \ + | grep -cF 'if post_process_container_running; then' || true)" +assert_eq "lowercase p allows exited post-process replacement" "1" \ + "$(printf '%s\n' "$pp_lower_case" \ + | grep -cF 'previous post-process container will be replaced' \ + || true)" assert_eq "lowercase p has no replacement prompt" "0" \ "$(printf '%s\n' "$pp_lower_case" \ | grep -cF 'Replace existing' || true)" @@ -751,10 +783,14 @@ assert_eq "footer merges P into the logs hint" "1" \ assert_eq "footer offers lowercase p to start post-process" "1" \ "$(printf '%s\n' "$help_bar" \ | grep -cF '[p]' || true)" -assert_eq "footer guards start hint on missing container" "1" \ +assert_eq "footer guards start hint on running container" "1" \ "$(printf '%s\n' "$help_bar" \ - | grep -cF 'post_process_configured && ! post_process_container_exists' \ + | grep -cF \ + 'post_process_configured && ! post_process_container_running' \ || true)" +assert_eq "footer keeps logs hint for exited container" "1" \ + "$(printf '%s\n' "$help_bar" \ + | grep -cF 'if post_process_container_exists; then' || true)" assert_eq "s stops post-process container" "1" \ "$(printf '%s\n' "$s_case" \ | grep -cF 'docker stop "${IMAGE_NAME}-post"' || true)"