Repository navigation
Keep the Windows daemon alive and let Reconnect restart it - #670
Merged
Merged
Conversation
The installed daemon task had only a logon trigger, so a stopped graphcoded was never restarted and Ctrl+R could not bring it back. Add a one-minute repeating trigger (IgnoreNew, the launchd KeepAlive equivalent) and remove the battery limits; Stop-InstalledDaemon now disables the task before ending it so upgrade/uninstall are not fought, and a refused uninstall re-registers it. Reconnect starts the registered task when the daemon endpoint is missing, falling back to a shell-owned daemon when no task exists. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Signed-off-by: Colin Neilens <coneilen@microsoft.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
On the installed beta18 candidate, once graphcoded stopped nothing restarted it: the scheduled task had only a logon trigger, and Ctrl+R Reconnect only redialed the pipe, so the shell and CLI stayed unavailable (errno 121) until
Start-ScheduledTaskor a re-logon. This keeps the daemon alive the way launchdKeepAlivedoes on macOS and lets Reconnect start it.Changes
Tools/windows/PackageRuntime.ps1): the task XML moves intoNew-DaemonTaskXmland gains a one-minute repeating trigger withMultipleInstancesPolicyIgnoreNew (a tick is a no-op while the daemon runs and relaunches it within a minute once it has stopped, whatever its exit code), andDisallowStartIfOnBatteries/StopIfGoingOnBatteriesfalse.RestartOnFailureis deliberately not used. I measured it on this machine: a task whose action exits non-zero, with a crash-style NTSTATUS code, or whose process is killed is not restarted (one run in 150 s with an interval of one minute); it only fires when the task cannot launch.Stop-InstalledDaemonandRemove-DaemonTaskdisable the task before/End, so Upgrade and Uninstall are not undone by the next tick. A refused Uninstall now re-registers the task, since stopping disabled it. Uninstall still ends and deletes the task; the Refuse Windows uninstall while GraphCode runs and make removal all-or-nothing #662 uninstall contracts pass unchanged.DaemonSupervisor.zig,DaemonClient.zig,App.zig):Supervisor.recoverruns on startup and on Ctrl+R. If the endpoint is missing and no daemon holds or is acquiring the lifetime lock, it starts the registered task withschtasks /Run(no elevation, no admin) and falls back to a shell-owned daemon when no task is registered. The task name is derived in Zig exactly asGet-TaskIdentityderives it. The status line reports the outcome. A single attempt per Reconnect; the existing client backoff handles the reconnection itself.PACKAGING.mddocuments the task policy and the recovery path.Test plan
Limits: CI and this session cannot prove recovery of a live Dev Box (installed candidate, real logon session, real kill). The scheduler behaviour is proven on the real Windows scheduler with a harmless action in place of graphcoded; the shell recovery is proven by the decision table, the real
schtasks.exehelpers against an unregistered task, and the endpoint probe, not by a live daemon being killed and restarted. The live shell/daemon walkthroughs (DaemonHandoff.Live, tray,windows-shell) were not run locally. I ran everything under a scratch USERPROFILE, GRAPHCODE_SUPPORT_DIR, LOCALAPPDATA, APPDATA and TEMP with unique task names, and never touched the installed task or daemon.RED: pwsh -NoProfile -File Tools\windows\Tests\Packaging.Scheduler.Tests.ps1 (new assertions, old PackageRuntime.ps1) -> The term 'New-DaemonTaskXml' is not recognized as a name of a cmdlet (missing function, a not-found failure and not a behavioural one; the behavioural finding is the measured one-run-in-150-s result above)
GREEN: pwsh -NoProfile -File Tools\windows\Tests\Packaging.Scheduler.Tests.ps1 -> Daemon task restarts after its process ends: PASS; Daemon task stays stopped after an explicit stop and is removable: PASS (exit 0, 2m01s); zig test src\DaemonSupervisor.zig (pinned 0.15.2, winghostty include) -> All 8 tests passed (RED for it was the compile error: use of undeclared identifier 'TaskState' and 'Recovery'); zig test src\DaemonClient.zig --test-filter "daemon task name" -> All 1 tests passed
REGRESSION: zig test src\App.zig with the WindowsShell.Tests.ps1 arguments -> All 905 tests passed; Tools\windows\Tests Packaging.Uninstall, Rollback, Signing, ScriptSigning, Standalone (18 cases), Release, PreviewCore (37) and SourceCustody (4/4) plus TrayDaemon.Tests.ps1 -> all exit 0
Checklist
git commit -s) per the DCOmake test) (macOS target; not run, this change is Windows-only)make check) (macOS target; not run, this change is Windows-only)