Repository navigation
feat(desktop): add multi-repo work item filters and PR checks - #6201
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe dashboard now supports multiple repository filters for tasks and pull requests. Routes, stores, navigation, and host queries preserve plural project state. Task views support expanded status tabs. Pull-request data includes normalized GitHub checks and update timestamps. ChangesDashboard filtering and navigation
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant DashboardView
participant ProjectQueryTargets
participant GitHubHost
participant PullRequestChecks
User->>DashboardView: select multiple projects
DashboardView->>ProjectQueryTargets: resolve project and host targets
ProjectQueryTargets->>GitHubHost: query issues or pull requests per target
GitHubHost->>PullRequestChecks: normalize statusCheckRollup
PullRequestChecks->>DashboardView: return checks and statuses
DashboardView->>User: render aggregated results
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
143e7bf to
dded9bb
Compare
Greptile SummaryThis PR expands desktop work-item browsing across multiple repositories, adds detailed task-state filters and pull-request check visibility, and migrates persisted filters to multi-select arrays.
Confidence Score: 4/5The pull-request checks enrichment should be fixed before merging because valid CI checks can be silently shown as absent when its independent search page differs from the displayed result page. Display rows come from GitHub’s search API, while their checks come from a separately paginated Files Needing Attention: packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts
|
| Filename | Overview |
|---|---|
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/hooks/useProjectQueryTargets/useProjectQueryTargets.ts | Resolves selected or all repositories to their serving host IDs and URLs using the existing canonical project key. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsContent/PullRequestsContent.tsx | Replaces single-repository infinite querying with per-repository/page fan-out, merged sorting, and repository-aware actions. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx | Adds multi-repository issue fan-out, repository-qualified selection identities, and cross-repository navigation. |
| packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts | Adds updated timestamps and check enrichment, but independently paginating the enrichment query can silently omit checks for displayed PRs. |
| packages/host-service/src/trpc/router/pull-requests/pull-request-checks.ts | Normalizes GitHub CheckRun and StatusContext rollups through existing pull-request mapper utilities. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/stores/tasks-filter-state.ts | Migrates the persisted single-repository value to a normalized multi-select array and serializes expanded task filters. |
Sequence Diagram
sequenceDiagram
participant UI as Desktop work-item view
participant Hosts as Selected host services
participant Search as GitHub search API
participant Checks as gh pr list
UI->>Hosts: Query each selected repository/page
par Per repository
Hosts->>Search: Fetch issue/PR result page
Search-->>Hosts: Results ordered by updated time
Hosts->>Checks: Fetch PR list with statusCheckRollup
Checks-->>Hosts: Independently paginated check results
Hosts-->>UI: Search rows enriched by PR number
end
UI->>UI: Merge and sort rows across repositories
Prompt To Fix All With AI
### Issue 1
packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts:194-197
**Checks use mismatched pagination**
When `gh api search/issues` and `gh pr list --search` return different membership or ordering for a page, this independent positional slice omits checks for displayed pull requests, causing PRs with CI checks to render as “No checks reported.”
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "fix(desktop): stabilize multi-repository..." | Re-trigger Greptile
| return new Map( | ||
| ghPrListSchema | ||
| .parse(raw) | ||
| .slice((page - 1) * perPage, page * perPage) |
There was a problem hiding this comment.
Checks use mismatched pagination
When gh api search/issues and gh pr list --search return different membership or ordering for a page, this independent positional slice omits checks for displayed pull requests, causing PRs with CI checks to render as “No checks reported.”
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts
Line: 194-197
Comment:
**Checks use mismatched pagination**
When `gh api search/issues` and `gh pr list --search` return different membership or ordering for a page, this independent positional slice omits checks for displayed pull requests, causing PRs with CI checks to render as “No checks reported.”
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx (1)
67-79: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winCancel pending search navigation before another filter changes.
The delayed callback retains
buildSearchwith the previous project and state filters. If a user types a search query and changes the repository or state within 300 ms, the delayed navigation restores the old filters.Clear
debounceRef.currentbefore project and state navigation, or read the latest filters from a ref.🤖 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 `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx` around lines 67 - 79, Update the filter navigation handlers in PullRequestsView so they clear any pending debounceRef timeout before navigating when the project or state filter changes. Preserve syncSearchToUrl’s debounce behavior while ensuring delayed search navigation cannot restore stale repository or state filters.
🧹 Nitpick comments (5)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.ts (1)
17-32: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace the double cast with an honest partial return type.
migratePullRequestsFilterStatedeclaresPullRequestsFilterStatebut returns only two fields. Theas unknown ascast hides that mismatch and removes type checking from this function. Zustand merges the migrated object with the store initializer, so a partial type is accurate and safe.Declare the partial shape and delete the cast.
As per coding guidelines: "Avoid
anyunless necessary; preserve type safety across JavaScript/TypeScript code."♻️ Proposed refactor
+type PersistedPullRequestsFilterState = Pick< + PullRequestsFilterState, + "projectFilters" | "includeClosed" +>; + export function migratePullRequestsFilterState( persistedState: unknown, -): PullRequestsFilterState { +): PersistedPullRequestsFilterState { const state = persistedState && typeof persistedState === "object" ? (persistedState as Record<string, unknown>) : {}; const legacyProject = typeof state.projectFilter === "string" ? state.projectFilter : null; return { projectFilters: normalizeProjectFilters( state.projectFilters ?? (legacyProject ? [legacyProject] : []), ), includeClosed: state.includeClosed === true, - } as unknown as PullRequestsFilterState; + }; }🤖 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 `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.ts` around lines 17 - 32, Update migratePullRequestsFilterState to return a partial PullRequestsFilterState matching the two migrated fields, and remove the as unknown as cast. Preserve the existing normalization and legacy project handling while allowing Zustand to merge the partial result with the store initializer.Source: Coding guidelines
apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsx (1)
40-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract one helper for the
projectsparameter with the legacyprojectfallback. Three task routes repeat the same nested ternary that prefersprojects, falls back to a one-element array fromproject, and otherwise yields an empty result. The shared root cause is a missing helper besideparseProjectFilterParam. AddresolveProjectFilterParams(projects, project)toapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/project-filter-utils.ts, then call it from each route.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsx#L40-L46: call the helper forinitialProjects, and keepundefinedfor the empty case so the persisted store value still applies.apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/page.tsx#L64-L82: call the helper forprojectFilters, and keep[]for the empty case.apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/$issueNumber/page.tsx#L51-L56: call the helper forprojectFilters, passing the localprojectIdas the legacy value.As per coding guidelines: "promote components used two or more times to the highest shared parent".
🤖 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 `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsx` around lines 40 - 46, Add the shared resolveProjectFilterParams(projects, project) helper beside parseProjectFilterParam in apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/project-filter-utils.ts, preferring parsed projects, then a one-item legacy project array, and otherwise returning the caller’s empty-case value. Update initialProjects in apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsx#L40-L46 to use it with undefined for empty results; update projectFilters in apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/page.tsx#L64-L82 to use [] for empty results; and update projectFilters in apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/$issueNumber/page.tsx#L51-L56 to pass projectId as the legacy value.Source: Coding guidelines
apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx (1)
114-128: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winMemoize
issuesinsideuseQuerieswithcombine.Without
combine,useQueriesreturns a fresh array each render by this installed version, so[queries, queryTargets]changes on every render and recomputesflatMap(...) + sort(...)each time. Return the combined, sorted issues fromcombineso the sort runs only when query state or project targets change.🤖 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 `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx` around lines 114 - 128, The issues aggregation currently memoizes over the unstable `queries` array, causing repeated flattening and sorting. Update the `useQueries` configuration to use its `combine` option, merging each query’s data with the corresponding `queryTargets` entry and returning the sorted issues there; then consume that combined result directly instead of the separate `useMemo` around `issues`.apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsx (1)
22-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse the renderer path alias.
Replace this relative component import with the existing
renderer/...alias.Proposed change
-import { PullRequestChecksSection } from "../components/PullRequestChecksSection"; +import { PullRequestChecksSection } from "renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSection";As per coding guidelines, use the path aliases defined in
tsconfig.jsonwhenever possible.🤖 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 `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/`$prNumber/page.tsx at line 22, Update the PullRequestChecksSection import in the pull-request page to use the existing renderer/... path alias instead of the relative ../components path, following the aliases defined in tsconfig.json.Source: Coding guidelines
packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts (1)
179-203: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy liftDo not refetch every prior page for check enrichment.
On page
n, this helper requestsn * perPagepull requests and then discards all earlier rows. Multi-repository queries repeat that work for every selected repository. This increases GitHub CLI payload size and makes later pages more likely to miss check summaries after the enrichment timeout or failure.Use a batched check query for only
result.items, or paginate check retrieval with the same cursor as the primary search.🤖 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 `@packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts` around lines 179 - 203, Update the pull-request search flow around execGh and normalizePullRequestChecks so check enrichment operates only on the current page’s result.items, rather than fetching page * perPage rows and slicing earlier pages. Use a batched check query for those items, or advance check retrieval with the same pagination cursor, while preserving the existing page results and normalized check fields.
🤖 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
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/project-filter-utils.ts`:
- Around line 1-16: Update normalizeProjectFilters to trim each string project
ID before checking that it is non-empty and before storing it in the Set, so
parseProjectFilterParam handles comma-separated values with surrounding
whitespace and returns canonical IDs.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSection/PullRequestChecksSection.tsx`:
- Around line 37-44: Update the summaryLabel logic in PullRequestChecksSection
to distinguish skipped or cancelled checks from the “none” state. When check
rows exist but all are skipped or cancelled, display an accurate skipped-check
label while preserving the existing success, failure, pending, and genuinely
empty-state labels.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsContent/PullRequestsContent.tsx`:
- Around line 90-92: The error handling in PullRequestsContent must preserve
usable results when only some repository queries fail. Update the
error-rendering branch that consumes error and queries so successful rows remain
visible alongside a partial-failure notice, and render the full error state only
when no query has usable results.
- Around line 102-118: The pullRequests useMemo currently globally re-sorts all
merged rows whenever another repository page loads, changing existing rendered
positions. Update the aggregation around queries, queryTargets, and the
pullRequests mapping to preserve previously emitted rows and append each
deterministic merged batch without reordering existing items, while retaining
correct multi-project pagination and avoiding duplicates or skipped items.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx`:
- Around line 64-112: Update the pagination flow around queryTargets and
pageCount so each target generates pages only through its latest fetched page
and only while that target reports hasNextPage; stop adding pages for exhausted
repositories instead of using the global pageCount cross product. Update the
sentinel/page increment logic to advance only targets whose latest page still
has hasNextPage, while preserving existing fetching and refetch behavior.
In
`@packages/host-service/src/trpc/router/pull-requests/procedures/get-content.ts`:
- Line 30: The pull request check rollup schemas currently reject GitHub’s
nullable statusCheckRollup values. Update both schema fields in
packages/host-service/src/trpc/router/pull-requests/procedures/get-content.ts
(line 30) and
packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts
(lines 55-57) to accept null alongside arrays, while leaving
normalizePullRequestChecks(raw ?? []) and all downstream normalization
unchanged.
---
Outside diff comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx`:
- Around line 67-79: Update the filter navigation handlers in PullRequestsView
so they clear any pending debounceRef timeout before navigating when the project
or state filter changes. Preserve syncSearchToUrl’s debounce behavior while
ensuring delayed search navigation cannot restore stale repository or state
filters.
---
Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/`$prNumber/page.tsx:
- Line 22: Update the PullRequestChecksSection import in the pull-request page
to use the existing renderer/... path alias instead of the relative
../components path, following the aliases defined in tsconfig.json.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.ts`:
- Around line 17-32: Update migratePullRequestsFilterState to return a partial
PullRequestsFilterState matching the two migrated fields, and remove the as
unknown as cast. Preserve the existing normalization and legacy project handling
while allowing Zustand to merge the partial result with the store initializer.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx`:
- Around line 114-128: The issues aggregation currently memoizes over the
unstable `queries` array, causing repeated flattening and sorting. Update the
`useQueries` configuration to use its `combine` option, merging each query’s
data with the corresponding `queryTargets` entry and returning the sorted issues
there; then consume that combined result directly instead of the separate
`useMemo` around `issues`.
In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsx`:
- Around line 40-46: Add the shared resolveProjectFilterParams(projects,
project) helper beside parseProjectFilterParam in
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/project-filter-utils.ts,
preferring parsed projects, then a one-item legacy project array, and otherwise
returning the caller’s empty-case value. Update initialProjects in
apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsx#L40-L46
to use it with undefined for empty results; update projectFilters in
apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/page.tsx#L64-L82
to use [] for empty results; and update projectFilters in
apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/$issueNumber/page.tsx#L51-L56
to pass projectId as the legacy value.
In
`@packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts`:
- Around line 179-203: Update the pull-request search flow around execGh and
normalizePullRequestChecks so check enrichment operates only on the current
page’s result.items, rather than fetching page * perPage rows and slicing
earlier pages. Use a batched check query for those items, or advance check
retrieval with the same pagination cursor, while preserving the existing page
results and normalized check fields.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 91684c7a-e1bf-4d1d-8950-dec1702910c0
⛔ Files ignored due to path filters (5)
.github/pr-assets/work-items-filters-checks/after-pr-detail.pngis excluded by!**/*.png.github/pr-assets/work-items-filters-checks/after-pr-list.pngis excluded by!**/*.png.github/pr-assets/work-items-filters-checks/after-task-statuses.pngis excluded by!**/*.png.github/pr-assets/work-items-filters-checks/before-pr-detail.pngis excluded by!**/*.png.github/pr-assets/work-items-filters-checks/before-pr-list.pngis excluded by!**/*.png
📒 Files selected for processing (44)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarHeader/DashboardSidebarHeader.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/ProjectFilter.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/project-filter-utils.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/project-filter-utils.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/hooks/useProjectQueryTargets/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/hooks/useProjectQueryTargets/useProjectQueryTargets.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSection/PullRequestChecksSection.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSection/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSummary/PullRequestChecksSummary.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSummary/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsContent/PullRequestsContent.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsTopBar/PullRequestsTopBar.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/pull-request-checks.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/layout.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/TasksTopBar.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/components/StatusFilter/StatusFilter.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/hooks/useTasksData/useTasksData.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/utils/matchesTaskStatusFilter/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/utils/matchesTaskStatusFilter/matchesTaskStatusFilter.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/utils/matchesTaskStatusFilter/matchesTaskStatusFilter.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/$issueNumber/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/layout.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/stores/tasks-filter-state.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/stores/tasks-filter-state.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/getRepositoryMismatchLabel/getRepositoryMismatchLabel.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/getRepositoryMismatchLabel/getRepositoryMismatchLabel.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/getRepositoryMismatchLabel/index.tsapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/WorkspaceSidebarHeader.tsxpackages/host-service/src/trpc/router/pull-requests/procedures/get-content.tspackages/host-service/src/trpc/router/pull-requests/pull-request-checks.test.tspackages/host-service/src/trpc/router/pull-requests/pull-request-checks.tspackages/host-service/src/trpc/router/workspace-creation/procedures/search-github-issues.tspackages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.tspackages/host-service/test/integration/workspace-creation-github.integration.test.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts (1)
197-230: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winBound the batch size and deduplicate numbers.
ghGetPullRequestChecksbuilds one aliasedpullRequestselection per result, each withcontexts(first: 100). The count followslimit, which comes frominput.limit ?? 30and is not capped in this file. A largelimitproduces a single query that can exceed GitHub GraphQL node and cost limits. The request then fails as a whole, and the caller drops checks for the entire page. Duplicate numbers also emit duplicate aliases.Chunk the numbers into fixed-size batches and merge the results, so one oversized page does not remove checks for all rows.
♻️ Proposed refactor sketch
+const CHECKS_BATCH_SIZE = 25; + async function ghGetPullRequestChecks( execGh: ExecGh, repo: ResolvedGithubRepo, pullRequestNumbers: number[], ): Promise<Map<number, Pick<PullRequestResult, "checks" | "checksStatus">>> { - if (pullRequestNumbers.length === 0) return new Map(); - const selections = pullRequestNumbers + const unique = [...new Set(pullRequestNumbers)]; + if (unique.length === 0) return new Map(); + if (unique.length > CHECKS_BATCH_SIZE) { + const batches: number[][] = []; + for (let i = 0; i < unique.length; i += CHECKS_BATCH_SIZE) { + batches.push(unique.slice(i, i + CHECKS_BATCH_SIZE)); + } + const results = await Promise.all( + batches.map((batch) => ghGetPullRequestChecks(execGh, repo, batch)), + ); + return new Map(results.flatMap((result) => [...result])); + } + const selections = unique .map(🤖 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 `@packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts` around lines 197 - 230, Update ghGetPullRequestChecks to deduplicate pullRequestNumbers, split the unique numbers into fixed-size batches, and execute the existing GraphQL selection/query flow once per batch. Merge each batch’s results into one Map while preserving the current empty-input behavior and ensuring a failed oversized request no longer drops checks for the entire page.
🤖 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
`@packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts`:
- Around line 231-243: Update the GraphQL argument construction in the search
pull-request procedure to use raw string fields for the owner and repository
name variables: replace the `-F` flags associated with `repo.owner` and
`repo.name` with `-f`, while leaving the query and working-directory handling
unchanged.
---
Nitpick comments:
In
`@packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts`:
- Around line 197-230: Update ghGetPullRequestChecks to deduplicate
pullRequestNumbers, split the unique numbers into fixed-size batches, and
execute the existing GraphQL selection/query flow once per batch. Merge each
batch’s results into one Map while preserving the current empty-input behavior
and ensuring a failed oversized request no longer drops checks for the entire
page.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 70b7b042-099a-4263-981e-f5ffde39b7c2
📒 Files selected for processing (2)
packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.tspackages/host-service/test/integration/workspace-creation-github.integration.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/host-service/test/integration/workspace-creation-github.integration.test.ts
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.ts (1)
46-46: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winNormalize values at the setter boundary.
setProjectFiltersstores raw values withoutnormalizeProjectFilters. A caller can persist blank, whitespace-prefixed, or duplicate IDs.pullRequestsSearchFromFilterscan then emit invalidsearch.projectsvalues.Proposed fix
- setProjectFilters: (projectFilters) => set({ projectFilters }), + setProjectFilters: (projectFilters) => + set({ projectFilters: normalizeProjectFilters(projectFilters) }),🤖 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 `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.ts` at line 46, Update setProjectFilters in the pull-requests filter store to normalize the incoming project filters with normalizeProjectFilters before storing them. Ensure all callers persist trimmed, non-blank, deduplicated project IDs so pullRequestsSearchFromFilters receives valid values.
🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/pull-request-checks.test.ts (1)
14-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCover the
pendingandsuccesssummary states directly.The current case includes both states, but
failuretakes priority. It does not verify either branch as the final status. Add one pending-only case and one all-success case.Based on the
pull-request-checks.tscontract,pendingandsuccessare separate output states.🤖 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 `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/pull-request-checks.test.ts` around lines 14 - 27, Extend the tests around summarizePullRequestChecks with two independent cases: one containing only pending checks that expects status "pending", and one containing only successful checks that expects status "success". Keep the existing failure-priority case unchanged and assert the corresponding passing, pending, and failing counts for each new summary.
🤖 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.
Outside diff comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.ts`:
- Line 46: Update setProjectFilters in the pull-requests filter store to
normalize the incoming project filters with normalizeProjectFilters before
storing them. Ensure all callers persist trimmed, non-blank, deduplicated
project IDs so pullRequestsSearchFromFilters receives valid values.
---
Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/pull-request-checks.test.ts`:
- Around line 14-27: Extend the tests around summarizePullRequestChecks with two
independent cases: one containing only pending checks that expects status
"pending", and one containing only successful checks that expects status
"success". Keep the existing failure-priority case unchanged and assert the
corresponding passing, pending, and failing counts for each new summary.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 129e93a9-64dd-4045-a860-8d6708d80bfc
📒 Files selected for processing (23)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/project-filter-utils.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/project-filter-utils.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/$prNumber/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSection/PullRequestChecksSection.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSummary/PullRequestChecksSummary.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsContent/PullRequestsContent.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/pull-request-checks.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/stores/pullRequestsFilterStore/pullRequestsFilterStore.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/$issueNumber/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/stores/tasks-filter-state.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/mergePaginatedProjectRows/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/mergePaginatedProjectRows/mergePaginatedProjectRows.test.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/utils/mergePaginatedProjectRows/mergePaginatedProjectRows.tspackages/host-service/src/trpc/router/pull-requests/procedures/get-content.tspackages/host-service/src/trpc/router/pull-requests/pull-request-checks.test.tspackages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.tspackages/host-service/test/integration/workspace-creation-github.integration.test.ts
🚧 Files skipped from review as they are similar to previous changes (16)
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/ProjectFilter/project-filter-utils.test.ts
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/page.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/issue/$issueNumber/page.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSection/PullRequestChecksSection.tsx
- packages/host-service/src/trpc/router/pull-requests/pull-request-checks.test.ts
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/$taskId/page.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/page.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/PullRequestsView.tsx
- packages/host-service/src/trpc/router/pull-requests/procedures/get-content.ts
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestChecksSummary/PullRequestChecksSummary.tsx
- packages/host-service/test/integration/workspace-creation-github.integration.test.ts
- packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/stores/tasks-filter-state.ts
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsx
- apps/desktop/src/renderer/routes/_authenticated/_dashboard/pull-requests/components/PullRequestsView/components/PullRequestsContent/PullRequestsContent.tsx
There was a problem hiding this comment.
1 issue found across 25 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsx:127">
P2: The same `cancelPendingSearchNavigation` helper (cancel + clear a `debounceRef` timeout), the reworked `syncSearchToUrl` that calls it, and the `useEffect(() => cancelPendingSearchNavigation, ...)` unmount cleanup are now duplicated verbatim across TasksView and PullRequestsView, and the helper is invoked in every filter/navigation handler in both. The debounce/cancel logic and its semantics (cancel a pending search push before a filter navigation so the stale debounced URL write can't clobber it) are identical, so keeping them in two places is exactly where behavior tends to drift — e.g. if this view needs a different debounce delay or a flush, it must be changed twice. Consider extracting a small shared hook (e.g. `useDebouncedSearchNavigation`) and using it in both views.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| includeClosedIssues, | ||
| ], | ||
| ); | ||
| const cancelPendingSearchNavigation = useCallback(() => { |
There was a problem hiding this comment.
P2: The same cancelPendingSearchNavigation helper (cancel + clear a debounceRef timeout), the reworked syncSearchToUrl that calls it, and the useEffect(() => cancelPendingSearchNavigation, ...) unmount cleanup are now duplicated verbatim across TasksView and PullRequestsView, and the helper is invoked in every filter/navigation handler in both. The debounce/cancel logic and its semantics (cancel a pending search push before a filter navigation so the stale debounced URL write can't clobber it) are identical, so keeping them in two places is exactly where behavior tends to drift — e.g. if this view needs a different debounce delay or a flush, it must be changed twice. Consider extracting a small shared hook (e.g. useDebouncedSearchNavigation) and using it in both views.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/TasksView.tsx, line 127:
<comment>The same `cancelPendingSearchNavigation` helper (cancel + clear a `debounceRef` timeout), the reworked `syncSearchToUrl` that calls it, and the `useEffect(() => cancelPendingSearchNavigation, ...)` unmount cleanup are now duplicated verbatim across TasksView and PullRequestsView, and the helper is invoked in every filter/navigation handler in both. The debounce/cancel logic and its semantics (cancel a pending search push before a filter navigation so the stale debounced URL write can't clobber it) are identical, so keeping them in two places is exactly where behavior tends to drift — e.g. if this view needs a different debounce delay or a flush, it must be changed twice. Consider extracting a small shared hook (e.g. `useDebouncedSearchNavigation`) and using it in both views.</comment>
<file context>
@@ -124,26 +124,31 @@ export function TasksView({
includeClosedIssues,
],
);
+ const cancelPendingSearchNavigation = useCallback(() => {
+ if (!debounceRef.current) return;
+ clearTimeout(debounceRef.current);
</file context>
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
All reported issues were addressed across 18 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 19 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…igation - setProjectFilters bails on equal content; the view sync effect plus an always-fresh normalized array was an infinite update loop that crashed the Pull Requests page with 'Maximum update depth exceeded' - search inputs sync only from the URL param, so typing no longer snaps back to the stale URL value during the debounced navigation - detail pages no longer treat the item's own project param as a filter fallback, so back from a detail keeps the all-repositories view - route pages memoize URL-derived project filters (stable identity) - Run in Workspace blocks mixed-repository issue selections instead of silently creating every workspace against one repository
- searchPullRequests/searchGitHubIssues accept projectIds and search all of a host's repos in shared queries (repo: qualifiers chunked to GitHub's 256-char limit), cutting search-quota usage from one call per repo to one per ~7 repos; rows carry projectId for attribution - direct ParadevAI#123/URL lookups use core-quota calls per repo instead of paging the Search API (up to 10 calls) per review-filter check - rate-limit failures map to TOO_MANY_REQUESTS with a friendly message and reset time instead of raw GitHub error text; never auto-retried - batch requests skip projects that fail to resolve (e.g. no GitHub remote) so one misconfigured repo cannot blank the whole list - Octokit fallback clamps pagination at GitHub's 1000-result search cap and enriches real check rollups via octokit.graphql instead of reporting 'No checks' when the gh CLI is unavailable
…etching - group project query targets by serving host and send one batched projectIds search per host page instead of one query per repository - generalize useMultiRepoProjectPagination over host targets; the pagination key encodes the project set so filter changes still reset - placeholderData keeps previous rows for the same target visible while a changed search or filter refetches (restores the placeholder behavior lost in the multi-repo rewrite) - issue selection clearing keys off filter content, not array identity - partial-failure banners get select-text cursor-text so errors can be copied into bug reports
|
Pushed three maintainer commits addressing the review findings plus a rate-limit fix that surfaced while verifying them. Review fixes
Rate limitingWith "All repositories" selected, every repo issued its own Search API call per page and per keystroke pause — ~28 repos against GitHub's 30-searches/minute quota tripped the limit after two interactions. Searches are now batched per serving host: one Verification
|
There was a problem hiding this comment.
8 issues found across 29 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/host-service/src/trpc/router/workspace-creation/shared/github-search.ts">
<violation number="1" location="packages/host-service/src/trpc/router/workspace-creation/shared/github-search.ts:114">
P2: Selecting two local projects that track the same GitHub repository silently drops the results for the later project, so users cannot open those work items in that project. The attribution contract should either deduplicate same-remotes before querying and intentionally expose one project, or preserve all matching project IDs and materialize a row for each selected project.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/components/RunIssuesInWorkspacePopover/RunIssuesInWorkspacePopover.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/components/RunIssuesInWorkspacePopover/RunIssuesInWorkspacePopover.tsx:151">
P2: Selecting issues from two project entries that point at the same GitHub repository is blocked as a mixed-repository batch, even though every issue can safely run against the selected project. Compare canonical repository identity (or include it in `SelectedIssue`) rather than project IDs for this guard.</violation>
</file>
<file name="packages/host-service/src/trpc/router/workspace-creation/procedures/search-github-issues.ts">
<violation number="1" location="packages/host-service/src/trpc/router/workspace-creation/procedures/search-github-issues.ts:213">
P2: Broad issue searches through the Octokit fallback can advertise pages beyond GitHub's 1,000-result search cap, causing infinite scroll to request unavailable pages. Bounding `hasNextPage` by the shared GitHub search result limit would keep the fallback consistent with pull-request search.</violation>
<violation number="2" location="packages/host-service/src/trpc/router/workspace-creation/procedures/search-github-issues.ts:312">
P2: When selected repositories require multiple query chunks, one requested page returns up to `limit` issues per chunk and later pages are appended chunk-by-chunk, so the task list can show more than 30 rows and place newer issues after older ones. A global pagination/merge step would need to fetch enough chunk results to produce one ordered `limit`-sized page (or explicitly expose chunk pagination).</violation>
</file>
<file name="packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts">
<violation number="1" location="packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts:374">
P2: Octokit direct review filters inspect only the first 100 reviews. PRs with older matching reviews or review decisions beyond that page can be incorrectly classified as not reviewed or as the wrong state; paginate review history until the viewer match or final decision is established.</violation>
<violation number="2" location="packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts:773">
P2: One repository's check-enrichment failure removes checks from every other repository in the same page because `Promise.all` rejects the entire batch and the outer catch keeps `merged`. Settling each repository independently would preserve successful check summaries while still allowing the failed repository's rows to render.</violation>
<violation number="3" location="packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts:897">
P1: Multi-repository searches do not return a global page: each query chunk fetches `limit` rows for the same `page`, then all rows are merged without a global slice. Chunked selections can therefore show too many rows on page 1 and omit or duplicate rows as the desktop list loads later pages; the chunk queries need a globally consistent pagination strategy.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx:109">
P1: The default/all-repositories Tasks view fails for any host serving more than 50 repositories because this call sends the entire host group while the `searchGitHubIssues` input rejects more than 50 `projectIds`; batching within the supported limit or changing the server contract would keep the all-repositories filter usable.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| const chunks = chunkProjectRepos(projectRepos, qualifiers); | ||
| const chunkResults = await Promise.all( | ||
| chunks.map((chunk) => | ||
| ghApiSearchPullRequests(ctx.execGh, chunk, qualifiers, page, limit), |
There was a problem hiding this comment.
P1: Multi-repository searches do not return a global page: each query chunk fetches limit rows for the same page, then all rows are merged without a global slice. Chunked selections can therefore show too many rows on page 1 and omit or duplicate rows as the desktop list loads later pages; the chunk queries need a globally consistent pagination strategy.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts, line 897:
<comment>Multi-repository searches do not return a global page: each query chunk fetches `limit` rows for the same `page`, then all rows are merged without a global slice. Chunked selections can therefore show too many rows on page 1 and omit or duplicate rows as the desktop list loads later pages; the chunk queries need a globally consistent pagination strategy.</comment>
<file context>
@@ -390,93 +719,194 @@ async function ghGetPullRequestChecks(
+ const chunks = chunkProjectRepos(projectRepos, qualifiers);
+ const chunkResults = await Promise.all(
+ chunks.map((chunk) =>
+ ghApiSearchPullRequests(ctx.execGh, chunk, qualifiers, page, limit),
+ ),
);
</file context>
| const client = getHostServiceClientByUrl(target.hostUrl); | ||
| return client.workspaceCreation.searchGitHubIssues.query({ | ||
| projectId: firstProject.projectId, | ||
| projectIds: target.projects.map((project) => project.projectId), |
There was a problem hiding this comment.
P1: The default/all-repositories Tasks view fails for any host serving more than 50 repositories because this call sends the entire host group while the searchGitHubIssues input rejects more than 50 projectIds; batching within the supported limit or changing the server contract would keep the all-repositories filter usable.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/GitHubIssuesContent/GitHubIssuesContent.tsx, line 109:
<comment>The default/all-repositories Tasks view fails for any host serving more than 50 repositories because this call sends the entire host group while the `searchGitHubIssues` input rejects more than 50 `projectIds`; batching within the supported limit or changing the server contract would keep the all-repositories filter usable.</comment>
<file context>
@@ -72,23 +88,25 @@ export function GitHubIssuesContent({
return client.workspaceCreation.searchGitHubIssues.query({
- projectId: target.projectId,
+ projectId: firstProject.projectId,
+ projectIds: target.projects.map((project) => project.projectId),
query: debouncedQuery.trim() || undefined,
limit: PAGE_SIZE,
</file context>
| const match = repositoryUrl?.match(REPOSITORY_URL_RE); | ||
| if (!match) return null; | ||
| const key = `${match[1]}/${match[2]}`.toLowerCase(); | ||
| for (const { projectId, repo } of chunk) { |
There was a problem hiding this comment.
P2: Selecting two local projects that track the same GitHub repository silently drops the results for the later project, so users cannot open those work items in that project. The attribution contract should either deduplicate same-remotes before querying and intentionally expose one project, or preserve all matching project IDs and materialize a row for each selected project.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/workspace-creation/shared/github-search.ts, line 114:
<comment>Selecting two local projects that track the same GitHub repository silently drops the results for the later project, so users cannot open those work items in that project. The attribution contract should either deduplicate same-remotes before querying and intentionally expose one project, or preserve all matching project IDs and materialize a row for each selected project.</comment>
<file context>
@@ -0,0 +1,206 @@
+ const match = repositoryUrl?.match(REPOSITORY_URL_RE);
+ if (!match) return null;
+ const key = `${match[1]}/${match[2]}`.toLowerCase();
+ for (const { projectId, repo } of chunk) {
+ if (`${repo.owner}/${repo.name}`.toLowerCase() === key) return projectId;
+ }
</file context>
| [issues], | ||
| ); | ||
|
|
||
| const hasMixedRepos = issueProjectIds.size > 1; |
There was a problem hiding this comment.
P2: Selecting issues from two project entries that point at the same GitHub repository is blocked as a mixed-repository batch, even though every issue can safely run against the selected project. Compare canonical repository identity (or include it in SelectedIssue) rather than project IDs for this guard.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/tasks/components/TasksView/components/TasksTopBar/components/RunIssuesInWorkspacePopover/RunIssuesInWorkspacePopover.tsx, line 151:
<comment>Selecting issues from two project entries that point at the same GitHub repository is blocked as a mixed-repository batch, even though every issue can safely run against the selected project. Compare canonical repository identity (or include it in `SelectedIssue`) rather than project IDs for this guard.</comment>
<file context>
@@ -141,7 +141,19 @@ export function RunIssuesInWorkspacePopover({
+ [issues],
+ );
+
+ const hasMixedRepos = issueProjectIds.size > 1;
+
const submitBlocker = useMemo<string | null>(() => {
</file context>
| }, | ||
| ]; | ||
| }); | ||
| const hasNextPage = page * perPage < data.total_count; |
There was a problem hiding this comment.
P2: Broad issue searches through the Octokit fallback can advertise pages beyond GitHub's 1,000-result search cap, causing infinite scroll to request unavailable pages. Bounding hasNextPage by the shared GitHub search result limit would keep the fallback consistent with pull-request search.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/workspace-creation/procedures/search-github-issues.ts, line 213:
<comment>Broad issue searches through the Octokit fallback can advertise pages beyond GitHub's 1,000-result search cap, causing infinite scroll to request unavailable pages. Bounding `hasNextPage` by the shared GitHub search result limit would keep the fallback consistent with pull-request search.</comment>
<file context>
@@ -110,82 +152,177 @@ async function ghApiSearchIssues(
+ },
+ ];
+ });
+ const hasNextPage = page * perPage < data.total_count;
+ return { items, totalCount: data.total_count, hasNextPage };
+}
</file context>
| issues: result.items, | ||
| totalCount: result.totalCount, | ||
| hasNextPage: result.hasNextPage, | ||
| issues: mergeByUpdatedAtDesc( |
There was a problem hiding this comment.
P2: When selected repositories require multiple query chunks, one requested page returns up to limit issues per chunk and later pages are appended chunk-by-chunk, so the task list can show more than 30 rows and place newer issues after older ones. A global pagination/merge step would need to fetch enough chunk results to produce one ordered limit-sized page (or explicitly expose chunk pagination).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/workspace-creation/procedures/search-github-issues.ts, line 312:
<comment>When selected repositories require multiple query chunks, one requested page returns up to `limit` issues per chunk and later pages are appended chunk-by-chunk, so the task list can show more than 30 rows and place newer issues after older ones. A global pagination/merge step would need to fetch enough chunk results to produce one ordered `limit`-sized page (or explicitly expose chunk pagination).</comment>
<file context>
@@ -110,82 +152,177 @@ async function ghApiSearchIssues(
- issues: result.items,
- totalCount: result.totalCount,
- hasNextPage: result.hasNextPage,
+ issues: mergeByUpdatedAtDesc(
+ chunkResults.map((result) => result.items),
+ ),
</file context>
| ); | ||
| const checksByProject = new Map<string, Map<number, PullRequestChecksInfo>>(); | ||
| await Promise.all( | ||
| [...numbersByProject].map(async ([projectId, numbers]) => { |
There was a problem hiding this comment.
P2: One repository's check-enrichment failure removes checks from every other repository in the same page because Promise.all rejects the entire batch and the outer catch keeps merged. Settling each repository independently would preserve successful check summaries while still allowing the failed repository's rows to render.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts, line 773:
<comment>One repository's check-enrichment failure removes checks from every other repository in the same page because `Promise.all` rejects the entire batch and the outer catch keeps `merged`. Settling each repository independently would preserve successful check summaries while still allowing the failed repository's rows to render.</comment>
<file context>
@@ -390,93 +719,194 @@ async function ghGetPullRequestChecks(
+ );
+ const checksByProject = new Map<string, Map<number, PullRequestChecksInfo>>();
+ await Promise.all(
+ [...numbersByProject].map(async ([projectId, numbers]) => {
+ const repo = repoByProject.get(projectId);
+ if (!repo) return;
</file context>
| owner: repo.owner, | ||
| repo: repo.name, | ||
| pull_number: pr.number, | ||
| per_page: 100, |
There was a problem hiding this comment.
P2: Octokit direct review filters inspect only the first 100 reviews. PRs with older matching reviews or review decisions beyond that page can be incorrectly classified as not reviewed or as the wrong state; paginate review history until the viewer match or final decision is established.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/host-service/src/trpc/router/workspace-creation/procedures/search-pull-requests.ts, line 374:
<comment>Octokit direct review filters inspect only the first 100 reviews. PRs with older matching reviews or review decisions beyond that page can be incorrectly classified as not reviewed or as the wrong state; paginate review history until the viewer match or final decision is established.</comment>
<file context>
@@ -166,13 +206,254 @@ async function ghDirectLookup(
+ owner: repo.owner,
+ repo: repo.name,
+ pull_number: pr.number,
+ per_page: 100,
+ });
+ const reviewedByViewer = reviews.some(
</file context>
hasNextPage reads the in-flight page's data, so a fresh next-page fetch unmounted the sentinel and no indicator showed; the row now stays mounted during isFetchingNextPage with a spinner.
…icons - useWorkItemsList extracts the host grouping, batched pagination, row attribution, totalCount, and repo-mismatch logic that PullRequestsContent and GitHubIssuesContent had duplicated token-for-token; both now keep only their query options, row rendering, and actions - LoadMoreSentinel shares the infinite-scroll sentinel/loading row - CHECK_STATUS_ICONS replaces the three value-identical status icon maps in PullRequestChecksSection, PullRequestChecksSummary, and the v2-workspace ChecksSection (v1 ReviewPanel left alone; surface is sunset)
Three groups of binary assets with zero references anywhere in the tree: - .github/pr-assets/ (644K): PR #6201's description screenshots; the description embeds the fork's raw URLs, so these copies serve nothing - assets/ (211K): example.png/notifs.png orphaned since the website moved into apps/marketing - apps/desktop/docs/assets/v1-migration-notices/ (1.2M): PR #6118 evidence screenshots never linked from any doc; the v1 surface is sunset Kept: apps/desktop/docs/artifacts (referenced by two runbooks) and packages/cli/demo recordings (outputs of the committed vhs demo kit).
Three groups of binary assets with zero references anywhere in the tree: - .github/pr-assets/ (644K): PR #6201's description screenshots; the description embeds the fork's raw URLs, so these copies serve nothing - assets/ (211K): example.png/notifs.png orphaned since the website moved into apps/marketing - apps/desktop/docs/assets/v1-migration-notices/ (1.2M): PR #6118 evidence screenshots never linked from any doc; the v1 surface is sunset Kept: apps/desktop/docs/artifacts (referenced by two runbooks) and packages/cli/demo recordings (outputs of the committed vhs demo kit).
* docs: weekly update for PRs merged 2026-08-05 to 2026-08-12 - workspaces: sessions (project-less workspaces, #6274), pin + drag-and-drop pinning (#6364), list/board views with Merged & Deleted history columns (#6296), per-project naming instructions (#6250), multi-PR links per branch with remove/restore (#5924) - automations: session automations (#6280), list health stats + Up next grouping + read-only team automations (#6291, #6276), detail page Settings/Run History tabs, inline run errors, would-run preview, RRule validation (#6305) - editor: image pane pan/pinch/zoom (#6285) - tasks: multi-repo filters, expanded task states, PR author/review filters, PR checks (#6201) - monorepos: per-project sparse checkout (#6034) - customization: project icon + color picker, v1 color carry-over (#6249) - terminal presets: built-in Superset CLI preset (#6270) - agents: resume interrupted sessions + Resume Args setting (#6253, #6265), Superset hooks toggle + scoping (#6072) - cli: interactive command browser + branded help (#6273); reference updated for session mode, --task, --skip-branch-prefix, --session (verified against packages/cli/src/commands) * docs: tighten wording and drop em dashes Shorten this week's additions per the writing guide: one thought per sentence, plainest word, no em dashes as prose punctuation. Also sweeps the three pre-existing em dashes in recipes. * docs: add screenshots from the 08-09 changelog batch cli command browser, agent resume banner, PR view with checks, and the project icon/color picker. Copied into apps/docs/public/images per the SOP rather than referenced across apps.
What & why
Tasks and Pull requests were limited to one repository at a time, task status filtering exposed only broad buckets, and pull request checks were invisible until leaving Superset.
This change:
How I tested it
bun teston the ten original focused test suites: 43 pass, 0 fail, 115 expectationsbun run lint: pass with zero warningsbun run typecheck: passbun run --cwd apps/desktop compile:app: passhttp://localhost:3205: selected two repositories through real pointer input, verified both repositories in Tasks and PRs, opened expanded task states, filtered PRs by sanitized author and review status, inspected PR check summaries and detail Checks, navigated detail/back, fully restarted the app, and confirmed the author/review/repository settings persistedAll visual fixtures use only
acme-*,demo-*, andexample.invaliddata. No production issues, pull requests, users, or URLs are included.Pull request list
Pull request detail
Expanded task states
Pull request author and review filters
Checklist
type(scope): subject)bun run lintandbun run typecheckpass