You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
one-time bundled-gateway reconciliation after the containing app is replaced;
fixed bundle-relative path and installation-fingerprint checks before restart;
deterministic changed, unchanged, mismatch, stopped, uninstalled, and restart-failure coverage.
Scope
Included
Detect a changed installed app before the initial gateway health/readiness refresh.
Restart only a loaded LaunchAgent that points at the exact bundled Rust gateway path.
Persist the installation fingerprint only after a successful restart.
Excluded
Provider, credential, routing-policy, or default-destination changes.
Automatic update support or replacement of the existing manual update/rollback contract.
Repairing a LaunchAgent that points at an external or mismatched gateway.
Product / Architecture Decisions
A loaded launchd process can retain the executable image and signing context from the app it was launched under even after /Applications/Wayfinder.app is replaced at the same path.
The app fingerprint combines desktop version/build, verified signing-Team state, and bundled-helper file metadata. It is an optimization marker, not a trust decision.
The reconciler locates only the fixed bundle-relative helper and requires the exact LaunchAgent executable path; full bundled-helper authentication still occurs at each execution boundary.
Reconciliation runs before the initial setup and gateway status refresh so Apple Foundation Models readiness is not computed through a stale process.
Failure is fail-closed and retryable: no marker is stored after locator, status, path, or restart failure.
User-Facing Contract
CLI
No CLI changes.
Human Output
Replacing Wayfinder and reopening it automatically refreshes the persistent bundled gateway. Users should not need to run launchctl kickstart to restore Apple Foundation Models availability.
JSON Output
No JSON changes.
Exit Codes
No exit-code changes.
Verification
Ran
env CLANG_MODULE_CACHE_PATH=/private/tmp/wayfinder-replacement-module-cache SWIFTPM_MODULECACHE_OVERRIDE=/private/tmp/wayfinder-replacement-module-cache WAYFINDER_DISABLE_SWIFTPM_SANDBOX=1 swift test --disable-sandbox --scratch-path /private/tmp/wayfinder-replacement-build --package-path macos/WayfinderMac
git diff --check
Covered
All 171 Swift tests passed.
Changed matching installations restart once and persist the new marker.
Unchanged installations do not query or restart launchd.
External/mismatched, stopped, and uninstalled services defer without mutation.
Failed restarts remain retryable and never persist success.
A Developer ID-signed replacement changed the live gateway PID from 23749 to 48559 and persisted the new 0.1.0|1|R8HXTBY3NM|... fingerprint.
The restarted gateway reported {"status":"ok","models":["apple-local"],"offline":true} and returned OK. from a real non-streaming apple-local request.
The first signed acceptance build exposed a release-only Swift concurrency crash in async helper verification during launch. The final implementation removes async execution from replacement detection; the corrected signed build remained running and passed the same end-to-end check.
Review Path
GatewayReplacementReconciler.swift for fingerprint, trust, and retry behavior.
WayfinderMacApp.swift for startup ordering.
GatewayReplacementReconcilerTests.swift for boundary coverage.
Notes For Reviewer
The bug was reproduced on a real signed/notarized RC: the live Foundation Models harness reported available, while the stale pre-replacement gateway returned wayfinder_router_not_ready. A manual launchd restart immediately restored a successful apple-local reply. The corrected branch was then Developer ID-signed and installed over the stale process; automatic reconciliation restarted the gateway and restored the same successful Apple reply without manual launchctl work.
Closing: this branch's content is already on main, and it can no longer be merged.
Why it can't merge
main and codex/restart-gateway-after-app-replacement share no common ancestor. main's history was rebuilt during the Rust-only migration — its root is b9b65ed with 58 commits total, while this branch descends from the original scaffold root 47e93ab. There's no merge base, so GitHub reports dirty and neither rebase nor merge is possible without --allow-unrelated-histories, which would conflict on every file.
Why it doesn't need to
Comparing this branch's tip (a73f26d) against main file by file, 9 of the 17 changed files are byte-identical on main — the content came across during the rebuild, it just can't be proven by ancestry:
This PR (fix(release): restart gateway after app replacement #106) — GatewayReplacementReconciler.swift and GatewayReplacementReconcilerTests.swift are identical on main, and the reconciler is wired into WayfinderMacApp.swift.
feat(chat): preserve conversation history #108 — ChatConversation.swift, ChatConversationStore.swift, ChatStateTests.swift, and the sidebar/turn-row/chrome views are all identical on main.
fix(accounts): guide ChatGPT route setup #109 — implemented on main under different names, and more capably. main has CodexAccountViewState.needsConfiguration, a "Connect ChatGPT" UI, and configureAndBeginLogin() backed by ChatGPTProviderConfigurator / LocalChatGPTProviderConfigurator, which resolves the gateway tool, writes the provider config, and restarts the service in one action. fix(accounts): guide ChatGPT route setup #109 deliberately took the opposite approach — never mutating the user's TOML, showing copy-paste steps instead. main's auto-configuring flow supersedes it, so porting fix(accounts): guide ChatGPT route setup #109 would add a duplicate setupRequired state beside needsConfiguration and reintroduce guidance that contradicts the shipped flow.
Note that #108 and #109 merged into this branch rather than into main, which is why the branch tip carries content main appeared to be missing.
One loose end
CodexAccountClient.swift on main still returns the older 404 copy:
"ChatGPT account routing is not configured. Add a codex-app-server model to the gateway first."
#109 replaced it with "ChatGPT has not been added to this gateway yet." That replacement was written for the manual-guidance flow, and main's auto-configure path rarely surfaces the string at all, so it isn't being carried over as-is. Worth a small standalone copy pass if the wording still reads too implementation-flavored.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements
WF-ROADMAP-0015 desktop v0.1.0 app-replacement recovery.Adds:
Scope
Included
Excluded
Product / Architecture Decisions
/Applications/Wayfinder.appis replaced at the same path.User-Facing Contract
CLI
No CLI changes.
Human Output
Replacing Wayfinder and reopening it automatically refreshes the persistent bundled gateway. Users should not need to run
launchctl kickstartto restore Apple Foundation Models availability.JSON Output
No JSON changes.
Exit Codes
No exit-code changes.
Verification
Ran
env CLANG_MODULE_CACHE_PATH=/private/tmp/wayfinder-replacement-module-cache SWIFTPM_MODULECACHE_OVERRIDE=/private/tmp/wayfinder-replacement-module-cache WAYFINDER_DISABLE_SWIFTPM_SANDBOX=1 swift test --disable-sandbox --scratch-path /private/tmp/wayfinder-replacement-build --package-path macos/WayfinderMacgit diff --checkCovered
23749to48559and persisted the new0.1.0|1|R8HXTBY3NM|...fingerprint.{"status":"ok","models":["apple-local"],"offline":true}and returnedOK.from a real non-streamingapple-localrequest.Review Path
GatewayReplacementReconciler.swiftfor fingerprint, trust, and retry behavior.WayfinderMacApp.swiftfor startup ordering.GatewayReplacementReconcilerTests.swiftfor boundary coverage.Notes For Reviewer
The bug was reproduced on a real signed/notarized RC: the live Foundation Models harness reported
available, while the stale pre-replacement gateway returnedwayfinder_router_not_ready. A manual launchd restart immediately restored a successfulapple-localreply. The corrected branch was then Developer ID-signed and installed over the stale process; automatic reconciliation restarted the gateway and restored the same successful Apple reply without manuallaunchctlwork.