workflow-fix: extend owner-fence awareness to the watcher's pod-safety STOP pass
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.
Goal
scripts/autonomous_session_watch.py's pod-safety pass can auto-STOP a RUNNING pod whose owning task carries an UNEXPIRED owner fence, killing the owner's live run. Decide whether that pass should honour the fence_until= / owner= convention #2277 landed on main, and if so, wire it.
Why this is the residual #2277 deliberately left
#2277 installed a fence-aware refusal on pod.py terminate (_guard_owner_fence_before_terminate, scripts/pod_lifecycle.py:3880, called from cmd_terminate at :4131) plus the kind-scoped owner= attribution that keeps a self-posted PASS from manufacturing the ownership that waives it.
cmd_stop was deliberately left untouched — terminate-only was judged correct scoping for that task's Goal, and the boundary was NAMED in its plan §4.2 rather than left silent. But the boundary has a live automated consumer on the other side of it:
- The watcher's pod-safety pass STOPS pods (never terminates) at parked/terminal status, and does NOT route through
cmd_terminate, so #2277's guard is structurally invisible to it. - Stopping is not benign for the incident class #2277 addresses. The harm actually inflicted on 2026-08-13 (pod-2054-tiers) was an in-flight round INTERRUPTED, not volume loss — and
pod.py stopinflicts exactly that. A STOPPED volume is additionally non-durable (#1112). - On 2026-08-13 that pass logged three
pod-keep-running-skipexemptions against these very pods: the timestamp-independentkeep-runningtag was the ONLY thing holding it off. That is the compensating control #2277 recorded, and it is an explicit operator action, not an inference from the owner's own fence.
So the fence is currently honoured on the destroy path and ignored on the interrupt path.
Scope
- Decide first, wire second: is fence-awareness correct for a pass whose whole purpose is reclaiming spend from parked/terminal tasks? A fence that suppresses auto-stop is an unbounded pod-alive shape (the fence-refresh-forever risk row in #2277 plan §8), and unlike terminate there is no
keep-running-style wedged-owner escalation arm for stop. There may be a case for surfacing rather than suppressing. - If wiring: reuse #2277's readers rather than re-implementing them —
_latest_pod_fence_untiland the_OWNER_REGISTRATION_KINDS-scoped_note_owner_token/_latest_pass_owner_for_pod(per the reuse-existing-in-repo-tools rule). Do NOT re-derive the parse; a permissiveness-broadened second parser is the defect class #2277's own review round flagged. - Preserve every existing protection: the ≥2-miss accumulation, the
keep-runningshield, the follow-up-signal marker predicate, and the #1961 per-pod named shield. - Honour the standing pod directive: nothing gets shut down on its own without Thomas's approval, and no watcher path may become MORE willing to stop a pod than it is today.
Non-goals
- Re-opening
pod.py terminate— #2277 shipped and verified that. - Closing the declared copied-token residual (unauthenticated string equality; pinned by
test_terminate_copied_owner_token_waives_fence_known_residual). Same trust model applies here: accidents, not adversaries.
Provenance
Deferred from #2277 at plan-critique round 1 (orchestrator finding OC-1, resolved via the alternatives lens's option (b): name the boundary as an explicit residual with its compensating control). Filing was deliberately held until #2277 merged — the fence_until= convention had to exist on main before a session could sensibly read it. #2277 merged as f573b6291acf8c094350aa90288a27677de5d15d (PR #1951) at 2026-08-14T04:14:07Z.
workflow_fix_target: scripts/autonomous_session_watch.py