Skip to content

fix(doctor): prevent non-interactive --fix from auto-restarting gateway#94148

Merged
steipete merged 3 commits into
openclaw:mainfrom
zhangguiping-xydt:feat/issue-78217
Jun 22, 2026
Merged

fix(doctor): prevent non-interactive --fix from auto-restarting gateway#94148
steipete merged 3 commits into
openclaw:mainfrom
zhangguiping-xydt:feat/issue-78217

Conversation

@zhangguiping-xydt

@zhangguiping-xydt zhangguiping-xydt commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

What problem does this PR solve?

doctor --fix --non-interactive needed to stop auto-restarting an already-running gateway after a transient health/probe failure, but the earlier PR branch also made loaded-but-stopped gateway services stop auto-starting. Root cause: the stopped-service start prompt was marked interactive-only, so DoctorPrompter.confirmRuntimeRepair could no longer auto-approve that repair in normal non-interactive --fix mode.

Why does this matter now?

Cron, launchd, systemd timer, and other unattended doctor runs rely on stopped-service repair for gateway self-healing. Keeping restart blocked for a running service is the safety fix; blocking start for an actually stopped service removes the recovery behavior that --fix --non-interactive is supposed to provide.

What is the intended outcome?

This is a root-cause lifecycle-scoping fix: the already-running restart path remains interactive-only, while the stopped-service start path again follows the existing non-interactive repair auto-approval policy. The source-of-truth boundary is the internal gateway daemon repair flow plus DoctorPrompter repair-mode policy; this PR does not change any public API, user config, schema, protocol, migration, or provider contract.

What is intentionally out of scope?

Gateway install policy, external supervisor policy, update-in-progress repairs, restart-handoff behavior, LaunchAgent/bootstrap handling, port/probe diagnostics, and any public contract/default changes are intentionally out of scope. Related PR scan: this is an update to existing PR #94148 for issue #78217, not a new competing contract or compatibility PR.

What does success look like?

A running but temporarily unhealthy gateway is not restarted by doctor --fix --non-interactive, and a loaded but stopped gateway still reaches the start repair path under the same command.

What should reviewers focus on?

Reviewers should focus on the lifecycle split in maybeRepairGatewayDaemon: stopped-service start repair should be auto-approved by normal non-interactive --fix, while running-service restart repair should require interactive confirmation.

Linked context

Which issue does this close?

Closes #78217

Which issues, PRs, or discussions are related?

Related: #94148 existing review noted that the previous patch over-broadened the non-interactive lifecycle skip by covering stopped-service starts.

Was this requested by a maintainer or owner?

No direct maintainer request; this update addresses the review concern on the existing PR branch.

Real behavior proof (required for external PRs)

  • Behavior or issue addressed: doctor --fix --non-interactive should not restart an already-running gateway service after a transient health/probe failure, while stopped gateway services continue through the normal start-repair path described in the validation section.
  • Real environment tested: Linux systemd user gateway, Node 24.16.0, OpenClaw current PR head 10b5073.
  • Exact steps or command run after this patch:
    pnpm openclaw gateway status --deep
    pnpm openclaw doctor --fix --non-interactive
    pnpm openclaw gateway status --deep
  • Evidence after fix (screenshot, recording, terminal capture, console output, redacted runtime log, linked artifact, or copied live output):
    copied live terminal output
    before_runtime=Runtime: running (pid 1964, state active, sub running, last exit 0, reason 0)
    doctor_complete=yes
    after_runtime=Runtime: running (pid 1964, state active, sub running, last exit 0, reason 0)
    pid_unchanged=yes
    
  • Observed result after fix: the copied live output shows the real running gateway kept pid 1964 before and after doctor --fix --non-interactive, and the doctor command completed without restarting that service.
  • What was not tested: a real stopped systemd/launchd service auto-start end-to-end, because stopping the local gateway would interrupt the active development environment.
  • Proof limitations or environment constraints: the live proof used the existing running systemd user gateway for the no-restart behavior and isolated temporary OpenClaw config/state for the doctor command so normal user config was not modified.
  • Before evidence (optional but encouraged): on the previous PR head, the new stopped-service regression failed with expected "vi.fn()" to be called 1 times, but got 0 times, proving the earlier patch skipped stopped-service starts.

Tests and validation

Which commands did you run?

pnpm exec vitest run src/commands/doctor-gateway-daemon-flow.test.ts --testNamePattern "starts stopped service during non-interactive repairs"
pnpm exec vitest run src/commands/doctor-gateway-daemon-flow.test.ts --testNamePattern "skips running service restart during non-interactive repairs"
pnpm exec vitest run src/commands/doctor-gateway-daemon-flow.test.ts
pnpm openclaw gateway status --deep
pnpm openclaw doctor --fix --non-interactive
pnpm openclaw gateway status --deep

What regression coverage was added or updated?

  • Updated the stopped-service non-interactive repair scenario to require the start repair path to call service.restart() once when serviceRuntime.status is stopped.
  • Kept the running-service non-interactive repair scenario requiring no automatic restart when the service is already running and only health/probe status is bad.
  • Guardrail rationale: these two scenarios pin the intended lifecycle split so future prompt-policy changes cannot collapse stopped start and running restart into the same non-interactive behavior.
  • Full related test file result: 25 tests passed.

What failed before this fix, if known?

FAIL starts stopped service during non-interactive repairs
AssertionError: expected "vi.fn()" to be called 1 times, but got 0 times

If no test was added, why not?

A regression test was updated for the stopped-service branch and kept alongside the existing running-service restart guardrail.

Risk checklist

Did user-visible behavior change? (Yes/No)

Yes. doctor --fix --non-interactive again auto-starts a loaded but stopped gateway service, while still refusing to auto-restart a running gateway service.

Did config, environment, or migration behavior change? (Yes/No)

No.

Did security, auth, secrets, network, or tool execution behavior change? (Yes/No)

No.

What is the highest-risk area?

Gateway service lifecycle semantics in non-interactive repair mode.

How is that risk mitigated?

The fix only removes the interactive-only flag from the stopped-service start prompt; the running-service restart prompt remains interactive-only. Regression tests cover both branches directly, and live proof shows a running gateway keeps the same PID across doctor --fix --non-interactive.

Current review state

What is the next action?

Ready for maintainer review after this PR branch update settles.

What is still waiting on author, maintainer, CI, or external proof?

No known author-side code changes remain. Remote CI and hosted ClawSweeper will need to run on the pushed head.

Which bot or reviewer comments were addressed?

  • Addressed the over-broad lifecycle skip concern by preserving stopped-service auto-start while retaining interactive-only running-service restart.
  • RF-001 was a ClawSweeper infrastructure failure rather than a code finding; a fresh local ClawSweeper commit review for 10b5073 reported Nothing found.

@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. size: XS labels Jun 17, 2026
@clawsweeper

clawsweeper Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 22, 2026, 3:43 AM ET / 07:43 UTC.

Summary
The PR adds a non-interactive guard before the running Gateway restart prompt and expands daemon-flow tests to preserve stopped-service auto-start and explicit restart approvals.

PR surface: Source +4, Tests +33. Total +37 across 2 files.

Reproducibility: yes. source-level: current main can reach the running-service restart prompt after a failed Gateway health/probe path, and doctor --fix --non-interactive auto-approves runtime repair prompts. I did not run the service-level scenario locally because this review is read-only.

Review metrics: 1 noteworthy metric.

  • Runtime restart policy changed: 1 running-service restart path changed. This prompt controls whether unattended doctor repair can restart an already-running Gateway during scheduled maintenance or recovery.

Stored data model
Persistent data-model change detected: migration/backfill/repair: src/commands/doctor-gateway-daemon-flow.test.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #78217
Summary: This PR is the open candidate fix for the canonical non-interactive doctor running-Gateway restart/SIGTERM issue.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

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

Rank-up moves:

  • [P2] Maintainers should explicitly accept the headless running-Gateway restart behavior change before merge.

Risk before merge

  • [P1] Existing unattended workflows that expected doctor --fix --non-interactive to restart an already-running unhealthy Gateway will now need an explicit openclaw gateway restart, interactive doctor run, or --yes path.
  • [P1] Stopped-service auto-start is covered by focused tests, but the PR body does not include live stopped systemd or launchd proof for that half of the lifecycle split.

Maintainer options:

  1. Accept explicit restart handoff (recommended)
    Merge after maintainers agree that non-interactive doctor repair should not restart an already-running Gateway by default and explicit gateway commands own that lifecycle action.
  2. Add an opt-in unattended restart path
    If unattended running-service restarts are still required, add a separate opt-in flag or command path with tests instead of generic repair prompt auto-approval.
  3. Pause if implicit restarts stay required
    Hold or close this PR if the product decision is that doctor --fix --non-interactive must keep restarting already-running unhealthy Gateways by default.

Next step before merge

  • The branch has no concrete contributor-facing code defect from this review; the remaining action is maintainer acceptance of the non-interactive Gateway restart compatibility tradeoff and normal merge gates.

Security
Cleared: The diff only changes doctor Gateway lifecycle policy and colocated tests; it adds no dependencies, workflows, scripts, secrets handling, or new code execution paths.

Review details

Best possible solution:

Land the narrow lifecycle split after maintainers accept that headless doctor repair may start stopped services but must not implicitly restart an already-running Gateway.

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

Yes, source-level: current main can reach the running-service restart prompt after a failed Gateway health/probe path, and doctor --fix --non-interactive auto-approves runtime repair prompts. I did not run the service-level scenario locally because this review is read-only.

Is this the best way to solve the issue?

Yes for implementation shape: the PR fixes the implicated owner path in maybeRepairGatewayDaemon and preserves stopped-service start plus explicit restart approvals. The remaining question is maintainer acceptance of the compatibility tradeoff for headless running-service restarts.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against f9ebb8d91bea.

Label changes

Label justifications:

  • P2: This is a normal-priority operator workflow bug fix for Gateway doctor lifecycle repair with limited blast radius.
  • merge-risk: 🚨 compatibility: The diff changes whether unattended doctor repair may restart an already-running unhealthy Gateway service.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body includes copied live Linux systemd output showing a running Gateway kept the same PID across doctor --fix --non-interactive; the latest maintainer commit preserves that behavior while adding explicit approval coverage.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes copied live Linux systemd output showing a running Gateway kept the same PID across doctor --fix --non-interactive; the latest maintainer commit preserves that behavior while adding explicit approval coverage.
Evidence reviewed

PR surface:

Source +4, Tests +33. Total +37 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 4 0 +4
Tests 1 36 3 +33
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 40 3 +37

What I checked:

Likely related people:

  • giulio-leone: History shows this contributor added the non-interactive doctor --fix auto-approval behavior and update-mode no-restart guard that this PR scopes for Gateway service repairs. (role: introduced adjacent behavior; confidence: high; commits: 3359dcfdcf17, d8aada9d4516; files: src/commands/doctor-prompter.ts, src/commands/doctor-repair-mode.ts, src/commands/doctor-gateway-daemon-flow.test.ts)
  • liaoyl830: Authored the merged recent-restart Gateway doctor fix in the same daemon repair flow and tests around restart prompt suppression. (role: recent adjacent contributor; confidence: high; commits: 6e8d2dbbbcf8; files: src/commands/doctor-gateway-daemon-flow.ts, src/commands/doctor-gateway-daemon-flow.test.ts)
  • steipete: History shows repeated doctor repair-flow and Gateway daemon lifecycle work in the affected files, and the latest PR branch commit also narrows this lifecycle guard. (role: doctor flow contributor; confidence: high; commits: 9e22f019db0c, 5c9e4cd30a1e, d4a539fc8021; files: src/commands/doctor-gateway-daemon-flow.ts, src/commands/doctor-prompter.ts, src/commands/doctor-gateway-daemon-flow.test.ts)
  • vincentkoc: Current-line blame and release history show this contributor carried the affected doctor files through current main and the latest release snapshot. (role: recent area contributor; confidence: medium; commits: 2c3519c1d7ff, c645ec4555c0; files: src/commands/doctor-gateway-daemon-flow.ts, src/commands/doctor-prompter.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.

@zhangguiping-xydt

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels Jun 17, 2026
@zhangguiping-xydt

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 18, 2026
@steipete steipete self-assigned this Jun 22, 2026
@steipete
steipete force-pushed the feat/issue-78217 branch 4 times, most recently from 82d5728 to d4a539f Compare June 22, 2026 07:30
@steipete

steipete commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Land-ready maintainer verification:

  • Reviewed the doctor gateway-daemon repair flow, prompt policy, stopped/running service branches, --yes, and recent restart handoff. The final guard applies only to explicit --non-interactive repair of an already-running service.

  • Fresh autoreview: clean after narrowing the original broad interactive-confirmation change.

  • Focused tests: src/commands/doctor-gateway-daemon-flow.test.ts and src/commands/doctor-prompter.test.ts — 33 passed.

  • Live Linux E2E: Azure Crabbox run run_f8b6ef222ff7 installed and started an isolated systemd user gateway, ran doctor --fix --non-interactive, and verified the service stayed active with the same MainPID (17596) before and after.

  • Current prepared head: 60d9d1c2424cada7b6aaff3d6f7919ed1e362e23; exact-head hosted CI/Testbox passed in run 27942684378.

  • scripts/pr prepare-run 94148: completed.

Known gaps: issue #78217 also describes timeout/port diagnostics outside this PR's self-restart fix.

@steipete
steipete force-pushed the feat/issue-78217 branch 3 times, most recently from b3f23b2 to e360313 Compare June 22, 2026 08:54
zhangguiping-xydt and others added 3 commits June 22, 2026 05:21
Mark gateway service start/restart lifecycle prompts as requiring
interactive confirmation, so `doctor --fix --non-interactive` skips
them instead of auto-approving via shouldAutoApproveDoctorFix.

Previously, a transient gateway health probe timeout would cause the
non-interactive repair path to auto-approve `service.restart()`,
which could SIGTERM a running gateway.
@steipete
steipete merged commit 8625b8a into openclaw:main Jun 22, 2026
201 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged via squash.

Thanks @zhangguiping-xydt!

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 23, 2026
…ay (openclaw#94148)

Merged via squash.

Prepared head SHA: 60d9d1c
Co-authored-by: zhangguiping-xydt <[email protected]>
Co-authored-by: steipete <[email protected]>
Reviewed-by: @steipete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

doctor --fix self-terminates with SIGTERM when gateway is running

2 participants