* fix(gateway): bound audit and Codex backlogs
Live Gateway SQLite lock failures and process heap pressure exposed two
independent queue owners. Route best-effort audit persistence through the
canonical shared-state connection with bounded contention retries, and remove
the per-notification Codex yield so the keyed turn queue can drain directly.
Follow-up to #126033 and #126073.
* fix(gateway): annotate raw SQLite cold-open probe
* test(codex): register notification burst shard
The cloud machine picker described each class in prose ("Cheap smoke checks
and small repos"), which was the widest thing in the row, got ellipsized, and
did not say what the operator actually picks on. Machine options now carry the
class shape and the picker renders "32 vCPU · 64 GB" instead.
Crabbox reports per-provider class shapes from `providers --json`; the plugin
reads that catalog once per lifecycle and attaches cpu/memoryGb to each option.
A missing binary, failed command, unparseable output, absent classes, or an
unmatched provider all degrade to label-only rows, so an older Crabbox keeps
working.
`description` was never released, so it is removed rather than deprecated, and
listMachineOptions becomes async for the catalog read. Deletes the duplicate
machine-option projector in the environments server method.
The Control UI settings page (#124864) and the New Session place
picker's machine section had no doc coverage — the configuration and
dispatch walkthroughs only described the JSON/RPC paths.
* fix(gateway): apply configured edge auth to session-URL targets
Follow-up to #125700.
A URL target with explicit auth skipped config loading and silently dropped
gateway.remote.edgeAuth. Config I/O is now skipped only for plaintext loopback
targets.
Also document mandatory passEnv: ["HOME"] for the cloudflared exec provider.
* fix(gateway): keep explicit secure connections working when config is invalid
Addresses the ClawSweeper finding on #125982 by preserving the invalid-config recovery path while still applying configured edge auth.
* fix(workboard): make ownership updates atomic
Persist launch intent before worker acceptance, reconcile accepted identities,
fence cross-host card mutations, and deduplicate session capture in SQLite.
Closes#125932
* test(workboard): keep race fixtures plugin-local
Use a plugin-local synchronization helper and update the sessions-page capture
expectation for the new store-owned RPC.
* fix(workboard): fence lifecycle and restore races
Apply terminal lifecycle state only while the matched association is current,
and converge concurrent archived session restores on the active winner.
* feat(ui): collapse automation sessions in activity feed
* feat(ui): show live run status on activity feed rows
* feat(ui): make run inspector deep-link only
* fix(ui): only link inspect run to a current active run
Addresses ClawSweeper P1: a stale observer digest could deep-link a live
row to an ended run's audit record; trust digest runId only when it is
still in activeRunIds.
The Gateway owns start-or-steer at admission (6515f6a255) and no
client produces expectedRunId anymore (d84a910fc8). The field shipped
only in v2026.8.1-beta.2 - never a stable tag - so it is removed rather
than deprecated. Steer sends resolve the selected session's current
operation; the exact-match branch, the operation|run target identity
discriminator, run_mismatch rejection, and the suggestion producers'
active-run-id selection (with its ambiguity failure) are deleted.
Provider-native turn fencing (Codex expectedTurnId) is unchanged:
the backend-captured runId on the injection target remains.
* fix(gateway): harden hook admission and shutdown
Flush structured 413 and 408 hook responses before closing oversized or timed-out request sockets, and reject deferred wake requests that target an explicit session.
Direct Gateway close now marks startup and readiness as draining immediately and attempts every HTTP listener shutdown before reporting an incomplete close.
* test(gateway): model request destruction in fuzzing
Give the HTTP request fuzz double the IncomingMessage destroy contract exercised after 413 and 408 responses finish.
* fix(gateway): preserve response and shutdown bounds
Route installed webhook body-limit failures through the shared response-first close owner and require complete Feishu 413/408 responses before terminal connection closure.
Retain the current-main grace bound for agent-harness disposal so one stuck harness cannot block listener teardown.
* test(telegram): model response-close request cleanup
Keep the slow-body request alive through response finish, then assert cleanup when the mock response transport closes.
* fix(agents): finalize guided creation safely
Run channel post-write hooks only after config publication, defer portable auth copying until agent creation succeeds without overwriting newer credentials, and provision existing workspaces before publishing updates.
Keep JSON-only guided creation interactive while routing wizard output to stderr so stdout remains one machine-readable summary.
* fix(terminal): preserve note call signature
* fix(agents): pass committed config to setup hooks
* ci: split heavy codex changed-test shards
Cap non-isolated Codex extension processes at 20 files so 4-vCPU changed-target jobs do not starve real-time watches or hit the no-output watchdog.
* test(ci): align codex shard cap fixture
* docs(cli): clarify agents add JSON mode
* fix(config): secure CLI reads and mutations
Use exact plugin metadata for config get redaction, reject impossible SecretRef provider bindings in canonical validation, preserve include CAS provenance, and skip destructive authored no-op rewrites.
* fix(config): preserve inactive SecretRef startup
Keep normal Gateway validation scoped to active secret owners while strict config validation and write paths reject impossible provider/source bindings, including authored no-ops.
* fix(gateway): preserve config patch no-op
Keep unchanged Gateway config.patch requests on the existing no-op path; strict validation remains owned by actual config writes and the config CLI mutation surfaces.
* feat(ui): promote session controls to toolbar
* test(ui): add session toolbar proof capture script
* chore: register session toolbar proof script
* test(ui): target ungrouped session drop zone
* fix(ui): trim session toolbar startup cost
* docs(ui): point new-session guide at the Sessions toolbar
Also name the accepted tradeoff for the inert ungrouped-collapse preference at the partition site (ClawSweeper P1/P3 response).
* test(ui): deflake new-session workspace-memory e2e
Menu-geometry assertions now measure relative to the picker anchor in one atomic evaluate (immune to unrelated page settles, still catches focus-induced moves). The post-reload refill raced the composer's async draft restore, which appended the stored draft to the typed text; waiting for the restored draft asserts the documented persistence instead. Failed CI shard checks-ui-e2e 5/12 on runs 32122284238 attempts 1-2.
* fix(ui): use canonical session owner filter state
* chore(ui): keep session toolbar proof artifacts ignored
* refactor(steering): gateway-owned start-or-steer via one captured-operation lifecycle
chat.send with queue mode steer now atomically captures the selected
session's current direct reply operation under the writer barrier and
injects into exactly that operation; with no direct owner it starts a
new run instead of failing with active-leaf-changed. Client-supplied
expectedRunId keeps exact-match semantics; the transcript-branch CAS
(expectedLeafEntryId) now guards only non-steer sends.
All three steering paths (gateway chat.send, channel queue steering,
server /steer) share one begin/finalize lifecycle on the reply-run
registry, including the captured-instance abort from 5a15e1a39c.
Deleted: the duplicate accepted/rejected/unconfirmed policy machine in
steer adoption, the raw embedded /steer runtime path, the leaf-bound
injection identity and reject-before-ack compat, and the dead target
tool-authority fingerprint. messageInjectionAttempted becomes a typed
messageInjectionDisposition (none | accepted | rejected); rejected
injections take one visible followup fallback, so non-injectable
runtimes queue instead of silently dropping.
Deliberate semantics: a registry-less active embedded run is no longer
steered by raw session id (correlation-only authority) - the message
becomes a visible followup; /steer injects under its own command
authorization instead of the inbound tool-authority gate it could
never satisfy.
* docs(protocol): describe gateway start-or-steer contract for chat.send
Targetless steer is no longer a leaf-bound compatibility path that can
reject with active-leaf-changed; it targets the selected session's
current state (inject into the direct run, else start a turn).
expectedRunId stays an exact-run fence; expectedLeafEntryId is the
non-steer transcript-branch CAS.
* fix(steering): require matching tool authority for /steer injection
The authorized-sender command gate is weaker than tool-authority equality. Make /steer and gateway injection present the same projected evidence as channel steering so mismatches fall back to a normal prompt under the sender’s own authority.
* feat: credit linked session participants as co-authors
Authenticated profiles can link GitHub and receive automatic co-author credit in shared coding sessions.
* style: format rebased co-author registries
* fix: mark profile schema DDL boundary