Skip to content

bug(lifecycle): merge-conflict on a PR is detected but never surfaced — no nudge, no notification, no status (suppressed for waiting_input sessions) #2173

Description

@neversettle17-101

Migrated from aoagents/ReverbCode/issues/295

Source: aoagents/ReverbCode#295
Original assignees: harshitsinghbhandari
Original labels: bug, lcm-sm, priority: high


Bug

PR #278 (ao/agent-orchestrator-2main) is CONFLICTING / DIRTY on GitHub. AO did detect it — the stored PR facts read mergeability: "conflicting":

GitHub:  mergeable=CONFLICTING  mergeStateStatus=DIRTY
AO  /sessions/agent-orchestrator-2/pr → { "mergeability": "conflicting", "ci": "passing", "review": "approved", ... }

But AO never surfaces the conflict: no agent nudge, no user notification, and no attention status/badge. To the user it looks like AO "didn't detect it."

Source: local observation, PR #278 | Reported by: @aditi1178 | Analyzed against: 73649b0
Confidence: High

Reproduction

  1. Worker opens a PR, finishes its turn, and goes waiting_input (needs_input).
  2. The base branch (main) advances so the PR becomes CONFLICTING.
  3. The SCM observer detects it and stores mergeability: conflicting.
  4. No nudge, no notification, no status change — the conflict is invisible unless you open the PR-facts panel and read a single mono row.

Root Cause

AO detects the conflict (data layer is correct), but all three surfacing paths are suppressed:

1. Agent nudge is gated off for waiting_input

backend/internal/lifecycle/reactions.go:65 short-circuits every PR reaction (CI-fail, review-feedback, AND merge-conflict) when the session is paused:

if rec.IsTerminated || rec.Activity.State == domain.ActivityWaitingInput {
    return nil
}

The merge-conflict nudge (reactions.go:90-103, "Your PR has merge conflicts. Rebase onto the base branch and resolve them.") sits below that gate, so for a waiting_input session it never runs. PR #278's session is exactly waiting_input.

2. No user notification type for conflicts

notificationIntentForCurrentSCM (reactions.go:~196-214) only emits PRMerged, PRClosedUnmerged, and ReadyToMerge. There is no Conflicting notification — and ReadyToMerge is itself gated by the same waiting_input check (reactions.go:209). A conflict produces zero notifications.

3. No UI status/badge for conflicts

The frontend status model has no conflicting state:

  • frontend/src/renderer/types/workspace.ts:100 WorkerDisplayStatus = "working" | "needs_you" | "mergeable" | "ci_failed" | "done" — nothing for conflicts.
  • mergeability is rendered only as a buried mono row: frontend/src/renderer/components/SessionInspector.tsx:255 <Row k="Merge" v={prFacts.mergeability || "—"} mono />.
  • Worse: a PR that is approved + passing + conflicting maps to the mergeable / "Ready to merge" attention zone (workspace.ts:199-201) — actively misleading.

Compounding factor

This PR's session is also stuck in waiting_input with a dead runtime pane (see #290), so it can never leave waiting_input; even if the gate allowed the nudge, delivery to the dead pane would fail (see #265). But the surfacing gap is independent: a live waiting_input session with a freshly-conflicting PR is equally silent.

Fix

A merge conflict needs a path to the human even when the agent is paused. Options (not mutually exclusive):

  1. Lift merge-conflict (and CI-fail) above the waiting_input gate in reactions.go, or carve an exception so authoritative PR problems still nudge/notify a waiting_input session.
  2. Add a Conflicting notification type in notificationIntentForCurrentSCM so the user is told regardless of agent state.
  3. Add a conflicting display status to the frontend status model so it lands in the "Needs you" attention zone instead of being invisible / mis-binned as "Ready to merge".

At minimum (1)+(3): surface conflicts as an attention item and stop a conflicting PR from showing as mergeable.

Impact

  • A PR with merge conflicts silently sits as if fine (or even as "Ready to merge") — the user only discovers it by manually opening the PR on GitHub.
  • Defeats the core promise that AO drives PRs to a mergeable state and flags what needs attention.

Related

  • #290 — session stuck in waiting_input with a dead pane (why this session can never leave the state that suppresses the nudge).
  • #265sendOnce blocked by a hung pane (nudge delivery path).
  • #240 — PR surface mapping gaps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions