Context
Parent: #8757. #8711's bug class exists because four surfaces re-derive disposition independently: unified-comment's deriveUnifiedStatus (src/review/unified-comment.ts:333-404), the planner (src/settings/agent-actions.ts), the executor's own live rechecks (src/services/agent-action-executor.ts), and label resolution — four different subsets of mergeableState treated as "bad" (comment {dirty,behind,unstable}; merge !clean; approve {dirty}; hold-label {}). A fifth re-implementation lives in src/signals/local-branch.ts:697,783. The targeted fix (see the sibling predicate-unification issue) removes today's divergence; this issue removes the CLASS.
Requirements
- Define one PrDisposition value (verdict, plannedActions, holdReasons[], mergeableAssessment, labelSet) computed once per evaluation pass in processors.ts, before any surface renders/executes.
- Unified comment renders FROM it (extend the existing bridge-args pattern in unified-comment-bridge.ts — the self-contained unified-comment.ts keeps receiving pre-rendered/plain data, no new imports there).
- Planner output and label resolution become projections of it; executor rechecks refresh THE OBJECT, not private state.
- An invariant test: for every reachable disposition, comment verdict, planned actions, and labels are mutually consistent (no approve while held; no ready-to-merge while unmergeable; every non-acted would-merge carries a public holdReason).
Deliverables
Test Coverage Requirements
99%+ patch coverage, branch-counted.
Boundaries
Pure refactor + invariant enforcement: byte-identical public outputs for every state where the surfaces already agreed. Blocked by the predicate-unification fix (its regression test defines the contract this refactor must preserve).
Context
Parent: #8757. #8711's bug class exists because four surfaces re-derive disposition independently: unified-comment's deriveUnifiedStatus (src/review/unified-comment.ts:333-404), the planner (src/settings/agent-actions.ts), the executor's own live rechecks (src/services/agent-action-executor.ts), and label resolution — four different subsets of mergeableState treated as "bad" (comment {dirty,behind,unstable}; merge !clean; approve {dirty}; hold-label {}). A fifth re-implementation lives in src/signals/local-branch.ts:697,783. The targeted fix (see the sibling predicate-unification issue) removes today's divergence; this issue removes the CLASS.
Requirements
Deliverables
Test Coverage Requirements
99%+ patch coverage, branch-counted.
Boundaries
Pure refactor + invariant enforcement: byte-identical public outputs for every state where the surfaces already agreed. Blocked by the predicate-unification fix (its regression test defines the contract this refactor must preserve).