fix(server): keep a failed service start out of the current state - #12199
vitalyiegorov wants to merge 1 commit into
Conversation
ApprovabilityVerdict: Approved at 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 You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthroughThe 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. ChangesRestart-pending recovery
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
Suggested reviewers: Merge Risk: ⚪ Minimal · up to 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)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
b4a6aee to
cb1e33b
Compare
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>
cb1e33b to
de4a8db
Compare
What Changed
BootService.installnow writes the existing.restart-pendingmarker on every install, not only forstart: false. One conditional removed inapps/server/src/cloud/bootService.ts; nothing else in the flow changes. A successfulactivatestill removes the marker, and so dorestartand the launcher when it comes up on the awaited version.Test: on launchd, a started install whose
launchctl bootstrapfails leaves the marker with the CLI version,statusreportsproblems: ["restart-pending"]withcurrent: false, and a secondinstall(whatreconcileServicedoes 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 installfails atbootstrap(#11995), but the plist,service-state.json, and runtime already name the new version and nothing records the failed start. The retry then printsT3 Code service is already installedand 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 seesrestart-pendingand 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
Implemented with Claude Fable 5.1 and Claude Opus 5 in T3 Code.
🤖 Generated with Claude Code
Summary by CodeRabbit