Skip to content

fix(daemon): skip pre-existing gateway listeners as schtask launch evidence (#91144)#91198

Closed
openperf wants to merge 1 commit into
openclaw:mainfrom
openperf:fix/91144-schtasks-foreground-gateway-evidence
Closed

fix(daemon): skip pre-existing gateway listeners as schtask launch evidence (#91144)#91198
openperf wants to merge 1 commit into
openclaw:mainfrom
openperf:fix/91144-schtasks-foreground-gateway-evidence

Conversation

@openperf

@openperf openperf commented Jun 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Problem: On Windows, when a foreground openclaw gateway is already running on the task port, openclaw gateway install/start/restart registers the Scheduled Task but the gateway does not persist after the foreground window is closed. The reporter saw "Verified gateway listener detected on port 18789 even though schtasks did not report a running task" in status output, then ECONNREFUSED 127.0.0.1:18789 after closing the foreground window ([Bug]: Windows native CLI gateway Scheduled Task does not stay running; foreground window worksWindows native CLI gateway Scheduled Task does not stay running; foreground window works #91144).
  • Root Cause: shouldFallbackScheduledTaskLaunch polled task state via readScheduledTaskRuntime, which calls resolveListenerBackedScheduledTaskRuntime to enrich non-running schtasks state. When a foreground gateway occupies the task port, resolveListenerBackedScheduledTaskRuntime finds its listener PID and returns status: "running" — so readLaunchObservation immediately returns state: "running" and shouldFallbackScheduledTaskLaunch returns false (no fallback needed) before schtasks can even report the task state. The same pre-existing PID also passed the hasLaunchEvidence port check at the end of the polling timeout window (src/daemon/schtasks.ts).
  • Fix: runScheduledTaskOrThrow snapshots any listener PIDs on the task port before calling schtasks /Run. shouldFallbackScheduledTaskLaunch receives taskPort and preExistingListenerPids; readLaunchObservation reads raw schtasks state directly (bypassing resolveListenerBackedScheduledTaskRuntime) and counts only PIDs that appeared after the task was launched as task-start evidence; hasLaunchEvidence filters the same pre-existing set.
  • What changed:
    • src/daemon/schtasks.tsrunScheduledTaskOrThrow captures pre-existing listener PIDs; shouldFallbackScheduledTaskLaunch takes new taskPort/preExistingListenerPids params; readLaunchObservation uses direct schtasks queries instead of readScheduledTaskRuntime; hasLaunchEvidence filters pre-existing PIDs
    • src/daemon/schtasks.startup-fallback.test.ts — one new regression test for the foreground-gateway-listener-as-evidence scenario
  • What did NOT change (scope boundary):
    • Node Scheduled Task path (!shouldManageGatewayListenerPort) — no changes to node task evidence logic
    • readScheduledTaskRuntime and resolveListenerBackedScheduledTaskRuntime — still used for runtime status reporting, unchanged
    • Startup-folder fallback, stop, uninstall, schtasks creation paths — unchanged
    • Config surface unchanged; plugin surface unchanged; no dependency changes

Reproduction

  1. On Windows, start a foreground openclaw gateway — it binds port 18789
  2. Run openclaw gateway install (or start or restart) — schtasks registers and attempts to run the Scheduled Task
  3. The Scheduled Task fails to bind port 18789 (already in use) and exits; schtasks reports the task as not-yet-run
  4. Before this PR: readLaunchObservation calls readScheduledTaskRuntimeresolveListenerBackedScheduledTaskRuntime sees the foreground gateway on port 18789 → returns status: "running" immediately → shouldFallbackScheduledTaskLaunch returns false with no fallback launched; closing the foreground window leaves no persistent gateway (ECONNREFUSED 127.0.0.1:18789)
  5. After this PR: The foreground gateway's PID is captured as pre-existing before schtasks /Run; readLaunchObservation and hasLaunchEvidence filter it out; after the polling timeout finds no new listeners, the fallback launcher is spawned; the managed gateway persists after the foreground window closes

Real behavior proof

Behavior addressed (#91144): When a foreground openclaw gateway occupies the task port before schtasks /Run, the pre-existing listener PID was falsely accepted as proof the Scheduled Task started, suppressing the Startup-style fallback launcher. Gateway did not persist after the foreground window closed.

Real environment tested (Linux, Node 22.19 — Vitest against production shouldFallbackScheduledTaskLaunch and hasLaunchEvidence with mocked schtasks calls and port listener detection):

node scripts/run-vitest.mjs src/daemon/schtasks.startup-fallback.test.ts src/daemon/schtasks.test.ts

Exact steps or command run after this patch:

node scripts/run-vitest.mjs src/daemon/schtasks.startup-fallback.test.ts src/daemon/schtasks.test.ts

Evidence after fix (Vitest output, 57 tests across startup-fallback + schtasks suites):

 Test Files  2 passed (2)
      Tests  57 passed (57)
   Start at  23:21:20
   Duration  1.36s (transform 979ms, setup 1.11s, import 437ms, tests 459ms, environment 1ms)

Observed result after fix: All 57 tests pass — including the new regression test "falls back when a foreground gateway on the task port is the only listener after /Run (#91144)" — and all 27 pre-existing startup-fallback scenarios remain green.

What was not tested: Live Windows run confirming the patched path end-to-end (the clawsweeper:source-repro label on #91144 notes a native Windows run is needed to capture the exact Task Scheduler Last Run Result and confirm behavior with a real Scheduled Task registration).

Repro confirmation: The new regression test fails on main with Expected "spawn" to have been called (the foreground PID suppresses the fallback before any polling completes) and passes after this patch. Confirmed by temporarily restoring src/daemon/schtasks.ts to origin/main with the test file in place.

Risk / Mitigation

  • Risk: readLaunchObservation now issues two schtasks calls directly instead of delegating to readScheduledTaskRuntime. Mitigation: The two calls (/Query for availability + /Query /TN ... /V /FO LIST for task state) are identical to what readScheduledTaskRuntime makes internally; all 57 existing tests pass; the readScheduledTaskRuntime code path for runtime status reporting is unchanged.

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Gateway / orchestration

Linked Issue/PR

Fixes #91144

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: S labels Jun 7, 2026
@clawsweeper

clawsweeper Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge. Reviewed June 7, 2026, 9:32 PM ET / 01:32 UTC.

Summary
The PR changes Windows Scheduled Task launch verification to bypass listener-backed status, snapshot pre-existing gateway listener PIDs, filter them from launch evidence, terminate verified foreground gateways before fallback, and add startup-fallback regression tests.

PR surface: Source +39, Tests +56. Total +95 across 2 files.

Reproducibility: yes. source-level reproduction is high confidence: current main lets readScheduledTaskRuntime report running from a verified listener before fallback polling, and the linked issue reports the same status followed by ECONNREFUSED after the foreground gateway closes. I did not run a live Windows Scheduled Task scenario in this read-only review.

Review metrics: none identified.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🌊 off-meta tidepool
Patch quality: 🦪 silver shellfish
Result: blocked by patch quality or review findings.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Fix launch-evidence filtering while keeping termination verified-only.
  • [P2] Add a regression for a non-OpenClaw process already bound to the task port before schtasks /Run.
  • Capture native Windows Task Scheduler proof if maintainers want runtime evidence before landing.

Risk before merge

  • [P2] The patch still uses resolveScheduledTaskGatewayListenerPids for launch evidence; because that helper can fall back to all busy-port PIDs, a non-OpenClaw process already bound to the task port can still be treated as new task-start evidence and suppress fallback.
  • [P1] The PR body reports Linux Vitest coverage with mocked schtasks and port discovery, but no native Windows Task Scheduler handoff run; maintainers may still want live Windows proof before landing this availability-sensitive path.

Maintainer options:

  1. Fix launch-evidence filtering before merge (recommended)
    Preserve the verified-only termination behavior while changing launch evidence to ignore all pre-existing port listeners or to require verified OpenClaw gateway evidence, then add the unverified busy-port regression.
  2. Accept source-level proof for this edge
    Maintainers could accept the current verified-gateway fix and track the unverified busy-port case separately, but that leaves a known false-positive launch-evidence path in the same availability surface.
  3. Pause for native Windows proof
    If maintainers want runtime confidence before changing task handoff behavior, pause merge until a native Windows run shows foreground gateway closure followed by a persistent managed gateway.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Keep termination limited to verified gateway PIDs, but make launch-evidence filtering ignore all listener PIDs that existed before schtasks /Run or otherwise require verified OpenClaw gateway evidence; add a startup-fallback regression for a non-OpenClaw process already bound to the task port, then run node scripts/run-vitest.mjs src/daemon/schtasks.startup-fallback.test.ts src/daemon/schtasks.test.ts and git diff --check.

Next step before merge

  • [P2] A narrow automated repair can adjust launch-evidence filtering and add the missing regression without making a product or API decision.

Security
Cleared: No dependency, workflow, package, secret, or new third-party code-execution surface was introduced; the process-termination concern is handled as an availability/safety review issue.

Review findings

  • [P2] Filter all pre-existing port listeners from launch evidence — src/daemon/schtasks.ts:1054-1057
Review details

Best possible solution:

Keep termination limited to verified gateway PIDs, but make launch evidence ignore all listeners that existed before schtasks /Run or only trust verified post-launch OpenClaw gateway evidence, with a regression for an unverified pre-bound port listener.

Do we have a high-confidence way to reproduce the issue?

Yes, source-level reproduction is high confidence: current main lets readScheduledTaskRuntime report running from a verified listener before fallback polling, and the linked issue reports the same status followed by ECONNREFUSED after the foreground gateway closes. I did not run a live Windows Scheduled Task scenario in this read-only review.

Is this the best way to solve the issue?

No. The fix direction is right, but the best version separates killable verified gateway cleanup from launch-evidence filtering so unverified pre-existing port owners cannot be mistaken for a task start.

Full review comments:

  • [P2] Filter all pre-existing port listeners from launch evidence — src/daemon/schtasks.ts:1054-1057
    This path still asks resolveScheduledTaskGatewayListenerPids for launch evidence, but that helper falls back to every busy-port listener PID when no gateway argv can be verified. Since preExistingListenerPids is populated only from verified gateway PIDs, a non-OpenClaw process that already owned the task port before /Run will not be filtered and can make this return running, suppressing the fallback again. Keep the kill set verified-only, but make evidence filtering ignore all pre-existing port owners or require verified post-launch gateway evidence.
    Confidence: 0.84

Overall correctness: patch is incorrect
Overall confidence: 0.86

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 5c5391836b1e.

Label changes

Label changes:

  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🌊 off-meta tidepool and patch quality is 🦪 silver shellfish.
  • remove rating: 🧂 unranked krab: Current PR rating is rating: 🦪 silver shellfish, so this older rating label is no longer current.

Label justifications:

  • P2: The PR targets a real Windows gateway Scheduled Task availability bug with limited platform-specific blast radius.
  • merge-risk: 🚨 availability: Merging this path with incorrect launch evidence can still suppress fallback and leave the gateway unavailable after install/start/restart.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🌊 off-meta tidepool and patch quality is 🦪 silver shellfish.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Not applicable: The PR author association is MEMBER, so the external contributor proof gate does not apply; the PR body still notes no live Windows Task Scheduler run was performed.
Evidence reviewed

PR surface:

Source +39, Tests +56. Total +95 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 62 23 +39
Tests 1 56 0 +56
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 118 23 +95

Acceptance criteria:

  • [P2] node scripts/run-vitest.mjs src/daemon/schtasks.startup-fallback.test.ts src/daemon/schtasks.test.ts.
  • [P1] git diff --check.
  • [P1] Optional native Windows proof: start a foreground openclaw gateway, run openclaw gateway install or restart, close the foreground window, then confirm openclaw gateway status --deep --require-rpc succeeds.

What I checked:

  • Current main launch path: Current main uses readScheduledTaskRuntime inside shouldFallbackScheduledTaskLaunch, so listener-backed status can mark the task as running before fallback is considered. (src/daemon/schtasks.ts:1028, 5c5391836b1e)
  • Broad listener resolver: resolveScheduledTaskGatewayListenerPids first returns verified gateway PIDs, then matched gateway command lines, and finally falls back to all finite busy-port listener PIDs. (src/daemon/schtasks.ts:658, 5c5391836b1e)
  • PR head evidence mismatch: PR head calls the broad resolver for launch evidence, but the pre-existing snapshot is populated only from findVerifiedGatewayListenerPidsOnPortSync, so an already-bound unverified listener is not filtered out. (src/daemon/schtasks.ts:1054, 66b3fb772c87)
  • Adjacent test invariant: Current tests already assert runtime status must not trust an unverified busy port while schtasks still says not-yet-run, but the PR does not add the equivalent launch-fallback regression. (src/daemon/schtasks.startup-fallback.test.ts:607, 5c5391836b1e)
  • History provenance: Local blame ties the central schtasks fallback and listener resolver lines to the current gateway daemon history, and public commit history shows recent Windows daemon/fallback work in the same files by steipete, vincentkoc, giodl73-repo, and arifahmedjoy. (src/daemon/schtasks.ts:629, 5c5391836b1e)

Likely related people:

  • steipete: Blame and public commit history show repeated recent ownership of Windows daemon lifecycle, schtasks fallback, and gateway process helper changes in the central files. (role: recent area contributor; confidence: high; commits: ce015cef5775, fccb2b8ace6c, 5ea03efe92d6; files: src/daemon/schtasks.ts, src/daemon/schtasks.startup-fallback.test.ts, src/infra/gateway-processes.ts)
  • vincentkoc: Recent commit history includes schtasks startup fallback helper work in the touched test file, and the PR timeline shows this person was mentioned on this thread. (role: adjacent test owner; confidence: medium; commits: ffd517b513fe; files: src/daemon/schtasks.startup-fallback.test.ts)
  • giodl73-repo: Recent history includes a Windows node/gateway scheduled-task cleanup fix that shares the same schtasks startup fallback tests and listener-ownership invariant. (role: recent Windows daemon contributor; confidence: medium; commits: a4e0b6ef472b; files: src/daemon/schtasks.startup-fallback.test.ts, src/daemon/schtasks.ts)
  • arifahmedjoy: Recent gateway-process history includes Windows stale gateway cleanup and PID verification work used by the listener snapshot path. (role: adjacent process-detection contributor; confidence: medium; commits: 63fcc52520d6; files: src/infra/gateway-processes.ts, src/infra/windows-port-pids.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 7, 2026
@openperf
openperf force-pushed the fix/91144-schtasks-foreground-gateway-evidence branch 2 times, most recently from ab9b8ef to 7cd0d5d Compare June 7, 2026 23:36
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 7, 2026
@openperf
openperf force-pushed the fix/91144-schtasks-foreground-gateway-evidence branch from 7cd0d5d to ef5ce9e Compare June 7, 2026 23:45
@clawsweeper clawsweeper Bot added status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 7, 2026
@openperf
openperf force-pushed the fix/91144-schtasks-foreground-gateway-evidence branch from ef5ce9e to a4db3ae Compare June 8, 2026 01:16
@openperf
openperf force-pushed the fix/91144-schtasks-foreground-gateway-evidence branch from a4db3ae to 66b3fb7 Compare June 8, 2026 01:25
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jun 8, 2026
@openperf openperf closed this Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: S status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

1 participant