Skip to content

fix(composer): keep the slash menu useful past the first line

MacroscopeApp / Macroscope - UI Consistency failed Aug 27, 2026 in 1m 34s

UI Consistency: 1 issue found

1 finding

  • apps/web/src/components/chat/ChatComposer.tsx β€” Slash-menu skill rows are labeled /skill:<name> whenever the trigger kind is slash-command, but after this change a pick with rangeStart > 0 inserts $<name> via formatProviderSkillInsertion. The visible affordance no longer matches the inserted text; the label should derive from the same trigger-position rule as the insertion.

No Tailwind ownership, shared-primitive, theming, scroll/mask, or environment-routing issues were found in the in-scope changes.

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/chat/ChatComposer.tsx (slash-menu assembly at ~1167-1195, skill selection at ~1865-1882)
  • apps/web/src/providerSkillSearch.ts and its test
  • Context (not targeted): packages/client-runtime/src/providerSkills.ts, apps/web/src/components/chat/ComposerCommandMenu.tsx, packages/contracts/src/server.ts, apps/mobile/src/features/threads/ThreadComposer.tsx

Analysis

  • The changes are behavioral (menu filtering and insertion syntax), not stylistic: no new raw controls replacing apps/web/src/components/ui primitives, no new class strings overriding primitive geometry/focus/hit targets, no CSS or theme-variant changes, and no scroll/mask or virtualized-fade changes.
  • searchProviderSkills switching from skill.enabled to isProviderSkillMentionable is consistent with the $ mention semantics and is covered by a focused test.
  • getProviderSkillsForSlashMenu gained a required triggerRangeStart parameter; both web and mobile call sites and the existing tests were updated, so the contract change is complete.
  • The one inconsistency is between the rendered row label and the applied insertion: ComposerCommandMenuItem decides the /skill: prefix from triggerKind alone, while insertion now also depends on trigger.rangeStart. For a / trigger later in the message the row displays /skill:Foo and inserts $foo, which contradicts the PR's own documented rule (docs/user/providers-claude.md) as presented to the user.

Verification notes

No emitted-CSS or visual-evidence gates were applicable (no styling or selector changes). Behavior change in the insertion helper is unit-tested; the label path has no coverage for the mid-message case.


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

πŸ’­ I'm focusing on the ChatComposer.tsx and providerSkillSearch files, since the changes there are logic-only. I want to verify that the call to getProviderSkillsForSlashMenu and the trigger object passed to formatProviderSkillInsertion are structured correctly, especially around rangeStart.

1. git_tools β€” getProviderSkillsForSlashMenu|formatProviderSkillInsertion|composerTrigger.rangeStart

2. browse_code β€” packages/client-runtime/src/providerSkills.ts

πŸ’­ I want to verify ChatComposer's skill selection logic around line 1866, checking whether the trigger's kind and rangeStart are handled correctly, and confirming the "$" mention menu properly filters user-invocation-only skills through searchProviderSkills. I should also check the slash trigger case at rangeStart 0, then look more closely at the code around lines 1830-1890.

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

πŸ’­ This looks logically sound, and there's little styling-related concern since it's behavior logic rather than UI. One thing worth checking: when a skill is picked via slash trigger, it inserts "$name" but the menu label might display "/name," which could be an inconsistent labeling issue worth verifying in how the slash menu renders skill items.

4. git_tools β€” type === "skill"|item.type === "skill"|\$\{.*skill.name|skill\.name

5. browse_code β€” apps/web/src/components/chat/ComposerCommandMenu.tsx

πŸ’­ I'm noticing a labeling mismatch introduced by this diff: when the trigger isn't at the start of the message, the slash menu still shows skills with a "/skill:Name" label based on trigger kind, but the actual insertion now uses a $name mention formatβ€”so the displayed label no longer matches what actually gets inserted. This seems like exactly the kind of label-vs-action inconsistency the check cares about, similar to flagging a renderer whose label points to one thing while its behavior targets another. Since the mismatch stems from the changed line in ChatComposer.tsx rather than the unchanged ComposerCommandMenu.tsx, I should trace where triggerKind gets passed through the composer to confirm this is the right place to flag it. Since findings exist here, I'll lean toward failure. Let me confirm exactly how triggerKind gets passed down and what item.label holds for skill entries in the slash menu, so my comment is precise.

6. git_tools β€” triggerKind=|slashMenuSkills

πŸ’­ I see a real UI consistency issue here: the slash menu labels skill items as "/skill:Name" but selecting them mid-message actually inserts "$name", so the displayed label doesn't match the resulting action. The fix should derive the label prefix from the same logic that determines insertion behavior, showing "$name" whenever the trigger isn't at the start of the message. For where to post the comment, I'm checking which lines are actually part of the diff β€” line 1866 in ChatComposer.tsx (the formatProviderSkillInsertion call) is in the diff, while line 1192 isn't, so I need to post there since GitHub requires inline comments to be on diff lines. Mobile has a similar pattern but that's out of scope for this review. I've also confirmed the rangeStart-based detection logic is consistent with existing tests, and edge cases like an empty menu showing "No matching command" seem acceptable, so I'll post the one comment about the label mismatch.

7. modify_pr β€” One finding: the slash menu's skill label no longer matches what selecting it inserts once the trigger is past the start of the message. Details inline.


Agent Credits: 61 credits