refactor: harden run_loop.sh verdict parser, add atomic promotion, and revise multi_agent_workflow.md guidelines
This commit is contained in:
@@ -8,6 +8,7 @@ set -euo pipefail
|
||||
# 1. Load Common Framework Library
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
source "$REPO_ROOT/.agents/skills/lib.sh"
|
||||
|
||||
# Default configuration parameters
|
||||
@@ -83,6 +84,12 @@ log_error() {
|
||||
echo -e "\033[1;31m[✗]\033[0m $1"
|
||||
}
|
||||
|
||||
# Verdict must occupy its own line — quoted/diff-embedded tokens ('+', '>' prefixed) never match.
|
||||
has_verdict() {
|
||||
local file="$1" verdict="$2"
|
||||
grep -qE "^\[VERDICT: ${verdict}\][[:space:]]*\r?$" "$file"
|
||||
}
|
||||
|
||||
# Helper: Blocking wait for a delegate job's completion or error state (with safety timeout)
|
||||
wait_for_job() {
|
||||
local job_id="$1"
|
||||
@@ -161,29 +168,29 @@ import sqlite3, os, yaml, json
|
||||
yaml_path = '.mam/agent-sessions.yaml'
|
||||
db_path = '.mam/agent-sessions.db'
|
||||
name = os.environ.get('NAME')
|
||||
agent = 'claude'
|
||||
agent = None
|
||||
if os.path.exists(db_path):
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
row = conn.execute('SELECT data FROM sessions WHERE name=?', (name,)).fetchone()
|
||||
if row:
|
||||
agent = json.loads(row[0]).get('agent', 'claude')
|
||||
agent = json.loads(row[0]).get('agent')
|
||||
conn.close()
|
||||
print(agent)
|
||||
raise SystemExit(0)
|
||||
except Exception:
|
||||
pass
|
||||
if os.path.exists(yaml_path):
|
||||
if not agent and os.path.exists(yaml_path):
|
||||
try:
|
||||
with open(yaml_path) as f:
|
||||
d = yaml.safe_load(f) or {}
|
||||
for s in d.get('tmux_sessions', []):
|
||||
if s.get('name') == name:
|
||||
print(s.get('agent', 'claude'))
|
||||
raise SystemExit(0)
|
||||
agent = s.get('agent') or s.get('pane', {}).get('cmd')
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
print('claude')
|
||||
if not agent:
|
||||
agent = 'agy' if 'agy' in name else 'cline' if 'cline' in name else 'hermes' if 'hermes' in name else 'claude'
|
||||
print(agent)
|
||||
"
|
||||
}
|
||||
|
||||
@@ -248,7 +255,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
log_info "Requesting initial implementation plan from Planner..."
|
||||
PLAN_JOB_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \
|
||||
--agent-session "tmux:$PLANNER_SESSION" \
|
||||
--agent "claude" \
|
||||
--agent "$(resolve_agent_type "$PLANNER_SESSION")" \
|
||||
--type "direct" \
|
||||
--prompt "태스크 목표를 바탕으로 구체적인 구현 계획서를 작성해주세요. 목표: $TASK")
|
||||
|
||||
@@ -266,7 +273,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
fi
|
||||
|
||||
# Retrieve plan text safely
|
||||
PLAN_FILE=$(find ".mam/jobs/$PLAN_JOB_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
PLAN_FILE=$(find ".mam/jobs/$PLAN_JOB_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$PLAN_FILE" ] || [ ! -f "$PLAN_FILE" ]; then
|
||||
log_error "Final plan file not found."
|
||||
exit 1
|
||||
@@ -298,7 +305,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CRITIQUE_FILE=$(find ".mam/jobs/$DEBATE_JOB_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
CRITIQUE_FILE=$(find ".mam/jobs/$DEBATE_JOB_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$CRITIQUE_FILE" ] || [ ! -f "$CRITIQUE_FILE" ]; then
|
||||
log_error "Critique file not found."
|
||||
exit 1
|
||||
@@ -308,7 +315,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
log_info "Planner refining plan with Creator's feedback..."
|
||||
REFINE_JOB_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \
|
||||
--agent-session "tmux:$PLANNER_SESSION" \
|
||||
--agent "claude" \
|
||||
--agent "$(resolve_agent_type "$PLANNER_SESSION")" \
|
||||
--type "direct" \
|
||||
--prompt "작업자(Creator)로부터 다음 이의제기 피드백을 받았습니다. 피드백을 반영하여 계획서를 정교하게 업데이트(Refine)하여 다시 출력해주세요. 피드백:\n$CRITIQUE_TEXT\n기존 계획서:\n$CURRENT_PLAN")
|
||||
|
||||
@@ -325,7 +332,7 @@ if [ "$PLAN_MODE" = true ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REFINE_FILE=$(find ".mam/jobs/$REFINE_JOB_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
REFINE_FILE=$(find ".mam/jobs/$REFINE_JOB_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$REFINE_FILE" ] || [ ! -f "$REFINE_FILE" ]; then
|
||||
log_error "Refinement plan file not found."
|
||||
exit 1
|
||||
@@ -395,7 +402,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
--agent-session "tmux:$TARGET_AGENT" \
|
||||
--agent "$(resolve_agent_type "$TARGET_AGENT")" \
|
||||
--type "direct" \
|
||||
--prompt "작업 완료 상태에 대해 스스로 검증(Self-Review)하여 결함이 없음을 확인하고 종결해주세요. 리포트에 반드시 '[VERDICT: PASS]' 혹은 '[VERDICT: NOT PASS]' 태그를 명시해주세요.")
|
||||
--prompt "작업 완료 상태에 대해 스스로 검증(Self-Review)하여 결함이 없음을 확인하고 종결해주세요. 리뷰 리포트 마지막에 단독 행으로 반드시 '[VERDICT: PASS]' 혹은 '[VERDICT: NOT PASS]' 태그를 명시해주세요.")
|
||||
|
||||
SELF_REV_ID=$(extract_job_id "$SELF_REV_OUTPUT")
|
||||
if [ -z "$SELF_REV_ID" ]; then
|
||||
@@ -405,8 +412,8 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
CREATED_JOBS+=("$SELF_REV_ID")
|
||||
wait_for_job "$SELF_REV_ID"
|
||||
|
||||
REPORT_FILE=$(find ".mam/jobs/$SELF_REV_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -n "$REPORT_FILE" ] && [ -f "$REPORT_FILE" ] && grep -q "\[VERDICT: PASS\]" "$REPORT_FILE" && ! grep -q "\[VERDICT: NOT PASS\]" "$REPORT_FILE"; then
|
||||
REPORT_FILE=$(find ".mam/jobs/$SELF_REV_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -n "$REPORT_FILE" ] && [ -f "$REPORT_FILE" ] && has_verdict "$REPORT_FILE" "PASS" && ! has_verdict "$REPORT_FILE" "NOT PASS"; then
|
||||
log_success "Self-Review PASS."
|
||||
break
|
||||
else
|
||||
@@ -432,7 +439,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
--agent-session "tmux:$rev" \
|
||||
--agent "$(resolve_agent_type "$rev")" \
|
||||
--type "direct" \
|
||||
--prompt "다음 구현 사항(작업 목표: $TASK) 및 누적 변경분(git diff)에 대해 린트, 동작성, 유실 등의 관점에서 교차 코드 리뷰를 수행해주세요. 확인 후 최종 Verdict로 '[VERDICT: PASS]' 혹은 '[VERDICT: NOT PASS]' 태그를 리뷰 리포트에 명시적으로 작성해주세요. 변경분:\n$CHANGES_DIFF")
|
||||
--prompt "다음 구현 사항(작업 목표: $TASK) 및 누적 변경분(git diff)에 대해 린트, 동작성, 유실 등의 관점에서 교차 코드 리뷰를 수행해주세요. 확인 후 최종 Verdict로 '[VERDICT: PASS]' 혹은 '[VERDICT: NOT PASS]' 태그를 리뷰 리포트 마지막에 단독 행으로 명시적으로 작성해주세요. 변경분:\n$CHANGES_DIFF")
|
||||
|
||||
REV_JOB_ID=$(extract_job_id "$REV_OUTPUT")
|
||||
if [ -z "$REV_JOB_ID" ]; then
|
||||
@@ -459,7 +466,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
fi
|
||||
|
||||
# Parse verdict from report file (fails-safe, M-2 anchored checks)
|
||||
REPORT_FILE=$(find ".mam/jobs/$job_id" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
REPORT_FILE=$(find ".mam/jobs/$job_id" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$REPORT_FILE" ] || [ ! -f "$REPORT_FILE" ]; then
|
||||
log_warn "Reviewer '$rev' report not found. Counting as NOT PASS."
|
||||
all_passed=false
|
||||
@@ -469,7 +476,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
REPORT_CONTENT=$(cat "$REPORT_FILE" 2>/dev/null || echo "")
|
||||
|
||||
# Precedence rules: NOT PASS wins over PASS. Absence of verdict tags is treated as NOT PASS (fail-closed)
|
||||
if grep -q "\[VERDICT: NOT PASS\]" "$REPORT_FILE" || ! grep -q "\[VERDICT: PASS\]" "$REPORT_FILE"; then
|
||||
if has_verdict "$REPORT_FILE" "NOT PASS" || ! has_verdict "$REPORT_FILE" "PASS"; then
|
||||
log_warn "Reviewer '$rev': NOT PASS"
|
||||
all_passed=false
|
||||
FEEDBACK_AGGREGATE="$FEEDBACK_AGGREGATE\n--- Reviewer ($rev) Feedback ---\n$REPORT_CONTENT"
|
||||
@@ -498,7 +505,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
|
||||
REFINE_PLAN_OUTPUT=$(bash .agents/skills/multi-agent-mux-delegate-job/multi-agent-mux-delegate-job submit \
|
||||
--agent-session "tmux:$PLANNER_SESSION" \
|
||||
--agent "claude" \
|
||||
--agent "$(resolve_agent_type "$PLANNER_SESSION")" \
|
||||
--type "direct" \
|
||||
--prompt "리뷰어들로부터 다음과 같이 정교한 코드 수정 피드백이 도착했습니다. 해당 피드백을 수렴하여 구현 계획서(Plan)를 갱신(Refine)하여 다시 작성해주세요. 피드백:\n$FEEDBACK_AGGREGATE\n기존 계획서:\n$CURRENT_PLAN")
|
||||
|
||||
@@ -510,7 +517,7 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
CREATED_JOBS+=("$REFINE_PLAN_ID")
|
||||
wait_for_job "$REFINE_PLAN_ID"
|
||||
|
||||
REFINE_PLAN_FILE=$(find ".mam/jobs/$REFINE_PLAN_ID" -name "*.md" 2>/dev/null | head -n 1 || true)
|
||||
REFINE_PLAN_FILE=$(find ".mam/jobs/$REFINE_PLAN_ID" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -z "$REFINE_PLAN_FILE" ] || [ ! -f "$REFINE_PLAN_FILE" ]; then
|
||||
log_error "Refined plan file not found."
|
||||
exit 1
|
||||
@@ -543,6 +550,30 @@ while [ "$loop_count" -le "$MAX_LOOP" ]; do
|
||||
loop_count=$((loop_count + 1))
|
||||
done
|
||||
|
||||
# Promote finalized plan and passed reviewer reports to durable location
|
||||
if [ "$PLAN_MODE" = true ] && [ -n "${CURRENT_PLAN:-}" ] && [ -n "${PLAN_JOB_ID:-}" ]; then
|
||||
plan_dest_dir=".agents/reports/$PLANNER_SESSION"
|
||||
log_info "Promoting final plan to durable location: $plan_dest_dir"
|
||||
mkdir -p "$plan_dest_dir"
|
||||
echo "$CURRENT_PLAN" > "$plan_dest_dir/plan-${PLAN_JOB_ID}.md.tmp"
|
||||
mv -f "$plan_dest_dir/plan-${PLAN_JOB_ID}.md.tmp" "$plan_dest_dir/plan-${PLAN_JOB_ID}.md"
|
||||
fi
|
||||
|
||||
if [ "${#REVIEWERS[@]}" -gt 0 ]; then
|
||||
log_info "Promoting final review reports to durable location..."
|
||||
for idx in "${!JOB_IDS[@]}"; do
|
||||
job_id="${JOB_IDS[$idx]}"
|
||||
rev="${JOB_REVS[$idx]}"
|
||||
report_file=$(find ".mam/jobs/$job_id" -maxdepth 2 -name "report-final.md" 2>/dev/null | head -n 1 || true)
|
||||
if [ -n "$report_file" ] && [ -f "$report_file" ]; then
|
||||
dest_dir=".agents/reports/$rev"
|
||||
mkdir -p "$dest_dir"
|
||||
cp "$report_file" "$dest_dir/report-${job_id}.md.tmp"
|
||||
mv -f "$dest_dir/report-${job_id}.md.tmp" "$dest_dir/report-${job_id}.md"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ===========================================================================
|
||||
# PHASE 4: CLEANUP
|
||||
# ===========================================================================
|
||||
|
||||
Reference in New Issue
Block a user