Skip to content

fix(server): keep a failed service start out of the current state - #12199

Open
vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/launchd-bootout-wait
Open

vitalyiegorov wants to merge 1 commit into
pingdotgg:mainfrom
vitalyiegorov:fix/launchd-bootout-wait

Conversation

@vitalyiegorov

@vitalyiegorov vitalyiegorov commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

What Changed

BootService.install now writes the existing .restart-pending marker on every install, not only for start: false. One conditional removed in apps/server/src/cloud/bootService.ts; nothing else in the flow changes. A successful activate still removes the marker, and so do restart and the launcher when it comes up on the awaited version.

Test: on launchd, a started install whose launchctl bootstrap fails leaves the marker with the CLI version, status reports problems: ["restart-pending"] with current: false, and a second install (what reconcileService does for a non-current status) runs bootout/enable/bootstrap again and removes the marker. The systemd install test also asserts a successful started install leaves no marker.

Why

Fixes #12197. On macOS 15 the first t3 service install fails at bootstrap (#11995), but the plist, service-state.json, and runtime already name the new version and nothing records the failed start. The retry then prints T3 Code service is already installed and exits 0 while launchd keeps running the previous launcher, so every remote update stays blocked by the #11940 protocol gate. With the marker, the retry sees restart-pending and repairs the service instead.

Complementary to #12005, which makes the stop/start actually replace the job. This PR only makes the retry honest.

Verification: vp test run apps/server/src/cloud/bootService.test.ts apps/server/src/cli/service.test.ts apps/server/src/cli/update.test.ts (61 passed), server typecheck, lint and fmt on the touched files. Reproduced and diagnosed on a Mac Studio running macOS 15.7.4 where the old npm launcher was still loaded under the new plist.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes (no UI)
  • I included a video for animation/interaction changes (none)

Implemented with Claude Fable 5.1 and Claude Opus 5 in T3 Code.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved service installation recovery by preserving a pending-restart status when setup or startup fails.
    • Incomplete installations are now retried during subsequent installation attempts.
    • Successfully completed installations clear the pending-restart status.
    • Service status more accurately reflects installations requiring follow-up across supported platforms, including macOS.
    • Failed startup attempts now retain the relevant version information until a later installation succeeds.

@github-actions github-actions Bot added size:S 10-29 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Sep 17, 2026
@macroscopeapp

macroscopeapp Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 2a55165

Macroscope's review found this PR approvable — This is a narrow service-install recovery fix that preserves the marker when activation fails, preventing a failed start from being reported as current and enabling a later retry. Successful installs still remove the marker, and targeted regression tests cover the failure and recovery flow.

No code changes detected at cb1e33b. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 2c6de1e2-d3da-4467-8757-24114bfdddb5

📥 Commits

Reviewing files that changed from the base of the PR and between cb1e33b and de4a8db.

📒 Files selected for processing (2)
  • apps/server/src/cloud/bootService.test.ts
  • apps/server/src/cloud/bootService.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The install flow now writes a restart-pending marker before service updates and removes it after successful activation. macOS tests verify failed launch-agent startup remains non-current and that a later install repairs the service.

Changes

Restart-pending recovery

Layer / File(s) Summary
Install marker and recovery flow
apps/server/src/cloud/bootService.ts, apps/server/src/cloud/bootService.test.ts
install() writes the restart-pending marker before service updates. Failed macOS launch-agent startup leaves the marker and reports restart-pending; a successful retry removes it and reports the service as current.

Priority: ➖ Normal

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

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant BootService.install
  participant launchctl
  participant BootService.status
  BootService.install->>BootService.install: Write SERVICE_RESTART_PENDING_FILE
  BootService.install->>launchctl: Bootstrap launch agent
  launchctl-->>BootService.install: Startup failure
  BootService.install->>BootService.status: Read restart-pending state
  BootService.status-->>BootService.install: Report restart-pending and not current
  BootService.install->>launchctl: Bootout, enable, and bootstrap
  launchctl-->>BootService.install: Startup succeeds
  BootService.install->>BootService.install: Remove SERVICE_RESTART_PENDING_FILE
Loading

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to de4a8

The recovery behavior preserves pending state until activation succeeds and correctly retries failed service updates. The change is ready to merge.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: failed service starts no longer appear as current. It is concise, specific, and directly related to the implementation and objectives.
Description check ✅ Passed The description includes complete What Changed, Why, and Checklist sections. It explains the failure mode, retry behavior, tests, verification, and lack of UI changes. The description is focused and s…
Linked Issues check ✅ Passed For #12197, BootService.install writes .restart-pending before each install. The tests verify that a failed launchctl bootstrap preserves the marker, that status reports restart-pending with…
Out of Scope Changes check ✅ Passed The production change updates restart-pending tracking during service installation. The tests cover marker cleanup, launchd bootstrap failure, status reporting, and retry behavior. These changes direc…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 2…
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

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

@vitalyiegorov
vitalyiegorov force-pushed the fix/launchd-bootout-wait branch 2 times, most recently from b4a6aee to cb1e33b Compare September 17, 2026 13:14
On launchd, `t3 service install` writes the plist and state before
`launchctl bootstrap`. When bootstrap fails, nothing on disk records it,
so the next `t3 service install` reports "already installed" while the
previous launcher keeps running.

Write the existing `.restart-pending` marker on every install, not only
for `start: false`. A successful start still removes it; a failed one now
leaves `status` reporting `restart-pending`, so the retry repairs the
service.

Fixes pingdotgg#12197

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@vitalyiegorov
vitalyiegorov force-pushed the fix/launchd-bootout-wait branch from cb1e33b to de4a8db Compare September 17, 2026 15:55

This branch has not been deployed

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

Labels

size:S 10-29 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: macOS t3 service install reports "already installed" while launchd still runs the previous launcher

1 participant