workflow-fix: add stall_reason to _PR struct in backends/runpod.py
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 the workflow-fix-on-bug protocol from a prose follow-up raised by the Claude code-reviewer during /issue #701 round 2 (tasks/completed/701/events.jsonl → epm:code-review v2, ts 2026-06-28T08:17:04Z, emitting agent: code-reviewer).
Goal
Restore stall_reason on the _PR struct so RunPodBackend.poll no longer raises AttributeError: '_PR' object has no attribute 'stall_reason' when constructing a PollResult at src/explore_persona_space/backends/runpod.py:389.
Workflow gap
- Bug observed:
tests/test_router*.py/tests/test_backend_*.pyproduces 2 failing tests withAttributeError: '_PR' object has no attribute 'stall_reason'atsrc/explore_persona_space/backends/runpod.py:389(stall_reason=raw.stall_reason). The line is identical onmain;backends/was never touched by #701. The Claude code-reviewer flagged this when running the full pytest suite during /issue #701 round-2 review. - Why it is a workflow gap: the
_PR(poll-result) struct is missing thestall_reasonattribute theRunPodBackend.pollmethod assumes — a workflow-helper code-shape regression on the unified backend router (per.claude/rules/workflow-fix-on-bug.md§ "Workflow surface" / "src/explore_persona_space/backends/*.py" listing). A planner has no way to satisfy the line without this fix. - Confidence (emitter): medium (the symptom is clear, the fix is to add the attribute; whether
stall_reasonshould be sourced from somewhere else or defaulted is the planner's call).
Proposed change (candidate diff sketch — refine in planning)
Add the missing stall_reason attribute to the _PR (poll-result raw) struct so the RunPodBackend.poll consumer at runpod.py:389 can read it. Two plausible patch shapes for the planner to choose between:
- (A) Add
stall_reason: str | None = Noneas a dataclass field on_PR(the raw poll-result struct populated by the SSH/SQL probe) — same default as thePollResultfield — and havepoll_pipeline.py(or whatever populates_PR) thread the actual stall-reason value when known. - (B) Default-source
stall_reasonfromraw.__dict__.get("stall_reason")at thePollResult(stall_reason=...)construction site so a missing attribute degrades toNone.
Option (A) is the cleaner structural fix (the field belongs on _PR); (B) is a one-line getattr(raw, "stall_reason", None) defensive patch.
Scope / surfaces
- Primary target:
src/explore_persona_space/backends/runpod.py(the_PRstruct definition + theRunPodBackend.pollconstruction site at line 389). - Probably also the
_PRpopulator (poll_pipeline.pyor the SSH probe path that builds_PR) — the planner should grep for_PR(constructors and confirm whether the new attribute needs threading or just a default.
Constraints / invariants
- Workflow-surface only — never experiment code,
configs/, ortasks/. tests/test_router*.py+tests/test_backend_*.pymust PASS after the fix.scripts/workflow_lint.pyPASS.- If
workflow.yamlorCLAUDE.mdneed to change to document the field's semantics, they stay consistent with the existingpoll_pipeline.pystall-reason taxonomy (see SKILL.md Step 7 § "Missingfailure_class— invoke the classifier script" +.claude/rules/failure_patterns.md).
Provenance
- workflow_fix_target: src/explore_persona_space/backends/runpod.py
- fingerprint: 9359cb308676
- related_task: #701 (the code-reviewer that surfaced this was reviewing #701; the bug itself predates #701 and is on
main)
The Claude code-reviewer's full verbatim surfaced-prose:
One note on the test claim The implementer's "356 tests passed" was a targeted subset. The full suite shows
5 failed, 5391 passed. I verified all 5 are unrelated to this doc-only patch:
- 2 are a pre-existing
mainbug —AttributeError: '_PR' object has no attribute 'stall_reason'atsrc/explore_persona_space/backends/runpod.py:389(line identical onmain;backends/never touched by the #701 branch).- 3 are test-isolation artifacts (they pass when the 5 are run alone).
- None reference
planner.md.These do not block the merge but the orchestrator may want a separate
kind: infrafix for therunpod.pystall_reasonregression onmain.