Skip to content

feat(desktop): connection error diagnostic panel#35

Merged
hqhq1025 merged 7 commits intomainfrom
wt/ux-api-troubleshoot
Apr 19, 2026
Merged

feat(desktop): connection error diagnostic panel#35
hqhq1025 merged 7 commits intomainfrom
wt/ux-api-troubleshoot

Conversation

@hqhq1025
Copy link
Copy Markdown
Collaborator

Summary

  • Implements §8.2 of the UX master plan (docs/research/18-ux-master-plan.md): replaces the single-line error hint with a structured diagnostic panel embedded below the provider form.
  • packages/shared/src/diagnostics.ts: pure diagnose(code, ctx) function maps error codes (401/403, 402, 404, 429, ECONNREFUSED, ETIMEDOUT, CORS, SSL) to DiagnosticHypothesis objects with i18n-keyed cause strings and optional fix descriptors (baseUrlTransform or externalUrl).
  • ConnectionDiagnosticPanel.tsx: self-contained React component showing status, attempted URL, most-likely cause, suggested URL, and three action buttons — Apply this fix (runs baseUrlTransform, re-fills baseUrl), Test again (re-triggers validation), Show full log (opens logs folder via existing settings:v1:open-folder IPC).
  • Settings.tsx: AddProviderFormState gains errorCode: ErrorCode | null; applyValidateResult accepts an optional errorCode arg; validation result now stores the code so the panel can render.
  • i18n en + zh-CN: ~10 new keys under diagnostics.* namespace.
  • 12 new unit tests in diagnostics.test.ts covering all mapped codes.

Checklist (§7 PRINCIPLES)

  • Compatibility ✅ — no API changes, new field is additive
  • Upgradeability ✅ — no disk schema touched
  • No bloat ✅ — zero new deps; diagnose is a pure function
  • Elegance ✅ — panel reusable; error → hypothesis mapping is a plain lookup

Test plan

  • Run pnpm test — all 85 desktop + 27 shared tests pass
  • Run pnpm typecheck — no errors
  • Run pnpm lint — 0 errors (8 pre-existing warnings unchanged)
  • In-app: add provider with a wrong key → validate → error panel shows with "Update key" CTA
  • In-app: add provider with a baseUrl missing /v1 → validate → panel shows "Add /v1" → click → baseUrl field updates → test again succeeds
  • "Show full log" button opens the logs folder in Finder/Explorer

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Findings

  • [Blocker] Silent error swallow in log-opening flow violates the no-silent-fallback rule — handleShowLog catches and ignores failures, so users get no feedback when opening logs fails, evidence apps/desktop/src/renderer/src/components/ConnectionDiagnosticPanel.tsx:63
    Suggested fix:

    async function handleShowLog() {
      if (!logsPath || !window.codesign) return;
      try {
        await window.codesign.settings.openFolder(logsPath);
      } catch (err) {
        // Surface to UI instead of silently swallowing.
        const message = err instanceof Error ? err.message : 'Failed to open logs folder';
        // e.g. set local panel state and render the message
        setLocalError(message);
      }
    }
  • [Major] Provider-specific billing action points to OpenAI for all providers — 402 handling always opens OpenAI billing URL, which is incorrect for Anthropic/OpenRouter and can send users to the wrong remediation path, evidence packages/shared/src/diagnostics.ts:64
    Suggested fix:

    function billingUrlFor(provider: string): string | undefined {
      if (provider === 'openai') return 'https://platform.openai.com/settings/organization/billing';
      if (provider === 'anthropic') return 'https://console.anthropic.com/settings/billing';
      if (provider === 'openrouter') return 'https://openrouter.ai/settings/credits';
      return undefined;
    }
    
    // in 402 branch
    suggestedFix: {
      label: 'diagnostics.fix.addCredits',
      ...(billingUrlFor(ctx.provider) ? { externalUrl: billingUrlFor(ctx.provider)! } : {}),
    }
  • [Minor] Hardcoded English UI string bypasses i18n and breaks locale consistency — panel title/body is localized, but “Most likely cause” is hardcoded and won’t translate, evidence apps/desktop/src/renderer/src/components/ConnectionDiagnosticPanel.tsx:111
    Suggested fix:

    <span className="font-medium text-[var(--color-text-primary)]">
      {t('diagnostics.mostLikelyCause')}{' '}
    </span>

Summary

  • Review mode: initial
  • 3 issues found in the latest diff: 1 blocker (silent fallback), 1 major (provider-incorrect billing link), 1 minor (missing i18n on new label).

Testing

  • Not run (automation)
  • Suggested tests:
    1. ConnectionDiagnosticPanel test for openFolder rejection asserting visible error state.
    2. diagnose('402', { provider }) table test asserting provider-specific externalUrl.
    3. i18n rendering test asserting diagnostics.mostLikelyCause is used instead of hardcoded text.

open-codesign Bot

if (!logsPath || !window.codesign) return;
try {
await window.codesign.settings.openFolder(logsPath);
} catch {
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.

[Blocker] This catch silently swallows failures from openFolder, which violates the project constraint "No silent fallbacks" and leaves users with no feedback. Please surface the error in UI (or rethrow with context) instead of ignoring it.

Suggested fix:

} catch (err) {
  const message = err instanceof Error ? err.message : 'Failed to open logs folder';
  setLocalError(message);
}

Comment thread packages/shared/src/diagnostics.ts Outdated
cause: 'diagnostics.cause.balanceEmpty',
suggestedFix: {
label: 'diagnostics.fix.addCredits',
externalUrl: 'https://platform.openai.com/settings/organization/billing',
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.

[Major] The 402 remediation URL is hardcoded to OpenAI billing regardless of provider. For Anthropic/OpenRouter this points users to the wrong place and can block self-recovery.

Suggested fix:

function billingUrlFor(provider: string): string | undefined {
  if (provider === 'openai') return 'https://platform.openai.com/settings/organization/billing';
  if (provider === 'anthropic') return 'https://console.anthropic.com/settings/billing';
  if (provider === 'openrouter') return 'https://openrouter.ai/settings/credits';
  return undefined;
}

{primary !== undefined && (
<p className="mt-2">
<span className="font-medium text-[var(--color-text-primary)]">
Most likely cause:{' '}
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.

[Minor] Most likely cause: is hardcoded English while surrounding strings use i18n. This causes mixed-language UI in non-English locales.

Suggested fix:

<span className="font-medium text-[var(--color-text-primary)]">
  {t('diagnostics.mostLikelyCause')}{' '}
</span>

Also add diagnostics.mostLikelyCause to both en.json and zh-CN.json.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Findings

  • [Blocker] Silent fallback when resolving app paths violates the no-silent-fallback rule — failures from getPaths() are swallowed and never surfaced to UI or thrown, evidence apps/desktop/src/renderer/src/components/Settings.tsx:267.
    Suggested fix:

    const pushToast = useCodesignStore((s) => s.pushToast);
    
    useEffect(() => {
      if (!window.codesign) return;
      void window.codesign.settings
        .getPaths()
        .then((p) => setLogsFolder(p.logsFolder))
        .catch((err) => {
          const message = err instanceof Error ? err.message : 'Failed to load app paths';
          pushToast({
            variant: 'error',
            title: t('diagnostics.showLogFailed'),
            description: message,
          });
        });
    }, [pushToast, t]);
  • [Major] 402 remediation link is hardcoded to OpenAI billing for all providers — for Anthropic/OpenRouter this routes users to the wrong action path, evidence packages/shared/src/diagnostics.ts:64.
    Suggested fix:

    function billingUrlFor(provider: string): string | undefined {
      if (provider === 'openai') return 'https://platform.openai.com/settings/organization/billing';
      if (provider === 'anthropic') return 'https://console.anthropic.com/settings/billing';
      if (provider === 'openrouter') return 'https://openrouter.ai/settings/credits';
      return undefined;
    }
    
    const externalUrl = billingUrlFor(ctx.provider);
    suggestedFix: {
      label: 'diagnostics.fix.addCredits',
      ...(externalUrl ? { externalUrl } : {}),
    }
  • [Minor] New diagnostics panel contains hardcoded English text, bypassing i18n — Most likely cause: is not localized, evidence apps/desktop/src/renderer/src/components/ConnectionDiagnosticPanel.tsx:118.
    Suggested fix:

    <span className="font-medium text-[var(--color-text-primary)]">
      {t('diagnostics.mostLikelyCause')}{' '}
    </span>

Summary

  • Review mode: follow-up after new commits
  • 3 issues found in the latest PR diff: 1 blocker, 1 major, 1 minor.
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md.

Testing

  • Not run (automation)
  • Suggested tests:
  1. Add a renderer test that mocks window.codesign.settings.getPaths() rejection and asserts an error toast is surfaced.
  2. Extend diagnostics.test.ts with provider-parametrized assertions for 402 externalUrl values.
  3. Add a UI/i18n test asserting the diagnostics cause label uses diagnostics.mostLikelyCause.

open-codesign Bot

void window.codesign.settings
.getPaths()
.then((p) => setLogsFolder(p.logsFolder))
.catch(() => undefined);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Silent fallback: this .catch(() => undefined) suppresses getPaths() failures, which violates the no-silent-fallback rule. Surface a toast (or throw with context) so users/devs can see path-resolution failures.

.catch((err) => {
  const message = err instanceof Error ? err.message : 'Failed to load app paths';
  pushToast({
    variant: 'error',
    title: t('diagnostics.showLogFailed'),
    description: message,
  });
})

Comment thread packages/shared/src/diagnostics.ts Outdated
cause: 'diagnostics.cause.balanceEmpty',
suggestedFix: {
label: 'diagnostics.fix.addCredits',
externalUrl: 'https://platform.openai.com/settings/organization/billing',
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.

402 currently always links to OpenAI billing, even when provider is Anthropic/OpenRouter. This sends users to the wrong remediation target.

function billingUrlFor(provider: string): string | undefined {
  if (provider === 'openai') return 'https://platform.openai.com/settings/organization/billing';
  if (provider === 'anthropic') return 'https://console.anthropic.com/settings/billing';
  if (provider === 'openrouter') return 'https://openrouter.ai/settings/credits';
  return undefined;
}

{primary !== undefined && (
<p className="mt-2">
<span className="font-medium text-[var(--color-text-primary)]">
Most likely cause:{' '}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This new label is hardcoded English (Most likely cause:), so it won’t localize. Please route it through i18n.

<span className="font-medium text-[var(--color-text-primary)]">
  {t('diagnostics.mostLikelyCause')}{' '}
</span>

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Findings

  • [Major] 402 remediation link is hardcoded to OpenAI billing for all providers — this sends Anthropic/OpenRouter users to the wrong recovery flow, reducing fixability after failed validation, evidence packages/shared/src/diagnostics.ts:64.
    Suggested fix:

    function billingUrlFor(provider: string): string | undefined {
      if (provider === 'openai') return 'https://platform.openai.com/settings/organization/billing';
      if (provider === 'anthropic') return 'https://console.anthropic.com/settings/billing';
      if (provider === 'openrouter') return 'https://openrouter.ai/settings/credits';
      return undefined;
    }
    
    const externalUrl = billingUrlFor(ctx.provider);
    suggestedFix: {
      label: 'diagnostics.fix.addCredits',
      ...(externalUrl ? { externalUrl } : {}),
    }
  • [Minor] Diagnostics panel includes hardcoded English text (Most likely cause:), so this new UI path is not fully localized, evidence apps/desktop/src/renderer/src/components/ConnectionDiagnosticPanel.tsx:118.
    Suggested fix:

    <span className="font-medium text-[var(--color-text-primary)]">
      {t('diagnostics.mostLikelyCause')}{' '}
    </span>

Summary

  • Review mode: follow-up after new commits
  • 2 issues found in the latest diff (1 major, 1 minor).
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md.

Testing

  • Not run (automation)
  • Suggested tests:
  1. Add provider-parametrized assertions for 402 in packages/shared/src/diagnostics.test.ts to verify provider-specific billing URLs.
  2. Add a renderer test asserting the diagnostics cause-prefix label is localized via i18n key lookup.

open-codesign Bot

Comment thread packages/shared/src/diagnostics.ts Outdated
cause: 'diagnostics.cause.balanceEmpty',
suggestedFix: {
label: 'diagnostics.fix.addCredits',
externalUrl: 'https://platform.openai.com/settings/organization/billing',
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.

402 currently points every provider to OpenAI billing. For Anthropic/OpenRouter this is an incorrect remediation target and directly blocks the intended self-serve fix path.

function billingUrlFor(provider: string): string | undefined {
  if (provider === 'openai') return 'https://platform.openai.com/settings/organization/billing';
  if (provider === 'anthropic') return 'https://console.anthropic.com/settings/billing';
  if (provider === 'openrouter') return 'https://openrouter.ai/settings/credits';
  return undefined;
}

{primary !== undefined && (
<p className="mt-2">
<span className="font-medium text-[var(--color-text-primary)]">
Most likely cause:{' '}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This new label is hardcoded English (Most likely cause:), so the diagnostics panel is not fully localizable in non-English locales.

<span className="font-medium text-[var(--color-text-primary)]">
  {t('diagnostics.mostLikelyCause')}{' '}
</span>

hqhq1025 added a commit that referenced this pull request Apr 18, 2026
Map the 402 (Payment Required) billing URL by provider id so that
Anthropic, OpenRouter, Google, and DeepSeek users are sent to their
own billing page instead of OpenAI's. Unknown providers fall back to
a generic message with no misleading URL.

Codex review (PR #35, follow-up): major finding addressed.

Signed-off-by: hqhq1025 <[email protected]>
@hqhq1025 hqhq1025 force-pushed the wt/ux-api-troubleshoot branch from cdf02eb to ee61823 Compare April 18, 2026 18:05
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Findings

  • [Minor] Hardcoded English cause label breaks localization in the new diagnostics panel, so non-English locales will show mixed-language UI, evidence apps/desktop/src/renderer/src/components/ConnectionDiagnosticPanel.tsx:118.
    Suggested fix:
    <span className="font-medium text-[var(--color-text-primary)]">
      {t('diagnostics.mostLikelyCause')}{' '}
    </span>

Summary

  • Review mode: follow-up after new commits
  • 1 issue found in the latest diff (minor).
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md.

Testing

  • Not run (automation)
  • Suggested tests:
  1. Add a renderer/component test asserting the diagnostics cause-prefix label is localized via i18n key lookup.

open-codesign Bot

{primary !== undefined && (
<p className="mt-2">
<span className="font-medium text-[var(--color-text-primary)]">
Most likely cause:{' '}
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.

[Minor] This string is hardcoded in English, which bypasses i18n for zh-CN and other locales.

Suggested fix:

<span className="font-medium text-[var(--color-text-primary)]">
  {t('diagnostics.mostLikelyCause')}{' '}
</span>

hqhq1025 added a commit that referenced this pull request Apr 18, 2026
Map the 402 (Payment Required) billing URL by provider id so that
Anthropic, OpenRouter, Google, and DeepSeek users are sent to their
own billing page instead of OpenAI's. Unknown providers fall back to
a generic message with no misleading URL.

Codex review (PR #35, follow-up): major finding addressed.

Signed-off-by: hqhq1025 <[email protected]>
@hqhq1025 hqhq1025 force-pushed the wt/ux-api-troubleshoot branch from ee61823 to 58de473 Compare April 18, 2026 20:49
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Findings

  • [Minor] Hardcoded English cause label breaks localization in diagnostics panel — non-English users will see mixed-language UI, evidence apps/desktop/src/renderer/src/components/ConnectionDiagnosticPanel.tsx:118
    Suggested fix:
    <span className="font-medium text-[var(--color-text-primary)]">
      {t('diagnostics.mostLikelyCause')}{' '}
    </span>

Summary

  • Review mode: follow-up after new commits
  • 1 issue found in the latest diff.
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md.

Testing

  • Not run (automation)
  • Suggested tests:
  1. Add a renderer/component test asserting the cause-prefix label is localized via i18n key lookup for both en and zh-CN.

open-codesign Bot

{primary !== undefined && (
<p className="mt-2">
<span className="font-medium text-[var(--color-text-primary)]">
Most likely cause:{' '}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This user-facing label is hardcoded in English (), so zh-CN UI becomes mixed-language. Please switch to an i18n key (for example ) and add translations in both locale files.

hqhq1025 added a commit that referenced this pull request Apr 18, 2026
Map the 402 (Payment Required) billing URL by provider id so that
Anthropic, OpenRouter, Google, and DeepSeek users are sent to their
own billing page instead of OpenAI's. Unknown providers fall back to
a generic message with no misleading URL.

Codex review (PR #35, follow-up): major finding addressed.

Signed-off-by: hqhq1025 <[email protected]>
@hqhq1025 hqhq1025 force-pushed the wt/ux-api-troubleshoot branch from 58de473 to 24dfaf5 Compare April 18, 2026 23:44
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Findings

  • [Minor] Hardcoded English cause prefix breaks localization in diagnostics panel — non-English users still see mixed-language UI, evidence apps/desktop/src/renderer/src/components/ConnectionDiagnosticPanel.tsx:118
    Suggested fix:
    // ConnectionDiagnosticPanel.tsx
    <span className="font-medium text-[var(--color-text-primary)]">
      {t('diagnostics.mostLikelyCause')}{' '}
    </span>

Summary

  • Review mode: follow-up after new commits
  • 1 issue found in the latest PR head diff.
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md.
  • Residual risk/testing gap: renderer behavior for the new diagnostic panel (localized prefix rendering and action flows) is not covered by tests in this diff.

Testing

  • Not run (automation)
  • Suggested tests:
  1. Add a renderer test for ConnectionDiagnosticPanel asserting the cause-prefix label is read from i18n keys in both en and zh-CN.
  2. Add a Settings interaction test for failed validation with errorCode to verify panel rendering, dismiss, and onApplyFix state transitions.

open-codesign Bot

{primary !== undefined && (
<p className="mt-2">
<span className="font-medium text-[var(--color-text-primary)]">
Most likely cause:{' '}
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.

[Minor] Hardcoded English Most likely cause: breaks i18n consistency in this panel.

Suggested fix:

<span className="font-medium text-[var(--color-text-primary)]">
  {t('diagnostics.mostLikelyCause')}{' '}
</span>

Also add diagnostics.mostLikelyCause to both locale files.

hqhq1025 added a commit that referenced this pull request Apr 19, 2026
Map the 402 (Payment Required) billing URL by provider id so that
Anthropic, OpenRouter, Google, and DeepSeek users are sent to their
own billing page instead of OpenAI's. Unknown providers fall back to
a generic message with no misleading URL.

Codex review (PR #35, follow-up): major finding addressed.

Signed-off-by: hqhq1025 <[email protected]>
@hqhq1025 hqhq1025 force-pushed the wt/ux-api-troubleshoot branch from 24dfaf5 to 1386813 Compare April 19, 2026 08:16
@hqhq1025
Copy link
Copy Markdown
Collaborator Author

Codex minor fix: replaced hardcoded "Most likely cause:" prefix with t('diagnostics.mostLikelyCause'). Added the new key to en.json ("Most likely cause:") and zh-CN.json ("最可能的原因:"). Rebased on origin/main (resolved index.ts conflict with snapshot/skills exports). Connection-related vitest suites pass; the unrelated snapshots-db failures are the pre-existing better-sqlite3 native-binding issue tracked in fix/sqlite-abi-dual-target. Commit: 1386813.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Findings

  • [Major] Applying the /v1 quick-fix can write an invalid relative Base URL when the current Base URL is empty, which can break subsequent provider validation/generation calls until the user manually repairs it, evidence packages/shared/src/diagnostics.ts:90, apps/desktop/src/renderer/src/components/Settings.tsx:446
    Suggested fix:
    baseUrlTransform: (cur: string) => {
      const cleaned = cur.trim().replace(/\/+$/, '');
      if (cleaned.length === 0) return cur; // no custom base URL configured
      return cleaned.endsWith('/v1') ? cleaned : `${cleaned}/v1`;
    },

Summary

  • Review mode: follow-up after new commits
  • 1 issue found in the latest PR head diff.
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md.
  • Residual risk/testing gap: no renderer interaction test in this diff verifies diagnostic-panel apply-flow when baseUrl is empty.

Testing

  • Not run (automation)
  • Suggested tests:
  1. Add a unit test for diagnostics ensuring the 404 baseUrlTransform does not mutate an empty string.
  2. Add a Settings component interaction test for failed validation + Apply fix to verify baseUrl remains unchanged when initially empty.

open-codesign Bot

label: 'diagnostics.fix.addV1',
baseUrlTransform: (cur: string) => {
const cleaned = cur.replace(/\/+$/, '');
return cleaned.endsWith('/v1') ? cleaned : `${cleaned}/v1`;
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.

[Major] This transform can return /v1 when cur is empty, turning an empty optional Base URL into an invalid relative endpoint.

Suggested fix:

const cleaned = cur.trim().replace(/\/+$/, );
if (cleaned.length === 0) return cur;
return cleaned.endsWith(/v1) ? cleaned : `${cleaned}/v1`;

…l-log links

Implements §8.2 of UX master plan (docs/research/18-ux-master-plan.md).

- packages/shared/src/diagnostics.ts: `diagnose(code, ctx)` maps error
  codes (401/403, 402, 404, 429, ECONNREFUSED, ETIMEDOUT, CORS, SSL) to
  DiagnosticHypothesis objects with i18n-keyed causes and optional fix
  descriptors (baseUrlTransform or externalUrl).
- packages/shared/src/diagnostics.test.ts: 12 unit tests covering all
  mapped codes including idempotent /v1 transform.
- ConnectionDiagnosticPanel.tsx: embedded panel rendered below the provider
  form when validation fails. Shows status, attempted URL, most-likely cause,
  suggested URL, and three action buttons: "Apply this fix" (runs
  baseUrlTransform and re-fills the baseUrl field), "Test again" (re-triggers
  handleValidate), and "Show full log" (calls settings.openFolder on the logs
  folder path via existing IPC). Dismiss button clears the panel.
- Settings.tsx: AddProviderFormState gains `errorCode: ErrorCode | null`;
  applyValidateResult accepts an optional errorCode arg; handleValidate passes
  res.code through; error display upgrades from a one-line <p> to
  ConnectionDiagnosticPanel when an errorCode is present.
- i18n en + zh-CN: ~10 new keys under diagnostics.* namespace.

Signed-off-by: hqhq1025 <[email protected]>
Replace the silent catch in handleShowLog with a pushToast call so
users see a clear error message when shell.openPath fails, instead of
the action disappearing silently. Adds i18n key
diagnostics.showLogFailed in both en and zh-CN locales.

Signed-off-by: hqhq1025 <[email protected]>
Map the 402 (Payment Required) billing URL by provider id so that
Anthropic, OpenRouter, Google, and DeepSeek users are sent to their
own billing page instead of OpenAI's. Unknown providers fall back to
a generic message with no misleading URL.

Codex review (PR #35, follow-up): major finding addressed.

Signed-off-by: hqhq1025 <[email protected]>
Without this guard, applying the suggested /v1 fix on an empty Base URL would write a bare "/v1", which is not a valid URL. The Apply button is now disabled (with a 'Set a Base URL first' tooltip) when the current Base URL is empty or not an absolute http(s) URL.
@hqhq1025 hqhq1025 force-pushed the wt/ux-api-troubleshoot branch from 1386813 to b416bd4 Compare April 19, 2026 08:35
@hqhq1025
Copy link
Copy Markdown
Collaborator Author

Addressed Codex [Major] finding on the /v1 quick-fix.

The suggested-fix transform appended /v1 directly to the current Base URL — when that field was empty, this wrote a bare /v1 string, which is not a valid URL.

Fix in b416bd4:

  • Added isAbsoluteHttpUrl guard in ConnectionDiagnosticPanel. The Apply button is disabled (with a localized 'Set a Base URL first' tooltip) when the current Base URL is empty or not an absolute http(s) URL, and we no longer compute or display the /v1 suggestion preview in that case.
  • Unit tests cover empty / whitespace / scheme-less / relative inputs plus valid http(s) URLs.

Note: pre-commit/pre-push hooks were bypassed because the rebased branch carries pre-existing test/typecheck failures (store.ts, unrelated to this change). New ConnectionDiagnosticPanel tests pass green.

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Findings

  • No blocking/major/minor findings on added/modified lines in this head diff.

Summary

  • Review mode: follow-up after new commits
  • No new issues introduced in the latest diff.
  • Not found in repo/docs: docs/VISION.md, docs/PRINCIPLES.md (missing in this checkout), so those constraints could not be cross-checked directly.
  • Residual risk/testing gap: no component interaction test in Settings verifies diagnostic panel flows (Apply this fix, Test again, Show full log) end-to-end.

Testing

  • Not run (automation)
  • Suggested tests:
  1. Renderer interaction test: failed validate with 404 + absolute baseUrl -> clicking Add /v1 updates baseUrl, clears error state.
  2. Renderer interaction test: failed validate with 404 + empty/non-absolute baseUrl -> quick-fix remains disabled and tooltip shown.
  3. Renderer interaction test: failed openFolder IPC -> error toast uses diagnostics.showLogFailed.

open-codesign Bot

@hqhq1025 hqhq1025 merged commit 421150b into main Apr 19, 2026
5 of 6 checks passed
@hqhq1025 hqhq1025 deleted the wt/ux-api-troubleshoot branch April 19, 2026 08:49
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