@runuai/host 0.9.1 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,11 +2,9 @@
2
2
  # task-down <taskId> — tear the task stack down. Idempotent (ADR-022).
3
3
  #
4
4
  # 1. docker compose down -v --rmi local --remove-orphans.
5
- # 2. For each selected project, git worktree remove --force its worktree
6
- # (falls back to rm -rf when git refuses).
7
- # 3. rm -rf the whole task dir — workspace + ephemeral .uai/ render. The
8
- # per-project bare mirrors under projects/<id>/repo.git persist.
9
- # 4. Persist final state: status=killed (unless already error/shipped).
5
+ # 2. rm -rf the whole task dir workspace + task-private Git repositories.
6
+ # Host caches under projects/<id>/host-cache.git persist.
7
+ # 3. Persist final state: status=killed (unless already error/shipped).
10
8
 
11
9
  set -euo pipefail
12
10
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -30,14 +28,14 @@ if [ "$row_count" -lt 1 ]; then
30
28
  log "task row unavailable — tearing down by derived names"
31
29
  worktree_path=""
32
30
  compose_project="$(compose_project_for_task "$task_id")"
33
- projects_json="[]"
34
31
  else
35
32
  worktree_path=$(jq -r '.[0].worktree_path // ""' <<<"$task_json")
36
33
  compose_project=$(jq -r '.[0].compose_project // ""' <<<"$task_json")
37
- projects_json=$(db_get_projects_for_task "$task_id" 2>/dev/null) \
38
- || projects_json="[]"
39
34
  fi
40
- projects_root="$UAI_WORKSPACE_ROOT/projects"
35
+ # A task-up failure can leave an error row with compose_project=NULL while its
36
+ # conventionally named container still exists. Bookkeeping must never suppress
37
+ # teardown; the compose project is deterministic from the task id.
38
+ [ -n "$compose_project" ] || compose_project=$(compose_project_for_task "$task_id")
41
39
 
42
40
  # task_dir falls back to the derived path when the row's worktree_path is
43
41
  # missing (e.g. crashed before persisting).
@@ -50,58 +48,36 @@ fi
50
48
  # 1. Compose stack.
51
49
  # -----------------------------------------------------------------------------
52
50
 
53
- if [ -n "$compose_project" ]; then
54
- step "COMPOSE_DOWN_FAILED" "docker compose down -v --rmi local"
55
- if [ -n "$task_dir" ] && [ -f "$task_dir/.uai/docker-compose.yml" ]; then
56
- docker compose -p "$compose_project" -f "$task_dir/.uai/docker-compose.yml" \
57
- down -v --rmi local --remove-orphans >/dev/null 2>&1 || true
58
- else
59
- # Fallback: tear down by project name only.
60
- docker compose -p "$compose_project" down -v --rmi local --remove-orphans >/dev/null 2>&1 || true
61
- fi
62
- # `compose down` is intentionally best-effort because an already-absent
63
- # stack can make a config-less fallback fail. Destruction itself is not
64
- # best-effort: before deleting the worktree or reporting success, prove that
65
- # no container remains under the stable compose project name.
66
- remaining_containers=""
67
- if ! remaining_containers=$(docker ps -aq \
68
- --filter "label=com.docker.compose.project=$compose_project"); then
69
- emit_err "COMPOSE_DOWN_FAILED" \
70
- "could not verify teardown for compose project $compose_project" \
71
- "docker ps after compose down"
72
- fi
73
- if [ -n "$remaining_containers" ]; then
74
- emit_err "COMPOSE_DOWN_FAILED" \
75
- "compose project $compose_project still has containers after teardown" \
76
- "docker ps after compose down"
77
- fi
51
+ step "COMPOSE_DOWN_FAILED" "docker compose down -v --rmi local"
52
+ if [ -n "$task_dir" ] && [ -f "$task_dir/.uai/docker-compose.yml" ]; then
53
+ docker compose -p "$compose_project" -f "$task_dir/.uai/docker-compose.yml" \
54
+ down -v --rmi local --remove-orphans >/dev/null 2>&1 || true
55
+ else
56
+ # Fallback: tear down by project name only.
57
+ docker compose -p "$compose_project" down -v --rmi local --remove-orphans >/dev/null 2>&1 || true
78
58
  fi
79
-
80
- # -----------------------------------------------------------------------------
81
- # 2. Worktrees one per selected project.
82
- # -----------------------------------------------------------------------------
83
-
84
- if [ -n "$task_dir" ] && [ -d "$task_dir/workspace" ]; then
85
- while IFS= read -r project_obj; do
86
- [ -n "$project_obj" ] || continue
87
- project_id=$(jq -r '.id' <<<"$project_obj")
88
- project_slug=$(jq -r '.slug' <<<"$project_obj")
89
- mirror_dir="$projects_root/$project_id/repo.git"
90
- worktree_target="$task_dir/workspace/$project_slug"
91
- [ -d "$worktree_target" ] || continue
92
- if [ -d "$mirror_dir" ]; then
93
- # `--force` because the worktree may carry uncommitted changes the
94
- # user asked us to discard.
95
- git -C "$mirror_dir" worktree remove --force "$worktree_target" 2>/dev/null \
96
- || rm -rf "$worktree_target"
97
- else
98
- rm -rf "$worktree_target"
99
- fi
100
- done < <(jq -c '.[]' <<<"$projects_json")
59
+ # `compose down` is intentionally best-effort because an already-absent
60
+ # stack can make a config-less fallback fail. Destruction itself is not
61
+ # best-effort: before deleting the worktree or reporting success, prove that
62
+ # no container remains under the stable compose project name.
63
+ remaining_containers=""
64
+ if ! remaining_containers=$(docker ps -aq \
65
+ --filter "label=com.docker.compose.project=$compose_project"); then
66
+ emit_err "COMPOSE_DOWN_FAILED" \
67
+ "could not verify teardown for compose project $compose_project" \
68
+ "docker ps after compose down"
69
+ fi
70
+ if [ -n "$remaining_containers" ]; then
71
+ emit_err "COMPOSE_DOWN_FAILED" \
72
+ "compose project $compose_project still has containers after teardown" \
73
+ "docker ps after compose down"
101
74
  fi
102
75
 
103
76
  # -----------------------------------------------------------------------------
104
- # 3. Remove the entire task directory (workspace + .uai/).
77
+ # 2. Remove the entire task directory (workspace + task-private Git data).
78
+ # No host Git command reads a repository that the just-removed container could
79
+ # have modified. Legacy shared-mirror worktree metadata may remain stale, but
80
+ # that retired `repo.git` path is never trusted by task-up again.
105
81
  # -----------------------------------------------------------------------------
106
82
 
107
83
  if [ -n "$task_dir" ] && [ -d "$task_dir" ]; then
@@ -110,10 +86,10 @@ if [ -n "$task_dir" ] && [ -d "$task_dir" ]; then
110
86
  fi
111
87
 
112
88
  # -----------------------------------------------------------------------------
113
- # 4. Persist final state.
89
+ # 3. Persist final state.
114
90
  # -----------------------------------------------------------------------------
115
91
 
116
- current_status=$(jq -r '.[0].status' <<<"$task_json")
92
+ current_status=$(jq -r '.[0].status // ""' <<<"$task_json")
117
93
  if [ "$current_status" != "shipped" ] && [ "$current_status" != "error" ]; then
118
94
  new_status="killed"
119
95
  else