Skip to content

feat(droid): add Factory Droid as a provider

MacroscopeApp / Macroscope - UI Consistency failed Aug 23, 2026 in 4m 56s

UI Consistency: 1 issue found

1 finding in the web UI scope (apps/web/src/**).

  • apps/web/src/components/Icons.tsx β€” the new DroidIcon paints with fill="currentColor", so it inherits the ambient text color, while every other provider glyph in PROVIDER_ICON_BY_PROVIDER (OpenAI, CursorIcon, GrokIcon, OpenCodeIcon) pins its own tone. In muted provider surfaces (ThreadCommandSubtitle with text-muted-foreground/70 + opacity-70, and the sidebar thread tooltip with text-muted-foreground + grayscale opacity-60) the Droid mark renders visibly fainter than its siblings. Suggested fix posted inline: pin the tone with cn("fill-[#171717] dark:fill-[#e5e5e5]", className), matching the #171717/#e5e5e5 mono tone this PR already uses in the mobile ProviderIcon.

No other web-scope issues found.

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.

Files examined (in scope)

  • apps/web/src/components/Icons.tsx (new DroidIcon)
  • apps/web/src/components/chat/providerIconUtils.ts
  • apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx
  • apps/web/src/components/settings/providerDriverMeta.ts
  • apps/web/src/components/settings/ProviderModelsSection.tsx
  • apps/web/src/components/settings/DiagnosticsSettings.tsx
  • apps/web/src/lib/contextWindow.ts
  • apps/web/src/session-logic.ts / session-logic.test.ts

Checks performed

  • Traced every consumer of PROVIDER_ICON_BY_PROVIDER (ProviderInstanceIcon, ModelListRow, ModelPickerSidebar, ProviderModelPicker, Sidebar, ThreadCommandSubtitle, ProviderInstanceCard, ProviderUpdatePrimaryNotification) and compared the icon color contract of the new glyph against the existing provider icons and the mobile ProviderIcon added in the same PR.
  • Verified the "plan" requestKind addition is handled consistently: PendingApproval union, requestKindFromRequestType, derivePendingApprovals, extractWorkLogRequestKind, and both label ternaries in ComposerPendingApprovalPanel (label + aria-label) follow the existing branch style; toolGroupAction falls through to "other", which matches how non-file/non-command kinds already behave.
  • Confirmed the new providerDriverMeta entry (label, icon, badgeLabel: "Early Access", settings schema) and the PROVIDER_OPTIONS entry (available, pickerSidebarBadge: "new") mirror the existing Cursor/Grok entries, and that the model placeholder (claude-opus-5) matches DEFAULT_MODEL_BY_PROVIDER.
  • No new raw controls, no primitive-overriding class strings, no CSS/Tailwind ownership or scroll/mask changes were introduced by this PR in the web scope.

πŸ” Agent activity β€” 22 tool calls

πŸ’­ I'm scanning the changed web files in scope, including the icons, composer approval panel, provider icon utilities, and diagnostics and provider settings components. Now I'm checking Icons.tsx to see whether the icon pattern is consistent -- specifically whether other icons destructure className and props and apply className properly.

1. browse_code β€” apps/web/src/components/Icons.tsx

πŸ’­ I'm noticing DroidIcon uses currentColor while others like CursorIcon and GrokIcon use explicit light/dark fills, so I want to check OpenCodeIcon and ClaudeAI to see if there's a consistent pattern across how these are used in provider cards.

2. git_tools β€” export const (OpenCodeIcon|ClaudeAI|OpenAI)

πŸ’­ I'm noticing DroidIcon uses currentColor so it inherits text color, unlike the branded provider icons (Claude, OpenAI, Cursor, Grok, OpenCode) which bake in their own fixed brand colors β€” that's an inconsistency worth flagging, along with the props spread order that would let fill get overridden. Now I want to check where PROVIDER_ICON_BY_PROVIDER is actually consumed, to see if a container's text color could cause DroidIcon to render in a muted tone while the others stay brand-colored, which would be a real consistency violation.

3. git_tools β€” PROVIDER_ICON_BY_PROVIDER|providerIconUtils

4. browse_code β€” apps/web/src/components/chat/ProviderInstanceIcon.tsx

πŸ’­ I'm noticing the Droid icon uses currentColor while other provider icons (Cursor, Grok, OpenAI) use fixed black/white fills, meaning Droid could appear muted in contexts where its parent text color is muted while others stay full contrast. I want to check which consumers apply muted text classes to confirm this is a real inconsistency.

5. git_tools β€” iconClassName|ProviderInstanceIcon

πŸ’­ I'm noticing that using currentColor for Droid means it'll inherit whatever text color classes consumers apply, like opacity or foreground tints, whereas other provider icons keep fixed brand fills regardless of contextβ€”so I need to check how this plays out across different UI spots like ProviderInstanceCard and Sidebar. This isn't obviously a violation since currentColor is self-theming and matches how TraeIcon behaves, and the Factory Droid brand logo genuinely does shift between black and white by theme. But I want to check ModelListRow specifically, since if it uses a muted-foreground text class, Droid could render visibly dimmer than sibling icons that hardcode their colors.

6. browse_code β€” apps/web/src/components/chat/ModelListRow.tsx

πŸ’­ The Droid icon inherits its color from the parent rather than using a fixed brand color like the others, which seems roughly equivalent rather than a real inconsistency. I want to check the other web changes now, starting with the ComposerPendingApprovalPanel where a nested ternary was added for "plan" β€” I should look at whether there's a shared label mapping elsewhere that this duplicates. I'm noticing the mobile PendingApprovalCard also handles "plan" but maps it differently from other kinds, though that's outside web scope. I want to check if "plan" is consistently handled everywhere in the web app where requestKind is switched on, since missing handling in an icon mapping or another kind-listing spot would be a real inconsistency β€” I'll grep for "file-change" across apps/web to check.

7. git_tools β€” "file-change"

8. browse_code β€” apps/web/src/components/chat/MessagesTimeline.logic.ts

πŸ’­ That grouping under "plan" as "other" seems acceptable. More importantly, I should check where else requestKind is rendered besides ComposerPendingApprovalPanelβ€”searching for other label/icon switches on requestKind that might now be missing "plan" handling since I added it to the union.

9. git_tools β€” requestKind

πŸ’­ I'm double-checking the diff against the current file state β€” it looks like the PR adds a "plan" branch to the approval type logic, checking whether the request kind is "plan" to label it "Plan approval" versus the default "File change approval". I want to confirm exactly where this conditional gets inserted in the code. Let me check whether the ref actually resolves correctly by confirming DroidIcon is present at that tree, since it was seen earlier. That confirms the ref works, so the discrepancy with "unknown" versus "plan" must mean the diff's base differs from head. I should directly inspect session-logic.ts around those line ranges at the head ref to resolve this.

10. browse_code β€” apps/web/src/session-logic.ts

πŸ’­ I realize my earlier grep likely ran against main rather than the PR head, which explains the discrepancy β€” main shows "unknown" for requestKind while the PR head has "plan". I should be explicit about the rev parameter with git_grep or just rely on the view tool to get the correct tree. Since the review scope is merge base to head, this is fine. Now I want to check the actual changed web files at head, starting with ComposerPendingApprovalPanel.

11. browse_code β€” apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx

πŸ’­ This container displays plan text using pre-formatted whitespace with a max height and scroll, which was really designed for commands, so long plan lines would just scroll horizontally rather than wrap. I'm wondering if there's a more plan-specific approval UI elsewhere in the web app that handles this better, so I should check other places that render pending approvals. I want to see where ComposerPendingApprovalPanel is used and whether other approval renderers handle "plan" specially, and also look into ActivePlanState and how MessagesTimeline renders approvals β€” I should grep the codebase for these.

12. git_tools β€” requestKind

πŸ’­ I'm checking how the composer renders pending approvals, particularly the Approve/Deny buttons, and wondering whether plan approvals with long markdown details display well given the whitespace-pre and max-height styling being usedβ€”it seems workable but not ideal, possibly inconsistent with the rest of the UI. Let me also check MessagesTimeline for how approval kind labels get rendered in the timeline, like whether there's a "Command approval requested" string, and search for "File change approval" text across the web codebase.

13. git_tools β€” File change approval|File read approval|Command approval

πŸ’­ Now I'm checking the other changed web files: the diagnostics regex adding droid support (noting grok's pre-existing absence isn't a concern), the placeholder change in ProviderModelsSection, and the new provider driver meta entry with an "Early Access" badge label, comparing it against how other providers are structured.

14. browse_code β€” apps/web/src/components/settings/providerDriverMeta.ts

πŸ’­ I'm checking the PROVIDER_OPTIONS entry for droid, which matches the pattern of cursor and grok with their "new" badges β€” now I want to look at the fuller list to confirm ordering and check if opencode is included too.

15. browse_code β€” apps/web/src/session-logic.ts

πŸ’­ Now I want to check whether formatProviderDisplayName in contextWindow.ts duplicates PROVIDER_DISPLAY_NAMES in contracts, since the PR also updated that with droid β€” let me look at contextWindow.ts.

16. browse_code β€” apps/web/src/lib/contextWindow.ts

πŸ’­ That's consistent with the existing pattern, since Grok is missing pre-existing and defaults to title-casing anyway. Looking at the DroidIcon in Icons.tsx now, I notice the fill is set to currentColor after spreading props, so any fill prop passed in gets overridden, and the className isn't merged with a default class so caller classes apply directly, which seems fine. I'm now considering that the Factory Droid glyph is fairly complex. The glyph's viewBox math checks out roughly centered, with only a minor vertical offset that's likely too small to matter. The more notable point is that DroidIcon uses currentColor while every other provider icon pins a fixed brand color across light and dark modes, meaning DroidIcon will inherit whatever text color the surrounding container sets rather than showing a consistent brand color like the others.

17. browse_code β€” apps/web/src/components/Sidebar.tsx

πŸ’­ I notice the ambient text uses text-muted-foreground, but the brand icons ignore that since they have fixed fills with grayscale and opacity applied. The Droid icon with currentColor ends up rendering at 60% opacity of the muted foreground, making it look noticeably fainter than the Cursor, Grok, OpenAI, and Claude glyphs in the same tooltip, and the subtitle component has a similar opacity mismatch issue. This looks like a real consistency deviation rather than something concrete-regression related β€” every other monochrome provider icon pins its tone explicitly so contextual opacity still applies but the base color matches the brand, while Droid instead inherits ambient text color and ends up dimmer in muted contexts like the sidebar tooltip and subtitle. I'm weighing whether currentColor could be a defensible choice on its own, but since the established sibling pattern in this codebase is explicit tone-pinning, I think it's worth suggesting that DroidIcon follow the same convention as its siblings. I should also check whether mobile's ProviderIcon uses a "mono" token for other monochrome brands too, since that would tell me whether the intent across platforms is consistently theme-following or brand-pinned.

18. browse_code β€” apps/mobile/src/components/ProviderIcon.tsx

πŸ’­ Mobile fixes the Droid mark to a specific near-black/near-white tone regardless of theme, confirming the intended appearance, while web's use of currentColor lets it inherit ambient text colors instead, causing it to diverge from both sibling icons and the mobile implementation. Checking sidebar tooltip and command subtitle contexts, the Droid glyph ends up muted and dimmed while Cursor, Grok, Claude, and OpenAI icons keep their brand tones under the same dimming β€” a real inconsistency worth fixing. I'm considering following the CursorIcon/GrokIcon pattern by hardcoding light/dark fill colors on the SVG rather than relying on currentColor, though the actual path data is long enough that presenting this as a minimal diff is awkward since the path itself doesn't change, only the styling wrapper around it. Actually I'll simplify by matching CursorIcon's style, dropping the fill attribute entirely and just adding the className with cn. Now I'm weighing whether this discrepancy is significant enough to flag as a failing issue, leaning toward reporting it as a single finding with a comment rather than an outright failure. Let me scan the rest of the web diff for other findings before finalizing. Everything else looks fine β€” the regex, placeholder model name, test additions, and the nested ternary all match existing patterns. The one thing worth flagging further is the plan approval panel rendering plan content in a pre-formatted monospace code block, when plan text is really prose/markdown that could wrap oddly. Since that box uses whitespace-pre without wrapping, long plan lines would force horizontal scrolling in a short fixed-height area, which seems like a real usability issue if this is the primary way plans get approved. But I should check whether there's another surface β€” like an approval card in the message timeline β€” that renders the plan properly with full text before this composer panel comes into play. I need to find where the approve/deny buttons actually live to judge whether this compact panel is just a secondary banner or the main interface.

19. git_tools β€” ComposerPendingApprovalPanel

20. browse_code β€” apps/web/src/components/chat/ChatComposer.tsx

πŸ’­ I notice there's already an existing plan-review UX with a proposed plan banner elsewhere in the app, which raises a design question: should Droid's plan approval reuse that surface instead of showing plan text in the compact monospace command strip? That feels like a bigger product decision than pure UI consistency, since the composer panel is a narrow strip not well-suited to displaying full plan content. I decide not to flag this as a hard violation since the existing pattern already shows multi-line prose for other permission types in that same strip. So I'll settle on one concrete finding β€” the DroidIcon color tone β€” and now I need to pinpoint the exact line in the diff to attach my comment to, checking the hunk header to confirm the new SVG block lands on lines 217-222 on the right side of the diff, with line 218 being the right target for the review comment. Weighing whether this should be a failure or neutral verdict, I lean toward failure since the icon's tone genuinely diverges from every sibling provider icon and from the same PR's own mobile icon, which counts as a real consistency regression rather than a nitpick. I also double-check whether the currentColor approach might have been intentional given how iconClassName="size-4 text-foreground/80" is applied elsewhere, but that class is meant for the fallback initials span, not clearly justifying the divergence in this new icon. Now I want to check what color context surrounds ThreadCommandSubtitle to see if the muted tones there would cause the same problem.

21. browse_code β€” apps/web/src/components/ThreadCommandSubtitle.tsx

πŸ’­ I've confirmed the inconsistency: the Droid glyph inherits a muted-foreground color at 70% combined with opacity-70, making it render much fainter than the Claude, Cursor, Grok, and OpenAI glyphs which use their brand color at 70%. This creates a visible mismatch in the command palette subtitle and sidebar tooltip, so I'm drafting a comment explaining the issue. I'm suggesting a fix that pins the DroidIcon to a fixed color that adapts to light/dark mode, similar to what the mobile ProviderIcon already does, while still allowing callers to override via className. Now I'm preparing to post this as a review comment on the PR.

22. modify_pr β€” UI consistency review of the web-scope changes (apps/web/src/**). One finding on the new DroidIcon tone contract; the remaining web changes (provider option/definition entries, plan approval labels, model placeholder, diagnostics regex, display-name mapping) follow the existing patterns.


Agent Credits: 435 credits