daily-fix: RunPod fallback rung pod lifecycle: orphans + sto
kind: infra#wf-fix#wf-fix-fp:606acca8ee2c#daily-auto-filed
track:Highlight text on any card (body, plan, or an event) to anchor a comment, or leave a whole-task comment here. Mention @claude to summon a reply.
No comments yet.
Overview / Motivation
Auto-filed by /daily 2026-08-01 (route 2: behavior/logic change → independent review) from consolidated problem sweep entry C1 (miners 3, 8, 1; sessions f98a12ed / 20e82ec2 / 55419495, all #1739).
Goal
Harden the RunPod terminal-rung / fallback launch path so (a) a pod whose workload launch fails does not bill unbounded behind the leave-RUNNING-for-diagnosis contract, (b) the rung never silently RESUMEs an existing deliberately-STOPPED pod-<N> it does not own, and (c) deferrable work can opt out of the paid RunPod fallback per dispatch.
Workflow gap
- Bug observed: Three #1739 incidents in one day: (1) 4 fallback-provisioned
pod-1739RunPod pods whose workloads never launched sat RUNNING (~0.10–0.20). - Why it is a workflow gap: the fallback rung's pod lifecycle has no bounded post-failure teardown, no ownership gate on reusing an existing stopped pod, and no per-dispatch opt-out for deferrable work — all three leak money without a user decision.
- Confidence: medium (incidents concrete; exact resume mechanism partially probed, see below).
- verified-at-filing:
grep -n 'workload_start_failed\|left RUNNING' src/explore_persona_space/backends/runpod.py→ 10+ hits (lines 352-354, 385, 861, 919-1009): leave-RUNNING-on-launch-failure is a DELIBERATE documented contract ("The pod is left RUNNING for SSH diagnosis (the RunPod-as-diagnosis-lane contract)") — so item (a) is a design-tension fix (bound the diagnosis window), not a missing branch.grep -n 'resume' scripts/pod_lifecycle.py→ provision refuses only a non-EXITED pod (line ~2122:desired_status != "EXITED"), i.e. a STOPPED pod does NOT block re-entry, and the SSH-preflight helper at pod_lifecycle.py:1475-1539 auto-runspod.py resume --issue <N>ONCE on first SSH failure whenallow_resume=True— a concrete candidate mechanism for the stopped-pod resume.git log --oneline --since='7 days ago' -- src/explore_persona_space/backends/{runpod,router,issue_dispatch}.py→ 7 commits (incl. f0c0e3dea4 #1838 "GCP->RunPod failover provision crash-safe (adopt residue pod)", a5c9a09427 #1698 launch-path branch/teardown contract) — none adds an ownership gate on stopped-pod resume, a post-failure teardown bound, or a fallback opt-out flag (2026-08-01).
Proposed change (refine in planning)
- unverified hypothesis — verify at plan time: the terminal rung's launch reached the deliberately-STOPPED pod-1739 via the
allow_resume=TrueSSH-preflight auto-resume (pod_lifecycle.py:1475-1539) and/or the #1838 residue-pod adoption path (miner-inferred from markers/launch logs; router code path not traced end-to-end). - (a) Bound the leave-RUNNING-for-diagnosis contract for FALLBACK-provisioned pods: after
runpod_workload_start_failed, arm a bounded diagnosis TTL (or an explicitepm:note naming who owns the diagnosis) after which the watcher/rung terminates — 4 orphan alerts per incident is the current "bound". - (b) Ownership gate: the terminal rung must not resume/adopt an existing STOPPED
pod-<N>unless the handle/lease shows the rung's own lineage owns it (or an explicit opt-in flag); otherwise provision a freshpod-<N>-<slug>suffixed pod. - (c) Add a
--no-runpod-fallbackper-dispatch flag (threaded like--min-ram-gbintospec.extra) so a deferrable/nice-to-have dispatch converts the terminal rung into a typedno_compute_availabledefer instead of paid spend.
Scope / surfaces
- Primary target:
src/explore_persona_space/backends/runpod.py, src/explore_persona_space/backends/router.py, src/explore_persona_space/backends/issue_dispatch.py - Also inspect
scripts/pod_lifecycle.py(the auto-resume-once branch + provision EXITED-pod semantics) andscripts/autonomous_session_watch.py(the unlaunched-orphan alert arm that could gain a terminate arm) — grepallow_resumeandrunpod_workload_start_failedacross scripts/ + src/ and list every hit in the plan.
Constraints / invariants
- Workflow-surface only — never experiment code,
configs/, ortasks/. scripts/workflow_lint.py --check-askspasses; ruff/bash -n on touched files passes.- Do NOT delete the RunPod-as-diagnosis-lane contract wholesale — it exists because GCP DELETE destroys crash logs (#658); bound it, don't remove it.
- Recursion guard: this task's session carries the workflow_fix_target Provenance line and MUST NOT auto-route its own subagents' workflow-fix candidates.
Provenance
- fingerprint: 606acca8ee2c
- workflow_fix_target: src/explore_persona_space/backends/runpod.py, src/explore_persona_space/backends/router.py, src/explore_persona_space/backends/issue_dispatch.py
- origin: /daily 2026-08-01 problem sweep, CONSOLIDATED.md entry C1.
Activity