Skip to content

feat(account): verify GitHub username - #2826

Open
WcaleNieWolny wants to merge 11 commits into
mainfrom
wolny/github-username-picker
Open

feat(account): verify GitHub username#2826
WcaleNieWolny wants to merge 11 commits into
mainfrom
wolny/github-username-picker

Conversation

@WcaleNieWolny

@WcaleNieWolny WcaleNieWolny commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Replaced the free-form GitHub username field with a browser-side GitHub profile lookup and confirmation flow.
  • Save the verified GitHub login and numeric github_id only after the user chooses Yes.
  • Keep the account field reactive so the confirmed username appears immediately, without a page refresh.

Why

FormKit treated the previous :value as an initial value, leaving the username display stale after the dialog saved successfully. The field now uses v-model.

Validation

  • bunx eslint src/pages/settings/account/index.vue src/services/githubProfile.ts tests/github-profile.unit.test.ts
  • bunx vitest run tests/github-profile.unit.test.ts
  • bun typecheck

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added GitHub profile selection to account settings.
    • Search by username, preview matching profiles, confirm selections, or remove an existing connection.
    • Selected profiles save automatically, while the connected profile field remains read-only.
    • Added clear messages for invalid usernames, unavailable profiles, rate limits, and lookup failures.
    • Added English translations for GitHub profile connection and validation messages.
  • Tests

    • Added coverage for username normalization, profile lookup, response handling, and error scenarios.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

GitHub profile selection

Layer / File(s) Summary
GitHub profile retrieval and validation
src/services/githubProfile.ts, tests/github-profile.unit.test.ts
The service normalizes and validates usernames, retrieves GitHub profiles, maps API responses and errors, and tests successful, invalid, missing, network, rate-limit, and malformed responses.
Account profile selection workflow
src/pages/settings/account/index.vue
Account settings manages profile lookup, confirmation, persistence, removal, error handling, and user-record synchronization. The account form no longer submits github_username.
Profile selection dialog UI
src/pages/settings/account/index.vue, messages/en.json
The GitHub field is read-only and opens a dialog with profile preview, loading states, validation errors, confirmation, removal, cancellation, and localized messages.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AccountSettings
  participant githubProfile
  participant GitHubAPI
  participant UserStore
  AccountSettings->>githubProfile: getGitHubProfile(username)
  githubProfile->>GitHubAPI: Fetch profile by encoded username
  GitHubAPI-->>githubProfile: Profile response or typed error
  githubProfile-->>AccountSettings: GitHubProfile or typed error
  AccountSettings->>UserStore: Persist or clear GitHub profile data
  UserStore-->>AccountSettings: Updated user record
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: GitHub username verification in the account flow.
Description check ✅ Passed The description explains the change, rationale, and validation steps, but omits the template headings, screenshots, and checklist status.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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

@codspeed-hq

codspeed-hq Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing wolny/github-username-picker (6d3b3be) with main (76712be)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@WcaleNieWolny
WcaleNieWolny marked this pull request as ready for review August 1, 2026 13:25

@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: 4

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

Inline comments:
In `@src/pages/settings/account/index.vue`:
- Around line 76-94: Update findGitHubProfile to return immediately when
githubProfileLoading.value is already true, before clearing state or starting
another request. Preserve the existing lookup, error handling, and finally
behavior for calls that proceed.
- Around line 673-680: Update the GitHub username field using githubUsername and
openGitHubProfileDialog so keyboard activation, such as Enter, opens the profile
dialog while preserving the existing click behavior. Also reorder the field
attributes so v-model appears before autocomplete, as required by static
analysis.

In `@src/services/githubProfile.ts`:
- Around line 25-30: Remove the X-GitHub-Api-Version header from the fetch
request in getGitHubProfile, leaving the Accept header and request behavior
unchanged; use a backend proxy separately if the version header remains
required.

In `@tests/github-profile.unit.test.ts`:
- Around line 1-40: Extend the getGitHubProfile tests with request_failed
coverage for both a rejected globalThis.fetch call and a successful 200 response
containing JSON without id, login, or avatar_url. Assert each invocation rejects
with code request_failed, while preserving the existing success, validation, and
not_found cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 104fe3e2-eb47-4821-9f72-aa8ee36f22b8

📥 Commits

Reviewing files that changed from the base of the PR and between 10c8a55 and 968a2f7.

📒 Files selected for processing (4)
  • messages/en.json
  • src/pages/settings/account/index.vue
  • src/services/githubProfile.ts
  • tests/github-profile.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread src/pages/settings/account/index.vue
Comment thread src/pages/settings/account/index.vue Outdated
Comment thread src/services/githubProfile.ts Outdated
Comment thread tests/github-profile.unit.test.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

7 issues found across 4 files

Confidence score: 3/5

  • In src/pages/settings/account/index.vue, the GitHub lookup result is not bound to the current input/dialog instance, so stale responses can overwrite newer entries and let users confirm the wrong GitHub identity — tie each response to a request token (or current username) before applying it.
  • In src/services/githubProfile.ts, getGitHubProfile has no timeout/abort path and relies on unauthenticated browser calls, so users can get stuck in perpetual loading and shared-IP environments can hit GitHub rate limits, blocking verification for many users — add an AbortController timeout mapped to request_failed and consider moving verification behind a server-backed/authenticated path.
  • In src/pages/settings/account/index.vue, the new flow can trap account edits: existing GitHub links cannot be explicitly cleared, and after a failed save the dialog can hide the username input while keeping only confirm/reject actions — add an explicit unlink/reset action and restore editable lookup state after persistence errors.
  • In src/pages/settings/account/index.vue, the readonly GitHub field is mouse-only, so keyboard users may be unable to open verification; and tests/github-profile.unit.test.ts does not cover request_failed error branches, increasing regression risk around failure handling — add Enter/button activation and unit tests for network and malformed-payload failures.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/services/githubProfile.ts">

<violation number="1" location="src/services/githubProfile.ts:25">
P2: A stalled GitHub connection can leave the profile dialog in a loading state indefinitely because this fetch has no timeout or abort path. A bounded `AbortController` timeout, mapped to `request_failed`, would let the user retry instead of retaining a pending lookup.</violation>

<violation number="2" location="src/services/githubProfile.ts:25">
P2: This verification runs the public GitHub API from each browser, unauthenticated. The unauthenticated core rate limit of 60 requests/hour per IP can be exhausted quickly when many console users share an IP (e.g. office/corporate NAT), at which point GitHub returns 403 and every search surfaces the generic 'could not load profile' error, which won't resolve on retry. Consider routing the lookup through a backend endpoint (with a token or server-side caching) or at least detecting the 403 rate-limit response (`X-RateLimit-Remaining: 0`) and showing a clearer, rate-limit-specific message instead of the generic retry prompt.</violation>
</file>

<file name="src/pages/settings/account/index.vue">

<violation number="1" location="src/pages/settings/account/index.vue:80">
P2: A lookup result is not tied to the input or dialog instance, so an older request can overwrite a newly entered or reopened dialog and the user can save the wrong GitHub login/ID by choosing Yes. Associating each request with the current username/dialog instance (or cancelling stale requests) would prevent this data-integrity failure.</violation>

<violation number="2" location="src/pages/settings/account/index.vue:112">
P3: When the save to the `users` table fails, the dialog keeps the already-found `githubProfile` and only renders the Confirm/No buttons, hiding the username input. A user who wants to correct the username or re-run the lookup must close and reopen the dialog. Clearing `githubProfile` (or otherwise showing the input) on a save error would let them fix the value in place.</violation>

<violation number="3" location="src/pages/settings/account/index.vue:675">
P2: Users with an existing GitHub association can no longer remove it: the readonly field always starts a lookup, while rejecting a profile only closes the dialog. Providing an explicit unlink/clear action would preserve the optional account setting's previous behavior.</violation>

<violation number="4" location="src/pages/settings/account/index.vue:680">
P2: Keyboard-only users cannot open the GitHub verification dialog because this readonly input has only a mouse click handler. Adding Enter activation or using a button-like control would keep the new flow accessible.</violation>
</file>

<file name="tests/github-profile.unit.test.ts">

<violation number="1" location="tests/github-profile.unit.test.ts:30">
P3: Test coverage for getGitHubProfile is missing the request_failed branch — neither a thrown network error nor a malformed JSON payload (missing id/login/avatar_url) is exercised, even though both are distinct code paths in the implementation.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/services/githubProfile.ts Outdated

let response: Response
try {
response = await fetch(`https://api.github.com/users/${encodeURIComponent(normalizedUsername)}`, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A stalled GitHub connection can leave the profile dialog in a loading state indefinitely because this fetch has no timeout or abort path. A bounded AbortController timeout, mapped to request_failed, would let the user retry instead of retaining a pending lookup.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 25:

<comment>A stalled GitHub connection can leave the profile dialog in a loading state indefinitely because this fetch has no timeout or abort path. A bounded `AbortController` timeout, mapped to `request_failed`, would let the user retry instead of retaining a pending lookup.</comment>

<file context>
@@ -0,0 +1,57 @@
+
+  let response: Response
+  try {
+    response = await fetch(`https://api.github.com/users/${encodeURIComponent(normalizedUsername)}`, {
+      headers: {
+        'accept': 'application/vnd.github+json',
</file context>

:disabled="isLoading"
:value="main.user?.github_username ?? ''"
validation="length:0,39"
readonly

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Users with an existing GitHub association can no longer remove it: the readonly field always starts a lookup, while rejecting a profile only closes the dialog. Providing an explicit unlink/clear action would preserve the optional account setting's previous behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 675:

<comment>Users with an existing GitHub association can no longer remove it: the readonly field always starts a lookup, while rejecting a profile only closes the dialog. Providing an explicit unlink/clear action would preserve the optional account setting's previous behavior.</comment>

<file context>
@@ -594,12 +670,14 @@ onMounted(async () => {
                   :disabled="isLoading"
-                  :value="main.user?.github_username ?? ''"
-                  validation="length:0,39"
+                  readonly
                   enterkeyhint="send"
                   :label="t('github-username')"
</file context>

:label="t('github-username')"
:help="t('github-username-help')"
:placeholder="t('github-username-select')"
@click="openGitHubProfileDialog"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Keyboard-only users cannot open the GitHub verification dialog because this readonly input has only a mouse click handler. Adding Enter activation or using a button-like control would keep the new flow accessible.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 680:

<comment>Keyboard-only users cannot open the GitHub verification dialog because this readonly input has only a mouse click handler. Adding Enter activation or using a button-like control would keep the new flow accessible.</comment>

<file context>
@@ -594,12 +670,14 @@ onMounted(async () => {
                   :label="t('github-username')"
                   :help="t('github-username-help')"
+                  :placeholder="t('github-username-select')"
+                  @click="openGitHubProfileDialog"
                 />
               </div>
</file context>
Suggested change
@click="openGitHubProfileDialog"
@click="openGitHubProfileDialog"
@keydown.enter.prevent="openGitHubProfileDialog"

Comment thread src/pages/settings/account/index.vue Outdated
githubProfileError.value = ''
githubProfileLoading.value = true
try {
githubProfile.value = await getGitHubProfile(githubUsernameInput.value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A lookup result is not tied to the input or dialog instance, so an older request can overwrite a newly entered or reopened dialog and the user can save the wrong GitHub login/ID by choosing Yes. Associating each request with the current username/dialog instance (or cancelling stale requests) would prevent this data-integrity failure.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 80:

<comment>A lookup result is not tied to the input or dialog instance, so an older request can overwrite a newly entered or reopened dialog and the user can save the wrong GitHub login/ID by choosing Yes. Associating each request with the current username/dialog instance (or cancelling stale requests) would prevent this data-integrity failure.</comment>

<file context>
@@ -41,8 +43,84 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+  githubProfileError.value = ''
+  githubProfileLoading.value = true
+  try {
+    githubProfile.value = await getGitHubProfile(githubUsernameInput.value)
+  }
+  catch (error) {
</file context>

Comment thread src/services/githubProfile.ts Outdated

let response: Response
try {
response = await fetch(`https://api.github.com/users/${encodeURIComponent(normalizedUsername)}`, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This verification runs the public GitHub API from each browser, unauthenticated. The unauthenticated core rate limit of 60 requests/hour per IP can be exhausted quickly when many console users share an IP (e.g. office/corporate NAT), at which point GitHub returns 403 and every search surfaces the generic 'could not load profile' error, which won't resolve on retry. Consider routing the lookup through a backend endpoint (with a token or server-side caching) or at least detecting the 403 rate-limit response (X-RateLimit-Remaining: 0) and showing a clearer, rate-limit-specific message instead of the generic retry prompt.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 25:

<comment>This verification runs the public GitHub API from each browser, unauthenticated. The unauthenticated core rate limit of 60 requests/hour per IP can be exhausted quickly when many console users share an IP (e.g. office/corporate NAT), at which point GitHub returns 403 and every search surfaces the generic 'could not load profile' error, which won't resolve on retry. Consider routing the lookup through a backend endpoint (with a token or server-side caching) or at least detecting the 403 rate-limit response (`X-RateLimit-Remaining: 0`) and showing a clearer, rate-limit-specific message instead of the generic retry prompt.</comment>

<file context>
@@ -0,0 +1,57 @@
+
+  let response: Response
+  try {
+    response = await fetch(`https://api.github.com/users/${encodeURIComponent(normalizedUsername)}`, {
+      headers: {
+        'accept': 'application/vnd.github+json',
</file context>

.single()

githubProfileSaving.value = false
if (error || !user) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: When the save to the users table fails, the dialog keeps the already-found githubProfile and only renders the Confirm/No buttons, hiding the username input. A user who wants to correct the username or re-run the lookup must close and reopen the dialog. Clearing githubProfile (or otherwise showing the input) on a save error would let them fix the value in place.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 112:

<comment>When the save to the `users` table fails, the dialog keeps the already-found `githubProfile` and only renders the Confirm/No buttons, hiding the username input. A user who wants to correct the username or re-run the lookup must close and reopen the dialog. Clearing `githubProfile` (or otherwise showing the input) on a save error would let them fix the value in place.</comment>

<file context>
@@ -41,8 +43,84 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+    .single()
+
+  githubProfileSaving.value = false
+  if (error || !user) {
+    githubProfileError.value = t('account-error')
+    return
</file context>

expect(fetchMock).toHaveBeenCalledWith('https://api.github.com/users/octocat', expect.any(Object))
})

it('rejects invalid and unknown usernames with user-facing error codes', async () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Test coverage for getGitHubProfile is missing the request_failed branch — neither a thrown network error nor a malformed JSON payload (missing id/login/avatar_url) is exercised, even though both are distinct code paths in the implementation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/github-profile.unit.test.ts, line 30:

<comment>Test coverage for getGitHubProfile is missing the request_failed branch — neither a thrown network error nor a malformed JSON payload (missing id/login/avatar_url) is exercised, even though both are distinct code paths in the implementation.</comment>

<file context>
@@ -0,0 +1,40 @@
+    expect(fetchMock).toHaveBeenCalledWith('https://api.github.com/users/octocat', expect.any(Object))
+  })
+
+  it('rejects invalid and unknown usernames with user-facing error codes', async () => {
+    await expect(getGitHubProfile('invalid_username')).rejects.toMatchObject({ code: 'invalid_username' } satisfies Partial<GitHubProfileError>)
+
</file context>

@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.

Caution

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

⚠️ Outside diff range comments (3)
src/pages/settings/account/index.vue (2)

842-850: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use DaisyUI classes for the new dialog input.

The new interactive input uses only Tailwind utility classes. Replace the custom input styling with the repository's d-input class and keep the existing layout utilities.

As per coding guidelines, use DaisyUI d- classes for interactive primitives.

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

In `@src/pages/settings/account/index.vue` around lines 842 - 850, Update the
input bound to githubUsernameInput in the findGitHubProfile flow to replace its
custom border, padding, rounding, and color styling with the repository’s
d-input class, while preserving the existing w-full layout utility and other
behavior.

Source: Coding guidelines


54-97: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Invalidate stale profile lookups before confirmation.

The loading guard prevents overlapping calls, but it does not bind the response to the current input or dialog. The input at Line 842 remains editable while Line 83 awaits the request.

If the user starts a lookup for alice, changes the input to bob, and the first request resolves, githubProfile contains Alice's profile. confirmGitHubProfile can then persist Alice's github_id. Closing and reopening during the request can also leave stale loading state.

Invalidate requests during reset or close, ignore stale success and error updates, and disable the input while loading.

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

In `@src/pages/settings/account/index.vue` around lines 54 - 97, Update the GitHub
profile dialog flow around resetGitHubProfileDialog, closeGitHubProfileDialog,
and findGitHubProfile to invalidate each lookup when the dialog resets or closes
and ignore success, error, and loading updates from stale requests or inputs
that no longer match. Ensure reopening the dialog cannot inherit a previous
request’s loading state, and disable the username input while
githubProfileLoading is active.
src/services/githubProfile.ts (1)

40-48: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Map malformed profile responses to GitHubProfileError('request_failed'). Catch response.json() failures and validate that the parsed value is a non-null object before reading its fields. Otherwise, "not-json" produces SyntaxError and "null" produces TypeError. Add tests for both cases.

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

In `@src/services/githubProfile.ts` around lines 40 - 48, Update the profile-fetch
flow around response.json() to catch parsing failures and throw
GitHubProfileError('request_failed'). Validate that the parsed profile is a
non-null object before accessing id, login, or avatar_url, mapping invalid
values—including null and malformed JSON—to the same error; add tests covering
both failure cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/pages/settings/account/index.vue`:
- Around line 842-850: Update the input bound to githubUsernameInput in the
findGitHubProfile flow to replace its custom border, padding, rounding, and
color styling with the repository’s d-input class, while preserving the existing
w-full layout utility and other behavior.
- Around line 54-97: Update the GitHub profile dialog flow around
resetGitHubProfileDialog, closeGitHubProfileDialog, and findGitHubProfile to
invalidate each lookup when the dialog resets or closes and ignore success,
error, and loading updates from stale requests or inputs that no longer match.
Ensure reopening the dialog cannot inherit a previous request’s loading state,
and disable the username input while githubProfileLoading is active.

In `@src/services/githubProfile.ts`:
- Around line 40-48: Update the profile-fetch flow around response.json() to
catch parsing failures and throw GitHubProfileError('request_failed'). Validate
that the parsed profile is a non-null object before accessing id, login, or
avatar_url, mapping invalid values—including null and malformed JSON—to the same
error; add tests covering both failure cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5e8cc297-cda9-434d-9073-ad46c88ddac5

📥 Commits

Reviewing files that changed from the base of the PR and between 968a2f7 and ea83ae4.

📒 Files selected for processing (3)
  • src/pages/settings/account/index.vue
  • src/services/githubProfile.ts
  • tests/github-profile.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

4 issues found across 4 files

Confidence score: 3/5

  • In src/pages/settings/account/index.vue, the async lookup can resolve after the dialog is reopened and populate stale profile data, which could let a user save the wrong GitHub account—tie each response to the current dialog/search token and ignore outdated results.
  • In src/pages/settings/account/index.vue, the current read-only/confirm behavior has no way to clear an existing GitHub link, so users can get stuck with an incorrect association—add an explicit remove action that persists github_id: null.
  • In src/services/githubProfile.ts, malformed or empty 2xx payloads currently leak SyntaxError/TypeError instead of GitHubProfileError('request_failed'), which can break caller-side error handling assumptions—wrap parse/null-shape failures in the service’s typed error.
  • The confirm/save flow added in src/pages/settings/account/index.vue introduces several async branches (preview, save, reactive update, save-failure handling) with limited new test coverage noted, increasing regression risk around edge paths—add focused tests for stale responses, clear/remove, and save-failure recovery.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/services/githubProfile.ts">

<violation number="1" location="src/services/githubProfile.ts:40">
P3: Malformed or empty 2xx responses currently escape as `SyntaxError`/`TypeError` instead of `GitHubProfileError('request_failed')`. This makes direct callers unable to rely on the service's error codes; wrapping parsing and rejecting non-object JSON keeps the lookup contract consistent.</violation>
</file>

<file name="src/pages/settings/account/index.vue">

<violation number="1" location="src/pages/settings/account/index.vue:83">
P2: A lookup that finishes after the dialog is closed and reopened can display the old profile for the new search, allowing the user to save the wrong GitHub account. Associating each request with the current dialog/search generation (or aborting and invalidating it on close) would prevent stale responses from mutating the active dialog.</violation>

<violation number="2" location="src/pages/settings/account/index.vue:99">
P3: The new interactive confirm flow (find profile -> preview -> save to users -> update reactive state -> error handling for save failures) in index.vue adds meaningful branching and async error paths, but the only added unit coverage targets src/services/githubProfile.ts (tests/github-profile.unit.test.ts). The dialog-level behavior — confirmGitHubProfile wiring the GitHubProfile into the users update and updating main.user/githubUsername, plus the save-failure path that surfaces t('account-error') — is untested. Adding coverage for the find/confirm/save/error flow would guard the most failure-prone part of this change.</violation>

<violation number="3" location="src/pages/settings/account/index.vue:678">
P2: Users cannot clear an already saved GitHub association after this change because the field is read-only and confirmation only writes a non-null profile. Providing an explicit remove/clear action that saves `github_id: null` and `github_username: null` would preserve the account-settings behavior for this optional field.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

:disabled="isLoading"
:value="main.user?.github_username ?? ''"
validation="length:0,39"
readonly

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Users cannot clear an already saved GitHub association after this change because the field is read-only and confirmation only writes a non-null profile. Providing an explicit remove/clear action that saves github_id: null and github_username: null would preserve the account-settings behavior for this optional field.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 678:

<comment>Users cannot clear an already saved GitHub association after this change because the field is read-only and confirmation only writes a non-null profile. Providing an explicit remove/clear action that saves `github_id: null` and `github_username: null` would preserve the account-settings behavior for this optional field.</comment>

<file context>
@@ -591,15 +670,18 @@ onMounted(async () => {
                   :disabled="isLoading"
-                  :value="main.user?.github_username ?? ''"
-                  validation="length:0,39"
+                  readonly
                   enterkeyhint="send"
                   :label="t('github-username')"
</file context>

Comment thread src/pages/settings/account/index.vue Outdated
githubProfileError.value = ''
githubProfileLoading.value = true
try {
githubProfile.value = await getGitHubProfile(githubUsernameInput.value)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A lookup that finishes after the dialog is closed and reopened can display the old profile for the new search, allowing the user to save the wrong GitHub account. Associating each request with the current dialog/search generation (or aborting and invalidating it on close) would prevent stale responses from mutating the active dialog.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 83:

<comment>A lookup that finishes after the dialog is closed and reopened can display the old profile for the new search, allowing the user to save the wrong GitHub account. Associating each request with the current dialog/search generation (or aborting and invalidating it on close) would prevent stale responses from mutating the active dialog.</comment>

<file context>
@@ -41,8 +43,87 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+  githubProfileError.value = ''
+  githubProfileLoading.value = true
+  try {
+    githubProfile.value = await getGitHubProfile(githubUsernameInput.value)
+  }
+  catch (error) {
</file context>

Comment thread src/services/githubProfile.ts Outdated
if (!response.ok)
throw new GitHubProfileError('request_failed')

const profile = await response.json() as {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Malformed or empty 2xx responses currently escape as SyntaxError/TypeError instead of GitHubProfileError('request_failed'). This makes direct callers unable to rely on the service's error codes; wrapping parsing and rejecting non-object JSON keeps the lookup contract consistent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 40:

<comment>Malformed or empty 2xx responses currently escape as `SyntaxError`/`TypeError` instead of `GitHubProfileError('request_failed')`. This makes direct callers unable to rely on the service's error codes; wrapping parsing and rejecting non-object JSON keeps the lookup contract consistent.</comment>

<file context>
@@ -0,0 +1,56 @@
+  if (!response.ok)
+    throw new GitHubProfileError('request_failed')
+
+  const profile = await response.json() as {
+    id?: unknown
+    login?: unknown
</file context>

}
}

async function confirmGitHubProfile() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The new interactive confirm flow (find profile -> preview -> save to users -> update reactive state -> error handling for save failures) in index.vue adds meaningful branching and async error paths, but the only added unit coverage targets src/services/githubProfile.ts (tests/github-profile.unit.test.ts). The dialog-level behavior — confirmGitHubProfile wiring the GitHubProfile into the users update and updating main.user/githubUsername, plus the save-failure path that surfaces t('account-error') — is untested. Adding coverage for the find/confirm/save/error flow would guard the most failure-prone part of this change.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 99:

<comment>The new interactive confirm flow (find profile -> preview -> save to users -> update reactive state -> error handling for save failures) in index.vue adds meaningful branching and async error paths, but the only added unit coverage targets src/services/githubProfile.ts (tests/github-profile.unit.test.ts). The dialog-level behavior — confirmGitHubProfile wiring the GitHubProfile into the users update and updating main.user/githubUsername, plus the save-failure path that surfaces t('account-error') — is untested. Adding coverage for the find/confirm/save/error flow would guard the most failure-prone part of this change.</comment>

<file context>
@@ -41,8 +43,87 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+  }
+}
+
+async function confirmGitHubProfile() {
+  if (!githubProfile.value || !main.user?.id || githubProfileSaving.value)
+    return
</file context>

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
src/services/githubProfile.ts (1)

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

Add a timeout to the GitHub API request.

If the request hangs, getGitHubProfile remains pending and githubProfileLoading remains true. Add signal: AbortSignal.timeout(10_000).

Map responses that indicate an exhausted GitHub rate limit to a distinct error code. Unauthenticated requests share a 60-request-per-hour limit per IP address.

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

In `@src/services/githubProfile.ts` around lines 23 - 38, Add signal:
AbortSignal.timeout(10_000) to the fetch call in getGitHubProfile so hung
requests terminate, and map GitHub responses indicating an exhausted rate limit
to the distinct rate-limit GitHubProfileError code before the generic
request_failed handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/pages/settings/account/index.vue`:
- Around line 918-927: Add an accessible label to the remove button in the
clearGitHubProfile control, using the existing t('button-remove') translation so
its accessible name remains available while the Spinner replaces the visible
text during githubProfileSaving.

---

Outside diff comments:
In `@src/services/githubProfile.ts`:
- Around line 23-38: Add signal: AbortSignal.timeout(10_000) to the fetch call
in getGitHubProfile so hung requests terminate, and map GitHub responses
indicating an exhausted rate limit to the distinct rate-limit GitHubProfileError
code before the generic request_failed handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8fb02cc8-ca06-41ec-8595-b2570459aa2e

📥 Commits

Reviewing files that changed from the base of the PR and between ea83ae4 and b9d7f0a.

📒 Files selected for processing (3)
  • src/pages/settings/account/index.vue
  • src/services/githubProfile.ts
  • tests/github-profile.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread src/pages/settings/account/index.vue

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

5 issues found across 4 files

Confidence score: 3/5

  • In src/pages/settings/account/index.vue, the save flow can race with modal dismissal/reopen, so an older in-flight save may close a newly opened dialog and confuse users about which edit actually completed — lock dismissal during save or gate close behavior to the active request instance.
  • In src/pages/settings/account/index.vue, a rejected Supabase update can bypass resetting githubProfileSaving.value, leaving the UI stuck in a perpetual saving/loading state and blocking further edits — move the reset into a finally path so it always clears.
  • In src/services/githubProfile.ts, the GitHub profile request has no timeout, so stalled network calls can leave the account dialog loading indefinitely — add a bounded abort timeout and map aborts to a handled request_failed outcome.
  • In src/services/githubProfile.ts, unauthenticated browser calls to the public GitHub API can hit the 60/hour IP limit and currently surface as generic request failures, which can look like random breakage to users — detect 403 rate-limit responses explicitly and provide a clear, actionable error path (or backoff/fallback).
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/pages/settings/account/index.vue">

<violation number="1" location="src/pages/settings/account/index.vue:75">
P2: A user can dismiss the modal while GitHub data is being saved, then reopen it and have the stale save close the newly opened dialog when its request finishes. The save operation should either prevent accidental dismissal while in flight or carry a dialog/request token and only close the dialog that started it.</violation>

<violation number="2" location="src/pages/settings/account/index.vue:114">
P2: If the Supabase update rejects (for example a dropped network connection during the request), the awaited promise throws before the `githubProfileSaving.value = false` line runs, leaving the flag stuck at `true`. The dialog then stays permanently disabled with the spinner showing and no error surfaced, so the user cannot retry, cancel (Cancel is also disabled by `githubProfileSaving`), or leave. Wrap the update in try/catch/finally (or a `finally` block) so the loading flag is always reset and a failure is reported via `githubProfileError`, matching how `findGitHubProfile` handles errors.</violation>
</file>

<file name="src/services/githubProfile.ts">

<violation number="1" location="src/services/githubProfile.ts:20">
P3: Inputs containing consecutive hyphens are treated as syntactically valid, so users receive “profile not found” for a username that is invalid before lookup. Adding a consecutive-hyphen guard to this validation keeps the error code and UI guidance accurate.</violation>

<violation number="2" location="src/services/githubProfile.ts:25">
P2: A stalled GitHub connection can leave the account dialog stuck in its loading state indefinitely because this request has no deadline. Applying a bounded abort timeout and mapping the abort to `request_failed` would let users recover and retry.</violation>

<violation number="3" location="src/services/githubProfile.ts:25">
P2: This lookup hits the public GitHub API unauthenticated from the browser, which is limited to 60 requests/hour per IP. Once that shared budget is exhausted GitHub returns 403, and the code classifies it as `request_failed`, so a valid username can intermittently fail with a confusing "could not load that profile" even though nothing is wrong. Consider routing profile verification through the backend with a token (and a cache), or at least detecting the 403/x-ratelimit-reset case and surfacing a clearer, less misleading message instead of a generic failure.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

title: t('github-username'),
description: t('github-username-dialog-description'),
size: 'sm',
buttons: [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A user can dismiss the modal while GitHub data is being saved, then reopen it and have the stale save close the newly opened dialog when its request finishes. The save operation should either prevent accidental dismissal while in flight or carry a dialog/request token and only close the dialog that started it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 75:

<comment>A user can dismiss the modal while GitHub data is being saved, then reopen it and have the stale save close the newly opened dialog when its request finishes. The save operation should either prevent accidental dismissal while in flight or carry a dialog/request token and only close the dialog that started it.</comment>

<file context>
@@ -41,8 +43,127 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+    title: t('github-username'),
+    description: t('github-username-dialog-description'),
+    size: 'sm',
+    buttons: [],
+  })
+}
</file context>

Comment thread src/services/githubProfile.ts Outdated

let response: Response
try {
response = await fetch(`https://api.github.com/users/${encodeURIComponent(normalizedUsername)}`, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: A stalled GitHub connection can leave the account dialog stuck in its loading state indefinitely because this request has no deadline. Applying a bounded abort timeout and mapping the abort to request_failed would let users recover and retry.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 25:

<comment>A stalled GitHub connection can leave the account dialog stuck in its loading state indefinitely because this request has no deadline. Applying a bounded abort timeout and mapping the abort to `request_failed` would let users recover and retry.</comment>

<file context>
@@ -0,0 +1,62 @@
+
+  let response: Response
+  try {
+    response = await fetch(`https://api.github.com/users/${encodeURIComponent(normalizedUsername)}`, {
+      headers: {
+        accept: 'application/vnd.github+json',
</file context>

Comment thread src/services/githubProfile.ts Outdated

let response: Response
try {
response = await fetch(`https://api.github.com/users/${encodeURIComponent(normalizedUsername)}`, {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This lookup hits the public GitHub API unauthenticated from the browser, which is limited to 60 requests/hour per IP. Once that shared budget is exhausted GitHub returns 403, and the code classifies it as request_failed, so a valid username can intermittently fail with a confusing "could not load that profile" even though nothing is wrong. Consider routing profile verification through the backend with a token (and a cache), or at least detecting the 403/x-ratelimit-reset case and surfacing a clearer, less misleading message instead of a generic failure.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 25:

<comment>This lookup hits the public GitHub API unauthenticated from the browser, which is limited to 60 requests/hour per IP. Once that shared budget is exhausted GitHub returns 403, and the code classifies it as `request_failed`, so a valid username can intermittently fail with a confusing "could not load that profile" even though nothing is wrong. Consider routing profile verification through the backend with a token (and a cache), or at least detecting the 403/x-ratelimit-reset case and surfacing a clearer, less misleading message instead of a generic failure.</comment>

<file context>
@@ -0,0 +1,62 @@
+
+  let response: Response
+  try {
+    response = await fetch(`https://api.github.com/users/${encodeURIComponent(normalizedUsername)}`, {
+      headers: {
+        accept: 'application/vnd.github+json',
</file context>

if (!githubProfile.value || !main.user?.id || githubProfileSaving.value)
return

githubProfileSaving.value = true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: If the Supabase update rejects (for example a dropped network connection during the request), the awaited promise throws before the githubProfileSaving.value = false line runs, leaving the flag stuck at true. The dialog then stays permanently disabled with the spinner showing and no error surfaced, so the user cannot retry, cancel (Cancel is also disabled by githubProfileSaving), or leave. Wrap the update in try/catch/finally (or a finally block) so the loading flag is always reset and a failure is reported via githubProfileError, matching how findGitHubProfile handles errors.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 114:

<comment>If the Supabase update rejects (for example a dropped network connection during the request), the awaited promise throws before the `githubProfileSaving.value = false` line runs, leaving the flag stuck at `true`. The dialog then stays permanently disabled with the spinner showing and no error surfaced, so the user cannot retry, cancel (Cancel is also disabled by `githubProfileSaving`), or leave. Wrap the update in try/catch/finally (or a `finally` block) so the loading flag is always reset and a failure is reported via `githubProfileError`, matching how `findGitHubProfile` handles errors.</comment>

<file context>
@@ -41,8 +43,127 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+  if (!githubProfile.value || !main.user?.id || githubProfileSaving.value)
+    return
+
+  githubProfileSaving.value = true
+  const { data: user, error } = await supabase
+    .from('users')
</file context>

Comment thread src/services/githubProfile.ts Outdated

export async function getGitHubProfile(username: string): Promise<GitHubProfile> {
const normalizedUsername = normalizeGitHubUsername(username)
if (!/^[a-z\d](?:[a-z\d-]{0,37}[a-z\d])?$/i.test(normalizedUsername))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Inputs containing consecutive hyphens are treated as syntactically valid, so users receive “profile not found” for a username that is invalid before lookup. Adding a consecutive-hyphen guard to this validation keeps the error code and UI guidance accurate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 20:

<comment>Inputs containing consecutive hyphens are treated as syntactically valid, so users receive “profile not found” for a username that is invalid before lookup. Adding a consecutive-hyphen guard to this validation keeps the error code and UI guidance accurate.</comment>

<file context>
@@ -0,0 +1,62 @@
+
+export async function getGitHubProfile(username: string): Promise<GitHubProfile> {
+  const normalizedUsername = normalizeGitHubUsername(username)
+  if (!/^[a-z\d](?:[a-z\d-]{0,37}[a-z\d])?$/i.test(normalizedUsername))
+    throw new GitHubProfileError('invalid_username')
+
</file context>

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
tests/github-profile.unit.test.ts (1)

30-38: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add rate-limit boundary tests.

The current test covers only a failed 403 with x-ratelimit-remaining: 0. Add these cases:

  • A valid 200 response with x-ratelimit-remaining: 0 must resolve successfully.
  • A 429 response with Retry-After must reject with rate_limited.

These cases protect the status-aware handling in src/services/githubProfile.ts. (docs.github.com)

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

In `@tests/github-profile.unit.test.ts` around lines 30 - 38, Extend the
getGitHubProfile rate-limit tests with a successful 200 response whose
x-ratelimit-remaining is 0, asserting it resolves normally, and a 429 response
including a Retry-After header, asserting it rejects with code rate_limited.
Keep the existing 403 coverage unchanged.

Source: MCP tools

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

Inline comments:
In `@src/services/githubProfile.ts`:
- Around line 38-41: The response classification in the GitHub profile request
must be status-aware: update the error handling around the response checks to
inspect rate-limit headers only for non-OK responses, classifying 429 and
rate-limited 403 responses—including Retry-After—as rate_limited while
preserving request_failed for other failures. In
tests/github-profile.unit.test.ts lines 30-38, add coverage for a 200 response
with x-ratelimit-remaining: 0 and a 429 response with Retry-After, asserting the
respective classifications.

---

Outside diff comments:
In `@tests/github-profile.unit.test.ts`:
- Around line 30-38: Extend the getGitHubProfile rate-limit tests with a
successful 200 response whose x-ratelimit-remaining is 0, asserting it resolves
normally, and a 429 response including a Retry-After header, asserting it
rejects with code rate_limited. Keep the existing 403 coverage unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c3d9d829-0e3a-4dd7-b9b1-cb686687433b

📥 Commits

Reviewing files that changed from the base of the PR and between b9d7f0a and 120fd2a.

📒 Files selected for processing (4)
  • messages/en.json
  • src/pages/settings/account/index.vue
  • src/services/githubProfile.ts
  • tests/github-profile.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)

Comment thread src/services/githubProfile.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found and verified against the latest diff

Confidence score: 3/5

  • In src/services/githubProfile.ts, the profile fetch path relies on AbortSignal.timeout, which is unavailable on supported iOS 15 and can throw before the request starts, causing GitHub lookup to fail for those users; this is a concrete user-facing regression risk — add a feature guard and fallback to AbortController + setTimeout when AbortSignal.timeout is missing.
  • In src/pages/settings/account/index.vue, the dialog save completion appears to close the currently open modal even if a newer dialog instance was reopened, so an earlier in-flight request can interrupt current user work and create confusing UI state — scope close behavior to the originating request/dialog instance (or ignore stale completions).
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/pages/settings/account/index.vue">

<violation number="1" location="src/pages/settings/account/index.vue:135">
P2: Reopening the GitHub dialog while a confirmation save is still in flight can be interrupted by the previous request: its completion unconditionally closes whichever dialog is currently open. The save flow would be safer if it tracked the originating dialog/request and only closed that instance, or otherwise prevented stale completions from mutating the current dialog.</violation>
</file>

<file name="src/services/githubProfile.ts">

<violation number="1" location="src/services/githubProfile.ts:29">
P2: GitHub profile lookup always fails on supported iOS 15 devices because `AbortSignal.timeout` is unavailable there and throws before the request starts. A guarded timeout or `AbortController`/`setTimeout` fallback would preserve the lookup on the declared iOS 15 target.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

main.user = user
githubUsername.value = user.github_username ?? ''
toast.success(t('account-updated-succ'))
dialogStore.closeDialog({ text: t('confirm'), role: 'primary' })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Reopening the GitHub dialog while a confirmation save is still in flight can be interrupted by the previous request: its completion unconditionally closes whichever dialog is currently open. The save flow would be safer if it tracked the originating dialog/request and only closed that instance, or otherwise prevented stale completions from mutating the current dialog.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 135:

<comment>Reopening the GitHub dialog while a confirmation save is still in flight can be interrupted by the previous request: its completion unconditionally closes whichever dialog is currently open. The save flow would be safer if it tracked the originating dialog/request and only closed that instance, or otherwise prevented stale completions from mutating the current dialog.</comment>

<file context>
@@ -41,8 +43,127 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+  main.user = user
+  githubUsername.value = user.github_username ?? ''
+  toast.success(t('account-updated-succ'))
+  dialogStore.closeDialog({ text: t('confirm'), role: 'primary' })
+  resetGitHubProfileDialog()
+}
</file context>

Comment thread src/services/githubProfile.ts Outdated
headers: {
accept: 'application/vnd.github+json',
},
signal: AbortSignal.timeout(10_000),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: GitHub profile lookup always fails on supported iOS 15 devices because AbortSignal.timeout is unavailable there and throws before the request starts. A guarded timeout or AbortController/setTimeout fallback would preserve the lookup on the declared iOS 15 target.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 29:

<comment>GitHub profile lookup always fails on supported iOS 15 devices because `AbortSignal.timeout` is unavailable there and throws before the request starts. A guarded timeout or `AbortController`/`setTimeout` fallback would preserve the lookup on the declared iOS 15 target.</comment>

<file context>
@@ -0,0 +1,65 @@
+      headers: {
+        accept: 'application/vnd.github+json',
+      },
+      signal: AbortSignal.timeout(10_000),
+    })
+  }
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found and verified against the latest diff

Confidence score: 2/5

  • In src/pages/settings/account/index.vue, the async GitHub save/remove flow can write main.user = user after logout or account switch, so a stale response may restore the wrong account in global state and cause cross-session identity confusion for users — gate the store update by validating the initiating user/request (or cancel/ignore outdated responses).
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/pages/settings/account/index.vue">

<violation number="1" location="src/pages/settings/account/index.vue:133">
P1: A delayed GitHub save or remove response can restore the previous account into the global store after logout or an account switch because `main.user = user` runs without checking which user started the request. Capturing the initiating user ID and discarding the response when the current store/auth identity differs would prevent this stale async write in both handlers.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

return
}

main.user = user

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: A delayed GitHub save or remove response can restore the previous account into the global store after logout or an account switch because main.user = user runs without checking which user started the request. Capturing the initiating user ID and discarding the response when the current store/auth identity differs would prevent this stale async write in both handlers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 132:

<comment>A delayed GitHub save or remove response can restore the previous account into the global store after logout or an account switch because `main.user = user` runs without checking which user started the request. Capturing the initiating user ID and discarding the response when the current store/auth identity differs would prevent this stale async write in both handlers.</comment>

<file context>
@@ -41,8 +43,127 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+    return
+  }
+
+  main.user = user
+  githubUsername.value = user.github_username ?? ''
+  toast.success(t('account-updated-succ'))
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

4 issues found across 4 files

Confidence score: 3/5

  • In src/services/githubProfile.ts, the non-AbortSignal.timeout fallback clears the 10s timer after fetch() resolves headers, so response.json() can still hang on a stalled body and leave GitHub lookup/save stuck for users—keep the timeout active through full body read (or race the entire request+parse path) before clearing it.
  • In src/pages/settings/account/index.vue, the dialog can still close via X/backdrop/Escape while githubProfileSaving is true, which can let an in-flight save finish against a dismissed UI and cause confusing state or duplicate retries—disable all close paths during save or guard close handlers on the saving flag.
  • In src/services/githubProfile.ts, username validation currently allows consecutive hyphens, so invalid input may be reported as unavailable/not found instead of invalid format, leading to misleading feedback—tighten the regex to reject consecutive hyphens before lookup.
  • In src/pages/settings/account/index.vue, githubUsername being a one-time ref snapshot instead of store-reactive can drift from current account data and undermine the intended sync behavior—bind it reactively to the store source (or watch and resync explicitly).
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/pages/settings/account/index.vue">

<violation number="1" location="src/pages/settings/account/index.vue:46">
P3: The GitHub username field is now a one-time snapshot (`const githubUsername = ref(main.user?.github_username ?? '')`) instead of a reactive binding to the store, which is the opposite of the PR's goal of keeping the account field reactive. Every sibling field on this form (country, discord_username, etc.) still binds with `:value="main.user?.xxx"` so it stays in sync when the store's `user` populates or changes, but the GitHub field reads `main.user` only at setup time and never tracks it afterward. If user data is loaded asynchronously in the app bootstrap, the field can render blank on first load and won't reflect later `main.user` updates until the confirm handler manually sets `githubUsername`. Consider deriving the field from the store (e.g. a `computed` that reads `main.user?.github_username`) while keeping the v-model for user edits after confirmation.</violation>

<violation number="2" location="src/pages/settings/account/index.vue:75">
P2: The profile dialog can still be dismissed with the X button, backdrop, or Escape while a GitHub save is in flight because only the custom buttons honor `githubProfileSaving`; the pending request then continues and can update the account after a new dialog has opened. Prevent accidental dismissal for this confirmation dialog (or otherwise invalidate/cancel the pending operation when it closes) so the dialog lifecycle stays consistent with the async save.</violation>
</file>

<file name="src/services/githubProfile.ts">

<violation number="1" location="src/services/githubProfile.ts:20">
P2: Consecutive-hyphen input passes local validation and can be surfaced as an unavailable/not-found lookup instead of the intended invalid-username message. Reject consecutive hyphens in this regex (for example, use a pattern where every hyphen must be followed by an alphanumeric character).</violation>

<violation number="2" location="src/services/githubProfile.ts:44">
P2: On runtimes without `AbortSignal.timeout`, the 10-second fallback is cleared as soon as `fetch()` resolves headers, before `response.json()` reads the body. A stalled GitHub response body can leave the lookup pending indefinitely; keep the fallback timer active through body parsing and clear it in an outer `finally`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

title: t('github-username'),
description: t('github-username-dialog-description'),
size: 'sm',
buttons: [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The profile dialog can still be dismissed with the X button, backdrop, or Escape while a GitHub save is in flight because only the custom buttons honor githubProfileSaving; the pending request then continues and can update the account after a new dialog has opened. Prevent accidental dismissal for this confirmation dialog (or otherwise invalidate/cancel the pending operation when it closes) so the dialog lifecycle stays consistent with the async save.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 75:

<comment>The profile dialog can still be dismissed with the X button, backdrop, or Escape while a GitHub save is in flight because only the custom buttons honor `githubProfileSaving`; the pending request then continues and can update the account after a new dialog has opened. Prevent accidental dismissal for this confirmation dialog (or otherwise invalidate/cancel the pending operation when it closes) so the dialog lifecycle stays consistent with the async save.</comment>

<file context>
@@ -41,8 +43,131 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+    title: t('github-username'),
+    description: t('github-username-dialog-description'),
+    size: 'sm',
+    buttons: [],
+  })
+}
</file context>
Suggested change
buttons: [],
buttons: [],
preventAccidentalClose: true,

Comment thread src/services/githubProfile.ts Outdated

export async function getGitHubProfile(username: string): Promise<GitHubProfile> {
const normalizedUsername = normalizeGitHubUsername(username)
if (!/^[a-z\d](?:[a-z\d-]{0,37}[a-z\d])?$/i.test(normalizedUsername))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Consecutive-hyphen input passes local validation and can be surfaced as an unavailable/not-found lookup instead of the intended invalid-username message. Reject consecutive hyphens in this regex (for example, use a pattern where every hyphen must be followed by an alphanumeric character).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 20:

<comment>Consecutive-hyphen input passes local validation and can be surfaced as an unavailable/not-found lookup instead of the intended invalid-username message. Reject consecutive hyphens in this regex (for example, use a pattern where every hyphen must be followed by an alphanumeric character).</comment>

<file context>
@@ -0,0 +1,77 @@
+
+export async function getGitHubProfile(username: string): Promise<GitHubProfile> {
+  const normalizedUsername = normalizeGitHubUsername(username)
+  if (!/^[a-z\d](?:[a-z\d-]{0,37}[a-z\d])?$/i.test(normalizedUsername))
+    throw new GitHubProfileError('invalid_username')
+
</file context>

throw new GitHubProfileError('request_failed')
}
finally {
if (timeout)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: On runtimes without AbortSignal.timeout, the 10-second fallback is cleared as soon as fetch() resolves headers, before response.json() reads the body. A stalled GitHub response body can leave the lookup pending indefinitely; keep the fallback timer active through body parsing and clear it in an outer finally.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 44:

<comment>On runtimes without `AbortSignal.timeout`, the 10-second fallback is cleared as soon as `fetch()` resolves headers, before `response.json()` reads the body. A stalled GitHub response body can leave the lookup pending indefinitely; keep the fallback timer active through body parsing and clear it in an outer `finally`.</comment>

<file context>
@@ -0,0 +1,77 @@
+    throw new GitHubProfileError('request_failed')
+  }
+  finally {
+    if (timeout)
+      clearTimeout(timeout)
+  }
</file context>

const organizationsToDelete = ref<string[]>([])
const formatLocaleOptions = computed(() => getFormatLocaleOptions(locale.value))
const paidOrganizationsToDelete = ref<Array<{ name: string, planName: string }>>([])
const githubUsername = ref(main.user?.github_username ?? '')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The GitHub username field is now a one-time snapshot (const githubUsername = ref(main.user?.github_username ?? '')) instead of a reactive binding to the store, which is the opposite of the PR's goal of keeping the account field reactive. Every sibling field on this form (country, discord_username, etc.) still binds with :value="main.user?.xxx" so it stays in sync when the store's user populates or changes, but the GitHub field reads main.user only at setup time and never tracks it afterward. If user data is loaded asynchronously in the app bootstrap, the field can render blank on first load and won't reflect later main.user updates until the confirm handler manually sets githubUsername. Consider deriving the field from the store (e.g. a computed that reads main.user?.github_username) while keeping the v-model for user edits after confirmation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 46:

<comment>The GitHub username field is now a one-time snapshot (`const githubUsername = ref(main.user?.github_username ?? '')`) instead of a reactive binding to the store, which is the opposite of the PR's goal of keeping the account field reactive. Every sibling field on this form (country, discord_username, etc.) still binds with `:value="main.user?.xxx"` so it stays in sync when the store's `user` populates or changes, but the GitHub field reads `main.user` only at setup time and never tracks it afterward. If user data is loaded asynchronously in the app bootstrap, the field can render blank on first load and won't reflect later `main.user` updates until the confirm handler manually sets `githubUsername`. Consider deriving the field from the store (e.g. a `computed` that reads `main.user?.github_username`) while keeping the v-model for user edits after confirmation.</comment>

<file context>
@@ -41,8 +43,131 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
 const organizationsToDelete = ref<string[]>([])
 const formatLocaleOptions = computed(() => getFormatLocaleOptions(locale.value))
 const paidOrganizationsToDelete = ref<Array<{ name: string, planName: string }>>([])
+const githubUsername = ref(main.user?.github_username ?? '')
+const githubUsernameInput = ref('')
+const githubProfile = ref<GitHubProfile | null>(null)
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 4 files

Confidence score: 4/5

  • In src/services/githubProfile.ts, the fallback timeout is cleared right after fetch resolves, so if response.json() stalls the request can hang indefinitely and leave profile lookups stuck — keep the timeout active until body parsing completes (or wrap full fetch+parse in one abortable window).
  • In src/services/githubProfile.ts, username validation currently accepts patterns like foo--bar, which routes users into the unknown-profile flow instead of the invalid-username message and weakens UX/error accuracy — tighten the hyphen rule so each hyphen is followed by an alphanumeric character.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/services/githubProfile.ts">

<violation number="1" location="src/services/githubProfile.ts:20">
P2: Inputs such as `foo--bar` are treated as valid and sent to GitHub, so users receive the unknown-profile path instead of the invalid-username message. Restrict hyphens to positions followed by an alphanumeric character.</violation>

<violation number="2" location="src/services/githubProfile.ts:44">
P2: When `AbortSignal.timeout` is unavailable, the 10-second fallback timer is cleared as soon as `fetch` resolves, before `response.json()` consumes the body. A stalled response body can therefore leave the lookup hanging indefinitely; keep the fallback timer alive through JSON parsing.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

throw new GitHubProfileError('request_failed')
}
finally {
if (timeout)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When AbortSignal.timeout is unavailable, the 10-second fallback timer is cleared as soon as fetch resolves, before response.json() consumes the body. A stalled response body can therefore leave the lookup hanging indefinitely; keep the fallback timer alive through JSON parsing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 44:

<comment>When `AbortSignal.timeout` is unavailable, the 10-second fallback timer is cleared as soon as `fetch` resolves, before `response.json()` consumes the body. A stalled response body can therefore leave the lookup hanging indefinitely; keep the fallback timer alive through JSON parsing.</comment>

<file context>
@@ -0,0 +1,77 @@
+    throw new GitHubProfileError('request_failed')
+  }
+  finally {
+    if (timeout)
+      clearTimeout(timeout)
+  }
</file context>

Comment thread src/services/githubProfile.ts Outdated

export async function getGitHubProfile(username: string): Promise<GitHubProfile> {
const normalizedUsername = normalizeGitHubUsername(username)
if (!/^[a-z\d](?:[a-z\d-]{0,37}[a-z\d])?$/i.test(normalizedUsername))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Inputs such as foo--bar are treated as valid and sent to GitHub, so users receive the unknown-profile path instead of the invalid-username message. Restrict hyphens to positions followed by an alphanumeric character.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 20:

<comment>Inputs such as `foo--bar` are treated as valid and sent to GitHub, so users receive the unknown-profile path instead of the invalid-username message. Restrict hyphens to positions followed by an alphanumeric character.</comment>

<file context>
@@ -0,0 +1,77 @@
+
+export async function getGitHubProfile(username: string): Promise<GitHubProfile> {
+  const normalizedUsername = normalizeGitHubUsername(username)
+  if (!/^[a-z\d](?:[a-z\d-]{0,37}[a-z\d])?$/i.test(normalizedUsername))
+    throw new GitHubProfileError('invalid_username')
+
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 4 files

Confidence score: 4/5

  • In src/pages/settings/account/index.vue, dismissing the dialog via backdrop/X/Escape while a GitHub save is in flight can skip cleanup and leave the next open stuck in a perpetual saving state, which can block account updates for affected users — disable those close paths during save or route all dismiss paths through the same reset logic.
  • In src/services/githubProfile.ts, the username validation regex rejects valid one-character GitHub usernames, so some users will be incorrectly prevented from saving their profile — relax the pattern to allow length 1 while keeping the existing character rules.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/pages/settings/account/index.vue">

<violation number="1" location="src/pages/settings/account/index.vue:75">
P2: Closing this dialog through the backdrop, X, or Escape during a GitHub save can leave the reopened dialog stuck in its saving state because those paths bypass the local cleanup. Prevent accidental dismissal for this dialog (the custom Cancel button still provides the intentional close path), or centralize cleanup in the dialog-close lifecycle.</violation>
</file>

<file name="src/services/githubProfile.ts">

<violation number="1" location="src/services/githubProfile.ts:20">
P3: A user whose GitHub username is exactly one character (valid on GitHub, min length is 1) will be told their username is invalid, because this regex forces a minimum of two characters (a mandatory leading and trailing `[a-z\d]`). The lookahead-based pattern otherwise correctly enforces the 1-39 char and hyphen rules, so this is just the lower-bound check being too strict. Consider allowing a single-character match for genuine 1-char usernames, e.g. `/^[a-z\d](?:(?!.*--)(?:[a-z\d]-?)*)?/i` style, or simply special-casing `normalizedUsername.length === 1` while keeping the rest of the regex.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

title: t('github-username'),
description: t('github-username-dialog-description'),
size: 'sm',
buttons: [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Closing this dialog through the backdrop, X, or Escape during a GitHub save can leave the reopened dialog stuck in its saving state because those paths bypass the local cleanup. Prevent accidental dismissal for this dialog (the custom Cancel button still provides the intentional close path), or centralize cleanup in the dialog-close lifecycle.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/settings/account/index.vue, line 75:

<comment>Closing this dialog through the backdrop, X, or Escape during a GitHub save can leave the reopened dialog stuck in its saving state because those paths bypass the local cleanup. Prevent accidental dismissal for this dialog (the custom Cancel button still provides the intentional close path), or centralize cleanup in the dialog-close lifecycle.</comment>

<file context>
@@ -41,8 +43,139 @@ const captchaKey = ref(import.meta.env.VITE_CAPTCHA_KEY)
+    title: t('github-username'),
+    description: t('github-username-dialog-description'),
+    size: 'sm',
+    buttons: [],
+  })
+}
</file context>

Comment thread src/services/githubProfile.ts Outdated

export async function getGitHubProfile(username: string): Promise<GitHubProfile> {
const normalizedUsername = normalizeGitHubUsername(username)
if (!/^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,37}[a-z\d]$/i.test(normalizedUsername))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: A user whose GitHub username is exactly one character (valid on GitHub, min length is 1) will be told their username is invalid, because this regex forces a minimum of two characters (a mandatory leading and trailing [a-z\d]). The lookahead-based pattern otherwise correctly enforces the 1-39 char and hyphen rules, so this is just the lower-bound check being too strict. Consider allowing a single-character match for genuine 1-char usernames, e.g. /^[a-z\d](?:(?!.*--)(?:[a-z\d]-?)*)?/i style, or simply special-casing normalizedUsername.length === 1 while keeping the rest of the regex.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/services/githubProfile.ts, line 20:

<comment>A user whose GitHub username is exactly one character (valid on GitHub, min length is 1) will be told their username is invalid, because this regex forces a minimum of two characters (a mandatory leading and trailing `[a-z\d]`). The lookahead-based pattern otherwise correctly enforces the 1-39 char and hyphen rules, so this is just the lower-bound check being too strict. Consider allowing a single-character match for genuine 1-char usernames, e.g. `/^[a-z\d](?:(?!.*--)(?:[a-z\d]-?)*)?/i` style, or simply special-casing `normalizedUsername.length === 1` while keeping the rest of the regex.</comment>

<file context>
@@ -0,0 +1,70 @@
+
+export async function getGitHubProfile(username: string): Promise<GitHubProfile> {
+  const normalizedUsername = normalizeGitHubUsername(username)
+  if (!/^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,37}[a-z\d]$/i.test(normalizedUsername))
+    throw new GitHubProfileError('invalid_username')
+
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 4 files

Confidence score: 5/5

  • In messages/en.json, the new github-username-reject key is currently unused in the dialog flow, which creates dead translation content and can confuse future localization/maintenance work — either wire it to an actual reject action in the UI or remove the key until it’s needed.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="messages/en.json">

<violation number="1" location="messages/en.json:1305">
P3: The new `github-username-reject` ('No') translation key isn't referenced anywhere in the dialog UI — the only confirm action is `github-username-confirm` ('Yes') plus the existing Cancel/Remove buttons. This is a dead translation string; consider removing it or wiring it to an explicit decline action so the locale catalog doesn't accumulate unused entries.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread messages/en.json Outdated
"github-username-error-request_failed": "We could not load that GitHub profile. Please try again.",
"github-username-help": "Your username without the at sign.",
"github-username-placeholder": "octocat",
"github-username-reject": "No",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The new github-username-reject ('No') translation key isn't referenced anywhere in the dialog UI — the only confirm action is github-username-confirm ('Yes') plus the existing Cancel/Remove buttons. This is a dead translation string; consider removing it or wiring it to an explicit decline action so the locale catalog doesn't accumulate unused entries.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At messages/en.json, line 1305:

<comment>The new `github-username-reject` ('No') translation key isn't referenced anywhere in the dialog UI — the only confirm action is `github-username-confirm` ('Yes') plus the existing Cancel/Remove buttons. This is a dead translation string; consider removing it or wiring it to an explicit decline action so the locale catalog doesn't accumulate unused entries.</comment>

<file context>
@@ -1293,7 +1293,17 @@
+  "github-username-error-request_failed": "We could not load that GitHub profile. Please try again.",
   "github-username-help": "Your username without the at sign.",
+  "github-username-placeholder": "octocat",
+  "github-username-reject": "No",
+  "github-username-select": "Select a GitHub username",
   "global": "Global",
</file context>

@sonarqubecloud

sonarqubecloud Bot commented Aug 1, 2026

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant