Skip to content

Gitea task source (#5493) rebased onto main + two validated fixes - #6920

Closed
charilaouc wants to merge 16 commits into
stablyai:mainfrom
charilaouc:gitea-task-source-main
Closed

charilaouc wants to merge 16 commits into
stablyai:mainfrom
charilaouc:gitea-task-source-main

Conversation

@charilaouc

@charilaouc charilaouc commented Jun 30, 2026 •

Copy link
Copy Markdown

This is @beatkapo's Gitea task source from #5493 — credit for the entire integration is theirs — brought current and hardened. Their branch had fallen 650+ commits behind main and gone conflicting; this resolves that and adds two functional fixes I found validating it against a live self-hosted Gitea (v1.26.2).

On top of #5493

  • Conflict resolution onto main — 6 files (5 i18n key-unions, cross-checked against an independent 3-way JSON merge; ChecksPanel.tsx, where main's refactored selectChecksPanelReview had no Gitea branch and would have dead-coded the Gitea checks path — extended the helper instead).
  • Type fix — widened WorkspaceIntentWorkItem.provider to include 'gitea' (the PR widened the central TaskProvider union but missed this downstream one → typecheck:web TS2322).
  • HIGH — Gitea on a non-default port was unauthenticated. parseGiteaRepoRef stored the repo host from url.hostname (no port), but the stored-server match (giteaServerHost/getServerForHost) and the ORCA_GITEA_API_BASE_URL scope check (sameHost) compare URL.host (with the port). A Gitea on e.g. :3000 never matched its stored server, so its token was dropped — reads ran unauthenticated, writes blocked. The repo host now stores url.host, so store and compare agree; :443/reverse-proxy still collapses identically, the env-token path is untouched, and SSH remotes keep the bare hostname. Validated against a live Gitea on :3000 (authenticated 200 with token; 404 without).
  • MEDIUM — label toggle dropped labels missing from the capped repo-label list. The editor rebuilt the full label-id set by matching applied label names against repoLabels (limit:100 page:1, no org labels); Gitea's label PUT replaces the whole set, so any applied label absent from that list was silently dropped. It now toggles as a delta against the IDs the issue actually carries (via GiteaIssue.labelIds), falling back to name-resolution only while detail is still loading. Org labels and labels beyond page 1 survive.

Tests

  • src/main/gitea suite + new label-toggle module: green.
  • Port-match parsing + cross-module host-match assertions (repository-ref, server-store); label-delta unit tests + mapper coverage.
  • typecheck:node and typecheck:web: clean.

cc @beatkapo — thank you for the original implementation. Happy to fold these fixes into #5493 instead if you'd prefer to keep it as your PR.


Open in Stage

fgo and others added 6 commits June 19, 2026 09:35
Checks, conversation comments (view + add), merge, close/reopen, and title
editing for Gitea PRs in the right sidebar — reusing the Gitea PR IPCs. The
main process already produces a Gitea HostedReviewInfo; flow it into the
panel's activeReview (guarded so it never masks a GitHub/GitLab review) and
mirror the GitLab branch for fetch/poll/refresh. Resolve/edit/delete of
individual review threads stays off (Gitea exposes no thread id).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conflicts resolved (6 files):
- i18n locales en/es/ja/ko/zh.json: union of additive sibling keys
  (PR's gitea.* keys + main's new namespaces). Verified each result
  parses and equals an independent 3-way JSON merge.
- ChecksPanel.tsx (6 hunks): kept main's selectChecksPanelReview refactor,
  pre-refresh git-identity block, gated onResolveSelectedCommentsWithAI, and
  refreshOutcome telemetry; re-applied PR's Gitea review path on top.
- checks-panel-review.ts (non-conflict edit): extended selectChecksPanelReview
  with a gitea hosted-review branch so the PR's Gitea checks path is reachable
  after main's helper extraction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Post-merge typecheck:web (TS2322) failed at launch-work-item-direct.ts:113:
LaunchableWorkItem.provider is TaskProvider (which PR #5493 widened to include
'gitea'), but WorkspaceIntentWorkItem.provider omitted 'gitea'. This is a latent
PR defect — the same incompatibility exists at the PR head, since all three
contributing types are unchanged by the merge. The field is type-only metadata
(naming helpers key off type/linearIdentifier/jiraIdentifier, not provider), so
widening the union is zero-risk.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parseGiteaRepoRef stored repo.host from url.hostname (no port), but the stored-
server match (getServerForHost/giteaServerHost) and the env-base-url scope check
(sameHost) compare URL.host, which keeps a non-default port. A Gitea on e.g.
:3000 therefore never matched its stored server, so the token was dropped —
reads ran unauthenticated and writes were blocked.

Store the web host with its non-default port (url.host) so the store and compare
sides agree. Default ports (the :443 reverse-proxy case) still collapse on both
sides, the env-token path with no base URL is untouched, and SSH remotes keep
the bare hostname (their port is the SSH port, not the web port).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GiteaIssueMetaControls rebuilt the full label-id set by matching applied label
names against repoLabels, which is fetched limit:100 page:1 and excludes org
labels. Gitea's label PUT replaces the whole set, so any applied label missing
from that capped list was silently dropped when toggling a different label.

Plumb the issue's own label IDs through (GiteaIssue.labelIds) and toggle as a
delta against them, so unrelated labels survive regardless of the repo-label
list. Falls back to name resolution only while the issue detail is still loading.

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

ghost commented Jun 30, 2026 •

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Jun 30, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Failed to post review comments.

We encountered an issue with GitHub. Use @coderabbitai full review to retry the review.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 89324aca-cf50-4770-99d2-c1ed5044be8c

📥 Commits

Reviewing files that changed from the base of the PR and between 6b66bda and 16fd528.

📒 Files selected for processing (84)
  • FIXES-CR.md
  • src/main/gitea/client.test.ts
  • src/main/gitea/client.ts
  • src/main/gitea/connect.test.ts
  • src/main/gitea/connect.ts
  • src/main/gitea/issues.test.ts
  • src/main/gitea/issues.ts
  • src/main/gitea/mappers.test.ts
  • src/main/gitea/mappers.ts
  • src/main/gitea/pull-request-reviews.ts
  • src/main/gitea/pull-requests.test.ts
  • src/main/gitea/pull-requests.ts
  • src/main/gitea/repository-ref.test.ts
  • src/main/gitea/repository-ref.ts
  • src/main/gitea/request.ts
  • src/main/gitea/server-store.test.ts
  • src/main/gitea/server-store.ts
  • src/main/ipc/gitea-pr.ts
  • src/main/ipc/gitea-repo-access.ts
  • src/main/ipc/gitea.ts
  • src/main/ipc/register-core-handlers.test.ts
  • src/main/ipc/register-core-handlers.ts
  • src/main/persistence.test.ts
  • src/preload/api-types.ts
  • src/preload/index.ts
  • src/renderer/src/assets/main.css
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/TaskPage.tsx
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.ts
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/components/gitea-pr-request-scope.ts
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/renderer/src/components/gitea-pr-tabs.ts
  • src/renderer/src/components/gitea-workspace-chrome.ts
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/right-sidebar/ChecksPanel.tsx
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/renderer/src/components/right-sidebar/checks-panel-review.ts
  • src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/components/settings/settings-search-keywords.test.ts
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/components/settings/tasks-search.ts
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/renderer/src/components/task-source-context-summary.test.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/store/index.ts
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/renderer/src/store/slices/ui.ts
  • src/renderer/src/store/types.ts
  • src/shared/gitea-types.ts
  • src/shared/integration-credential-errors.ts
  • src/shared/task-providers.test.ts
  • src/shared/task-providers.ts
  • src/shared/task-source-context.ts
  • src/shared/types.ts
  • src/shared/workspace-name.ts
🧰 Additional context used
📓 Path-based instructions (4)
src/renderer/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/renderer/src/**/*.{ts,tsx}: All UI work — layout, color, typography, spacing, component selection, UX behavior — must follow docs/STYLEGUIDE.md and use tokens defined in src/renderer/src/assets/main.css and shadcn primitives in src/renderer/src/components/ui/
Use CmdOrCtrl for Electron menu accelerators and display ⌘ / ⇧ on Mac and Ctrl+ / Shift+ on other platforms in UI shortcut labels

Files:

  • src/renderer/src/store/index.ts
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/settings/tasks-search.ts
  • src/renderer/src/store/types.ts
  • src/renderer/src/components/settings/settings-search-keywords.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-review.ts
  • src/renderer/src/components/gitea-pr-tabs.ts
  • src/renderer/src/components/gitea-workspace-chrome.ts
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/store/slices/ui.ts
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/gitea-issue-label-toggle.ts
  • src/renderer/src/components/gitea-pr-request-scope.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/renderer/src/components/task-source-context-summary.test.ts
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/components/TaskPage.tsx
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/components/right-sidebar/ChecksPanel.tsx
src/renderer/src/**/*.{ts,tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

Never invent new color values, font sizes, or shadow tiers when a documented one in STYLEGUIDE.md already covers the role

Files:

  • src/renderer/src/store/index.ts
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/settings/tasks-search.ts
  • src/renderer/src/store/types.ts
  • src/renderer/src/components/settings/settings-search-keywords.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-review.ts
  • src/renderer/src/components/gitea-pr-tabs.ts
  • src/renderer/src/components/gitea-workspace-chrome.ts
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/store/slices/ui.ts
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/gitea-issue-label-toggle.ts
  • src/renderer/src/components/gitea-pr-request-scope.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/assets/main.css
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/renderer/src/components/task-source-context-summary.test.ts
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/components/TaskPage.tsx
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/components/right-sidebar/ChecksPanel.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: When writing or modifying code driven by a design doc or non-obvious constraint, add a comment explaining why the code behaves the way it does. Keep comments short — one or two lines, capturing only the non-obvious reason
Never add a max-lines disable comment (eslint-disable max-lines, oxlint-disable max-lines, or line-specific variants). Split the file, extract focused modules, move fixtures/builders into named files, or otherwise reduce the counted lines instead
Keep all platform-dependent behavior behind runtime checks: use platform checks (navigator.userAgent.includes('Mac')) to pick metaKey on Mac and ctrlKey on Linux/Windows for keyboard shortcuts
Use path.join or Electron/Node path utilities for file paths — never assume / or \ for cross-platform compatibility

Files:

  • src/renderer/src/store/index.ts
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/settings/tasks-search.ts
  • src/renderer/src/store/types.ts
  • src/renderer/src/components/settings/settings-search-keywords.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-review.ts
  • src/renderer/src/components/gitea-pr-tabs.ts
  • src/renderer/src/components/gitea-workspace-chrome.ts
  • src/shared/workspace-name.ts
  • src/shared/integration-credential-errors.ts
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/shared/task-providers.test.ts
  • src/main/gitea/repository-ref.test.ts
  • src/main/gitea/server-store.test.ts
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/store/slices/ui.ts
  • src/shared/types.ts
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/main/ipc/register-core-handlers.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/gitea-issue-label-toggle.ts
  • src/renderer/src/components/gitea-pr-request-scope.ts
  • src/main/gitea/issues.test.ts
  • src/main/ipc/gitea-repo-access.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/main/persistence.test.ts
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/main/ipc/gitea.ts
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/main/gitea/client.test.ts
  • src/shared/task-source-context.ts
  • src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
  • src/main/ipc/register-core-handlers.test.ts
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/main/gitea/pull-request-reviews.ts
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/shared/task-providers.ts
  • src/main/gitea/pull-requests.test.ts
  • src/renderer/src/components/task-source-context-summary.test.ts
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/main/gitea/client.ts
  • src/main/gitea/mappers.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/main/gitea/repository-ref.ts
  • src/shared/gitea-types.ts
  • src/main/gitea/connect.test.ts
  • src/preload/api-types.ts
  • src/renderer/src/components/TaskPage.tsx
  • src/main/ipc/gitea-pr.ts
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/main/gitea/pull-requests.ts
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/main/gitea/mappers.ts
  • src/main/gitea/connect.ts
  • src/main/gitea/request.ts
  • src/preload/index.ts
  • src/main/gitea/issues.ts
  • src/renderer/src/components/right-sidebar/ChecksPanel.tsx
  • src/main/gitea/server-store.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer .ts over .d.ts for type declarations

Files:

  • src/renderer/src/store/index.ts
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/settings/tasks-search.ts
  • src/renderer/src/store/types.ts
  • src/renderer/src/components/settings/settings-search-keywords.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-review.ts
  • src/renderer/src/components/gitea-pr-tabs.ts
  • src/renderer/src/components/gitea-workspace-chrome.ts
  • src/shared/workspace-name.ts
  • src/shared/integration-credential-errors.ts
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/shared/task-providers.test.ts
  • src/main/gitea/repository-ref.test.ts
  • src/main/gitea/server-store.test.ts
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/store/slices/ui.ts
  • src/shared/types.ts
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/main/ipc/register-core-handlers.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/gitea-issue-label-toggle.ts
  • src/renderer/src/components/gitea-pr-request-scope.ts
  • src/main/gitea/issues.test.ts
  • src/main/ipc/gitea-repo-access.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/main/persistence.test.ts
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/main/ipc/gitea.ts
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/main/gitea/client.test.ts
  • src/shared/task-source-context.ts
  • src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
  • src/main/ipc/register-core-handlers.test.ts
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/main/gitea/pull-request-reviews.ts
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/shared/task-providers.ts
  • src/main/gitea/pull-requests.test.ts
  • src/renderer/src/components/task-source-context-summary.test.ts
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/main/gitea/client.ts
  • src/main/gitea/mappers.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/main/gitea/repository-ref.ts
  • src/shared/gitea-types.ts
  • src/main/gitea/connect.test.ts
  • src/preload/api-types.ts
  • src/renderer/src/components/TaskPage.tsx
  • src/main/ipc/gitea-pr.ts
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/main/gitea/pull-requests.ts
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/main/gitea/mappers.ts
  • src/main/gitea/connect.ts
  • src/main/gitea/request.ts
  • src/preload/index.ts
  • src/main/gitea/issues.ts
  • src/renderer/src/components/right-sidebar/ChecksPanel.tsx
  • src/main/gitea/server-store.ts
🧠 Learnings (5)
📚 Learning: 2026-06-16T15:35:16.815Z
Learnt from: gsxdsm
Repo: stablyai/orca PR: 5501
File: src/renderer/src/components/right-sidebar/checks-panel-content.tsx:1504-1504
Timestamp: 2026-06-16T15:35:16.815Z
Learning: In the stablyai/orca renderer codebase, Tailwind classes using the custom variant `can-hover` (e.g., `can-hover:opacity-0`) are intentionally treated as self-documenting, with the hover/pointer-only rationale defined once at `src/renderer/src/assets/main.css` via `custom-variant can-hover (media (hover: hover))`. When reviewing `.ts/.tsx` files, do not flag usage sites for missing inline “why” comments or duplicated rationale—assume the variant name and its central definition provide sufficient context.

Applied to files:

  • src/renderer/src/store/index.ts
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/settings/tasks-search.ts
  • src/renderer/src/store/types.ts
  • src/renderer/src/components/settings/settings-search-keywords.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-review.ts
  • src/renderer/src/components/gitea-pr-tabs.ts
  • src/renderer/src/components/gitea-workspace-chrome.ts
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/store/slices/ui.ts
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/gitea-issue-label-toggle.ts
  • src/renderer/src/components/gitea-pr-request-scope.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/renderer/src/components/task-source-context-summary.test.ts
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/components/TaskPage.tsx
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/components/right-sidebar/ChecksPanel.tsx
📚 Learning: 2026-06-24T11:33:31.894Z
Learnt from: gatsby74
Repo: stablyai/orca PR: 6256
File: src/renderer/src/components/terminal-pane/terminal-agent-session-fork.test.ts:83-84
Timestamp: 2026-06-24T11:33:31.894Z
Learning: When reviewing *.test.ts files, treat hardcoded OS-specific path literals used as mock fixture inputs (e.g., Windows `C:\...`, UNC paths like `\\wsl.localhost\Ubuntu\...`, and POSIX `/repo/...`) as intentional if they exist to simulate real platform path formats for path/launch-platform detection logic. Do not replace these static fixture strings with `path.join` or other runtime path construction utilities—doing so can make assertions OS-dependent and break test determinism. Reserve `path.join` guidance for production/runtime path construction, not for static test fixture strings used to exercise detection/parsing logic.

Applied to files:

  • src/renderer/src/components/settings/settings-search-keywords.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/shared/task-providers.test.ts
  • src/main/gitea/repository-ref.test.ts
  • src/main/gitea/server-store.test.ts
  • src/main/gitea/issues.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/main/persistence.test.ts
  • src/main/gitea/client.test.ts
  • src/main/ipc/register-core-handlers.test.ts
  • src/main/gitea/pull-requests.test.ts
  • src/renderer/src/components/task-source-context-summary.test.ts
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/main/gitea/mappers.test.ts
  • src/main/gitea/connect.test.ts
📚 Learning: 2026-06-16T21:44:06.855Z
Learnt from: Devin345458
Repo: stablyai/orca PR: 5522
File: src/renderer/src/i18n/locales/es.json:594-597
Timestamp: 2026-06-16T21:44:06.855Z
Learning: In stablyai/orca locale JSON files under `src/renderer/src/i18n/locales/`, it’s an intentional convention for newly added i18n entries to be auto-keyed from `SHA1(file path + source text)` and to use English as an initial fallback value in all locales. Before flagging mixed-language regressions in non-English locale files (e.g., `es.json`, `ja.json`, `ko.json`, `zh.json`), verify catalog parity is maintained—i.e., the new keys/entries exist consistently across locale files after running `pnpm run sync:localization-catalog` (English source values propagated). If parity is maintained, do not treat the presence of English fallback strings in non-English locale JSON files as a regression; rely on the separate localization pass to replace non-English values later.

Applied to files:

  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
📚 Learning: 2026-06-16T21:44:07.826Z
Learnt from: Devin345458
Repo: stablyai/orca PR: 5522
File: src/renderer/src/i18n/locales/es.json:7955-7986
Timestamp: 2026-06-16T21:44:07.826Z
Learning: For the `stablyai/orca` repo’s localization workflow, feature PRs should add new user-facing strings by propagating the English source into all non-English locale JSON files as English fallbacks (until a dedicated translation pass runs). Strings are auto-keyed (SHA1 of file path + source text) and catalog parity is maintained via `pnpm run sync:localization-catalog`. During review, do not request inline translations in feature PRs as long as catalog parity across locales is preserved (i.e., the sync output/keys match across locale files).

Applied to files:

  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
📚 Learning: 2026-06-16T21:44:10.732Z
Learnt from: Devin345458
Repo: stablyai/orca PR: 5522
File: src/renderer/src/i18n/locales/ja.json:596-597
Timestamp: 2026-06-16T21:44:10.732Z
Learning: When reviewing PR changes in the localization catalog (e.g., under src/renderer/src/i18n/locales/*.json), use the repo’s localization convention: new strings should be auto-keyed using SHA1(file path + source text) and English text is propagated into all locale files via `pnpm run sync:localization-catalog`. In feature PRs, don’t require inline human translations for es/ja/ko/zh; instead, verify catalog parity by ensuring the locale JSONs contain the expected set of keys (including newly added ones) and that diffs reflect synced English/source text rather than demanding immediate translated values.

Applied to files:

  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
🪛 ast-grep (0.44.0)
src/renderer/src/store/slices/gitea.ts

[error] 74-76: Recursive/iterative merge copies attacker-controllable keys from a source object into a target via a computed property assignment without rejecting dangerous keys, allowing prototype pollution. Skip or block "proto", "constructor", and "prototype" keys (e.g. if (key === "__proto__" || key === "constructor" || key === "prototype") continue;), use a null-prototype object (Object.create(null)), or use a safe merge utility instead.
Context: for (const key of sorted.slice(sorted.length - MAX_CACHE_ENTRIES)) {
pruned[key] = cache[key]
}
Note: [CWE-1321] Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution').

(prototype-pollution-recursive-merge-typescript)

🪛 LanguageTool
FIXES-CR.md

[uncategorized] ~64-~64: The official name of this software platform is spelled with a capital “H”.
Context: ...s on resolvedSource === 'gitea' (only github/linear prefetch), so the value can't re...

(GITHUB)

🪛 markdownlint-cli2 (0.22.1)
FIXES-CR.md

[warning] 27-27: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

📝 Walkthrough

Walkthrough

This pull request adds Gitea as a first-class task, issue, and pull-request provider. It introduces shared Gitea types, persistent server/token handling, repo-scoped request helpers, issue and PR backends, IPC/preload wiring, renderer store and workspace support, hosted-review checks integration, settings surfaces, and localization updates across the app.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers summary and testing, but it omits the required Screenshots, AI Review Report, Security Audit, and Notes sections. Add the missing template sections with screenshots or no visual change, testing results, AI review and security summaries, and any platform-specific notes.
Docstring Coverage ⚠️ Warning Docstring coverage is 21.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is specific and accurately summarizes the rebase plus Gitea fixes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (1)
src/renderer/src/store/slices/ui.ts (1)

1228-1233: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Don't treat Gitea as globally available from connection state alone.

giteaConfigured: state.giteaStatus?.connected === true makes openTaskPage resolve the Gitea source even when the current repo selection contains repos that are not registered against a Gitea host. TaskPage then passes selectedRepos straight into GiteaTaskList, and that list does a Promise.all(...) over fetchGiteaWorkItems(...), so one unsupported repo rejects the entire load. Filter to Gitea-backed repos before resolving the source, or make the Gitea load path skip unsupported repos instead of failing the whole view.

🟡 Minor comments (11)
src/main/ipc/gitea.ts-57-78 (1)

57-78: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Reject empty or array updates payloads before reporting success.

normalizeIssueUpdate currently accepts arrays and {}; downstream can then skip every write and still return { ok: true }, making a failed renderer mutation look successful.

Proposed fix
 function normalizeIssueUpdate(value: unknown): GiteaIssueUpdate | null {
-  if (!value || typeof value !== 'object') {
+  if (!value || typeof value !== 'object' || Array.isArray(value)) {
     return null
   }
   const input = value as GiteaIssueUpdate
+  const hasKnownUpdate =
+    input.title !== undefined ||
+    input.body !== undefined ||
+    input.state !== undefined ||
+    input.assignees !== undefined ||
+    input.labelIds !== undefined
+  if (!hasKnownUpdate) {
+    return null
+  }

Also applies to: 192-196

src/main/ipc/gitea-pr.ts-58-66 (1)

58-66: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not fetch PR file contents with empty refs.

If baseSha or headSha is malformed, this passes '' into the raw-content requests. Return the empty content shape instead so the diff view does not render content from an unintended ref.

Proposed fix
       if (typeof args.path !== 'string' || !args.path) {
         return { original: '', modified: '', originalIsBinary: false, modifiedIsBinary: false }
       }
+      if (
+        typeof args.baseSha !== 'string' ||
+        !args.baseSha ||
+        typeof args.headSha !== 'string' ||
+        !args.headSha
+      ) {
+        return { original: '', modified: '', originalIsBinary: false, modifiedIsBinary: false }
+      }
       return getGiteaPullRequestFileContents(
         repo.path,
         {
           path: args.path,
           oldPath: typeof args.oldPath === 'string' ? args.oldPath : undefined,
           status: args.status,
-          baseSha: typeof args.baseSha === 'string' ? args.baseSha : '',
-          headSha: typeof args.headSha === 'string' ? args.headSha : ''
+          baseSha: args.baseSha,
+          headSha: args.headSha
         },
src/renderer/src/i18n/locales/zh.json-6283-6285 (1)

6283-6285: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add matching Settings-search keywords for the new provider.

This adds the Gitea toggle to settings.TasksPane, but settings.tasks.search still only indexes Jira/Linear/GitLab/GitHub. Searching Settings for “Gitea” will not surface this control.

Suggested follow-up
         "tasks": {
           "search": {
             "58cda6f9c0": "隐藏",
             "44083ae418": "展示",
             "604d8e4089": "阿特拉斯语",
             "5430396e11": "Jira",
             "412ec3c702": "Linear",
             "11f001cdd4": "GitLab",
             "c10ac2125e": "GitHub",
+            "gitea": "Gitea",
             "3d81c26d78": "来源",
             "cf0e3e0c2f": "提供者",
             "2ec54bee51": "任务",
             "765f0c544d": "选择要在“任务”页面和侧边栏快捷键中显示的任务提供商。",
             "5b8e4aace5": "任务提供者"
           }
         },
src/renderer/src/components/task-source-context-summary.ts-53-53 (1)

53-53: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Pass Gitea repo contexts through the caller.

TaskPage still builds taskSourceRepoContexts only for 'github' and 'gitlab', so this new branch receives [] for Gitea and the header summary falls back to the zero-repo state. Please update that caller in the same change so the Gitea summary gets the selected repo context too.

src/renderer/src/i18n/locales/ja.json-12172-12320 (1)

12172-12320: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Merge this into the existing gitea locale object.

Line 12172 starts a second top-level "gitea" key. In JSON, the later object overwrites the earlier one, so the gitea.integration.card entries added at Line 8473 stop existing in the Japanese catalog.

Suggested structure
-      "gitea": {
-        "connect": {
-          "dialog": {
-            ...
-          }
-        },
-        "issue": {
-          ...
-        },
-        "pr": {
-          ...
-        },
-        "new": {
-          ...
-        }
-      },
+      "gitea": {
+        "integration": {
+          "card": {
+            ...
+          }
+        },
+        "connect": {
+          "dialog": {
+            ...
+          }
+        },
+        "issue": {
+          ...
+        },
+        "pr": {
+          ...
+        },
+        "new": {
+          ...
+        }
+      },
src/renderer/src/components/gitea-issue-comments.tsx-7-10 (1)

7-10: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Localize the invalid-date fallback.

The "recently" fallback bypasses translate(), so this path always renders in English. Route the fallback string through i18n like the rest of the component.

src/renderer/src/components/right-sidebar/ChecksPanel.tsx-674-676 (1)

674-676: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Also gate the GitHub PR refresh path on Gitea review context.

isGiteaReviewContext is derived here, but the existing enqueueGitHubPRRefresh(...) effect still only excludes GitLab. In a Gitea review, opening the panel will continue to schedule the GitHub PR refresh flow, which is unnecessary work and can surface GitHub-side noise on Gitea-only repos.

src/renderer/src/components/GiteaPullRequestWorkspace.tsx-101-129 (1)

101-129: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the workspace loading state alive until the checks request finishes.

prChecks is launched after the first Promise.all(...) resolves, but Line 127 clears loading immediately. That makes the spinner disappear and the Checks tab briefly render 0 / empty-state before the checks call comes back.

Suggested fix
-      .then(([prDetail, prFiles, prComments, prReviewComments]) => {
+      .then(async ([prDetail, prFiles, prComments, prReviewComments]) => {
         if (requestId !== requestRef.current) {
           return
         }
         setDetail(prDetail)
         setFiles(prFiles)
         setComments(prComments)
         setReviewComments(prReviewComments)
-        if (prDetail?.headSha) {
-          void (
-            window.api.gitea.prChecks(scoped(scope, { headSha: prDetail.headSha })) as Promise<
-              GiteaPRCheck[]
-            >
-          )
-            .then((result) => {
-              if (requestId === requestRef.current) {
-                setChecks(result)
-              }
-            })
-            .catch(() => {
-              // Why: keep checks state consistent and surface the failure.
-              if (requestId === requestRef.current) {
-                setChecks([])
-                toast.error(
-                  translate(
-                    'auto.components.GiteaPullRequestWorkspace.c2d3e4f5a6',
-                    'Failed to load PR checks.'
-                  )
-                )
-              }
-            })
+        if (prDetail?.headSha) {
+          try {
+            const result = (await window.api.gitea.prChecks(
+              scoped(scope, { headSha: prDetail.headSha })
+            )) as GiteaPRCheck[]
+            if (requestId === requestRef.current) {
+              setChecks(result)
+            }
+          } catch {
+            if (requestId === requestRef.current) {
+              setChecks([])
+              toast.error(
+                translate(
+                  'auto.components.GiteaPullRequestWorkspace.c2d3e4f5a6',
+                  'Failed to load PR checks.'
+                )
+              )
+            }
+          }
         }
       })
src/renderer/src/components/gitea-pr-merge-button.tsx-33-40 (1)

33-40: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Translate the merge-method menu labels.

The main action button is localized, but the popover still renders raw enum values (merge, squash, rebase). That leaves the new merge flow partially untranslated in non-English locales.

Suggested fix
+const MERGE_METHOD_LABELS: Record<GiteaMergeMethod, string> = {
+  merge: translate('auto.components.gitea.pr.merge.button.method.merge', 'Merge commit'),
+  squash: translate('auto.components.gitea.pr.merge.button.method.squash', 'Squash and merge'),
+  rebase: translate('auto.components.gitea.pr.merge.button.method.rebase', 'Rebase and merge')
+}
+
         <PopoverContent align="end" className="w-40 p-1">
           {MERGE_METHODS.map((method) => (
             <button
               key={method}
               type="button"
               onClick={() => onMerge(method)}
               className="flex w-full items-center rounded-sm px-2 py-1.5 text-left text-[12px] capitalize hover:bg-accent"
             >
-              {method}
+              {MERGE_METHOD_LABELS[method]}
             </button>
           ))}
         </PopoverContent>
src/renderer/src/components/gitea-pr-file-diff.tsx-27-34 (1)

27-34: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Localize the file-status badges.

Added / Modified / Deleted / ... is hardcoded English, so this new diff header will stay untranslated even when the rest of the Gitea workspace is localized.

Suggested fix
-const STATUS_LABELS: Record<GiteaPRFile['status'], string> = {
-  added: 'Added',
-  modified: 'Modified',
-  deleted: 'Deleted',
-  renamed: 'Renamed',
-  copied: 'Copied',
-  changed: 'Changed'
-}
+const STATUS_LABELS: Record<GiteaPRFile['status'], string> = {
+  added: translate('auto.components.gitea.pr.file.diff.status.added', 'Added'),
+  modified: translate('auto.components.gitea.pr.file.diff.status.modified', 'Modified'),
+  deleted: translate('auto.components.gitea.pr.file.diff.status.deleted', 'Deleted'),
+  renamed: translate('auto.components.gitea.pr.file.diff.status.renamed', 'Renamed'),
+  copied: translate('auto.components.gitea.pr.file.diff.status.copied', 'Copied'),
+  changed: translate('auto.components.gitea.pr.file.diff.status.changed', 'Changed')
+}
src/renderer/src/components/settings/gitea-integration-card.tsx-21-25 (1)

21-25: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Track test state per server, not in a single testingServerId.

If the user starts a test for server A and then server B before A settles, A's finally clears testingServerId and re-enables B's row even though B is still running. Use a Set<string> or per-server boolean map so concurrent tests can't clobber each other's loading state.

Also applies to: 43-80

🧹 Nitpick comments (1)
src/renderer/src/components/settings/IntegrationsPane.tsx (1)

55-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a short why-comment for Gitea appearing in both sections.

Seeing Gitea under both “Review providers” and “Task providers” reads like accidental duplication unless you already know the cross-provider design. A one-line comment here would prevent future cleanup from removing one of the cards. As per coding guidelines, "When writing or modifying code driven by a design doc or non-obvious constraint, add a comment explaining why the code behaves the way it does."

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6616fa44-85d1-40e5-a725-a3462f242572

📥 Commits

Reviewing files that changed from the base of the PR and between 25a7a6f and 30877fe.

📒 Files selected for processing (77)
  • src/main/gitea/client.test.ts
  • src/main/gitea/client.ts
  • src/main/gitea/connect.test.ts
  • src/main/gitea/connect.ts
  • src/main/gitea/issues.test.ts
  • src/main/gitea/issues.ts
  • src/main/gitea/mappers.test.ts
  • src/main/gitea/mappers.ts
  • src/main/gitea/pull-requests.test.ts
  • src/main/gitea/pull-requests.ts
  • src/main/gitea/repository-ref.test.ts
  • src/main/gitea/repository-ref.ts
  • src/main/gitea/request.ts
  • src/main/gitea/server-store.test.ts
  • src/main/gitea/server-store.ts
  • src/main/ipc/gitea-pr.ts
  • src/main/ipc/gitea-repo-access.ts
  • src/main/ipc/gitea.ts
  • src/main/ipc/register-core-handlers.test.ts
  • src/main/ipc/register-core-handlers.ts
  • src/main/persistence.test.ts
  • src/preload/api-types.ts
  • src/preload/index.ts
  • src/renderer/src/assets/main.css
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/TaskPage.tsx
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.ts
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/components/gitea-pr-request-scope.ts
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/right-sidebar/ChecksPanel.tsx
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/renderer/src/components/right-sidebar/checks-panel-review.ts
  • src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/store/index.ts
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/renderer/src/store/slices/ui.ts
  • src/renderer/src/store/types.ts
  • src/shared/gitea-types.ts
  • src/shared/integration-credential-errors.ts
  • src/shared/task-providers.test.ts
  • src/shared/task-providers.ts
  • src/shared/task-source-context.ts
  • src/shared/types.ts
  • src/shared/workspace-name.ts

Comment thread src/main/gitea/client.ts Resolved
Comment thread src/main/gitea/connect.ts Resolved
Comment thread src/main/gitea/issues.ts Resolved
Comment thread src/main/gitea/issues.ts Resolved
Comment thread src/main/gitea/mappers.ts Resolved
Comment thread src/renderer/src/store/slices/gitea.ts Resolved
Comment thread src/renderer/src/store/slices/gitea.ts Resolved
charilaouc and others added 10 commits July 1, 2026 01:17
… 403

Address CodeRabbit findings on env/stored credential resolution and the
env-token auth probe:

- request.ts: scope ORCA_GITEA_TOKEN to the exact API base (host + subpath),
  not the host alone, so a token can't leak to a sibling instance under a
  different subpath on the same host. Pass repo.apiBaseUrl into the stored
  lookup. (CR Major: request.ts)
- server-store.ts: add giteaServerKey() (host+path, keeps the #5493 non-default
  port) and match getServerForHost() on it when an apiBaseUrl is supplied,
  falling back to host-only for back-compat. (CR Major: server-store.ts)
- issues.ts: pass repo.apiBaseUrl when resolving the server for issue labels so
  multi-instance hosts label the correct serverId/serverName.
- connect.ts: only treat the read:user-scope 403 on /user as a valid token;
  every other 403 now fails. Export fetchGiteaUser with a timeout option.
  (CR Major: connect.ts)
- client.ts: getGiteaAuthStatus() reuses fetchGiteaUser for the token probe so a
  read:user-scope 403 reports authenticated (account null) instead of the raw
  GET's null. (CR Major: client.ts)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address CodeRabbit findings in the Gitea issue/PR data layer and IPC validators:

- issues.ts: decouple the owner-wide search page size from the requested result
  cap (limit MAX_LIMIT, not max) so a small limit doesn't under-scan and miss a
  matching issue past the first global hits. (CR Major: issues.ts:53-60)
- issues.ts: when the field PATCH commits but the labels PUT fails, return a
  partial success { ok: true, warning } instead of a clean failure, so the UI
  refreshes the already-changed issue. Adds GiteaMutationResult.warning.
  (CR Major: issues.ts:266-280)
- mappers.ts: preserve the raw issue body (raw.body ?? undefined) instead of
  trimming it, so opening/saving can't drop meaningful whitespace. (CR Major)
- pull-requests.ts: paginate the PR file list instead of stopping at page 1, so
  large PRs don't silently drop changed files. (CR Major)
- ipc/gitea.ts: reject array/empty issue-update payloads (no recognized field)
  so a no-op update can't skip every write and still report success. (CR Minor)
- ipc/gitea-pr.ts: return the empty content shape when base/head SHAs are
  missing, so the diff view never renders against an unintended ref. (CR Minor)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address CodeRabbit findings on stale async results overwriting a newly-selected
Gitea item, and a couple of adjacent load/partial-success issues:

- GiteaIssueWorkspace: capture requestRef before each awaited mutation
  (refreshDetail, toggle state, add comment) and bail if the selection changed,
  so a late response can't overwrite the new sheet. (CR Major)
- GiteaPullRequestWorkspace: same requestRef guard on add-comment, merge, and
  add-review-comment; and keep `loading` up until the checks request finishes so
  the Checks tab doesn't flash an empty state. (CR Major + Minor)
- GiteaTaskList: load with Promise.allSettled and merge fulfilled repos, only
  showing the error banner when every repo failed, so one bad repo in a mixed
  selection can't blank the whole list. (CR Major)
- gitea-issue-meta-controls: surface the partial-success warning (fields saved
  but labels rejected) as a non-fatal toast instead of swallowing it.

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

Address CodeRabbit findings on the Gitea store slice cache keys:

- scopeKey() now keys unpinned entries by activeServerId (falling back to the
  explicit selection, then 'all'), so two single-server states stay distinct and
  reconnecting to a different host can't reuse the previous server's cached work
  items / issue detail until the TTL expires. (CR Major)
- updateGiteaIssue() now also drops the cached work-item lists for the scope, not
  just the detail entry, so closing/relabeling/retitling an issue refreshes the
  task list instead of leaving it stale for up to 60s. (CR Major)

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

Address CodeRabbit findings spanning the Tasks page and the hosted-review checks
panel:

- TaskPage: opening a Gitea issue now clears any selected PR (and vice versa), so
  the two modal drawers stay mutually exclusive. (CR Major)
- TaskPage: build taskSourceRepoContexts for Gitea too, so the header summary
  reflects the selected projects instead of the zero-repo fallback. (CR Minor)
- ChecksPanel: refreshHostedReviewAfterMutation gains a Gitea branch so a
  successful Gitea title/merge edit refreshes via the Gitea path instead of
  falling through to the GitHub PR force-refresh, which could error/stale the
  panel. (CR Major)
- ChecksPanel: also skip enqueueGitHubPRRefresh in a Gitea review context, not
  only GitLab, to avoid wasted work and GitHub-side noise. (CR Minor)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address CodeRabbit i18n/UX findings in the Gitea renderer surface:

- gitea-issue-comments: route the invalid-date "recently" fallback through
  translate(). (CR Minor)
- gitea-pr-merge-button: localize the merge-method menu labels (merge/squash/
  rebase) instead of rendering raw enum values. (CR Minor)
- gitea-pr-file-diff: localize the file-status badges (Added/Modified/…). (CR Minor)
- settings/tasks-search: index "gitea" as a Tasks-pane provider search keyword so
  Settings search surfaces the Gitea toggle. (CR Minor)
- settings/gitea-integration-card: track test state per server in a Set so
  concurrent tests can't clear each other's spinner. (CR Minor)
- settings/IntegrationsPane: document why Gitea appears under both Review and
  Task providers. (CR Nitpick)
- i18n catalogs: register the new keys across all locales.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The selection-race guards and PR-file pagination pushed three files just over
their max-lines budgets. Per the repo guideline (never disable max-lines),
extract focused modules instead:

- pull-request-reviews.ts: the diff-anchored review-comment reads/writes, moved
  out of pull-requests.ts (now PR detail/files/checks/merge only).
- gitea-workspace-chrome.ts: shared isWindows + copyGiteaText helpers used by
  both Gitea work-item sheets.
- gitea-pr-tabs.ts: the PR workspace tab model/builder.

No behavior change. Also switches a test assertion to String#startsWith to
satisfy the lint rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Maps every CodeRabbit finding on PR #6920 (16 inline Major + 1 outside-diff +
11 Minor + 1 Nitpick) to its resolution, tests, and validation results.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Self-review caught a regression in the new credential scoping: an SSH clone URL
(git@host:owner/repo.git) can't carry the host's web ROOT_URL subpath, so a
subpath-hosted Gitea accessed over SSH derived a bare-host apiBaseUrl that no
longer key-matched the stored server's subpath base — dropping the token (reads
returned null, writes failed with "Connect a Gitea account to make changes").

Fix: flag such bases as host-inferred (GiteaRepoRef.apiBaseFromHost, set only for
SSH/SCP remotes with no subpath segment) and allow a host-only credential match
for that case alone:

- repository-ref.ts: set apiBaseFromHost when an SSH/SCP remote has no subpath.
- server-store.ts: getServerForHost(host, apiBaseUrl?, apiBaseFromHost?) keeps the
  exact host+path match for authoritative bases, but accepts the host's lone
  connected server for a host-inferred base; never guesses across instances.
- request.ts: sameApiBase falls back to a host match only for host-inferred bases;
  pass the flag into the stored lookup.
- issues.ts: pass the flag through for serverId/serverName labeling.

Keeps the #5493 host+port match and the multi-instance-same-host protection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
@charilaouc

Copy link
Copy Markdown
Author

The pushed commits address all comments from the previous review — each fix carries a short inline // Why: rationale at the relevant line. Requesting a fresh pass to confirm.

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026 •

Copy link
Copy Markdown
Contributor

@charilaouc Understood — I'll do a fresh full review of all the changes to verify the fixes and inline // Why: rationale are properly applied.

✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026 •

Copy link
Copy Markdown
Contributor

@charilaouc Sounds good — I'll go through the entire PR fresh, confirming each previously flagged issue is resolved and checking the inline // Why: rationale comments at each fix site.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
src/main/gitea/repository-ref.ts (1)

92-99: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

KNOWN_NON_GITEA_HOSTS exclusion now compares against a port-qualified host for HTTP(S) remotes.

normalizedHost in makeRepoRef is derived from the host param, which for HTTP(S) is now url.host (includes an explicit port if present). If a remote explicitly specifies a default port (e.g. https://github.com:443/owner/repo.git), normalizedHost (github.com:443) won't match KNOWN_NON_GITEA_HOSTS entries (presumably bare hostnames like github.com), letting a known non-Gitea host slip past the exclusion check.

🐛 Suggested fix — strip the port before the exclusion check
   const normalizedHost = host.toLowerCase()
+  const exclusionHost = normalizedHost.replace(/:\d+$/, '')
   if (
-    !normalizedHost ||
-    KNOWN_NON_GITEA_HOSTS.has(normalizedHost) ||
-    normalizedHost.endsWith('.visualstudio.com')
+    !normalizedHost ||
+    KNOWN_NON_GITEA_HOSTS.has(exclusionHost) ||
+    exclusionHost.endsWith('.visualstudio.com')
   ) {
     return null
   }

Also applies to: 146-153

♻️ Duplicate comments (1)
src/renderer/src/components/gitea-issue-meta-controls.tsx (1)

34-46: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Title draft still isn't reset when the selected issue changes.

titleDraft initializes from title only on mount (useState(title)). Without a remount key or a syncing effect on issueNumber/title, switching to a different Gitea issue keeps the previous issue's title in the input — saving from there renames the wrong issue. This matches a previously flagged concern that doesn't show as resolved in this update.

🐛 Suggested fix
   const [titleDraft, setTitleDraft] = useState(title)
   const [pending, setPending] = useState<string | null>(null)
+
+  useEffect(() => {
+    setTitleDraft(title)
+  }, [issueNumber, title])
🧹 Nitpick comments (4)
src/renderer/src/components/gitea-pr-request-scope.ts (1)

5-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a more descriptive function name.

scoped is generic given the exported surface; a name like buildGiteaPrRequestPayload would be clearer at call sites given the file already carries the "gitea-pr" context in its filename.

src/main/gitea/request.ts (1)

104-125: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate fetch-with-timeout boilerplate across three functions.

giteaGetJsonAtBase, giteaRepoGetText, and giteaRepoWrite each re-implement the same AbortController + timeout + clearTimeout scaffolding. Consider extracting a shared withGiteaTimeout(fn, timeoutMs) helper (or a common giteaFetch wrapper) to reduce duplication and the risk of the pattern diverging (e.g., a future edit forgetting clearTimeout in one of the three copies).

♻️ Sketch of a shared timeout helper
+async function withTimeout<T>(
+  timeoutMs: number,
+  run: (signal: AbortSignal) => Promise<T>
+): Promise<T> {
+  const controller = new AbortController()
+  const timeout = setTimeout(() => controller.abort(), timeoutMs)
+  try {
+    return await run(controller.signal)
+  } finally {
+    clearTimeout(timeout)
+  }
+}

Then each of giteaGetJsonAtBase, giteaRepoGetText, and giteaRepoWrite can call withTimeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS, (signal) => fetch(...)) instead of duplicating the controller/timeout setup.

Also applies to: 139-161, 180-214

src/renderer/src/components/TaskPage.tsx (2)

6594-6611: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

prefilledName for Gitea workspaces loses the issue/PR title.

handleUseGiteaItem builds prefilledName as a bare ${type}-${number} string, unlike GitHub/GitLab which derive a richer, title-based seed (getGitHubWorkItemWorkspaceSeed, getGitLabWorkItemWorkspaceSeed). Consider adding an equivalent getGiteaWorkItemWorkspaceSeed(item) helper for naming consistency across providers.

♻️ Suggested direction
-        prefilledName: `${item.type === 'pull' ? 'pr' : 'issue'}-${item.number}`,
+        prefilledName: getGiteaWorkItemWorkspaceSeed(item),

10082-10113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate TaskProjectSourceCombobox wiring across GitHub/GitLab/Gitea branches.

This block repeats the same onChange/onSelectAll logic (normalize selection → setRepoSelection → persist defaultRepoSelection) already present verbatim for GitHub and GitLab. Now that it's tripled, consider extracting a shared handleRepoSelectionChange/handleSelectAllRepos pair (memoized once) reused by all three branches.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 89324aca-cf50-4770-99d2-c1ed5044be8c

📥 Commits

Reviewing files that changed from the base of the PR and between 6b66bda and 16fd528.

📒 Files selected for processing (84)
  • FIXES-CR.md
  • src/main/gitea/client.test.ts
  • src/main/gitea/client.ts
  • src/main/gitea/connect.test.ts
  • src/main/gitea/connect.ts
  • src/main/gitea/issues.test.ts
  • src/main/gitea/issues.ts
  • src/main/gitea/mappers.test.ts
  • src/main/gitea/mappers.ts
  • src/main/gitea/pull-request-reviews.ts
  • src/main/gitea/pull-requests.test.ts
  • src/main/gitea/pull-requests.ts
  • src/main/gitea/repository-ref.test.ts
  • src/main/gitea/repository-ref.ts
  • src/main/gitea/request.ts
  • src/main/gitea/server-store.test.ts
  • src/main/gitea/server-store.ts
  • src/main/ipc/gitea-pr.ts
  • src/main/ipc/gitea-repo-access.ts
  • src/main/ipc/gitea.ts
  • src/main/ipc/register-core-handlers.test.ts
  • src/main/ipc/register-core-handlers.ts
  • src/main/persistence.test.ts
  • src/preload/api-types.ts
  • src/preload/index.ts
  • src/renderer/src/assets/main.css
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/TaskPage.tsx
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.ts
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/components/gitea-pr-request-scope.ts
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/renderer/src/components/gitea-pr-tabs.ts
  • src/renderer/src/components/gitea-workspace-chrome.ts
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/right-sidebar/ChecksPanel.tsx
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/renderer/src/components/right-sidebar/checks-panel-review.ts
  • src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/components/settings/settings-search-keywords.test.ts
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/components/settings/tasks-search.ts
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/renderer/src/components/task-source-context-summary.test.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/store/index.ts
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/renderer/src/store/slices/ui.ts
  • src/renderer/src/store/types.ts
  • src/shared/gitea-types.ts
  • src/shared/integration-credential-errors.ts
  • src/shared/task-providers.test.ts
  • src/shared/task-providers.ts
  • src/shared/task-source-context.ts
  • src/shared/types.ts
  • src/shared/workspace-name.ts

Comment on lines +38 to +46
const groups = await Promise.all(
withComments.map((review) =>
giteaRepoGet<RawGiteaReviewComment[]>(repo, `${base}/${review.id}/comments`)
)
)
return groups
.flat()
.filter((comment): comment is RawGiteaReviewComment => Boolean(comment))
.filter((comment) => typeof comment.id === 'number' && typeof comment.position === 'number')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

One failing review-comments fetch drops all inline comments for the PR.

Promise.all rejects entirely if any single giteaRepoGet call for a review's comments fails, so a transient error on one review among many wipes out the whole result instead of degrading gracefully.

🛡️ Proposed fix using Promise.allSettled
-  const groups = await Promise.all(
-    withComments.map((review) =>
-      giteaRepoGet<RawGiteaReviewComment[]>(repo, `${base}/${review.id}/comments`)
-    )
-  )
-  return groups
-    .flat()
+  const groups = await Promise.allSettled(
+    withComments.map((review) =>
+      giteaRepoGet<RawGiteaReviewComment[]>(repo, `${base}/${review.id}/comments`)
+    )
+  )
+  return groups
+    .filter(
+      (result): result is PromiseFulfilledResult<RawGiteaReviewComment[]> =>
+        result.status === 'fulfilled'
+    )
+    .map((result) => result.value)
+    .flat()
📝 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.

Suggested change
const groups = await Promise.all(
withComments.map((review) =>
giteaRepoGet<RawGiteaReviewComment[]>(repo, `${base}/${review.id}/comments`)
)
)
return groups
.flat()
.filter((comment): comment is RawGiteaReviewComment => Boolean(comment))
.filter((comment) => typeof comment.id === 'number' && typeof comment.position === 'number')
const groups = await Promise.allSettled(
withComments.map((review) =>
giteaRepoGet<RawGiteaReviewComment[]>(repo, `${base}/${review.id}/comments`)
)
)
return groups
.filter(
(result): result is PromiseFulfilledResult<RawGiteaReviewComment[]> =>
result.status === 'fulfilled'
)
.map((result) => result.value)
.flat()

Comment on lines +78 to +93
if (result.ok) {
setBaseUrl('')
setToken('')
setConnectState('idle')
onOpenChange(false)
onConnected?.()
return
}
setConnectState('error')
setConnectError(result.error ?? null)
} catch (error) {
if (mountedRef.current) {
setConnectState('error')
setConnectError(error instanceof Error ? error.message : 'Connection failed')
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Silent failure when result.error is empty.

If giteaConnect resolves with result.ok === false and a falsy result.error, connectError is set to null (Line 87). The error message <p> only renders connectState === 'error' && connectError (Line 161), so the dialog silently shows nothing — the user sees the form stay in place with no explanation, unlike the catch branch which already has a 'Connection failed' fallback (Line 91).

🐛 Proposed fix: fall back to a generic error message
       setConnectState('error')
-      setConnectError(result.error ?? null)
+      setConnectError(
+        result.error ??
+          translate('auto.components.gitea.connect.dialog.generic_error', 'Connection failed')
+      )

Also applies to: 161-165

Comment on lines +79 to +128
void Promise.all([
window.api.gitea.prDetail(
scoped(scope, { number: item.number })
) as Promise<GiteaPullRequestDetail | null>,
window.api.gitea.prFiles(scoped(scope, { number: item.number })) as Promise<GiteaPRFile[]>,
window.api.gitea.issueComments(scoped(scope, { number: item.number })) as Promise<
GiteaComment[]
>,
window.api.gitea.prReviewComments(scoped(scope, { number: item.number })) as Promise<
GiteaPRReviewComment[]
>
])
.then(async ([prDetail, prFiles, prComments, prReviewComments]) => {
if (requestId !== requestRef.current) {
return
}
setDetail(prDetail)
setFiles(prFiles)
setComments(prComments)
setReviewComments(prReviewComments)
if (prDetail?.headSha) {
// Why: await checks before the chained finally clears `loading`, so the
// spinner stays up and the Checks tab doesn't flash an empty state first.
try {
const result = (await window.api.gitea.prChecks(
scoped(scope, { headSha: prDetail.headSha })
)) as GiteaPRCheck[]
if (requestId === requestRef.current) {
setChecks(result)
}
} catch {
// Why: keep checks state consistent and surface the failure.
if (requestId === requestRef.current) {
setChecks([])
toast.error(
translate(
'auto.components.GiteaPullRequestWorkspace.c2d3e4f5a6',
'Failed to load PR checks.'
)
)
}
}
}
})
.catch(() => {})
.finally(() => {
if (requestId === requestRef.current) {
setLoading(false)
}
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Silent failure on primary PR-detail load.

The nested prChecks failure surfaces a toast, but the top-level Promise.all failure at Line 123 is swallowed by .catch(() => {}), leaving the sheet stuck in an empty, no-error state if prDetail/prFiles/issueComments/prReviewComments reject.

🩹 Proposed fix
       })
-      .catch(() => {})
+      .catch(() => {
+        if (requestId === requestRef.current) {
+          toast.error(
+            translate(
+              'auto.components.GiteaPullRequestWorkspace.load-failed',
+              'Failed to load pull request.'
+            )
+          )
+        }
+      })
       .finally(() => {
📝 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.

Suggested change
void Promise.all([
window.api.gitea.prDetail(
scoped(scope, { number: item.number })
) as Promise<GiteaPullRequestDetail | null>,
window.api.gitea.prFiles(scoped(scope, { number: item.number })) as Promise<GiteaPRFile[]>,
window.api.gitea.issueComments(scoped(scope, { number: item.number })) as Promise<
GiteaComment[]
>,
window.api.gitea.prReviewComments(scoped(scope, { number: item.number })) as Promise<
GiteaPRReviewComment[]
>
])
.then(async ([prDetail, prFiles, prComments, prReviewComments]) => {
if (requestId !== requestRef.current) {
return
}
setDetail(prDetail)
setFiles(prFiles)
setComments(prComments)
setReviewComments(prReviewComments)
if (prDetail?.headSha) {
// Why: await checks before the chained finally clears `loading`, so the
// spinner stays up and the Checks tab doesn't flash an empty state first.
try {
const result = (await window.api.gitea.prChecks(
scoped(scope, { headSha: prDetail.headSha })
)) as GiteaPRCheck[]
if (requestId === requestRef.current) {
setChecks(result)
}
} catch {
// Why: keep checks state consistent and surface the failure.
if (requestId === requestRef.current) {
setChecks([])
toast.error(
translate(
'auto.components.GiteaPullRequestWorkspace.c2d3e4f5a6',
'Failed to load PR checks.'
)
)
}
}
}
})
.catch(() => {})
.finally(() => {
if (requestId === requestRef.current) {
setLoading(false)
}
})
})
.catch(() => {
if (requestId === requestRef.current) {
toast.error(
translate(
'auto.components.GiteaPullRequestWorkspace.load-failed',
'Failed to load pull request.'
)
)
}
})
.finally(() => {

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 19

♻️ Duplicate comments (1)
src/main/gitea/pull-request-reviews.ts (1)

38-45: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use Promise.allSettled() for per-review comment fetches.

This earlier failure mode is still present: one rejected ${base}/${review.id}/comments request rejects the whole function and drops every inline comment for the PR. Keep fulfilled groups and degrade gracefully instead.

🧹 Nitpick comments (6)
src/renderer/src/components/settings/settings-search-keywords.test.ts (1)

41-45: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid coupling this test to catalog order.

Using [0] makes this fail if another Tasks search entry is inserted ahead of “Task Providers,” even though the Gitea keyword is still indexed correctly. Look up the specific entry instead of relying on position.

Proposed test shape
-    const keywords = getTasksPaneSearchEntries()[0]?.keywords ?? []
-    expect(keywords).toContain('gitea')
-    // Sanity: the other providers are still indexed alongside it.
-    expect(keywords).toEqual(expect.arrayContaining(['github', 'gitlab']))
+    const entry = getTasksPaneSearchEntries().find(({ title }) => title === 'Task Providers')
+    expect(entry?.keywords).toEqual(expect.arrayContaining(['gitea', 'github', 'gitlab']))
src/renderer/src/components/gitea-pr-merge-button.tsx (1)

45-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the shared menu/item styling here instead of a custom text-[12px] button list.

This hand-rolled list sidesteps the renderer’s shared typography/component primitives, and the arbitrary text-[12px] size will drift from the rest of the UI. Please swap these actions to the existing tokenized/menu-item pattern used elsewhere.

As per coding guidelines, “All UI work — layout, color, typography, spacing, component selection, UX behavior — must follow docs/STYLEGUIDE.md and use tokens defined in src/renderer/src/assets/main.css and shadcn primitives in src/renderer/src/components/ui/” and “Never invent new color values, font sizes, or shadow tiers when a documented one in STYLEGUIDE.md already covers the role.”

Source: Coding guidelines

src/shared/task-providers.ts (1)

64-66: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make giteaConfigured required once the rollout is complete.

Keeping the new availability flag optional means any missed producer silently evaluates to “hide Gitea” instead of failing the build. Once the remaining call sites are updated, this should be required so the type system enforces the provider contract.

src/renderer/src/components/gitea-issue-comments.tsx (1)

26-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace the ad-hoc text sizes with shared typography tokens.

text-[13px] and text-[12px] introduce new font sizes in src/renderer, which drifts from the shared type scale. Please swap them for the matching tokenized classes from the style guide. As per coding guidelines, src/renderer/src/**/*.{ts,tsx,css} must not invent new font sizes, and src/renderer/src/**/*.{ts,tsx} UI work must use tokens defined in src/renderer/src/assets/main.css.

Source: Coding guidelines

src/renderer/src/components/settings/gitea-integration-card.tsx (1)

193-203: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the shared button primitive for the unlink action.

This is the only new control in the card that bypasses the shared shadcn button, so it won't inherit the same interaction styling as the rest of the settings UI.

Proposed refactor
-                <button
+                <Button
                   onClick={() => void handleDisconnect(server.id)}
                   aria-label={translate(
                     'auto.components.settings.gitea.integration.card.d0b31b4845',
                     'Disconnect {{value0}}',
                     { value0: server.displayName }
                   )}
-                  className="rounded-md p-1 text-muted-foreground/50 transition-colors hover:text-destructive"
+                  variant="ghost"
+                  size="icon"
+                  className="size-7 text-muted-foreground/50 hover:text-destructive"
                 >
                   <Unlink className="size-3.5" />
-                </button>
+                </Button>

As per coding guidelines, "All UI work — layout, color, typography, spacing, component selection, UX behavior — must follow docs/STYLEGUIDE.md and use tokens defined in src/renderer/src/assets/main.css and shadcn primitives in src/renderer/src/components/ui/."

Source: Coding guidelines

src/renderer/src/components/gitea-pr-line-comments.tsx (1)

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

Use the shared textarea primitive for this composer.

This raw <textarea> bypasses the renderer’s shared shadcn primitives and duplicates focus/border styling locally. As per coding guidelines, “All UI work … must follow docs/STYLEGUIDE.md and use tokens defined in src/renderer/src/assets/main.css and shadcn primitives in src/renderer/src/components/ui/.”

Proposed direction
+import { Textarea } from '`@/components/ui/textarea`'
...
-            <textarea
+            <Textarea
               value={body}
               onChange={(event) => setBody(event.target.value)}
               rows={2}
               disabled={submitting}
...
-              className="min-h-10 flex-1 resize-none rounded-md border border-input bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
+              className="min-h-10 flex-1 resize-none text-sm"
             />

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 89324aca-cf50-4770-99d2-c1ed5044be8c

📥 Commits

Reviewing files that changed from the base of the PR and between 6b66bda and 16fd528.

📒 Files selected for processing (84)
  • FIXES-CR.md
  • src/main/gitea/client.test.ts
  • src/main/gitea/client.ts
  • src/main/gitea/connect.test.ts
  • src/main/gitea/connect.ts
  • src/main/gitea/issues.test.ts
  • src/main/gitea/issues.ts
  • src/main/gitea/mappers.test.ts
  • src/main/gitea/mappers.ts
  • src/main/gitea/pull-request-reviews.ts
  • src/main/gitea/pull-requests.test.ts
  • src/main/gitea/pull-requests.ts
  • src/main/gitea/repository-ref.test.ts
  • src/main/gitea/repository-ref.ts
  • src/main/gitea/request.ts
  • src/main/gitea/server-store.test.ts
  • src/main/gitea/server-store.ts
  • src/main/ipc/gitea-pr.ts
  • src/main/ipc/gitea-repo-access.ts
  • src/main/ipc/gitea.ts
  • src/main/ipc/register-core-handlers.test.ts
  • src/main/ipc/register-core-handlers.ts
  • src/main/persistence.test.ts
  • src/preload/api-types.ts
  • src/preload/index.ts
  • src/renderer/src/assets/main.css
  • src/renderer/src/components/GiteaIssueWorkspace.tsx
  • src/renderer/src/components/GiteaPullRequestWorkspace.tsx
  • src/renderer/src/components/GiteaTaskList.tsx
  • src/renderer/src/components/TaskPage.tsx
  • src/renderer/src/components/automations/automation-source-display.ts
  • src/renderer/src/components/automations/automation-target-availability.ts
  • src/renderer/src/components/gitea-connect-dialog.tsx
  • src/renderer/src/components/gitea-issue-comments.tsx
  • src/renderer/src/components/gitea-issue-label-toggle.test.ts
  • src/renderer/src/components/gitea-issue-label-toggle.ts
  • src/renderer/src/components/gitea-issue-meta-controls.tsx
  • src/renderer/src/components/gitea-new-issue-dialog.tsx
  • src/renderer/src/components/gitea-pr-checks.tsx
  • src/renderer/src/components/gitea-pr-file-diff.tsx
  • src/renderer/src/components/gitea-pr-line-comments.tsx
  • src/renderer/src/components/gitea-pr-merge-button.tsx
  • src/renderer/src/components/gitea-pr-request-scope.ts
  • src/renderer/src/components/gitea-pr-tab-body.tsx
  • src/renderer/src/components/gitea-pr-tabs.ts
  • src/renderer/src/components/gitea-workspace-chrome.ts
  • src/renderer/src/components/icons/GiteaIcon.tsx
  • src/renderer/src/components/right-sidebar/ChecksPanel.tsx
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.test.ts
  • src/renderer/src/components/right-sidebar/checks-panel-gitea.ts
  • src/renderer/src/components/right-sidebar/checks-panel-review.ts
  • src/renderer/src/components/right-sidebar/use-hosted-review-actions.ts
  • src/renderer/src/components/settings/IntegrationsPane.tsx
  • src/renderer/src/components/settings/TasksPane.tsx
  • src/renderer/src/components/settings/gitea-integration-card.test.tsx
  • src/renderer/src/components/settings/gitea-integration-card.tsx
  • src/renderer/src/components/settings/settings-search-keywords.test.ts
  • src/renderer/src/components/settings/task-tracker-integration-cards.tsx
  • src/renderer/src/components/settings/tasks-search.ts
  • src/renderer/src/components/settings/use-integration-provider-status-refresh.ts
  • src/renderer/src/components/task-page-list-chrome-visibility.ts
  • src/renderer/src/components/task-page-localized-options.tsx
  • src/renderer/src/components/task-source-context-summary-format.ts
  • src/renderer/src/components/task-source-context-summary.test.ts
  • src/renderer/src/components/task-source-context-summary.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/store/index.ts
  • src/renderer/src/store/slices/diffComments.test.ts
  • src/renderer/src/store/slices/gitea.test.ts
  • src/renderer/src/store/slices/gitea.ts
  • src/renderer/src/store/slices/store-test-helpers.ts
  • src/renderer/src/store/slices/ui.ts
  • src/renderer/src/store/types.ts
  • src/shared/gitea-types.ts
  • src/shared/integration-credential-errors.ts
  • src/shared/task-providers.test.ts
  • src/shared/task-providers.ts
  • src/shared/task-source-context.ts
  • src/shared/types.ts
  • src/shared/workspace-name.ts

Comment thread FIXES-CR.md
Comment on lines +27 to +35
```
refactor(gitea): extract focused modules to stay within max-lines
fix(gitea): localize remaining UI strings and per-server settings state
fix(gitea): correct Tasks-page drawers and review-panel refresh for Gitea
fix(gitea): key issue caches by active server and invalidate lists on update
fix(gitea): guard renderer work-item state against selection changes
fix(gitea): correct issue/PR read & write edge cases in the main process
fix(gitea): scope credentials by API-base subpath and fix auth-status 403
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language to the fenced commit block.

This fence is already tripping markdownlint (MD040) on the touched file. text is enough here.

Proposed fix
-```
+```text
 refactor(gitea): extract focused modules to stay within max-lines
 fix(gitea): localize remaining UI strings and per-server settings state
 fix(gitea): correct Tasks-page drawers and review-panel refresh for Gitea
 fix(gitea): key issue caches by active server and invalidate lists on update
 fix(gitea): guard renderer work-item state against selection changes
 fix(gitea): correct issue/PR read & write edge cases in the main process
 fix(gitea): scope credentials by API-base subpath and fix auth-status 403
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 27-27: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Source: Linters/SAST tools

Comment thread src/main/gitea/connect.ts
Comment on lines +133 to +140
saveToken(id, token)
const file = getServerFile()
writeServerFile({
version: 1,
activeServerId: id,
selectedServerId: id,
servers: [server, ...file.servers.filter((entry) => entry.id !== id)]
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Rollback the saved token if metadata persistence fails.

saveToken(id, token) runs before writeServerFile(...). If the file write throws, the connect flow fails but the PAT stays stored locally without any matching server entry, so the app silently retains a credential the user can no longer manage from settings.

Suggested fix
   saveToken(id, token)
-  const file = getServerFile()
-  writeServerFile({
-    version: 1,
-    activeServerId: id,
-    selectedServerId: id,
-    servers: [server, ...file.servers.filter((entry) => entry.id !== id)]
-  })
+  try {
+    const file = getServerFile()
+    writeServerFile({
+      version: 1,
+      activeServerId: id,
+      selectedServerId: id,
+      servers: [server, ...file.servers.filter((entry) => entry.id !== id)]
+    })
+  } catch (error) {
+    deleteToken(id)
+    throw error
+  }
📝 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.

Suggested change
saveToken(id, token)
const file = getServerFile()
writeServerFile({
version: 1,
activeServerId: id,
selectedServerId: id,
servers: [server, ...file.servers.filter((entry) => entry.id !== id)]
})
saveToken(id, token)
try {
const file = getServerFile()
writeServerFile({
version: 1,
activeServerId: id,
selectedServerId: id,
servers: [server, ...file.servers.filter((entry) => entry.id !== id)]
})
} catch (error) {
deleteToken(id)
throw error
}

Comment thread src/main/ipc/gitea-pr.ts
Comment on lines +60 to +69
// Why: empty/malformed base or head SHAs would make the raw-content endpoint
// fall back to the default branch, rendering a diff against an unintended ref.
if (
typeof args.baseSha !== 'string' ||
!args.baseSha ||
typeof args.headSha !== 'string' ||
!args.headSha
) {
return { original: '', modified: '', originalIsBinary: false, modifiedIsBinary: false }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Actually validate “malformed” SHAs here.

This guard only rejects missing values. Non-empty junk like 'foo' or whitespace still reaches getGiteaPullRequestFileContents, so the raw-content endpoint can still fall back to the default branch and show the wrong diff—the exact case the new comment says we're preventing.

Comment thread src/main/ipc/gitea.ts
Comment on lines +122 to +166
ipcMain.handle(
'gitea:listWorkItems',
async (
_event,
args: GiteaRepoSelectorArgs & { filter?: GiteaWorkItemFilter; limit?: number }
) => {
const repo = assertRegisteredRepo(args, store)
const filter = VALID_FILTERS.has(args?.filter as GiteaWorkItemFilter)
? (args.filter as GiteaWorkItemFilter)
: undefined
return listGiteaWorkItems(repo.path, filter, clampLimit(args.limit), repoConnectionId(repo))
}
)

ipcMain.handle(
'gitea:issue',
async (_event, args: GiteaRepoSelectorArgs & { number: number }) => {
const repo = assertRegisteredRepo(args, store)
if (typeof args.number !== 'number') {
return null
}
return getGiteaIssue(repo.path, args.number, repoConnectionId(repo))
}
)

ipcMain.handle(
'gitea:issueComments',
async (_event, args: GiteaRepoSelectorArgs & { number: number }) => {
const repo = assertRegisteredRepo(args, store)
if (typeof args.number !== 'number') {
return []
}
return listGiteaIssueComments(repo.path, args.number, repoConnectionId(repo))
}
)

ipcMain.handle('gitea:labels', async (_event, args: GiteaRepoSelectorArgs) => {
const repo = assertRegisteredRepo(args, store)
return listGiteaLabels(repo.path, repoConnectionId(repo))
})

ipcMain.handle('gitea:assignees', async (_event, args: GiteaRepoSelectorArgs) => {
const repo = assertRegisteredRepo(args, store)
return listGiteaAssignees(repo.path, repoConnectionId(repo))
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check how assertRegisteredRepo validates its args parameter.
rg -n -B2 -A15 'function assertRegisteredRepo' src/main/ipc/gitea-repo-access.ts

Repository: stablyai/orca

Length of output: 866


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the repo lookup helper used by assertRegisteredRepo and the surrounding types.
rg -n -B4 -A20 'function findRegisteredGiteaRepo|const findRegisteredGiteaRepo|export function findRegisteredGiteaRepo|type GiteaRepoSelectorArgs|interface GiteaRepoSelectorArgs' src/main/ipc/gitea-repo-access.ts src/main/ipc/gitea.ts

Repository: stablyai/orca

Length of output: 4020


Guard assertRegisteredRepo against missing IPC args
assertRegisteredRepo(args, store) still dereferences args directly, so a malformed IPC payload can throw a TypeError before these handlers return a structured access error. Add a null/undefined check in the helper.

Comment thread src/preload/index.ts
Comment on lines +1734 to +1741
updateIssue: (args: {
repoPath: string
repoId?: string | null
sourceContext?: unknown
number: number
updates: unknown
}): Promise<{ ok: true } | { ok: false; error: string }> =>
ipcRenderer.invoke('gitea:updateIssue', args),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve updateIssue partial-success warnings in the preload contract.

updateGiteaIssue() can return { ok: true, warning } after a PATCH succeeds but label saving fails; this preload type erases that warning, so renderer callers can’t surface the label failure type-safely.

Proposed fix
-    }): Promise<{ ok: true } | { ok: false; error: string }> =>
+    }): Promise<{ ok: true; warning?: string } | { ok: false; error: string }> =>
       ipcRenderer.invoke('gitea:updateIssue', args),
📝 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.

Suggested change
updateIssue: (args: {
repoPath: string
repoId?: string | null
sourceContext?: unknown
number: number
updates: unknown
}): Promise<{ ok: true } | { ok: false; error: string }> =>
ipcRenderer.invoke('gitea:updateIssue', args),
updateIssue: (args: {
repoPath: string
repoId?: string | null
sourceContext?: unknown
number: number
updates: unknown
}): Promise<{ ok: true; warning?: string } | { ok: false; error: string }> =>
ipcRenderer.invoke('gitea:updateIssue', args),

Comment on lines +14 to +17
const giteaStatusLoaded = useAppStore((s) => s.giteaStatusLoaded)
const checkLinearConnection = useAppStore((s) => s.checkLinearConnection)
const checkJiraConnection = useAppStore((s) => s.checkJiraConnection)
const refreshGiteaStatus = useAppStore((s) => s.refreshGiteaStatus)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## File outline\n'
ast-grep outline src/renderer/src/components/settings/use-integration-provider-status-refresh.ts --view expanded || true

printf '\n## Relevant file contents\n'
sed -n '1,220p' src/renderer/src/components/settings/use-integration-provider-status-refresh.ts

printf '\n## Search for related store methods\n'
rg -n "refreshGiteaStatus|giteaStatusLoaded|checkLinearConnection|checkJiraConnection" src/renderer/src -g '*.ts' -g '*.tsx'

printf '\n## Likely store file(s)\n'
fd -a "useAppStore" src/renderer/src || true
fd -a "store" src/renderer/src || true

Repository: stablyai/orca

Length of output: 19842


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '## Gitea slice outline\n'
ast-grep outline src/renderer/src/store/slices/gitea.ts --view expanded || true

printf '\n## Gitea slice contents\n'
sed -n '1,240p' src/renderer/src/store/slices/gitea.ts

printf '\n## Gitea tests around refresh behavior\n'
sed -n '240,340p' src/renderer/src/store/slices/gitea.test.ts

printf '\n## Similar hook in TaskPage\n'
sed -n '7100,7145p' src/renderer/src/components/TaskPage.tsx

Repository: stablyai/orca

Length of output: 13033


Prevent duplicate Gitea status refreshes. giteaStatusLoaded stays false until refreshGiteaStatus() resolves, so any rerender from the Linear/Jira/Preflight updates can start another Gitea fetch before the first one completes. Add an in-flight guard or store-level dedupe here.

Comment on lines +6591 to +6592
const [selectedGiteaItem, setSelectedGiteaItem] = useState<GiteaWorkspaceSelection | null>(null)
const [selectedGiteaPr, setSelectedGiteaPr] = useState<GiteaWorkspaceSelection | null>(null)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear Gitea selections when leaving the Gitea source.

selectedGiteaItem/selectedGiteaPr persist after switching providers, but the Escape guard still treats them as open modal state. That leaves Esc unable to close the current Tasks page and can reopen stale Gitea details when switching back.

Suggested fix
   const [selectedGiteaItem, setSelectedGiteaItem] = useState<GiteaWorkspaceSelection | null>(null)
   const [selectedGiteaPr, setSelectedGiteaPr] = useState<GiteaWorkspaceSelection | null>(null)
+
+  useEffect(() => {
+    if (taskSource !== 'gitea') {
+      setSelectedGiteaItem(null)
+      setSelectedGiteaPr(null)
+    }
+  }, [taskSource])

Also applies to: 7059-7061

Comment on lines +6596 to +6604
const linkedWorkItem: LinkedWorkItemSummary = {
type: item.type === 'pull' ? 'pr' : 'issue',
number: item.number,
title: item.title,
url: item.url
}
openModal('new-workspace-composer', {
linkedWorkItem,
taskSourceContext: getTaskPageRepoSourceContext(repo, 'gitea'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Mark Gitea composer links with provider: 'gitea'.

Gitea issues/PRs are converted to the same issue/pr shape used by GitHub, so omitting provider can misclassify the workspace intent and attachment metadata.

Suggested fix
       const linkedWorkItem: LinkedWorkItemSummary = {
         type: item.type === 'pull' ? 'pr' : 'issue',
+        provider: 'gitea',
         number: item.number,
         title: item.title,
         url: item.url
       }
📝 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.

Suggested change
const linkedWorkItem: LinkedWorkItemSummary = {
type: item.type === 'pull' ? 'pr' : 'issue',
number: item.number,
title: item.title,
url: item.url
}
openModal('new-workspace-composer', {
linkedWorkItem,
taskSourceContext: getTaskPageRepoSourceContext(repo, 'gitea'),
const linkedWorkItem: LinkedWorkItemSummary = {
type: item.type === 'pull' ? 'pr' : 'issue',
provider: 'gitea',
number: item.number,
title: item.title,
url: item.url
}
openModal('new-workspace-composer', {
linkedWorkItem,
taskSourceContext: getTaskPageRepoSourceContext(repo, 'gitea'),

Comment on lines +12297 to +12454
"gitea": {
"connect": {
"dialog": {
"d2823a1de0": "Connect Gitea server",
"d076a10ae3": "Use a Gitea server URL and a personal access token to browse and manage issues.",
"690c1da1bb": "Gitea server URL",
"83b75fa9b7": "https://gitea.example.com",
"062ae1ea05": "Access token",
"79c04f358b": "Gitea personal access token",
"3ecb3e1153": "Create a token under your Gitea user settings → Applications, with read/write access to issues.",
"4d1f2d9f21": "Your token is stored locally and encrypted when local runtime storage supports it.",
"7acfed1da5": "Cancel",
"f5d526570f": "Verifying…",
"f68c3d011f": "Connect"
}
},
"issue": {
"meta": {
"controls": {
"ba343731c0": "Failed to update issue.",
"63f46a8d86": "Title",
"8cc407607b": "Labels",
"11fac85e07": "No labels in this repo.",
"401f7dc7a2": "Assignees",
"3cd6f5acbd": "No assignable users."
}
},
"comments": {
"8608602d16": "Comments",
"ea3f6f14a1": "No comments yet.",
"6a519bdb1c": "Unknown",
"7649eb9d31": "recently"
}
},
"pr": {
"file": {
"diff": {
"81e07de6a5": "Binary file not shown.",
"f1a2b3c4d5": "Failed to load file diff.",
"e6f7a8b9c0": "Retry",
"643d805160": "Added",
"552647cfb3": "Modified",
"6a18d47646": "Deleted",
"184ffbc460": "Renamed",
"3e96a427af": "Copied",
"1e228bc58e": "Changed"
}
},
"checks": {
"bed82a55cd": "No status checks reported for this PR.",
"448c6113a4": "Open check details"
},
"line": {
"comments": {
"489b36633a": "Line {{value0}}",
"c01a062b92": "Unknown",
"4e59b0c4f8": "Line #",
"a43b5cc38b": "New-file line number from the diff",
"02339776da": "Review comment…",
"317313f8af": "Comment",
"6cbd906d88": "Comment on a line"
}
},
"tab": {
"body": {
"1fe3a05742": "No description provided.",
"413feaa83a": "Inline",
"5372c28376": "Side by side",
"53ab0c5a49": "No changed files."
}
},
"merge": {
"button": {
"2378ab4908": "Conflicts",
"8e662b9b6b": "Merge",
"725a660786": "Squash",
"f765e24257": "Rebase"
}
}
},
"new": {
"issue": {
"dialog": {
"db951c3a29": "New issue in {{value0}}",
"b42f72cfc5": "Create an issue in the selected Gitea project.",
"becfadf1df": "Title",
"6c8b731fb2": "Description",
"032fd2ed27": "Cancel",
"5e1d23a112": "Create issue"
}
}
}
},
"GiteaTaskList": {
"heading": "Issues & pull requests",
"c07dd13940": "All open",
"ab2a229e50": "Assigned to me",
"74f30cd767": "Created",
"031c70d963": "Closed",
"bf66fad935": "All",
"65a3bffe7a": "Issues",
"9b21aab4c9": "PRs",
"9986e0e679": "Merged",
"747423c0f8": "Draft",
"980519b0c8": "Open",
"0285721e62": "Failed to load Gitea tasks.",
"5997067bee": "New issue",
"e3db16e7c7": "Refresh Gitea tasks",
"34cd2d1024": "Select a Gitea-hosted project to see its issues and pull requests.",
"6e1e6691d7": "Loading Gitea tasks…",
"00f67933b5": "No open issues or pull requests."
},
"GiteaIssueWorkspace": {
"comments": "Comments",
"noComments": "No comments yet.",
"unknown": "Unknown",
"47b064c003": "{{value0}} copied",
"927222e135": "Failed to copy.",
"2d2e69fe70": "Failed to update issue.",
"b7c8d9e0f1": "Failed to load issue.",
"565680a39b": "Failed to add comment.",
"a792c22414": "Gitea item",
"01b6ea43ca": "Preview and start work from the selected Gitea item.",
"ce513e3ca2": "Close",
"e849a4c80f": "Start workspace",
"c0d4f97901": "Closed",
"ab08895bb5": "Open",
"1ab551635b": "Reopen",
"6b3c5a3144": "Close issue",
"63cbb99973": "Open in Gitea",
"c29b657be3": "Copy URL",
"ca242eaf5d": "No description provided.",
"0fee3f06ff": "Add a comment...",
"d3e0033e0f": "Comment"
},
"GiteaPullRequestWorkspace": {
"conflicts": "Conflicts",
"merge": "Merge",
"noDescription": "No description provided.",
"inline": "Inline",
"sideBySide": "Side by side",
"noFiles": "No changed files.",
"f5cf34ed40": "Failed to add comment.",
"c2d3e4f5a6": "Failed to load PR checks.",
"22f9c29c9b": "Pull request merged.",
"d93415a77d": "Failed to merge.",
"c7c84081fc": "Failed to add review comment.",
"f51240a59a": "Conversation",
"4f4abe43ab": "Files",
"95a6c97ba6": "Checks",
"8e44409da0": "Gitea pull request",
"0a432f2c1a": "Review files, checks, and conversation, then merge or start work.",
"5fa5ec9fff": "Close",
"f88c7cf08c": "Start workspace",
"9813b44d73": "Open in Gitea",
"ddc99ed766": "Add a comment...",
"d646ddef48": "Comment"
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Regenerate these locale IDs with the catalog sync.

Keys like f1a2b3c4d5, c2d3e4f5a6, and d4e5f6a7b8 look hand-authored instead of sync-generated. That makes the locale catalog easy to drift or get rewritten on the next localization sync. Based on learnings, new locale entries should be auto-keyed using SHA1(file path + source text) and kept in parity via pnpm run sync:localization-catalog.

Source: Learnings

Comment on lines +126 to +137
giteaConnect: async (args) => {
const result = await window.api.gitea.connect(args)
if (result.ok) {
await get().refreshGiteaStatus()
}
return result.ok ? { ok: true } : { ok: false, error: result.error }
},

giteaDisconnect: async (serverId) => {
await window.api.gitea.disconnect(serverId ? { serverId } : undefined)
await get().refreshGiteaStatus()
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Clear cached issue data when credentials change.

refreshGiteaStatus() updates status, but the caches survive connect/disconnect. Because scopeKey() can be derived from sourceContext alone and server IDs are stable per apiBaseUrl, reconnecting the same host with a different token can serve the previous account's work items/details until the TTL expires.

Proposed fix
   giteaConnect: async (args) => {
     const result = await window.api.gitea.connect(args)
     if (result.ok) {
       await get().refreshGiteaStatus()
+      set({ giteaWorkItems: {}, giteaIssueDetail: {} })
     }
     return result.ok ? { ok: true } : { ok: false, error: result.error }
   },

   giteaDisconnect: async (serverId) => {
     await window.api.gitea.disconnect(serverId ? { serverId } : undefined)
     await get().refreshGiteaStatus()
+    set({ giteaWorkItems: {}, giteaIssueDetail: {} })
   },
📝 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.

Suggested change
giteaConnect: async (args) => {
const result = await window.api.gitea.connect(args)
if (result.ok) {
await get().refreshGiteaStatus()
}
return result.ok ? { ok: true } : { ok: false, error: result.error }
},
giteaDisconnect: async (serverId) => {
await window.api.gitea.disconnect(serverId ? { serverId } : undefined)
await get().refreshGiteaStatus()
},
giteaConnect: async (args) => {
const result = await window.api.gitea.connect(args)
if (result.ok) {
await get().refreshGiteaStatus()
set({ giteaWorkItems: {}, giteaIssueDetail: {} })
}
return result.ok ? { ok: true } : { ok: false, error: result.error }
},
giteaDisconnect: async (serverId) => {
await window.api.gitea.disconnect(serverId ? { serverId } : undefined)
await get().refreshGiteaStatus()
set({ giteaWorkItems: {}, giteaIssueDetail: {} })
},

@charilaouc charilaouc closed this by deleting the head repository Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants