Skip to content

feat(web): add pull request list filters

MacroscopeApp / Macroscope - UI Consistency failed Aug 30, 2026 in 4m 46s

UI Consistency: 5 issues found

Reviewed the changed web UI code against the shared component system, Tailwind ownership, and interaction/accessibility constraints. Five concrete findings were posted as inline comments.

  • apps/web/src/components/pullRequest/PullRequestRow.tsx (line 128): PullRequestRowLabels is always rendered as an element while returning null internally, so PullRequestMetaLine draws a separator for it — every row without labels now shows a stray ·. Guard the element at the call site.
  • apps/web/src/routes/_chat.pull-requests.tsx (line 1747): the new outlined branch of CompactFilterMenu hand-rebuilds Button variant="outline" on a raw MenuTrigger (h-8 vs h-9 sm:h-8, rounded-md vs --control-radius, no focus-visible ring, no dark:bg-input/32, no coarse-pointer hit target) and renders directly beside a real outline Button.
  • apps/web/src/components/pullRequest/PullRequestListFilters.tsx (lines 269–273): InputGroup className="h-8" overrides the primitive's control height, leaving the wrapper shorter than the h-8.5 input it contains; size the input (size="compact" or **:[input]:h-8) as the sibling search inputs do.
  • apps/web/src/components/pullRequest/PullRequestListFilters.tsx (line 50): labelDotColor plus the dot markup is duplicated verbatim in PullRequestRow.tsx; one owner in pullRequestPresentation.tsx would keep the two in step.
  • apps/web/src/components/pullRequest/PullRequestListFilters.test.tsx (line 38): the project tests still search for an inline MenuRadioGroup that the submenu refactor removed; the CI Test job fails at lines 131 and 161 with expected undefined to be defined.

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.

Methodology and evidence:

  • Compared the new controls against the core primitives in apps/web/src/components/ui (button.tsx, menu.tsx, input.tsx, input-group.tsx, badge.tsx) and against existing call sites of the same patterns (PullRequestSearchInput, PullRequestReviewerPicker).
  • Verified --control-radius: 0.5rem in apps/web/src/index.css against the rounded-md used by the new outlined trigger, and the Button size scale (default: h-9 px-… sm:h-8) against the trigger's fixed h-8.
  • Confirmed the MenuPopup width-defaulting contract still holds for the new w-80 / w-72 / min-w-56 / min-w-72 popups (string classNames containing width utilities correctly suppress min-w-32); no finding there.
  • Read PullRequestMetaLine in pullRequestPresentation.tsx, whose doc comment states the caller-side {condition ? … : null} contract that the new labels segment breaks.
  • Test failure is not inferred: GitHub check run Test (id 99325532338) for head e8405eb reports failure annotations at PullRequestListFilters.test.tsx:131 and :161, matching the submenu composition change.
  • No environment-routing, scroll/virtualized-mask, or global-CSS/theme concerns were introduced by this diff; docs/user/source-control.md is outside this check's file scope and was used only as context.

🔍 Agent activity — 25 tool calls

💭 Let me check which files changed and look at their current state at head.

1. browse_code — apps/web/src/components/pullRequest/PullRequestListFilters.tsx

2. browse_code — apps/web/src/components/pullRequest/PullRequestRow.tsx

💭 I'm checking the UI primitives now—Menu, MenuSubPopup, MenuCheckboxItem, MenuSubTrigger, and the Button component, since the size="icon" prop was removed and the label changed to "Filters" with an aria-label. I want to verify the Button's default size behavior and confirm MenuSubPopup handles its width classes consistently across the different variants used. I'm also wondering whether there's a shared InputGroup search pattern used elsewhere, like in a combobox primitive, and whether the labelDotColor logic duplicated across two files should really be extracted into a shared helper given the UI consistency guidance around repeated geometry. I'll take a look at ui/menu.tsx next to confirm.

3. browse_code — apps/web/src/components/ui/menu.tsx

4. browse_code — apps/web/src/components/ui/input-group.tsx

💭 I'm checking whether the Button component still uses the icon size or defaults now, and confirming the trigger passes its icon and label children through correctly via MenuTrigger's render prop. There's a subtler accessibility concern: the aria-label "Filter pull requests" overrides the visible "Filters" text and the count badge, so screen readers won't announce the count, and the visible label isn't a substring of the accessible name per WCAG 2.5.3 — worth flagging as a minor issue. Now I'm moving on to check the project option inside PullRequestFilterRadioSubmenu. Checking other behavior changes: filter updates and undefined-spreading look fine, project selection guard logic still holds since the radio group only fires onChange when the value differs, and the "Anyone" author option correctly maps empty string to undefined. Now I'm considering whether the search input inside the author menu properly handles keyboard events, since Base UI's typeahead and arrow-key navigation rely on keydown propagation that's being stopped except for Escape. I want to check whether a plain input placed inside a Base UI menu popup is treated as a focusable menu item or gets skipped during keyboard navigation, since this could be an accessibility gap or an accepted pattern already used elsewhere. I'm going to search the codebase for similar search-inside-menu patterns or a Combobox/Command primitive that might already solve this.

5. browse_code — apps/web/src/components/ui/*.tsx

6. git_tools — MenuSubPopup|MenuPopup

💭 There's a PullRequestReviewerPicker component with a searchable menu that likely uses the Command primitive — I should check it since the author filter I'm building resembles that same search-in-menu pattern already implemented there.

7. browse_code — apps/web/src/components/pullRequest/PullRequestReviewerPicker.tsx

8. browse_code — apps/web/src/components/ui/command.tsx

💭 I notice the reviewer picker uses a compact Input size, while the new author filter applies a fixed height class to the InputGroup wrapper instead—since the Input primitive already has its own height behavior, these two approaches could clash. I should check how the Input primitive defines its sizes to be sure.

9. browse_code — apps/web/src/components/ui/input.tsx

10. browse_code — apps/web/src/components/ui/button.tsx

💭 I'm keeping the currentColor override for the icon when filters are active, since that logic still holds. Then I'm checking the trigger's accessibility—now that "Filters" is visible as text rather than icon-only, the aria-label might be redundant and worth reconsidering for a label-in-name mismatch. For the InputGroup class in the author submenu, setting the group height to h-8 conflicts with the inner input's own height (h-8.5/sm:h-7.5), which could cause the input to overflow its container by a couple pixels on mobile—similar to how PullRequestSearchInput instead sets height directly on the input rather than the group wrapper. This looks like a case where the call-site override mismatches the primitive's intended control height. Now I'm scanning other spots for real issues: checking MenuSubPopup's explicit width classes work fine, confirming the "No authors found" disabled MenuItem is handled properly, and looking at whether MenuCheckboxItem's default closeOnClick behavior causes any unwanted menu closures—it doesn't. I also want to check for potential conflicts between Base UI Menu's typeahead handling and any custom keyboard logic in this component. The input's stopPropagation logic blocks all keys except Escape from reaching the menu, which stops typeahead hijacking but also blocks ArrowDown/ArrowUp/Home/End/Tab, so a keyboard user can't navigate from the search input into the results list—this is a real accessibility regression compared to the existing reviewer picker pattern, which uses plain buttons without stopPropagation. I want to suggest only stopping propagation for printable/typeahead-relevant keys instead of all keys, though I need to be careful to still allow Home/End/space for normal text editing within the input. I'm also considering whether Base UI's composite menu handles ArrowDown correctly when focus is on a non-item element like this input, and whether Tab behavior inside the menu might compound the issue—this feels like a defensible, medium-confidence finding since it's new code rather than a migration. I should also check how autoFocus behaves for a submenu in this context. Now moving on to look at the route file itself, since there appear to be more significant behavioral issues to examine there.

11. browse_code — apps/web/src/routes/_chat.pull-requests.tsx

12. browse_code — apps/web/src/routes/_chat.pull-requests.tsx

💭 I want to check how the display groups sort area and CompactFilterMenu handle a group with an empty label, so I'll look at the relevant sections around lines 1200-1300, 1420-1480, the CompactFilterMenu near 1700-1780, and the toolbar area around 2000-2060.

13. browse_code — apps/web/src/routes/_chat.pull-requests.tsx

14. browse_code — apps/web/src/routes/_chat.pull-requests.tsx

💭 Now I'm looking at CompactFilterMenu and the toolbar layout code around line 2000-2060.

15. browse_code — apps/web/src/routes/_chat.pull-requests.tsx

16. browse_code — apps/web/src/routes/_chat.pull-requests.tsx

💭 I notice CompactFilterMenu with the outlined prop is manually recreating the outline button style using a raw MenuTrigger and custom classes, rather than reusing the actual Button variant="outline" component that sits next to it in the filters menu. This means it's missing the design system's focus-visible ring, the shared --control-radius, the responsive height bump on mobile, the coarse pointer hit-target expansion, the dark-mode input background, and the cursor pointer style — all things the real primitive provides. This matches the pattern I should flag: a local trigger reconstructing a Menu/Button variant with large call-site class strings overriding core height, radius, focus ring, cursor, and hit target. The stronger fix is using MenuTrigger render={<Button variant="outline" />} directly instead of hand-rolling these styles. This outlined variant looks newly introduced in this PR, and it sits right next to a genuine outline Button trigger in the same row — so the mismatch in height (h-8 vs h-9 on mobile), radius, and dark background is a visible, concrete inconsistency worth confirming against the actual --control-radius value. Now I want to look at a second candidate: how PullRequestDiffStat handles the zero-value case, comparing it to how it previously rendered, and also check PullRequestActorAvatar and the row's label dot styling that uses an inline hex color from the host data. There's also a small duplicated helper for the label dot color across two files — a minor consistency issue rather than something serious, so I'll keep it brief and focus the review on two or three real findings rather than padding it out.

17. git_tools — export function PullRequest(DiffStat|ActorAvatar|ActorLabel)

18. git_tools — --control-radius

💭 I've confirmed the radius mismatch: control-radius is 0.5rem versus outlined's 0.375rem, plus a height difference of h-8 vs h-9 sm:h-8 for the default button. This means the Sort control next to Filters will be shorter on small screens with a mismatched corner radius, missing focus ring, no dark surface treatment, and no coarse-pointer sizing. I'm also noticing the MenuTrigger button lacks a type attribute. Since it's not inside a form, that's not an issue. Turning to PullRequestRowLabels rendered inside PullRequestMetaLine — since it returns null internally when there are no labels but is still a valid element (not null itself), the meta line's separator logic might insert a stray "·" before it since Children.toArray won't filter it out. I need to check how PullRequestMetaLine actually handles separators between children.

19. browse_code — apps/web/src/components/pullRequest/pullRequestPresentation.tsx

💭 I'm realizing the contract expects callers to render conditional expressions inline so null children get skipped by Children.toArray, but the new PullRequestRowLabels component always returns an element even when empty, so it's still counted and produces a stray separator dot before it. I need to double check whether Children.toArray actually treats that returned element as truthy despite it rendering null internally. Confirming the fix: guard the call site with entry.labels.length > 0 ? <PullRequestRowLabels labels={entry.labels} /> : null on line 128, since other callers like PullRequestChecksPopover already follow this pattern correctly with their own undefined guard. I'm noting this as a self-contained inline suggestion, then moving on to check the diff-stat change in the row next. Since PullRequestReviewOutcomeBadge already uses Badge elsewhere, and given the guidance against demanding unrelated cleanup, I'll lean toward not flagging this too heavily but want to peek at badge.tsx first. I also want to check the PullRequestListFilters test file, since the menu items moved into submenus and findLabeledGroup now recurses—I need to verify the tests still meaningfully cover the rendered structure.

20. browse_code — apps/web/src/components/pullRequest/PullRequestListFilters.test.tsx

21. browse_code — apps/web/src/components/ui/badge.tsx

💭 I'm checking whether the test's findValueChange helper will still locate the right element now that the project group renders as a PullRequestFilterRadioSubmenu component with an onChange prop instead of onValueChange directly in the tree — this could break the test's assumption about where that prop appears. I'm double-checking the head commit to confirm the test file still calls findValueChange(view) for the project tests, tracing through the element tree from Menu down to MenuPopup's submenu children, and confirming none of them expose an onValueChange prop—meaning those tests would genuinely fail. This raises the question of whether broken tests are even in scope for this UI consistency check, or whether the check is only meant to flag missing focused tests around primitive components. Confirming none of the submenu children expose an onValueChange prop, so the project-related tests will indeed break since they rely on finding a labeled group that no longer exists directly — it's now nested inside a submenu structure requiring recursive lookup through MenuSubTrigger/MenuSubPopup before reaching the actual PullRequestFilterRadioGroup with the "Project" label. I should verify this rather than assume — let me check whether CI check runs exist for this PR to see if these tests actually failed, and also double check that findLabeledGroup's recursion terminates correctly since MenuGroupLabel uses children rather than a label prop.

22. github_api_read_only — repos/{owner}/{repo}/commits/e8405eb63139b8ac50182afb9adde25bcae49819/check-runs?per_page=30

23. git_tools — 86c9a92..e8405eb

💭 The "Test" check failed with 4 annotations, so I want to pull those annotations to see if they point to actual project test failures.

24. github_api_read_only — repos/{owner}/{repo}/check-runs/99325532338/annotations

💭 The failing tests confirm my finding with solid CI evidence. Now I'm finalizing this: PullRequestRow.tsx has a stray meta-line separator bug — since PullRequestRowLabels returns null internally but PullRequestMetaLine still counts it as an element, label-less rows render a doubled "·". I need to check the exact diff line numbers so I can place an inline comment suggesting a guard at the call site. Line 128 is confirmed as part of the diff, with 10-space indentation, so I'm drafting a suggestion that wraps the labels component in a conditional check for entry.labels.length. For the second finding on PullRequestListFilters.test.tsx, the failing assertions are on lines 130 and 160, but only lines 37-38 are actually part of the diff — since GitHub's review API requires comments to land on diffed lines, I'll need to place the comment on line 38 while referencing the actual failure locations in the body text. Now looking at the third issue in CompactFilterMenu, the outlined variant's raw MenuTrigger doesn't match the real outline Button styling — wrong height, radius, missing focus ring, missing dark background, and no coarse-pointer target — sitting right next to the genuine outline Button trigger for the Filters menu. Since Button is already imported and used elsewhere in the file, I'm considering whether to suggest swapping in MenuTrigger render={<Button variant="outline" />} versus just patching the class names, but since CompactFilterMenu is reused in both ghost-pill and outlined contexts, I need to figure out the simplest fix that doesn't break the other usage. There's also a minor sizing mismatch: the InputGroup's forced h-8 shell doesn't match the actual input height, causing overflow on smaller screens, unlike how PullRequestSearchInput or the reviewer picker handle sizing. The fix would be dropping the fixed height and using a compact size variant or a direct height override on the inner input instead. Separately, I notice labelDotColor and the associated dot markup are duplicated across PullRequestListFilters.tsx and PullRequestRow.tsx, so consolidating both into pullRequestPresentation would clean things up. I'm also weighing whether to flag a mismatch between the visible "Filters" label and the aria-label "Filter pull requests" on the trigger — since the count badge isn't announced and there's now visible text, the aria-label could either be dropped or updated to include the count. It's a small accessibility issue worth a brief note on its own line. Given I already have several candidate comments, I'll prioritize: the stray separator, failing tests, the outlined trigger reconstruction, InputGroup height, and the duplicated helper, capping it at five and leaving the aria-label issue as optional since "Filter pull requests" is a reasonably adequate description on its own. Now I need to pin down exact line numbers for each inline comment by cross-referencing the diff hunks in PullRequestRow.tsx, PullRequestListFilters.tsx, _chat.pull-requests.tsx, and the test file to make sure I'm anchoring to lines actually present on the added side of the diff. For the first comment, I'll flag that PullRequestMetaLine always renders a separator dot for its label element even when there are no labels, producing an awkward "author · · Approved" — the fix is guarding the element at the call site so it only renders when labels exist. Then I'm moving to the test file comment about the two project cases needing similar handling. Since it's two hunks rather than one, I'll skip the unified diff format and just describe the fix in prose instead. Moving to the third comment, about the outlined button hand-built on a raw MenuTrigger near line 1747 — it mismatches the real Button component in height, corner radius, focus ring, dark mode background, and touch target size, creating visible inconsistency next to the actual Filters button which uses variant="outline" properly. Writing conditional className logic... I'm weighing whether className merging with the rendered Button component would behave predictably, since this is genuinely ambiguous. Given that uncertainty, I'll present the diff as a rough sketch rather than a guaranteed fix. For the InputGroup sizing issue, I'm noting that using a fixed h-8 class creates a mismatch with the Input component's actual height, and instead the fix should size the inner input directly the way other components in the codebase already do it. I'm sketching out a targeted replacement that removes the height override from InputGroup and applies sizing to the input element instead. One more thing to flag: the label dot color logic and markup are duplicated between the row and menu components, so I should suggest consolidating that into the shared presentation helper. Given there's a confirmed test regression among the findings, this pushes the overall check toward failure, so I'll write up the review now.

25. modify_pr — UI consistency review found several issues in the changed web UI code: a meta-line separator regression on rows without labels, a menu trigger that hand-rebuilds the outline Button, a call-site height override on InputGroup, stale tests for the new submenu composition (currently failing in CI), and a duplicated label-dot helper.


Agent Credits: 212 credits

Token usage: 30 uncached input · 1,215,293 cache-read input · 146,294 cache-write input · 19,633 output
Agent Credits may also include non-token charges from external tools such as web research.