build(agents): add stale supervisor cleanup at startup to prevent duplicates
When the product-builder is interrupted mid-run (Ctrl+C, crash, timeout), supervisor sessions launched via prompt_async survive independently on the OpenCode server. Restarting the product-builder without cleanup creates duplicate supervisors — 22 agents competing for the same work, causing duplicate PR reviews, conflicting git pushes, and wasted resources. Added Phase C.0 (runs before planning or launching) that: 1. Queries GET /session for all server sessions 2. Filters for sessions with titles starting with "[CA-AUTO] supervisor:" 3. Aborts any that are still active via POST /session/:id/abort 4. Deletes the stale sessions via DELETE /session/:id 5. Cleans up the /tmp/ca-supervisor-sessions.env tracking file 6. Logs the cleanup count to the session state issue Also changed the supervisor session title convention from "supervisor: <name>" to "[CA-AUTO] supervisor: <name>" to reliably distinguish product-builder-managed sessions from user-created ones.
This commit is contained in:
@@ -359,6 +359,55 @@ milestones = list of all milestones to complete (ordered)
|
||||
ref_summary = result from ca-ref-reader
|
||||
SERVER = "http://localhost:4096"
|
||||
|
||||
# ── PHASE C.0: Clean Up Stale Supervisor Sessions ───────────────
|
||||
# If the product-builder was previously interrupted (Ctrl+C, crash,
|
||||
# session timeout, etc.), old supervisor sessions launched via
|
||||
# prompt_async may still be running on the server. They are
|
||||
# independent sessions that survive the product-builder's death.
|
||||
#
|
||||
# We MUST find and abort them before launching fresh supervisors,
|
||||
# otherwise there will be duplicate supervisors competing for the
|
||||
# same work — duplicate PR reviews, conflicting git pushes, etc.
|
||||
#
|
||||
# Identification: All supervisor sessions have titles starting with
|
||||
# "[CA-AUTO] supervisor:". This prefix is unique to product-builder-
|
||||
# managed supervisors and will not match user-created sessions.
|
||||
|
||||
# Step 1: Query all sessions from the server
|
||||
ALL_SESSIONS=$(bash("curl -s ${SERVER}/session", timeout=30000))
|
||||
|
||||
# Step 2: Extract sessions whose title starts with "[CA-AUTO] supervisor:"
|
||||
# Use python3 to parse JSON and filter:
|
||||
STALE_IDS=$(bash("echo '${ALL_SESSIONS}' | python3 -c \"
|
||||
import sys, json
|
||||
sessions = json.loads(sys.stdin.read())
|
||||
for s in sessions:
|
||||
if s.get('title', '').startswith('[CA-AUTO] supervisor:'):
|
||||
print(s['id'])
|
||||
\"", timeout=30000))
|
||||
|
||||
# Step 3: Abort and delete each stale session
|
||||
stale_count = 0
|
||||
for session_id in STALE_IDS (one per line):
|
||||
# Abort if still running
|
||||
bash("curl -s -X POST ${SERVER}/session/${session_id}/abort", timeout=15000)
|
||||
# Delete the session entirely to avoid clutter
|
||||
bash("curl -s -X DELETE ${SERVER}/session/${session_id}", timeout=15000)
|
||||
stale_count += 1
|
||||
|
||||
# Step 4: Clean up tracking file from previous run
|
||||
bash("rm -f /tmp/ca-supervisor-sessions.env", timeout=5000)
|
||||
|
||||
# Step 5: Log cleanup results
|
||||
if stale_count > 0:
|
||||
invoke ca-session-persister with:
|
||||
checkpoint: "Phase C.0: Cleaned up <stale_count> stale supervisor
|
||||
sessions from a previous interrupted run.
|
||||
All old supervisors aborted and deleted."
|
||||
else:
|
||||
# No stale sessions — first run or clean shutdown previously.
|
||||
pass
|
||||
|
||||
# ── PHASE C.1: Planning (if needed) ─────────────────────────────
|
||||
# If there are milestones with no issues yet, plan them first.
|
||||
# This is the ONLY step that uses the Task tool (one-shot planners).
|
||||
@@ -411,7 +460,7 @@ function launch_supervisor(agent_name, display_name, prompt_text):
|
||||
# Step 1: Create a session
|
||||
SESSION_ID=$(curl -s -X POST "${SERVER}/session" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"title\": \"supervisor: ${display_name}\"}" \
|
||||
-d "{\"title\": \"[CA-AUTO] supervisor: ${display_name}\"}" \
|
||||
| python3 -c "import sys,json; print(json.loads(sys.stdin.read())['id'])")
|
||||
|
||||
# Step 2: Fire-and-forget launch via prompt_async
|
||||
|
||||
Reference in New Issue
Block a user