Conversation
- IntegrationError, SyncStatus, InvestigationStatus - PaginationState, FilterState (GitHub/GitLab specific) - Shared types for GitHub and GitLab integration layer Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- calculateHasMore, appendWithoutDuplicates - getNextPage, resetPagination - DRY utilities for GitHub and GitLab pagination Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- applyFilter, getFilterPredicate, isValidFilterState - Normalization for 'open' vs 'opened' (GitHub vs GitLab) - Generic filter functions for both platforms Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Zustand store with actions and selectors - Filter state management (opened/closed/all) - loadGitLabIssues and importGitLabIssues functions - Mock factory tests following codebase patterns Note: Pagination to be added with IPC handlers (Task 8) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- sync-status-store.ts: Connection status tracking - investigation-store.ts: Issue investigation state tracking - Both stores follow Zustand patterns with actions and selectors - Tests using mock factory patterns Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Export issues-store, investigation-store, sync-status-store - Include cleanupMRReviewListeners - Add cleanupGitLabListeners function Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- GITLAB_MR_LIST_MORE: Load more MRs (pagination) - GITLAB_MR_DELETE_REVIEW: Delete a posted review - GITLAB_MR_CHECK_MERGE_READINESS: Check if MR can be merged - GITLAB_MR_GET_REVIEWS_BATCH: Batch load reviews for multiple MRs - GITLAB_MR_FIX: Auto-fix issues in MR - GITLAB_PR_GET_LOGS: Get AI review logs - GITLAB_PR_STATUS_POLL_START/STOP: Poll MR status - GITLAB_PR_MEMORY_GET/SEARCH: Memory operations for MR reviews Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- GITLAB_MR_DELETE_REVIEW: Delete posted review notes - GITLAB_MR_CHECK_MERGE_READINESS: Check if MR can be merged - GITLAB_PR_GET_LOGS: Get AI review logs from disk - GITLAB_PR_STATUS_POLL_START/STOP: Poll MR status updates - GITLAB_PR_MEMORY_GET/SEARCH: Memory operations (TODO: Graphiti) - GITLAB_MR_FIX: Auto-fix placeholder (use GITLAB_AUTOFIX_START) - GITLAB_MR_GET_REVIEWS_BATCH: Batch load reviews for multiple MRs - GITLAB_MR_LIST_MORE: Load more MRs (pagination) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- listMoreGitLabMRs: Load more MRs (pagination) - getGitLabMRReviewsBatch: Batch load reviews for multiple MRs - deleteGitLabMRReview: Delete posted review notes - fixGitLabMR: Auto-fix issues in MR - startGitLabMRStatusPoll/stopGitLabMRStatusPoll: Poll MR status - getGitLabMRLogs: Get AI review logs - getGitLabMRMemory/searchGitLabMRMemory: Memory operations - checkGitLabMRMergeReadiness: Check if MR can be merged Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use await for getGitLabConfig (returns Promise) - Access config.project instead of project.projectPathWithNamespace - Fix TypeScript errors in new handlers Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Import IPCResult type from shared/types - Use getMainWindow() instead of mainWindow in polling handler - Get config inside interval to avoid stale references Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Parse GitLab API errors into user-friendly messages - Handle authentication, rate limiting, network errors - Provide recoverable status and suggested actions - Tests for all error types Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add listMoreMRs, deleteReview, checkMergeReadiness, and getLogs to the UseGitLabMRsResult interface and return statement. Remove unused _mrReviews variable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add MRFilterBar, MRStatusIndicator, MRLogs, GitLabAutoFixButton, and GitLabBatchReviewWizard components following GitHub patterns. Updated component exports for both gitlab-issues and gitlab-merge-requests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add filtering hook for GitLab MRs following the pattern of usePRFiltering for GitHub PRs. Supports search, contributors, statuses, and sort options. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use existing PR types for MRLogs (PRLogs, PRLogEntry, etc.) - Fix MRFilterBar import path for useGitLabMRFiltering hook - Fix BatchReviewWizard to use correct GitLab type properties (iid, primaryIssue) - Fix useGitLabMRFiltering to use actual GitLabMRReviewResult properties - Use title length sorting instead of additions/deletions for GitLab MRs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fixed ElectronAPI type definition to import from preload API - Changed shared/types/ipc.ts to import PreloadElectronAPI - This ensures all GitLab methods are properly typed - Fixed BatchReviewWizard component variable names - setSelectedSingleIssueIids -> setSelectedSingleIids - Added GitLabAnalyzePreviewProgress type definition - Fixed AutoFixButton error callback signature - Changed to match API: (projectId: string, error: string) - Fixed useGitLabMRs.ts return type handling - Properly handle undefined result.data with null coalescing - Fixed issues-store.ts type issues - Changed IssueFilterState to exclude 'merged' (issues don't have merged state) - Removed invalid 'open' state comparisons - Fixed mr-review-handlers.ts null return issues - Wrapped withProjectOrNull calls to handle null returns - Added BrowserWindow import (was type-only import) - Fixed async/await issues in listMoreMRs handler - Fixed test files to match actual store implementation - Removed pagination test references (not implemented yet) - Fixed sync-status-store.test.ts property name Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Clear ANTHROPIC_DEFAULT_OPUS_MODEL env var in beforeEach - Prevents environment from affecting test results - All 4247 tests now pass (191 test files) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix memory leak in GitLab MR status polling by adding clearPollingForProject() - Fix race condition in polling by adding concurrency guard with pollingInProgress Set - Rename IPC channels from GITLAB_PR_* to GITLAB_MR_* for consistency - Fix redundant condition in useGitLabMRFiltering hook - Remove useless switch cases in StatusIndicator components - Export GitLab API from preload module index - Refactor gitlab-error-parser to return error codes for i18n - Update tests to match new error parser API Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
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:
📝 WalkthroughWalkthroughAdds extensive GitLab MR and issue functionality across main, preload, renderer, and shared layers: MR status polling and IPC handlers, expanded preload APIs, new renderer components/hooks/stores for MR filtering, logs, auto‑fix and batch reviews, shared integration types/utilities, tests, mocks, i18n, and defensive runtime checks. Changes
Sequence Diagram(s)sequenceDiagram
participant Renderer
participant Preload
participant Main
participant GitLabAPI
rect rgba(200,230,255,0.5)
Renderer->>Preload: invoke startGitLabMRStatusPoll(projectId,mrIid,intervalMs)
Preload->>Main: IPC GITLAB_MR_STATUS_POLL_START
Main->>Main: register interval (statusPollingIntervals)
end
rect rgba(220,255,220,0.5)
loop periodic
Main->>GitLabAPI: fetch MR status
GitLabAPI-->>Main: MR status response
Main->>Renderer: BrowserWindow.webContents.send(GITLAB_MR_STATUS_UPDATE, ...)
end
end
rect rgba(255,230,200,0.5)
Renderer->>Preload: invoke stopGitLabMRStatusPoll(projectId,mrIid)
Preload->>Main: IPC GITLAB_MR_STATUS_POLL_STOP
Main->>Main: clear interval, update pollingInProgress
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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 |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the application by introducing comprehensive GitLab integration, bringing it to feature parity with the existing GitHub support. The changes enable users to seamlessly manage GitLab issues and merge requests, leveraging AI-powered tools for review, analysis, and workflow automation. This broad update includes new user interface elements, robust state management, and extended backend capabilities to ensure a consistent and powerful experience across both platforms. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx
Fixed
Show fixed
Hide fixed
There was a problem hiding this comment.
Code Review
This pull request introduces significant new functionality to achieve feature parity with the existing GitLab integration. The changes are extensive, including new UI components, state management stores, backend IPC handlers, and shared utilities. Overall, the code is well-structured and follows existing patterns from the GitHub integration. I've identified a critical bug related to type mismatch in the StatusIndicator component, along with a few medium-severity issues concerning type safety, code clarity, and robustness in the IPC handlers. Addressing these points will improve the correctness and maintainability of the new GitLab features.
| export function MRStatusIndicator({ | ||
| checksStatus, | ||
| reviewsStatus, | ||
| mergeableState, | ||
| className, | ||
| compact = false, | ||
| showMergeStatus = true, | ||
| }: MRStatusIndicatorProps) { |
There was a problem hiding this comment.
There's a critical type mismatch issue with the mergeableState prop. It is typed as MergeableState (e.g., 'clean', 'dirty'), but it's used as a key for mergeKeyMap, which expects GitLab-specific statuses (e.g., 'can_be_merged'). It's also passed directly to MergeReadinessIcon, which expects the MergeableState enum.
This will cause incorrect behavior: mergeKey will be undefined, and MergeReadinessIcon will always fall back to the default case.
To fix this, you should probably pass the raw GitLab merge status string (e.g., 'can_be_merged') as a prop (e.g., mergeStatus: string | null) and then derive both the mergeKey for the tooltip and the correct MergeableState for the icon inside this component.
|
|
||
| try { | ||
| // Emit status update to renderer | ||
| const mainWindow = BrowserWindow.getAllWindows()[0]; |
There was a problem hiding this comment.
The use of BrowserWindow.getAllWindows()[0] to get the main window is not robust. This assumes there is always exactly one window and that it is the correct one to send updates to. This can lead to errors if, for example, a splash screen is introduced, or if multiple project windows are ever supported. A more robust approach would be to manage window references more explicitly, perhaps by associating a polling operation with a specific window ID.
| `/projects/${encodedProject}/merge_requests?${queryParams.toString()}` | ||
| ) as any[]; | ||
|
|
||
| // Check if there are more MRs by fetching one more item |
There was a problem hiding this comment.
The comment here is misleading. The code doesn't fetch one more item to check if there are more pages. Instead, it checks if the number of returned items equals the page size, which is a standard pagination strategy. The comment should be updated to accurately describe the logic to avoid confusion for future maintainers.
| // Check if there are more MRs by fetching one more item | |
| // Check if there might be more MRs if the returned count matches the page size |
| state?: 'opened' | 'closed' | 'merged' | 'all', | ||
| page?: number | ||
| ) => Promise<IPCResult<{ mrs: any[]; hasMore: boolean }>>; | ||
| getGitLabMRReviewsBatch: (projectId: string, mrIids: number[]) => Promise<IPCResult<Record<number, any>>>; |
There was a problem hiding this comment.
The return type for getGitLabMRReviewsBatch uses Record<number, any>, which is not type-safe. The corresponding IPC handler in mr-review-handlers.ts returns a more specific type: IPCResult<Record<number, MRReviewResult | null>>. Using the specific type here will improve type safety and prevent potential runtime errors.
| getGitLabMRReviewsBatch: (projectId: string, mrIids: number[]) => Promise<IPCResult<Record<number, any>>>; | |
| getGitLabMRReviewsBatch: (projectId: string, mrIids: number[]) => Promise<IPCResult<Record<number, import('../../../../shared/types').GitLabMRReviewResult | null>>>; |
There was a problem hiding this comment.
Actionable comments posted: 33
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/stores/gitlab/index.ts (1)
44-55:⚠️ Potential issue | 🟠 MajorAdd investigation listener lifecycle to
initializeGitLabListeners()andcleanupGitLabListeners().The investigation event listeners (
onGitLabInvestigationProgress,onGitLabInvestigationComplete,onGitLabInvestigationError) are exposed in the preload API and emitted by the main process, but they are not wired into the global listener initialization. Currently, they are only registered inside the component hookuseGitLabInvestigation.tsat render time. Follow the MR review pattern (implemented inmr-review-store.ts:181-242) by exportinginitializeInvestigationListeners()andcleanupInvestigationListeners()frominvestigation-store.ts, then call them from the barrel's init/cleanup functions to establish a consistent global listener lifecycle.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/stores/gitlab/index.ts` around lines 44 - 55, The initialize/cleanup barrel is missing the investigation listener lifecycle; implement and export initializeInvestigationListeners() and cleanupInvestigationListeners() in investigation-store.ts (mirroring the MR review pattern in mr-review-store.ts) that register the preload events onGitLabInvestigationProgress, onGitLabInvestigationComplete, and onGitLabInvestigationError, then call initializeInvestigationListeners() from initializeGitLabListeners() and cleanupInvestigationListeners() from cleanupGitLabListeners() so the global init/teardown mirrors _initMRReviewListeners()/_cleanupMRReviewListeners() and ensures listeners are registered at app init and removed on unmount/hot-reload.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts`:
- Around line 1234-1254: The two IPC handlers registered with ipcMain.handle for
IPC_CHANNELS.GITLAB_MR_MEMORY_GET and IPC_CHANNELS.GITLAB_MR_MEMORY_SEARCH
should not return { success: true, data: [] } while unimplemented; change them
to return a clear not-implemented IPCResult (e.g., success: false with a
standardized error/code like error: 'NOT_IMPLEMENTED' and a message) so the
renderer can distinguish "feature unavailable" from "no results"; update the
handlers in mr-review-handlers.ts where the async callbacks for those channels
are defined to return that not-implemented result (or call a shared helper like
notImplementedIPCResult()) instead of an empty success payload.
- Around line 1336-1337: The current logic sets hasMore from mrs.length === 20
which is unreliable; change the MR fetch to either (a) use GitLab pagination
metadata from the API response (the Link header or response.pagination fields)
to determine hasMore, or (b) over-fetch by one item (request pageSize + 1 MRs),
then set hasMore = fetched.length > pageSize and slice the returned array to
pageSize before returning; update the related variables/returns in the MR
handler (mrs, hasMore) in mr-review-handlers.ts accordingly so the UI only shows
"load more" when there truly are more pages.
- Around line 1004-1035: The delete handler for
IPC_CHANNELS.GITLAB_MR_DELETE_REVIEW only removes the remote note (in the
ipcMain.handle block using withProjectOrNull, encodeProjectPath and gitlabFetch)
but does not update the local posted-review cache; after a successful DELETE
(before returning { success: true, data: { deleted: true } }) invalidate or
update the local cache: either remove the noteId's entries from the
posted-review cache (maintain or consult a noteId -> findingIds mapping) or
trigger a cache refresh for that project/MR (call your existing cache
refresh/invalidate function, e.g. refreshPostedReviewsCache(project, mrIid) or
invalidatePostedReviewCache(project, mrIid)) so
has_posted_findings/posted_finding_ids are cleared when the remote note is
deleted.
- Around line 1060-1075: The code currently reads mrData.merge_status and
mrData.discussion_locked which are deprecated/incorrect for mergeability and
discussion resolution; update the logic to use mrData.detailed_merge_status and
mrData.blocking_discussions_resolved: replace mergeStatus = mrData.merge_status
with detailedStatus = mrData.detailed_merge_status (default to
'cannot_be_merged') and compute canMerge from detailedStatus === 'can_be_merged'
(or allowed detailed values), replace hasConflicts = mrData.has_conflicts ||
false unchanged, and replace needsDiscussion = !mrData.discussion_locked with
needsDiscussion derived from (detailedStatus === 'discussions_not_resolved' ||
mrData.blocking_discussions_resolved === false) so discussion resolution
reflects GitLab’s fields; keep pipelineStatus = mrData.pipeline?.status.
In `@apps/desktop/src/preload/api/modules/gitlab-api.ts`:
- Around line 83-95: The public IPC types exposing `any` should be replaced with
the proper types: change listMoreGitLabMRs to return Promise<IPCResult<{ mrs:
GitLabMergeRequest[]; hasMore: boolean }>>; change getGitLabMRReviewsBatch to
return Promise<IPCResult<Record<number, GitLabMRReviewResult>>>; change
getGitLabMRMemory and searchGitLabMRMemory to return
Promise<IPCResult<unknown[]>> (keep getGitLabMRLogs as string[]). Update the
corresponding implementations for the same functions (listMoreGitLabMRs,
getGitLabMRReviewsBatch, getGitLabMRMemory, searchGitLabMRMemory) to
return/construct values matching these types.
In
`@apps/desktop/src/renderer/components/gitlab-issues/components/AutoFixButton.tsx`:
- Around line 11-14: Replace the relative imports in AutoFixButton.tsx with the
project path aliases: change '../../ui/button' and '../../ui/progress' to the
`@components` alias (e.g., '@components/ui/button' and '@components/ui/progress')
and change '../../../../shared/types' to the `@shared` alias (e.g.,
'@shared/types') for the GitLabIssue and GitLabAutoFix* type imports; update the
import statements that reference Button, Progress, GitLabIssue,
GitLabAutoFixConfig, GitLabAutoFixProgress, and GitLabAutoFixQueueItem so they
use the alias paths instead of relative paths and then run the build/TS check to
verify no unresolved module errors.
- Around line 65-73: The error handler currently only filters by projectId
causing all AutoFixButton instances to receive unrelated errors; update the IPC
signature in gitlab-api.ts so onGitLabAutoFixError includes issueIid (e.g.,
(projectId: string, issueIid: string, error: string)), then update the listener
usage in AutoFixButton.tsx (window.electronAPI.onGitLabAutoFixError) to accept
and check issueIid === issue.iid in the callback before calling
setError/setProgress/setIsStarting; also update the emitter side that sends the
error IPC to include issueIid so the new signature is respected.
In
`@apps/desktop/src/renderer/components/gitlab-issues/components/BatchReviewWizard.tsx`:
- Around line 42-63: Move the exported type definitions
GitLabAnalyzePreviewProgress and GitLabProposedBatch out of the UI component
file and into the shared types module (e.g. add them to
`@shared/types/integrations.ts` alongside
GitLabAutoFixProgress/GitLabAutoFixQueueItem), export them from that module,
then update BatchReviewWizard.tsx to import { GitLabAnalyzePreviewProgress,
GitLabProposedBatch } from `@shared/types/integrations` instead of declaring them
locally; also search for any other usages and update imports to the new shared
export.
- Around line 191-223: handleApprove currently awaits onApproveBatches without
catching rejections, which can leave the UI stuck; wrap the onApproveBatches
call in a try/catch inside handleApprove, move setStep('done') into the try
block, and in the catch block log the error and surface it to the user (e.g.,
set an error state or call your existing toast/error UI) and reset the step or
approving state as appropriate; reference the handleApprove function, the
onApproveBatches callback, and setStep to implement this error handling and
recovery flow.
- Around line 20-40: Update the import paths in BatchReviewWizard.tsx to use the
project's path aliases instead of relative paths: replace imports like
'../../ui/button', '../../ui/badge', '../../ui/progress',
'../../ui/scroll-area', '../../ui/checkbox', '../../ui/dialog',
'../../ui/collapsible' with the corresponding '@components/...' alias modules
and change '../../../../shared/types' (GitLabAnalyzePreviewResult) to use the
'@shared/types' alias; keep the same imported symbols (Button, Badge, Progress,
ScrollArea, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter,
DialogHeader, DialogTitle, Collapsible, CollapsibleContent, CollapsibleTrigger,
GitLabAnalyzePreviewResult) so only the module specifiers are updated.
- Around line 547-549: The percentage is concatenated into the translation key
instead of using interpolation; update the span in BatchReviewWizard to call t
with an interpolated value (e.g., t('gitlab:batchReview.similar', { percent:
Math.round(issue.similarityToPrimary * 100) })) and remove the manual
concatenation so the translator can place the percent correctly; use the same
symbol issue.similarityToPrimary and the existing
t('gitlab:batchReview.similar') call.
- Around line 260-262: The percentage is concatenated into the translation
string in BatchReviewWizard; change the t call to use interpolation (pass the
numeric value as a variable, e.g., t('gitlab:batchReview.percentComplete', {
value: analysisProgress?.progress ?? 0 })) and update the translation entry
"percentComplete" to use interpolation like "{{value}}% complete" so locales can
reorder/format the number correctly.
- Around line 78-89: The GitLabBatchReviewWizard component currently
destructures a projectId prop that isn't used; remove projectId from the
GitLabBatchReviewWizardProps type and from the parameter list of the
GitLabBatchReviewWizard function, and then update any call sites that pass
projectId to stop providing it (or refactor them if they rely on it) so the prop
is fully removed. Ensure you search for the symbol projectId in relation to
GitLabBatchReviewWizardProps and the GitLabBatchReviewWizard component to update
types and invocations consistently.
In
`@apps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.ts`:
- Around line 96-101: The default error object in the GitLab error parser
currently marks unknown errors as recoverable; update the default return so
unknown errors use recoverable: false instead of true (the object containing
code: GitLabErrorCode.UNKNOWN, details: message) so that non-Error fallback
paths and retry logic do not treat unknown messages as retryable; locate the
default return in gitlab-error-parser.ts (the block returning { code:
GitLabErrorCode.UNKNOWN, recoverable: true, details: message }) and change
recoverable to false.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx`:
- Around line 198-205: The UI still contains hardcoded English strings and
US-only time formatting in MRLogs.tsx (e.g., the "MR #{mrIid}" label,
orchestrator summary fragments, the More/Less toggle, and any direct
toLocaleTimeString('en-US', ...) calls); update the component to use
react-i18next's t(...) for all user-facing text (reference symbols: mrIid
display, logs.is_followup Badge label, the More/Less toggle text, isStreaming
Badge text and any orchestrator summary fragments) and replace explicit 'en-US'
time formatting with locale-aware formatting (e.g., Intl.DateTimeFormat or
toLocaleTimeString(undefined, ...) driven by the active locale), then add
corresponding keys to the en and fr translation JSON files so those strings are
localized.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsx`:
- Around line 122-126: The mergeKeyMap keys are using GitLab statuses
(can_be_merged, cannot_be_merged, checking) while the code (and
MergeReadinessIcon) uses MergeableState values ('clean', 'dirty', 'blocked'),
causing mergeKey to be undefined and the status UI to not render; update
mergeKeyMap to use the MergeableState keys (e.g., 'clean' -> 'ready', 'dirty' ->
'conflict', 'blocked' -> 'checking' or whatever the intended mapping is) and
ensure any other occurrences (the block around mergeKeyMap and the code
referenced by MergeReadinessIcon and the mergeKey lookup in the same component)
are updated to match the same keys.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts`:
- Around line 9-10: The callback contract for getReviewStateForMR uses the wrong
type for the progress parameter; change the parameter type from
GitLabMRReviewResult | null to GitLabMRReviewProgress | null and update the
import list at the top of the file to include GitLabMRReviewProgress (add it
alongside GitLabMergeRequest, GitLabMRReviewResult, GitLabNewCommitsCheck) so
the signature in useGitLabMRFiltering.ts matches the progress shape supplied by
useGitLabMRs.ts.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRs.ts`:
- Around line 295-302: In the unsuccessful IPC branches of listMoreGitLabMRs,
checkMergeReadiness, and getLogs inside useGitLabMRs.ts, propagate the returned
result.error instead of failing silently: when result.success is false, call
setError(result.error) and then throw new Error(result.error) (or return a
rejected promise) so both component state and callers receive the error; update
the error-handling blocks in the functions that call
window.electronAPI.listMoreGitLabMRs, window.electronAPI.checkMergeReadiness,
and window.electronAPI.getLogs to perform these two steps, referencing the
existing setError setter and the functions
listMoreGitLabMRs/checkMergeReadiness/getLogs to locate the changes.
- Around line 312-318: The current code clears local review state when
result.success is true, but the backend may return success with deleted:false;
update the post-IPC check so you only call
useMRReviewStore.getState().clearMRReview(projectId, mrIid) when the deletion
was actually performed (e.g., require result.deleted === true in addition to
result.success) after the window.electronAPI.deleteGitLabMRReview(...) call
inside the deleteReview handler, leaving the returned value logic unchanged.
In
`@apps/desktop/src/renderer/stores/gitlab/__tests__/investigation-store.test.ts`:
- Line 6: The test imports GitLabInvestigationStatus and
GitLabInvestigationResult via a deep relative path; replace that relative import
with the project's path alias for shared types (e.g., import {
GitLabInvestigationStatus, GitLabInvestigationResult } from '@shared/types' or
the configured alias in tsconfig) so the test uses the canonical alias import
for shared types in investigation-store.test.ts.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.ts`:
- Around line 49-58: The test "should append issues without duplicates" is
misleading because it calls useIssuesStore.getState().setIssues directly instead
of exercising the append/dedupe logic; update the test to either (A) rename the
spec to reflect plain replacement (e.g., "should set issues") if append isn't
implemented, or (B) implement a real append scenario: call the store's
appendIssues (or add an appendIssues helper) with an array containing a
duplicate createMockGitLabIssue({ iid: X }) and a new issue, then assert
useIssuesStore.getState().issues has deduped entries (length matches unique iids
and no duplicate iid present). Ensure you reference
useIssuesStore.getState().setIssues, useIssuesStore.getState().appendIssues (or
the new append helper), and createMockGitLabIssue when making the change.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts`:
- Around line 8-61: Add tests that invoke the exported async helper
checkGitLabConnection (which calls window.electronAPI.checkGitLabConnection and
then updates useSyncStatusStore) instead of only calling raw setters; mock
window.electronAPI.checkGitLabConnection to resolve with a success payload and
assert the store reflects connected=true and projectPathWithNamespace, then mock
it to reject or return an error payload and assert connectionError and
disconnected state (use useSyncStatusStore.getState().clearSyncStatus() in
beforeEach to isolate tests).
In `@apps/desktop/src/renderer/stores/gitlab/investigation-store.ts`:
- Around line 8-11: The import of GitLabInvestigationStatus and
GitLabInvestigationResult should use the project path alias instead of a
relative path; update the import statement that currently brings in
"GitLabInvestigationStatus" and "GitLabInvestigationResult" from
'../../../shared/types' to use the `@shared/`* alias (e.g. import from
'@shared/types') so the store (investigation-store.ts) follows the tsconfig
path-alias guideline.
In `@apps/desktop/src/renderer/stores/gitlab/issues-store.ts`:
- Around line 128-148: importGitLabIssues never clears the store error, so a
prior failure can linger; update importGitLabIssues to reset the error state by
calling store.setError('') (or the store's empty-value) at the start of the
function (after setLoading(true)) and again immediately when result.success is
true (before returning true) so successful imports clear any previous error;
reference the importGitLabIssues function and the store.setError /
store.setLoading calls to locate where to add these resets.
- Around line 98-123: loadGitLabIssues can be overwritten by stale async
responses; generate a unique request token at the start of loadGitLabIssues,
save it to the shared store (useIssuesStore) as the currentRequestToken before
awaiting window.electronAPI.getGitLabIssues, and attach that token to any state
changes triggered by this call (e.g., when you call
store.setFilterState(state)). After awaiting the API, only call store.setIssues,
store.setError, or clear loading if the token still matches
store.currentRequestToken (so a later call won’t be clobbered by an earlier
response); include the same token check in the catch and finally blocks to
ensure loading/ error aren’t applied from stale requests.
In `@apps/desktop/src/renderer/stores/gitlab/sync-status-store.ts`:
- Around line 63-69: When a GitLab connection check fails the code only calls
store.setConnectionError, leaving any previously populated store.syncStatus
stale; update both failure paths (the result.error branch and the catch block)
to also clear the stored status by calling the store method that resets
syncStatus (e.g., setSyncStatus(null) or clearSyncStatus) so that isConnected()
and getProjectPath() do not return outdated data; modify the branches around the
existing store.setConnectionError(...) calls to additionally clear/reset
syncStatus.
In `@apps/desktop/src/shared/i18n/locales/en/gitlab.json`:
- Around line 252-298: There are two duplicate "mrReview" objects; merge them
into a single mrReview entry by combining all unique keys from both blocks
(include keys from the first block such as runReview, followupReview,
newCommits, cancel, postFindings, approve, merge, status, overallStatus,
resolution, etc. and keys from the second block such as reviewed, posted,
changesRequested, searchPlaceholder, contributors, sort, logs, selectedCount,
noResultsFound, clearFilters, reset, etc.), ensure no key names conflict
(preserve nested objects like status, overallStatus, and logs), and then remove
the duplicate mrReview block so the JSON contains only one consolidated mrReview
object.
In `@apps/desktop/src/shared/i18n/locales/fr/gitlab.json`:
- Around line 262-308: The fr locale defines mrReview twice which causes the
latter block to overwrite the former and drop keys like runReview, postFindings,
approve, status; merge the two mrReview objects into a single mrReview entry
that contains all unique child keys (keep reviewed, posted, changesRequested,
readyToMerge, runReview, postFindings, approve, status, logs, etc.), remove the
duplicate mrReview object, and ensure the resulting JSON remains valid (no
duplicate keys, commas and braces correct) so all translations are preserved.
In `@apps/desktop/src/shared/integrations/filters/__tests__/filter-utils.test.ts`:
- Around line 22-32: Add a regression test that exercises the GitHub-native
'open' filter value and validate normalization: update the test in
filter-utils.test.ts to call applyFilter(items, 'open') (using the existing
TestItem array) and assert it returns the same two ids [1,2]; also add
assertions for isValidFilterState('open') and isValidFilterState('opened') to
ensure both are considered valid by the public API (referencing applyFilter,
isValidFilterState, FilterState and TestItem to locate the relevant
functions/types); mirror this change for the similar case around lines 43-48 to
keep coverage consistent.
In
`@apps/desktop/src/shared/integrations/pagination/__tests__/pagination-utils.test.ts`:
- Around line 1-2: The test suite currently only covers calculateHasMore and
appendWithoutDuplicates; import getNextPage and resetPagination from
'../pagination-utils' and add unit tests for them: call getNextPage with sample
values (e.g., 1 -> 2, 5 -> 6) and assert results, and call resetPagination(),
then assert returned object has currentPage === 1 and hasMore === true; ensure
the new tests use the same vitest imports (describe/it/expect) and are placed
alongside the existing tests.
In `@apps/desktop/src/shared/integrations/pagination/pagination-utils.ts`:
- Around line 5-7: The current calculateHasMore(totalCount, pageSize) only
checks if totalCount > pageSize (i.e., more than one page) which is misleading
for general pagination; either change the API to accept currentPage and return
(currentPage * pageSize) < totalCount (update calculateHasMore to
calculateHasMore(totalCount, pageSize, currentPage) and adjust callsites), or if
the intent is strictly a first-page-only check, rename the function to something
explicit like isMultiPageOrHasMoreAfterFirstPage and add a JSDoc comment on
calculateHasMore (or the renamed function) stating it only checks whether
totalCount exceeds a single page; pick one approach and apply it consistently
across usages of calculateHasMore.
In `@apps/desktop/src/shared/types/ipc.ts`:
- Around line 173-177: Replace the relative import of the Preload ElectronAPI
with the path-alias import: change the import that currently reads import type {
ElectronAPI as PreloadElectronAPI } from '../../preload/api' to use the
`@preload/`* alias (import type { ElectronAPI as PreloadElectronAPI } from
'@preload/api'), leaving the export type ElectronAPI = PreloadElectronAPI;
unchanged so the re-export continues to work.
- Around line 186-188: Add a deprecation note and tracking reference to the
legacy interface by annotating the ElectronAPILegacy interface with a JSDoc
`@deprecated` tag and a short comment or TODO linking to the tracking issue/PR
that will remove it (or an issue number/URL), so future maintainers know when
and why it can be removed; update the comment above ElectronAPILegacy to include
the `@deprecated` tag and the tracking link/issue ID.
---
Outside diff comments:
In `@apps/desktop/src/renderer/stores/gitlab/index.ts`:
- Around line 44-55: The initialize/cleanup barrel is missing the investigation
listener lifecycle; implement and export initializeInvestigationListeners() and
cleanupInvestigationListeners() in investigation-store.ts (mirroring the MR
review pattern in mr-review-store.ts) that register the preload events
onGitLabInvestigationProgress, onGitLabInvestigationComplete, and
onGitLabInvestigationError, then call initializeInvestigationListeners() from
initializeGitLabListeners() and cleanupInvestigationListeners() from
cleanupGitLabListeners() so the global init/teardown mirrors
_initMRReviewListeners()/_cleanupMRReviewListeners() and ensures listeners are
registered at app init and removed on unmount/hot-reload.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6581afab-1b76-445b-a7cf-a9114cbffb5c
📒 Files selected for processing (34)
apps/desktop/src/main/ai/config/__tests__/phase-config.test.tsapps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.tsapps/desktop/src/preload/api/modules/gitlab-api.tsapps/desktop/src/preload/api/modules/index.tsapps/desktop/src/renderer/components/github-prs/components/StatusIndicator.tsxapps/desktop/src/renderer/components/gitlab-issues/components/AutoFixButton.tsxapps/desktop/src/renderer/components/gitlab-issues/components/BatchReviewWizard.tsxapps/desktop/src/renderer/components/gitlab-issues/components/index.tsapps/desktop/src/renderer/components/gitlab-issues/utils/__tests__/gitlab-error-parser.test.tsapps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.tsapps/desktop/src/renderer/components/gitlab-merge-requests/components/MRFilterBar.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/components/index.tsapps/desktop/src/renderer/components/gitlab-merge-requests/hooks/index.tsapps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.tsapps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRs.tsapps/desktop/src/renderer/stores/gitlab/__tests__/investigation-store.test.tsapps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.tsapps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.tsapps/desktop/src/renderer/stores/gitlab/index.tsapps/desktop/src/renderer/stores/gitlab/investigation-store.tsapps/desktop/src/renderer/stores/gitlab/issues-store.tsapps/desktop/src/renderer/stores/gitlab/sync-status-store.tsapps/desktop/src/shared/constants/ipc.tsapps/desktop/src/shared/i18n/locales/en/gitlab.jsonapps/desktop/src/shared/i18n/locales/fr/gitlab.jsonapps/desktop/src/shared/integrations/filters/__tests__/filter-utils.test.tsapps/desktop/src/shared/integrations/filters/filter-utils.tsapps/desktop/src/shared/integrations/pagination/__tests__/pagination-utils.test.tsapps/desktop/src/shared/integrations/pagination/pagination-utils.tsapps/desktop/src/shared/integrations/types/__tests__/base-types.test.tsapps/desktop/src/shared/integrations/types/base-types.tsapps/desktop/src/shared/types/ipc.ts
apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts
Outdated
Show resolved
Hide resolved
| it('should normalize open/opened states (GitHub vs GitLab)', () => { | ||
| const items: TestItem[] = [ | ||
| { id: 1, state: 'open' }, // GitHub format | ||
| { id: 2, state: 'opened' }, // GitLab format | ||
| { id: 3, state: 'closed' } | ||
| ]; | ||
| // Filter with 'opened' should match both 'open' and 'opened' | ||
| const result = applyFilter(items, 'opened'); | ||
| expect(result).toHaveLength(2); | ||
| expect(result.map((i: TestItem) => i.id)).toEqual([1, 2]); | ||
| }); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add a GitHub-side 'open' regression case.
These tests only prove that filtering with 'opened' matches 'open'/'opened'; they never exercise callers passing the GitHub-native 'open' filter from FilterState. If input normalization regresses, GitHub flows can break while this suite stays green. It would also be worth covering isValidFilterState, since it's part of the new public API.
As per coding guidelines, apps/desktop/**/*.test.{ts,tsx}: Ensure tests are comprehensive and follow Vitest conventions. Check for proper mocking and test isolation.
Also applies to: 43-48
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/integrations/filters/__tests__/filter-utils.test.ts`
around lines 22 - 32, Add a regression test that exercises the GitHub-native
'open' filter value and validate normalization: update the test in
filter-utils.test.ts to call applyFilter(items, 'open') (using the existing
TestItem array) and assert it returns the same two ids [1,2]; also add
assertions for isValidFilterState('open') and isValidFilterState('opened') to
ensure both are considered valid by the public API (referencing applyFilter,
isValidFilterState, FilterState and TestItem to locate the relevant
functions/types); mirror this change for the similar case around lines 43-48 to
keep coverage consistent.
| import { describe, it, expect } from 'vitest'; | ||
| import { calculateHasMore, appendWithoutDuplicates } from '../pagination-utils'; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Missing test coverage for getNextPage and resetPagination.
The test file imports only calculateHasMore and appendWithoutDuplicates, but pagination-utils.ts also exports getNextPage and resetPagination. Consider adding tests for these functions to ensure complete coverage.
🧪 Suggested additional tests
-import { calculateHasMore, appendWithoutDuplicates } from '../pagination-utils';
+import { calculateHasMore, appendWithoutDuplicates, getNextPage, resetPagination } from '../pagination-utils';Add tests after the existing ones:
it('should get next page', () => {
expect(getNextPage(1)).toBe(2);
expect(getNextPage(5)).toBe(6);
});
it('should reset pagination', () => {
const result = resetPagination();
expect(result.currentPage).toBe(1);
expect(result.hasMore).toBe(true);
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { describe, it, expect } from 'vitest'; | |
| import { calculateHasMore, appendWithoutDuplicates } from '../pagination-utils'; | |
| import { describe, it, expect } from 'vitest'; | |
| import { calculateHasMore, appendWithoutDuplicates, getNextPage, resetPagination } from '../pagination-utils'; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/shared/integrations/pagination/__tests__/pagination-utils.test.ts`
around lines 1 - 2, The test suite currently only covers calculateHasMore and
appendWithoutDuplicates; import getNextPage and resetPagination from
'../pagination-utils' and add unit tests for them: call getNextPage with sample
values (e.g., 1 -> 2, 5 -> 6) and assert results, and call resetPagination(),
then assert returned object has currentPage === 1 and hasMore === true; ensure
the new tests use the same vitest imports (describe/it/expect) and are placed
alongside the existing tests.
| export function calculateHasMore(totalCount: number, pageSize: number): boolean { | ||
| return totalCount > pageSize; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider renaming or documenting the semantics of calculateHasMore.
The function checks if totalCount > pageSize, which determines if there's more than one page of data. However, for multi-page navigation, you'd typically need (currentPage * pageSize) < totalCount.
If this is intentionally a first-page-only check (e.g., after initial fetch), consider adding a JSDoc comment to clarify the intended usage:
📝 Suggested documentation
+/**
+ * Determines if there are more items beyond the first page.
+ * Use after initial fetch to check if pagination is needed.
+ * `@param` totalCount - Total number of items available
+ * `@param` pageSize - Number of items per page
+ */
export function calculateHasMore(totalCount: number, pageSize: number): boolean {
return totalCount > pageSize;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function calculateHasMore(totalCount: number, pageSize: number): boolean { | |
| return totalCount > pageSize; | |
| } | |
| /** | |
| * Determines if there are more items beyond the first page. | |
| * Use after initial fetch to check if pagination is needed. | |
| * `@param` totalCount - Total number of items available | |
| * `@param` pageSize - Number of items per page | |
| */ | |
| export function calculateHasMore(totalCount: number, pageSize: number): boolean { | |
| return totalCount > pageSize; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/integrations/pagination/pagination-utils.ts` around
lines 5 - 7, The current calculateHasMore(totalCount, pageSize) only checks if
totalCount > pageSize (i.e., more than one page) which is misleading for general
pagination; either change the API to accept currentPage and return (currentPage
* pageSize) < totalCount (update calculateHasMore to
calculateHasMore(totalCount, pageSize, currentPage) and adjust callsites), or if
the intent is strictly a first-page-only check, rename the function to something
explicit like isMultiPageOrHasMoreAfterFirstPage and add a JSDoc comment on
calculateHasMore (or the renamed function) stating it only checks whether
totalCount exceeds a single page; pick one approach and apply it consistently
across usages of calculateHasMore.
| // Legacy: Keep the old interface for reference, but use the imported type above | ||
| // This will be removed once all references are updated | ||
| export interface ElectronAPILegacy { |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding a deprecation notice or tracking issue.
The ElectronAPILegacy interface is marked for removal but could remain indefinitely without tracking. Consider adding a @deprecated JSDoc tag and/or linking to a tracking issue.
📝 Suggested documentation
-// Legacy: Keep the old interface for reference, but use the imported type above
-// This will be removed once all references are updated
-export interface ElectronAPILegacy {
+/**
+ * `@deprecated` This interface is kept for migration reference only.
+ * Use the `ElectronAPI` type alias instead.
+ * TODO: Remove once all references are updated (see issue `#XXX`)
+ */
+export interface ElectronAPILegacy {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/types/ipc.ts` around lines 186 - 188, Add a
deprecation note and tracking reference to the legacy interface by annotating
the ElectronAPILegacy interface with a JSDoc `@deprecated` tag and a short comment
or TODO linking to the tracking issue/PR that will remove it (or an issue
number/URL), so future maintainers know when and why it can be removed; update
the comment above ElectronAPILegacy to include the `@deprecated` tag and the
tracking link/issue ID.
- Added missing API type definitions for GitHub OAuth (deviceCode, authUrl, etc.) - Added glab to tool detection result type - Added memory infrastructure API methods (getMemoryInfrastructureStatus, testMemoryConnection, listMemoryDatabases) - Added terminal buffer persistence method (saveTerminalBuffer) - Added release operations API methods (getReleaseableVersions, runReleasePreflightCheck, createRelease) - Fixed type guards in context-store and release-store for unknown return types - Fixed GraphitiStep type guards for infrastructure and memory APIs - Updated mock implementations for browser-mock, project-mock, and terminal-mock All 4245 tests now pass and typecheck succeeds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Disable noNonNullAssertion rule (appropriate for tests) - Remove unused biome-ignore comments for disabled rule - Fix switch declaration scope issue in useGitLabMRFiltering - Rename duplicate mrReview key to mrFiltering in i18n - Update MRFilterBar component to use mrFiltering keys Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 19
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/components/onboarding/GraphitiStep.tsx (1)
246-262:⚠️ Potential issue | 🟡 MinorHardcoded user-facing strings violate i18n guidelines.
The changed lines introduce hardcoded strings (
'Connection test completed','Connection failed', and the template literal for the embedding provider message) that should use translation keys. As per coding guidelines, all frontend user-facing text inapps/desktop/src/renderer/**/*.{tsx,jsx}must usereact-i18nexttranslation keys.Additionally, when
data.successisundefined, the validation status will showsuccess: false(line 251) but no error message is set (line 261 checks=== falseexplicitly). Consider setting a fallback error for this ambiguous state.Suggested fix with i18n and undefined handling
+ import { useTranslation } from 'react-i18next';Then in the component:
+ const { t } = useTranslation(); // ... if (result?.success && result?.data && typeof result.data === 'object') { const data = result.data as { success?: boolean; message?: string }; + const isSuccess = data.success === true; setValidationStatus({ database: { tested: true, - success: data.success ?? false, - message: data.message || 'Connection test completed' + success: isSuccess, + message: data.message || t('onboarding.graphiti.connectionTestCompleted') }, provider: { tested: true, success: true, - message: `${config.embeddingProvider} embedding provider configured` + message: t('onboarding.graphiti.embeddingProviderConfigured', { provider: config.embeddingProvider }) } }); - if (data.success === false) { - setError(`Database: ${data.message || 'Connection failed'}`); + if (!isSuccess) { + setError(t('onboarding.graphiti.databaseError', { message: data.message || t('onboarding.graphiti.connectionFailed') })); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/onboarding/GraphitiStep.tsx` around lines 246 - 262, The new code in GraphitiStep.tsx sets user-facing messages directly and treats undefined success as false without an error message; update the setValidationStatus and error handling to use react-i18next translation keys via the component's t(...) function for the messages currently written as 'Connection test completed', 'Connection failed', and the embedding provider template, and ensure when result.data.success is undefined you treat it as a failure case and set a fallback translated error (e.g., use t('onboarding.connectionUnknown') or similar) before calling setError; locate and modify the block around setValidationStatus and the subsequent if (data.success === false) check to replace hardcoded strings with t(...) and to change the truthiness check to handle undefined (e.g., data.success !== true) so a translated error is set for ambiguous states, referencing config.embeddingProvider for the provider message.
♻️ Duplicate comments (1)
apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts (1)
90-96:⚠️ Potential issue | 🟠 MajorFix type mismatch:
progressshould beGitLabMRReviewProgress, notGitLabMRReviewResult.The callback signature declares
progress: GitLabMRReviewResult | nullon line 92, butuseGitLabMRs.tsprovidesprogress: GitLabMRReviewProgress | nullfrom the store (which usesMRReviewState.progress: GitLabMRReviewProgress | null). Under TypeScript strict mode, this breaks type compatibility.🔧 Proposed fix
export function useGitLabMRFiltering( mrs: GitLabMergeRequest[], getReviewStateForMR: (mrIid: number) => { isReviewing: boolean; - progress: GitLabMRReviewResult | null; + progress: GitLabMRReviewProgress | null; result: GitLabMRReviewResult | null; error: string | null; newCommitsCheck: GitLabNewCommitsCheck | null; } | null ) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts` around lines 90 - 96, The getReviewStateForMR callback signature incorrectly types progress as GitLabMRReviewResult | null; change it to GitLabMRReviewProgress | null to match the store (MRReviewState.progress) and the value returned in useGitLabMRs.ts so TypeScript strict mode compatibility is restored; update the type in the function signature where getReviewStateForMR is declared to reference GitLabMRReviewProgress instead of GitLabMRReviewResult.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/biome.jsonc`:
- Around line 48-49: Change the global Biome rule "noNonNullAssertion" from
"off" to "warn" in the configuration (currently shown alongside "useTemplate")
so the rule emits warnings instead of being disabled; keep "useTemplate" as-is,
and update developer guidance to use targeted suppressions (e.g., file- or
line-level // `@biome-ignore` comments) for unavoidable non-null assertions while
addressing other instances gradually.
In `@apps/desktop/src/preload/api/modules/github-api.ts`:
- Around line 195-202: The implementation of startGitHubAuth has a narrower
return type than the interface; update the implementation signature for
startGitHubAuth to match the interface by returning Promise<IPCResult<{ success:
boolean; message?: string; deviceCode?: string; authUrl?: string;
browserOpened?: boolean; fallbackUrl?: string }>> so consumers can safely access
result.data.deviceCode, authUrl, browserOpened and fallbackUrl; adjust the
function declaration where startGitHubAuth is defined to use this expanded
generic type.
- Around line 181-191: The three methods getReleaseableVersions,
runReleasePreflightCheck, and createRelease currently return IPCResult<unknown>;
replace unknown with concrete response types by defining appropriate interfaces
(e.g., ReleaseableVersionsResult, PreflightCheckResult, CreateReleaseResult)
that model the known shapes for each API response and update the signatures to
IPCResult<ReleaseableVersionsResult>, IPCResult<PreflightCheckResult>, and
IPCResult<CreateReleaseResult> respectively; ensure any IPC handler/consumer
code that calls these functions and any mapping logic (e.g., the implementation
of getReleaseableVersions, runReleasePreflightCheck, createRelease and their
callers) is updated to use the new types and adjust any casting or guards
accordingly so callers get full type safety.
- Around line 592-606: Replace the hardcoded channel strings in the preload
methods getReleaseableVersions, runReleasePreflightCheck and createRelease to
use the IPC_CHANNELS constants (e.g. IPC_CHANNELS.RELEASE_GET_VERSIONS,
IPC_CHANNELS.RELEASE_PREFLIGHT, IPC_CHANNELS.RELEASE_CREATE) and correct the
name mismatch for the preflight channel to use RELEASE_PREFLIGHT; then register
handlers in the main process by adding ipcMain.handle(...) for
IPC_CHANNELS.RELEASE_GET_VERSIONS and IPC_CHANNELS.RELEASE_PREFLIGHT inside the
release-handlers.ts (and the GitLab equivalent) so the calls from
release-store.ts resolve (ensure handler function names and signatures match the
invoke args used by the preload methods).
In `@apps/desktop/src/preload/api/project-api.ts`:
- Around line 49-52: Update the three Memory Infrastructure API method return
types to the concrete shared types: change getMemoryInfrastructureStatus to
return Promise<IPCResult<InfrastructureStatus>>, listMemoryDatabases to
Promise<IPCResult<string[]>>, and testMemoryConnection to
Promise<IPCResult<MemoryValidationResult>>; import the InfrastructureStatus and
MemoryValidationResult types (and IPCResult if not already referenced) from the
shared types module used across the project and replace the generic unknown
usages in the interface declaration for getMemoryInfrastructureStatus,
listMemoryDatabases, and testMemoryConnection.
- Around line 295-302: The preload methods getMemoryInfrastructureStatus,
listMemoryDatabases, and testMemoryConnection are invoking hardcoded
'infrastructure:*' IPC channels that aren't registered, causing runtime
failures; add corresponding constants (e.g., INFRASTRUCTURE_GET_STATUS,
INFRASTRUCTURE_LIST_DATABASES, INFRASTRUCTURE_TEST_CONNECTION) to the
IPC_CHANNELS file and implement matching ipcMain.handle handlers inside
registerMemoryHandlers in memory-handlers.ts (implement logic to return status,
list DBs, and test connection), then update the preload methods to invoke the
new IPC_CHANNELS constants instead of hardcoded strings.
In `@apps/desktop/src/preload/api/terminal-api.ts`:
- Around line 219-221: The saveTerminalBuffer API currently invokes ipcRenderer
with a hard-coded channel string ('terminal:saveBuffer'); update the call in
saveTerminalBuffer to use the shared IPC_CHANNELS constant instead (e.g.,
ipcRenderer.invoke(IPC_CHANNELS.TERMINAL_SAVE_BUFFER, terminalId,
serializedBuffer)) so it stays in sync with main-process handlers and follows
the existing IPC_CHANNELS usage.
- Around line 75-77: Add a main-process IPC handler and fix the type/constant
mismatch for saveTerminalBuffer: register an ipcMain.handle in
terminal-handlers.ts (e.g., handle the IPC_CHANNELS.TERMINAL_SAVE_BUFFER
channel) that performs the same save logic the preload expects, update the
IPC_CHANNELS enum to include TERMINAL_SAVE_BUFFER and replace the hardcoded
'terminal:saveBuffer' invocation with IPC_CHANNELS.TERMINAL_SAVE_BUFFER in the
preload, and align the shared type in ipc.ts (change Promise<void> to
Promise<IPCResult> or vice versa) so saveTerminalBuffer's signature and the
shared ipc type match to prevent runtime errors and type drift.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRFilterBar.tsx`:
- Around line 28-39: The new deep relative imports (e.g., Input, Badge, Button,
Separator, DropdownMenu components, useTranslation,
GitLabMRFilterState/GitLabMRStatusFilter/GitLabMRSortOption types, and cn)
should be switched to the renderer path aliases defined in tsconfig (e.g.,
`@components/`*, `@hooks/`*, `@lib/`*, `@shared/`*) to avoid brittle relative paths;
update the import statements in MRFilterBar.tsx to use the appropriate aliases
for the UI components (Input, Badge, Button, Separator, DropdownMenu,
DropdownMenuContent, DropdownMenuTrigger), the useTranslation hook, the GitLab
MR types, and the cn utility so all references resolve via the configured alias
paths.
- Line 321: In MRFilterBar, change the i18n namespace passed to useTranslation
from 'common' to 'gitlab' so mrFiltering.* lookups resolve correctly, and update
the reset button translation key from 'mrReview.reset' to 'mrFiltering.reset'
(update the references inside the MRFilterBar component where useTranslation is
used and where the reset button label is read).
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts`:
- Around line 194-196: The setContributors callback currently uses a parameter
named contributors which shadows the memoized contributors variable; rename the
parameter to something clear like newContributors or selectedContributors in the
setContributors definition (the useCallback that calls setFiltersState(prev =>
({ ...prev, contributors }))) and update the inner reference accordingly so it
sets the filtersState.contributors from the new parameter without shadowing the
outer variable.
- Line 9: Replace the deep relative import in useGitLabMRFiltering.ts that
brings in types GitLabMergeRequest, GitLabMRReviewResult, and
GitLabNewCommitsCheck from '../../../../shared/types' with the path-alias import
using the tsconfig alias (e.g. import from '@shared/types'); update the import
statement referencing those type symbols so the module resolves via '@shared/*'
instead of the long relative path.
In `@apps/desktop/src/renderer/components/onboarding/GraphitiStep.tsx`:
- Around line 251-252: In the GraphitiStep component where the response is
mapped into state (the object using success: data.success ?? false and message:
data.message || ...), make the success value an explicit boolean by using a
strict check (e.g., success: data.success === true) so undefined does not get
treated as false; also ensure downstream logic that currently only treats
explicit false as error is aligned (handle undefined separately or treat only
=== false as failure) so the UI and message handling remain consistent with the
API shape.
In `@apps/desktop/src/renderer/lib/browser-mock.ts`:
- Around line 35-36: Remove the broad "@ts-expect-error" and declare the mock
with a partial type so implemented members are still type-checked: replace the
current declaration with a typed partial (e.g., use the TypeScript satisfies
operator or an explicit Partial type such as "const browserMockAPI = { ... }
satisfies Partial<ElectronAPI>" or "const browserMockAPI: Partial<ElectronAPI> =
{ ... }"), keep the existing explicit cast when assigning to window ((window as
Window & { electronAPI: ElectronAPI }).electronAPI = browserMockAPI as
ElectronAPI), and ensure any missing members remain intentionally omitted rather
than suppressing all signature checks.
In `@apps/desktop/src/renderer/lib/mocks/terminal-mock.ts`:
- Line 85: The mock saveTerminalBuffer currently takes no parameters; update its
signature to match the real preload API by accepting (terminalId: string,
serializedBuffer: string) and keep returning the same shape ({ success: true });
change the mock function definition named saveTerminalBuffer to accept those two
parameters (and update any local typing) so tests exercise argument order/types
the same as the real implementation.
In `@apps/desktop/src/renderer/stores/context-store.ts`:
- Around line 110-128: The property type checks in the IPC result are
inconsistent: update the interim typed shape (replace the broad unknowns) with a
precise interface for { projectIndex?: ProjectIndex | null; memoryStatus?:
MemorySystemStatus | null; memoryState?: MemorySystemState | null;
recentMemories?: RendererMemory[] | null } and apply consistent guards before
calling store setters (e.g., check data.projectIndex != null && typeof
data.projectIndex === 'object' before store.setProjectIndex, check
data.memoryStatus != null && typeof data.memoryStatus === 'object' before
store.setMemoryStatus, check data.memoryState != null && typeof data.memoryState
=== 'object' before store.setMemoryState, and keep Array.isArray for
recentMemories) so non-object/truthy primitives cannot slip through; update the
local variable declaration and all usages (store.setProjectIndex,
store.setMemoryStatus, store.setMemoryState, store.setRecentMemories) to use
these stronger guards.
- Around line 205-207: The handler currently updates recentMemories only when
result.success && Array.isArray(result.data), leaving stale data if
result.success is true but data is malformed; modify the surrounding logic where
result is processed (the block that calls store.setRecentMemories) to add an
else branch that clears recent memories (e.g., call store.setRecentMemories([])
or equivalent) when result.success is true but result.data is not an array,
mirroring the behavior in searchMemories and ensuring stale data is not
retained.
In `@apps/desktop/src/renderer/stores/release-store.ts`:
- Around line 136-137: The current type guard before calling
store.setPreflightStatus allows arrays and null because it only checks typeof
result.data === 'object'; update the condition to exclude arrays and null (e.g.,
check result.success && result.data && !Array.isArray(result.data) &&
result.data !== null) so that only plain objects are passed to
store.setPreflightStatus(result.data as ReleasePreflightStatus); this change
should be made around the handling that reads result.data in the same block to
defensively ensure a ReleasePreflightStatus object is supplied.
In `@apps/desktop/src/shared/i18n/locales/en/gitlab.json`:
- Around line 252-297: MRLogs.tsx is still referencing the removed mrReview.logs
keys and wrong namespace; update every translation lookup in the MRLogs
component that uses "common:mrReview.logs.*" or "gitlab:mrReview.logs.*" to the
new path "gitlab:mrFiltering.logs.*" and ensure the useTranslation/useI18n call
uses the "gitlab" namespace (or explicitly prefix keys with "gitlab:") so
lookups like agentActivity, showMore, hideMore, noLogsYet, waitingForLogs, etc.
resolve from mrFiltering.logs.
---
Outside diff comments:
In `@apps/desktop/src/renderer/components/onboarding/GraphitiStep.tsx`:
- Around line 246-262: The new code in GraphitiStep.tsx sets user-facing
messages directly and treats undefined success as false without an error
message; update the setValidationStatus and error handling to use react-i18next
translation keys via the component's t(...) function for the messages currently
written as 'Connection test completed', 'Connection failed', and the embedding
provider template, and ensure when result.data.success is undefined you treat it
as a failure case and set a fallback translated error (e.g., use
t('onboarding.connectionUnknown') or similar) before calling setError; locate
and modify the block around setValidationStatus and the subsequent if
(data.success === false) check to replace hardcoded strings with t(...) and to
change the truthiness check to handle undefined (e.g., data.success !== true) so
a translated error is set for ambiguous states, referencing
config.embeddingProvider for the provider message.
---
Duplicate comments:
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts`:
- Around line 90-96: The getReviewStateForMR callback signature incorrectly
types progress as GitLabMRReviewResult | null; change it to
GitLabMRReviewProgress | null to match the store (MRReviewState.progress) and
the value returned in useGitLabMRs.ts so TypeScript strict mode compatibility is
restored; update the type in the function signature where getReviewStateForMR is
declared to reference GitLabMRReviewProgress instead of GitLabMRReviewResult.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 56af90e9-abfb-4102-ab12-0a0bc123cd24
📒 Files selected for processing (17)
apps/desktop/biome.jsoncapps/desktop/src/__tests__/integration/claude-profile-ipc.test.tsapps/desktop/src/main/changelog/changelog-service.tsapps/desktop/src/preload/api/modules/github-api.tsapps/desktop/src/preload/api/project-api.tsapps/desktop/src/preload/api/settings-api.tsapps/desktop/src/preload/api/terminal-api.tsapps/desktop/src/renderer/components/gitlab-merge-requests/components/MRFilterBar.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.tsapps/desktop/src/renderer/components/onboarding/GraphitiStep.tsxapps/desktop/src/renderer/lib/browser-mock.tsapps/desktop/src/renderer/lib/mocks/project-mock.tsapps/desktop/src/renderer/lib/mocks/terminal-mock.tsapps/desktop/src/renderer/stores/context-store.tsapps/desktop/src/renderer/stores/release-store.tsapps/desktop/src/shared/i18n/locales/en/gitlab.jsonapps/desktop/src/shared/i18n/locales/fr/gitlab.json
💤 Files with no reviewable changes (2)
- apps/desktop/src/tests/integration/claude-profile-ipc.test.ts
- apps/desktop/src/main/changelog/changelog-service.ts
| "useTemplate": "off", | ||
| "noNonNullAssertion": "off" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify impact before deciding to disable rule globally.
# Expected result:
# - If count is low/moderate: keep rule enabled (warn/error) and fix/suppress case-by-case.
# - If count is very high: use scoped overrides for specific legacy/test paths, not global "off".
echo "Non-null assertion expressions (\$A!):"
ast-grep --pattern '$A!' || true
echo
echo "Definite assignment assertions (\$A!: \$B):"
ast-grep --pattern '$A!: $B' || trueRepository: AndyMik90/Aperant
Length of output: 23001
Use warn instead of off for noNonNullAssertion.
Disabling this rule globally removes an important null-safety check across the entire app. While the codebase has ~120+ non-null assertions indicating this is intentional in many places, warn maintains the guardrail and allows gradual refactoring. For unavoidable cases, use targeted // @biome-ignore`` suppressions.
Suggested config adjustment
"style": {
"recommended": true,
"noDefaultExport": "off",
"useNamingConvention": "off",
"noProcessEnv": "off",
"useNodejsImportProtocol": "off",
"useImportType": "off",
"useTemplate": "off",
- "noNonNullAssertion": "off"
+ "noNonNullAssertion": "warn"
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "useTemplate": "off", | |
| "noNonNullAssertion": "off" | |
| "useTemplate": "off", | |
| "noNonNullAssertion": "warn" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/biome.jsonc` around lines 48 - 49, Change the global Biome rule
"noNonNullAssertion" from "off" to "warn" in the configuration (currently shown
alongside "useTemplate") so the rule emits warnings instead of being disabled;
keep "useTemplate" as-is, and update developer guidance to use targeted
suppressions (e.g., file- or line-level // `@biome-ignore` comments) for
unavoidable non-null assertions while addressing other instances gradually.
| // Release operations (changelog-based) | ||
| getReleaseableVersions: (projectId: string) => Promise<IPCResult<unknown>>; | ||
| runReleasePreflightCheck: (projectId: string, version: string) => Promise<IPCResult<unknown>>; | ||
| createRelease: (options: { | ||
| projectId: string; | ||
| version: string; | ||
| body: string; | ||
| draft?: boolean; | ||
| prerelease?: boolean; | ||
| }) => Promise<IPCResult<unknown>>; | ||
|
|
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding explicit return types instead of unknown.
Using IPCResult<unknown> for all three release operations loses type safety. Callers will need type assertions or guards to use the returned data. If the response shapes are known, define and use proper types.
💡 Example typed return
- getReleaseableVersions: (projectId: string) => Promise<IPCResult<unknown>>;
- runReleasePreflightCheck: (projectId: string, version: string) => Promise<IPCResult<unknown>>;
- createRelease: (options: {
+ getReleaseableVersions: (projectId: string) => Promise<IPCResult<{ versions: string[] }>>;
+ runReleasePreflightCheck: (projectId: string, version: string) => Promise<IPCResult<{ ready: boolean; issues?: string[] }>>;
+ createRelease: (options: {
projectId: string;
version: string;
body: string;
draft?: boolean;
prerelease?: boolean;
- }) => Promise<IPCResult<unknown>>;
+ }) => Promise<IPCResult<{ url: string; tagName: string }>>;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/preload/api/modules/github-api.ts` around lines 181 - 191,
The three methods getReleaseableVersions, runReleasePreflightCheck, and
createRelease currently return IPCResult<unknown>; replace unknown with concrete
response types by defining appropriate interfaces (e.g.,
ReleaseableVersionsResult, PreflightCheckResult, CreateReleaseResult) that model
the known shapes for each API response and update the signatures to
IPCResult<ReleaseableVersionsResult>, IPCResult<PreflightCheckResult>, and
IPCResult<CreateReleaseResult> respectively; ensure any IPC handler/consumer
code that calls these functions and any mapping logic (e.g., the implementation
of getReleaseableVersions, runReleasePreflightCheck, createRelease and their
callers) is updated to use the new types and adjust any casting or guards
accordingly so callers get full type safety.
| startGitHubAuth: () => Promise<IPCResult<{ | ||
| success: boolean; | ||
| message?: string; | ||
| deviceCode?: string; | ||
| authUrl?: string; | ||
| browserOpened?: boolean; | ||
| fallbackUrl?: string; | ||
| }>>; |
There was a problem hiding this comment.
Interface updated but implementation type annotation not updated — type mismatch.
The interface now declares startGitHubAuth returns deviceCode, authUrl, browserOpened, and fallbackUrl fields. However, the implementation at line 615 still has the narrower type annotation:
startGitHubAuth: (): Promise<IPCResult<{ success: boolean; message?: string }>> =>This causes TypeScript to reject property accesses like result.data?.deviceCode in consumers (e.g., GitHubOAuthFlow.tsx at line 249), even though the runtime data includes these fields.
🔧 Proposed fix — update implementation type annotation
- startGitHubAuth: (): Promise<IPCResult<{ success: boolean; message?: string }>> =>
+ startGitHubAuth: (): Promise<IPCResult<{
+ success: boolean;
+ message?: string;
+ deviceCode?: string;
+ authUrl?: string;
+ browserOpened?: boolean;
+ fallbackUrl?: string;
+ }>> =>
invokeIpc(IPC_CHANNELS.GITHUB_START_AUTH),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/preload/api/modules/github-api.ts` around lines 195 - 202,
The implementation of startGitHubAuth has a narrower return type than the
interface; update the implementation signature for startGitHubAuth to match the
interface by returning Promise<IPCResult<{ success: boolean; message?: string;
deviceCode?: string; authUrl?: string; browserOpened?: boolean; fallbackUrl?:
string }>> so consumers can safely access result.data.deviceCode, authUrl,
browserOpened and fallbackUrl; adjust the function declaration where
startGitHubAuth is defined to use this expanded generic type.
| // Release operations (changelog-based) | ||
| getReleaseableVersions: (projectId: string): Promise<IPCResult<unknown>> => | ||
| invokeIpc('release:getVersions', projectId), | ||
|
|
||
| runReleasePreflightCheck: (projectId: string, version: string): Promise<IPCResult<unknown>> => | ||
| invokeIpc('release:preflightCheck', projectId, version), | ||
|
|
||
| createRelease: (options: { | ||
| projectId: string; | ||
| version: string; | ||
| body: string; | ||
| draft?: boolean; | ||
| prerelease?: boolean; | ||
| }): Promise<IPCResult<unknown>> => | ||
| invokeIpc('release:create', options), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check what the main process handler expects for the preflight channel
rg -n "preflightCheck|preflight" --type ts -g '!*.test.ts' -g '!*.spec.ts' apps/desktop/src/main apps/desktop/src/shared/constantsRepository: AndyMik90/Aperant
Length of output: 147
🏁 Script executed:
#!/bin/bash
# Get all RELEASE_* constants from ipc.ts
rg -n "RELEASE_" --type ts apps/desktop/src/shared/constants/ipc.ts | head -20Repository: AndyMik90/Aperant
Length of output: 298
🏁 Script executed:
#!/bin/bash
# Check what the main process release handlers are registered as
rg -n "release:" --type ts -A 2 -B 2 apps/desktop/src/main/ipc-handlers | grep -E "(handle|on|register|release:)" | head -20Repository: AndyMik90/Aperant
Length of output: 563
🏁 Script executed:
#!/bin/bash
# Check the actual imports and usage in github-api.ts at the specific lines
sed -n '1,20p' apps/desktop/src/preload/api/modules/github-api.ts
tail -n +580 apps/desktop/src/preload/api/modules/github-api.ts | head -30Repository: AndyMik90/Aperant
Length of output: 1658
🏁 Script executed:
#!/bin/bash
# Search for handler registrations in main process for release channels
rg -n "handle|\.on\(" --type ts apps/desktop/src/main/ipc-handlers -A 3 | grep -E "(release:preflight|release:preflightCheck|preflightCheck)" -B 3 -A 3Repository: AndyMik90/Aperant
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check the release handler files more thoroughly
find apps/desktop/src/main/ipc-handlers -name "*release*" -type fRepository: AndyMik90/Aperant
Length of output: 184
🏁 Script executed:
#!/bin/bash
# Check the github release handlers to see what channels are registered
cat -n apps/desktop/src/main/ipc-handlers/github/release-handlers.ts | head -100Repository: AndyMik90/Aperant
Length of output: 3781
🏁 Script executed:
#!/bin/bash
# Search specifically for ipcMain.handle or channel registration in release handlers
rg -n "ipcMain|handle|preflight|preflightCheck" --type ts apps/desktop/src/main/ipc-handlers/github/release-handlers.tsRepository: AndyMik90/Aperant
Length of output: 228
🏁 Script executed:
#!/bin/bash
# Check what handler is registered at line 189 and around line 261
sed -n '185,200p' apps/desktop/src/main/ipc-handlers/github/release-handlers.tsRepository: AndyMik90/Aperant
Length of output: 592
🏁 Script executed:
#!/bin/bash
# Check the handler registration section around line 261
sed -n '260,330p' apps/desktop/src/main/ipc-handlers/github/release-handlers.tsRepository: AndyMik90/Aperant
Length of output: 216
🏁 Script executed:
#!/bin/bash
# Search for any handlers registered for the release channels that appear in preload
rg -n "RELEASE_GET_VERSIONS|RELEASE_CREATE|RELEASE_PREFLIGHT" --type ts apps/desktop/src/mainRepository: AndyMik90/Aperant
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check if there are handlers for getReleaseableVersions, runReleasePreflightCheck, or createRelease
rg -n "getReleaseableVersions|getVersions|preflightCheck|preflight" --type ts apps/desktop/src/main/ipc-handlersRepository: AndyMik90/Aperant
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check all IPC handlers being registered globally
rg -n "ipcMain\.handle|registerReleaseHandlers|registerCreateRelease" --type ts apps/desktop/src/mainRepository: AndyMik90/Aperant
Length of output: 29490
🏁 Script executed:
#!/bin/bash
# Check the main.ts or index to see which handlers are registered
find apps/desktop/src/main -maxdepth 2 -name "*.ts" | xargs grep -l "registerReleaseHandlers\|ipcMain"Repository: AndyMik90/Aperant
Length of output: 1447
🏁 Script executed:
#!/bin/bash
# Check if there's a comprehensive handler registration file
cat -n apps/desktop/src/main/index.ts 2>/dev/null || cat -n apps/desktop/src/main/main.ts 2>/dev/null || find apps/desktop/src/main -maxdepth 1 -name "*.ts" -exec echo {} \; -exec cat {} \;Repository: AndyMik90/Aperant
Length of output: 32539
🏁 Script executed:
#!/bin/bash
# Search for calls to these three preload methods from renderer
rg -n "getReleaseableVersions|runReleasePreflightCheck" --type ts -g '!*.test.ts' apps/desktop/src/rendererRepository: AndyMik90/Aperant
Length of output: 858
🏁 Script executed:
#!/bin/bash
# Check if there are any handlers registered for these channels in the entire main process
find apps/desktop/src/main -name "*.ts" ! -name "*.test.ts" -exec grep -l "release:getVersions\|release:preflightCheck\|release:preflight" {} \;Repository: AndyMik90/Aperant
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Look for where the release handlers are being called/imported
rg -n "registerReleaseHandlers" --type ts apps/desktop/src/mainRepository: AndyMik90/Aperant
Length of output: 771
🏁 Script executed:
#!/bin/bash
# Check if any of these handlers are actually defined anywhere in main process
# Search for handler definitions for these channels
rg -n "RELEASE_GET_VERSIONS|RELEASE_PREFLIGHT|RELEASE_CREATE" --type ts apps/desktop/src/main/ipc-handlers | grep "handle\|ipcMain"Repository: AndyMik90/Aperant
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Verify what handlers ARE registered in github release-handlers.ts
cat -n apps/desktop/src/main/ipc-handlers/github/release-handlers.ts | grep -A 2 "ipcMain.handle"Repository: AndyMik90/Aperant
Length of output: 312
🏁 Script executed:
#!/bin/bash
# Final verification: confirm no handlers exist for these three channels
rg -n "RELEASE_GET_VERSIONS|'release:getVersions'" --type ts apps/desktop/src/main/ipc-handlersRepository: AndyMik90/Aperant
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check what happens when a non-existent channel is called
rg -n "ipcMain.handle.*RELEASE" --type ts apps/desktop/src/main/ipc-handlers/github/release-handlers.tsRepository: AndyMik90/Aperant
Length of output: 43
Use IPC_CHANNELS constants and fix missing handlers; these preload methods will fail at runtime.
Three critical issues in the release operations API:
-
Hardcoded strings (lines 594, 597, 606): Import and use
IPC_CHANNELSconstants instead of string literals to match the established pattern. -
Channel name mismatch (line 597): Preload uses
'release:preflightCheck'but the constant inipc.tsisRELEASE_PREFLIGHT: 'release:preflight'. This will cause IPC calls to fail silently since no handler is registered. -
Missing main process handlers: The renderer actively calls
getReleaseableVersions()andrunReleasePreflightCheck()fromrelease-store.ts, but no correspondingipcMain.handle()registrations exist forRELEASE_GET_VERSIONSorRELEASE_PREFLIGHTchannels. The methods are dead in the main process.
These calls will hang/fail at runtime. Register handlers in apps/desktop/src/main/ipc-handlers/github/release-handlers.ts (and GitLab equivalent) and use the correct channel constants.
Proposed fix
// Release operations (changelog-based)
getReleaseableVersions: (projectId: string): Promise<IPCResult<unknown>> =>
- invokeIpc('release:getVersions', projectId),
+ invokeIpc(IPC_CHANNELS.RELEASE_GET_VERSIONS, projectId),
runReleasePreflightCheck: (projectId: string, version: string): Promise<IPCResult<unknown>> =>
- invokeIpc('release:preflightCheck', projectId, version),
+ invokeIpc(IPC_CHANNELS.RELEASE_PREFLIGHT, projectId, version),
createRelease: (options: {
projectId: string;
version: string;
body: string;
draft?: boolean;
prerelease?: boolean;
}): Promise<IPCResult<unknown>> =>
- invokeIpc('release:create', options),
+ invokeIpc(IPC_CHANNELS.RELEASE_CREATE, options),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/preload/api/modules/github-api.ts` around lines 592 - 606,
Replace the hardcoded channel strings in the preload methods
getReleaseableVersions, runReleasePreflightCheck and createRelease to use the
IPC_CHANNELS constants (e.g. IPC_CHANNELS.RELEASE_GET_VERSIONS,
IPC_CHANNELS.RELEASE_PREFLIGHT, IPC_CHANNELS.RELEASE_CREATE) and correct the
name mismatch for the preflight channel to use RELEASE_PREFLIGHT; then register
handlers in the main process by adding ipcMain.handle(...) for
IPC_CHANNELS.RELEASE_GET_VERSIONS and IPC_CHANNELS.RELEASE_PREFLIGHT inside the
release-handlers.ts (and the GitLab equivalent) so the calls from
release-store.ts resolve (ensure handler function names and signatures match the
invoke args used by the preload methods).
| // Memory Infrastructure operations (LadybugDB - no Docker required) | ||
| getMemoryInfrastructureStatus: (dbPath?: string) => Promise<IPCResult<unknown>>; | ||
| listMemoryDatabases: (dbPath?: string) => Promise<IPCResult<unknown>>; | ||
| testMemoryConnection: (dbPath?: string, database?: string) => Promise<IPCResult<unknown>>; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify canonical shared types for these API methods and current preload typing.
rg -n -C2 "getMemoryInfrastructureStatus|listMemoryDatabases|testMemoryConnection" apps/desktop/src/shared/types/ipc.ts
rg -n -C2 "interface MemoryDatabaseStatus|interface InfrastructureStatus|interface MemoryValidationResult" apps/desktop/src/shared/types/project.ts
rg -n -C3 "getMemoryInfrastructureStatus|listMemoryDatabases|testMemoryConnection" apps/desktop/src/preload/api/project-api.tsRepository: AndyMik90/Aperant
Length of output: 1994
Replace unknown with concrete IPC payload types for memory infrastructure APIs.
The preload API interface declaration (lines 50–52) uses IPCResult<unknown>, but the shared types layer already defines specific return types: InfrastructureStatus, string[], and MemoryValidationResult. This type mismatch violates TypeScript strict mode and obscures the actual contract between preload and renderer.
Update the preload interface to match shared types
import type {
Project,
ProjectSettings,
IPCResult,
InitializationResult,
AutoBuildVersionInfo,
ProjectEnvConfig,
GitStatus,
KanbanPreferences,
- GitBranchDetail
+ GitBranchDetail,
+ InfrastructureStatus,
+ MemoryValidationResult
} from '../../shared/types';
@@
- getMemoryInfrastructureStatus: (dbPath?: string) => Promise<IPCResult<unknown>>;
- listMemoryDatabases: (dbPath?: string) => Promise<IPCResult<unknown>>;
- testMemoryConnection: (dbPath?: string, database?: string) => Promise<IPCResult<unknown>>;
+ getMemoryInfrastructureStatus: (dbPath?: string) => Promise<IPCResult<InfrastructureStatus>>;
+ listMemoryDatabases: (dbPath?: string) => Promise<IPCResult<string[]>>;
+ testMemoryConnection: (dbPath?: string, database?: string) => Promise<IPCResult<MemoryValidationResult>>;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Memory Infrastructure operations (LadybugDB - no Docker required) | |
| getMemoryInfrastructureStatus: (dbPath?: string) => Promise<IPCResult<unknown>>; | |
| listMemoryDatabases: (dbPath?: string) => Promise<IPCResult<unknown>>; | |
| testMemoryConnection: (dbPath?: string, database?: string) => Promise<IPCResult<unknown>>; | |
| import type { | |
| Project, | |
| ProjectSettings, | |
| IPCResult, | |
| InitializationResult, | |
| AutoBuildVersionInfo, | |
| ProjectEnvConfig, | |
| GitStatus, | |
| KanbanPreferences, | |
| GitBranchDetail, | |
| InfrastructureStatus, | |
| MemoryValidationResult | |
| } from '../../shared/types'; | |
| // Memory Infrastructure operations (LadybugDB - no Docker required) | |
| getMemoryInfrastructureStatus: (dbPath?: string) => Promise<IPCResult<InfrastructureStatus>>; | |
| listMemoryDatabases: (dbPath?: string) => Promise<IPCResult<string[]>>; | |
| testMemoryConnection: (dbPath?: string, database?: string) => Promise<IPCResult<MemoryValidationResult>>; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/preload/api/project-api.ts` around lines 49 - 52, Update the
three Memory Infrastructure API method return types to the concrete shared
types: change getMemoryInfrastructureStatus to return
Promise<IPCResult<InfrastructureStatus>>, listMemoryDatabases to
Promise<IPCResult<string[]>>, and testMemoryConnection to
Promise<IPCResult<MemoryValidationResult>>; import the InfrastructureStatus and
MemoryValidationResult types (and IPCResult if not already referenced) from the
shared types module used across the project and replace the generic unknown
usages in the interface declaration for getMemoryInfrastructureStatus,
listMemoryDatabases, and testMemoryConnection.
| }), | ||
|
|
||
| saveTerminalBuffer: async () => {}, | ||
| saveTerminalBuffer: async () => ({ success: true }), |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Mirror the real method signature in the mock.
Line 85 should accept (terminalId: string, serializedBuffer: string) to match preload API shape; otherwise test/mock paths can miss argument-order/type mistakes.
Proposed change
- saveTerminalBuffer: async () => ({ success: true }),
+ saveTerminalBuffer: async (_terminalId: string, _serializedBuffer: string) => ({ success: true }),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| saveTerminalBuffer: async () => ({ success: true }), | |
| saveTerminalBuffer: async (_terminalId: string, _serializedBuffer: string) => ({ success: true }), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/lib/mocks/terminal-mock.ts` at line 85, The mock
saveTerminalBuffer currently takes no parameters; update its signature to match
the real preload API by accepting (terminalId: string, serializedBuffer: string)
and keep returning the same shape ({ success: true }); change the mock function
definition named saveTerminalBuffer to accept those two parameters (and update
any local typing) so tests exercise argument order/types the same as the real
implementation.
| if (result.success && result.data && typeof result.data === 'object') { | ||
| const data = result.data as { | ||
| projectIndex?: unknown; | ||
| memoryStatus?: unknown; | ||
| memoryState?: unknown; | ||
| recentMemories?: unknown; | ||
| }; | ||
| if (data.projectIndex && typeof data.projectIndex === 'object') { | ||
| store.setProjectIndex(data.projectIndex as ProjectIndex); | ||
| } | ||
| if (data.memoryStatus) { | ||
| store.setMemoryStatus(data.memoryStatus as MemorySystemStatus); | ||
| } | ||
| if (data.memoryState) { | ||
| store.setMemoryState(data.memoryState as MemorySystemState); | ||
| } | ||
| if (Array.isArray(data.recentMemories)) { | ||
| store.setRecentMemories(data.recentMemories as RendererMemory[]); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Type guards are inconsistent across properties.
The guard for projectIndex (line 117) checks typeof === 'object', but memoryStatus and memoryState (lines 120, 123) only use truthy checks. This inconsistency could allow non-object values through if the IPC contract changes unexpectedly.
Additionally, the intermediate cast on lines 111-116 uses unknown for all properties but then re-casts each one individually. Consider defining a discriminated type or using a more precise type guard.
♻️ Optional: Consistent type guards
if (result.success && result.data && typeof result.data === 'object') {
const data = result.data as {
projectIndex?: unknown;
memoryStatus?: unknown;
memoryState?: unknown;
recentMemories?: unknown;
};
if (data.projectIndex && typeof data.projectIndex === 'object') {
store.setProjectIndex(data.projectIndex as ProjectIndex);
}
- if (data.memoryStatus) {
+ if (data.memoryStatus && typeof data.memoryStatus === 'object') {
store.setMemoryStatus(data.memoryStatus as MemorySystemStatus);
}
- if (data.memoryState) {
+ if (data.memoryState && typeof data.memoryState === 'object') {
store.setMemoryState(data.memoryState as MemorySystemState);
}
if (Array.isArray(data.recentMemories)) {
store.setRecentMemories(data.recentMemories as RendererMemory[]);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (result.success && result.data && typeof result.data === 'object') { | |
| const data = result.data as { | |
| projectIndex?: unknown; | |
| memoryStatus?: unknown; | |
| memoryState?: unknown; | |
| recentMemories?: unknown; | |
| }; | |
| if (data.projectIndex && typeof data.projectIndex === 'object') { | |
| store.setProjectIndex(data.projectIndex as ProjectIndex); | |
| } | |
| if (data.memoryStatus) { | |
| store.setMemoryStatus(data.memoryStatus as MemorySystemStatus); | |
| } | |
| if (data.memoryState) { | |
| store.setMemoryState(data.memoryState as MemorySystemState); | |
| } | |
| if (Array.isArray(data.recentMemories)) { | |
| store.setRecentMemories(data.recentMemories as RendererMemory[]); | |
| } | |
| if (result.success && result.data && typeof result.data === 'object') { | |
| const data = result.data as { | |
| projectIndex?: unknown; | |
| memoryStatus?: unknown; | |
| memoryState?: unknown; | |
| recentMemories?: unknown; | |
| }; | |
| if (data.projectIndex && typeof data.projectIndex === 'object') { | |
| store.setProjectIndex(data.projectIndex as ProjectIndex); | |
| } | |
| if (data.memoryStatus && typeof data.memoryStatus === 'object') { | |
| store.setMemoryStatus(data.memoryStatus as MemorySystemStatus); | |
| } | |
| if (data.memoryState && typeof data.memoryState === 'object') { | |
| store.setMemoryState(data.memoryState as MemorySystemState); | |
| } | |
| if (Array.isArray(data.recentMemories)) { | |
| store.setRecentMemories(data.recentMemories as RendererMemory[]); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/stores/context-store.ts` around lines 110 - 128,
The property type checks in the IPC result are inconsistent: update the interim
typed shape (replace the broad unknowns) with a precise interface for {
projectIndex?: ProjectIndex | null; memoryStatus?: MemorySystemStatus | null;
memoryState?: MemorySystemState | null; recentMemories?: RendererMemory[] | null
} and apply consistent guards before calling store setters (e.g., check
data.projectIndex != null && typeof data.projectIndex === 'object' before
store.setProjectIndex, check data.memoryStatus != null && typeof
data.memoryStatus === 'object' before store.setMemoryStatus, check
data.memoryState != null && typeof data.memoryState === 'object' before
store.setMemoryState, and keep Array.isArray for recentMemories) so
non-object/truthy primitives cannot slip through; update the local variable
declaration and all usages (store.setProjectIndex, store.setMemoryStatus,
store.setMemoryState, store.setRecentMemories) to use these stronger guards.
| if (result.success && result.data && Array.isArray(result.data)) { | ||
| store.setRecentMemories(result.data as RendererMemory[]); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider clearing stale data on unexpected response.
Unlike searchMemories (line 184) which clears results when data is not an array, this function silently retains the previous recentMemories if the response is malformed. If result.success is true but result.data is not an array, stale data could persist indefinitely, potentially masking an API contract issue.
♻️ Optional: Add else branch for consistency
if (result.success && result.data && Array.isArray(result.data)) {
store.setRecentMemories(result.data as RendererMemory[]);
+ } else if (result.success) {
+ // Unexpected data shape - clear to avoid stale data
+ store.setRecentMemories([]);
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (result.success && result.data && Array.isArray(result.data)) { | |
| store.setRecentMemories(result.data as RendererMemory[]); | |
| } | |
| if (result.success && result.data && Array.isArray(result.data)) { | |
| store.setRecentMemories(result.data as RendererMemory[]); | |
| } else if (result.success) { | |
| // Unexpected data shape - clear to avoid stale data | |
| store.setRecentMemories([]); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/stores/context-store.ts` around lines 205 - 207,
The handler currently updates recentMemories only when result.success &&
Array.isArray(result.data), leaving stale data if result.success is true but
data is malformed; modify the surrounding logic where result is processed (the
block that calls store.setRecentMemories) to add an else branch that clears
recent memories (e.g., call store.setRecentMemories([]) or equivalent) when
result.success is true but result.data is not an array, mirroring the behavior
in searchMemories and ensuring stale data is not retained.
| if (result.success && result.data && typeof result.data === 'object') { | ||
| store.setPreflightStatus(result.data as ReleasePreflightStatus); |
There was a problem hiding this comment.
Weak type guard: arrays would also pass typeof === 'object'.
The check typeof result.data === 'object' will also match arrays since typeof [] === 'object' in JavaScript. While the API contract likely prevents this, the guard at line 98 correctly uses Array.isArray for arrays—this check should similarly exclude arrays for consistency and defensive type safety.
🛡️ Proposed fix to strengthen the type guard
- if (result.success && result.data && typeof result.data === 'object') {
+ if (result.success && result.data && typeof result.data === 'object' && !Array.isArray(result.data)) {
store.setPreflightStatus(result.data as ReleasePreflightStatus);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (result.success && result.data && typeof result.data === 'object') { | |
| store.setPreflightStatus(result.data as ReleasePreflightStatus); | |
| if (result.success && result.data && typeof result.data === 'object' && !Array.isArray(result.data)) { | |
| store.setPreflightStatus(result.data as ReleasePreflightStatus); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/stores/release-store.ts` around lines 136 - 137,
The current type guard before calling store.setPreflightStatus allows arrays and
null because it only checks typeof result.data === 'object'; update the
condition to exclude arrays and null (e.g., check result.success && result.data
&& !Array.isArray(result.data) && result.data !== null) so that only plain
objects are passed to store.setPreflightStatus(result.data as
ReleasePreflightStatus); this change should be made around the handling that
reads result.data in the same block to defensively ensure a
ReleasePreflightStatus object is supplied.
| "mrFiltering": { | ||
| "reviewed": "Reviewed", | ||
| "posted": "Posted", | ||
| "changesRequested": "Changes Requested", | ||
| "readyToMerge": "Ready to Merge", | ||
| "readyForFollowup": "Ready for Follow-up", | ||
| "reviewing": "Reviewing", | ||
| "notReviewed": "Not Reviewed", | ||
| "searchPlaceholder": "Search merge requests...", | ||
| "contributors": "Contributors", | ||
| "searchContributors": "Search contributors...", | ||
| "selectedCount": "{{count}} selected", | ||
| "noResultsFound": "No results found", | ||
| "clearFilters": "Clear filters", | ||
| "allStatuses": "All Statuses", | ||
| "clearSearch": "Clear search", | ||
| "reset": "Reset", | ||
| "sort": { | ||
| "label": "Sort", | ||
| "newest": "Newest", | ||
| "oldest": "Oldest", | ||
| "largest": "Largest" | ||
| }, | ||
| "logs": { | ||
| "agentActivity": "Agent Activity", | ||
| "showMore": "Show {{count}} more", | ||
| "hideMore": "Hide {{count}} more", | ||
| "noLogsYet": "No logs yet", | ||
| "waitingForLogs": "Waiting for logs...", | ||
| "reviewStarting": "Review is starting", | ||
| "noLogsAvailable": "No logs available", | ||
| "runReviewGenerateLogs": "Run a review to generate logs", | ||
| "entries": "entries", | ||
| "less": "Less", | ||
| "more": "More", | ||
| "followup": "Follow-up", | ||
| "live": "Live", | ||
| "streaming": "Streaming", | ||
| "running": "Running", | ||
| "pending": "Pending", | ||
| "complete": "Complete", | ||
| "failed": "Failed", | ||
| "contextGathering": "Context Gathering", | ||
| "aiAnalysis": "AI Analysis", | ||
| "synthesis": "Synthesis" | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "mrReview\.logs|mrFiltering\.logs" \
apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx \
apps/desktop/src/shared/i18n/locales/en/gitlab.json \
apps/desktop/src/shared/i18n/locales/fr/gitlab.jsonRepository: AndyMik90/Aperant
Length of output: 3125
🏁 Script executed:
#!/bin/bash
# Check the full structure of gitlab.json to see if mrReview.logs still exists
head -300 apps/desktop/src/shared/i18n/locales/en/gitlab.json | tail -100Repository: AndyMik90/Aperant
Length of output: 3881
🏁 Script executed:
#!/bin/bash
# Search for mrReview section in the gitlab.json file
rg -n "mrReview" apps/desktop/src/shared/i18n/locales/en/gitlab.jsonRepository: AndyMik90/Aperant
Length of output: 80
🏁 Script executed:
#!/bin/bash
# Count total lines and get full file to understand structure
wc -l apps/desktop/src/shared/i18n/locales/en/gitlab.jsonRepository: AndyMik90/Aperant
Length of output: 116
🏁 Script executed:
#!/bin/bash
# View the mrReview section to see if mrReview.logs still exists
sed -n '130,200p' apps/desktop/src/shared/i18n/locales/en/gitlab.jsonRepository: AndyMik90/Aperant
Length of output: 2496
🏁 Script executed:
#!/bin/bash
# Check if mrReview.logs exists in the file
rg "\"logs\"" -B 2 -A 2 apps/desktop/src/shared/i18n/locales/en/gitlab.json | head -50Repository: AndyMik90/Aperant
Length of output: 190
🏁 Script executed:
#!/bin/bash
# Check French translation file for the same structure
rg -n "mrReview\.logs|mrFiltering\.logs" apps/desktop/src/shared/i18n/locales/fr/gitlab.jsonRepository: AndyMik90/Aperant
Length of output: 43
🏁 Script executed:
#!/bin/bash
# Check if French gitlab.json exists
ls -la apps/desktop/src/shared/i18n/locales/fr/gitlab.jsonRepository: AndyMik90/Aperant
Length of output: 162
🏁 Script executed:
#!/bin/bash
# Search for any remaining mrReview.logs in the entire en gitlab.json to be thorough
cat apps/desktop/src/shared/i18n/locales/en/gitlab.json | grep -c "mrReview" || echo "No mrReview.logs section found"Repository: AndyMik90/Aperant
Length of output: 62
🏁 Script executed:
#!/bin/bash
# View the complete gitlab.json file to understand the full structure
cat apps/desktop/src/shared/i18n/locales/en/gitlab.json | jq . 2>/dev/null | grep -A 30 '"mrReview"' | head -40Repository: AndyMik90/Aperant
Length of output: 1355
🏁 Script executed:
#!/bin/bash
# Check French file structure for mrFiltering.logs
rg -n "mrFiltering" apps/desktop/src/shared/i18n/locales/fr/gitlab.json | head -5Repository: AndyMik90/Aperant
Length of output: 83
🏁 Script executed:
#!/bin/bash
# Verify French file has the logs section under mrFiltering
rg -A 25 '"logs"' apps/desktop/src/shared/i18n/locales/fr/gitlab.json | tail -30Repository: AndyMik90/Aperant
Length of output: 871
🏁 Script executed:
#!/bin/bash
# Check if there are any i18n config files that might create aliases or mappings
fd -e json -e js -e ts "i18n|translation|locale" apps/desktop/src/shared/i18n/ --type f | head -20Repository: AndyMik90/Aperant
Length of output: 43
🏁 Script executed:
#!/bin/bash
# View MRLogs.tsx to see the exact imports and how it uses translations
head -60 apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsxRepository: AndyMik90/Aperant
Length of output: 1531
🏁 Script executed:
#!/bin/bash
# Check what the common namespace contains in gitlab translations
rg '"agentActivity"|"showMore"|"hideMore"' apps/desktop/src/shared/i18n/locales/en/ -lRepository: AndyMik90/Aperant
Length of output: 215
🏁 Script executed:
#!/bin/bash
# Verify if common.json has any logs section
cat apps/desktop/src/shared/i18n/locales/en/common.json | jq '.mrReview' 2>/dev/null || echo "No mrReview section in common.json"Repository: AndyMik90/Aperant
Length of output: 65
🏁 Script executed:
#!/bin/bash
# Get all the translations that MRLogs.tsx actually needs and verify they exist in new location
cat > /tmp/check_keys.sh << 'EOF'
#!/bin/bash
# Keys used in MRLogs.tsx
keys=("followup" "live" "streaming" "running" "pending" "complete" "failed" "waitingForLogs" "reviewStarting" "noLogsAvailable" "runReviewGenerateLogs" "entries" "less" "more" "agentActivity" "showMore" "hideMore" "noLogsYet")
# Check if all keys exist in mrFiltering.logs
echo "Checking if all keys exist in mrFiltering.logs section:"
for key in "${keys[@]}"; do
if rg -q "\"$key\"" apps/desktop/src/shared/i18n/locales/en/gitlab.json; then
echo "✓ $key found"
else
echo "✗ $key NOT found"
fi
done
EOF
bash /tmp/check_keys.shRepository: AndyMik90/Aperant
Length of output: 444
Update MRLogs.tsx to use mrFiltering.logs path and correct namespace.
The mrReview.logs section no longer exists in gitlab.json. All log-related translation keys have moved to mrFiltering.logs. Additionally, lines 437, 545, and 550 incorrectly reference common:mrReview.logs.* for keys that should come from gitlab:mrFiltering.logs.*. Update all translation lookups in MRLogs.tsx from gitlab:mrReview.logs.* to gitlab:mrFiltering.logs.* and fix the namespace errors.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/i18n/locales/en/gitlab.json` around lines 252 - 297,
MRLogs.tsx is still referencing the removed mrReview.logs keys and wrong
namespace; update every translation lookup in the MRLogs component that uses
"common:mrReview.logs.*" or "gitlab:mrReview.logs.*" to the new path
"gitlab:mrFiltering.logs.*" and ensure the useTranslation/useI18n call uses the
"gitlab" namespace (or explicitly prefix keys with "gitlab:") so lookups like
agentActivity, showMore, hideMore, noLogsYet, waitingForLogs, etc. resolve from
mrFiltering.logs.
- Remove unused PHASE_LABELS from MRLogs.tsx (GitHub security) - Replace `any` types with proper types in gitlab-api.ts (CodeRabbit) - Fix needsDiscussion to use blocking_discussions_resolved (Sentry) - Fix BrowserWindow.getAllWindows robustness (Gemini) - Fix StatusIndicator type mismatch (Gemini) - Add cache invalidation after note deletion (CodeRabbit) - Use detailed_merge_status instead of deprecated merge_status (CodeRabbit) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Return NOT_IMPLEMENTED error from TODO memory APIs instead of empty payload - Fix path aliases in AutoFixButton.tsx to use @/components and @shared/types - Update onGitLabAutoFixError to include issueIid parameter for proper filtering Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix path aliases in BatchReviewWizard.tsx to use @/components and @shared/types - Add error handling in handleApprove with try/catch and approvalError state Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix path alias in investigation-store.test.ts to use @shared/types - Fix deleteReview to check result.data?.deleted before clearing review state Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add clearSyncStatus() calls in sync-status-store on connection failure - Add request token guarding to loadGitLabIssues() to prevent stale responses Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use @shared/types path alias in investigation-store.ts - Make unknown errors non-recoverable in gitlab-error-parser.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused projectId prop from GitLabBatchReviewWizardProps - Display approvalError in the intro screen to show batch creation failures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Clear error state in importGitLabIssues on start/success - Propagate IPC errors in listMoreMRs, checkMergeReadiness, getLogs - Use translation interpolation for percentages in BatchReviewWizard - Add similarPercent translation key with interpolation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use @preload/api path alias instead of relative path in ipc.ts - Update misleading comment in mr-review-handlers.ts (hasMore check) - Rename test to reflect actual behavior (replace vs append) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add GitLabMRReviewProgress to imports - Change progress type from GitLabMRReviewResult to GitLabMRReviewProgress Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add tests for successful connection, failed connection, and exceptions - Mock window.electronAPI.checkGitLabConnection for testing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Over-fetch by 1 item to reliably determine hasMore in listMoreMRs - Localize MR label, More/Less button, and time formatting in MRLogs - Remove hardcoded 'en-US' from toLocaleTimeString calls - Add mrLabel translation key to en/fr gitlab.json Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
There was a problem hiding this comment.
Actionable comments posted: 13
♻️ Duplicate comments (2)
apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts (1)
9-14: 🧹 Nitpick | 🔵 TrivialUse path alias for shared types import.
Per coding guidelines, use
@shared/*path alias instead of relative paths.♻️ Suggested fix
-import type { - GitLabMergeRequest, - GitLabMRReviewResult, - GitLabMRReviewProgress, - GitLabNewCommitsCheck -} from '../../../../shared/types'; +import type { + GitLabMergeRequest, + GitLabMRReviewResult, + GitLabMRReviewProgress, + GitLabNewCommitsCheck +} from '@shared/types';As per coding guidelines: "Use path aliases defined in tsconfig.json for imports:
@shared/*(shared)".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts` around lines 9 - 14, The import in useGitLabMRFiltering.ts currently uses a relative path for shared types; update the import that brings in GitLabMergeRequest, GitLabMRReviewResult, GitLabMRReviewProgress, and GitLabNewCommitsCheck to use the path alias `@shared/types` instead of '../../../../shared/types' so it follows the project's tsconfig path aliases and coding guidelines.apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx (1)
414-418:⚠️ Potential issue | 🟡 MinorHardcoded English strings in orchestrator summary.
These summary strings are not internationalized, which breaks localization for non-English users.
if (readCount > 0) summaryParts.push(`${readCount} file${readCount > 1 ? 's' : ''} read`); if (searchCount > 0) summaryParts.push(`${searchCount} search${searchCount > 1 ? 'es' : ''}`); if (otherCount > 0) summaryParts.push(`${otherCount} other`); const summary = summaryParts.join(', ') || `${entries.length} operations`;♻️ Suggested fix
const summaryParts: string[] = []; - if (readCount > 0) summaryParts.push(`${readCount} file${readCount > 1 ? 's' : ''} read`); - if (searchCount > 0) summaryParts.push(`${searchCount} search${searchCount > 1 ? 'es' : ''}`); - if (otherCount > 0) summaryParts.push(`${otherCount} other`); - const summary = summaryParts.join(', ') || `${entries.length} operations`; + if (readCount > 0) summaryParts.push(t('common:mrReview.logs.filesRead', { count: readCount })); + if (searchCount > 0) summaryParts.push(t('common:mrReview.logs.searches', { count: searchCount })); + if (otherCount > 0) summaryParts.push(t('common:mrReview.logs.other', { count: otherCount })); + const summary = summaryParts.join(', ') || t('common:mrReview.logs.operations', { count: entries.length });Add corresponding translation keys to
en/*.jsonandfr/*.json.As per coding guidelines: "All frontend user-facing text must use
react-i18nexttranslation keys."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx` around lines 414 - 418, The summary strings in MRLogs.tsx (variables summaryParts, readCount, searchCount, otherCount, and summary) are hardcoded in English; replace them with react-i18next translation calls (t('...')) using pluralization where needed (e.g., file(s), search(es), other, and operations fallback) and build the summaryParts with translated segments instead of template literals, and add the corresponding keys with plural forms to the en/*.json and fr/*.json locales so the UI is fully localized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts`:
- Line 1342: Replace the loose any[] return type for mrs with the concrete
GitLabMergeRequest[] type: import GitLabMergeRequest from the shared types
module and change the function's return type from Promise<IPCResult<{ mrs:
any[]; hasMore: boolean }>> to Promise<IPCResult<{ mrs: GitLabMergeRequest[];
hasMore: boolean }>>; also cast or map the API response where mrs is assigned to
ensure the returned items conform to GitLabMergeRequest (refer to the mrs
variable and the function signature that currently declares mrs: any[]).
- Around line 49-68: Add calls to clearPollingForProject when a project is
removed and when the main window closes: import clearPollingForProject from the
MR review handlers module into project-handlers.ts and invoke
clearPollingForProject(projectId) inside the PROJECT_REMOVE handler (using the
same projectId the handler removes), and also import clearPollingForProject into
main/index.ts and call clearPollingForProject for the relevant project(s) inside
mainWindow.on('closed') before or after other cleanup to ensure any timers in
statusPollingIntervals are cleared. Ensure the imported symbol name matches
clearPollingForProject and that you handle cases where projectId may be
undefined/nullable.
- Around line 1180-1223: The interval callback currently closes over the outer
`project` variable causing stale config use; change the callback to obtain the
current project before calling `getGitLabConfig` (e.g., fetch the project by
`projectId` inside the setInterval) or change `getGitLabConfig` usage to
accept/lookup `projectId` so the code always calls `getGitLabConfig` with
up-to-date data; update references inside the interval where `project` was used
(the call to getGitLabConfig, then `encodeProjectPath(config.project)`, and
subsequent `gitlabFetch` with `token`/`instanceUrl`) to use the freshly-fetched
project/config, leaving `pollingInProgress`, `callingWindow`, `mrIid`, and
`intervalMs` logic unchanged.
In
`@apps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.ts`:
- Around line 27-39: The parseGitLabError function currently returns UNKNOWN for
non-Error values even when they are plain objects with a message; update
parseGitLabError to first detect Error-like objects (e.g., typeof error ===
'object' && error !== null && 'message' in error && typeof (error as
any).message === 'string') and call parseGitLabErrorMessage on that message,
otherwise fall back to returning { code: GitLabErrorCode.UNKNOWN, recoverable:
false }; make the change inside the parseGitLabError function and ensure types
remain strict for ParsedGitLabError and parseGitLabErrorMessage usage.
- Around line 49-90: The current substring checks on lowerMessage (e.g.,
lowerMessage.includes('401')) are too broad and can misclassify errors; update
the logic in gitlab-error-parser to prefer an explicit numeric status when
available (e.g., read error.response?.status or parse a "status" field) and
otherwise replace naive includes('401'|'403'|'404'|'409'|'429') with regex
word-boundary checks like /\b401\b/ so digits inside URLs/payloads won't match;
update the branches that return GitLabErrorCode.* (AUTHENTICATION_FAILED,
RATE_LIMITED, NETWORK_ERROR, PROJECT_NOT_FOUND, INSUFFICIENT_PERMISSIONS,
CONFLICT) to use the stricter status detection (fall back to the safer regex
only if no explicit status present).
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx`:
- Around line 27-35: Update MRLogs.tsx to replace relative import paths with the
project's TypeScript path aliases: change imports of Badge and
Collapsible/CollapsibleTrigger/CollapsibleContent to use the UI alias (e.g.,
from '../../ui/...' to the configured alias like '@/ui/...'), update cn import
to use the utils alias (e.g., from '../../../lib/utils' to '@/lib/utils'), and
update the types import (PRLogs, PRLogPhase, PRPhaseLog, PRLogEntry) to use the
preload/api alias (e.g., from '../../../../preload/api/modules/github-api' to
the configured alias such as '@/preload/api/modules/github-api'); keep the
imported symbols (Badge, Collapsible, CollapsibleTrigger, CollapsibleContent,
cn, PRLogs, PRLogPhase, PRPhaseLog, PRLogEntry) unchanged so references inside
MRLogs.tsx continue to work.
- Around line 37-48: The MRLogs component expects a structured GitLabMRLogs
(alias PRLogs) object but the GITLAB_MR_GET_LOGS IPC handler currently returns
Promise<IPCResult<string[]>>, causing accesses like logs.is_followup,
logs.updated_at, and logs.phases[...] to blow up; fix by adding a transformer in
the data layer that calls GITLAB_MR_GET_LOGS and converts the returned string[]
into a PRLogs-shaped object (populate phases array with PRPhaseLog/PRLogEntry
items, set is_followup and updated_at metadata) before passing it into MRLogs,
or alternatively update the IPC handler itself to return PRLogs directly—ensure
the conversion logic maps entries into PRPhaseLog/PRLogEntry types so MRLogs can
safely access logs.is_followup, logs.updated_at, and logs.phases.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsx`:
- Around line 2-4: Replace the relative imports in StatusIndicator.tsx for the
symbols Badge, cn, and the types ChecksStatus/ReviewsStatus/MergeableState with
the project's tsconfig path aliases (instead of '../../ui/badge',
'../../../lib/utils', '../../../../shared/types/pr-status'); update the import
specifiers to the alias equivalents used in the repo (e.g., the alias that maps
to the UI badge module, the utils module, and the shared types module) so the
file imports Badge, cn, and the three types via configured path aliases.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts`:
- Around line 93-102: The useGitLabMRFiltering hook and MRFilterBar component
are defined but never used; either integrate them into the merge requests UI or
remove them. To fix, decide whether to enable filtering: if yes, import and
render MRFilterBar inside the GitLabMergeRequests component (pass required props
such as mrs and getReviewStateForMR) and call useGitLabMRFiltering within that
component or its parent to derive the filtered list; if no, remove the unused
exports (useGitLabMRFiltering and MRFilterBar) and any related types to avoid
dead code. Ensure references to useGitLabMRFiltering, MRFilterBar, and
GitLabMergeRequests are updated/cleaned accordingly.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.ts`:
- Around line 91-103: The test case titled "should get filtered issues" is
duplicated; remove the redundant test block (the second occurrence starting at
the later lines) so only one test asserting
useIssuesStore.getState().setIssues(...),
useIssuesStore.getState().setFilterState('opened') and
useIssuesStore.getState().getFilteredIssues() remains; delete the duplicate
`it('should get filtered issues', ...)` which repeats the same assertions to
avoid redundant tests.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts`:
- Around line 73-117: Add a test to cover the IPC payload shape where the
backend returns success: true but data.connected is false (e.g.,
mockElectronAPI.checkGitLabConnection resolves to { success: true, data: {
connected: false, error: '...' } }), call checkGitLabConnection('project-123'),
and assert that the function returns null,
useSyncStatusStore.getState().syncStatus is null, and
useSyncStatusStore.getState().connectionError equals the provided error string;
place this alongside the existing tests for checkGitLabConnection to prevent
regression of the "connected: false" failure path.
In `@apps/desktop/src/renderer/stores/gitlab/issues-store.ts`:
- Around line 108-125: The stale-request guard is reading a captured `store`
object (set via `const store = useIssuesStore.getState()`) so
`store.currentRequestToken` can be outdated; change the checks that compare the
in-flight `requestId` to instead read the live state with
`useIssuesStore.getState().currentRequestToken` (replace uses of
`store.currentRequestToken` in the response-guard where `requestId` is compared
and apply the same fix to the other similar guards later in `fetchGitLabIssues`
/ wherever `requestId` is used, e.g., the two additional checks mentioned),
keeping the initial `store` for setters like `setLoading`, `setError`, and
`setFilterState` but always reading current token via
`useIssuesStore.getState().currentRequestToken` for stale-response detection.
In `@apps/desktop/src/renderer/stores/gitlab/sync-status-store.ts`:
- Around line 60-66: The current branch treats any result.success + result.data
as success and calls store.setSyncStatus, which swallows connection failures
encoded as result.data.connected === false; change the logic in the handler that
processes result to first check result.success && result.data &&
result.data.connected === true before calling store.setSyncStatus; if
result.data.connected is false, call store.clearSyncStatus() and
store.setConnectionError(result.data.error || 'Failed to check GitLab
connection') and return null, preserving the existing else branch for
result.success === false to use result.error.
---
Duplicate comments:
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx`:
- Around line 414-418: The summary strings in MRLogs.tsx (variables
summaryParts, readCount, searchCount, otherCount, and summary) are hardcoded in
English; replace them with react-i18next translation calls (t('...')) using
pluralization where needed (e.g., file(s), search(es), other, and operations
fallback) and build the summaryParts with translated segments instead of
template literals, and add the corresponding keys with plural forms to the
en/*.json and fr/*.json locales so the UI is fully localized.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts`:
- Around line 9-14: The import in useGitLabMRFiltering.ts currently uses a
relative path for shared types; update the import that brings in
GitLabMergeRequest, GitLabMRReviewResult, GitLabMRReviewProgress, and
GitLabNewCommitsCheck to use the path alias `@shared/types` instead of
'../../../../shared/types' so it follows the project's tsconfig path aliases and
coding guidelines.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 939a1e97-5ce0-4f39-88f4-75d565342940
📒 Files selected for processing (19)
apps/desktop/src/main/ipc-handlers/gitlab/autofix-handlers.tsapps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.tsapps/desktop/src/preload/api/modules/gitlab-api.tsapps/desktop/src/renderer/components/gitlab-issues/components/AutoFixButton.tsxapps/desktop/src/renderer/components/gitlab-issues/components/BatchReviewWizard.tsxapps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.tsapps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.tsapps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRs.tsapps/desktop/src/renderer/stores/gitlab/__tests__/investigation-store.test.tsapps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.tsapps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.tsapps/desktop/src/renderer/stores/gitlab/investigation-store.tsapps/desktop/src/renderer/stores/gitlab/issues-store.tsapps/desktop/src/renderer/stores/gitlab/sync-status-store.tsapps/desktop/src/shared/i18n/locales/en/gitlab.jsonapps/desktop/src/shared/i18n/locales/fr/gitlab.jsonapps/desktop/src/shared/types/ipc.ts
| /** | ||
| * Registry of status polling intervals for MR updates | ||
| * Key format: `${projectId}:${mrIid}` | ||
| */ | ||
| const statusPollingIntervals = new Map<string, NodeJS.Timeout>(); | ||
| const pollingInProgress = new Set<string>(); | ||
|
|
||
| /** | ||
| * Clear all polling intervals for a specific project | ||
| * Called when a project is deleted to prevent memory leaks | ||
| */ | ||
| function clearPollingForProject(projectId: string): void { | ||
| const keysToDelete = Array.from(statusPollingIntervals.keys()) | ||
| .filter(key => key.startsWith(`${projectId}:`)); | ||
| keysToDelete.forEach(key => { | ||
| clearInterval(statusPollingIntervals.get(key)!); | ||
| statusPollingIntervals.delete(key); | ||
| pollingInProgress.delete(key); | ||
| }); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify clearPollingForProject is not called anywhere
rg "clearPollingForProject" apps/desktop/src/main/ -lRepository: AndyMik90/Aperant
Length of output: 124
🏁 Script executed:
#!/bin/bash
# Check if clearPollingForProject is exported from mr-review-handlers.ts
rg "export.*clearPollingForProject|clearPollingForProject.*export" apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.tsRepository: AndyMik90/Aperant
Length of output: 95
🏁 Script executed:
#!/bin/bash
# Check PROJECT_REMOVE handler in project-handlers.ts
rg -A 10 "PROJECT_REMOVE" apps/desktop/src/main/ipc-handlers/Repository: AndyMik90/Aperant
Length of output: 2023
🏁 Script executed:
#!/bin/bash
# Check mainWindow close handler in main/index.ts
rg -B 5 -A 10 "mainWindow\.on\('closed" apps/desktop/src/main/Repository: AndyMik90/Aperant
Length of output: 1029
Polling cleanup not integrated into project/window lifecycle.
clearPollingForProject is defined and exported but never called when:
- A project is removed (
PROJECT_REMOVEhandler inproject-handlers.ts) - The main window closes (
mainWindow.on('closed')inmain/index.ts)
This causes memory leaks as polling intervals accumulate indefinitely. Add calls to clearPollingForProject in both locations: import it in project-handlers.ts and call it within the PROJECT_REMOVE handler, and import it in main/index.ts and call it in the mainWindow close handler.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts` around lines
49 - 68, Add calls to clearPollingForProject when a project is removed and when
the main window closes: import clearPollingForProject from the MR review
handlers module into project-handlers.ts and invoke
clearPollingForProject(projectId) inside the PROJECT_REMOVE handler (using the
same projectId the handler removes), and also import clearPollingForProject into
main/index.ts and call clearPollingForProject for the relevant project(s) inside
mainWindow.on('closed') before or after other cleanup to ensure any timers in
statusPollingIntervals are cleared. Ensure the imported symbol name matches
clearPollingForProject and that you handle cases where projectId may be
undefined/nullable.
apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts
Outdated
Show resolved
Hide resolved
apps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.ts
Show resolved
Hide resolved
apps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.ts
Outdated
Show resolved
Hide resolved
| export function useGitLabMRFiltering( | ||
| mrs: GitLabMergeRequest[], | ||
| getReviewStateForMR: (mrIid: number) => { | ||
| isReviewing: boolean; | ||
| progress: GitLabMRReviewProgress | null; | ||
| result: GitLabMRReviewResult | null; | ||
| error: string | null; | ||
| newCommitsCheck: GitLabNewCommitsCheck | null; | ||
| } | null | ||
| ) { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if useGitLabMRFiltering is actually used anywhere
echo "=== Searching for useGitLabMRFiltering usage ==="
rg -n "useGitLabMRFiltering" apps/desktop/src/renderer/ --type ts --type tsx
echo ""
echo "=== Check if MRFilterBar is rendered ==="
rg -n "<MRFilterBar" apps/desktop/src/renderer/ --type tsxRepository: AndyMik90/Aperant
Length of output: 224
🏁 Script executed:
#!/bin/bash
# Search for useGitLabMRFiltering usage in the codebase
echo "=== Searching for useGitLabMRFiltering usage ==="
rg "useGitLabMRFiltering" apps/desktop/src/renderer/
echo ""
echo "=== Check if MRFilterBar is rendered or imported ==="
rg "MRFilterBar" apps/desktop/src/renderer/
echo ""
echo "=== List files in the gitlab-merge-requests directory ==="
find apps/desktop/src/renderer/components/gitlab-merge-requests -type f -name "*.ts*"Repository: AndyMik90/Aperant
Length of output: 2759
🏁 Script executed:
#!/bin/bash
# Check GitLabMergeRequests.tsx for MRFilterBar usage
echo "=== GitLabMergeRequests.tsx content ==="
cat -n apps/desktop/src/renderer/components/gitlab-merge-requests/GitLabMergeRequests.tsx
echo ""
echo "=== MRFilterBar.tsx content (first 50 lines) ==="
head -50 apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRFilterBar.tsxRepository: AndyMik90/Aperant
Length of output: 6388
Unused hook and filter component: Confirm integration plan or remove.
Verification confirms the useGitLabMRFiltering hook and MRFilterBar component are defined and exported but never instantiated. GitLabMergeRequests.tsx does not import or render MRFilterBar, and the useGitLabMRFiltering hook is not called anywhere. This appears to be prepared but unfinished work. Either complete the filtering integration or remove the unused code.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts`
around lines 93 - 102, The useGitLabMRFiltering hook and MRFilterBar component
are defined but never used; either integrate them into the merge requests UI or
remove them. To fix, decide whether to enable filtering: if yes, import and
render MRFilterBar inside the GitLabMergeRequests component (pass required props
such as mrs and getReviewStateForMR) and call useGitLabMRFiltering within that
component or its parent to derive the filtered list; if no, remove the unused
exports (useGitLabMRFiltering and MRFilterBar) and any related types to avoid
dead code. Ensure references to useGitLabMRFiltering, MRFilterBar, and
GitLabMergeRequests are updated/cleaned accordingly.
apps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.ts
Outdated
Show resolved
Hide resolved
apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts
Show resolved
Hide resolved
- Replace any[] with GitLabMergeRequest[] in MR list handler - Add clearPollingForProject calls on project remove & window close - Fix stale config closure in MR status polling interval - Handle Error-like objects with message property in error parser - Use regex word boundaries (\b401\b) for status code detection - Replace relative imports with path aliases in MRLogs/StatusIndicator - Add note reserving useGitLabMRFiltering for future functionality - Remove duplicate test case in issues-store.test.ts - Add test for connected:false case in sync-status-store.test.ts - Fix stale-request guard to use getState() for live token comparison - Handle result.data.connected === false explicitly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 14
♻️ Duplicate comments (2)
apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx (1)
43-48:⚠️ Potential issue | 🔴 CriticalType mismatch: Component expects structured
PRLogsbut handler returnsstring[].The
MRLogscomponent expectsGitLabMRLogs(aliased toPRLogs) with properties likelogs.is_followup(line 195),logs.updated_at(line 205), andlogs.phases[phase](line 220). However, perapps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.tslines 1124-1157, theGITLAB_MR_GET_LOGSIPC handler returnsPromise<IPCResult<string[]>>.This will cause runtime errors when the component tries to access properties on what is actually a string array.
Options:
- Update the IPC handler to build and return a
PRLogs-compatible structure- Add a transformation layer in the hook/store that converts
string[]toPRLogs- Update the component to handle the
string[]format directlyAlso applies to: 189-227
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx` around lines 43 - 48, The MRLogs component (MRLogs) expects a structured GitLabMRLogs/PRLogs object (accessing logs.is_followup, logs.updated_at, logs.phases[...] etc.) but the GITLAB_MR_GET_LOGS IPC handler in mr-review-handlers currently returns string[]; fix by making the data shape consistent: either (preferred) update the GITLAB_MR_GET_LOGS handler to construct and return a PRLogs-compatible object (populate is_followup, updated_at, phases, messages, etc.), or add a transformation in the hook/store that calls GITLAB_MR_GET_LOGS and maps the returned string[] into a GitLabMRLogs/PRLogs shape before passing to MRLogs; ensure the transformation lives near the IPC call (mr-review-handlers or the store/hook that consumes it) and reference the MRLogs prop names (logs.is_followup, logs.updated_at, logs.phases) when building the returned object so the component no longer accesses array methods on a plain string[].apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts (1)
1372-1381: 🧹 Nitpick | 🔵 TrivialCast API response to the declared return type.
The return type declares
mrs: GitLabMergeRequest[], but line 1376 casts toany[], losing the type safety benefit. Cast to the correct type for consistency with the signature.♻️ Proposed fix
const mrs = await gitlabFetch( token, instanceUrl, `/projects/${encodedProject}/merge_requests?${queryParams.toString()}` - ) as any[]; + ) as GitLabMergeRequest[];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts` around lines 1372 - 1381, The variable mrs is being cast to any[] after calling gitlabFetch, which loses type safety; change the cast to the declared return type so mrs is typed as GitLabMergeRequest[] (i.e., replace the "as any[]" cast on the gitlabFetch result with "as GitLabMergeRequest[]") and keep the rest of the logic (hasMore and returnMrs) unchanged so the function signature and downstream code remain consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/main/index.ts`:
- Around line 355-364: The window-close cleanup uses a non-existent method
projectStore.getAllProjects() causing a runtime error; update the handler to
call projectStore.getProjects() instead and iterate that result when invoking
clearPollingForProject (retain
import('./ipc-handlers/gitlab/mr-review-handlers') and the
require('./project-store') usage and ensure you call
clearPollingForProject(project.id) for each project returned by getProjects()).
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts`:
- Around line 1219-1225: Replace the hardcoded channel string in the call to
callingWindow.webContents.send('gitlab:mr:statusUpdate', ...) with a constant
from IPC_CHANNELS: either add GITLAB_MR_STATUS_UPDATE = 'gitlab:mr:statusUpdate'
to the IPC_CHANNELS object in the ipc constants file and use
IPC_CHANNELS.GITLAB_MR_STATUS_UPDATE here, or reuse an existing channel like
IPC_CHANNELS.GITLAB_MR_REVIEW_PROGRESS; update the send call to use the chosen
IPC_CHANNELS constant and ensure imports reference IPC_CHANNELS where this
handler defines or already imports it.
In
`@apps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.ts`:
- Around line 103-117: The 404 and 403 branches currently return recoverable:
true which is incorrect; update the return objects for the Project not found and
Permission denied cases in gitlab-error-parser.ts so that
GitLabErrorCode.PROJECT_NOT_FOUND and GitLabErrorCode.INSUFFICIENT_PERMISSIONS
both have recoverable: false, and make the same change in the corresponding HTTP
status-code handling branches (the 404/403 status-code branches) so retries are
not triggered for these errors.
- Around line 95-101: The substring check for 'connect' in the network-error
branch is too broad and yields false positives; update the condition that
inspects lowerMessage (used in the network detection block that returns
GitLabErrorCode.NETWORK_ERROR, recoverable: true) to use either word-boundary
matching or explicit failure phrases (e.g., "connection refused", "connection
failed", "unable to connect", "connect timeout") instead of a plain 'connect'
contains check so only real connection failures are classified as network
errors.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx`:
- Around line 414-418: Replace the hardcoded English fragments built into
summaryParts (using readCount, searchCount, otherCount) and the computed summary
with i18n translation calls; use react-i18next's t function with pluralization
for the file/read and search/s searches (e.g. t('mrReview.logs.filesRead', {
count: readCount })) and similar for searches, other and the fallback operations
(t('mrReview.logs.operations', { count: entries.length })), then push the
translated strings into summaryParts and join into summary; also add the new
keys (mrReview.logs.filesRead/_plural, searches/_plural, other, operations) to
the translation JSON so translations exist.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsx`:
- Around line 48-62: The translation keys in the StatusIndicator component cases
('approved', 'changes_requested', 'pending') use the wrong namespace (e.g.,
t('mrStatus.review.approved')); update those calls to the correct root-level
keys (e.g., t('review.approved'), t('review.changesRequested'),
t('review.pending')) so the Badge labels render localized text; locate and
update the t(...) usages inside the StatusIndicator switch/case return blocks to
use the corrected keys.
- Around line 122-133: The merge status mappings are incomplete causing
undefined mergeKey/mergeableState; update the mergeKeyMap and
gitlabToMergeableState objects to include all GitLab detailed_merge_status
values (e.g., policies, merge_when_pipeline_succeeds, pipeline_failed,
pipeline_success, cant_be_merged, blocked, unchecked, web_ide, etc.) and ensure
a safe default mapping for any unknown status (e.g., map unrecognized keys to
'unknown' or 'conflict' and a corresponding MergeableState like 'blocked' or
'dirty'); modify the objects named mergeKeyMap and gitlabToMergeableState and
ensure the component that uses showMergeStatus && mergeKey && mergeableState
will receive defined values for all API responses.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts`:
- Around line 75-81: The condition checking `hasPosted &&
hasCommitsAfterPosting` is redundant because `hasCommitsAfterPosting` is already
`hasNewCommits && hasPosted`; update the conditional in the
`useGitLabMRFiltering` hook to only check `hasCommitsAfterPosting` (i.e.,
replace `if (hasPosted && hasCommitsAfterPosting)` with `if
(hasCommitsAfterPosting)`) so the logic is simpler and equivalent.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.ts`:
- Around line 5-6: Tests import the store and type using relative paths; update
the imports to use the project's TS path aliases: replace the relative import of
useIssuesStore with the renderer alias (import from
'@/renderer/stores/gitlab/issues-store' or simply '@/stores/gitlab/issues-store'
matching your alias layout) and replace the GitLabIssue type import with the
shared alias (import from '@shared/types'); update the import statements that
reference useIssuesStore and GitLabIssue accordingly so they use `@/`* for
renderer code and `@shared/`* for shared types.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts`:
- Around line 5-7: Replace the relative imports with the project's path aliases:
import useSyncStatusStore and checkGitLabConnection from the renderer alias
(e.g. '@/stores/gitlab/sync-status-store') instead of '../sync-status-store',
and import GitLabSyncStatus from the shared alias (e.g. '@shared/types') instead
of '../../../../shared/types'; update the import statements that reference
useSyncStatusStore, checkGitLabConnection, and GitLabSyncStatus to use these
aliases to match tsconfig path mappings.
In `@apps/desktop/src/renderer/stores/gitlab/issues-store.ts`:
- Line 68: setError currently forces isLoading to false (setError -> set({
error, isLoading: false })), which prematurely clears loading for in-flight
async flows that call setError(null); remove the isLoading toggle from setError
so it only updates the error state (i.e., setError: (error) => set({ error })),
and let the async callers that previously invoked setError(null) manage
isLoading explicitly (keep their set({ isLoading: true/false }) calls intact) so
loading state is not cleared unexpectedly.
- Around line 10-11: Replace the relative shared imports with the configured
path aliases: change the imports that reference '../../../shared/types' and
'../../../shared/integrations/types/base-types' to use the `@shared` alias (e.g.,
import GitLabIssue from '@shared/types' and GitLabFilterState from
'@shared/integrations/types/base-types') so the module resolution uses the
tsconfig path mappings; update the import statements where GitLabIssue and
GitLabFilterState are referenced to use these `@shared/`* paths.
- Around line 74-79: The clearIssues action currently resets issues,
selectedIssueIid, error, and currentRequestToken but omits the loading flag,
which can leave the store stuck in loading; update the clearIssues setter to
also reset loading to false (alongside issues, selectedIssueIid, error,
currentRequestToken) so the store is fully reset when clearIssues() is called.
In `@apps/desktop/src/renderer/stores/gitlab/sync-status-store.ts`:
- Line 8: The import currently uses a relative path "import type {
GitLabSyncStatus } from '../../../shared/types';" — change it to use the
tsconfig alias by replacing the relative import with the aliased path
"@shared/types"; update the import statement in sync-status-store.ts (the line
importing GitLabSyncStatus) so it reads from "@shared/*" instead of
"../../../shared/*" to follow renderer import conventions.
---
Duplicate comments:
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts`:
- Around line 1372-1381: The variable mrs is being cast to any[] after calling
gitlabFetch, which loses type safety; change the cast to the declared return
type so mrs is typed as GitLabMergeRequest[] (i.e., replace the "as any[]" cast
on the gitlabFetch result with "as GitLabMergeRequest[]") and keep the rest of
the logic (hasMore and returnMrs) unchanged so the function signature and
downstream code remain consistent.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx`:
- Around line 43-48: The MRLogs component (MRLogs) expects a structured
GitLabMRLogs/PRLogs object (accessing logs.is_followup, logs.updated_at,
logs.phases[...] etc.) but the GITLAB_MR_GET_LOGS IPC handler in
mr-review-handlers currently returns string[]; fix by making the data shape
consistent: either (preferred) update the GITLAB_MR_GET_LOGS handler to
construct and return a PRLogs-compatible object (populate is_followup,
updated_at, phases, messages, etc.), or add a transformation in the hook/store
that calls GITLAB_MR_GET_LOGS and maps the returned string[] into a
GitLabMRLogs/PRLogs shape before passing to MRLogs; ensure the transformation
lives near the IPC call (mr-review-handlers or the store/hook that consumes it)
and reference the MRLogs prop names (logs.is_followup, logs.updated_at,
logs.phases) when building the returned object so the component no longer
accesses array methods on a plain string[].
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: d4968ddd-8f18-4149-ab86-8b4593a8ffdb
📒 Files selected for processing (11)
apps/desktop/src/main/index.tsapps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.tsapps/desktop/src/main/ipc-handlers/project-handlers.tsapps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.tsapps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.tsapps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.tsapps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.tsapps/desktop/src/renderer/stores/gitlab/issues-store.tsapps/desktop/src/renderer/stores/gitlab/sync-status-store.ts
apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts
Outdated
Show resolved
Hide resolved
apps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.ts
Outdated
Show resolved
Hide resolved
apps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.ts
Outdated
Show resolved
Hide resolved
apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx
Outdated
Show resolved
Hide resolved
| import { useSyncStatusStore } from '../sync-status-store'; | ||
| import { checkGitLabConnection } from '../sync-status-store'; | ||
| import type { GitLabSyncStatus } from '../../../../shared/types'; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use configured path aliases instead of relative imports.
Please switch these imports to the project aliases (@/*, @shared/*) to keep imports consistent with repo standards.
As per coding guidelines, apps/desktop/src/**/*.{ts,tsx}: Use path aliases defined in tsconfig.json for imports: @/* (renderer), @shared/* (shared), @preload/* (preload), @features/*, @components/*, @hooks/*, @lib/*.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts`
around lines 5 - 7, Replace the relative imports with the project's path
aliases: import useSyncStatusStore and checkGitLabConnection from the renderer
alias (e.g. '@/stores/gitlab/sync-status-store') instead of
'../sync-status-store', and import GitLabSyncStatus from the shared alias (e.g.
'@shared/types') instead of '../../../../shared/types'; update the import
statements that reference useSyncStatusStore, checkGitLabConnection, and
GitLabSyncStatus to use these aliases to match tsconfig path mappings.
| clearIssues: () => set({ | ||
| issues: [], | ||
| selectedIssueIid: null, | ||
| error: null, | ||
| currentRequestToken: null | ||
| }), |
There was a problem hiding this comment.
clearIssues should reset loading as part of full store reset.
If this action runs while loading is true, the store can remain in a loading state after reset.
🔧 Proposed fix
clearIssues: () => set({
issues: [],
+ isLoading: false,
selectedIssueIid: null,
error: null,
currentRequestToken: null
}),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| clearIssues: () => set({ | |
| issues: [], | |
| selectedIssueIid: null, | |
| error: null, | |
| currentRequestToken: null | |
| }), | |
| clearIssues: () => set({ | |
| issues: [], | |
| isLoading: false, | |
| selectedIssueIid: null, | |
| error: null, | |
| currentRequestToken: null | |
| }), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/stores/gitlab/issues-store.ts` around lines 74 -
79, The clearIssues action currently resets issues, selectedIssueIid, error, and
currentRequestToken but omits the loading flag, which can leave the store stuck
in loading; update the clearIssues setter to also reset loading to false
(alongside issues, selectedIssueIid, error, currentRequestToken) so the store is
fully reset when clearIssues() is called.
Critical fixes: - Fix projectStore.getAllProjects() -> getProjects() in index.ts - Add type guard and fallback for MRLogs string[] vs PRLogs mismatch - Add IPC_CHANNELS.GITLAB_MR_STATUS_UPDATE constant Other fixes: - Cast gitlabFetch result to GitLabMergeRequest[] not any[] - 404/403 errors: recoverable: false (not recoverable) - Network error: use specific phrases instead of 'connect' substring - Add i18n keys for MRLogs summary (filesRead, searches, other, operations) - Fix StatusIndicator translation namespace (prStatus.review.*) - Add comprehensive merge status mappings for GitLab - Remove redundant hasPosted check in useGitLabMRFiltering - Remove isLoading toggle from setError (callers manage explicitly) - Add loading: false to clearIssues reset - Use @shared/types alias in store files - Add fr translations for MRLogs summary keys Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| function isStructuredLogs(logs: unknown): logs is PRLogs { | ||
| return ( | ||
| typeof logs === 'object' && | ||
| logs !== null && |
Check warning
Code scanning / CodeQL
Comparison between inconvertible types Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
In general, to fix "comparison between inconvertible types" and related structural issues in a type guard, we should ensure that:
- comparisons against
nullor other primitives are done only where appropriate, and - for a structural type guard, we validate that the object really matches the expected shape, not just that certain keys exist.
Here, the minimal functional behavior is: if logs is a PRLogs-shaped object, isStructuredLogs should return true; otherwise, including when it is a string[], it should return false. Currently, the guard only checks that logs is a non-null object and that three property names exist on it, which would be true for many unrelated objects. We can keep the initial typeof logs === 'object' && logs !== null (this is valid and needed), but we should then refine the shape checks by:
- Asserting that
logshas aphasesproperty that is an array. - Asserting that its
is_followupfield is boolean andupdated_atis a string (or at least present with reasonable primitive types).
This more precise validation removes the heuristic that may have confused CodeQL and makes the type guard more robust.
Concretely, in apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx, edit the isStructuredLogs function (lines 51–58) as follows:
- Keep the function signature and
typeof logs === 'object' && logs !== nullcheck. - Inside, assign
const candidate = logs as Partial<PRLogs>;. - Return a conjunction that ensures
candidate.phasesis an array,candidate.is_followupis a boolean, andcandidate.updated_atis a string.
No new imports are required; this uses only existing types and built-in JavaScript type checks.
| @@ -49,12 +49,16 @@ | ||
|
|
||
| // Type guard to check if logs is the expected PRLogs structure or a plain string array | ||
| function isStructuredLogs(logs: unknown): logs is PRLogs { | ||
| if (typeof logs !== 'object' || logs === null) { | ||
| return false; | ||
| } | ||
|
|
||
| const candidate = logs as Partial<PRLogs>; | ||
|
|
||
| return ( | ||
| typeof logs === 'object' && | ||
| logs !== null && | ||
| 'is_followup' in logs && | ||
| 'updated_at' in logs && | ||
| 'phases' in logs | ||
| Array.isArray(candidate.phases) && | ||
| typeof candidate.is_followup === 'boolean' && | ||
| typeof candidate.updated_at === 'string' | ||
| ); | ||
| } | ||
|
|
|
|
||
| // Handle both structured PRLogs and plain string[] formats | ||
| // TODO: Remove this fallback when GITLAB_MR_GET_LOGS returns structured PRLogs | ||
| const isStructured = logs && isStructuredLogs(logs); |
Check notice
Code scanning / CodeQL
Unused variable, import, function or class Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 2 hours ago
To fix the problem, remove the unused variable binding isStructured. Since the code only needs the array form (logsAsArray) for the fallback behavior, we can derive that directly from logs without computing and storing isStructured.
The minimal, behavior-preserving change is:
- Delete the line defining
isStructured. - Keep
logsAsArrayas-is (or, if desired, slightly adjust it to be robust, but that is not necessary for this fix).
Concretely, in apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx, in the MRLogs component right after the comment // TODO: Remove this fallback when GITLAB_MR_GET_LOGS returns structured PRLogs, remove line 175 that defines isStructured. No imports or additional methods are needed.
| @@ -172,7 +172,6 @@ | ||
|
|
||
| // Handle both structured PRLogs and plain string[] formats | ||
| // TODO: Remove this fallback when GITLAB_MR_GET_LOGS returns structured PRLogs | ||
| const isStructured = logs && isStructuredLogs(logs); | ||
| const logsAsArray = Array.isArray(logs) ? logs : null; | ||
|
|
||
| const togglePhase = (phase: GitLabMRLogPhase) => { |
There was a problem hiding this comment.
Actionable comments posted: 8
♻️ Duplicate comments (5)
apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts (1)
5-6: 🛠️ Refactor suggestion | 🟠 MajorUse project path alias for the store import.
These imports should use the configured renderer alias instead of relative traversal.
♻️ Suggested import update
-import { useSyncStatusStore } from '../sync-status-store'; -import { checkGitLabConnection } from '../sync-status-store'; +import { checkGitLabConnection, useSyncStatusStore } from '@/stores/gitlab/sync-status-store';As per coding guidelines,
apps/desktop/src/**/*.{ts,tsx}: Use path aliases defined in tsconfig.json for imports:@/*(renderer),@shared/*,@preload/*,@features/*,@components/*,@hooks/*,@lib/*.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts` around lines 5 - 6, The imports for the store use relative paths; update them to use the renderer path alias so they follow project tsconfig conventions. Replace the two relative imports of useSyncStatusStore and checkGitLabConnection with the aliased import from the store module (e.g. import { useSyncStatusStore, checkGitLabConnection } from '@/stores/gitlab/sync-status-store'), ensuring the exported symbols useSyncStatusStore and checkGitLabConnection are imported via the `@/`* alias.apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts (1)
1214-1217:⚠️ Potential issue | 🟠 MajorPrefer
detailed_merge_statusin poll payloads for GitLab API compatibility.The status event still emits
merge_status. Line 1215 keeps relying on a deprecated API field; emit detailed status (or fallback) instead.🔧 Proposed fix
const mrData = await gitlabFetch( token, instanceUrl, `/projects/${encodedProject}/merge_requests/${mrIid}` ) as { state?: string; + detailed_merge_status?: string; merge_status?: string; updated_at?: string; }; callingWindow.webContents.send(IPC_CHANNELS.GITLAB_MR_STATUS_UPDATE, { projectId, mrIid, state: mrData.state, - mergeStatus: mrData.merge_status, + mergeStatus: mrData.detailed_merge_status ?? mrData.merge_status, updatedAt: mrData.updated_at });In the GitLab Merge Requests REST API, is `merge_status` deprecated in favor of `detailed_merge_status`, and what field should clients use for forward compatibility?Also applies to: 1223-1224
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts` around lines 1214 - 1217, The poll payload currently emits the deprecated merge_status field; update the payload to prefer detailed_merge_status (and include detailed_merge_status?: string in the payload shape) and emit detailed_merge_status when available, falling back to merge_status only if detailed_merge_status is undefined; update both occurrences noted around the merge_status declaration (and the other instance at lines referenced) so the status event uses detailed_merge_status (with fallback to merge_status) and keep updated_at/state unchanged.apps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.ts (1)
5-5: 🧹 Nitpick | 🔵 TrivialUse configured path aliases in test imports.
Please switch the relative store import to the repo alias to keep import resolution consistent with the rest of
src.♻️ Proposed fix
-import { useIssuesStore } from '../issues-store'; +import { useIssuesStore } from '@/renderer/stores/gitlab/issues-store';As per coding guidelines,
apps/desktop/src/**/*.{ts,tsx}must use path aliases defined in tsconfig (@/*,@shared/*, etc.).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.ts` at line 5, Replace the relative import of the store in the test with the tsconfig path alias: change the import of useIssuesStore from '../issues-store' to use the repo alias (e.g. '@/renderer/stores/gitlab/issues-store') so the test imports useIssuesStore via the project alias instead of a relative path.apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx (1)
214-273:⚠️ Potential issue | 🟠 MajorTranslation key path/namespace is inconsistent with locale structure.
This component reads
gitlab:mrReview.logs.*andcommon:mrReview.logs.*, but the provided locale additions are undergitlab:mrFiltering.logs.*. These lookups will miss at runtime.🔧 Proposed fix
- {t('gitlab:mrReview.logs.mrLabel', { iid: mrIid })} + {t('gitlab:mrFiltering.logs.mrLabel', { iid: mrIid })} - {t('gitlab:mrReview.logs.waitingForLogs')} + {t('gitlab:mrFiltering.logs.waitingForLogs')} - {t('gitlab:mrReview.logs.filesRead', { count: readCount })} + {t('gitlab:mrFiltering.logs.filesRead', { count: readCount })} - <span className="text-xs text-muted-foreground">{t('common:mrReview.logs.agentActivity')}</span> + <span className="text-xs text-muted-foreground">{t('gitlab:mrFiltering.logs.agentActivity')}</span> - <span>{t('common:mrReview.logs.showMore', { count: otherEntries.length })}</span> + <span>{t('gitlab:mrFiltering.logs.showMore', { count: otherEntries.length })}</span>As per coding guidelines,
apps/desktop/src/renderer/**/*.{tsx,jsx}requires user-facing text to resolve throughreact-i18nextkeys that exist in locale files for both English and French.Also applies to: 302-334, 362-375, 449-457, 476-477, 584-590, 654-660, 718-724
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx` around lines 214 - 273, The component MRLogs.tsx is using translation keys under the gitlab:mrReview.logs.* (and some common: namespaces) but the locale entries were added under gitlab:mrFiltering.logs.*, so lookups will fail at runtime; update MRLogs.tsx to use the exact namespace/key names present in the locale files (e.g., replace gitlab:mrReview.logs.* with gitlab:mrFiltering.logs.* or vice versa), or alternatively add the missing gitlab:mrReview.logs.* entries to both English and French locale files; ensure you also apply the same fix for the other affected usages at the indicated ranges (302-334, 362-375, 449-457, 476-477, 584-590, 654-660, 718-724) so all user-facing strings resolve via react-i18next.apps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsx (1)
123-154:⚠️ Potential issue | 🟠 MajorUnknown
mergeStatusvalues are dropped instead of rendered with a fallback.The maps are partial, and Lines 171-172 read them without fallback. Then Line 198 gates rendering on
mergeKey && mergeableState, so any unrecognized status silently hides merge readiness.♻️ Proposed fix
+const DEFAULT_MERGE_KEY = 'checking'; +const DEFAULT_MERGEABLE_STATE: MergeableState = 'blocked'; + const mergeKeyMap: Record<string, string> = { can_be_merged: 'ready', cannot_be_merged: 'conflict', checking: 'checking', @@ }; const gitlabToMergeableState: Record<string, MergeableState> = { can_be_merged: 'clean', cannot_be_merged: 'dirty', checking: 'blocked', @@ }; @@ - const mergeKey = mergeStatus ? mergeKeyMap[mergeStatus] : null; - const mergeableState = mergeStatus ? gitlabToMergeableState[mergeStatus] : null; + const mergeKey = mergeStatus ? (mergeKeyMap[mergeStatus] ?? DEFAULT_MERGE_KEY) : null; + const mergeableState = mergeStatus + ? (gitlabToMergeableState[mergeStatus] ?? DEFAULT_MERGEABLE_STATE) + : null;GitLab Merge Requests API docs: what are the currently documented possible values for `detailed_merge_status`?Also applies to: 171-173, 198-199
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsx` around lines 123 - 154, The mapping lookups drop unknown GitLab statuses; update the code that derives mergeKey and mergeableState from mergeKeyMap and gitlabToMergeableState (and any locations where mergeKey/mergeableState are used to gate rendering) to provide explicit fallbacks (e.g., default keys like 'unknown' -> 'checking' or MergeableState 'blocked'/'clean' as your app prefers) using nullish/boolean-safe fallback logic so unknown detailed_merge_status values still render a sensible indicator instead of hiding it; adjust the conditional that currently checks mergeKey && mergeableState to allow the fallback values to pass so the component always shows a default state for unrecognized statuses.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts`:
- Around line 1195-1201: The poll loop no-ops forever when the window or project
goes away; modify the poller callback (where callingWindow and
projectStore.getProject(projectId) are checked) to stop the poller when either
callingWindow is destroyed or currentProject is missing by calling
clearInterval/clearTimeout on the poll timer and removing any poller
registration for this project/merge request; locate the poller using the poll
setup identifiers in mr-review-handlers.ts (the function containing
callingWindow, projectStore.getProject(projectId), and the timer reference used
to schedule the poll) and ensure the same cleanup is applied at the other
occurrence around lines 1234-1235.
- Around line 1165-1166: Clamp the incoming polling interval in the
statusPollStart IPC handler before any timer is created: validate the parameter
`intervalMs` at the top of the async handler (the function declared as async
(event, projectId: string, mrIid: number, intervalMs: number = 5000) => ...) and
coerce it into a safe range (e.g. Math.max(MIN_INTERVAL_MS, Math.min(intervalMs
|| DEFAULT_MS, MAX_INTERVAL_MS))). Use the clamped value for any
setInterval/setTimeout creation and for storage in any maps/records used by the
polling logic (same handler and related stop/cleanup code around the
statusPollStart/statusPollStop logic), and ensure negative/zero/NaN values are
replaced with the default or minimum to prevent a tight loop.
- Around line 1219-1225: Add a new preload listener method named
onGitLabMRStatusUpdate that mirrors the existing onPRStatusUpdate pattern: in
the gitlab-api module expose onGitLabMRStatusUpdate and implement it by calling
createIpcListener(IPC_CHANNELS.GITLAB_MR_STATUS_UPDATE, callback) so the
renderer can subscribe to MR status events emitted by the MR review handler;
ensure the exported API surface includes onGitLabMRStatusUpdate and that it
forwards the callback to createIpcListener exactly like onPRStatusUpdate does.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx`:
- Around line 66-68: getPhaseLabel builds locale keys by appending "Gathering"
to the phase (e.g., "analysisGathering"/"synthesisGathering") which don't exist;
change getPhaseLabel to map GitLabMRLogPhase values to the actual locale key
names (e.g., map the analysis phase to "aiAnalysis" and the synthesis phase to
"synthesis") then call t with the mapped key
(t(`gitlab:mrReview.logs.${mappedKey}`)). Update getPhaseLabel to use a switch
or a small lookup object keyed by GitLabMRLogPhase to avoid generating missing
keys.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsx`:
- Around line 167-169: The current guard in StatusIndicator (checking only
!checksStatus && !reviewsStatus && !mergeStatus) can still render an empty
container when statuses are present but equal to non-renderable values like
"none"; update the conditional used before returning null to explicitly test for
renderable statuses (e.g., checksStatus !== "none" || reviewsStatus !== "none"
|| mergeStatus !== "none") or introduce a small helper like
isRenderableStatus(status) and use isRenderableStatus(checksStatus) ||
isRenderableStatus(reviewsStatus) || isRenderableStatus(mergeStatus) so the
component truly returns null when nothing should be rendered and avoids
producing an empty <div>.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts`:
- Around line 20-28: The 'all' variant of GitLabMRStatusFilter is currently
being treated like any other status which causes matchesStatus to return false
when the UI sends ['all']; update the matching logic (the function/variable
named matchesStatus used when applying GitLabMRStatusFilter) so that if the
incoming statusFilters array includes 'all' it immediately returns true (treat
'all' as a wildcard), otherwise continue checking the other statuses; ensure
this change references the GitLabMRStatusFilter type and the matchesStatus
implementation so the list is not emptied when 'all' is selected.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts`:
- Around line 14-22: The test currently mutates globalThis.window at module
scope with mockElectronAPI which pollutes the global environment; replace that
pattern by stubbing the global using vi.stubGlobal('window', { electronAPI:
mockElectronAPI }) inside the test lifecycle and ensure it is undone after each
test using vi.unstubAllGlobals() (or vi.restoreAllMocks()/appropriate Vitest
cleanup) so the global window is not left set between tests; update the setup
that references mockElectronAPI and keep the existing
useSyncStatusStore.getState().clearSyncStatus() in beforeEach, adding an
afterEach that calls vi.unstubAllGlobals() to restore the global state.
In `@apps/desktop/src/renderer/stores/gitlab/sync-status-store.ts`:
- Around line 55-77: The checkGitLabConnection function can let an out-of-order
IPC response overwrite the singleton store; to fix, capture a request identifier
before awaiting (e.g., read currentProjectId or increment a requestCounter on
useSyncStatusStore.getState()) and after the await compare that identifier to
the latest in the store—only call store.setSyncStatus, store.clearSyncStatus, or
store.setConnectionError if the identifiers match; implement this check around
all post-await branches in checkGitLabConnection to ignore stale responses.
---
Duplicate comments:
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts`:
- Around line 1214-1217: The poll payload currently emits the deprecated
merge_status field; update the payload to prefer detailed_merge_status (and
include detailed_merge_status?: string in the payload shape) and emit
detailed_merge_status when available, falling back to merge_status only if
detailed_merge_status is undefined; update both occurrences noted around the
merge_status declaration (and the other instance at lines referenced) so the
status event uses detailed_merge_status (with fallback to merge_status) and keep
updated_at/state unchanged.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx`:
- Around line 214-273: The component MRLogs.tsx is using translation keys under
the gitlab:mrReview.logs.* (and some common: namespaces) but the locale entries
were added under gitlab:mrFiltering.logs.*, so lookups will fail at runtime;
update MRLogs.tsx to use the exact namespace/key names present in the locale
files (e.g., replace gitlab:mrReview.logs.* with gitlab:mrFiltering.logs.* or
vice versa), or alternatively add the missing gitlab:mrReview.logs.* entries to
both English and French locale files; ensure you also apply the same fix for the
other affected usages at the indicated ranges (302-334, 362-375, 449-457,
476-477, 584-590, 654-660, 718-724) so all user-facing strings resolve via
react-i18next.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsx`:
- Around line 123-154: The mapping lookups drop unknown GitLab statuses; update
the code that derives mergeKey and mergeableState from mergeKeyMap and
gitlabToMergeableState (and any locations where mergeKey/mergeableState are used
to gate rendering) to provide explicit fallbacks (e.g., default keys like
'unknown' -> 'checking' or MergeableState 'blocked'/'clean' as your app prefers)
using nullish/boolean-safe fallback logic so unknown detailed_merge_status
values still render a sensible indicator instead of hiding it; adjust the
conditional that currently checks mergeKey && mergeableState to allow the
fallback values to pass so the component always shows a default state for
unrecognized statuses.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.ts`:
- Line 5: Replace the relative import of the store in the test with the tsconfig
path alias: change the import of useIssuesStore from '../issues-store' to use
the repo alias (e.g. '@/renderer/stores/gitlab/issues-store') so the test
imports useIssuesStore via the project alias instead of a relative path.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts`:
- Around line 5-6: The imports for the store use relative paths; update them to
use the renderer path alias so they follow project tsconfig conventions. Replace
the two relative imports of useSyncStatusStore and checkGitLabConnection with
the aliased import from the store module (e.g. import { useSyncStatusStore,
checkGitLabConnection } from '@/stores/gitlab/sync-status-store'), ensuring the
exported symbols useSyncStatusStore and checkGitLabConnection are imported via
the `@/`* alias.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 69617973-700d-428f-856d-343d9c5f415c
📒 Files selected for processing (13)
apps/desktop/src/main/index.tsapps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.tsapps/desktop/src/renderer/components/gitlab-issues/utils/gitlab-error-parser.tsapps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsxapps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.tsapps/desktop/src/renderer/stores/gitlab/__tests__/issues-store.test.tsapps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.tsapps/desktop/src/renderer/stores/gitlab/issues-store.tsapps/desktop/src/renderer/stores/gitlab/sync-status-store.tsapps/desktop/src/shared/constants/ipc.tsapps/desktop/src/shared/i18n/locales/en/gitlab.jsonapps/desktop/src/shared/i18n/locales/fr/gitlab.json
| async (event, projectId: string, mrIid: number, intervalMs: number = 5000): Promise<IPCResult<{ polling: boolean }>> => { | ||
| debugLog('statusPollStart handler called', { projectId, mrIid, intervalMs }); |
There was a problem hiding this comment.
Clamp polling interval input before creating timers.
intervalMs is used as-is. A zero/negative/super-low value can create a tight loop on the main process.
🔧 Proposed fix
ipcMain.handle(
IPC_CHANNELS.GITLAB_MR_STATUS_POLL_START,
async (event, projectId: string, mrIid: number, intervalMs: number = 5000): Promise<IPCResult<{ polling: boolean }>> => {
debugLog('statusPollStart handler called', { projectId, mrIid, intervalMs });
+ const safeIntervalMs = Number.isFinite(intervalMs)
+ ? Math.min(60_000, Math.max(1_000, intervalMs))
+ : 5_000;
...
- const interval = setInterval(async () => {
+ const interval = setInterval(async () => {
...
- }, intervalMs);
+ }, safeIntervalMs);Also applies to: 1183-1232
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts` around lines
1165 - 1166, Clamp the incoming polling interval in the statusPollStart IPC
handler before any timer is created: validate the parameter `intervalMs` at the
top of the async handler (the function declared as async (event, projectId:
string, mrIid: number, intervalMs: number = 5000) => ...) and coerce it into a
safe range (e.g. Math.max(MIN_INTERVAL_MS, Math.min(intervalMs || DEFAULT_MS,
MAX_INTERVAL_MS))). Use the clamped value for any setInterval/setTimeout
creation and for storage in any maps/records used by the polling logic (same
handler and related stop/cleanup code around the statusPollStart/statusPollStop
logic), and ensure negative/zero/NaN values are replaced with the default or
minimum to prevent a tight loop.
| if (callingWindow && !callingWindow.isDestroyed()) { | ||
| // Fetch current project to avoid stale config from closure | ||
| const currentProject = projectStore.getProject(projectId); | ||
| if (!currentProject) { | ||
| debugLog('Project not found during poll', { projectId }); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Stop orphaned pollers when the project/window is gone.
When callingWindow is destroyed or the project disappears, the timer keeps running forever and repeatedly no-ops.
🔧 Proposed fix
const interval = setInterval(async () => {
const pollKey = `${projectId}:${mrIid}`;
...
try {
- // Emit status update to renderer
- if (callingWindow && !callingWindow.isDestroyed()) {
+ if (!callingWindow || callingWindow.isDestroyed()) {
+ clearInterval(interval);
+ statusPollingIntervals.delete(pollKey);
+ pollingInProgress.delete(pollKey);
+ return;
+ }
+
+ // Emit status update to renderer
+ if (callingWindow && !callingWindow.isDestroyed()) {
// Fetch current project to avoid stale config from closure
const currentProject = projectStore.getProject(projectId);
if (!currentProject) {
debugLog('Project not found during poll', { projectId });
+ clearInterval(interval);
+ statusPollingIntervals.delete(pollKey);
return;
}Also applies to: 1234-1235
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts` around lines
1195 - 1201, The poll loop no-ops forever when the window or project goes away;
modify the poller callback (where callingWindow and
projectStore.getProject(projectId) are checked) to stop the poller when either
callingWindow is destroyed or currentProject is missing by calling
clearInterval/clearTimeout on the poll timer and removing any poller
registration for this project/merge request; locate the poller using the poll
setup identifiers in mr-review-handlers.ts (the function containing
callingWindow, projectStore.getProject(projectId), and the timer reference used
to schedule the poll) and ensure the same cleanup is applied at the other
occurrence around lines 1234-1235.
| callingWindow.webContents.send(IPC_CHANNELS.GITLAB_MR_STATUS_UPDATE, { | ||
| projectId, | ||
| mrIid, | ||
| state: mrData.state, | ||
| mergeStatus: mrData.merge_status, | ||
| updatedAt: mrData.updated_at | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Locate all definitions/usages of the status update channel
rg -n "GITLAB_MR_STATUS_UPDATE|gitlab:mr:statusUpdate" apps/desktop/src -C2
# 2) Verify preload exposes an on/off API for this event
rg -n "onGitLab.*Status|statusUpdate|remove.*Status" apps/desktop/src/preload apps/desktop/src/shared/types/ipc.ts -C2
# 3) Verify renderer subscribes/unsubscribes to the event
rg -n "GITLAB_MR_STATUS_UPDATE|statusUpdate|startGitLabMRStatusPoll|stopGitLabMRStatusPoll" apps/desktop/src/renderer -C2Repository: AndyMik90/Aperant
Length of output: 980
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check preload structure
echo "=== Preload files ==="
find apps/desktop/src/preload -type f -name "*.ts" | head -20
# Look for any event listeners exposed in preload
echo -e "\n=== Preload electron API exposure ==="
rg "contextBridge|exposeInMainWorld" apps/desktop/src/preload -A5 | head -50
# Look for any GITLAB in preload/renderer types
echo -e "\n=== IPC types related to GitLab ==="
rg "GITLAB|GitLab" apps/desktop/src/shared/types/ipc.ts -C1 | head -60Repository: AndyMik90/Aperant
Length of output: 5365
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check gitlab-api.ts in preload
echo "=== Preload gitlab-api.ts ==="
cat -n apps/desktop/src/preload/api/modules/gitlab-api.ts
# Also check the full IPC types for status update listener
echo -e "\n=== Full IPC types search for statusUpdate ==="
rg "statusUpdate|MRStatusUpdate" apps/desktop/src/shared/types/ipc.ts -B2 -A2Repository: AndyMik90/Aperant
Length of output: 28540
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Check IPC types file for event listener definitions
echo "=== Check IPC types for event listeners ==="
rg "on[A-Z].*:" apps/desktop/src/shared/types/ipc.ts | head -30
# Check if renderer tries to subscribe to statusUpdate
echo -e "\n=== Search renderer for statusUpdate subscription ==="
rg -i "statusupdate|mr.*status.*poll|subscribe.*status" apps/desktop/src/renderer -B2 -A2 2>/dev/null || echo "No matches found"
# Check if the handler that sends this is part of a poll mechanism
echo -e "\n=== Check mr-review-handlers.ts for context around line 1219 ==="
sed -n '1180,1230p' apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.tsRepository: AndyMik90/Aperant
Length of output: 6973
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Direct check for any MR status update listener in preload
echo "=== Direct search for MR status listener in preload ==="
rg "onGitLabMRStatusUpdate|MRStatusUpdate" apps/desktop/src/preload -r '' || echo "NOT FOUND"
# Check GitHub pattern for comparison
echo -e "\n=== GitHub PRStatusUpdate listener in preload ==="
rg "onPRStatusUpdate" apps/desktop/src/preload/api/modules/github-api.ts -B2 -A5
# Check if there's any reference to handling GITLAB_MR_STATUS_UPDATE in renderer
echo -e "\n=== Any renderer subscription attempt for GITLAB_MR_STATUS_UPDATE ==="
rg "GITLAB_MR_STATUS_UPDATE" apps/desktop/src/renderer -B2 -A2 || echo "NOT FOUND in renderer"
# Check what's in the constants to see if listener was planned
echo -e "\n=== Context around GITLAB_MR_STATUS_UPDATE in constants ==="
rg "GITLAB_MR.*UPDATE" apps/desktop/src/shared/constants/ipc.ts -B3 -A3Repository: AndyMik90/Aperant
Length of output: 1047
Add missing onGitLabMRStatusUpdate listener to preload.
The handler sends GITLAB_MR_STATUS_UPDATE events (line 1219), but the preload API does not expose a listener for them, making the wiring incomplete. The renderer has no way to subscribe to these status updates.
Follow the GitHub pattern already implemented (onPRStatusUpdate): add onGitLabMRStatusUpdate to apps/desktop/src/preload/api/modules/gitlab-api.ts and its implementation using createIpcListener(IPC_CHANNELS.GITLAB_MR_STATUS_UPDATE, callback).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/ipc-handlers/gitlab/mr-review-handlers.ts` around lines
1219 - 1225, Add a new preload listener method named onGitLabMRStatusUpdate that
mirrors the existing onPRStatusUpdate pattern: in the gitlab-api module expose
onGitLabMRStatusUpdate and implement it by calling
createIpcListener(IPC_CHANNELS.GITLAB_MR_STATUS_UPDATE, callback) so the
renderer can subscribe to MR status events emitted by the MR review handler;
ensure the exported API surface includes onGitLabMRStatusUpdate and that it
forwards the callback to createIpcListener exactly like onPRStatusUpdate does.
| function getPhaseLabel(phase: GitLabMRLogPhase, t: (key: string) => string): string { | ||
| return t(`gitlab:mrReview.logs.${phase}Gathering`); | ||
| } |
There was a problem hiding this comment.
Phase label key builder generates missing keys.
Line 67 derives analysisGathering / synthesisGathering, but your locale keys are aiAnalysis and synthesis (not synthesisGathering).
🔧 Proposed fix
-function getPhaseLabel(phase: GitLabMRLogPhase, t: (key: string) => string): string {
- return t(`gitlab:mrReview.logs.${phase}Gathering`);
-}
+const PHASE_LABEL_KEYS: Record<GitLabMRLogPhase, string> = {
+ context: 'gitlab:mrFiltering.logs.contextGathering',
+ analysis: 'gitlab:mrFiltering.logs.aiAnalysis',
+ synthesis: 'gitlab:mrFiltering.logs.synthesis',
+};
+
+function getPhaseLabel(phase: GitLabMRLogPhase, t: (key: string) => string): string {
+ return t(PHASE_LABEL_KEYS[phase]);
+}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/MRLogs.tsx`
around lines 66 - 68, getPhaseLabel builds locale keys by appending "Gathering"
to the phase (e.g., "analysisGathering"/"synthesisGathering") which don't exist;
change getPhaseLabel to map GitLabMRLogPhase values to the actual locale key
names (e.g., map the analysis phase to "aiAnalysis" and the synthesis phase to
"synthesis") then call t with the mapped key
(t(`gitlab:mrReview.logs.${mappedKey}`)). Update getPhaseLabel to use a switch
or a small lookup object keyed by GitLabMRLogPhase to avoid generating missing
keys.
| if (!checksStatus && !reviewsStatus && !mergeStatus) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
Avoid rendering an empty status container.
The null guard only checks presence, not renderability. With values like checksStatus="none" and reviewsStatus="none", this returns an empty <div>.
♻️ Proposed fix
- // Don't render if no status data is available
- if (!checksStatus && !reviewsStatus && !mergeStatus) {
+ const showChecks = Boolean(checksStatus && checksStatus !== 'none');
+ const showReviews = Boolean(reviewsStatus && reviewsStatus !== 'none');
+ const showMerge = Boolean(showMergeStatus && mergeStatus);
+
+ if (!showChecks && !showReviews && !showMerge) {
return null;
}
@@
- {checksStatus && checksStatus !== 'none' && (
+ {showChecks && (
@@
- {reviewsStatus && reviewsStatus !== 'none' && (
+ {showReviews && (Also applies to: 177-207
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/components/StatusIndicator.tsx`
around lines 167 - 169, The current guard in StatusIndicator (checking only
!checksStatus && !reviewsStatus && !mergeStatus) can still render an empty
container when statuses are present but equal to non-renderable values like
"none"; update the conditional used before returning null to explicitly test for
renderable statuses (e.g., checksStatus !== "none" || reviewsStatus !== "none"
|| mergeStatus !== "none") or introduce a small helper like
isRenderableStatus(status) and use isRenderableStatus(checksStatus) ||
isRenderableStatus(reviewsStatus) || isRenderableStatus(mergeStatus) so the
component truly returns null when nothing should be rendered and avoids
producing an empty <div>.
| export type GitLabMRStatusFilter = | ||
| | 'all' | ||
| | 'reviewing' | ||
| | 'not_reviewed' | ||
| | 'reviewed' | ||
| | 'posted' | ||
| | 'changes_requested' | ||
| | 'ready_to_merge' | ||
| | 'ready_for_followup'; |
There was a problem hiding this comment.
'all' status currently filters out all MRs.
'all' is part of GitLabMRStatusFilter (Line 20), but Lines 145-157 never treat it as a wildcard. If the UI sends ['all'], matchesStatus is always false and the list becomes empty.
♻️ Proposed fix
- if (filters.statuses.length > 0) {
+ const activeStatuses = filters.statuses.filter(status => status !== 'all');
+ if (activeStatuses.length > 0) {
const reviewInfo = getReviewStateForMR(mr.iid);
const computedStatus = getMRComputedStatus(reviewInfo);
// Check if MR matches any of the selected statuses
- const matchesStatus = filters.statuses.some(status => {
+ const matchesStatus = activeStatuses.some(status => {
// Special handling: 'posted' should match any posted state
if (status === 'posted') {
const hasPosted = reviewInfo?.result?.hasPostedFindings;
return hasPosted;
}
return computedStatus === status;
});Also applies to: 145-157
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts`
around lines 20 - 28, The 'all' variant of GitLabMRStatusFilter is currently
being treated like any other status which causes matchesStatus to return false
when the UI sends ['all']; update the matching logic (the function/variable
named matchesStatus used when applying GitLabMRStatusFilter) so that if the
incoming statusFilters array includes 'all' it immediately returns true (treat
'all' as a wildcard), otherwise continue checking the other statuses; ensure
this change references the GitLabMRStatusFilter type and the matchesStatus
implementation so the list is not emptied when 'all' is selected.
| (globalThis as any).window = { | ||
| electronAPI: mockElectronAPI | ||
| }; | ||
|
|
||
| describe('sync-status-store', () => { | ||
| beforeEach(() => { | ||
| useSyncStatusStore.getState().clearSyncStatus(); | ||
| vi.clearAllMocks(); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Vitest environment =="
fd -HI 'vitest.config.*' -x sh -c 'echo "--- $1"; rg -n "environment\\s*:" "$1" || true' sh {}
echo
echo "== Direct window reassignment occurrences =="
rg -nP --type=ts '\(globalThis as any\)\.window\s*=' apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.tsRepository: AndyMik90/Aperant
Length of output: 228
🏁 Script executed:
cat -n apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.tsRepository: AndyMik90/Aperant
Length of output: 5426
🏁 Script executed:
#!/bin/bash
# Check for vi.stubGlobal usage patterns in the codebase
echo "=== vi.stubGlobal usage ==="
rg -n 'vi\.stubGlobal' apps/desktop/src --type=ts --type=tsx
echo
echo "=== Module-scope global reassignments in test files ==="
rg -nP 'globalThis\.\w+\s*=' apps/desktop/src --type=ts -g '**/*.test.ts' -g '**/*.spec.ts'
echo
echo "=== vitest.config content ==="
cat -n apps/desktop/vitest.config.tsRepository: AndyMik90/Aperant
Length of output: 1646
🏁 Script executed:
cat -n apps/desktop/src/__tests__/setup.ts 2>/dev/null || echo "File not found"Repository: AndyMik90/Aperant
Length of output: 5200
🏁 Script executed:
#!/bin/bash
# Check if there's a setup file and what it contains
fd -H 'setup.ts' apps/desktop/src/__tests__/ -x cat -n {}
# Also check what the checkGitLabConnection function looks like to understand dependencies
echo
echo "=== Checking sync-status-store.ts implementation ==="
cat -n apps/desktop/src/renderer/stores/gitlab/sync-status-store.ts | head -50Repository: AndyMik90/Aperant
Length of output: 6858
Use vi.stubGlobal() with cleanup to avoid polluting the global window object across tests.
Lines 14–16 assign globalThis.window at module scope without restoration, which violates test isolation. Even though vi.clearAllMocks() clears function calls, it doesn't remove the window object itself, potentially causing issues with parallel test execution or when tests depend on window being undefined in the Node environment.
🧪 Safer mocking pattern
-import { describe, it, expect, beforeEach, vi } from 'vitest';
+import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
@@
-(globalThis as any).window = {
- electronAPI: mockElectronAPI
-};
-
describe('sync-status-store', () => {
beforeEach(() => {
+ vi.stubGlobal('window', {
+ ...(globalThis.window ?? {}),
+ electronAPI: mockElectronAPI
+ } as Window & typeof globalThis);
useSyncStatusStore.getState().clearSyncStatus();
vi.clearAllMocks();
});
+
+ afterEach(() => {
+ vi.unstubAllGlobals();
+ });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/stores/gitlab/__tests__/sync-status-store.test.ts`
around lines 14 - 22, The test currently mutates globalThis.window at module
scope with mockElectronAPI which pollutes the global environment; replace that
pattern by stubbing the global using vi.stubGlobal('window', { electronAPI:
mockElectronAPI }) inside the test lifecycle and ensure it is undone after each
test using vi.unstubAllGlobals() (or vi.restoreAllMocks()/appropriate Vitest
cleanup) so the global window is not left set between tests; update the setup
that references mockElectronAPI and keep the existing
useSyncStatusStore.getState().clearSyncStatus() in beforeEach, adding an
afterEach that calls vi.unstubAllGlobals() to restore the global state.
| export async function checkGitLabConnection(projectId: string): Promise<GitLabSyncStatus | null> { | ||
| const store = useSyncStatusStore.getState(); | ||
|
|
||
| try { | ||
| const result = await window.electronAPI.checkGitLabConnection(projectId); | ||
| // Only set sync status if actually connected (connected === true) | ||
| if (result.success && result.data && result.data.connected === true) { | ||
| store.setSyncStatus(result.data); | ||
| return result.data; | ||
| } else if (result.success && result.data && result.data.connected === false) { | ||
| // Connection failed but request succeeded - treat as error | ||
| store.clearSyncStatus(); | ||
| store.setConnectionError(result.data.error || 'Failed to check GitLab connection'); | ||
| return null; | ||
| } else { | ||
| store.clearSyncStatus(); | ||
| store.setConnectionError(result.error || 'Failed to check GitLab connection'); | ||
| return null; | ||
| } | ||
| } catch (error) { | ||
| store.clearSyncStatus(); | ||
| store.setConnectionError(error instanceof Error ? error.message : 'Unknown error'); | ||
| return null; |
There was a problem hiding this comment.
Prevent stale async responses from overwriting newer project state.
Line 59 awaits IPC and Lines 61–76 always mutate the singleton store. If two checks overlap (e.g., rapid project switch), an older response can arrive last and overwrite the latest status/error.
💡 Suggested fix (ignore stale responses)
+let latestConnectionRequestId = 0;
+
export async function checkGitLabConnection(projectId: string): Promise<GitLabSyncStatus | null> {
const store = useSyncStatusStore.getState();
+ const requestId = ++latestConnectionRequestId;
try {
const result = await window.electronAPI.checkGitLabConnection(projectId);
+ if (requestId !== latestConnectionRequestId) return null;
+
// Only set sync status if actually connected (connected === true)
if (result.success && result.data && result.data.connected === true) {
store.setSyncStatus(result.data);
return result.data;
@@
} catch (error) {
+ if (requestId !== latestConnectionRequestId) return null;
store.clearSyncStatus();
store.setConnectionError(error instanceof Error ? error.message : 'Unknown error');
return null;
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/stores/gitlab/sync-status-store.ts` around lines 55
- 77, The checkGitLabConnection function can let an out-of-order IPC response
overwrite the singleton store; to fix, capture a request identifier before
awaiting (e.g., read currentProjectId or increment a requestCounter on
useSyncStatusStore.getState()) and after the await compare that identifier to
the latest in the store—only call store.setSyncStatus, store.clearSyncStatus, or
store.setConnectionError if the identifiers match; implement this check around
all post-await branches in checkGitLabConnection to ignore stale responses.
Update tests to reflect that 403 (permission denied) and 404 (not found) errors are now non-recoverable, matching the updated error parser behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| const hasNewCommits = reviewInfo.newCommitsCheck?.hasNewCommits; | ||
| // For GitLab, check if new commits exist after review | ||
| const hasCommitsAfterPosting = hasNewCommits && hasPosted; | ||
|
|
||
| // Check for ready for follow-up first (highest priority after posting) | ||
| // Must have new commits that happened AFTER findings were posted | ||
| if (hasCommitsAfterPosting) { | ||
| return 'ready_for_followup'; |
There was a problem hiding this comment.
Bug: The logic for the "ready_for_followup" status in useGitLabMRFiltering.ts incorrectly uses hasNewCommits && hasPosted, mislabeling MRs that had new commits before a review was posted.
Severity: MEDIUM
Suggested Fix
To fix this, add a hasCommitsAfterPosting field to the GitLabNewCommitsCheck type and implement the logic in the backend to calculate it by comparing commit timestamps against the review post time. This would align its behavior with the existing GitHub implementation.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location:
apps/desktop/src/renderer/components/gitlab-merge-requests/hooks/useGitLabMRFiltering.ts#L73-L80
Potential issue: The logic to determine if a GitLab Merge Request is
"ready_for_followup" is flawed. It calculates `hasCommitsAfterPosting` by checking
`hasNewCommits && hasPosted`. This is semantically incorrect because it doesn't
distinguish between new commits that existed before a review was posted and those added
after. Unlike the GitHub implementation which uses a dedicated `hasCommitsAfterPosting`
field, this logic will incorrectly flag an MR as needing follow-up if a review is posted
on an MR that already has new commits. This results in misleading status assignments for
users.
All 110 PR review items have been addressed: - 75 resolved with code fixes - 35 skipped as false positives or already fixed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Implements full GitLab feature parity to match the existing GitHub integration. Users can now work with GitLab issues and merge requests using the same AI-powered workflows available for GitHub.
Changes
New GitLab Components:
State Management:
issues-store.ts- GitLab issues state with filtering and selectionmr-review-store.ts- MR review progress trackingsync-status-store.ts- Connection status monitoringinvestigation-store.ts- AI investigation stateBackend (Main Process):
mr-review-handlers.ts- AI-powered MR review IPC handlersgitlab-api.ts- Preload API methods for GitLab operationsHooks:
useGitLabMRs- MR state management hookuseGitLabMRFiltering- MR filtering by state, status, contributoruseGitLabInvestigation- Investigation workflow hookShared Utilities:
filter-utils.ts- Platform-agnostic filter utilities (GitHub vs GitLab state normalization)pagination-utils.ts- Reusable pagination logicbase-types.ts- Shared integration type definitionsi18n:
Testing
Technical Notes
GitLabMRLogs = PRLogs)iidinstead ofnumber,openedvsopen,mergedstateRelated
🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com
Summary by CodeRabbit
New Features
Bug Fixes / Robustness
Tests
Documentation