Skip to content

feat(clients): add GitHub issue browsing - #8046

Open
mtdewwolf wants to merge 8 commits into
pingdotgg:mainfrom
mtdewwolf:feat/github-issues
Open

feat(clients): add GitHub issue browsing#8046
mtdewwolf wants to merge 8 commits into
pingdotgg:mainfrom
mtdewwolf:feat/github-issues

Conversation

@mtdewwolf

@mtdewwolf mtdewwolf commented Aug 24, 2026

Copy link
Copy Markdown

Summary

  • add GitHub issue browsing to the clients
  • surface GitHub Issues alongside the existing navigation destinations
  • support searching, filtering, inspecting, and opening issues in a thread

Testing

  • manually verified the desktop GitHub Issues flow

Before and after

Before

Before: navigation without GitHub Issues

After

After: navigation with GitHub Issues

After: GitHub Issues browser


Generated with GPT-5.6 Sol in T3 Code via the Codex harness.


Note

Medium Risk
New read-only RPC and gh spawning across workspace repos, plus Windows executable resolution and default stdin handling for all child processes. Auth is scoped to orchestration read, but process and capability negotiation changes can affect existing GitHub CLI flows.

Overview
Lets users browse GitHub issues in the app and hand one to an agent, gated by a new optional githubIssues server capability.

The server lists and views issues through gh for GitHub-backed workspace projects (githubIssues.list / githubIssues.detail, read scope). Lists merge across repos, sort by recency, keep per-host auth failures, and fail the whole request if gh is missing. Detail includes body and comments.

The client adds a primary-environment /issues page (search, state, project filters), a github-issue right-panel tab, sidebar entry, and in-app opening of issue URLs that match a workspace project. Fix in a thread opens the project and seeds the composer.

Also verifies the real GitHub CLI on Windows (skip npm gh shims), classifies gh.exe / absolute paths as GitHub CLI, and ignores unused child stdin to avoid Windows ECONNRESET crashes. Pull-request refresh no longer double-refreshes the same cached query.

Reviewed by Cursor Bugbot for commit 61becff. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add GitHub issue browsing with list/detail APIs, web UI, and Windows CLI fix

  • Adds GitHubIssueService server service that lists and fetches issue details across workspace GitHub projects via the gh CLI, with typed error handling for missing/unauthenticated CLI states
  • Introduces contracts (githubIssue.ts), WebSocket RPC methods (githubIssues.list, githubIssues.detail), capability flag githubIssues in ExecutionEnvironmentCapabilities, and client-runtime atoms for environment-scoped queries
  • Adds a /_chat/issues route listing up to 50 issues with debounced search, a GitHubIssueDetailPanel right-panel surface, sidebar navigation entry, and in-app interception of GitHub issue links
  • Fixes resolveGitHubCliExecutable to probe where.exe candidates on Windows and classify gh.exe / absolute-path invocations as GitHub CLI in classifyNonZeroExit
  • Bumps RIGHT_PANEL_STORAGE_VERSION to 12 with a migrator that normalizes legacy singleton github-issue surfaces to reference-keyed ids
  • Risk: RIGHT_PANEL_STORAGE_VERSION bump to 12 in rightPanelStore.ts drops persisted github-issue entries lacking valid projectId/repository/number; processRunner.runProcessCore now sets stdin: "ignore" by default, which may affect downstream spawners relying on inherited stdin

Macroscope summarized 61becff.

- Add issue listing and detail panels across server and web
- Resolve the GitHub CLI reliably on Windows
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e9f3f464-1284-4d24-a3af-85b7b2a686ce

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 24, 2026
Comment thread apps/web/src/components/sidebar/SidebarChrome.tsx 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: 268826c2a4

ℹ️ 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".

readonly q?: string;
}

export const Route = createFileRoute("/_chat/issues")({

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add the GitHub issue browser to mobile

This new route implements issue browsing only in the web client, which desktop inherits; a repo-wide search under apps/mobile finds no GitHub-issue RPC, navigation, list, detail, or handoff implementation. Consequently, iOS and Android users cannot access this newly advertised client feature at all, so add the corresponding mobile surface or explicitly scope the capability and documentation to web/desktop.

AGENTS.md reference: AGENTS.md:L67-L70

Useful? React with 👍 / 👎.

Comment on lines +67 to +71
const primaryEnvironment = usePrimaryEnvironment();
const environmentId = primaryEnvironment?.environmentId ?? null;
const capabilityKnown = primaryEnvironment !== null && primaryEnvironment.serverConfig !== null;
const supported =
primaryEnvironment?.serverConfig?.environment.capabilities.githubIssues === true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Read issues from every capable environment

In a multi-environment workspace, this page binds exclusively to the primary environment. This conflicts with SidebarChrome.tsx:169-171, which shows the GitHub Issues entry when any environment supports it: if the primary server is older but a secondary server is capable, the visible destination incorrectly reports the feature unavailable, and even when the primary is capable all GitHub projects and issues on secondary environments are omitted. Aggregate capable environments or carry the selected environment through the list/detail references.

AGENTS.md reference: AGENTS.md:L74-L74

Useful? React with 👍 / 👎.

Comment on lines +158 to +161
const unavailable = batches.find(
(batch) => "error" in batch && batch.error._tag === "GitHubIssueUnavailableError",
);
if (unavailable && "error" in unavailable) return yield* unavailable.error;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve successful repositories when one host is unauthenticated

When a workspace contains an authenticated GitHub repository alongside a GitHub Enterprise host for which gh is not authenticated, the latter batch becomes GitHubIssueUnavailableError and this branch fails the entire list RPC, discarding the successful repository's issues. Authentication is host-specific, unlike a genuinely missing CLI, and the result already supports per-project errors, so only fail globally for CLI absence or when no repository can be read.

Useful? React with 👍 / 👎.

Comment on lines +133 to +134
void navigate({
search: (current) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Replace history entries while editing issue filters

When a user types a multi-character issue search, every keystroke calls this navigation without replace: true, so each intermediate query is pushed into browser history. Pressing Back then walks through the search one character at a time and repeatedly reruns debounced issue queries instead of leaving the page; use replacement navigation for these transient filter updates, as the pull-request route does.

Useful? React with 👍 / 👎.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Effect service conventions review of the new GitHub issues feature. Two findings, both in newly added code; the rest of the service/layer/import structure (Context.Service tag with inline interface, make, layer, namespace imports, Foo["Service"] typing, Layer.mock test seams) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread packages/contracts/src/githubIssue.ts Outdated
Comment thread apps/server/src/sourceControl/GitHubCli.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the new GitHub Issues UI for consistency with the shared component system and Tailwind ownership. Four findings, all in the new web surfaces: two native filter controls that bypass the shared control primitives, a hand-rebuilt input group whose padding lands on the wrong element, a duplicated empty state, and a state tone in the right-panel tab that cannot be correct for closed issues.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
Comment thread apps/web/src/components/RightPanelTabs.tsx Outdated
Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
Comment thread apps/web/src/routes/_chat.issues.tsx
Comment thread apps/web/src/components/sidebar/SidebarChrome.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

UI consistency review

Four consistency findings, all in the new GitHub Issues surface. The most important one is the page header: it relies on a workspace-topbar class that does not exist anywhere in the repo, so the top bar loses its flex layout and titlebar geometry entirely.

  • apps/web/src/routes/_chat.issues.tsx — header applies an undefined workspace-topbar class instead of WorkspacePageHeader
  • apps/web/src/routes/_chat.issues.tsx — two raw <select> filters reconstruct the shared Select primitive
  • apps/web/src/routes/_chat.issues.tsx — search field hand-rolls InputGroup's leading addon, and pl-8 lands on Input's wrapper rather than the input
  • apps/web/src/routes/_chat.issues.tsx — empty state duplicated across the route and the detail panel, with the action outside EmptyContent

Suggested gates for the fixes: typecheck plus a production build and a quick real-app check of the header at the collapsed-sidebar and Electron/window-controls-overlay states.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a substantial new GitHub issue workflow spanning contracts, server RPC/CLI execution, web navigation, persistence, and agent handoff, with additional shared process and authorization changes. Missing mobile and multi-environment coverage plus unresolved issue-list and UI handling concerns require human review.

Not approved because:

  • 1 blocking correctness issue found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

- Share the GitHub issue empty state across list and detail views
- Use shared workspace controls and primary-environment capability gating
- Distinguish missing and unauthenticated GitHub CLI errors
Comment thread apps/server/src/githubIssue/GitHubIssueService.ts
Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
Comment thread apps/web/src/routes/_chat.issues.tsx

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One responsive-geometry finding on the new issue detail header; everything else (shared workspace topbar, InputGroup, Select, the single GitHubIssueEmptyState, the neutral issue tab glyph) now matches the shared component system.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/githubIssue/GitHubIssueDetailPanel.tsx Outdated
An unauthenticated GitHub Enterprise remote failed the whole issue list,
discarding the repositories that had answered; only a missing `gh` ends the
request now, and a locked-out host becomes a per-project error naming the
hostname to sign in to. On the web side the detail header wrapped its actions
into the title in the narrow right panel, the project filter read an empty
sentinel, filter edits pushed a history entry per keystroke, and the
"no projects" gate waited on every environment while the page reads only the
primary.

Model: Claude Opus 5 (1M context) via Claude Code

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
</div>
</div>

{selectedRef ? (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium routes/_chat.issues.tsx:345

On narrow screens, a selected issue still opens the full-screen detail overlay when GitHub issues are unsupported, so the overlay displays “Select an issue” and hides the actual “GitHub issues unavailable” state until Back is pressed. The overlay is gated only by selectedRef, while detailQuery is disabled when supported is false; gate it with supported as well.

-      {selectedRef ? (
+      {selectedRef && supported ? (
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/routes/_chat.issues.tsx around line 345:

On narrow screens, a selected issue still opens the full-screen detail overlay when GitHub issues are unsupported, so the overlay displays “Select an issue” and hides the actual “GitHub issues unavailable” state until `Back` is pressed. The overlay is gated only by `selectedRef`, while `detailQuery` is disabled when `supported` is false; gate it with `supported` as well.

updateFilters({
projectId:
value && value !== ALL_PROJECTS_VALUE ? (value as ProjectId) : undefined,
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Auth fix hint hidden

Medium Severity

Partial list success now keeps issues from signed-in hosts and puts host-specific gh auth login --hostname text on each failed project, but the issues page only renders a count banner when entries is non-empty. That remediation string never appears in the case this change targets, so a locked-out Enterprise remote stays unexplained beside a working list.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d196efe. Configure here.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Three UI consistency findings in the new GitHub issues surfaces. Everything raised in earlier runs (shared topbar, InputGroup/Select primitives, single owned empty state, neutral issue tab glyph) is addressed.

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
Comment thread apps/web/src/routes/_chat.issues.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated
The unavailable-projects banner hard-coded bg-warning/8, so it missed the dark
bump and the runtime theme bridge that --warning-surface carries. The row hover
tone applied to selected rows too, washing the open issue's selection back out
on hover. And the right panel borrowed PullRequestDetailGhost while the
capability was unknown, announcing "Loading pull request" over PR-shaped chrome
that an issue layout then replaced; the issue module now owns a ghost in its own
geometry, used for both of this surface's pending states.

Model: Claude Opus 5 (1M context) via Claude Code

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two layout findings on the new GitHub issue surfaces. The first is a real behavioral regression in the right panel; the second is an inert flex class in the issues route.

Posted via Macroscope — UI Consistency

description="Add a project backed by a GitHub repository and its issues will appear here."
/>
) : listQuery.isPending && listQuery.data === null ? (
<div className="flex flex-1 items-center justify-center gap-2 text-muted-foreground text-sm">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

flex-1 is inert here: body is rendered into <div className="min-h-0 flex-1 overflow-y-auto">, a block container, so this row cannot grow or centre vertically. With no vertical padding either, "Loading issues..." renders as a bare ~20px line flush under the filter bar, unlike the sibling connecting state (py-12) and the empty states (Empty className="min-h-72") in the same slot.

Suggested change
<div className="flex flex-1 items-center justify-center gap-2 text-muted-foreground text-sm">
<div className="flex items-center justify-center gap-2 py-12 text-muted-foreground text-sm">

Posted via Macroscope — UI Consistency

Comment on lines +41 to +49
return (
<GitHubIssueDetailContent
environmentId={environmentId}
detail={query.data}
error={query.error}
loading={query.isPending}
onRetry={query.refresh}
/>
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This panel never establishes a scroll container, but the right panel does not scroll for it. RightPanelTabs renders surfaces into <div className="flex min-h-0 flex-1 flex-col" data-right-panel-surface-content> and PreviewPanelShell is flex h-full min-h-0 flex-col — neither sets overflow, so every other surface owns its own scroller (PullRequestDetailPanel: min-h-0 flex-1 overflow-hidden + inner scroller; AgentsPanel: ScrollArea className="min-h-0 flex-1"; FilePreviewPanel likewise). GitHubIssueDetailContent returns a plain <article> sized by its content, so an issue with a long body or discussion grows past the panel and is clipped by the workspace's overflow-hidden with no way to reach it.

The route call sites already wrap the content in their own overflow-y-auto sections, so the smallest fix is to give this right-panel-only wrapper the scroller:

Suggested change
return (
<GitHubIssueDetailContent
environmentId={environmentId}
detail={query.data}
error={query.error}
loading={query.isPending}
onRetry={query.refresh}
/>
);
return (
<div className="min-h-0 flex-1 overflow-y-auto">
<GitHubIssueDetailContent
environmentId={environmentId}
detail={query.data}
error={query.error}
loading={query.isPending}
onRetry={query.refresh}
/>
</div>
);

Posted via Macroscope — UI Consistency

}
const sortedEntries = entries.toSorted((left, right) =>
right.updatedAt.localeCompare(left.updatedAt),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Issue list sorts by string time

Medium Severity

GitHubIssueService.list orders merged issues with updatedAt.localeCompare, so cross-repo ranking follows string order rather than instant order. Mixed timezone offsets can invert which issues appear in the newest window and which get truncated.

Fix in Cursor Fix in Web

Triggered by learned rule: Use parseTimestampDate for ISO string parsing, return null for invalid

Reviewed by Cursor Bugbot for commit b2a3a06. Configure here.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two consistency findings on the new GitHub issues UI. Earlier findings from previous runs on this PR (shared controls, bg-warning-surface, exclusive row hover, wrapping detail header, neutral tab glyph, issue-shaped ghost) all look addressed.

Still open from a previous run and not repeated inline: GitHubIssueDetailContent/GitHubIssueDetailGhost establish no scroll container, and the right panel's surface slot (flex min-h-0 flex-1 flex-col in RightPanelTabs) does not scroll for them, so a long issue body is unreachable in the thread panel; and the Loading issues... row in _chat.issues.tsx uses flex-1 inside a block scroll container with no vertical padding, so it renders as a bare line unlike the sibling states in the same slot.

Posted via Macroscope — UI Consistency

Comment on lines +346 to +347
<div className="fixed inset-0 z-50 overflow-y-auto bg-background md:hidden">
<div className="sticky top-0 z-10 flex h-12 items-center border-b border-border bg-background/95 px-3 backdrop-blur">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This mobile detail overlay is position: fixed, so it resolves against the viewport and escapes #root's padding-top: max(env(safe-area-inset-top), 0px) (index.css). The app opts into viewport-fit=cover, so on a notched device the sticky "Back to issues" bar renders under the status bar — every other edge-to-edge surface compensates (the mobile sidebar sheet uses pb-safe pt-safe; dialogs, sheets and the command palette inset their content instead of sitting flush at top: 0).

Suggest padding the bar itself rather than the scroller, so the inset survives while the bar is stuck, with min-h-12 so the padding does not eat its content box. Adding pb-safe to the overlay would also keep the last comment clear of the home indicator.

Suggested change
<div className="fixed inset-0 z-50 overflow-y-auto bg-background md:hidden">
<div className="sticky top-0 z-10 flex h-12 items-center border-b border-border bg-background/95 px-3 backdrop-blur">
<div className="sticky top-0 z-10 flex min-h-12 items-center border-b border-border bg-background/95 px-3 pt-safe backdrop-blur">

Posted via Macroscope — UI Consistency

<GitHubIssueEmptyState
title="Could not load this issue"
description={error}
action={<Button onClick={onRetry}>Try again</Button>}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This retry is a default-size, solid primary button, which is a heavier control than every other empty-state action in the app: PullRequestsUnavailableState, PullRequestListEmptyState and SourceControlSettings all use <Button size="sm" variant="outline"> with a RefreshCwIcon className="size-3.5". Since this state sits in the same slots as the pull-request one, the difference reads as a different kind of action.

Suggest size="sm" variant="outline" plus the refresh icon here and at the two matching call sites in routes/_chat.issues.tsx (the list error and the per-project error states), which already import RefreshCwIcon.

Posted via Macroscope — UI Consistency

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 16afd1b. Configure here.

"--json",
ISSUE_LIST_FIELDS,
...(input.query === undefined ? [] : ["--search", input.query]),
],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Issue list misses recently updated issues

High Severity

gh issue list is called with a per-repo --limit but no sort:updated-desc search qualifier. GitHub’s default list/search order is not last-updated, so recently updated older issues can fall outside the fetched window before the later updatedAt merge. The UI still presents the result as the newest issues.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 16afd1b. Configure here.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One remaining consistency finding on the GitHub issues UI. Earlier findings from previous runs on this PR (mobile detail overlay safe-area inset, empty-state retry button weight, detail panel scroll ownership, list loading-state padding) are still open in the current head and are not repeated here.

Posted via Macroscope — UI Consistency

Comment on lines +392 to +409
const StateIcon = issue.state === "open" ? CircleDotIcon : CircleSlash2Icon;
return (
<button
type="button"
aria-current={selected ? "true" : undefined}
className={cn(
"grid w-full grid-cols-[auto_minmax(0,1fr)_auto] gap-3 px-4 py-3 text-left transition-colors [contain-intrinsic-block-size:72px] [content-visibility:auto] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
// Exclusive, or hovering the open issue would wash its own selection back out.
selected ? "bg-accent" : "hover:bg-accent/60",
)}
onClick={() => onSelect(issue)}
>
<StateIcon
className={cn(
"mt-0.5 size-4",
issue.state === "open" ? "text-success-foreground" : "text-muted-foreground",
)}
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The state glyph mapping is now duplicated: this row re-derives CircleDotIcon/CircleSlash2Icon and the open/closed tone inline, while the detail panel routes the same decision through the exported GitHubIssueStateIcon (added for exactly this reason — the panel previously drew CircleDotIcon for closed issues too). The pull request feature keeps this in one owner (PullRequestStateGlyph) shared by row and detail. Worth reusing the helper here so the two surfaces cannot drift again — and, since state is carried by icon shape/colour alone in a row whose text never says "open"/"closed", giving the helper an accessible name the way PullRequestStateGlyph does (role="img" + label) would close the same gap in all three call sites at once.

-      <StateIcon
+      <GitHubIssueStateIcon
+        state={issue.state}
         className={cn(
           "mt-0.5 size-4",
           issue.state === "open" ? "text-success-foreground" : "text-muted-foreground",
         )}
       />

The local const StateIcon = ... line and the CircleDotIcon/CircleSlash2Icon imports can then go, and GitHubIssueStateIcon joins the existing import from ../components/githubIssue/GitHubIssueDetailPanel.

Posted via Macroscope — UI Consistency

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One finding on the new GitHubIssueService: the per-project read recovers its entire error channel and then re-raises one tag by string comparison, where Effect.catchTags on the survivable tags would express the same policy.

Posted via Macroscope — Effect Service Conventions

Comment on lines +159 to +171
Effect.match({
onFailure: (error) => ({ project, error }),
onSuccess: (value) => value,
}),
),
{ concurrency: PROJECT_CONCURRENCY },
);

// A missing `gh` is the one failure no repository can survive, so it ends the request.
const cliMissing = batches.find(
(batch) => "error" in batch && batch.error._tag === "GitHubIssueCliMissingError",
);
if (cliMissing && "error" in cliMissing) return yield* cliMissing.error;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Effect.match recovers the whole error channel per project, and the one failure that must not be recovered (GitHubIssueCliMissingError) is then re-raised below by inspecting _tag on the collected value. These are statically known tagged failures, so consider recovering only the survivable ones with Effect.catchTags({ GitHubIssueCliUnauthenticatedError: (error) => Effect.succeed({ project, error }), GitHubIssueOperationError: (error) => Effect.succeed({ project, error }) }). A missing gh then short-circuits Effect.forEach on its own, the batches.find(...) / "error" in cliMissing re-raise disappears, and the narrowed batch union no longer needs a tag string test to stay correct.

Posted via Macroscope — Effect Service Conventions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant