Skip to content

fix(installer): validate port health on restart and reset OpenClaw session state (KAS-049)#92

Open
Parad0x-Labs wants to merge 2 commits into
mainfrom
fix/kas049-launcher-openclaw-reliability
Open

fix(installer): validate port health on restart and reset OpenClaw session state (KAS-049)#92
Parad0x-Labs wants to merge 2 commits into
mainfrom
fix/kas049-launcher-openclaw-reliability

Conversation

@Parad0x-Labs

Copy link
Copy Markdown
Owner

Addresses the two startup-reliability failure modes in KAS-049.

A. Launcher restart validated existence, not port health

The Windows watchdog (nulla_background.cmd) treated a bare open socket on 11435 as healthy, so a NULLA process that crashed after binding the port (or wedged mid-prewarm) kept the port open but never served /healthz. The watchdog looped as "healthy" while OpenClaw never came up on 18789, and the stale process was never cleared.

  • New installer/startup_readiness.pyhealthz_ok (health = /healthz 200, not port-open) and reconcile_before_start, which kills only the NULLA-owned :11435 listener and the pid-file process (reusing the guarded seams in installer/nulla_stop.py) and clears the stale nulla_api.pid. A non-python process holding the port is reported (port_conflict), never killed. Every network/process action is injected, so it is unit-tested without real ports or processes.
  • nulla_background.cmd — health and readiness are /healthz-only (bare-TCP branches removed); the reconcile runs before the detached restart.

B. OpenClaw 2nd-message session-init conflict recurred across reinstalls

Fresh installs never reset the NULLA agent's session state, and the global default model kept a stale ollama/* tag that disagreed with the NULLA agent.

  • register_openclaw_agent.py — clear only <openclaw_home>/agents/nulla/sessions on install, gated by NULLA_CLEAR_OPENCLAW_SESSIONS (the installers set it; the every-launch re-register does not, so a normal launch never wipes a live session). Force the global default model to the NULLA agent's own model when NULLA is the default agent; a deliberate non-NULLA default is left untouched.
  • install_nulla.bat / install_nulla.sh — set that env var (process/command-scoped) around the fresh-install register calls only.

Covered by CI (green)

  • installer/startup_readiness.py: health parsing, and reconcile kills NULLA-owned only / clears the pid file / never kills a non-python port owner.
  • register_openclaw_agent.py: default-model force vs preserve, gated session-clear (fires on install, no-op on launch, leaves workspace/memory), full register() round-trip.
  • Launcher/installer string-contract tests updated to the new shape (/healthz-only, reconcile present, no bare-TCP; installer sets the session-clear flag).

Needs a manual Windows smoke test before merge (why this is a PR, not a direct push)

The .cmd/.bat/.sh edits are string-contract-tested but not executed on CI. On a repro box, please verify:

  1. A wedged API (port 11435 open but /healthz failing) is reconciled and restarted, and OpenClaw then comes up on 18789.
  2. The reconcile kills only NULLA-owned processes (Ollama and unrelated processes untouched).
  3. A fresh install clears agents/nulla/sessions; a normal launch does not touch a live session.
  4. The global default model reads nulla/nulla after registration (no stale ollama/qwen2.5:14b).

Not in this PR (flagged, needs a decision)

  • The root fix for the race is upstream — openclaw #101250 (serialize reply-init per sessionKey). The bundled retry patch is unchanged; it only masks the race, which is why the session-clear (removing the recurrence trigger) is the local lever here.
  • openclaw@beta (2026.7.1-beta.2) version test / pin — a maintainer decision, not done here.
  • Start_NULLA.bat log-path uniqueness — deferred; the detached wrapper (start_windows_detached.py) already falls back to a per-PID log on a lock, so this is lower value.

…ssion state

Addresses two startup-reliability failure modes reported in KAS-049.

(A) The Windows watchdog validated process/port existence, not port
health. nulla_background.cmd treated a bare open socket on 11435 as
healthy, so a NULLA process that crashed after binding the port (or
wedged mid-prewarm) left the watchdog looping while OpenClaw never came
up on 18789, and the stale process was never cleared.
  - Add installer/startup_readiness.py: healthz_ok (health is /healthz
    200, not port-open) and reconcile_before_start, which kills only the
    NULLA-owned :11435 listener and the pid-file process (reusing the
    guarded seams in installer/nulla_stop.py) and clears the stale
    nulla_api.pid; a non-python process holding the port is reported, not
    killed. Network/process actions are injected, so it is unit-tested
    without real ports or processes.
  - Watchdog: health and readiness are /healthz-only (drop the bare-TCP
    branches); run the reconcile before the detached restart.

(B) The OpenClaw reply-session initialization conflict on the second
message recurred across reinstalls because fresh installs never reset
the NULLA agent's session state, and the global default model kept a
stale ollama tag that disagreed with the NULLA agent.
  - register_openclaw_agent.py: clear only <openclaw_home>/agents/nulla/
    sessions on install (gated by NULLA_CLEAR_OPENCLAW_SESSIONS, set by
    the installers and not by the every-launch re-register, so a normal
    launch never wipes a live session); force the global default model to
    the NULLA agent's own model when NULLA is the default agent, leaving
    a deliberate non-NULLA default untouched.

The session-init race itself is upstream (openclaw #101250); the bundled
retry patch is unchanged. Contract tests updated to the new launcher
shape. The .cmd/.bat/.sh edits are string-contract-tested but not
executed on CI and need a manual Windows smoke test.
…A agent

OpenClaw's default heartbeat cadence (30m) runs heartbeat turns in the agent main
session (agent:nulla:main); those scheduled turns mutate the reply-session entry
during normal dashboard chat and produce "reply session initialization conflicted
for agent:nulla:main" -- the second-message failure in KAS-049. Write a per-agent
override "heartbeat": {"every": "0m"} (OpenClaw's documented disable value) into the
NULLA agent registration so fresh installs land with scheduled heartbeats off. Pairs
with the default-model force (nulla/nulla) and the gated session clear already on this
branch. Test asserts the registered agent entry carries heartbeat.every == "0m".
@Parad0x-Labs

Copy link
Copy Markdown
Owner Author

Added the KAS-049 root-cause fix (commit 28fa1b1): the NULLA agent registration now writes "heartbeat": {"every": "0m"} so OpenClaw's default 30m scheduled heartbeat no longer runs in agent:nulla:main and corrupts reply-session reuse (the second-message conflict). This is the piece KAS validated as the real fix.

PR #92 now carries all three parts of the validated remediation:

  • heartbeat.every: 0m on the NULLA agent (stops the scheduled agent:nulla:main turns).
  • global default model forced to nulla/nulla (was a stale ollama/* setdefault).
  • fresh-install clear of only <openclaw_home>/agents/nulla/sessions (gated by NULLA_CLEAR_OPENCLAW_SESSIONS, set by the installers, not the every-launch re-register) so already-broken installs reset the failed session row.

Windows smoke test for KAS before merge:

  1. Fresh install -> generated openclaw.json shows agents.list[nulla].heartbeat.every == "0m" and agents.defaults.model.primary == "nulla/nulla"; OpenClaw logs heartbeat: disabled.
  2. Second dashboard message in the same session no longer errors with "reply session initialization conflicted for agent:nulla:main".
  3. Already-broken install: reinstall clears agents/nulla/sessions (a failed agent:nulla:main row is not reused).
  4. The launcher reconcile (/healthz + kill-only-NULLA-owned + restart) behaves on a wedged API.

Do not self-merge — waiting on the Windows verification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant