Skip to content

fix(web): gate PR agent comments by context

MacroscopeApp / Macroscope - UI Consistency failed Aug 15, 2026 in 3m 10s

UI Consistency: 1 issue found

apps/web/src/components/pullRequest/pullRequestDetail.logic.ts — buildAddSelectionToAgentHandoff now emits the selection chip with text: "". ComposerPendingReviewComments renders every chip inside a Tooltip/TooltipPopup whose body is {comment.text} with no empty guard, so hovering an "Add to agent" chip shows an empty bordered tooltip box. The sent-message card (UserMessageReviewCommentCard in MessagesTimeline.tsx) already guards with comment.text.length > 0; the composer chip should do the same and skip the tooltip when there is no text.

Inline comment posted on the changed line.

Details

Note

Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.

Scope reviewed: apps/web/src/components/pullRequest/PullRequestCodeTab.tsx, PullRequestDetailPanel.tsx, pullRequestDetail.logic.ts, DiffCommentAnnotation.test.tsx, plus directly affected consumers DiffCommentAnnotation.tsx, ComposerPendingReviewComments.tsx, MessagesTimeline.tsx, and AnnotatableCodeView.tsx.

Checked and found acceptable:

  • The secondary action still routes through the shared Button primitive (size="xs", variant="outline"); dropping the SparklesIcon is a label-only change and no other agent-handoff control in apps/web/src carries that icon, so no variant or icon-tone inconsistency is introduced.
  • Removing allowEmpty means the "Add to agent" button is now disabled while the textarea is empty, matching the primary "Add to review" button's disabled contract in the same row; the updated test covers this state.
  • The gating change ({...(attachTarget ? { onAddToAgentSelection: addSelectionToAgent } : {})} with pullRequestComposerTarget) resolves the earlier mismatch where the button appeared in page context but fell through to opening a new thread. Behavior is now covered by a focused unit test.
  • No Tailwind/global CSS, theme variant, scroll/mask, or virtualized-fade surface is touched by this diff.

Minor, not reported as a violation: DiffCommentSecondaryAction.icon and allowEmpty in DiffCommentAnnotation.tsx now have no consumers. That is dead contract rather than a behavioral or visual regression, and removing them is optional cleanup.


🔍 Agent activity — 1 tool call

💭 Looking at the diff, I can see the prior feedback about the composerDraftTarget gate has been addressed with the conditional spread operator for onAddToAgentSelection. Now I'm checking the UI consistency changes — the selection action is being simplified by removing the sparkles icon and allowEmpty flag, and the button text is shifting from "Ask" to "Add to". Let me trace through DiffCommentAnnotation to understand how these properties interact and whether the icon removal affects the component's behavior. I need to check the DiffCommentAnnotation.tsx file to understand how the secondaryAction prop works—specifically whether it still accepts icon and allowEmpty properties, and if the button gets disabled when the text field is empty. I should verify whether any other parts of the codebase are actually using the icon and allowEmpty props on secondaryAction, since they might be orphaned properties that aren't needed anymore. I'm noticing that allowEmpty and icon props in DiffCommentSecondaryAction aren't being used anywhere now, which is a minor contract issue but probably not significant enough to flag given the rules focus on concrete violations. More importantly, the "Add to agent" button has become disabled. The label change and requirement for text appears intentional based on the test assertions. For the icon removal from the secondary button — it previously had a SparklesIcon — I should check if other "Add to agent" buttons throughout the codebase use icons consistently. If similar handoff buttons in thread diffs or file previews also have icons, removing this one creates an inconsistency I should investigate. The gating with attachTarget resolved the earlier behavioral issue, so the "Add to agent" button now only shows up in thread context when there's a composer target available. But I need to verify that startAsk still works properly when attachTarget is set—I should trace through the flow in PullRequestDetailPanel to check the toast messaging and overall behavior. Looking at finishSelection, the logic is consistent: if the comment is null, the draft clears and nothing happens, same as before. The secondary button being disabled when the textarea is empty makes sense since the handoff builder clears the comment text anyway and sends just the request in the composer prompt—so requiring non-empty text aligns with that behavior. Now I'm checking UI consistency in PullRequestCodeTab around the area where DiffCommentAnnotation renders to make sure the secondaryAction changes don't break anything there. Looking at where DiffCommentAnnotation gets used with secondary actions in thread diff panels—it's only PullRequestCodeTab. The finishSelection comment mentions that the line selection logic is built by the same function the thread panel uses, so I should check the thread panel's own line selection to see if there's an equivalent "Add to agent" flow and whether there's a naming or labeling inconsistency I need to address. Let me search for buildDiffReviewComment consumers to trace this. I should check how AnnotatableCodeView handles line selection and agent comments to see if it uses different labeling like "Add to chat" for consistency. Looking at how AnnotatableCodeView handles the diff line selection flow, the submit button defaults to "Comment" through DiffCommentAnnotation's submitLabel. The key difference from the PR flow is that this path preserves the comment text when adding to the composer, whereas the PR flow clears it and moves the request to the prompt instead — it's more of a behavioral distinction than a UI styling issue. I'm noticing a potential inefficiency: in the PR flow, finishSelection constructs the comment with the typed text, then buildAddSelectionToAgentHandoff immediately strips it with text: "", so passing text into buildDiffReviewComment seems redundant. I should verify whether buildDiffReviewComment actually uses that text field for anything else like rangeLabel or diff context. Also wondering if there's a risk of prompt duplication or whether the composer prompt gets overwritten if it already has content when startAsk writes the task. Now looking at the UI consistency findings: the DiffCommentSecondaryAction.icon and allowEmpty props are no longer used anywhere — dead contract in a shared component, though leaving unused optional props isn't necessarily a violation. More importantly, I should check if there's a consistency rule about agent handoff actions carrying a sparkles icon by looking at other handoff buttons in the panel like "Fix in a thread" and "Ask". The button is now disabled when empty, but there's no tooltip or aria label explaining why it's disabled, which is an accessibility gap. The prop-spread pattern for conditionally adding onAddToAgentSelection is fine. The real question is whether gating the button behind attachTarget creates a behavioral regression — on the PR page context, "Add to agent" disappears entirely whereas previously "Ask" was always available to open a new thread. That's an intentional product decision from the prior feedback. In thread context where composerDraftTarget is undefined, attachTarget is null and the button stays hidden, which is correct. Let me trace through startAsk to confirm it properly writes to the composer when attachTarget is non-null. Looking at the startAsk function, it checks that detail exists and handoff is null before proceeding, which makes sense when attachTarget is provided. The toast notification says "Added to the composer" with a description about reading over the question before sending, but there's a minor inconsistency where the UI refers to it as a "request" rather than a "question" when text is typed in. The button disables when the composer is empty, which aligns with the test expectations and makes sense given that the typed text now serves as the prompt. However, there are unused fields in DiffCommentAnnotation — specifically icon and allowEmpty — that are now dead props after the refactoring, which violates the principle of keeping component contracts minimal and focused. I'm also checking whether the secondary button's missing icon causes any visual issues, and examining how the review comment chips render when the text is blanked out to ensure the UI doesn't break. I'm noticing that the composer chip's tooltip now displays empty content when the selection chip has an empty text field, which creates a blank bordered tooltip box. This is actually consistent with the previous "Ask" flow behavior — even when nothing was typed, the tooltip would show empty since it was rendering whatever text was available. Looking at other chips in the codebase, the pull request context comment always has text content, and other flows like the fix finding handoff either have text or don't generate chips at all (the submit button is disabled when empty). So this change introduces a guaranteed empty tooltip state specifically for the "Add to agent" chip, which is a small but concrete visual regression tied to this particular change. I'm wondering if this falls within the scope of the UI consistency check — it's a minor regression from an empty text value being set on the chip, and while it's not a major issue, it's a reportable finding. Let me verify how the Tooltip component actually behaves when content is empty to confirm whether it renders that small empty box. The popup renders as a bordered box with padding, so clearing the text creates an empty bordered tooltip on hover—a visual regression from { ...input.comment, text: "" }. The chip text might also feed into the agent's prompt serialization through formatReviewCommentContext, but clearing it still passes the diff to the agent, which seems intentional. I'm checking whether the chip actually renders as ComposerPendingReviewComments. I'm seeing that empty tooltips already existed in the previous version when the "Ask" feature had allowEmpty enabled, so this behavior isn't entirely new—the patch just makes it the consistent state rather than an exception. Given that the rules account for changes that make pre-existing edge cases more prominent, I should post an inline comment on the pull request to flag this. The core issue is that rendering an empty tooltip box looks odd, and the smallest fix would be to skip the Tooltip wrapper entirely when the comment text is empty—just render the chip span alone. But since ComposerPendingReviewComments.tsx isn't in the diff itself, I'd need to post the comment on the changed logic line and describe the fix, though I can't include a cross-file diff per the guidelines. I'm also considering whether unused optional props like icon and allowEmpty in DiffCommentAnnotation are worth flagging, but they're more of a maintenance cleanup than a UI regression, so I'll focus on the tooltip finding instead. Let me verify the tooltip issue is real by checking how empty text renders in the timeline and whether sent messages show empty cards. I'm noticing that MessagesTimeline properly validates comment text length before rendering, but ComposerPendingReviewComments skips this check, which is why the empty tooltip appears. The issue is that the sent-message card handles empty text gracefully while the composer chip doesn't. I should add an inline comment on the changed line in pullRequestDetail.logic.ts where the review comment is constructed with empty text, and I need to find the diff line number for the right side of the PR file. I'm looking at line 675 as a changed line on the right side. The issue is that ComposerPendingReviewComments unconditionally renders the tooltip with comment.text, which means a selection chip with empty text displays an empty tooltip box on hover—MessagesTimeline already prevents this with a length check. I should suggest either guarding the tooltip to skip rendering it when text is empty, or keeping a fallback label. This is a cross-file fix that doesn't require a diff. For the check state, this is a minor visual regression that qualifies as a concrete finding, so I'll mark it as failure. I've verified that Base UI's tooltip viewport renders empty children as an empty string, but the popup still has border, background, and padding, confirming the empty box appears. I'm confident enough to post this.

1. modify_pr — One finding on the composer-chip rendering path introduced by this change. Details inline.


Agent Credits: 110 credits