* fix: gateway service commands refuse a named profile or relocated OPENCLAW_HOME
- Resolve the default install identity against the canonical state directory
for the active OpenClaw home and profile instead of the unprofiled OS
account default.
- `--profile <name>` / `--dev` project `.openclaw-<profile>` state and config
paths, so every named profile was classified as isolated state and refused
`install`, `start`, `stop`, `restart`, `uninstall`, Doctor service repair,
and self-update service handling.
- `OPENCLAW_HOME` relocates all OpenClaw path defaults and is documented for
running as a dedicated service user; a relocated home is now an install
identity. `HOME` alone still is not.
- An `OPENCLAW_STATE_DIR` or `OPENCLAW_CONFIG_PATH` pointing outside those
canonical paths is still treated as isolated state.
- Recovery guidance in the refusal message now names the paths that must match.
Verified: focused vitest shards for the changed suites plus the daemon, CLI,
and doctor suites that consume the identity check; tsgo core and core-test
lanes; oxlint; docs format, MDX, link, and map checks.
* fix(gateway): keep relocated homes isolated
* fix(config): validate service profile identity
* fix(daemon): enforce named-profile service ownership
* fix(update): reject drifted service selectors before probes
* test(windows): prove scheduled task lifecycle
* test(windows): harden scheduled task proof cleanup
* test(windows): bind lifecycle proof to checkout
* test(windows): normalize cleanup exit status
* test(windows): verify effective task privilege
* test(windows): protect scheduled task proof roots
* test(windows): prove listener-owned task lifecycle
* test(windows): fix scheduled task proof contracts
* test(windows): remove redundant mock coercions
* test(windows): measure fallback before task probes
* test(windows): prove scheduled task process origin
* fix(gateway): preserve unmanaged restart fallback
* test(gateway): cover denied restart ownership
* test(gateway): keep restart helper types private
* test(gateway): classify lifecycle helpers as test code
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
`openclaw config patch` with a null leaf at an array index removed three
entries instead of one, and reported success. runConfigOperations spliced the
element out of the in-memory config and also pushed the path into unsetPaths,
which the writer replays twice against the persist candidate, so every replay
removed one more neighbour.
runConfigUnset already suppresses unsetPaths for array leaves (fb73f2161e,
issue #76290). runConfigOperations ignored the leafContainer that unsetAtPath
returns and pushed unconditionally. It now applies the same guard, so object
keys keep their write-level unset path and array indices do not.
* fix(cli): make commands, completion, and JSON output reliable
* fix(cli): reconcile completion coverage with current main
* test(cli): keep test routing stable across isolation lanes
Keep log retrieval diagnostic-first and non-mutating while core configuration validation remains active. Explicit Doctor runs continue to own plugin-sensitive validation and state migration.\n\nFixes #116853
* feat(cli): add openclaw automations alias and reword cron display prose
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhJ8EiMXue6ADLmHfb7FL6
* test(doctor): update cron doctor prose assertions for automations wording
* fix(plugins): include command aliases in plugin CLI collision detection
Codex review finding on the automations alias: plugin CLI registration
seeded existingCommands from command names only, so a plugin exposing a
top-level command matching an alias-only root name (automations, terminal,
chat) would crash Commander at startup instead of being skipped. Seed from
names plus aliases; regression test covers the alias path.
* fix(cli): rename residual cron prose in CLI and gateway RPC errors
Found in combined dev-gateway E2E: automation not found / unknown automation
id errors, add/edit prose, docs tip, skills-cli mention, and the gateway RPC
not-found message. The CLI missing-get matcher accepts both message forms so
older gateways keep resolving name lookups.
* fix(gateway): keep cron.get missing wording as a wire contract for older CLI matchers
ClawSweeper rank-up: shipped CLI matchers parse 'cron job not found: <id>'
before the name-lookup fallback; the rename stays CLI-display only. Adds a
regression pinning the exact wire form.
* fix(cli): rename doctor and task-summary cron prose flagged in review
Repair-plan advisories, session-registry task summary, and the heartbeat
migration health check now say automations; recreate hints use the
openclaw automations CLI form.
---------
Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
After upgrading on Linux, a user-scope unit
(~/.config/systemd/user/openclaw-gateway.service) and a system-scope unit
(/etc/systemd/system/openclaw-gateway.service) can both exist and both try to
manage the gateway. They bind the same port and each instance's stale-process
detection SIGTERMs the other, producing an endless restart cascade (#79375).
The detector `findInstalledSystemdGatewayScope` checked the user path first and
returned early, so it could never surface that both scopes coexist.
This adds, in three layers:
1. Detection: `findSystemdGatewayInstallation` — a discriminated union
(none/user/system/dueling) that reports every installed scope without
early-returning. `findInstalledSystemdGatewayScope` is refactored to delegate
to it while preserving the exact user-first preference its four lifecycle
callers rely on (no behavior change for stop/restart/is-enabled/runtime).
Adds `uninstallUserSystemdGatewayUnit` (removes only the $HOME user unit, no
root needed) and the pure `formatDuelingScopesWarning` helper.
2. Doctor: `maybeResolveDuelingSystemdGatewayScopes` detects the dueling state
and, after the existing confirm/policy gate, removes the redundant user-scope
unit while keeping the root-installed system unit authoritative. Declining or
an externally-managed policy falls back to the existing cleanup hints.
3. Startup guard: in service mode, when a stale-kill actually happened, log a
targeted remediation pointing at `openclaw doctor --fix` instead of letting
the loop look like routine stale cleanup. Diagnostic only — the kill decision
is unchanged.
* feat(cli): run agent exec against the ambient config, composed in memory
Exec previously ignored the operator's config entirely, so a one-shot turn
could not reach configured providers, credentials, or agentRuntime harness
selection. It now layers config the way other folder-scoped coding CLIs do.
The composed config is published as this process's runtime snapshot rather
than serialized to a temp file and re-read through OPENCLAW_CONFIG_PATH. The
snapshot is the only in-process config cache, so the file only ever fed it --
while writing env-substituted provider keys to disk where the run's own exec
tool could read them.
* fix(cli): resolve exec stored credentials from the configured agent dir
* chore(scripts): allow agent exec the file-scoped config loader at its process boundary
* test(cli): cover the exec credential default and pinned-config flags
* refactor(config): move model normalization to input owners
* fix(config): merge canonical doctor model rows
* fix(config): align touched model map identities
* ci(env): lower production variable budget
* chore: leave changelog to release automation