Skip to content

fix(gui): clear a stale install failure once that action succeeded (#1245) - #1275

Merged
lidge-jun merged 1 commit into
devfrom
codex/260808-1245-startup-stale-error
Aug 8, 2026
Merged

fix(gui): clear a stale install failure once that action succeeded (#1245)#1275
lidge-jun merged 1 commit into
devfrom
codex/260808-1245-startup-stale-error

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

The Startup Safety page kept showing "Installation failed" after startup protection was already in place. A user who hit a failing install and then fixed it another way — the CLI, the service manager, a retry the page did not initiate — saw "Restart protected" and "Installation failed" at the same time, with no way to dismiss the contradiction.

installResult was only reset when the page itself started an action, so a refresh replaced the health data underneath a notice describing a past attempt.

fetchStartup now retires a failed notice only when health independently shows that attempt's goal is met. Three conditions, each added after reproducing a case where a looser rule hid something true:

  • per action, not overall status — a machine protected by the service must not erase a failed shim install, which is still true and actionable
  • serviceViable, not installed-and-running — a stale or conflicting service can be both while the page reports it unhealthy, so a failed repair would vanish while the UI still said "Stale"
  • native routing retires only installs made under a local routing dependency, recorded on the error at action time; a native machine is still offered the shim button, so an optional shim failure there stands

Success notices are left alone — those are the receipt for an action the user just ran.

Closes #1245.

GUI change

Before — a failed install, then the service comes up another way. Both messages on screen:

before

After — the stale failure is gone, and the shim correctly still reads "Not installed":

after

Verification

  • bun test tests (gui) — 684 pass / 0 fail
  • bun run lint:gui — clean
  • bun run typecheck — clean
  • Five regressions drive the real page through button clicks and fetch responses: two assert clearing, three assert survival. Every branch has a test that goes red when that branch is deleted or widened.
  • Screenshots above are a live render against a stubbed startup-health API, driven through the page's own Install and Refresh buttons.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Summary by CodeRabbit

  • Bug Fixes

    • Improved startup install error handling during health refreshes.
    • Resolved failed installation notices when the related service or routing condition is restored.
    • Preserved unrelated or still-relevant failure notices instead of clearing them prematurely.
  • Tests

    • Added coverage for service recovery, routing changes, stale failures, and native routing restoration.

…1245)

The Startup Safety page kept showing "Installation failed" after startup
protection was already in place. A user who hit a failing install and then
fixed it another way saw "Restart protected" and "Installation failed" at
the same time, with no way to dismiss the contradiction.

installResult was only reset when the page itself started an action, so a
refresh replaced the health data underneath a notice describing a past
attempt.

fetchStartup now retires a failed notice only when health independently
shows that attempt's goal is met. Three conditions, each added after
reproducing a case where a looser rule hid something true:

- per action, not overall status: a machine protected by the service must
  not erase a failed shim install, which is still true and actionable
- serviceViable, not installed-and-running: a stale or conflicting service
  can be both while the page reports it unhealthy, so a failed repair
  would vanish while the UI still said Stale
- native routing retires only installs made under a local routing
  dependency, recorded on the error at action time; a native machine is
  still offered the shim button, so an optional shim failure there stands

Success notices are left alone - those are the receipt for an action the
user just ran.

Five regressions drive the real page through button clicks and fetch
responses: two assert clearing, three assert survival. Every branch has a
test that fails when that branch is removed or widened.

Buttons are selected by accessible name because both installs read
"Install" and only aria-label separates them.

gui: 684 pass / 0 fail; lint:gui and typecheck clean.
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Aug 8, 2026
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Startup install failures now record local-routing context. Health refreshes clear failures only when the matching service, shim, or routing condition is healthy. New tests verify clearing and retention across recovery and native-routing transitions.

Changes

Startup reconciliation

Layer / File(s) Summary
Routing-aware failure state
gui/src/pages/Startup.tsx
Lines 84 and 273-277 store whether an install attempt required local routing. Lines 110-132 reconcile failed service and shim installs against refreshed health and native-routing status.
Reconciliation behavior tests
gui/tests/startup-install-result-reconciliation.test.tsx
Lines 1-259 add a browser-like Startup test harness and verify failure clearing and retention across service recovery, shim recovery, stale repairs, and native-routing changes.

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

Sequence Diagram(s)

sequenceDiagram
  participant StartupPage
  participant StartupHealthAPI
  participant InstallResultState
  StartupPage->>StartupHealthAPI: Request startup health
  StartupHealthAPI-->>StartupPage: Return service, shim, and routing status
  StartupPage->>InstallResultState: Reconcile failed install notices
  InstallResultState-->>StartupPage: Retain or clear notices by matching health state
Loading

Possibly related PRs

Suggested reviewers: ingwannu, wibias, himomohi

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes clearing stale installation failures after a successful alternative installation.
Linked Issues check ✅ Passed The changes satisfy issue #1245 by clearing stale failures only when the corresponding startup protection goal is independently healthy.
Out of Scope Changes check ✅ Passed The implementation and regression tests are directly related to startup install-failure reconciliation and contain no unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/260808-1245-startup-stale-error

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@gui/tests/startup-install-result-reconciliation.test.tsx`:
- Around line 157-181: Add a test alongside the existing shim-install
reconciliation test that first records a failed shim installation, then
refreshes with both shimInstalled and shimHealthy set to true, and asserts the
shim failure notice is removed. Reuse the existing mount, click, settle, and
cleanup helpers and target the reconciliation behavior in Startup.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f29cca6a-d786-46ac-ab94-e69b840881a5

📥 Commits

Reviewing files that changed from the base of the PR and between 517f446 and 44a0040.

📒 Files selected for processing (2)
  • gui/src/pages/Startup.tsx
  • gui/tests/startup-install-result-reconciliation.test.tsx

Comment on lines +157 to +181
test("a failed shim install survives a refresh that only proves the service is healthy (#1245)", async () => {
let health = atRiskHealth();
const root = await mount(() => health, () => Response.json({ error: "shim install failed" }, { status: 500 }));

await act(async () => {
clickTarget(/shim.*install/i).dispatchEvent(new testWindow.Event("click", { bubbles: true }));
});
await settle();
expect(container().textContent).toContain("shim install failed");

// Restart safety is now covered BY THE SERVICE. The shim is still not installed,
// so the shim failure is still true — clearing it here would hide a real problem
// behind an unrelated success.
health = { ...atRiskHealth(), status: "protected", protection: "service", serviceInstalled: true, serviceViable: true, serviceEnabled: true, serviceRunning: true, rebootSafe: true };
await act(async () => {
clickTarget(/refresh/i).dispatchEvent(new testWindow.Event("click", { bubbles: true }));
});
await settle();

expect(container().textContent).toContain("shim install failed");

await act(async () => { root.unmount(); });
containerEl!.remove();
containerEl = null;
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a successful shim-reconciliation test.

gui/src/pages/Startup.tsx Lines 126-131 clear an install-shim failure only when shimInstalled and shimHealthy are both true. This test verifies retention when only the service becomes viable, but it does not verify that the shim failure clears after the shim becomes healthy.

Add a test that fails a shim install, refreshes with both fields true, and asserts that the failure notice is removed. This protects the action-specific shim predicate from regression.

🤖 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 `@gui/tests/startup-install-result-reconciliation.test.tsx` around lines 157 -
181, Add a test alongside the existing shim-install reconciliation test that
first records a failed shim installation, then refreshes with both shimInstalled
and shimHealthy set to true, and asserts the shim failure notice is removed.
Reuse the existing mount, click, settle, and cleanup helpers and target the
reconciliation behavior in Startup.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 44a0040138

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gui/src/pages/Startup.tsx
Comment on lines +120 to +121
setInstallResult(current => {
if (current?.kind !== "error") return current;

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 Badge Reconcile failures regardless of response ordering

When a user clicks Refresh while a long-running install request is still pending, the refreshed health can report the action's goal as satisfied before the POST eventually returns an error. This updater then sees installResult === null and does nothing; the later catch stores the failure, leaving the page showing healthy state alongside the stale error until another refresh. The Refresh button is disabled only by loading, not installBusy, so this ordering is possible. Reconcile whenever either health or the failure changes (or compare the failure against the latest health when storing it), and add a test where the health response completes before the failed action response.

Useful? React with 👍 / 👎.

@lidge-jun
lidge-jun merged commit 671a0df into dev Aug 8, 2026
26 checks passed
@Wibias
Wibias deleted the codex/260808-1245-startup-stale-error branch August 9, 2026 05:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant