Skip to content

feat(composer): mention GitHub pull requests and issues - #1128

Merged
hdkshingala merged 39 commits into
mainfrom
traycer/lucky-owl
Aug 13, 2026
Merged

hdkshingala merged 39 commits into
mainfrom
traycer/lucky-owl

Conversation

@hdkshingala

@hdkshingala hdkshingala commented Aug 12, 2026 •

Copy link
Copy Markdown
Member

Adds two @-mention sections to the composer — Pull requests and Issues — listing GitHub items from the repos behind the composer's attached folders, plus the protocol contract the host serves them over.

The sections are provider-agnostic by design: Linear issues later merge into the same Issues list rather than growing a third root row.

What you get

  • Two root mention rows (order 32/34, between Git and Task), gated on the composer having roots and on the bound host advertising both mention methods at handshake.
  • Rows ranked by involvement bucket (epic → review-requested → assigned → authored), then recency. Typed queries rank by match strength first, buckets as tiebreak.
  • Local + remote search, deduplicated. Typing filters the cached rows instantly; a debounced GitHub search merges in behind it, deduplicated by (githubHost, owner, repo, number) — the cached row keeps its position and key, the fresh row supplies the payload.
  • A filter popover (State / Involvement / Repository-when->1) with a dot badge when off-default, persisted per (epicId, section) as a view preference only.
  • Click-to-refresh in the step chrome bar, with freshness stamp and the rate-limit ⓘ notice.
  • Reference-shaped queries (#123, org/repo#123, a GitHub URL) are exempt from root's zero-match auto-close and offer Resolve in Pull requests… navigate rows.
  • Preview cards and github_pull_request / github_issue mention chips.

Protocol

Two additive unary methods — mention.githubCatalog and mention.githubSearch — both degrade: { kind: "unsupported" }, so an older host answers cleanly. Artifacts-style unary rather than streams, matching the existing mention surfaces.

Per-provider methods rather than a generic mention.catalog(provider) is deliberate: each method carries its own capability answer through the handshake, so "does this host serve GitHub mentions?" needs no second in-band mechanism, and a future mention.linearCatalog gets an old host's clean "no" for free. The scopes also differ in kind — GitHub's is folders → git remotes → repos, Linear's is a workspace/team selection with its own auth and its own rate budget.

Rides along

  • The Artifacts mention refresh button was a no-op — setStep(current) early-returns in the picker store, so the exposed refetch() was never called. The new per-step chrome contract routes it to the real one.
  • Provider submenu height is bounded again. feat(gui-app): fuzzy-rank the slash-command menu + step-aware picker height #1045 gave provider steps a bare max-h-[70vh]; with a 25-row result limit that is a menu that takes over the window rather than one that grew, and it affected Files just as much as the new sections. Now max-h-[min(70vh,24rem)], matching the min(70vh, N rem) idiom every other tall surface here already uses. Root and typed-query lists are unchanged, and short windows still resolve to the 70vh term.

Notes for review

  • Refresh is click-only, deliberately. ⌘R was the original design; Electron's window Reload accelerator fires in the main process before the renderer sees the key, so it could never have worked. Enter is inert inside the filter popover as a consequence of adopting Radix RadioGroup (WAI-ARIA radios activate on Space) — arrows and Space drive it.
  • keepPreviousData is lane-scoped on the search, not blanket. It is what stops typing from blanking the remote hits on each keystroke, so it is kept across a query or filter change - both of which the merged list corrects for downstream. It is dropped across a host, epic, roots or section change, which nothing downstream can correct: those rows are merged into the new scope and stay selectable. Both arms are also filtered by a kind predicate at point-of-use, so switching sections cannot insert a pull-request chip from the Issues list.
  • Catalog rows live only in TanStack Query, keyed per (hostId, epicId, folders, section). A review round pointed out the session row store was a second copy of server data; it is deleted — root search ranks the cache-only hydration reads' answers directly, and a long gcTime (not a parallel store) carries session warmth across menu opens, so a drifted scope re-keys instead of being served stale rows.

Requires the matching host change (internal traycer-host PR) — the sections stay hidden against a host without these methods.

Follow-ups in this branch

  • Filter changes no longer flash the list away. The catalog only sweeps the default view, so a funnel it cannot answer (State: Merged over a cache of open PRs) excluded every cached row the instant it was selected, leaving a lone Searching GitHub… row for the length of a round trip. The rows on screen now hold until the answer lands, keyed per (scope, section, query) so it is only ever a filter swap — a new query still collapses, and a settled empty answer always wins over the hold.

Deliberately not in this PR

Six review findings are real, confirmed against the code, and left for their own change rather than half-wired here. Each carries its reasoning on its thread; summarised so they are not read as missed.

Their threads are resolved, and none of them is fixed. This repo requires every review thread resolved before a PR can merge, so resolution on these carries no claim that the code changed — this table is the record.

Finding Why it is its own change
githubMentionToken omits githubHost, so one scope spanning two GitHub hosts collides on owner/repo#number The token is the serialized mention form in the message text, so changing identity means the serializer, parser, dedup key and their contract tests move together
A rejected GitHub catalog read does not reach mentionNoMatchDismissVerdict, so an incomplete search can auto-close as an authoritative zero-match Needs the catalog error threaded from the sections hook into use-mention-items, plus a decision on hold-open vs. inline error row
The search projection drops searchQuery.error, so a rejected search reads as a completed empty one The automatic lane, under the rule the catalog's two refresh lanes and the Artifacts refresh button now both follow: a user-initiated action reports a rejection, an automatic one degrades in place. A rejected search leaves the local rows on screen, so when this is reported it belongs in the existing notice vocabulary rather than a per-keystroke toast
Typed searches do not share a latest-wins lane, so a superseded query keeps spending rate-limit budget The coordinator keys lanes by params; a per-method "identity excluding these params" is a host-rpc-policy shape change, and getting it wrong silently collapses lanes meant to be distinct
sourceStatus: "error" and "partial" get no banner, so a section can present an incomplete read as an authoritative empty one The third of three questions about what the section says when a read came back incomplete, alongside the two rows above. Answering them one thread at a time is how you end up with three different degraded states
Rows held across a filter swap stay selectable while the new filter's search runs Withholding restores the collapsing frame the hold exists to remove; disabling them needs a disabled-row state the mention menu does not have

The last one is bounded by the row being a real, resolvable pull request the user can see and clicked - the list disagrees with the funnel for one round-trip, nothing broken is committed.

Two new @-mention sections list the viewer's pull requests and issues
from the GitHub repos of the composer's attached folders, ranked with
the epic's own PRs first, then review-requested / assigned / authored.
Typing filters the cached rows instantly while a debounced GitHub
search merges in, deduplicated by GitHub identity so a cached row keeps
its reference. A funnel popover filters by state, involvement and
repository; refresh is a click, since the window's Reload owns Cmd-R.

The wire gains mention.githubCatalog@1.0 and mention.githubSearch@1.0,
both registered as additive degrade: unsupported methods, plus a
github_pull_request context type beside the existing github_issue.
Committing a row inserts a chip that serializes to org/repo#number and
a URL, so the agent resolves the reference with its own tools rather
than receiving inlined content that is stale by the time it is read.

A paused limiter or a suppressed budget floor renders the cached rows
with an info notice and a countdown rather than an error, and a missing
or signed-out gh CLI raises a banner instead - waiting does not help
there, so the copy has to carry it.

Also fixes the Artifacts mention refresh, which re-set the step it was
already on and therefore refetched nothing.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Summary by CodeRabbit

  • New Features

    • Added GitHub pull request and issue mentions to the composer.
    • Added search, repository, state, and involvement filters, previews, icons, and refresh controls.
    • GitHub mention filters now persist between sessions.
    • Added support for serializing and displaying GitHub references and URLs.
    • Improved composer focus restoration and loading, empty, and unavailable-source states.
  • Bug Fixes

    • Improved mention dismissal and reference matching for GitHub queries.
    • Corrected source notices to distinguish pull requests from issues.

Walkthrough

The PR adds GitHub pull-request and issue mentions to the composer. It adds host contracts, catalog and search hooks, filtering, persistence, picker providers, serialization, previews, focus restoration, and step-specific menu controls. It also adds regression coverage.

Changes

GitHub composer mentions

Layer / File(s) Summary
Contracts, data models, and serialization
protocol/src/host/*, protocol/src/common/*, clients/gui-app/src/lib/composer/types.ts, clients/gui-app/src/lib/composer/tiptap-json-content.ts
Adds GitHub catalog and search RPCs, schemas, attachment types, token formats, previews, and serializer support for pull requests and issues.
Rows, providers, catalog, and search state
clients/gui-app/src/lib/composer/mentions/*, clients/gui-app/src/hooks/composer/*, clients/gui-app/src/stores/composer/*
Adds section-aware filtering, reference parsing, ranking, display data, cached catalogs, debounced searches, refresh handling, and scoped filter persistence.
Picker orchestration and composer UI
clients/gui-app/src/components/chat/composer/picker/*, clients/gui-app/src/components/chat/composer/menu/*, clients/gui-app/src/components/chat/composer/nodes/*
Publishes step chrome, preserves reference queries, restores editor focus, and renders filters, freshness, banners, status rows, prefixes, previews, and GitHub icons.
Existing PR notice and RPC integration
clients/gui-app/src/components/epic-canvas/pr/*, clients/gui-app/src/lib/pr/*, clients/gui-app/src/lib/host-rpc-policy/*, clients/gui-app/src/lib/query-keys/*, clients/gui-app/src/lib/persist/*
Makes PR source notices subject-aware, centralizes review-decision labels, and adds query, persistence, and RPC policy entries.
Validation and regression coverage
clients/gui-app/src/**/__tests__/*, protocol/src/**/__tests__/*
Adds coverage for GitHub contracts, serialization, row behavior, catalog writes, search enablement, filters, dismissal, providers, picker chrome, refresh behavior, and focus-state fixtures.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Composer
  participant Picker
  participant Catalog
  participant Search
  participant Host
  Composer->>Picker: Open GitHub mention step
  Picker->>Catalog: Read cached catalog
  Catalog->>Host: Call mention.githubCatalog
  Picker->>Search: Search typed or reference query
  Search->>Host: Call mention.githubSearch
  Picker->>Composer: Publish rows and step chrome
  Composer->>Picker: Apply filter or restore focus
Loading

Poem

A rabbit checks each issue row,
Pull requests sort and filters glow.
Fresh searches cross the host with care,
The picker adds chrome everywhere.
The editor finds its focus bright,
GitHub mentions hop in sight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the primary change: adding GitHub pull-request and issue mentions to the composer.
Description check ✅ Passed The description directly explains the GitHub mention sections, protocol methods, search behavior, refresh changes, and related scope.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch traycer/lucky-owl

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
clients/gui-app/src/components/chat/composer/__tests__/composer-picker-store.test.ts (1)

74-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for setStepChrome.

This suite owns the picker store's guard behavior. The PR adds setStepChrome with five new branches: closed store, non-mention kind, stale sessionId, stale step id, and the value-equal dedupe through sameMentionStepChrome. None of those branches is exercised here.

The stale-session and stale-step guards are the same class of defect this suite already tests for setItems (lines 173 and 265 set up superseding sessions). Add the matching cases for setStepChrome, plus one that asserts stepChrome returns to null after setStep.

Do you want me to generate these test cases?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@clients/gui-app/src/components/chat/composer/__tests__/composer-picker-store.test.ts`
around lines 74 - 91, Add test coverage in the composer picker store suite for
setStepChrome: verify no update when the store is closed, the picker kind is
non-mention, the sessionId is stale, or the step id is stale; verify
sameMentionStepChrome value-equal updates are deduplicated; and verify setStep
resets stepChrome to null. Reuse the existing setItems superseding-session setup
and the open helper for consistent fixtures.
clients/gui-app/src/components/chat/composer/menu/composer-menu.tsx (1)

339-355: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Move the chrome banner out of the role="listbox" container.

Line 342 sets role="listbox" on the scroll container. Line 355 renders MentionStepChromeBanner as a direct child of that container. The banner carries role="status". ARIA requires a listbox to own only option and group children. A status live region inside a listbox is not a valid owned element, so assistive technology can drop the "GitHub CLI unavailable" announcement or map it to the wrong context.

Render the banner as a sibling above the listbox. The banner is static chrome, not a row, so it does not need to scroll with the results.

♿ Proposed fix to move the banner outside the listbox
+        {chrome === null ? null : <MentionStepChromeBanner chrome={chrome} />}
         <div
           ref={listRef}
           id={menuId}
           role="listbox"
           // The first menu (mention categories, slash commands) stays compact:
           // tall enough for the full category roster without a scrollbar,
           // while typed-query results scroll behind the cap. A provider
           // submenu mirrors a bounded roster (terminals, git refs), so it
           // grows to the viewport cap instead of scrolling.
           className={cn(
             "overflow-y-auto py-1",
             kind === "mention" && step.kind === "provider"
               ? "max-h-[70vh]"
               : "max-h-[min(50vh,16rem)]",
           )}
         >
-          {chrome === null ? null : <MentionStepChromeBanner chrome={chrome} />}
           <ComposerMenuBody
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@clients/gui-app/src/components/chat/composer/menu/composer-menu.tsx` around
lines 339 - 355, Move MentionStepChromeBanner out of the div identified by
listRef, menuId, and role="listbox", rendering it as a sibling immediately above
the listbox. Keep the banner’s existing chrome-null conditional and ensure the
listbox contains only its option/group result content while the banner remains
visible outside the scrolling container.
🔇 Additional comments (71)
clients/gui-app/src/components/chat/composer/editor/extensions/attribute-helpers.ts (1)

55-62: LGTM!

clients/gui-app/src/components/epic-canvas/pr/pr-source-notice.tsx (1)

6-9: LGTM!

Also applies to: 21-33

clients/gui-app/src/components/epic-canvas/pr/__tests__/pr-source-notice.test.tsx (1)

6-6: LGTM!

Also applies to: 15-27, 39-52, 72-76, 98-101, 136-140

protocol/src/common/__tests__/json-content-serializer-github-mentions.test.ts (1)

1-114: LGTM!

clients/gui-app/src/lib/query-keys/index.ts (1)

53-53: LGTM!

clients/gui-app/src/lib/persist/index.ts (1)

12-12: LGTM!

clients/gui-app/src/lib/persist/keys.ts (1)

62-68: LGTM!

Also applies to: 194-194, 263-267

clients/gui-app/src/stores/composer/github-mention-filter-store.ts (1)

1-143: LGTM!

clients/gui-app/src/stores/composer/__tests__/github-mention-filter-store.test.ts (1)

1-262: LGTM!

clients/gui-app/src/hooks/composer/__tests__/use-epic-mention-entries.test.tsx (1)

89-146: LGTM!

clients/gui-app/src/lib/composer/segments.ts (1)

13-17: LGTM!

protocol/src/common/json-content-serializer.ts (1)

8-8: LGTM!

Also applies to: 249-249, 292-293, 409-418, 515-528

protocol/src/host/mention-schemas.ts (1)

1-157: LGTM!

clients/gui-app/src/components/epic-canvas/pr/pr-detail-card.tsx (1)

26-26: LGTM!

clients/gui-app/src/lib/pr/pr-source-notice-message.ts (1)

18-39: LGTM!

Also applies to: 48-53

clients/gui-app/src/lib/query-keys/mention-query-keys.ts (1)

1-23: LGTM!

clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts (4)

1-65: LGTM!

Also applies to: 100-139, 230-261


204-216: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Report rejected manual refreshes.

A thrown host RPC error does not produce a notice. This empty catch therefore gives no feedback when a manual refresh fails. Add toastFromHostError in the mutation onError path unless this hook exposes an inline error surface.

As per coding guidelines, “Use toastFromHostError, toastFromAuthError, or toastFromRunnerError in onError, except for inline-error surfaces.”

⛔ Skipped due to learnings
Learnt from: hdkshingala
Repo: traycerai/traycer PR: 889
File: clients/gui-app/src/hooks/local-store/use-rebind-local-store-mutation.ts:0-0
Timestamp: 2026-08-11T12:49:03.942Z
Learning: For `host.rebindLocalStore` in `clients/gui-app/src/hooks/local-store/use-rebind-local-store-mutation.ts`, a successful `rebound` response republishes the host durability store. Invalidate the complete `hostQueryKeys.scope(hostId)` rather than a hand-picked set of queries, because all reads that use that store can become stale. Capture `hostId` in `onMutate` and use the captured value in `onSuccess` to avoid invalidating a newly active host after a host swap.
Learnt from: tanveergill
Repo: traycerai/traycer PR: 951
File: clients/gui-app/src/lib/host-rpc-policy/host-method-policy-table.ts:741-754
Timestamp: 2026-08-11T09:21:07.652Z
Learning: In `clients/gui-app/src/lib/host-rpc-policy/host-method-policy-table.ts`, `epic.listChatPublicationTargets` intentionally does not poll or invalidate on fork lifecycle changes. A fork can change the mapping once, and a temporary duplicate-looking cloud sidebar row is an accepted bounded freshness trade-off. The mapping refetches on mount, focus, and host/auth transitions. Do not report this as a data-integrity issue unless the lifecycle-observer design changes.
Learnt from: hdkshingala
Repo: traycerai/traycer PR: 1103
File: clients/gui-app/src/components/settings/panels/provider-pack-version-manager-presence.ts:0-0
Timestamp: 2026-08-12T00:53:09.461Z
Learning: In `clients/gui-app/src/components/settings/panels/provider-pack-version-manager-presence.ts`, mutation outcome delivery must use a per-panel identity token, not global panel presence. Provider pack version mutation hooks must capture the initiating token in `onMutate` and show a fallback toast if `versionManagerPanelIsMounted(token)` is false when the outcome arrives. Panels must register only when `hostId !== null` and version-manager method support is `true`.
Learnt from: tanveergill
Repo: traycerai/traycer PR: 951
File: clients/gui-app/src/components/layout/__tests__/tab-strip-app-route.test.tsx:68-74
Timestamp: 2026-08-11T09:20:45.595Z
Learning: In `clients/gui-app` focused component tests, a hook mock may return only the fields consumed by the rendered code path. The coding guideline to return the full `UseQueryResult` or `UseMutationResult` applies to production hooks, not to narrow test doubles. Do not require a disabled real TanStack Query solely to emulate unused status and error fields when the test does not validate query lifecycle behavior.
Learnt from: tanveergill
Repo: traycerai/traycer PR: 1102
File: protocol/src/host/usage-analytics/schemas.ts:219-223
Timestamp: 2026-08-11T05:31:30.952Z
Learning: For `host.usage.summary`, the host implementation is intended to reject an unscoped `window: "epic"` request before data access. `host-usage-summary-resolver.ts` calls `assertValidEpicWindowRequest(request)` for the `"epic"` window and maps `InvalidUsageSummaryRequestError` to `InvalidArgumentError`; `usage-summary-local-reader.ts` performs the same validation. The expected error message is `window "epic" requires an epicId or chatId filter`.
Learnt from: tanveergill
Repo: traycerai/traycer PR: 1102
File: clients/gui-app/src/components/chat/__tests__/chat-usage-dialog.test.tsx:0-0
Timestamp: 2026-08-11T05:30:59.515Z
Learning: In `clients/gui-app/src/components/chat/chat-usage-dialog.tsx`, `ChatUsageDialog` is mounted once for the whole session rather than once per tab. Its usage-summary query must remain gated by a non-null dialog target. Rendering no dialog does not prove that no `host.usage.summary` request occurs; tests should verify zero handler invocations with a null target and request activity only after the dialog target opens.
Learnt from: tanveergill
Repo: traycerai/traycer PR: 1102
File: clients/gui-app/src/components/epic-canvas/panels/epic-usage-entry-point.tsx:44-46
Timestamp: 2026-08-11T05:31:01.771Z
Learning: In `clients/gui-app`, `EpicSessionProvider` binds Epic sessions to `useReactiveActiveHostId()` and re-acquires the session when the active host changes. `EpicShell` status-row components, including `clients/gui-app/src/components/epic-canvas/panels/epic-usage-entry-point.tsx`, are outside `TabHostProvider` scope and must use the app-wide host binding. Do not introduce a tab-bound host only in a status-row leaf component without redesigning the existing Epic session binding.
Learnt from: tanveergill
Repo: traycerai/traycer PR: 1102
File: protocol/src/host/usage-analytics/schemas.ts:176-176
Timestamp: 2026-08-10T22:02:30.958Z
Learning: For the `host.usage.summary` RPC, the host validates request timezone values with `assertValidIanaTimeZone` and rejects invalid values with a typed error. In `clients/gui-app/src/lib/usage-analytics/day-window.ts`, `lastNCalendarDays` must tolerate an unknown response timezone by catching `Intl.DateTimeFormat` construction errors and falling back to UTC.

Source: Coding guidelines


66-99: 📐 Maintainability & Code Quality

Inspect the hook implementations and call sites to determine whether they must expose complete TanStack Query and mutation results.


179-182: 🎯 Functional Correctness

Guard against placeholder data before the automatic refresh.

If this query uses placeholderData: keepPreviousData, return when catalogQuery.isPlaceholderData is true. Otherwise, data.stale can describe the previous scope and trigger an "auto" request for the new scope.

clients/gui-app/src/hooks/composer/use-github-mention-search.ts (2)

1-54: LGTM!

Also applies to: 62-88, 101-130


89-90: 🎯 Functional Correctness

Check the keepPreviousData behavior before merging. If prior-query rows remain visible for the current query, remove placeholderData: keepPreviousData.

clients/gui-app/src/stores/composer/github-mention-catalog-store.ts (1)

19-21: 📐 Maintainability & Code Quality

Confirm catalog data ownership before changing the store.

Trace useGithubMentionCatalog and useGithubMentionSearch to determine whether GithubMentionRow and GithubMentionRepository are host response data. Move repositoriesByScope and rowsByKey to TanStack Query only if its cache provides the required cross-observer lifetime; otherwise retain the Zustand state and tests.

clients/gui-app/src/lib/composer/tiptap-json-content.ts (1)

8-9: LGTM!

Also applies to: 172-192, 232-234, 748-795, 879-887

clients/gui-app/src/lib/composer/types.ts (1)

14-26: LGTM!

Also applies to: 198-234, 263-282

protocol/src/host/mention-contracts.ts (1)

1-32: LGTM!

protocol/src/host/registry.ts (1)

403-406: LGTM!

Also applies to: 6132-6159

protocol/src/host/index.ts (1)

11-12: LGTM!

protocol/src/host/__tests__/mention-contracts.test.ts (1)

1-396: LGTM!

clients/gui-app/src/lib/pr/pr-review-decision-label.ts (1)

1-17: LGTM!

clients/gui-app/src/lib/host-rpc-policy/host-method-policy-table.ts (1)

756-764: LGTM!

clients/gui-app/src/hooks/composer/use-epic-mention-entries.ts (1)

21-31: LGTM!

Also applies to: 58-61

clients/gui-app/src/hooks/composer/__tests__/use-github-mention-search.test.tsx (1)

1-106: LGTM!

clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts (1)

18-196: LGTM!

Also applies to: 198-322, 324-387, 389-475

clients/gui-app/src/lib/composer/mentions/github-mention-chrome.ts (1)

32-59: LGTM!

Also applies to: 61-124

clients/gui-app/src/lib/composer/mentions/index.ts (1)

2-4: LGTM!

Also applies to: 7-7

clients/gui-app/src/lib/composer/mentions/__tests__/github-mention-rows.test.ts (1)

16-62: LGTM!

Also applies to: 64-175, 177-263, 265-319, 321-347, 468-554, 556-576

clients/gui-app/src/lib/composer/mentions/__tests__/github-mention-chrome.test.ts (1)

13-47: LGTM!

Also applies to: 49-108, 110-146, 148-193

clients/gui-app/src/components/chat/composer/picker/use-github-mention-sections.ts (1)

86-95: LGTM!

Also applies to: 102-115, 117-130, 146-171, 187-212, 214-250, 257-292, 294-315, 317-349, 351-385

clients/gui-app/src/components/chat/composer/picker/use-mention-items.ts (2)

585-589: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

⚠️ Unverified finding
Sandbox verification was unavailable.

Consider useEffectEvent in place of the ref-and-effect pair.

The ref plus the sync effect reproduce what useEffectEvent provides: a stable identity that always calls the latest closure. React 19.2 ships useEffectEvent. If the installed React version has it, this collapses to one line and removes the window in which the ref still holds the previous render's refetch.

♻️ Proposed refactor
-  // `refetch` is rebuilt every render (it closes over the current query array),
-  // so publishing it directly would change the chrome's identity on every pass
-  // and republish forever. The ref holds ONE stable closure over the latest.
-  const artifactRefetchRef = useRef(artifactRefetch);
-  useEffect(() => {
-    artifactRefetchRef.current = artifactRefetch;
-  }, [artifactRefetch]);
-  const refreshArtifacts = useCallback(() => artifactRefetchRef.current(), []);
+  // `refetch` is rebuilt every render (it closes over the current query array),
+  // so publishing it directly would change the chrome's identity on every pass
+  // and republish forever. `useEffectEvent` keeps ONE stable identity over the
+  // latest closure.
+  const refreshArtifacts = useEffectEvent(() => artifactRefetch());

Keep the current code if the project pins React below 19.2.


29-42: LGTM!

Also applies to: 66-68, 232-256, 271-273, 287-295, 325-325, 394-403, 430-470, 521-521, 543-583, 591-611, 628-628, 660-660

clients/gui-app/src/components/chat/composer/picker/__tests__/use-github-mention-sections-catalog-write.test.tsx (1)

82-132: LGTM!

Also applies to: 136-162, 164-213, 215-241, 243-282

clients/gui-app/src/components/chat/composer/menu/mention-menu-item.tsx (1)

16-22: LGTM!

clients/gui-app/src/components/chat/composer/menu/mention-preview-panel.tsx (1)

2-2: LGTM!

Also applies to: 210-234

clients/gui-app/src/components/chat/composer/nodes/composer-mention-decorator.tsx (2)

68-83: 🎯 Functional Correctness | ⚡ Quick win

⚠️ Unverified finding
Sandbox verification was unavailable.

Verify the two contextType literals against the protocol enum.

These branches compare against "github_pull_request" and "github_issue". The neighbouring literals use kebab-case, such as "terminal-agent". If the protocol enum values are kebab-case, TypeScript narrows mention.contextType to never inside these branches, the code still compiles, and the decorator silently falls through to the GitBranch default icon.


1-8: LGTM!

clients/gui-app/src/lib/composer/mentions/github-mention-display.tsx (1)

1-243: LGTM!

clients/gui-app/src/lib/composer/mentions/step-chrome.ts (1)

1-235: LGTM!

clients/gui-app/src/lib/composer/mentions/mention-dismissal.ts (1)

17-33: LGTM!

Also applies to: 56-65, 83-83

clients/gui-app/src/lib/composer/mentions/providers.tsx (1)

16-37: LGTM!

Also applies to: 74-75, 102-110, 227-254, 311-321, 831-840, 842-1007, 1102-1106, 1121-1122, 1148-1148, 1161-1161, 1177-1177

clients/gui-app/src/lib/composer/mentions/root-search-ranking.ts (1)

25-38: LGTM!

clients/gui-app/src/lib/composer/mentions/__tests__/mention-dismissal.test.ts (1)

10-47: LGTM!

Also applies to: 62-62, 117-149

clients/gui-app/src/lib/composer/mentions/__tests__/providers.test.tsx (1)

8-12: LGTM!

Also applies to: 33-37, 135-144, 175-184, 196-196, 234-234

clients/gui-app/src/lib/composer/mentions/__tests__/root-search-ranking.test.ts (1)

17-17: LGTM!

clients/gui-app/src/lib/composer/mentions/__tests__/step-chrome.test.ts (1)

1-128: LGTM!

clients/gui-app/src/components/chat/composer/picker/suggestion-render.ts (1)

5-5: LGTM!

Also applies to: 75-85, 138-144, 154-157, 171-174

clients/gui-app/src/components/epic-canvas/pr/pr-detail-header.tsx (1)

126-126: LGTM!

clients/gui-app/src/components/epic-canvas/pr/pr-panel-actions.tsx (1)

82-84: LGTM!

clients/gui-app/src/components/chat/composer/picker/composer-picker-store.ts (1)

143-151: LGTM!

Also applies to: 163-174, 221-221, 243-254, 298-301, 383-383, 404-407, 464-481

clients/gui-app/src/components/chat/composer/picker/__tests__/use-slash-items-session.test.tsx (1)

60-60: LGTM!

clients/gui-app/src/components/chat/composer/menu/__tests__/github-mention-filter-popover.test.tsx (1)

31-38: LGTM!

Also applies to: 47-99, 101-135, 137-158, 160-227, 229-283

clients/gui-app/src/components/chat/composer/menu/composer-menu.tsx (1)

29-39: LGTM!

Also applies to: 79-79, 92-92, 104-104, 125-125, 146-146, 162-162, 177-177, 218-225, 271-273, 318-337, 364-364, 457-463, 475-475, 512-523, 577-586

clients/gui-app/src/components/chat/composer/menu/github-mention-filter-popover.tsx (1)

1-57: LGTM!

Also applies to: 59-79, 80-172, 174-207, 209-278, 280-333

clients/gui-app/src/components/chat/composer/menu/mention-step-chrome-bar.tsx (2)

37-44: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

⚠️ Unverified finding
Sandbox verification was unavailable.

Do not infer the notice subject from chrome.filter.

Line 41 derives the notice subject from chrome.filter?.section. filter and notice are independently nullable on MentionStepChrome. If an Issues step publishes a notice while filter is null, the optional chain yields undefined and the subject falls back to "pull-requests". The hint then describes the wrong entity on the Issues section.

Carry the section explicitly on the chrome or on the notice, and read it directly instead of reading a sibling field.

Run the following script to check whether any step publishes notice without filter:


1-36: LGTM!

Also applies to: 45-57, 59-96, 98-140, 142-164, 166-180

clients/gui-app/src/components/chat/composer/menu/__tests__/composer-menu-disabled-row.test.tsx (1)

68-68: LGTM!

clients/gui-app/src/components/chat/composer/menu/__tests__/composer-menu.test.tsx (1)

39-39: LGTM!

clients/gui-app/src/components/chat/composer/__tests__/composer-picker-store.test.ts (1)

33-33: LGTM!

Also applies to: 88-88, 106-106, 128-128, 140-140, 173-173, 265-265, 444-444, 577-577

clients/gui-app/src/components/chat/composer/__tests__/chat-list-keymap-submit-source.test.tsx (1)

79-79: LGTM!

Also applies to: 115-115, 165-165, 209-209, 237-237, 265-265, 328-328, 371-371

clients/gui-app/src/components/chat/composer/__tests__/composer-mention-dismissal-flow.test.tsx (1)

83-83: LGTM!

clients/gui-app/src/components/chat/composer/__tests__/composer-mention-flow.test.tsx (1)

133-133: LGTM!

Also applies to: 173-173, 206-206

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@clients/gui-app/src/components/chat/composer/picker/__tests__/use-mention-items.test.ts`:
- Line 671: Add a test in the mention no-match dismissal suite using the
existing settledNoMatch fixture with referenceQuery set to true, and assert that
mentionNoMatchDismissVerdict returns false, covering the reference-shaped
zero-match case while preserving existing assertions.

In
`@clients/gui-app/src/components/chat/composer/picker/use-github-mention-sections.ts`:
- Around line 179-185: Update the repositories effect in
useGithubMentionSections to depend on each catalog’s scopeResolved,
isPlaceholder, and repositories fields instead of whole catalog objects,
preserving the existing selection and setRepositories behavior. In
clients/gui-app/src/components/chat/composer/picker/__tests__/use-github-mention-sections-catalog-write.test.tsx
lines 39-65, have the mocked useGithubMentionCatalog return shallow copies of
catalogMocks.pullRequests and catalogMocks.issues to match production render
identity.

In `@clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts`:
- Around line 140-160: Update the mention.githubCatalog refreshMutation
lifecycle to capture the current hostId and cache-only key in onMutate, then
return that scope as mutation context. Make onSuccess apply the response using
the captured cache key and ensure onError uses the same context, preventing
pending mutations from writing to a newer host’s cache.

In
`@clients/gui-app/src/lib/composer/mentions/__tests__/github-mention-rows.test.ts`:
- Around line 349-388: Add a “filter coercion” test suite alongside
filterGithubMentionRows covering asPullRequestMentionFilter with an issue-shaped
involvement and asIssueMentionFilter with a PR-shaped involvement, asserting
each returns its corresponding default filter. Also verify coercion preserves
the supplied repository object, using the existing GithubMentionFilter and
default-filter symbols.

---

Outside diff comments:
In
`@clients/gui-app/src/components/chat/composer/__tests__/composer-picker-store.test.ts`:
- Around line 74-91: Add test coverage in the composer picker store suite for
setStepChrome: verify no update when the store is closed, the picker kind is
non-mention, the sessionId is stale, or the step id is stale; verify
sameMentionStepChrome value-equal updates are deduplicated; and verify setStep
resets stepChrome to null. Reuse the existing setItems superseding-session setup
and the open helper for consistent fixtures.

In `@clients/gui-app/src/components/chat/composer/menu/composer-menu.tsx`:
- Around line 339-355: Move MentionStepChromeBanner out of the div identified by
listRef, menuId, and role="listbox", rendering it as a sibling immediately above
the listbox. Keep the banner’s existing chrome-null conditional and ensure the
listbox contains only its option/group result content while the banner remains
visible outside the scrolling container.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 723a71d1-0ba4-4bb6-b407-c478cfffcf4f

📥 Commits

Reviewing files that changed from the base of the PR and between 36f0795 and cc2aee3.

📒 Files selected for processing (66)
  • clients/gui-app/src/components/chat/composer/__tests__/chat-list-keymap-submit-source.test.tsx
  • clients/gui-app/src/components/chat/composer/__tests__/composer-mention-dismissal-flow.test.tsx
  • clients/gui-app/src/components/chat/composer/__tests__/composer-mention-flow.test.tsx
  • clients/gui-app/src/components/chat/composer/__tests__/composer-picker-store.test.ts
  • clients/gui-app/src/components/chat/composer/editor/extensions/attribute-helpers.ts
  • clients/gui-app/src/components/chat/composer/menu/__tests__/composer-menu-disabled-row.test.tsx
  • clients/gui-app/src/components/chat/composer/menu/__tests__/composer-menu.test.tsx
  • clients/gui-app/src/components/chat/composer/menu/__tests__/github-mention-filter-popover.test.tsx
  • clients/gui-app/src/components/chat/composer/menu/composer-menu.tsx
  • clients/gui-app/src/components/chat/composer/menu/github-mention-filter-popover.tsx
  • clients/gui-app/src/components/chat/composer/menu/mention-menu-item.tsx
  • clients/gui-app/src/components/chat/composer/menu/mention-preview-panel.tsx
  • clients/gui-app/src/components/chat/composer/menu/mention-step-chrome-bar.tsx
  • clients/gui-app/src/components/chat/composer/nodes/composer-mention-decorator.tsx
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-github-mention-sections-catalog-write.test.tsx
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-mention-items.test.ts
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-slash-items-session.test.tsx
  • clients/gui-app/src/components/chat/composer/picker/composer-picker-store.ts
  • clients/gui-app/src/components/chat/composer/picker/suggestion-render.ts
  • clients/gui-app/src/components/chat/composer/picker/use-github-mention-sections.ts
  • clients/gui-app/src/components/chat/composer/picker/use-mention-items.ts
  • clients/gui-app/src/components/epic-canvas/pr/__tests__/pr-source-notice.test.tsx
  • clients/gui-app/src/components/epic-canvas/pr/pr-detail-card.tsx
  • clients/gui-app/src/components/epic-canvas/pr/pr-detail-header.tsx
  • clients/gui-app/src/components/epic-canvas/pr/pr-panel-actions.tsx
  • clients/gui-app/src/components/epic-canvas/pr/pr-source-notice.tsx
  • clients/gui-app/src/hooks/composer/__tests__/use-epic-mention-entries.test.tsx
  • clients/gui-app/src/hooks/composer/__tests__/use-github-mention-search.test.tsx
  • clients/gui-app/src/hooks/composer/use-epic-mention-entries.ts
  • clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts
  • clients/gui-app/src/hooks/composer/use-github-mention-search.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/github-mention-chrome.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/github-mention-rows.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/mention-dismissal.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/providers.test.tsx
  • clients/gui-app/src/lib/composer/mentions/__tests__/root-search-ranking.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/step-chrome.test.ts
  • clients/gui-app/src/lib/composer/mentions/github-mention-chrome.ts
  • clients/gui-app/src/lib/composer/mentions/github-mention-display.tsx
  • clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts
  • clients/gui-app/src/lib/composer/mentions/index.ts
  • clients/gui-app/src/lib/composer/mentions/mention-dismissal.ts
  • clients/gui-app/src/lib/composer/mentions/providers.tsx
  • clients/gui-app/src/lib/composer/mentions/root-search-ranking.ts
  • clients/gui-app/src/lib/composer/mentions/step-chrome.ts
  • clients/gui-app/src/lib/composer/segments.ts
  • clients/gui-app/src/lib/composer/tiptap-json-content.ts
  • clients/gui-app/src/lib/composer/types.ts
  • clients/gui-app/src/lib/host-rpc-policy/host-method-policy-table.ts
  • clients/gui-app/src/lib/persist/index.ts
  • clients/gui-app/src/lib/persist/keys.ts
  • clients/gui-app/src/lib/pr/pr-review-decision-label.ts
  • clients/gui-app/src/lib/pr/pr-source-notice-message.ts
  • clients/gui-app/src/lib/query-keys/index.ts
  • clients/gui-app/src/lib/query-keys/mention-query-keys.ts
  • clients/gui-app/src/stores/composer/__tests__/github-mention-catalog-store.test.ts
  • clients/gui-app/src/stores/composer/__tests__/github-mention-filter-store.test.ts
  • clients/gui-app/src/stores/composer/github-mention-catalog-store.ts
  • clients/gui-app/src/stores/composer/github-mention-filter-store.ts
  • protocol/src/common/__tests__/json-content-serializer-github-mentions.test.ts
  • protocol/src/common/json-content-serializer.ts
  • protocol/src/host/__tests__/mention-contracts.test.ts
  • protocol/src/host/index.ts
  • protocol/src/host/mention-contracts.ts
  • protocol/src/host/mention-schemas.ts
  • protocol/src/host/registry.ts

Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cc2aee329e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts
Comment thread clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts
Comment thread clients/gui-app/src/lib/composer/tiptap-json-content.ts Outdated
Comment thread clients/gui-app/src/components/chat/composer/picker/use-mention-items.ts Outdated
Comment thread protocol/src/common/json-content-serializer.ts Outdated
Signed-off-by: Hardik Shingala <hardik@traycer.ai>

# Conflicts:
#	clients/gui-app/src/lib/persist/keys.ts
#	clients/gui-app/src/lib/query-keys/index.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 81084064e8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts Outdated
Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts Outdated
Comment thread clients/gui-app/src/components/chat/composer/menu/composer-menu.tsx
Capability gate, cache-write and filter-coercion fixes from the review pass
on #1128, plus the provider submenu height regression #1045 introduced.

- Gate both root rows on the host actually advertising the two mention
  methods, not just on the composer having roots.
- Key the row store on the resolved (hostId, epicId, folders) identity so a
  host swap cannot serve another host's rows.
- Merge fresh payloads over cached rows while preserving key and position;
  an authoritative empty now writes instead of being dropped as a placeholder.
- Route the catalog refresh mutation's destination through onMutate context
  so a host swap mid-flight cannot apply the response to the wrong cache.
- Parse integer-string issue numbers from HTML round-trips, and omit the
  url suffix from a serialized mention that has none.
- Widen the filter popover's return-focus handoff to carry the printable
  character that reopened typing, and disambiguate repository labels on
  collision.
- Bound the provider submenu again: max-h-[min(70vh,24rem)].

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

Changing State or Involvement emptied the list for the length of a round
trip. The catalog only ever sweeps the DEFAULT view, so a filter it cannot
answer - "State: Merged" over a cache of open pull requests - excludes every
cached row the instant it is selected, leaving a lone "Searching GitHub…"
row where the list was. The menu collapsed and re-expanded on every change,
and while the remote was slow or rate-limit paused that empty frame read as
the filter doing nothing at all.

An empty local list under an in-flight search is "not answered yet", not
"nothing matches", so the rows already on screen stay until the answer lands.
Held per (section, query) so it is only ever a filter swap: a new query is a
different question and still collapses, and a settled empty answer - however
it settles - always wins over the hold.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fbc25fdbe3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/stores/composer/github-mention-catalog-store.ts Outdated
Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts Outdated
Comment thread clients/gui-app/src/components/chat/composer/menu/composer-menu.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 16

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@clients/gui-app/src/components/chat/composer/menu/__tests__/github-mention-filter-popover.test.tsx`:
- Around line 274-282: The test currently hardcodes the internal
`"epic-1\x1fpull-requests"` store key. Update the lookup around
`useGithubMentionFilterStore.getState().filtersByKey` to derive the key through
the store’s exported key builder or a selector, reusing the existing store API
and preserving the current assertions.

In
`@clients/gui-app/src/components/chat/composer/menu/mention-step-chrome-bar.tsx`:
- Around line 121-135: Update the refresh button’s icon rendering in the visible
Button component to use AgentSpinningDots when spinner.refreshing is true, while
retaining RefreshCwIcon for the idle state. Remove the animate-spin class from
RefreshCwIcon and preserve the existing button behavior and accessibility
attributes.

In `@clients/gui-app/src/components/chat/composer/picker/suggestion-render.ts`:
- Around line 163-166: Update the mention branch in onStart to pass false
directly to isDismissedMentionQuery instead of calling inGithubMentionSection(),
ensuring every new occurrence uses the root-step punctuation rules before the
new picker session opens.

In
`@clients/gui-app/src/components/chat/composer/picker/use-github-mention-sections.ts`:
- Around line 275-282: Update the enabled condition in useGithubMentionSearch so
it also requires supported, matching both catalog query gates. Preserve the
existing active and openSection checks, ensuring the search is disabled
immediately when host support changes to false.

In `@clients/gui-app/src/lib/composer/__tests__/tiptap-json-content.test.ts`:
- Around line 613-617: Strengthen the test around
buildAttachmentsFromJSONContent by asserting the string-input result has a
non-zero length before comparing it with the numeric-input result. Keep the
existing equality assertion to verify both paths produce the same attachment.

In `@clients/gui-app/src/lib/composer/mentions/__tests__/providers.test.tsx`:
- Around line 185-201: Extend the test around mentionProviderRegistry.entries
and the unsupported context by first creating a supported context with the same
"`#123`" query and asserting its labels include both “Resolve in Pull requests...”
and “Resolve in Issues...”. Keep the existing unsupported-context assertions to
verify those rows are absent when github.supported is false.
- Around line 240-241: Update the submenu provider tests around navigateEntry to
resolve the “Agents” and “Terminals” entries by label rather than using
entries[7] or entries[8]. Add or reuse an entryByLabel helper that returns the
matching entry with a clear failure when absent, then pass its results to
navigateEntry.

In `@clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts`:
- Around line 472-482: Update referenceMatchesRow so the githubHost comparison
in the "url" branch lowercases both reference.githubHost and row.githubHost
before comparing, while preserving the existing owner, repo, and non-URL
matching behavior.

In `@clients/gui-app/src/lib/composer/mentions/mention-dismissal.ts`:
- Around line 22-33: Update the doc comment for the function containing the
inGithubSection check to name commas and semicolons, matching the punctuation
tested by query.includes(",") and query.includes(";"). Adjust the example title
accordingly so it demonstrates the actual dismissal rules without introducing
colon behavior.

In `@clients/gui-app/src/lib/composer/mentions/providers.tsx`:
- Around line 977-992: Update referenceResolveEntries to use the full
available(context) gate instead of checking only context.roots.length,
preserving the existing empty-entry behavior and aligning its comment with
rootEntry’s supported-host and non-empty-roots requirements.
- Around line 1027-1034: Update the doc comment for githubMentionSectionForStep
to describe that it returns the active GitHub section when the step is in one,
or null otherwise, rather than describing a boolean predicate.

In `@clients/gui-app/src/lib/host-rpc-policy/host-method-policy-table.ts`:
- Around line 821-824: Update the scheduling configuration for
mention.githubSearch in the host-method policy table so typed queries share one
latest-wins coordinator lane keyed by host, scope, and section, excluding query
parameters. Preserve the existing LATEST_SCHEDULING behavior while adding the
scheduling identity required to cancel or supersede older searches.

In
`@clients/gui-app/src/stores/composer/__tests__/github-mention-catalog-store.test.ts`:
- Around line 103-118: In the test around selectGithubMentionScopeRepositories,
remove the local queryRepositories declaration, its equality/inequality
assertions, and the redundant recomputation of scopeRepositories already
represented by persisted. Keep only the assertion that the store-backed scope
result equals [ONE_REPO], since the ternary behavior is covered by the
sections-hook suite.

In `@clients/gui-app/src/stores/composer/github-mention-filter-store.ts`:
- Around line 110-120: Update selectGithubMentionFilter to apply the existing
section-specific filter coercion to persisted filters before returning them,
while leaving the default filter path unchanged. Ensure the coercion preserves
filter.repository by reference, and use the coerced result for filters returned
from state.filtersByKey.

In
`@protocol/src/common/__tests__/json-content-serializer-github-mentions.test.ts`:
- Around line 106-139: Update the test case “does not apply file-style
validation markers to either GitHub context” so each GitHub context is checked
in both “llm” and “user” formats against both “NOT FOUND” and “DELETED”. Ensure
the GithubPullRequest user-format assertion explicitly rejects its applicable
marker, covering the branch in jsonContentToMarkdown, while preserving the
existing validationResults setup and loop.

In `@protocol/src/host/__tests__/mention-contracts.test.ts`:
- Around line 346-354: The test named “accepts search responses with either row
variant and nullable notices” duplicates an earlier assertion and fails to cover
a non-null notice. Update this test around mentionGithubSearchResponseSchema to
use a valid non-null notice value while retaining both row variants, or remove
the redundant test if no additional coverage is needed.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7991bdb9-c252-40df-bc8b-bf1cce02e697

📥 Commits

Reviewing files that changed from the base of the PR and between 36f0795 and fbc25fd.

📒 Files selected for processing (67)
  • clients/gui-app/src/components/chat/composer/__tests__/chat-list-keymap-submit-source.test.tsx
  • clients/gui-app/src/components/chat/composer/__tests__/composer-mention-dismissal-flow.test.tsx
  • clients/gui-app/src/components/chat/composer/__tests__/composer-mention-flow.test.tsx
  • clients/gui-app/src/components/chat/composer/__tests__/composer-picker-store.test.ts
  • clients/gui-app/src/components/chat/composer/editor/extensions/attribute-helpers.ts
  • clients/gui-app/src/components/chat/composer/menu/__tests__/composer-menu-disabled-row.test.tsx
  • clients/gui-app/src/components/chat/composer/menu/__tests__/composer-menu.test.tsx
  • clients/gui-app/src/components/chat/composer/menu/__tests__/github-mention-filter-popover.test.tsx
  • clients/gui-app/src/components/chat/composer/menu/composer-menu.tsx
  • clients/gui-app/src/components/chat/composer/menu/github-mention-filter-popover.tsx
  • clients/gui-app/src/components/chat/composer/menu/mention-menu-item.tsx
  • clients/gui-app/src/components/chat/composer/menu/mention-preview-panel.tsx
  • clients/gui-app/src/components/chat/composer/menu/mention-step-chrome-bar.tsx
  • clients/gui-app/src/components/chat/composer/nodes/composer-mention-decorator.tsx
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-github-mention-sections-catalog-write.test.tsx
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-mention-items.test.ts
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-slash-items-session.test.tsx
  • clients/gui-app/src/components/chat/composer/picker/composer-picker-store.ts
  • clients/gui-app/src/components/chat/composer/picker/suggestion-render.ts
  • clients/gui-app/src/components/chat/composer/picker/use-github-mention-sections.ts
  • clients/gui-app/src/components/chat/composer/picker/use-mention-items.ts
  • clients/gui-app/src/components/epic-canvas/pr/__tests__/pr-source-notice.test.tsx
  • clients/gui-app/src/components/epic-canvas/pr/pr-detail-card.tsx
  • clients/gui-app/src/components/epic-canvas/pr/pr-detail-header.tsx
  • clients/gui-app/src/components/epic-canvas/pr/pr-panel-actions.tsx
  • clients/gui-app/src/components/epic-canvas/pr/pr-source-notice.tsx
  • clients/gui-app/src/hooks/composer/__tests__/use-epic-mention-entries.test.tsx
  • clients/gui-app/src/hooks/composer/__tests__/use-github-mention-search.test.tsx
  • clients/gui-app/src/hooks/composer/use-epic-mention-entries.ts
  • clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts
  • clients/gui-app/src/hooks/composer/use-github-mention-search.ts
  • clients/gui-app/src/lib/composer/__tests__/tiptap-json-content.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/github-mention-chrome.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/github-mention-rows.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/mention-dismissal.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/providers.test.tsx
  • clients/gui-app/src/lib/composer/mentions/__tests__/root-search-ranking.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/step-chrome.test.ts
  • clients/gui-app/src/lib/composer/mentions/github-mention-chrome.ts
  • clients/gui-app/src/lib/composer/mentions/github-mention-display.tsx
  • clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts
  • clients/gui-app/src/lib/composer/mentions/index.ts
  • clients/gui-app/src/lib/composer/mentions/mention-dismissal.ts
  • clients/gui-app/src/lib/composer/mentions/providers.tsx
  • clients/gui-app/src/lib/composer/mentions/root-search-ranking.ts
  • clients/gui-app/src/lib/composer/mentions/step-chrome.ts
  • clients/gui-app/src/lib/composer/segments.ts
  • clients/gui-app/src/lib/composer/tiptap-json-content.ts
  • clients/gui-app/src/lib/composer/types.ts
  • clients/gui-app/src/lib/host-rpc-policy/host-method-policy-table.ts
  • clients/gui-app/src/lib/persist/index.ts
  • clients/gui-app/src/lib/persist/keys.ts
  • clients/gui-app/src/lib/pr/pr-review-decision-label.ts
  • clients/gui-app/src/lib/pr/pr-source-notice-message.ts
  • clients/gui-app/src/lib/query-keys/index.ts
  • clients/gui-app/src/lib/query-keys/mention-query-keys.ts
  • clients/gui-app/src/stores/composer/__tests__/github-mention-catalog-store.test.ts
  • clients/gui-app/src/stores/composer/__tests__/github-mention-filter-store.test.ts
  • clients/gui-app/src/stores/composer/github-mention-catalog-store.ts
  • clients/gui-app/src/stores/composer/github-mention-filter-store.ts
  • protocol/src/common/__tests__/json-content-serializer-github-mentions.test.ts
  • protocol/src/common/json-content-serializer.ts
  • protocol/src/host/__tests__/mention-contracts.test.ts
  • protocol/src/host/index.ts
  • protocol/src/host/mention-contracts.ts
  • protocol/src/host/mention-schemas.ts
  • protocol/src/host/registry.ts

Comment thread clients/gui-app/src/components/chat/composer/menu/mention-step-chrome-bar.tsx Outdated
Comment thread clients/gui-app/src/lib/host-rpc-policy/host-method-policy-table.ts
Comment thread clients/gui-app/src/stores/composer/github-mention-filter-store.ts
Comment thread protocol/src/host/__tests__/mention-contracts.test.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a2a8c25b99

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts
Comment thread clients/gui-app/src/hooks/composer/use-github-mention-search.ts Outdated
Comment thread clients/gui-app/src/hooks/composer/use-github-mention-search.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@clients/gui-app/src/components/chat/composer/picker/use-github-mention-sections.ts`:
- Around line 321-328: The held-row key in useGithubMentionSections must include
scopeKey alongside openSection and query so hostId, currentEpicId, or
mentionRoots changes cannot retain rows from the previous scope. Update the key
passed to useHeldRowsDuringSearch and add a regression test covering a scope
change while the replacement search is in flight, ensuring stale rows are not
shown or selectable.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ed8e0264-7288-416c-bec7-da0b26597eab

📥 Commits

Reviewing files that changed from the base of the PR and between fbc25fd and a2a8c25.

📒 Files selected for processing (2)
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-github-mention-sections-filter-swap.test.tsx
  • clients/gui-app/src/components/chat/composer/picker/use-github-mention-sections.ts

Merges `main` and answers the Codex/CodeRabbit threads on #1128. The merge
itself broke two files nothing conflicted in: `MentionMenuEntry` grew
`updatedAt`/`archived` on main (#1127) while this branch grew `labelPrefix`.
GitHub rows leave the new time slot null - their age is already composed into
`detail` beside the repository, and filling both renders it twice.

Correctness:

- Sent-message chips render GitHub mentions. Their `path` is a synthetic token
  (`github-pr:org/repo#12`), so the generic chip read a basename off it and
  showed the raw token as the tooltip: `#4917` became `traycer#4917` with a
  file icon the moment the message was posted.
- The live search is gated on `supported`, like the catalog reads already were.
  A host swap could negotiate the method away while the section stayed open.
- The held-row key includes the scope. Holding rows across a host, epic or
  roots change offered selectable mentions the new scope cannot resolve.
- Placeholder catalog responses are reported as the unanswered case rather than
  as rows. Same class as above: `keepPreviousData` hands over the PREVIOUS
  scope's answer, and every row in it is committable.
- A placeholder response no longer arms the automatic stale follow-up, which
  spent a GitHub request deciding for a scope that had not answered yet.
- `sourceStatus` and `notice` are gated on `wanted` like the rows, so a
  `gh-unavailable` banner cannot outlive the search that produced it.
- URL host comparison is case-folded, like owner and repo already were.
- `onStart` judges a new `@` by the root rules. The store can still hold the
  departing session's drilled step there, so `@x, y` could open the menu.
- Persisted filters are coerced to their section on read - identity-preserving
  when there is nothing to coerce, because the selector's result identity is
  what decides whether to re-render.
- The refresh control uses `AgentSpinningDots` rather than a spun icon.

Tests: the vacuous halves are the point - marker assertions that checked for a
marker the fixture never produces, a round-trip that passed if both sides went
empty, absence assertions with no positive control, and store assertions
comparing a local constant to itself. Category lookups go by label, not index.

Not changed, with reasons in the thread replies: the menu's `min(70vh,24rem)`
cap is the repository's viewport-cap idiom, not a fixed bound; the catalog's
Zustand store is deliberate and outlives query `gcTime` by design.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e755e5971

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/hooks/composer/use-github-mention-search.ts
Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts Outdated
…ow-up guard

Two review findings on the GitHub mention picker, both about state
outliving the question it answered.

The live search carries `keepPreviousData` so typing does not blank the
remote hits on every keystroke. That held the previous answer across ANY
key change, including a host, epic, roots or section change - and those
rows are merged into the new scope's list and stay selectable, so a user
could commit a mention naming a pull request the current scope cannot
resolve. The placeholder is now lane-scoped: kept across a query or
filter change (which the merge corrects for downstream), dropped across
a scope or section change. Both sides of the comparison build the lane
through one function, and an unreadable key fails closed to "no
placeholder" rather than to a cross-scope hold.

The catalog's one-automatic-sweep-per-session guard reset whenever
`allowStaleFollowUp` or `enabled` went false. Both flip inside a single
menu session - stepping back to root turns the follow-up off, opening
the other section disables the read - so a user moving in and out of a
section whose cache the host still answers `stale: true` for enqueued a
`refresh: "auto"` sweep every time. The guard now has one reset edge,
the picker closing, expressed as an explicit `pickerActive` input; the
scope and section stay in the stored value, where a change to either
already fails the comparison.

Both are covered by tests that drive the real hooks against a real
QueryClient, each with a positive control for the case the fix must NOT
change - the query-change hold, and the first sweep of a session.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1880fd3a7f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts Outdated
Comment thread clients/gui-app/src/hooks/composer/use-epic-mention-entries.ts Outdated
Comment thread clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts Outdated
…uite

The search hook now reads `useReactiveHostReadiness` directly, to build its
placeholder lane from the same host id `useHostQuery` keys the cache by. This
suite mocks `useHostQuery` wholesale and passes a bare `{}` client, so that
read was never reached before and had no `getActiveHostId` to call.

Mocked rather than satisfied on the client, because the lane is not what these
four cases are about - they capture the `enabled` decision. The lane has its
own suite, which drives the real readiness path.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
…rtifact refresh

Three findings from the review round on 1880fd3.

Host in the auto-refresh guard. `followKey` carried epic, paths and section
but not the host, while the `cacheKey` beside it always has. An app-wide
composer rebinding to a second host that advertises the same epic and the same
paths left every term unchanged, so that host's `stale: true` catalog read as a
sweep that had already run and it never got the one refresh a session owes it.

Zero-valued references. The patterns match `\d{1,7}`, which accepts `0` and
`000`, while the wire row schema requires a positive number - so `@#0`
classified as a resolvable reference, suppressed root's zero-match auto-close
and offered a Resolve row for an identity no response can ever contain.

Silent artifact refresh. `refetch()` resolves with a failed result rather than
rejecting, so the button's `Promise.all` settled either way and the spinner
stopped identically on success and failure; `useMentionItems` publishes
`loadFailed: false`, so there was no inline surface either. Reported through
`toastFromHostError`, the same manual-lane rule the GitHub catalog already
follows - the automatic lanes stay silent and degrade in place.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 12, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f009aff81

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/lib/composer/mentions/github-mention-chrome.ts
Review round on 4f009af.

The visible list is the catalog's rows merged with the live search's, and the
chrome's notice and banner can come from either, but the button was wired to
`refreshManually` alone. With a typed query or a non-default filter, clicking
"Refresh pull requests" therefore completed without touching the rows on
screen or the search's own status - so a refresh pressed BECAUSE the section
reported GitHub unreachable left that report standing. The search hook now
exposes a `refresh` that no-ops while the observer is disabled, and the
section asks both.

Also stops the picker's Tab branch claiming Shift+Tab. That one is defensive,
not a verified repair: by inspection the branch did match Shift+Tab, but
instrumenting the handler shows it is never reached with Shift held, so the
line is the right behaviour for a case that may not occur. It is deliberately
untested - a test passes identically with and without it, which is the wrong
kind of green. The reasoning is in the comment at the branch.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
clients/gui-app/src/lib/composer/mentions/providers.tsx (1)

953-969: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reset stale GitHub steps when availability changes.

stepEntries still returns cached rows and stepChromeCapability still enables full chrome after support or roots disappear. Reset the active step to root, or make both methods availability-aware while preserving the back entry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@clients/gui-app/src/lib/composer/mentions/providers.tsx` around lines 953 -
969, Update the GitHub provider’s stepEntries and stepChromeCapability methods
to account for current support and configured roots before exposing cached rows
or full step chrome. When GitHub is unavailable, reset the active step to the
root step (or return availability-aware results) while always preserving the
Mentions backEntry; retain the existing behavior when availability is valid.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@clients/gui-app/src/components/chat/__tests__/chat-user-message-content-github.test.tsx`:
- Around line 62-77: Update the test for ChatUserMessageContent to open the
rendered composer mention tooltip and assert it contains “traycerai/traycer#4917
· Stop the busy-loop”. Keep the existing `#4917` chip assertion, but replace the
query that only verifies the raw token is absent with a direct assertion of the
tooltip content.

---

Outside diff comments:
In `@clients/gui-app/src/lib/composer/mentions/providers.tsx`:
- Around line 953-969: Update the GitHub provider’s stepEntries and
stepChromeCapability methods to account for current support and configured roots
before exposing cached rows or full step chrome. When GitHub is unavailable,
reset the active step to the root step (or return availability-aware results)
while always preserving the Mentions backEntry; retain the existing behavior
when availability is valid.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b9301bf5-326d-45b0-b652-53bbb8a5b8d9

📥 Commits

Reviewing files that changed from the base of the PR and between a2a8c25 and 03e5fbc.

📒 Files selected for processing (37)
  • clients/gui-app/src/components/chat/__tests__/chat-user-message-content-github.test.tsx
  • clients/gui-app/src/components/chat/chat-user-message-content.tsx
  • clients/gui-app/src/components/chat/composer/__tests__/composer-mention-dismissal-flow.test.tsx
  • clients/gui-app/src/components/chat/composer/__tests__/composer-mention-flow.test.tsx
  • clients/gui-app/src/components/chat/composer/__tests__/composer-picker-store.test.ts
  • clients/gui-app/src/components/chat/composer/menu/__tests__/github-mention-filter-popover.test.tsx
  • clients/gui-app/src/components/chat/composer/menu/__tests__/mention-menu-item.test.tsx
  • clients/gui-app/src/components/chat/composer/menu/mention-menu-item.tsx
  • clients/gui-app/src/components/chat/composer/menu/mention-step-chrome-bar.tsx
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-github-mention-sections-filter-swap.test.tsx
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-github-mention-sections-refresh.test.tsx
  • clients/gui-app/src/components/chat/composer/picker/__tests__/use-mention-items.test.ts
  • clients/gui-app/src/components/chat/composer/picker/suggestion-render.ts
  • clients/gui-app/src/components/chat/composer/picker/use-github-mention-sections.ts
  • clients/gui-app/src/components/chat/composer/picker/use-mention-items.ts
  • clients/gui-app/src/hooks/composer/__tests__/use-epic-mention-entries-refresh-error.test.tsx
  • clients/gui-app/src/hooks/composer/__tests__/use-github-mention-catalog-follow-up.test.tsx
  • clients/gui-app/src/hooks/composer/__tests__/use-github-mention-search-scope.test.tsx
  • clients/gui-app/src/hooks/composer/__tests__/use-github-mention-search.test.tsx
  • clients/gui-app/src/hooks/composer/use-epic-mention-entries.ts
  • clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts
  • clients/gui-app/src/hooks/composer/use-github-mention-search.ts
  • clients/gui-app/src/lib/composer/__tests__/tiptap-json-content.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/github-mention-rows.test.ts
  • clients/gui-app/src/lib/composer/mentions/__tests__/providers.test.tsx
  • clients/gui-app/src/lib/composer/mentions/__tests__/root-search-ranking.test.ts
  • clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts
  • clients/gui-app/src/lib/composer/mentions/mention-dismissal.ts
  • clients/gui-app/src/lib/composer/mentions/providers.tsx
  • clients/gui-app/src/lib/composer/types.ts
  • clients/gui-app/src/lib/host-rpc-policy/host-method-policy-table.ts
  • clients/gui-app/src/lib/query-keys/index.ts
  • clients/gui-app/src/stores/composer/__tests__/github-mention-catalog-store.test.ts
  • clients/gui-app/src/stores/composer/github-mention-filter-store.ts
  • protocol/src/common/__tests__/json-content-serializer-github-mentions.test.ts
  • protocol/src/host/__tests__/mention-contracts.test.ts
  • protocol/src/host/registry.ts
💤 Files with no reviewable changes (1)
  • clients/gui-app/src/stores/composer/tests/github-mention-catalog-store.test.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 03e5fbc03b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts Outdated
@hdkshingala

Copy link
Copy Markdown
Member Author

Before pushing this round I ran a cold adversarial audit over the PR's full cumulative diff — same posture as a review bot (reachable states only, no author intent), briefed on every defect class this PR's review history has surfaced. It confirmed six further defects in those same families; all are fixed in d9f0808 alongside the four thread fixes, so the diff under review has already survived one cold read:

  • Section-level error honesty — a rejected catalog read or live search inside an open PR/Issue section rendered the settled "No matching …" verdict over a question GitHub never answered. The section chrome now carries errored; the appended status row became { label, busy } and reports "Couldn't reach GitHub." without spinning, while also suppressing the premature empty verdict.
  • isChecking re-forgot in-flight refreshes — the pending-keys set was ANDed with refreshMutation.isPending, which tracks only the observer's latest mutate(); once a newer refresh settled, an older scope's in-flight one read as done. The set is now the sole authority.
  • Frozen-closure host guard — the manual-refresh error toast compared against a render-closure hostId, frozen for a mutation replaced by a newer one; an away-and-back host walk suppressed a toast the current host should show. It now reads a live ref.
  • Verbatim collision counts — githubRepositoryQualification and the popover's repositoryLabel counted name collisions verbatim across the two provenances the identity key folds, so the escalation that keeps two same-name attachments distinguishable silently missed. Both fold per segment now via a shared foldGithubIdentitySegment.
  • Write-through-projection data loss — any State/Involvement change wrote the reconciled filter back to the store, converting reconcile's out-of-scope display fallback into a permanent delete of a remembered repository selection. Those writes now pass restoreUnrepresentedRepositorySelection (the write-path complement of reconcile); the Repository group's own writes stay verbatim, including the explicit clear. One pre-existing test pinned the delete behavior and was rewritten to pin the preserve contract, with a control proving the Repository group can still replace a detached selection outright.
  • Host-less human projections — the user-format serialization, chip tooltip, preview subtitle, and menu description all dropped githubHost, so the same coordinates on two hosts rendered identically to the human even though the wire identity was correct. githubMentionReference now applies the token's omit-only-github.com rule and the token builds from it, keeping every existing token byte-identical.

Each fix carries its own pinning tests.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eb5a6bf90e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts Outdated
Comment thread clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts Outdated
Comment thread clients/gui-app/src/components/chat/composer/picker/suggestion-render.ts Outdated
…rt held rows, readiness-gated follow-up, fluid menu cap

Four review findings, each fixed at the class level:

- The section matcher searched only `repo` and `owner/repo`, so typing the
  host-qualified identity the UI itself prints for cross-host name
  collisions dropped the row. Text matching now scores one host-qualified
  haystack (which subsumes the old clauses as substrings), and the
  reference grammar gained the three-segment `host/owner/repo#123` form,
  parsed with `githubHost` and matched host-aware at exact rank - a
  repository name cannot contain `/`, keeping the segment count
  unambiguous. The two-segment form stays host-agnostic.

- Rows held on screen across a filter swap (deliberately, to prevent the
  collapse-flash) were published as ordinary selectable entries, so the
  funnel could claim the new filter while Enter inserted a row it never
  matched. MentionMenuEntry gained `disabledReason` (mention items could
  never be disabled; only command items could), the picker's disabled read
  honors it, and the sections hook reports `rowsHeld` for exactly the
  stand-in window - held rows render inert with a screen-reader reason
  until the search answers.

- The auto follow-up effect marked its one-per-session key and issued the
  mutation without checking host readiness, so a request rejected at
  preflight spent the key and left the scope unrefreshed once the request
  context returned. The effect now defers both the mark and the send until
  `readiness.isReady`; it re-runs when readiness flips.

- The provider submenu's height cap had been changed from main's fluid
  `max-h-[70vh]` to a rem-ceilinged min() that binds on effectively every
  desktop viewport, against the repo's fluid-sizing rule. Reverted to the
  viewport cap.

Each fix is pinned: qualified-query matcher and three-segment parser tests
with github.com and case-fold controls, a rowsHeld projection test beside
the existing hold pins, provider entries carrying the disabled reason
exactly while held, the mention-item disabled read, and a
not-ready-then-ready follow-up test asserting zero mutations before the
flip and exactly one after.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 12, 2026
…ests

The scope's identity is order-independent everywhere it is compared -
githubMentionScopeKey sorts, the follow-up key sorts - but the WIRE
requests carried the caller's ordering, and the TanStack query key, the
pending-refresh keys, and the refresh destination all hash the request.
An order-only change in the same folder set (detach /a and re-attach it)
therefore forked one logical scope into two cache slots: a refresh issued
under the old ordering wrote its response to a slot the menu no longer
read, and the current slot neither showed the response nor reported the
refresh as pending for its 60s staleTime.

The catalog hook now derives one sorted canonicalWorkspacePaths and feeds
it to the cache-only request, both refresh mutations, and the follow-up
key; buildSearchRequest sorts identically. Pinned by a reorder-mid-refresh
test (isChecking survives the same set in reversed order while the refresh
is in flight) and a search-request canonicalization test.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 12, 2026
@hdkshingala
hdkshingala enabled auto-merge (squash) August 12, 2026 23:11

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 734b891d33

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/lib/composer/mentions/root-search-ranking.ts
…path to the chrome

Three review findings, each fixed at the class level:

- githubMentionRowKey joined host/owner/repo verbatim while every
  comparison around it folds - the one key derivation the casing rule
  missed. A repository whose casing was renamed between the cached sweep
  and the live search arrived as two spellings of one artifact, and the
  merge appended a committable duplicate instead of replacing the stale
  payload. The key now folds through foldGithubIdentitySegment.

- The filter popover's onCloseAutoFocus unconditionally returned focus to
  the composer, so closing it by clicking another control stole the focus
  that click had just placed and routed subsequent typing into the mention
  query. onInteractOutside now marks outside-interaction closes (the
  trigger exempt - the funnel button is a return-to-composer path like
  Escape), and the refocus is skipped for exactly those; Radix's default
  trigger-focus stays prevented on every path.

- The step chrome's Filter and Refresh buttons render through a portal
  after the editor in DOM order, so no native traversal direction could
  reach them from the composer - keyboard-only users had no route to
  either control. Shift+Tab now moves focus into the chrome explicitly
  (marker attribute rendered only when a step published chrome), consuming
  the key only when focus actually moved.

Pinned by a two-spellings merge test with a different-repo control, a
row-key equality unit, an outside-click no-refocus test beside the Escape
control, and handler-level Shift+Tab tests (focus lands and the key is
consumed; no chrome falls through; plain Tab still commits).

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 12, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b2d4fb4e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/lib/composer/mentions/providers.tsx
Comment thread clients/gui-app/src/lib/host-rpc-policy/host-method-policy-table.ts
Comment thread clients/gui-app/src/lib/composer/mentions/github-mention-chrome.ts
Comment thread clients/gui-app/src/hooks/composer/use-github-mention-catalog.ts
…bled state

Four review fixes, then five pre-push audit fixes of the same class:
every one collapses a second hand-written answer into the one that
already existed.

Review round:

- Mention rows honor `entry.disabledReason`: `renderPickerItem` flowed
  slash items through `pickerItemDisabledReason` but hardcoded `null`
  for mentions, so a held row rendered actionable while the store
  blocked its commit. Both kinds now share the predicate.

- `githubMentionToken` folds host/owner/repo through the row key's own
  fold: the row key merging two spellings into one row made a verbatim
  token a live defect - the payload respelling a merged row
  re-identified its attachment, minting two paths for one artifact.
  The tiptap rebuild fallback delegates to the same exported builder
  instead of restating the rule by hand.

- The cross-section scope preference compares `answeredAt` (client
  arrival, TanStack `dataUpdatedAt`) instead of `freshnessAt`, which
  stamps the host's last successful GitHub reach and does not advance
  across a degraded re-resolution.

- Root ranking rewrites a URL-shaped query to the `owner/repo#123`
  reference form rows actually carry, so a pasted URL's exact row takes
  the top tier instead of losing a fuzzy match on strings that can
  never contain a URL.

Audit round (restatement sweep over the cumulative diff):

- ONE default-host predicate: `isDefaultGithubMentionHost` (folded)
  lives in protocol beside the serializer that needs it; the serializer
  arms, the prose reference, the token builder and the ranking rewrite
  all call it. A row or node spelled `GitHub.com` is the default host
  everywhere now - prose and serialized forms no longer assert a host
  qualification the identity layer says does not exist.

- `githubRepositoryQualification` is THE escalation walk: the filter
  popover's label switches on it instead of re-implementing the
  collision walk (format divergence stays, commented as deliberate).

- Qualification distinguishes ignorance from answer: the section
  context's `repositories` is null until the scope resolves, and null
  qualifies rows at `owner/repo` - the live search can put rows from
  several repositories on screen before any catalog answers, and the
  old empty-array masquerade labelled them all bare `#123`. The comment
  claiming that window had no rows was false and is corrected.

- The zero-match reference exemption gates on the provider's own
  exported `githubMentionCategoryAvailable` instead of a hand-written
  twin of it.

Every behavioral fix is pinned by revert-probed tests.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 13, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3e85e47096

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts Outdated
Comment thread clients/gui-app/src/lib/composer/mentions/github-mention-chrome.ts
gui-app's type-aware `eslint . --cache --fix --max-warnings 0` runs
against a cold cache in CI and has hit Node's ~4GB default heap ceiling
twice in a row (FATAL heap OOM, SIGABRT mid-lint) on two different
heads, while the same command passes locally against a warm cache. The
ubuntu runner has 16GB; 6GB of old-space is headroom, not a workaround
- the alternative is rerunning the gate until garbage collection gets
lucky.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
… it is

`numberMatchScore` answers "is this query a number naming a row?" with
yes for bare digits (`^#?\d+$` is its own test), while
`parseGithubReferenceQuery` demanded the hash - so `@4917` ranked as
exact-number intent yet earned no `Resolve in ...` row and no
zero-match dismissal exemption, and a settled empty search closed the
picker over an artifact the section could still fetch. The parser now
accepts `^#?(\d{1,7})$`, making it the one answer both gates read; the
positive-number rule still rejects `@0`, and digits embedded in prose
stay prose. Pinned by parser tests.

Also moves the lint heap headroom into gui-app's own `lint` script:
the 4GB default OOM first hit two consecutive CI heads and then the
local hook, so a per-runner workflow env was the wrong home - the
script is the one place every invocation inherits it. The workflow
env from ff7d79f is reverted to keep the rule in one place.

Signed-off-by: Hardik Shingala <hardik@traycer.ai>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Aug 13, 2026
# Conflicts:
#	clients/gui-app/src/traycer-app.tsx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aeebdbc9dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread clients/gui-app/src/lib/composer/mentions/github-mention-rows.ts
@hdkshingala
hdkshingala merged commit fa18929 into main Aug 13, 2026
20 checks passed
@hdkshingala
hdkshingala deleted the traycer/lucky-owl branch August 13, 2026 01:48
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.

1 participant