@ai-dev-methodologies/rlp-desk 0.3.5 → 0.4.0
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.
- package/docs/blueprints/blueprint-v0.4-evolution.md +347 -0
- package/docs/prompts/ralplan-codex-review.md +55 -0
- package/package.json +1 -1
- package/src/commands/rlp-desk.md +73 -23
- package/src/governance.md +39 -22
- package/src/scripts/init_ralph_desk.zsh +139 -4
- package/src/scripts/run_ralph_desk.zsh +358 -80
|
@@ -55,6 +55,7 @@ HEARTBEAT_STALE_THRESHOLD="${HEARTBEAT_STALE_THRESHOLD:-120}"
|
|
|
55
55
|
MAX_RESTARTS="${MAX_RESTARTS:-3}"
|
|
56
56
|
IDLE_NUDGE_THRESHOLD="${IDLE_NUDGE_THRESHOLD:-30}"
|
|
57
57
|
MAX_NUDGES="${MAX_NUDGES:-3}"
|
|
58
|
+
WITH_SELF_VERIFICATION="${WITH_SELF_VERIFICATION:-0}"
|
|
58
59
|
|
|
59
60
|
# --- Engine Selection ---
|
|
60
61
|
WORKER_ENGINE="${WORKER_ENGINE:-claude}" # claude|codex
|
|
@@ -69,6 +70,13 @@ CODEX_BIN="" # resolved by check_dependencies when engine=codex
|
|
|
69
70
|
VERIFY_MODE="${VERIFY_MODE:-per-us}" # per-us|batch
|
|
70
71
|
VERIFY_CONSENSUS="${VERIFY_CONSENSUS:-0}" # 0|1
|
|
71
72
|
CONSENSUS_SCOPE="${CONSENSUS_SCOPE:-all}" # all|final-only
|
|
73
|
+
CB_THRESHOLD="${CB_THRESHOLD:-3}" # consecutive failures before BLOCKED (default: 3)
|
|
74
|
+
# Effective CB threshold: doubled when consensus mode active (AC2 auto-double)
|
|
75
|
+
if [[ "${VERIFY_CONSENSUS:-0}" = "1" ]]; then
|
|
76
|
+
EFFECTIVE_CB_THRESHOLD=$(( CB_THRESHOLD * 2 ))
|
|
77
|
+
else
|
|
78
|
+
EFFECTIVE_CB_THRESHOLD=$CB_THRESHOLD
|
|
79
|
+
fi
|
|
72
80
|
|
|
73
81
|
# --- Derived Paths ---
|
|
74
82
|
DESK="$ROOT/.claude/ralph-desk"
|
|
@@ -89,6 +97,7 @@ STATUS_FILE="$LOGS_DIR/status.json"
|
|
|
89
97
|
SESSION_CONFIG="$LOGS_DIR/session-config.json"
|
|
90
98
|
WORKER_HEARTBEAT="$LOGS_DIR/worker-heartbeat.json"
|
|
91
99
|
VERIFIER_HEARTBEAT="$LOGS_DIR/verifier-heartbeat.json"
|
|
100
|
+
COST_LOG="$LOGS_DIR/cost-log.jsonl"
|
|
92
101
|
|
|
93
102
|
# --- Session Naming ---
|
|
94
103
|
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
@@ -105,6 +114,8 @@ CONSECUTIVE_FAILURES=0
|
|
|
105
114
|
PREV_CONTEXT_HASH=""
|
|
106
115
|
ITERATION=0
|
|
107
116
|
START_TIME=$(date +%s)
|
|
117
|
+
BASELINE_COMMIT="" # git HEAD at campaign start (captured before loop)
|
|
118
|
+
CAMPAIGN_REPORT_GENERATED=0 # guard against double-generation in cleanup trap
|
|
108
119
|
VERIFIED_US="" # comma-separated list of verified US IDs (per-us mode)
|
|
109
120
|
CONSENSUS_ROUND=0 # current consensus round for current US
|
|
110
121
|
US_LIST="" # comma-separated US IDs from PRD (per-us mode)
|
|
@@ -148,12 +159,28 @@ replace_worker_pane() {
|
|
|
148
159
|
log " Replacing dead $role pane $old_pane..."
|
|
149
160
|
tmux kill-pane -t "$old_pane" 2>/dev/null
|
|
150
161
|
|
|
151
|
-
# Create fresh pane
|
|
162
|
+
# Create fresh pane maintaining original layout: worker(top-right) / verifier(bottom-right)
|
|
152
163
|
local new_pane
|
|
153
|
-
|
|
164
|
+
if [[ "$role" == "verifier" ]]; then
|
|
165
|
+
# Verifier goes below worker: split vertically from worker pane
|
|
166
|
+
if tmux display-message -t "$WORKER_PANE" -p '#{pane_id}' &>/dev/null; then
|
|
167
|
+
new_pane=$(tmux split-window -v -d -t "$WORKER_PANE" -P -F '#{pane_id}' -c "$ROOT")
|
|
168
|
+
else
|
|
169
|
+
# Fallback: worker pane also dead, split horizontally from leader
|
|
170
|
+
new_pane=$(tmux split-window -h -d -t "$LEADER_PANE" -P -F '#{pane_id}' -c "$ROOT")
|
|
171
|
+
fi
|
|
172
|
+
else
|
|
173
|
+
# Worker goes above verifier: split vertically before verifier pane
|
|
174
|
+
if tmux display-message -t "$VERIFIER_PANE" -p '#{pane_id}' &>/dev/null; then
|
|
175
|
+
new_pane=$(tmux split-window -v -b -d -t "$VERIFIER_PANE" -P -F '#{pane_id}' -c "$ROOT")
|
|
176
|
+
else
|
|
177
|
+
# Fallback: verifier pane also dead, split horizontally from leader
|
|
178
|
+
new_pane=$(tmux split-window -h -d -t "$LEADER_PANE" -P -F '#{pane_id}' -c "$ROOT")
|
|
179
|
+
fi
|
|
180
|
+
fi
|
|
154
181
|
|
|
155
182
|
log " New $role pane: $new_pane (replaced $old_pane)"
|
|
156
|
-
log_debug "[
|
|
183
|
+
log_debug "[FLOW] iter=$ITERATION pane_replaced=${role} old=$old_pane new=$new_pane"
|
|
157
184
|
|
|
158
185
|
# Update session-config.json with new pane ID
|
|
159
186
|
if [[ -f "$SESSION_CONFIG" ]]; then
|
|
@@ -300,15 +327,42 @@ create_session() {
|
|
|
300
327
|
|
|
301
328
|
fi
|
|
302
329
|
|
|
330
|
+
# Set pane titles and enable border labels for visual distinction
|
|
331
|
+
local worker_label="Worker ($WORKER_ENGINE:$WORKER_MODEL)"
|
|
332
|
+
local verifier_label="Verifier ($VERIFIER_ENGINE:$VERIFIER_MODEL)"
|
|
333
|
+
[[ "$VERIFY_CONSENSUS" = "1" ]] && verifier_label="Verifier ($VERIFIER_ENGINE:$VERIFIER_MODEL + codex:$VERIFIER_CODEX_MODEL)"
|
|
334
|
+
tmux select-pane -t "$LEADER_PANE" -T "Leader" 2>/dev/null
|
|
335
|
+
tmux select-pane -t "$WORKER_PANE" -T "$worker_label" 2>/dev/null
|
|
336
|
+
tmux select-pane -t "$VERIFIER_PANE" -T "$verifier_label" 2>/dev/null
|
|
337
|
+
# Color-coded pane borders: green=leader, blue=worker, yellow=verifier
|
|
338
|
+
tmux set-option -p -t "$LEADER_PANE" pane-border-style "fg=green" 2>/dev/null
|
|
339
|
+
tmux set-option -p -t "$WORKER_PANE" pane-border-style "fg=blue" 2>/dev/null
|
|
340
|
+
tmux set-option -p -t "$VERIFIER_PANE" pane-border-style "fg=yellow" 2>/dev/null
|
|
341
|
+
# Show pane titles in border
|
|
342
|
+
tmux set-option pane-border-status top 2>/dev/null
|
|
343
|
+
tmux set-option pane-border-format "#{?pane_active,#[fg=white bold],#[fg=grey]} #{pane_title} " 2>/dev/null
|
|
344
|
+
|
|
303
345
|
log " Leader pane: $LEADER_PANE"
|
|
304
346
|
log " Worker pane: $WORKER_PANE"
|
|
305
347
|
log " Verifier pane: $VERIFIER_PANE"
|
|
306
348
|
|
|
349
|
+
# AC12: Capture baseline commit before writing session config
|
|
350
|
+
BASELINE_COMMIT=$(git -C "$ROOT" rev-parse HEAD 2>/dev/null || echo "none")
|
|
351
|
+
|
|
352
|
+
# Truncate cost-log for fresh run (previous data in versioned campaign reports)
|
|
353
|
+
> "$COST_LOG"
|
|
354
|
+
|
|
355
|
+
# SV flag warning for tmux mode
|
|
356
|
+
if (( WITH_SELF_VERIFICATION )); then
|
|
357
|
+
log " NOTE: --with-self-verification recorded but SV report generation is Agent-mode only"
|
|
358
|
+
fi
|
|
359
|
+
|
|
307
360
|
# Write session config (atomic write)
|
|
308
361
|
echo '{
|
|
309
362
|
"session_name": "'"$SESSION_NAME"'",
|
|
310
363
|
"slug": "'"$SLUG"'",
|
|
311
364
|
"created_at": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",
|
|
365
|
+
"baseline_commit": "'"$BASELINE_COMMIT"'",
|
|
312
366
|
"panes": {
|
|
313
367
|
"leader": "'"$LEADER_PANE"'",
|
|
314
368
|
"worker": "'"$WORKER_PANE"'",
|
|
@@ -340,7 +394,10 @@ create_session() {
|
|
|
340
394
|
"heartbeat_stale_threshold": '"$HEARTBEAT_STALE_THRESHOLD"',
|
|
341
395
|
"max_restarts": '"$MAX_RESTARTS"',
|
|
342
396
|
"idle_nudge_threshold": '"$IDLE_NUDGE_THRESHOLD"',
|
|
343
|
-
"max_nudges": '"$MAX_NUDGES"'
|
|
397
|
+
"max_nudges": '"$MAX_NUDGES"',
|
|
398
|
+
"cb_threshold": '"$CB_THRESHOLD"',
|
|
399
|
+
"effective_cb_threshold": '"$EFFECTIVE_CB_THRESHOLD"',
|
|
400
|
+
"with_self_verification": '"$WITH_SELF_VERIFICATION"'
|
|
344
401
|
}
|
|
345
402
|
}' | atomic_write "$SESSION_CONFIG"
|
|
346
403
|
|
|
@@ -704,11 +761,15 @@ write_worker_trigger() {
|
|
|
704
761
|
if [[ -n "$next_us" ]]; then
|
|
705
762
|
echo ""
|
|
706
763
|
echo "---"
|
|
707
|
-
echo "## PER-US SCOPE LOCK (this iteration)"
|
|
764
|
+
echo "## PER-US SCOPE LOCK (this iteration) — OVERRIDES memory contract"
|
|
765
|
+
echo "**IGNORE the 'Next Iteration Contract' from memory if it references a different story.**"
|
|
766
|
+
echo "The Leader has determined that **${next_us}** is the next unverified story."
|
|
708
767
|
echo "You MUST implement ONLY **${next_us}** in this iteration."
|
|
709
768
|
echo "Do NOT implement any other user stories."
|
|
710
769
|
echo "When done, signal verify with us_id=\"${next_us}\" (not \"ALL\")."
|
|
711
770
|
echo "Signal format: {\"iteration\": N, \"status\": \"verify\", \"us_id\": \"${next_us}\", ...}"
|
|
771
|
+
echo ""
|
|
772
|
+
echo "**Update the campaign memory's 'Next Iteration Contract' to reflect ${next_us}.**"
|
|
712
773
|
elif [[ -n "$VERIFIED_US" ]]; then
|
|
713
774
|
# All individual US verified — this is the final full verify iteration
|
|
714
775
|
echo ""
|
|
@@ -895,6 +956,7 @@ update_status() {
|
|
|
895
956
|
|
|
896
957
|
echo '{
|
|
897
958
|
"slug": "'"$SLUG"'",
|
|
959
|
+
"baseline_commit": "'"${BASELINE_COMMIT:-none}"'",
|
|
898
960
|
"iteration": '"$ITERATION"',
|
|
899
961
|
"max_iter": '"$MAX_ITER"',
|
|
900
962
|
"phase": "'"$phase"'",
|
|
@@ -922,7 +984,20 @@ write_result_log() {
|
|
|
922
984
|
local result_file="$LOGS_DIR/iter-$(printf '%03d' $iter).result.md"
|
|
923
985
|
|
|
924
986
|
local git_diff=""
|
|
925
|
-
|
|
987
|
+
if git -C "$ROOT" rev-parse HEAD &>/dev/null; then
|
|
988
|
+
git_diff=$(git -C "$ROOT" diff --stat HEAD 2>/dev/null || echo "(no git diff available)")
|
|
989
|
+
else
|
|
990
|
+
git_diff="(no commits in repo — cannot diff)"
|
|
991
|
+
fi
|
|
992
|
+
# Include untracked new files in result log
|
|
993
|
+
local result_untracked
|
|
994
|
+
result_untracked=$(git -C "$ROOT" ls-files --others --exclude-standard 2>/dev/null | head -20)
|
|
995
|
+
if [[ -n "$result_untracked" ]]; then
|
|
996
|
+
git_diff="${git_diff}
|
|
997
|
+
|
|
998
|
+
Untracked new files:
|
|
999
|
+
${result_untracked}"
|
|
1000
|
+
fi
|
|
926
1001
|
|
|
927
1002
|
{
|
|
928
1003
|
echo "# Iteration $iter Result"
|
|
@@ -941,6 +1016,168 @@ write_result_log() {
|
|
|
941
1016
|
} | atomic_write "$result_file"
|
|
942
1017
|
}
|
|
943
1018
|
|
|
1019
|
+
# --- step 7d: Archive iteration artifacts (done-claim + verdict) to logs/ ---
|
|
1020
|
+
archive_iter_artifacts() {
|
|
1021
|
+
local iter="$1"
|
|
1022
|
+
local iter_padded
|
|
1023
|
+
iter_padded=$(printf '%03d' "$iter")
|
|
1024
|
+
if [[ -f "$DONE_CLAIM_FILE" ]]; then
|
|
1025
|
+
cp "$DONE_CLAIM_FILE" "$LOGS_DIR/iter-${iter_padded}-done-claim.json" 2>/dev/null
|
|
1026
|
+
fi
|
|
1027
|
+
if [[ -f "$VERDICT_FILE" ]]; then
|
|
1028
|
+
cp "$VERDICT_FILE" "$LOGS_DIR/iter-${iter_padded}-verify-verdict.json" 2>/dev/null
|
|
1029
|
+
fi
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
# --- AC5: Write per-iteration cost estimate to cost-log.jsonl ---
|
|
1033
|
+
write_cost_log() {
|
|
1034
|
+
local iter="$1"
|
|
1035
|
+
local iter_padded
|
|
1036
|
+
iter_padded=$(printf '%03d' "$iter")
|
|
1037
|
+
|
|
1038
|
+
local prompt_bytes=0 claim_bytes=0 verdict_bytes=0
|
|
1039
|
+
local worker_prompt_file="$LOGS_DIR/iter-${iter_padded}.worker-prompt.md"
|
|
1040
|
+
[[ -f "$worker_prompt_file" ]] && prompt_bytes=$(wc -c < "$worker_prompt_file" 2>/dev/null || echo 0)
|
|
1041
|
+
[[ -f "$DONE_CLAIM_FILE" ]] && claim_bytes=$(wc -c < "$DONE_CLAIM_FILE" 2>/dev/null || echo 0)
|
|
1042
|
+
[[ -f "$VERDICT_FILE" ]] && verdict_bytes=$(wc -c < "$VERDICT_FILE" 2>/dev/null || echo 0)
|
|
1043
|
+
|
|
1044
|
+
local estimated_tokens=$(( (prompt_bytes + claim_bytes + verdict_bytes) / 4 ))
|
|
1045
|
+
|
|
1046
|
+
echo '{"iteration":'"$iter"',"estimated_tokens":'"$estimated_tokens"',"token_source":"estimated","prompt_bytes":'"$prompt_bytes"',"claim_bytes":'"$claim_bytes"',"verdict_bytes":'"$verdict_bytes"',"timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}' >> "$COST_LOG"
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
# --- AC4: Generate campaign-report.md on all terminal states ---
|
|
1050
|
+
generate_campaign_report() {
|
|
1051
|
+
# Guard: idempotent — only generate once per campaign run
|
|
1052
|
+
if (( CAMPAIGN_REPORT_GENERATED )); then return 0; fi
|
|
1053
|
+
CAMPAIGN_REPORT_GENERATED=1
|
|
1054
|
+
|
|
1055
|
+
local final_status="UNKNOWN"
|
|
1056
|
+
if [[ -f "$COMPLETE_SENTINEL" ]]; then final_status="COMPLETE"
|
|
1057
|
+
elif [[ -f "$BLOCKED_SENTINEL" ]]; then final_status="BLOCKED"
|
|
1058
|
+
else final_status="TIMEOUT"; fi
|
|
1059
|
+
|
|
1060
|
+
local report_file="$LOGS_DIR/campaign-report.md"
|
|
1061
|
+
|
|
1062
|
+
# AC9: Version existing report before writing new one
|
|
1063
|
+
if [[ -f "$report_file" ]]; then
|
|
1064
|
+
local v=1
|
|
1065
|
+
while [[ -f "${report_file%.md}-v${v}.md" ]]; do (( v++ )); done
|
|
1066
|
+
mv "$report_file" "${report_file%.md}-v${v}.md"
|
|
1067
|
+
fi
|
|
1068
|
+
|
|
1069
|
+
local end_time
|
|
1070
|
+
end_time=$(date +%s)
|
|
1071
|
+
local elapsed=$(( end_time - START_TIME ))
|
|
1072
|
+
|
|
1073
|
+
local baseline_commit_val="${BASELINE_COMMIT:-none}"
|
|
1074
|
+
local files_changed=""
|
|
1075
|
+
if [[ "$baseline_commit_val" != "none" ]]; then
|
|
1076
|
+
files_changed=$(git -C "$ROOT" diff --stat "${baseline_commit_val}" 2>/dev/null || echo "(git diff unavailable)")
|
|
1077
|
+
elif git -C "$ROOT" rev-parse HEAD &>/dev/null; then
|
|
1078
|
+
files_changed=$(git -C "$ROOT" diff --stat HEAD 2>/dev/null || echo "(git diff unavailable)")
|
|
1079
|
+
else
|
|
1080
|
+
files_changed="(no commits in repo — cannot diff)"
|
|
1081
|
+
fi
|
|
1082
|
+
# Include untracked new files
|
|
1083
|
+
local untracked
|
|
1084
|
+
untracked=$(git -C "$ROOT" ls-files --others --exclude-standard 2>/dev/null | head -20)
|
|
1085
|
+
if [[ -n "$untracked" ]]; then
|
|
1086
|
+
files_changed="${files_changed}
|
|
1087
|
+
|
|
1088
|
+
Untracked new files:
|
|
1089
|
+
${untracked}"
|
|
1090
|
+
fi
|
|
1091
|
+
|
|
1092
|
+
local sv_summary=""
|
|
1093
|
+
if (( WITH_SELF_VERIFICATION )); then
|
|
1094
|
+
local sv_report
|
|
1095
|
+
sv_report=$(ls -t "$LOGS_DIR"/self-verification-report-*.md 2>/dev/null | head -1)
|
|
1096
|
+
if [[ -n "$sv_report" ]]; then
|
|
1097
|
+
sv_summary="See: $(basename "$sv_report")"
|
|
1098
|
+
else
|
|
1099
|
+
sv_summary="SV report generation requires Agent mode. Flag recorded in session-config."
|
|
1100
|
+
fi
|
|
1101
|
+
else
|
|
1102
|
+
sv_summary="N/A — --with-self-verification not enabled"
|
|
1103
|
+
fi
|
|
1104
|
+
|
|
1105
|
+
{
|
|
1106
|
+
echo "# Campaign Report: $SLUG"
|
|
1107
|
+
echo ""
|
|
1108
|
+
echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ) | Status: $final_status | Iterations: $ITERATION"
|
|
1109
|
+
echo ""
|
|
1110
|
+
echo "## Objective"
|
|
1111
|
+
local prd_file="$DESK/plans/prd-$SLUG.md"
|
|
1112
|
+
if [[ -f "$prd_file" ]]; then
|
|
1113
|
+
grep '^## Objective' -A3 "$prd_file" 2>/dev/null | tail -n +2 | head -3
|
|
1114
|
+
else
|
|
1115
|
+
echo "(PRD not found)"
|
|
1116
|
+
fi
|
|
1117
|
+
echo ""
|
|
1118
|
+
echo "## Execution Summary"
|
|
1119
|
+
echo "- Terminal state: $final_status"
|
|
1120
|
+
echo "- Iterations run: $ITERATION / $MAX_ITER"
|
|
1121
|
+
echo "- Elapsed: ${elapsed}s"
|
|
1122
|
+
echo "- Worker model: $WORKER_MODEL ($WORKER_ENGINE)"
|
|
1123
|
+
echo "- Verifier model: $VERIFIER_MODEL ($VERIFIER_ENGINE)"
|
|
1124
|
+
echo ""
|
|
1125
|
+
echo "## US Status"
|
|
1126
|
+
echo "- Verified: ${VERIFIED_US:-none}"
|
|
1127
|
+
echo "- Consecutive failures at end: $CONSECUTIVE_FAILURES"
|
|
1128
|
+
echo ""
|
|
1129
|
+
echo "## Verification Results"
|
|
1130
|
+
local ri=1
|
|
1131
|
+
while (( ri <= ITERATION )); do
|
|
1132
|
+
local iter_dc="$LOGS_DIR/iter-$(printf '%03d' $ri)-done-claim.json"
|
|
1133
|
+
if [[ -f "$iter_dc" ]]; then
|
|
1134
|
+
local us_id
|
|
1135
|
+
us_id=$(jq -r '.us_id // "unknown"' "$iter_dc" 2>/dev/null)
|
|
1136
|
+
echo "- $(basename "$iter_dc"): us_id=$us_id"
|
|
1137
|
+
fi
|
|
1138
|
+
(( ri++ ))
|
|
1139
|
+
done
|
|
1140
|
+
echo ""
|
|
1141
|
+
echo "## Issues Encountered"
|
|
1142
|
+
local fi_found=0
|
|
1143
|
+
local fi_i=1
|
|
1144
|
+
while (( fi_i <= ITERATION )); do
|
|
1145
|
+
local fix_f="$LOGS_DIR/iter-$(printf '%03d' $fi_i).fix-contract.md"
|
|
1146
|
+
if [[ -f "$fix_f" ]]; then
|
|
1147
|
+
echo "- $(basename "$fix_f")"
|
|
1148
|
+
fi_found=1
|
|
1149
|
+
fi
|
|
1150
|
+
(( fi_i++ ))
|
|
1151
|
+
done
|
|
1152
|
+
(( fi_found == 0 )) && echo "- None"
|
|
1153
|
+
echo ""
|
|
1154
|
+
echo "## Cost & Performance"
|
|
1155
|
+
if [[ -f "$COST_LOG" ]]; then
|
|
1156
|
+
local total_tokens=0
|
|
1157
|
+
while IFS= read -r line; do
|
|
1158
|
+
local t
|
|
1159
|
+
t=$(echo "$line" | jq -r '.estimated_tokens // 0' 2>/dev/null || echo 0)
|
|
1160
|
+
total_tokens=$(( total_tokens + t ))
|
|
1161
|
+
done < "$COST_LOG"
|
|
1162
|
+
echo "- Total estimated tokens: $total_tokens (source: estimated, tmux mode)"
|
|
1163
|
+
echo "- See: cost-log.jsonl for per-iteration breakdown"
|
|
1164
|
+
else
|
|
1165
|
+
echo "- No cost data available"
|
|
1166
|
+
fi
|
|
1167
|
+
echo ""
|
|
1168
|
+
echo "## SV Summary"
|
|
1169
|
+
echo "$sv_summary"
|
|
1170
|
+
echo ""
|
|
1171
|
+
echo "## Files Changed"
|
|
1172
|
+
echo '```'
|
|
1173
|
+
echo "$files_changed"
|
|
1174
|
+
echo '```'
|
|
1175
|
+
echo "Note: Files Changed may include pre-existing uncommitted changes if the campaign started in a dirty worktree."
|
|
1176
|
+
} | atomic_write "$report_file"
|
|
1177
|
+
|
|
1178
|
+
log "Campaign report written: $report_file"
|
|
1179
|
+
}
|
|
1180
|
+
|
|
944
1181
|
# =============================================================================
|
|
945
1182
|
# Sentinel Writers
|
|
946
1183
|
# =============================================================================
|
|
@@ -1002,6 +1239,9 @@ cleanup() {
|
|
|
1002
1239
|
setopt local_options nonomatch 2>/dev/null
|
|
1003
1240
|
rm -f "$LOGS_DIR"/*.tmp.* "$MEMOS_DIR"/*.tmp.* 2>/dev/null
|
|
1004
1241
|
|
|
1242
|
+
# AC4: Generate campaign report on all terminal states (always-on)
|
|
1243
|
+
generate_campaign_report
|
|
1244
|
+
|
|
1005
1245
|
# Print summary
|
|
1006
1246
|
local end_time
|
|
1007
1247
|
end_time=$(date +%s)
|
|
@@ -1018,13 +1258,13 @@ cleanup() {
|
|
|
1018
1258
|
local end_ts=$(date +%s)
|
|
1019
1259
|
local elapsed=$((end_ts - START_TIME))
|
|
1020
1260
|
|
|
1021
|
-
log_debug "[
|
|
1261
|
+
log_debug "[FLOW] final status=$final_status iterations=$ITERATION elapsed=${elapsed}s"
|
|
1022
1262
|
|
|
1023
1263
|
# --- Validation ---
|
|
1024
|
-
log_debug "[
|
|
1264
|
+
log_debug "[FLOW] === Execution Validation ==="
|
|
1025
1265
|
|
|
1026
1266
|
# 1. Did the correct verify mode run?
|
|
1027
|
-
log_debug "[
|
|
1267
|
+
log_debug "[FLOW] verify_mode=$VERIFY_MODE configured=true"
|
|
1028
1268
|
|
|
1029
1269
|
# 2. Per-US: were all US individually verified?
|
|
1030
1270
|
if [[ "$VERIFY_MODE" = "per-us" ]]; then
|
|
@@ -1038,39 +1278,39 @@ cleanup() {
|
|
|
1038
1278
|
|
|
1039
1279
|
if [[ "$final_status" = "COMPLETE" ]]; then
|
|
1040
1280
|
if (( verified_count >= expected_count )); then
|
|
1041
|
-
log_debug "[
|
|
1281
|
+
log_debug "[FLOW] per_us_coverage=PASS verified=$verified_count/$expected_count us=$VERIFIED_US"
|
|
1042
1282
|
else
|
|
1043
|
-
log_debug "[
|
|
1283
|
+
log_debug "[FLOW] per_us_coverage=FAIL verified=$verified_count/$expected_count expected=$expected_us got=$VERIFIED_US"
|
|
1044
1284
|
fi
|
|
1045
1285
|
else
|
|
1046
|
-
log_debug "[
|
|
1286
|
+
log_debug "[FLOW] per_us_coverage=INCOMPLETE verified=$verified_count/$expected_count status=$final_status"
|
|
1047
1287
|
fi
|
|
1048
1288
|
fi
|
|
1049
1289
|
|
|
1050
1290
|
# 3. Consensus: were both engines used?
|
|
1051
1291
|
if [[ "$VERIFY_CONSENSUS" = "1" ]]; then
|
|
1052
1292
|
if [[ -n "${CLAUDE_VERDICT:-}" && -n "${CODEX_VERDICT:-}" ]]; then
|
|
1053
|
-
log_debug "[
|
|
1293
|
+
log_debug "[FLOW] consensus=USED claude=$CLAUDE_VERDICT codex=$CODEX_VERDICT rounds=$CONSENSUS_ROUND"
|
|
1054
1294
|
else
|
|
1055
|
-
log_debug "[
|
|
1295
|
+
log_debug "[FLOW] consensus=NOT_TRIGGERED claude=${CLAUDE_VERDICT:-none} codex=${CODEX_VERDICT:-none}"
|
|
1056
1296
|
fi
|
|
1057
1297
|
fi
|
|
1058
1298
|
|
|
1059
1299
|
# 4. Engine match: did the configured engines actually run?
|
|
1060
|
-
local worker_dispatches=$(grep -c '\[
|
|
1061
|
-
local verifier_dispatches=$(grep -c '\[
|
|
1062
|
-
log_debug "[
|
|
1300
|
+
local worker_dispatches=$(grep -c '\[FLOW\].*phase=worker.*dispatched=true' "$DEBUG_LOG" 2>/dev/null || echo 0)
|
|
1301
|
+
local verifier_dispatches=$(grep -c '\[FLOW\].*phase=verifier.*dispatched=true' "$DEBUG_LOG" 2>/dev/null || echo 0)
|
|
1302
|
+
log_debug "[FLOW] dispatches worker=$worker_dispatches verifier=$verifier_dispatches"
|
|
1063
1303
|
|
|
1064
1304
|
# 5. Fix loops: how many fix contracts were generated?
|
|
1065
|
-
local fix_count=$(grep -c '\[
|
|
1066
|
-
log_debug "[
|
|
1305
|
+
local fix_count=$(grep -c '\[DECIDE\].*phase=fix_loop' "$DEBUG_LOG" 2>/dev/null || echo 0)
|
|
1306
|
+
log_debug "[FLOW] fix_loops=$fix_count consecutive_failures=$CONSECUTIVE_FAILURES"
|
|
1067
1307
|
|
|
1068
1308
|
# 6. Circuit breakers: any triggered?
|
|
1069
|
-
local cb_count=$(grep -c '\[
|
|
1070
|
-
log_debug "[
|
|
1309
|
+
local cb_count=$(grep -c '\[GOV\].*circuit_breaker=' "$DEBUG_LOG" 2>/dev/null || echo 0)
|
|
1310
|
+
log_debug "[FLOW] circuit_breakers_triggered=$cb_count"
|
|
1071
1311
|
|
|
1072
1312
|
# 7. Overall result
|
|
1073
|
-
log_debug "[
|
|
1313
|
+
log_debug "[FLOW] result=$final_status iterations=$ITERATION elapsed=${elapsed}s verified_us=$VERIFIED_US"
|
|
1074
1314
|
fi
|
|
1075
1315
|
|
|
1076
1316
|
echo ""
|
|
@@ -1163,7 +1403,7 @@ poll_for_signal() {
|
|
|
1163
1403
|
(( HEARTBEAT_STALE_COUNT++ ))
|
|
1164
1404
|
# Circuit breaker: 3 consecutive heartbeat stale events
|
|
1165
1405
|
if (( HEARTBEAT_STALE_COUNT >= 3 )); then
|
|
1166
|
-
log_debug "[
|
|
1406
|
+
log_debug "[GOV] iter=$ITERATION circuit_breaker=heartbeat_stale detail=\"3 consecutive heartbeat stale events\""
|
|
1167
1407
|
log_error "Circuit breaker: 3 consecutive heartbeat stale events"
|
|
1168
1408
|
return 1
|
|
1169
1409
|
fi
|
|
@@ -1186,7 +1426,7 @@ poll_for_signal() {
|
|
|
1186
1426
|
poll_capture=$(tmux capture-pane -t "$pane_id" -p 2>/dev/null)
|
|
1187
1427
|
if echo "$poll_capture" | grep -q "Do you want to" 2>/dev/null; then
|
|
1188
1428
|
log " Permission prompt detected during poll, auto-approving..."
|
|
1189
|
-
log_debug "[
|
|
1429
|
+
log_debug "[FLOW] iter=$ITERATION permission_prompt_auto_approved=true"
|
|
1190
1430
|
tmux send-keys -t "$pane_id" Enter
|
|
1191
1431
|
sleep 0.5
|
|
1192
1432
|
fi
|
|
@@ -1362,9 +1602,9 @@ run_consensus_verification() {
|
|
|
1362
1602
|
CLAUDE_VERDICT=""
|
|
1363
1603
|
CODEX_VERDICT=""
|
|
1364
1604
|
|
|
1365
|
-
while (( CONSENSUS_ROUND <
|
|
1605
|
+
while (( CONSENSUS_ROUND < 6 )); do
|
|
1366
1606
|
(( CONSENSUS_ROUND++ ))
|
|
1367
|
-
log " Consensus round $CONSENSUS_ROUND/
|
|
1607
|
+
log " Consensus round $CONSENSUS_ROUND/6..."
|
|
1368
1608
|
|
|
1369
1609
|
# Run claude verifier first
|
|
1370
1610
|
if ! run_single_verifier "$iter" "claude" "$VERIFIER_MODEL" "-claude" "$claude_verdict_file"; then
|
|
@@ -1372,7 +1612,7 @@ run_consensus_verification() {
|
|
|
1372
1612
|
return 1
|
|
1373
1613
|
fi
|
|
1374
1614
|
CLAUDE_VERDICT=$(jq -r '.verdict' "$claude_verdict_file" 2>/dev/null)
|
|
1375
|
-
log_debug "[
|
|
1615
|
+
log_debug "[GOV] iter=$iter phase=consensus_claude verdict=$CLAUDE_VERDICT model=$VERIFIER_MODEL"
|
|
1376
1616
|
|
|
1377
1617
|
# Run codex verifier second
|
|
1378
1618
|
if ! run_single_verifier "$iter" "codex" "$VERIFIER_CODEX_MODEL" "-codex" "$codex_verdict_file"; then
|
|
@@ -1380,14 +1620,14 @@ run_consensus_verification() {
|
|
|
1380
1620
|
return 1
|
|
1381
1621
|
fi
|
|
1382
1622
|
CODEX_VERDICT=$(jq -r '.verdict' "$codex_verdict_file" 2>/dev/null)
|
|
1383
|
-
log_debug "[
|
|
1623
|
+
log_debug "[GOV] iter=$iter phase=consensus_codex verdict=$CODEX_VERDICT model=$VERIFIER_CODEX_MODEL reasoning=$VERIFIER_CODEX_REASONING"
|
|
1384
1624
|
|
|
1385
1625
|
log " Consensus: claude=$CLAUDE_VERDICT codex=$CODEX_VERDICT"
|
|
1386
1626
|
local _combined_action="retry"
|
|
1387
1627
|
if [[ "$CLAUDE_VERDICT" = "pass" && "$CODEX_VERDICT" = "pass" ]]; then _combined_action="pass"
|
|
1388
|
-
elif (( CONSENSUS_ROUND >=
|
|
1628
|
+
elif (( CONSENSUS_ROUND >= 6 )); then _combined_action="blocked"
|
|
1389
1629
|
fi
|
|
1390
|
-
log_debug "[
|
|
1630
|
+
log_debug "[GOV] iter=$iter phase=consensus round=$CONSENSUS_ROUND claude=$CLAUDE_VERDICT codex=$CODEX_VERDICT combined_action=$_combined_action"
|
|
1391
1631
|
|
|
1392
1632
|
# Both pass → success
|
|
1393
1633
|
if [[ "$CLAUDE_VERDICT" = "pass" && "$CODEX_VERDICT" = "pass" ]]; then
|
|
@@ -1409,7 +1649,7 @@ run_consensus_verification() {
|
|
|
1409
1649
|
fi
|
|
1410
1650
|
|
|
1411
1651
|
# Consensus disagreement
|
|
1412
|
-
log_debug "[
|
|
1652
|
+
log_debug "[GOV] iter=$iter phase=consensus_disagreement round=$CONSENSUS_ROUND claude=$CLAUDE_VERDICT codex=$CODEX_VERDICT action=fix_contract"
|
|
1413
1653
|
|
|
1414
1654
|
# NOTE: pre_existing_failure heuristic was removed (v0.3.5).
|
|
1415
1655
|
# It used unreliable grep-in-description string matching to classify
|
|
@@ -1442,14 +1682,19 @@ run_consensus_verification() {
|
|
|
1442
1682
|
|
|
1443
1683
|
# If this is not the last round, the caller will dispatch the Worker with the fix contract
|
|
1444
1684
|
# For now, write a fail verdict so the main loop can handle the fix loop
|
|
1445
|
-
if (( CONSENSUS_ROUND <
|
|
1446
|
-
# Create a merged fail verdict for the main loop
|
|
1685
|
+
if (( CONSENSUS_ROUND < 6 )); then
|
|
1686
|
+
# Create a merged fail verdict for the main loop — include issues from BOTH verdicts
|
|
1687
|
+
local merged_issues="[]"
|
|
1688
|
+
local claude_issues codex_issues
|
|
1689
|
+
claude_issues=$(jq -c '[.issues[]? | . + {"source": "claude"}]' "$claude_verdict_file" 2>/dev/null || echo '[]')
|
|
1690
|
+
codex_issues=$(jq -c '[.issues[]? | . + {"source": "codex"}]' "$codex_verdict_file" 2>/dev/null || echo '[]')
|
|
1691
|
+
merged_issues=$(echo "$claude_issues $codex_issues" | jq -s 'add // []')
|
|
1447
1692
|
{
|
|
1448
1693
|
echo '{'
|
|
1449
1694
|
echo ' "verdict": "fail",'
|
|
1450
1695
|
echo ' "verified_at_utc": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",'
|
|
1451
|
-
echo ' "summary": "Consensus disagreement (round '"$CONSENSUS_ROUND"'/
|
|
1452
|
-
echo ' "issues":
|
|
1696
|
+
echo ' "summary": "Consensus disagreement (round '"$CONSENSUS_ROUND"'/6): claude='"$CLAUDE_VERDICT"' codex='"$CODEX_VERDICT"'",'
|
|
1697
|
+
echo ' "issues": '"$merged_issues"','
|
|
1453
1698
|
echo ' "recommended_state_transition": "continue",'
|
|
1454
1699
|
echo ' "consensus": { "claude": "'"$CLAUDE_VERDICT"'", "codex": "'"$CODEX_VERDICT"'", "round": '"$CONSENSUS_ROUND"' }'
|
|
1455
1700
|
echo '}'
|
|
@@ -1458,16 +1703,20 @@ run_consensus_verification() {
|
|
|
1458
1703
|
fi
|
|
1459
1704
|
done
|
|
1460
1705
|
|
|
1461
|
-
# Max consensus rounds exceeded
|
|
1462
|
-
log_error "Consensus failed after
|
|
1706
|
+
# Max consensus rounds exceeded — include issues from both verdicts
|
|
1707
|
+
log_error "Consensus failed after 6 rounds"
|
|
1708
|
+
local final_claude_issues final_codex_issues final_merged_issues
|
|
1709
|
+
final_claude_issues=$(jq -c '[.issues[]? | . + {"source": "claude"}]' "$claude_verdict_file" 2>/dev/null || echo '[]')
|
|
1710
|
+
final_codex_issues=$(jq -c '[.issues[]? | . + {"source": "codex"}]' "$codex_verdict_file" 2>/dev/null || echo '[]')
|
|
1711
|
+
final_merged_issues=$(echo "$final_claude_issues $final_codex_issues" | jq -s 'add // []')
|
|
1463
1712
|
{
|
|
1464
1713
|
echo '{'
|
|
1465
1714
|
echo ' "verdict": "fail",'
|
|
1466
1715
|
echo ' "verified_at_utc": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",'
|
|
1467
|
-
echo ' "summary": "Consensus failed after
|
|
1468
|
-
echo ' "issues":
|
|
1716
|
+
echo ' "summary": "Consensus failed after 6 rounds: claude='"$CLAUDE_VERDICT"' codex='"$CODEX_VERDICT"'",'
|
|
1717
|
+
echo ' "issues": '"$final_merged_issues"','
|
|
1469
1718
|
echo ' "recommended_state_transition": "blocked",'
|
|
1470
|
-
echo ' "consensus": { "claude": "'"$CLAUDE_VERDICT"'", "codex": "'"$CODEX_VERDICT"'", "round":
|
|
1719
|
+
echo ' "consensus": { "claude": "'"$CLAUDE_VERDICT"'", "codex": "'"$CODEX_VERDICT"'", "round": 6 }'
|
|
1471
1720
|
echo '}'
|
|
1472
1721
|
} | atomic_write "$VERDICT_FILE"
|
|
1473
1722
|
return 1
|
|
@@ -1509,6 +1758,15 @@ main() {
|
|
|
1509
1758
|
fi
|
|
1510
1759
|
mkdir -p "$LOGS_DIR" 2>/dev/null
|
|
1511
1760
|
|
|
1761
|
+
# --- AC7: debug.log versioning: rename existing debug.log before fresh run ---
|
|
1762
|
+
if (( DEBUG )) && [[ -f "$DEBUG_LOG" ]]; then
|
|
1763
|
+
local dbg_n=1
|
|
1764
|
+
while [[ -f "${DEBUG_LOG%.log}-v${dbg_n}.log" ]]; do
|
|
1765
|
+
(( dbg_n++ ))
|
|
1766
|
+
done
|
|
1767
|
+
mv "$DEBUG_LOG" "${DEBUG_LOG%.log}-v${dbg_n}.log"
|
|
1768
|
+
fi
|
|
1769
|
+
|
|
1512
1770
|
# --- Startup ---
|
|
1513
1771
|
log "Ralph Desk Tmux Runner starting..."
|
|
1514
1772
|
log " Slug: $SLUG"
|
|
@@ -1531,10 +1789,11 @@ main() {
|
|
|
1531
1789
|
fi
|
|
1532
1790
|
local us_count=$(echo "$us_list" | tr ',' '\n' | grep -c 'US-')
|
|
1533
1791
|
|
|
1534
|
-
log_debug "[
|
|
1535
|
-
log_debug "[
|
|
1536
|
-
log_debug "[
|
|
1537
|
-
log_debug "[
|
|
1792
|
+
log_debug "[OPTION] slug=$SLUG us_count=$us_count us_list=$us_list"
|
|
1793
|
+
log_debug "[OPTION] worker_engine=$WORKER_ENGINE worker_model=$WORKER_MODEL"
|
|
1794
|
+
log_debug "[OPTION] verifier_engine=$VERIFIER_ENGINE verifier_model=$VERIFIER_MODEL"
|
|
1795
|
+
log_debug "[OPTION] verify_mode=$VERIFY_MODE consensus=$VERIFY_CONSENSUS consensus_scope=$CONSENSUS_SCOPE max_iter=$MAX_ITER"
|
|
1796
|
+
log_debug "[OPTION] cb_threshold=$CB_THRESHOLD effective_cb_threshold=$EFFECTIVE_CB_THRESHOLD iter_timeout=$ITER_TIMEOUT with_self_verification=$WITH_SELF_VERIFICATION debug=$DEBUG"
|
|
1538
1797
|
|
|
1539
1798
|
if [[ "$VERIFY_MODE" = "per-us" ]]; then
|
|
1540
1799
|
# Build expected flow
|
|
@@ -1543,13 +1802,13 @@ main() {
|
|
|
1543
1802
|
expected_flow="${expected_flow}worker->verify($us)->"
|
|
1544
1803
|
done
|
|
1545
1804
|
expected_flow="${expected_flow}verify(ALL)->COMPLETE"
|
|
1546
|
-
log_debug "[
|
|
1805
|
+
log_debug "[OPTION] expected_flow=$expected_flow"
|
|
1547
1806
|
else
|
|
1548
|
-
log_debug "[
|
|
1807
|
+
log_debug "[OPTION] expected_flow=worker(all)->verify(ALL)->COMPLETE"
|
|
1549
1808
|
fi
|
|
1550
1809
|
|
|
1551
1810
|
if [[ "$VERIFY_CONSENSUS" = "1" ]]; then
|
|
1552
|
-
log_debug "[
|
|
1811
|
+
log_debug "[OPTION] consensus_flow=each_verify_runs_claude+codex_both_must_pass"
|
|
1553
1812
|
fi
|
|
1554
1813
|
fi
|
|
1555
1814
|
|
|
@@ -1559,6 +1818,18 @@ main() {
|
|
|
1559
1818
|
if [[ -f "$prd_file" ]]; then
|
|
1560
1819
|
US_LIST=$(grep -oE 'US-[0-9]+' "$prd_file" | sort -u | tr '\n' ',' | sed 's/,$//')
|
|
1561
1820
|
fi
|
|
1821
|
+
|
|
1822
|
+
# Initialize VERIFIED_US from memory's Completed Stories (carry over previous runs)
|
|
1823
|
+
local memory_file="$DESK/memos/${SLUG}-memory.md"
|
|
1824
|
+
if [[ -f "$memory_file" ]]; then
|
|
1825
|
+
local completed_us
|
|
1826
|
+
completed_us=$(sed -n '/^## Completed Stories$/,/^## /p' "$memory_file" 2>/dev/null | grep '^- US-' | sed 's/^- \(US-[0-9]*\):.*/\1/' | sort -u | tr '\n' ',' | sed 's/,$//')
|
|
1827
|
+
if [[ -n "$completed_us" ]]; then
|
|
1828
|
+
VERIFIED_US="$completed_us"
|
|
1829
|
+
log " Loaded completed stories from memory: $VERIFIED_US"
|
|
1830
|
+
log_debug "[FLOW] loaded_verified_us_from_memory=$VERIFIED_US"
|
|
1831
|
+
fi
|
|
1832
|
+
fi
|
|
1562
1833
|
fi
|
|
1563
1834
|
|
|
1564
1835
|
# Dependency checks
|
|
@@ -1592,7 +1863,7 @@ main() {
|
|
|
1592
1863
|
ITER_START_TIME=$(date +%s)
|
|
1593
1864
|
local _iter_contract=""
|
|
1594
1865
|
_iter_contract=$(sed -n '/^## Next Iteration Contract$/,/^## /{ /^## Next/d; /^## [^N]/d; p; }' "$MEMORY_FILE" 2>/dev/null | head -1 | tr '\n' ' ')
|
|
1595
|
-
log_debug "[
|
|
1866
|
+
log_debug "[FLOW] iter=$ITERATION start contract=\"${_iter_contract:-none}\""
|
|
1596
1867
|
|
|
1597
1868
|
# --- governance.md s7 step 1: Check sentinels ---
|
|
1598
1869
|
if [[ -f "$COMPLETE_SENTINEL" ]]; then
|
|
@@ -1644,7 +1915,7 @@ main() {
|
|
|
1644
1915
|
fi
|
|
1645
1916
|
tmux send-keys -t "$WORKER_PANE" -l -- "$worker_launch"
|
|
1646
1917
|
tmux send-keys -t "$WORKER_PANE" Enter
|
|
1647
|
-
log_debug "[
|
|
1918
|
+
log_debug "[FLOW] iter=$ITERATION phase=worker engine=$WORKER_ENGINE model=$WORKER_MODEL dispatched=true"
|
|
1648
1919
|
|
|
1649
1920
|
# Step 5b: Wait for claude TUI to be ready (tmux pattern)
|
|
1650
1921
|
if ! wait_for_pane_ready "$WORKER_PANE" 30; then
|
|
@@ -1669,7 +1940,7 @@ main() {
|
|
|
1669
1940
|
pane_check=$(tmux capture-pane -t "$WORKER_PANE" -p 2>/dev/null)
|
|
1670
1941
|
if echo "$pane_check" | grep -qi "esc to interrupt\|thinking\|working\|kneading\|crunching\|clauding\|billowing\|brewing\|tinkering\|burrowing\|saut\|Exploring\|Running\|exec\|Explored" 2>/dev/null; then
|
|
1671
1942
|
log_debug "Worker started working after $((submit_attempts + 1)) submit checks"
|
|
1672
|
-
log_debug "[
|
|
1943
|
+
log_debug "[FLOW] iter=$ITERATION worker_submit_check=OK attempts=$((submit_attempts + 1))"
|
|
1673
1944
|
break
|
|
1674
1945
|
fi
|
|
1675
1946
|
# After 8 failed attempts, try C-u clear + re-type (omc-teams adaptive retry)
|
|
@@ -1687,7 +1958,7 @@ main() {
|
|
|
1687
1958
|
done
|
|
1688
1959
|
if (( submit_attempts >= 15 )); then
|
|
1689
1960
|
log " WARNING: Could not confirm Worker started working after 15 attempts"
|
|
1690
|
-
log_debug "[
|
|
1961
|
+
log_debug "[FLOW] iter=$ITERATION worker_submit_check=FAILED attempts=15"
|
|
1691
1962
|
fi
|
|
1692
1963
|
|
|
1693
1964
|
# --- governance.md s7 step 5+6: Poll for Worker completion ---
|
|
@@ -1696,7 +1967,7 @@ main() {
|
|
|
1696
1967
|
while (( ! worker_poll_done )); do
|
|
1697
1968
|
if poll_for_signal "$SIGNAL_FILE" "$WORKER_HEARTBEAT" "$WORKER_PANE" "$worker_launch" "Worker"; then
|
|
1698
1969
|
worker_poll_done=1
|
|
1699
|
-
log_debug "[
|
|
1970
|
+
log_debug "[FLOW] iter=$ITERATION poll_signal_received=true"
|
|
1700
1971
|
else
|
|
1701
1972
|
# Check if Worker is still actively running (not stuck)
|
|
1702
1973
|
local worker_cmd
|
|
@@ -1706,41 +1977,41 @@ main() {
|
|
|
1706
1977
|
local iter_elapsed=$(( $(date +%s) - ITER_START_TIME ))
|
|
1707
1978
|
if (( iter_elapsed >= HARD_CEILING )); then
|
|
1708
1979
|
log_error "Worker hit hard ceiling (${HARD_CEILING}s = 3x iter_timeout). Killing iteration."
|
|
1709
|
-
log_debug "[
|
|
1980
|
+
log_debug "[GOV] iter=$ITERATION hard_ceiling_hit=true elapsed=${iter_elapsed}s ceiling=${HARD_CEILING}s process=$worker_cmd"
|
|
1710
1981
|
tmux send-keys -t "$WORKER_PANE" C-c 2>/dev/null
|
|
1711
1982
|
sleep 1
|
|
1712
|
-
|
|
1713
|
-
update_status "
|
|
1714
|
-
|
|
1715
|
-
break
|
|
1983
|
+
write_blocked_sentinel "Worker hit hard ceiling (${HARD_CEILING}s). Pane preserved for inspection."
|
|
1984
|
+
update_status "blocked" "hard_timeout"
|
|
1985
|
+
return 1
|
|
1716
1986
|
fi
|
|
1717
1987
|
log " Worker timed out but still active ($worker_cmd). Extending poll... (${iter_elapsed}s/${HARD_CEILING}s)"
|
|
1718
|
-
log_debug "[
|
|
1719
|
-
log_debug "[
|
|
1988
|
+
log_debug "[GOV] iter=$ITERATION timeout_active=true process=$worker_cmd elapsed=${iter_elapsed}s ceiling=${HARD_CEILING}s"
|
|
1989
|
+
log_debug "[FLOW] iter=$ITERATION poll_extended=true worker_cmd=$worker_cmd"
|
|
1720
1990
|
update_status "worker" "slow"
|
|
1721
1991
|
# Loop continues — re-poll same iteration
|
|
1722
1992
|
else
|
|
1723
1993
|
# Worker is truly dead/stuck
|
|
1724
1994
|
(( MONITOR_FAILURE_COUNT++ ))
|
|
1725
|
-
log_debug "[
|
|
1995
|
+
log_debug "[GOV] iter=$ITERATION monitor_failure=$MONITOR_FAILURE_COUNT/3"
|
|
1726
1996
|
if (( MONITOR_FAILURE_COUNT >= 3 )); then
|
|
1727
|
-
log_debug "[
|
|
1997
|
+
log_debug "[GOV] iter=$ITERATION circuit_breaker=monitor_failures detail=\"3 consecutive monitor failures\""
|
|
1728
1998
|
write_blocked_sentinel "3 consecutive monitor failures (worker not active)"
|
|
1729
1999
|
update_status "blocked" "monitor_failures"
|
|
1730
2000
|
return 1
|
|
1731
2001
|
fi
|
|
1732
2002
|
log " WARNING: Worker poll failed (monitor failure $MONITOR_FAILURE_COUNT/3)"
|
|
1733
2003
|
update_status "worker" "poll_failed"
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
2004
|
+
log_debug "[FLOW] iter=$ITERATION poll_worker_dead=true worker_cmd=$worker_cmd"
|
|
2005
|
+
# Worker is truly dead/stuck — BLOCK and let user decide
|
|
2006
|
+
write_blocked_sentinel "Worker process dead/stuck (poll failed). Pane preserved for inspection."
|
|
2007
|
+
update_status "blocked" "worker_dead"
|
|
2008
|
+
return 1
|
|
1738
2009
|
fi
|
|
1739
2010
|
fi
|
|
1740
2011
|
done
|
|
1741
2012
|
|
|
1742
2013
|
if [[ ! -f "$SIGNAL_FILE" ]]; then
|
|
1743
|
-
log_debug "[
|
|
2014
|
+
log_debug "[FLOW] iter=$ITERATION no_signal_after_poll=true continuing"
|
|
1744
2015
|
# No signal — monitor failure, go to next iteration
|
|
1745
2016
|
continue
|
|
1746
2017
|
fi
|
|
@@ -1759,7 +2030,7 @@ main() {
|
|
|
1759
2030
|
# Read us_id early for EXEC logging (also used later in verify branch)
|
|
1760
2031
|
local signal_us_id_early=""
|
|
1761
2032
|
signal_us_id_early=$(jq -r '.us_id // empty' "$SIGNAL_FILE" 2>/dev/null)
|
|
1762
|
-
log_debug "[
|
|
2033
|
+
log_debug "[FLOW] iter=$ITERATION phase=worker_signal status=$signal_status us_id=${signal_us_id_early:-none} summary=\"$signal_summary\""
|
|
1763
2034
|
|
|
1764
2035
|
case "$signal_status" in
|
|
1765
2036
|
continue)
|
|
@@ -1827,7 +2098,7 @@ main() {
|
|
|
1827
2098
|
fi
|
|
1828
2099
|
tmux send-keys -t "$VERIFIER_PANE" -l -- "$verifier_launch"
|
|
1829
2100
|
tmux send-keys -t "$VERIFIER_PANE" Enter
|
|
1830
|
-
log_debug "[
|
|
2101
|
+
log_debug "[FLOW] iter=$ITERATION phase=verifier engine=$VERIFIER_ENGINE model=$VERIFIER_MODEL scope=${signal_us_id:-all} dispatched=true"
|
|
1831
2102
|
|
|
1832
2103
|
# Step 7b: Wait for TUI to be ready
|
|
1833
2104
|
if ! wait_for_pane_ready "$VERIFIER_PANE" 30; then
|
|
@@ -1871,10 +2142,10 @@ main() {
|
|
|
1871
2142
|
log " Polling for verify-verdict.json..."
|
|
1872
2143
|
if ! poll_for_signal "$VERDICT_FILE" "$VERIFIER_HEARTBEAT" "$VERIFIER_PANE" "$verifier_launch" "Verifier"; then
|
|
1873
2144
|
log_error "Verifier poll failed"
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
2145
|
+
# Verifier is dead/stuck — BLOCK and let user decide
|
|
2146
|
+
write_blocked_sentinel "Verifier process dead/stuck (poll failed). Pane preserved for inspection."
|
|
2147
|
+
update_status "blocked" "verifier_dead"
|
|
2148
|
+
return 1
|
|
1878
2149
|
fi
|
|
1879
2150
|
fi
|
|
1880
2151
|
|
|
@@ -1889,7 +2160,7 @@ main() {
|
|
|
1889
2160
|
log " Verifier: verdict=$verdict recommended=$recommended"
|
|
1890
2161
|
log " Verifier summary: \"$verdict_summary\""
|
|
1891
2162
|
local _issues_count=$(jq '.issues | length' "$VERDICT_FILE" 2>/dev/null || echo 0)
|
|
1892
|
-
log_debug "[
|
|
2163
|
+
log_debug "[GOV] iter=$ITERATION phase=verdict engine=$VERIFIER_ENGINE verdict=$verdict recommended=$recommended us_id=${signal_us_id:-all} issues=$_issues_count"
|
|
1893
2164
|
|
|
1894
2165
|
case "$verdict" in
|
|
1895
2166
|
pass)
|
|
@@ -1905,7 +2176,7 @@ main() {
|
|
|
1905
2176
|
VERIFIED_US="$signal_us_id"
|
|
1906
2177
|
fi
|
|
1907
2178
|
log " US $signal_us_id verified. Verified so far: $VERIFIED_US"
|
|
1908
|
-
log_debug "[
|
|
2179
|
+
log_debug "[FLOW] iter=$ITERATION verified_us_update=$signal_us_id verified_us_total=$VERIFIED_US"
|
|
1909
2180
|
update_status "verifier" "pass_us"
|
|
1910
2181
|
# Worker will do next US on next iteration
|
|
1911
2182
|
elif [[ "$recommended" == "complete" || "$signal_us_id" == "ALL" ]]; then
|
|
@@ -1940,13 +2211,13 @@ main() {
|
|
|
1940
2211
|
jq -r '.next_iteration_contract // "Fix the issues listed above."' "$VERDICT_FILE" 2>/dev/null
|
|
1941
2212
|
} | atomic_write "$fix_contract"
|
|
1942
2213
|
log " Fix contract: $fix_contract"
|
|
1943
|
-
log_debug "[
|
|
2214
|
+
log_debug "[DECIDE] iter=$ITERATION phase=fix_loop trigger=$verdict consecutive_failures=$CONSECUTIVE_FAILURES fix_contract=$fix_contract"
|
|
1944
2215
|
|
|
1945
2216
|
# Circuit breaker: consecutive failures
|
|
1946
|
-
if (( CONSECUTIVE_FAILURES >=
|
|
1947
|
-
log_debug "[
|
|
1948
|
-
log_error "Circuit breaker:
|
|
1949
|
-
write_blocked_sentinel "
|
|
2217
|
+
if (( CONSECUTIVE_FAILURES >= EFFECTIVE_CB_THRESHOLD )); then
|
|
2218
|
+
log_debug "[GOV] iter=$ITERATION circuit_breaker=consecutive_failures detail=\"${EFFECTIVE_CB_THRESHOLD} consecutive verification failures\""
|
|
2219
|
+
log_error "Circuit breaker: ${EFFECTIVE_CB_THRESHOLD} consecutive verification failures"
|
|
2220
|
+
write_blocked_sentinel "${EFFECTIVE_CB_THRESHOLD} consecutive verification failures"
|
|
1950
2221
|
update_status "blocked" "consecutive_failures"
|
|
1951
2222
|
return 1
|
|
1952
2223
|
fi
|
|
@@ -1985,12 +2256,18 @@ main() {
|
|
|
1985
2256
|
;;
|
|
1986
2257
|
esac
|
|
1987
2258
|
|
|
2259
|
+
# --- step 7d: Archive iteration artifacts before cleanup ---
|
|
2260
|
+
archive_iter_artifacts "$ITERATION"
|
|
2261
|
+
|
|
2262
|
+
# --- AC5: Write per-iteration cost estimate ---
|
|
2263
|
+
write_cost_log "$ITERATION"
|
|
2264
|
+
|
|
1988
2265
|
# --- governance.md s7 step 8: Write result log ---
|
|
1989
2266
|
write_result_log "$ITERATION" "$signal_status"
|
|
1990
2267
|
|
|
1991
2268
|
# --- governance.md s7 step 8: Circuit breaker - stale context check ---
|
|
1992
2269
|
if ! check_stale_context; then
|
|
1993
|
-
log_debug "[
|
|
2270
|
+
log_debug "[GOV] iter=$ITERATION circuit_breaker=stale_context detail=\"context unchanged for 3 consecutive iterations\""
|
|
1994
2271
|
write_blocked_sentinel "Context unchanged for 3 consecutive iterations (stale)"
|
|
1995
2272
|
update_status "blocked" "stale_context"
|
|
1996
2273
|
return 1
|
|
@@ -2002,6 +2279,7 @@ main() {
|
|
|
2002
2279
|
|
|
2003
2280
|
# Max iterations reached
|
|
2004
2281
|
log "Max iterations ($MAX_ITER) reached."
|
|
2282
|
+
generate_campaign_report # AC4: TIMEOUT terminal path
|
|
2005
2283
|
update_status "timeout" "max_iter"
|
|
2006
2284
|
return 1
|
|
2007
2285
|
}
|