Skip to content

Fix planner deadlock when triage goes stale mid-cycle#413

Closed
imetandy wants to merge 1 commit intopeteromallet:mainfrom
imetandy:fix/triage-objective-deadlock
Closed

Fix planner deadlock when triage goes stale mid-cycle#413
imetandy wants to merge 1 commit intopeteromallet:mainfrom
imetandy:fix/triage-objective-deadlock

Conversation

@imetandy
Copy link
Contributor

@imetandy imetandy commented Mar 13, 2026

Summary

I ran into a planner deadlock after a trusted review import landed mid-cycle. New review issues made triage stale, but I still had open objective work in the queue. In that state, desloppify plan resolve ... --confirm was blocked because triage was stale, while desloppify plan triage --run-stages --runner codex was also blocked because objective backlog was still open.

This change makes that state consistent instead of deadlocking it. When triage is stale but still deferred behind objective backlog, objective resolves can continue, direct review resolves stay blocked, and the CLI now says TRIAGE PENDING instead of nudging me toward a triage command that cannot run yet.

What I changed

  • detect when stale triage is pending behind objective backlog
  • allow objective fixed resolves to proceed in that pending state
  • keep direct review resolves blocked until objective backlog is cleared
  • align banner and lifecycle copy around TRIAGE PENDING
  • pass the already-loaded plan into the resolve guard instead of reloading it
  • add regression coverage for the pending-vs-blocked behavior

Testing

pytest desloppify/tests/commands/helpers/test_guardrails.py desloppify/tests/plan/test_triage_phase_banner.py desloppify/tests/commands/resolve/test_cmd_resolve.py desloppify/tests/plan/test_triage_snapshot.py desloppify/tests/commands/plan/test_workflow_gates.py desloppify/tests/commands/plan/test_triage_dependency_guard.py

Copy link
Owner

@peteromallet peteromallet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

The core logic is sound — the four-part condition correctly identifies the deadlock state and the fix breaks the cycle in the right way (objective resolves proceed, review resolves stay blocked).

Looks good

  • The pending_behind_objective_backlog detection at guardrails.py:55-59 is the right set of conditions
  • Lifecycle copy change ("Triage is pending behind...") is clearer than the old "Cannot start triage" framing
  • Test coverage hits the main cases (status marking, messaging, objective allowed, review blocked)

Minor suggestions

  1. Duplicated condition logic — the four-part pending check appears in both guardrails.py:55-59 and plan_triage.py:83-87. If the definition changes, both need updating. Worth a cross-referencing comment at minimum, since the banner function can't depend on the app-layer guardrails.

  2. _REVIEW_DETECTORS is a subset of NON_OBJECTIVE_DETECTORS — intentional, but a comment explaining why would help (it only needs direct review work, not all non-objective detectors). Easy to miss when a new review-like detector gets added later.

  3. Missing test paths — no test for require_triage_current_or_exit with patterns=None (falls through to CommandError at line 149), and no test for mixed patterns that include both objective and review issues (currently the whole batch gets blocked, which is correct but undocumented).

  4. cmd.py:77 — the plan=plan_access.plan if isinstance(plan_access.plan, dict) and not plan_access.degraded else None line is quite long. A local variable would clean it up.

Overall this is a solid fix for a real deadlock. Approval-worthy with the minor suggestions above.

@peteromallet
Copy link
Owner

The fix for this is already on the 0.9.9 branch (c08b84d and subsequent commits built on top). Thanks for the clean PR and tests @imetandy — shipping in 0.9.9.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants