|
5 | 5 | LOG_DIR="${1:-.gaai/project/contexts/backlog/.delivery-logs}" |
6 | 6 | PROJECT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)" |
7 | 7 | BACKLOG="$PROJECT_DIR/.gaai/project/contexts/backlog/active.backlog.yaml" |
8 | | -LOCK_DIR="${PROJECT_DIR}/.gaai/project/contexts/backlog/.delivery-locks" |
9 | | -WORKTREE_BASE="${PROJECT_DIR}/../.gaai-worktrees/$(basename "$PROJECT_DIR")" |
10 | 8 |
|
11 | 9 | HAS_JQ=false |
12 | 10 | command -v jq &>/dev/null && HAS_JQ=true |
@@ -52,157 +50,9 @@ format_model() { |
52 | 50 |
|
53 | 51 | PHASE_CACHE_DIR="${PROJECT_DIR}/.gaai/project/contexts/backlog/.delivery-locks/.phase-cache" |
54 | 52 |
|
55 | | -# Returns story IDs — one per line — for all currently active deliveries. |
56 | | -# For legacy pipeline: active tmux sessions named gaai-deliver-{id}. |
57 | | -# For 3phase pipeline: .lock files in LOCK_DIR where backlog status=in_progress. |
58 | | -detect_active_stories() { |
59 | | - local seen=() |
60 | | - |
61 | | - # Legacy: tmux sessions |
62 | | - local tmux_ids |
63 | | - tmux_ids=$(tmux list-sessions -F '#{session_name}' 2>/dev/null \ |
64 | | - | grep '^gaai-deliver-' \ |
65 | | - | sed 's/gaai-deliver-//' || true) |
66 | | - for _id in $tmux_ids; do |
67 | | - local _dp |
68 | | - _dp=$(awk -v id="$_id" ' |
69 | | - $0 == "- id: " id { found=1; next } |
70 | | - found && /^- id:/ { exit } |
71 | | - found && /^[[:space:]]+delivery_pipeline:/ { |
72 | | - gsub(/^[[:space:]]+delivery_pipeline:[[:space:]]*/, "") |
73 | | - gsub(/[[:space:]]*/, ""); print; exit |
74 | | - } |
75 | | - ' "$BACKLOG" 2>/dev/null || true) |
76 | | - [[ "$_dp" != "3phase" ]] && echo "$_id" |
77 | | - seen+=("$_id") |
78 | | - done |
79 | | - |
80 | | - # 3phase: .lock files with in_progress status |
81 | | - if [[ -d "$LOCK_DIR" ]]; then |
82 | | - for _lf in "$LOCK_DIR"/*.lock; do |
83 | | - [[ -f "$_lf" ]] || continue |
84 | | - local _sid |
85 | | - _sid=$(basename "$_lf" .lock) |
86 | | - # Skip if already emitted via tmux path |
87 | | - local _dup=0 |
88 | | - for _s in "${seen[@]:-}"; do [[ "$_s" == "$_sid" ]] && _dup=1 && break; done |
89 | | - [[ $_dup -eq 1 ]] && continue |
90 | | - local _status _dp2 |
91 | | - _status=$(awk -v id="$_sid" ' |
92 | | - $0 == "- id: " id { found=1; next } |
93 | | - found && /^- id:/ { exit } |
94 | | - found && /^[[:space:]]+status:/ { |
95 | | - gsub(/^[[:space:]]+status:[[:space:]]*/, "") |
96 | | - gsub(/[[:space:]]*/, ""); print; exit |
97 | | - } |
98 | | - ' "$BACKLOG" 2>/dev/null || true) |
99 | | - _dp2=$(awk -v id="$_sid" ' |
100 | | - $0 == "- id: " id { found=1; next } |
101 | | - found && /^- id:/ { exit } |
102 | | - found && /^[[:space:]]+delivery_pipeline:/ { |
103 | | - gsub(/^[[:space:]]+delivery_pipeline:[[:space:]]*/, "") |
104 | | - gsub(/[[:space:]]*/, ""); print; exit |
105 | | - } |
106 | | - ' "$BACKLOG" 2>/dev/null || true) |
107 | | - [[ "$_status" == "in_progress" && "$_dp2" == "3phase" ]] && echo "$_sid" |
108 | | - done |
109 | | - fi |
110 | | -} |
111 | | - |
112 | | -# Returns the canonical log path for the current active phase of a 3phase story. |
113 | | -# AC2: per-phase log at {worktree}/.delivery-logs/{id}.{phase}.log |
114 | | -# Falls back to [no log yet] sentinel string when log does not exist. |
115 | | -resolve_3phase_log() { |
116 | | - local story_id="$1" |
117 | | - local worktree="${WORKTREE_BASE}/${story_id}-workspace" |
118 | | - |
119 | | - # Determine active phase from markers (AC1 priority order) |
120 | | - local active_phase="" |
121 | | - for _ph in plan impl qa commit; do |
122 | | - if [[ -f "${LOCK_DIR}/${story_id}.${_ph}.active" ]]; then |
123 | | - active_phase="$_ph" |
124 | | - break |
125 | | - fi |
126 | | - done |
127 | | - |
128 | | - if [[ -z "$active_phase" ]]; then |
129 | | - # No active marker: derive last relevant phase from phase_status |
130 | | - local ps |
131 | | - ps=$(awk -v id="$story_id" ' |
132 | | - $0 == "- id: " id { found=1; next } |
133 | | - found && /^- id:/ { exit } |
134 | | - found && /^[[:space:]]+phase_status:/ { |
135 | | - gsub(/^[[:space:]]+phase_status:[[:space:]]*/, "") |
136 | | - gsub(/[[:space:]]*/, ""); print; exit |
137 | | - } |
138 | | - ' "$BACKLOG" 2>/dev/null || true) |
139 | | - case "$ps" in |
140 | | - not_started) active_phase="plan" ;; |
141 | | - planned) active_phase="plan" ;; |
142 | | - implemented) active_phase="impl" ;; |
143 | | - qa_passed) active_phase="qa" ;; |
144 | | - done) active_phase="commit" ;; |
145 | | - qa_failed|qa_escalated) active_phase="qa" ;; |
146 | | - failed|escalated) active_phase="impl" ;; |
147 | | - "") echo "[?]"; return ;; |
148 | | - *) echo "[?]"; return ;; |
149 | | - esac |
150 | | - fi |
151 | | - |
152 | | - local log_path="${worktree}/.delivery-logs/${story_id}.${active_phase}.log" |
153 | | - if [[ -f "$log_path" ]]; then |
154 | | - echo "$log_path" |
155 | | - else |
156 | | - echo "[no log yet]" |
157 | | - fi |
158 | | -} |
159 | | - |
160 | | -# Returns the display phase label for a 3phase story using authoritative markers. |
161 | | -# AC1: markers take priority over phase_status for in-progress display. |
162 | | -detect_phase_3phase() { |
163 | | - local story_id="$1" |
164 | | - |
165 | | - # Active marker check (highest priority) |
166 | | - for _ph in plan impl qa commit; do |
167 | | - if [[ -f "${LOCK_DIR}/${story_id}.${_ph}.active" ]]; then |
168 | | - case "$_ph" in |
169 | | - plan) echo "PLAN" ;; |
170 | | - impl) echo "IMPL" ;; |
171 | | - qa) echo "QA" ;; |
172 | | - commit) echo "COMMIT" ;; |
173 | | - esac |
174 | | - return |
175 | | - fi |
176 | | - done |
177 | | - |
178 | | - # No active marker: read phase_status for terminal / idle display |
179 | | - local ps |
180 | | - ps=$(awk -v id="$story_id" ' |
181 | | - $0 == "- id: " id { found=1; next } |
182 | | - found && /^- id:/ { exit } |
183 | | - found && /^[[:space:]]+phase_status:/ { |
184 | | - gsub(/^[[:space:]]+phase_status:[[:space:]]*/, "") |
185 | | - gsub(/[[:space:]]*/, ""); print; exit |
186 | | - } |
187 | | - ' "$BACKLOG" 2>/dev/null || true) |
188 | | - |
189 | | - case "$ps" in |
190 | | - done) echo "DONE" ;; |
191 | | - failed|escalated) echo "FAILED" ;; |
192 | | - qa_failed) echo "QA_FAILED" ;; |
193 | | - qa_escalated) echo "QA_ESCALATED" ;; |
194 | | - not_started|planned|implemented|qa_passed) |
195 | | - echo "IDLE @ ${ps}" ;; |
196 | | - "") echo "[?]" ;; |
197 | | - *) echo "[?]" ;; |
198 | | - esac |
199 | | -} |
200 | | - |
201 | 53 | parse_log() { |
202 | 54 | local log_file="$1" |
203 | 55 | local story_id="$2" |
204 | | - local pipeline="${3:-legacy}" # "3phase" or "legacy" |
205 | | - local phase_override="${4:-}" # pre-computed phase label (3phase only) |
206 | 56 |
|
207 | 57 | if [[ ! -f "$log_file" ]]; then |
208 | 58 | echo -e " ${DIM}(log not yet created)${NC}" |
@@ -335,12 +185,6 @@ parse_log() { |
335 | 185 | fi |
336 | 186 |
|
337 | 187 | # ── Phase detection ── |
338 | | - if [[ "$pipeline" == "3phase" && -n "$phase_override" ]]; then |
339 | | - # 3phase: use authoritative marker-derived label (AC1) |
340 | | - phase_label="$phase_override" |
341 | | - phase_origin="" |
342 | | - else |
343 | | - # Legacy pipeline: existing log-content heuristic (unchanged) |
344 | 188 | # Walk recent events, classify each Bash command / Write target into a phase tag, |
345 | 189 | # keep the LAST non-empty classification — that's the current phase. |
346 | 190 | # Origin is captured from the same event so we can show e.g. "IMPL (sub)" when |
@@ -456,7 +300,6 @@ parse_log() { |
456 | 300 | # Current window advanced: write updated winner back to cache |
457 | 301 | printf '%s\t%s\t%s\n' "$cur_rank" "$phase_label" "$phase_origin" > "$cache_file" 2>/dev/null || true |
458 | 302 | fi |
459 | | - fi # close: if [[ 3phase ]] ... else (legacy heuristic) ... fi |
460 | 303 | else |
461 | 304 | last_text=$(tail -200 "$log_file" 2>/dev/null \ |
462 | 305 | | grep -o '"type":"tool_use"[^}]*"name":"[^"]*"' \ |
@@ -539,59 +382,30 @@ parse_log() { |
539 | 382 | fi |
540 | 383 | } |
541 | 384 |
|
542 | | -if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then |
543 | 385 | while true; do |
544 | 386 | clear |
545 | 387 | # In tmux: clear scrollback left by `clear` so prior refresh doesn't ghost below |
546 | 388 | [[ -n "${TMUX:-}" ]] && tmux clear-history 2>/dev/null || true |
547 | 389 | echo "═══ Active Deliveries (refreshes every 5s) ═══" |
548 | 390 | echo "" |
549 | 391 |
|
550 | | - # Read active stories (3phase from locks + legacy from tmux) |
551 | | - active_ids=() |
552 | | - while IFS= read -r _id; do |
553 | | - [[ -n "$_id" ]] && active_ids+=("$_id") |
554 | | - done < <(detect_active_stories) |
| 392 | + # Find active tmux delivery sessions |
| 393 | + active_sessions=$(tmux list-sessions -F '#{session_name}' 2>/dev/null \ |
| 394 | + | grep '^gaai-deliver-' \ |
| 395 | + | sed 's/gaai-deliver-//' || true) |
555 | 396 |
|
556 | | - if [[ ${#active_ids[@]} -eq 0 ]]; then |
| 397 | + if [[ -z "$active_sessions" ]]; then |
557 | 398 | echo -e " ${DIM}No active deliveries. Use /gaai-discover to create stories for the backlog.${NC}" |
558 | 399 | sleep 5 |
559 | 400 | continue |
560 | 401 | fi |
561 | 402 |
|
562 | | - for story_id in "${active_ids[@]}"; do |
563 | | - # Determine pipeline |
564 | | - pipeline=$(awk -v id="$story_id" ' |
565 | | - $0 == "- id: " id { found=1; next } |
566 | | - found && /^- id:/ { exit } |
567 | | - found && /^[[:space:]]+delivery_pipeline:/ { |
568 | | - gsub(/^[[:space:]]+delivery_pipeline:[[:space:]]*/, "") |
569 | | - gsub(/[[:space:]]*/, ""); print; exit |
570 | | - } |
571 | | - ' "$BACKLOG" 2>/dev/null || true) |
572 | | - |
573 | | - if [[ "$pipeline" == "3phase" ]]; then |
574 | | - # AC1: marker-based phase detection |
575 | | - phase_label=$(detect_phase_3phase "$story_id") |
576 | | - # AC2: per-phase log path resolution |
577 | | - log_path=$(resolve_3phase_log "$story_id") |
578 | | - if [[ "$log_path" == "[no log yet]" || "$log_path" == "[?]" ]]; then |
579 | | - echo "── $story_id ── [${phase_label}]" |
580 | | - echo -e " ${DIM}${log_path}${NC}" |
581 | | - echo "" |
582 | | - continue |
583 | | - fi |
584 | | - echo "── $story_id ── [${phase_label}]" |
585 | | - parse_log "$log_path" "$story_id" "3phase" "$phase_label" |
586 | | - else |
587 | | - # Legacy: unchanged path |
588 | | - log_path="$LOG_DIR/${story_id}.log" |
589 | | - echo "── $story_id ──" |
590 | | - parse_log "$log_path" "$story_id" "legacy" "" |
591 | | - fi |
| 403 | + for story_id in $active_sessions; do |
| 404 | + log_file="$LOG_DIR/${story_id}.log" |
| 405 | + echo "── $story_id ──" |
| 406 | + parse_log "$log_file" "$story_id" |
592 | 407 | echo "" |
593 | 408 | done |
594 | 409 |
|
595 | 410 | sleep 5 |
596 | 411 | done |
597 | | -fi |
0 commit comments