Skip to content

feat(gateway): lane-health watchdog + post-deploy verification gate#34

Merged
patelmm79 merged 1 commit into
mainfrom
fix/lane-starvation-observability
Jun 29, 2026
Merged

feat(gateway): lane-health watchdog + post-deploy verification gate#34
patelmm79 merged 1 commit into
mainfrom
fix/lane-starvation-observability

Conversation

@patelmm79

Copy link
Copy Markdown
Contributor

Why

On 2026-06-28 three user pings on agent:linux_desktop_seed:discord:channel:1492701850217218268 went unanswered for 13 hours. Root cause: [email protected] does not preempt a wedged model_call lane. One agent's runaway model call held the event loop for 275s (recovery=none, liveness warning eventLoopDelayMaxMs=1841.3), starving every sibling agent lane.

The upstream runtime logs [diagnostic] long-running session ... recovery=none but takes no operator action. This PR ships the L3b-side mitigation that the upstream bug forces us to own.

Full incident timeline + root causes in linux-desktop-seed/docs/incidents/2026-06-28-multi-session-gateway-starvation.md.

What

  1. scripts/lane-health-probe.sh — watchdog that scans the gateway journal every 30s, kills wedged lanes (openclaw lane kill best-effort), alerts operator.
  2. scripts/install/install-lane-health-probe.sh — installs the probe as a user systemd timer.
  3. scripts/post-deploy-verify-lane-health.sh — deploy-time gate that fails the deploy if any lane is wedged.
  4. scripts/install/deploy.sh — wires both into the install flow (steps 7 + 8).
  5. scripts/lib-extract-wedged-lanes.py — single source of truth for the long-running-session regex.
  6. tests/lane-health-probe.bats — 13 cases (all passing).

Tests

bats tests/lane-health-probe.bats
# 1..13
# 13/13 passing
shellcheck scripts/{lane-health-probe,post-deploy-verify-lane-health,install/install-lane-health-probe}.sh
# only intentional warnings (SC2016 in bash -c single-quote arg, SC2001 style)

Caveats / known limits

  • This is mitigation, not fix. The actual lane-scheduler fix lives in the upstream openclaw/openclaw npm package and requires upstream maintainer action. I'll file the upstream issue + minimal-patch PR separately.
  • The probe assumes a user systemd environment. On systems without systemd --user, the probe still runs (via direct bash scripts/lane-health-probe.sh) but loses the 30s scheduling — caller is expected to install the timer.
  • openclaw lane kill is a hypothetical subcommand in [email protected] (the current binary may not have it). The probe falls back to systemctl --user restart openclaw-gateway.service when the subcommand is missing. Once upstream ships lane-management subcommands, the probe will use them automatically.

Behavior knobs

Env var Default Effect
LANE_WALL_BUDGET_SECONDS 90 Wall-clock seconds before a lane is considered wedged
LANE_GRACE_SECONDS 30 Stream-progress grace period
SKIP_LANE_HEALTH_PROBE=1 (deploy) Skip the probe installer during deploy
SKIP_POST_DEPLOY_LANE_CHECK=1 (deploy) Skip the post-deploy gate
OPENCLAW_OPERATOR_CHANNEL (unset) Discord channel id for alerts

Refs: DarojaAI/linux-desktop-seed#1054 (postmortem), openclaw/openclaw issue TBD

Adds L3b-side mitigation for the upstream bug in [email protected]
where a wedged model_call lane starves all sibling agent lanes. The
upstream runtime logs 'long-running session ... recovery=none' but
takes no operator action and never preempts. Tracked in:
  DarojaAI/linux-desktop-seed/docs/incidents/2026-06-28-multi-session-gateway-starvation.md

This commit ships three pieces:

1. scripts/lane-health-probe.sh
   Scans the gateway user journal every 30s (via user systemd timer)
   for  events. When a lane exceeds the wall-
   clock budget (default 90s) with no stream progress AND recovery=none,
   kills it via 'openclaw lane kill' (best effort) and Discord-alerts
   the operator channel. Debounced per (sessionKey, hour).

2. scripts/install/install-lane-health-probe.sh
   Installs the probe as a user systemd timer (on-boot 30s, every 30s).
   Writes operator-channel to ~/.openclaw/operator-channel (mode 0600).
   Refuses on missing systemctl / unset HOME.

3. scripts/post-deploy-verify-lane-health.sh
   Health gate invoked at the end of deploy.sh. Refuses to declare a
   deploy healthy if any lane in the last 60s is wedged. Forces the
   operator to restart cleanly or acknowledge the wedge.

4. scripts/install/deploy.sh
   Wires the installer (step 7) and the verifier (step 8). Both can
   be skipped with SKIP_LANE_HEALTH_PROBE=1 / SKIP_POST_DEPLOY_LANE_CHECK=1
   for emergency bypasses.

5. scripts/lib-extract-wedged-lanes.py
   Single source of truth for the long-running-session regex. Tolerant
   of optional fields (reason=, activeTool=, ...). Honors
   WEDGED_MIN_AGE_SECONDS env var.

6. tests/lane-health-probe.bats (13 cases, all passing)
   Covers detection, debouncing, filtering by kind/recovery/grace,
   installer behavior (operator channel, systemd units, symlink),
   post-deploy gate pass/fail. Uses stub journalctl + openclaw +
   systemctl on a sandboxed HOME.

Verified: bats tests/lane-health-probe.bats 13/13 passing.
           shellcheck on all new scripts: only intentional warnings.

Refs: docs/incidents/2026-06-28-multi-session-gateway-starvation.md (L3a)
      openclaw/openclaw issue TBD (upstream RC1-RC4 patches)
@patelmm79
patelmm79 force-pushed the fix/lane-starvation-observability branch from 4be23a8 to bd2cfd2 Compare June 29, 2026 00:17
@patelmm79
patelmm79 merged commit 963b99d into main Jun 29, 2026
4 checks passed
@patelmm79
patelmm79 deleted the fix/lane-starvation-observability branch June 29, 2026 00:27
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