refactor(disposition): one shared mergeable-state interpretation consumed by planner, comment, and labels - #8800
Conversation
…umed by planner, comment, and labels (#8759) The #8711 incident's root CLASS: four surfaces each re-derived their own meaning for GitHub's raw mergeable_state string (comment {dirty,behind, unstable}; merge !== clean; approve {dirty}; hold-label none pre-#8758). #8758 unified the predicates; this removes the class: - src/settings/pr-disposition.ts (new, pure, dependency-free): assessMergeableState — THE single raw-string interpretation point — and derivePrDisposition (heldForManualReview / wouldApprove / wouldMerge / commentMergeStateHeld), each semantic documented as a contract. - agent-actions.ts derives the disposition once and consumes its fields for heldForManualReview, the approve gate, and canMerge; isConflict routes through assessMergeableState. Byte-identical: all 308 existing planner tests pass unchanged. - unified-comment.ts's readiness gains mergeStateHeld — resolved by processors.ts via isCommentMergeStateHeld so the self-contained renderer consumes a boolean instead of re-deriving meaning (zero-import contract intact); legacy callers keep the byte-identical raw-string fallback. - test/unit/pr-disposition-invariants.test.ts: the cross-surface invariant suite — module invariants over the full state matrix, planner-vs- disposition agreement per state, renderer-vs-disposition agreement through the bridge boolean, and the legacy-fallback equivalence pin.
Logic backtestReplayed 0 historical case(s) for Backtest comparison:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8800 +/- ##
==========================================
- Coverage 93.81% 92.94% -0.88%
==========================================
Files 797 702 -95
Lines 79608 57138 -22470
Branches 24127 20253 -3874
==========================================
- Hits 74686 53107 -21579
+ Misses 3556 3145 -411
+ Partials 1366 886 -480
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Caution 🛑 LoopOver review result - fixes requiredReview updated: 2026-07-26 07:03:27 UTC
Review summary Nits — 5 non-blocking
CI checks failing
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionPartially addressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
…e-facts exact-shape expectation
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
1 similar comment
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Summary
The final structural item of adversarial-audit epic #8757. #8758 fixed the divergent predicates; this removes the CLASS by giving every disposition surface one shared interpretation of GitHub's raw
mergeable_statestring, with a cross-surface invariant suite that turns any future re-divergence into a test failure.Changes
src/settings/pr-disposition.ts(new, pure, dependency-free — cannot participate in an import cycle):assessMergeableStateis now THE single point interpreting the raw string (clean/conflict/behind/unstable/indeterminate, each meaning documented as a contract), andderivePrDispositioncomputes the shared held/approve/merge core plus the comment surface's downgrade set.agent-actions.tsderives the disposition once per plan and consumes its fields (heldForManualReview,wouldApproveat the approve gate,wouldMergeinsidecanMerge);isConflictroutes throughassessMergeableState. Planner-private terms (autonomy, idempotency, approvals, RC3 terminal-block) stay conjoined at their gates and are pinned as planner-private by test. Byte-identical: all 308 existing planner tests pass unchanged.unified-comment.tsreadiness gainsmergeStateHeld?: boolean, resolved byprocessors.tsviaisCommentMergeStateHeld— the self-contained renderer consumes plain data (zero-import contract intact, parity suite green); callers that don't pass it keep the byte-identical pre-refactor(disposition): single shared PrDisposition object consumed by comment renderer, planner, executor, and labels #8759 raw-string behavior, with an equivalence test proving the fallback matches the shared interpretation for every state.test/unit/pr-disposition-invariants.test.ts: module invariants over the full state matrix (wouldMerge⇒wouldApprove; held⇒neither; behind/blocked/unknown approvable-never-held; conflict unapprovable-never-held), planner-vs-disposition agreement for every state on a green PR (actions + label), renderer-vs-disposition agreement through the bridge boolean, boolean-authoritative-over-raw, and RC3 planner-privacy.Deliberate deviation from the issue's literal text, documented in the module header: the surfaces share the pure derivation function rather than one threaded object instance — same can-never-drift guarantee (same function + same inputs), a fraction of the plumbing. The executor's live rechecks already re-verify through the same predicates post-#8758 and are unchanged.
Test plan
npm run typecheck— cleanunified-comment-parityCloses #8759