Repository navigation
fix(windows): startup safety falsely marks healthy scheduler service stale #432
Description
Client or integration
OpenCodex dashboard
Area
Service lifecycle
Summary
On Windows, a successfully installed and functioning Task Scheduler service is reported as stale by both ocx status and the Dashboard's Startup Safety page.
The generated task XML explicitly contains enabled/default lifecycle fields, but Windows canonicalizes the registered task on export and omits default-valued elements. windowsTaskRegistrationHealthy() currently requires those elements to be present literally, so a healthy task is rejected.
Expected: an enabled, logon-triggered scheduler task using the expected wscript.exe launcher is considered viable even when Windows omits XML elements whose effective values are the expected defaults.
Actual: Restart safety: AT RISK after restart (background service files are stale...) remains after a successful elevated reinstall.
Reproduction
-
Install OpenCodex 2.7.39 on Windows 11.
-
In an elevated PowerShell, run:
ocx service install Start-Sleep 8 ocx status
-
Installation reports success:
opencodex service installed + started (auto-starts on login, auto-restarts on crash). -
Status still reports:
Restart safety: AT RISK after restart (background service files are stale; run 'ocx service install') Service: installed, stale or missing service assets -
The generated
opencodex-service-task.xmlcontains:<LogonTrigger> <Enabled>true</Enabled> </LogonTrigger> <Principal id="Author"> <LogonType>InteractiveToken</LogonType> <RunLevel>LeastPrivilege</RunLevel> </Principal> ... <Enabled>true</Enabled>
-
The registered task exported by
schtasks.exe /query /tn opencodex-proxy /xmlis canonicalized to this equivalent form:<LogonTrigger /> <Principal id="Author"> <UserId>[REDACTED SID]</UserId> <LogonType>InteractiveToken</LogonType> </Principal> <Settings> ... <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> <ExecutionTimeLimit>PT0S</ExecutionTimeLimit> ... </Settings>
The default-valued
Enabled=trueandRunLevel=LeastPrivilegeelements are omitted. -
Independent task/process checks show the installation is functional:
- Task is enabled.
- Trigger is at user logon.
- Last task result is
0. - Expected
wscript.exe /b /nologo ...opencodex-service-launcher.vbsaction is registered. service-state.jsonrecords backendscheduler; its baked Bun and CLI paths exist.- Proxy health is OK on port 10100.
Re-running ocx service install reproduces the same result because Task Scheduler exports the same canonicalized XML.
Version
2.7.39
Operating system
Microsoft Windows 11 Pro, version 10.0.26200, x64; PowerShell 7.6.3
Provider and model
Not provider-specific.
Logs or error output
$ ocx health --json
{"ok":true,"pid":[REDACTED],"port":10100}
$ ocx status
Proxy: running
Health: http://127.0.0.1:10100/healthz ok v2.7.39
Restart safety: AT RISK after restart (background service files are stale; run 'ocx service install')
Service: installed, stale or missing service assetsSuggested fix
In windowsTaskRegistrationHealthy(), interpret omitted default-valued fields according to Task Scheduler semantics:
- Missing trigger
Enabled=> enabled; explicitfalse=> unhealthy. - Missing settings
Enabled=> enabled; explicitfalse=> unhealthy. - Missing
RunLevel=>LeastPrivilege; explicit non-LeastPrivilege=> unhealthy.
Keep the existing exact checks for action path, launcher arguments, logon type, multiple-instance policy, and execution limit.
A regression test using the canonicalized exported XML above would catch this without weakening checks for explicit unsafe values.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
docs(devlog): 260725_bug_sweep 로드맵 — 미해결 비-GUI 버그 5건 diff-level 계획
docs(devlog): 260725_bug_sweep r2 — A-gate blocker 12건 반영
fix(service): read omitted Task Scheduler defaults as defaults (#432)
lidge-jun commentedon Jul 25, 2026
Fixed on
devin f0afdfce, included in integration SHAa5ec15e3.Task Scheduler XML now interprets omitted default-valued
EnabledandRunLevelfields according to their effective Windows defaults, while still rejecting explicit unsafe values, duplicate/shadowed elements, namespace tricks, and decoy sections. The production diagnostic path and canonicalized XML regression are covered by tests.Validation: Cross-platform CI and the Windows
windows-schtasksjob in Service lifecycle both passed for the exact integration SHA.0disoft commentedon Jul 26, 2026
Thanks for landing the omitted-default fix. I installed the stable
2.7.40release and re-ran the original reproduction. The default-value portion is fixed, but the same service still remainsstalebecause Task Scheduler also canonicalizes the action arguments' quote entities.2.7.40 result
The generated task file contains:
After registration,
schtasks.exe /query /tn opencodex-proxy /xmlexports the equivalent text-node value with literal quotes:Directly evaluating the production diagnostic against that exported XML on 2.7.40 gives:
{ "healthy": false, "state": { "installed": true, "enabled": true, "registrationHealthy": false }, "hasEntity": false, "hasLiteralArgs": true }The remaining mismatch appears to be this exact string check in
windowsTaskRegistrationHealthy():taskXmlString()produces", while the registered-task export has already decoded it to literal quotes. The command, launcher path, task enabled state, logon trigger, service-state paths, and live proxy are otherwise all valid.Could the action text be parsed/decoded before comparison (or safely accept both XML-equivalent encodings), with regression coverage for the literal-quote export shape? Re-running elevated
ocx service installcannot change the canonicalized export, so it continues to reproduce on 2.7.40.docs(devlog): 260725_bug_sweep 로드맵 — 미해결 비-GUI 버그 5건 diff-level 계획
docs(devlog): 260725_bug_sweep r2 — A-gate blocker 12건 반영
fix(service): read omitted Task Scheduler defaults as defaults (lidge…