Skip to content

feat(chat): flag a conflicted pull request on its session chip - #3760

Merged
iamwhatever merged 1 commit into
mainfrom
feat/chip-merge-conflict-glyph
Aug 15, 2026
Merged

feat(chat): flag a conflicted pull request on its session chip#3760
iamwhatever merged 1 commit into
mainfrom
feat/chip-merge-conflict-glyph

Conversation

@NicholasRBowers

Copy link
Copy Markdown
Contributor

Problem

A session-list pull-request chip shows a green check whenever the PR's checks
pass — including on a branch that conflicts with its base and cannot merge. The
chip reads "ready" on work that needs a rebase.

Why it matters

The chip is the session list's only at-a-glance answer to "where is this PR?".
A green check there is read as "nothing to do", so a conflicted branch is
invisible until someone opens the PR and finds it unmergeable. The signal is
worst exactly when it matters most: a long-running session accumulates chips, and
the conflicted one looks identical to the landed-and-waiting ones.

Fix (symptoms -> root cause -> change)

Symptom: ✓ on an unmergeable PR.

Root cause: the chip only ever consulted link.ci. The merge answer was
already on the wire and ignored — the chip-status cache projects mergeable /
mergeStateStatus (_record_merge_state), the slot serializer spreads the whole
entry into source_links, and ChatSidebar's own Slot type already declared
both fields. Only the render never read them.

Change: replace the three independent && conditionals with one
chipStatusGlyph() decision, so the precedence is stated once instead of being
an emergent property of guard order:

chip state glyph
checks failed ✗ (unchanged)
conflict, checks passed or pending new
checks pending amber dot (unchanged)
checks passed, mergeable ✓ (unchanged)
merged / closed lifecycle glyph only (unchanged)

A conflict therefore replaces a passing or pending glyph but never masks a
failure — with both blockers live, the worse outcome is the one worth surfacing.

Two deliberate exclusions. mergeStateStatus: 'blocked' is not treated as a
conflict: on a repo with required reviews it is the normal state of every open PR,
so flagging it would decorate the whole session list and mean nothing. And
behind is left alone because such a branch still merges.

The conflict test is mergeable === 'conflicting' || mergeStateStatus === 'dirty'
rather than one field, because _record_merge_state settles the two fields
independently — a poll can land dirty while mergeable is still unknown
and GitLab's conflict normalizes into both.

The glyph reuses the panel's existing components.pullRequestPanel.merge_conflicts
key rather than adding a pages.chatSidebar one, so the chip and the panel's
conflict banner cannot word the same pull request differently in any of the 12
shipped locales. No catalog changes.

Owner-gating is inherited unchanged: the merge pair reaches owner clients only,
exactly like ci, so an embed or app-token client renders precisely what it did
before.

Tests

14 cases added to ChatSidebar.sourceLinkChip.test.tsx, all asserting rendered
aria-labels:

  • the conflict glyph replaces a passing check, across all three ways the backend
    can express a conflict (both fields, mergeable alone, mergeStateStatus alone)
  • a failed rollup wins over a live conflict
  • a conflict outranks a pending rollup
  • a conflict renders on a chip carrying no rollup at all
  • the passing check survives on a clean branch, a blocked branch, a behind
    branch, and a payload with no merge fields (the non-owner shape)
  • the conflict glyph stays suppressed on merged and closed chips

Inverting the precedence order in chipStatusGlyph fails 4 of them, so they pin
the behaviour rather than passing vacuously.

Manual verification

Captured through a new Playwright harness,
website/scripts/capture-sidebar-chip-conflict.mjs, which drives the real built
SPA with fixture slots and asserts all six chips' aria-labels before it
exits
— it cannot ship a screenshot of the old rendering. Both themes.

Screenshots

Chip rows in dark and light. Row 1: #843 passes its checks and conflicts (⚠),
#844 differs only in being mergeable (✓), #845 is pending. Row 2 is the
precedence: #846 carries both blockers and shows the failure, #847 conflicts
while pending, #848 is merged so the merge pair is moot.

Chip rows, dark theme

Chip rows, light theme

Full sidebar context

Sidebar, dark theme

Sidebar, light theme

no linked issue: raised directly as a feature request in a dashboard session, not filed first.

A session-list chip showed a green check whenever the PR's checks passed,
including on a branch that conflicts with the base and cannot merge — so the
chip read "ready" on work that needs a rebase.

The merge pair the chip needed was already on the wire: the chip-status cache
projects `mergeable` / `mergeStateStatus` and the slot serializer spreads the
whole entry, owner-gated exactly like `ci`. Only the render ignored it.

Replace the three independent CI conditionals with one `chipStatusGlyph`
decision so the precedence is stated once: a failed rollup, then a conflict,
then pending, then passed. A conflict therefore replaces a passing or pending
glyph but never masks a failure, and stays suppressed on a terminal PR where
the providers stop answering the merge pair at all.
@NicholasRBowers
NicholasRBowers requested a review from a team August 15, 2026 06:04
@NicholasRBowers
NicholasRBowers requested a review from a team as a code owner August 15, 2026 06:04
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 40b841d36d0fd4d52df3a084f5a3e8162237128f and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 40b841d

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 40b841d36d0fd4d52df3a084f5a3e8162237128f: <one-sentence reason>

@github-actions

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — 🟡 CONCERNS

Advisory design-level review of 40b841d36d0fd4d52df3a084f5a3e8162237128f — updated in place on each push; does not block merge.

Design-Verdict: CONCERNS

Sound single-precedence design, but GitLab need_rebase MRs — unmergeable work needing a rebase — still render the green check this PR set out to kill.

Watch

  • The stated harm — "the chip reads 'ready' on work that needs a rebase" — survives on GitLab fast-forward-only projects: the backend deliberately keeps need_rebase as its own mergeStateStatus (_GITLAB_MERGE_STATE_MAP, "a merge commit cannot unblock the MR") with mergeable: "unknown", so chipStatusGlyph's conflicting || dirty test passes it through to ✓. The description enumerates blocked and behind as deliberate exclusions but never accounts for this case — decide it explicitly (flag it, possibly under different wording than "Merge conflicts") or document why not.

Suggestions

  • This PR introduces a second, broader definition of "conflicted" than PullRequestPanel.tsx:207 (mergeable === 'conflicting' alone): during the dirty-while-unknown window the chip shows a conflict the panel's banner denies. Extract one shared predicate so the two surfaces that share the i18n key also share the decision.

[DESIGN-REVIEWED] 40b841d

@github-actions

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

Advisory UX-level review of 40b841d36d0fd4d52df3a084f5a3e8162237128f — updated in place on each push; does not block merge.

UX-Verdict: CONCERNS

A red 10px triangle carries the whole message, but nothing visible ever says "merge conflicts" — hover explains the click, not the glyph.

Watch

  • The glyph's meaning is AT-only. The chip's title ("Open {{url}} in the side panel…") covers the icon area, so a sighted user hovering the new TriangleAlert never sees "Merge conflicts" — they must distinguish a red triangle from the red X ("Checks failed") at 10px and guess the remedy (rebase vs. re-run checks). Every conflicted chip, every viewing, until learned by clicking through — friction, not failure, since the panel banner explains. Smallest fix already exists three lines up in the same JSX: the merged glyph wraps in a span with aria-label + title; give the conflict glyph (and ideally the CI glyphs) the same titled span.

Suggestions

  • A settled conflict suppresses "Checks running" (outranks a pending rollup test), so the amber dot silently vanishes mid-run; if the titled-span fix lands, the tooltip absorbs this ambiguity too — one more reason to do it in this PR.

[UX-REVIEWED] 40b841d

@github-actions

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Advisory premise-level review of 40b841d36d0fd4d52df3a084f5a3e8162237128f — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push; does not block merge.

First-Principles-Verdict: CONCERNS

The chip fix is cause-level and earns its place, but it ships a fourth spelling of "is this PR conflicted", and the panel it borrows its label from disagrees with it.

What this change ships

Intent: stop a session-list PR chip from reading "ready" on a branch that cannot merge — a FIX to a misleading signal.

  1. A conflicted PR's chip shows a warning triangle instead of a green check — justified (the defect itself; data was already on the wire, ChatSidebar.tsx:470-471).
  2. A conflict also replaces the pending amber dot — declared, justified (a settled verdict outranks a non-verdict).
  3. A conflict glyph now appears on chips carrying no rollup at all — declared.
  4. Failed checks still outrank a conflict — declared precedence, justified.
  5. blocked and behind branches keep the green check — declared exclusion, derived (blocked is every open PR under required reviews).
  6. Chip label reuses components.pullRequestPanel.merge_conflicts — justified, avoids 12-locale duplication.
  7. New capture harness capture-sidebar-chip-conflict.mjs — declared; convention (123 sibling capture-*.mjs scripts).
  8. Four screenshots under temp-screenshots/ — documented convention (temp-screenshots/README.md).

Watch

  • The frontend now holds four distinct conflict predicates (grepped 'conflicting' / 'dirty' outside tests): chip mergeable==='conflicting' || mergeStateStatus==='dirty' (ChatSidebar.tsx:537), panel banner mergeable==='conflicting' only (PullRequestPanel.tsx:207), Sage chips mergeable only (PrStatusChips.tsx:46), issue-radar dirty only (prActions.ts:110). The PR argues dirty-alone is a real conflict answer and reuses the panel's label so the two surfaces "cannot word the same pull request differently" — yet on a dirty-while-mergeable-unknown payload the chip flags a conflict the panel banner stays silent about. 2 unfixed siblings of the exact gap this PR names; accepted-and-deferred, but a human should decide whether the panel's predicate widens in step.

[FIRST-PRINCIPLES-REVIEWED] 40b841d

@github-actions

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 40b841d36d0fd4d52df3a084f5a3e8162237128f — this comment is updated in place on each push.

Review details

I've independently verified the key claims. The core logic is sound:

  • TriangleAlert is a real lucide-react export (used in 8+ sibling files against the same version).
  • components.pullRequestPanel.merge_conflicts exists in en.manual.json and every locale, and is consumed with the same full-path i18nT(...) lookup already used by PullRequestPanel.tsx:210.
  • The backend genuinely normalizes and serializes mergeable/mergeStateStatus onto source_links via get_cached_check_statusstate.py:2268, gated to kind === 'change', so the conflict branch is reachable.
  • Precedence (failed > conflict > running > passed, all under showsChipCi) is internally consistent and matches the added tests; exactly one glyph renders.

The committed temp-screenshots/ PNGs and .mjs capture harness are dev tooling, not a behavioral defect, and fall in excluded categories. No grounded defect survives falsification, and I found no new one at the Step-2 bar.

No findings.

[OPUS-REVIEWED] 40b841d

Verdict parsed from the review's SHA-scoped output markers for commit 40b841d36d0fd4d52df3a084f5a3e8162237128f.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 40b841d36d0fd4d52df3a084f5a3e8162237128f: <one-sentence reason>

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 15, 2026
@iamwhatever
iamwhatever enabled auto-merge (squash) August 15, 2026 07:05

@iamwhatever iamwhatever left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Tier 1 auto-approve: cosmetic-UI (7 files). Criteria: no conflict, no requested changes, no security surface, AI reviewers green. Category: adds a visual conflict indicator on the PR session chip — purely presentational change.

@iamwhatever
iamwhatever merged commit 4826436 into main Aug 15, 2026
58 checks passed
@iamwhatever
iamwhatever deleted the feat/chip-merge-conflict-glyph branch August 15, 2026 07:05
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 15, 2026
encomjp pushed a commit to encomjp/kirocrew-customapi that referenced this pull request Aug 22, 2026
…otdev#3760)

A session-list chip showed a green check whenever the PR's checks passed,
including on a branch that conflicts with the base and cannot merge — so the
chip read "ready" on work that needs a rebase.

The merge pair the chip needed was already on the wire: the chip-status cache
projects `mergeable` / `mergeStateStatus` and the slot serializer spreads the
whole entry, owner-gated exactly like `ci`. Only the render ignored it.

Replace the three independent CI conditionals with one `chipStatusGlyph`
decision so the precedence is stated once: a failed rollup, then a conflict,
then pending, then passed. A conflict therefore replaces a passing or pending
glyph but never masks a failure, and stays suppressed on a terminal PR where
the providers stop answering the merge pair at all.

Co-authored-by: Nick Bowers <nrb@amazon.com>
@bolichen97

Copy link
Copy Markdown
Collaborator

Open PR relationship audit

This is a consolidated, point-in-time code-level audit note. It compares complete merge-base diffs and current/merged code; it does not treat a shared topic as duplication or partial coverage as completion.

Relationship findings

  • PR #8295 is PARTIALLY_COVERED relative to this PR. Coverage is explicitly incomplete; this finding is not a completion or closure claim. Recommended action for PR #8295: KEEP. The merged sidebar change covers a different surface; the panel tab strip it left behind is exactly what this PR fixes, so nothing here is already shipped. Files: website/src/pages/ChatSidebar.tsx.

No PR, Issue, label, branch, or review state was changed by the relationship-note portion of this audit.

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.

3 participants