Writing to a pipe whose reader has already exited raises SIGPIPE, which
kills the whole process instead of throwing. MacNodeHostWorker already
guarded its stdin pipe with F_SETNOSIGPIPE; the codex App Server client
and the MLX TTS helper transport did not, so a child exiting mid-write
could take down the app. Suppressing the signal exposed that an
undelivered App Server request write was reported as appServerUnavailable
even though the frame was provably never sent, so it now requeues once
onto a fresh child instead of failing the caller.
Test-side pipe write ends whose readers are spawned children (or a
readability handler that can close the pipe mid-test) get the same
suppression so a racing reader exit fails the assertion instead of
killing swiftpm-testing-helper with signal 13, which is what caused the
macos-swift CI lane's intermittent unrelated-test crashes (e.g. PR
#126559, run 32341197738 job 96340683947).
The gateway HTTP auth-bypass path cache was keyed only by config object
identity, so replacing a channel plugin's declared bypass contract while
config identity stayed stable preserved obsolete unauthenticated HTTP
routes. It now clears on plugin metadata lifecycle resets alongside every
other plugin-derived process memo.
notifyPluginMetadataChanged cleared the process plugin metadata snapshot
slot, but when config bytes were unchanged the config-reload diff took its
early no-op return and skipped both plugin reload and republishing, leaving
the snapshot slot empty so configless readers cold-scanned repeatedly. An
unchanged-bytes metadata signal now forces a plugin-reload plan so the
runtime generation republishes.
* feat(ui): add cpu/memory graphs to system busyness overlay
* refactor(ui): redesign busyness overlay vitals as sparkline stat tiles
Merge the duplicated sparkline row and numeric tile grid into three vital
tiles (CPU, memory, event-loop delay) with gradient area charts, a live
pulse dot, pointer scrubbing with value+age readout, and a danger tint
driven by the gateway's own event-loop degradation reasons. Memory
auto-ranges its baseline so RSS trends stay visible.
enableConsoleCapture() replaces every console method with a forwarder and
routeLogsToStderr()/`--json` mode latch loggingState.forceConsoleToStderr,
which production only unwinds at process exit. loggingState is keyed off
globalThis so it survives vi.resetModules(), so a shared Vitest worker carried
both into the next file: the inherited forwarder writes to process.stderr and
the next file's console.error spy records nothing.
That is the source of the order-dependent
`src/entry.run-main.test.ts > keeps expected conditions at exit 1` flake in
shard core-unit-src-security-2 — it failed only on workers that ran
src/mcp/codex-supervision-tools-serve.test.ts (serveCodexSupervisionToolsMcp
-> routeLogsToStderr) first.
Restore the worker's baseline console methods and logging state in the
runner's existing cross-file cleanup, and drop the two consumer-side
workarounds that were compensating for it.
approvals get --json and every local approvals write emitted
file.socket.token in clear text, while the gateway RPC, the node-host RPC,
and the state snapshot sanitizer all strip it, and the human-readable table
shows only the socket path.
The token is root key material: operator-approval-runtime-token.ts HMACs it
to authorize local operator-approval clients, and agent-runtime-identity-token.ts
reads it for the agent runtime identity token.
Promote one canonical redactExecApprovals to src/infra/exec-approvals.ts,
retire the two duplicate copies, and apply it to the local CLI JSON paths.
It also drops the snapshot's raw field, which carried the same token as
stored JSON.
When two config writes raced, the loser printed a bare "config changed
since last load" and exited 1 — no statement that its write was abandoned
and no hint that re-running works. The gateway sibling already says
"re-run config.get and retry".
Format ConfigMutationConflictError at handleConfigMutationError, the single
CLI boundary, so the low-level throw sites keep serving gateway and
programmatic callers unchanged. Dry-run JSON now reports kind "conflict"
instead of mislabelling a concurrency conflict as a schema error.
Concurrency control itself is unchanged: baseHash pinning and the retry
loop stay exactly as they were.
Table-driven regressions prove personal and group sends never carry a
Teams thread suffix while channels preserve ;messageid, and that agent
react actions route to the prepared Graph target per conversation kind
with exact result payloads and inbound reaction event labels.
Parity proof for Microsoft Lobster patches 0038/0026 via
giodl73-repo/lobster-plugins-and-patches.
* fix(crabbox): decouple lease heartbeat budget from cadence
Give coordinator lease heartbeats an independent 150-second execution budget, capped at half the idle timeout, after production AWS samples reached 107.6 seconds. Preserve the renewal cadence and unrelated lifecycle timeouts while identifying Crabbox v0.44.0 as the first heartbeat-capable release.
* refactor(crabbox): parse profile durations once
requirePositiveDuration already parsed the duration to validate it, then
discarded the result so the caller re-parsed and coerced an Option to a
number without handling undefined. Return the parsed milliseconds instead:
one parse, and no path where a NaN idle timeout reaches the heartbeat
budget.
* feat(ui): add Person grouping mode for sessions sidebar and sessions page
Sessions can now be grouped by their durable owner identity: the sidebar
Group-by menu gains a capability-gated Person mode (self first, humans by
label, agent identities after; ownerless rows keep their smart zones), and
the sessions page gains the matching person mode. Person section headers
render the owner avatar and profile label; the mock dev server now
advertises the multi-identity policy and carries explicit row owners the
way the gateway projects createdActor fallbacks.
* feat(ui): gate sessions-page Person grouping on the identity capability
Mirrors the sidebar: the Person option hides without
hasMultipleSessionSharingIdentities and a stored Person preference
renders as None until the capability returns.
The workflow ran a Codex agent over the full test suite and pushed
`test: optimize slow tests` straight to `main` under `contents: write`, with no
pull request and no human review. Its gates were a path allowlist, a
no-add/delete/rename rule, a non-decreasing total test count, and
`pnpm check:changed` -- which covers changed lanes, not the full suite. Test
optimization is exactly the class of change where a plausible edit can weaken
coverage without moving the test count, so unattended landing is the wrong
trade. Autonomous commits to `main` are not something this repo wants.
It had also been inert since well before this. The daily-cadence gate excluded
prior runs with `select(.status != "cancelled")`, but a finished cancelled run
reports `status: "completed"` with `conclusion: "cancelled"` -- verified against
run 32506655531, which that filter counts as a prior run. Its `concurrency`
block sets `cancel-in-progress: false`, so main's push rate produced dozens of
cancelled runs per hour and every trigger skipped, reporting green after ~2
minutes of doing nothing. No `test: optimize slow tests` commit has ever landed
on `main`.
`pnpm test:perf:groups` and the rest of the performance tooling it drove stay;
they are useful by hand and documented in docs/reference/test.md.
Repository secret OPENCLAW_TEST_PERF_AGENT_OPENAI_API_KEY now has no consumer
and can be deleted.
openclaw status exited 0 with a complete, healthy-looking report when the
config file failed validation, silently substituting defaults for the
operator's authored values. Every sibling command — config validate,
agents list, models list, doctor — reported the failure.
readBestEffortConfigSnapshotFromContext already read snapshot.valid and
dropped it, and BestEffortConfigSnapshot had nowhere to carry it, so no
consumer of the best-effort path could report it. Carry the fact from its
producer as configDiagnostics and render it in text, --json, and --all.
The sibling readSourceConfigBestEffortFromContext already states the rule
seventeen lines below: best-effort legitimizes the fallback value, not the
silence.
* Let Codex run newly available account models without host credentials
* Keep authored provider routes ahead of native account authentication
* Keep native Codex account discovery consistent with model availability
* Preserve advanced reasoning for newly discovered Codex models
The /restart command is dispatched from a durable channel ingress row. The
gateway begins its restart drain before that dispatch returns, so the row is
still claimed when root-work admission closes. The drain sees
GatewayDrainingError and releases the row without spending its retry budget
(#125918), which is correct for an ordinary message and wrong for a command
that caused the drain: the successor gateway claims the same row and runs
/restart again, forever. Every boot is clean, so the gateway looks healthy
throughout.
Adopt the ingress claim in the restart command handler before scheduling the
restart. The command is not idempotent, so losing the acknowledgement is better
than an unbounded restart loop. The drain's deferral branch is unchanged.
Co-authored-by: Marvinthebored <262704729+Marvinthebored@users.noreply.github.com>