control socket - part d (final) - #5594
Conversation
…/status verbs Config-gated [control_socket] table (off by default) binds <sessions-dir>/<session-id>/control.sock (0600) per running session, speaking a newline-framed JSON-RPC. Verbs: message (structured user message through the composer dispatch path; queued under load), interrupt (the extracted Esc cancel body, shared with the Esc key path), relaunch (seam: dispatches the /relaunch slash-command path — no mechanics duplicated here), status (turn/goal snapshot answered by the socket thread). Wiring: run_event_loop constructs SessionControl and reconciles/updates/drains once per iteration; the socket runs on background threads with bounded reads (1 MiB) and 5 s dispatch timeouts. Unix-only; non-unix parses the key but refuses to bind. Signed-off-by: M-Maciej <130112810+M-Maciej@users.noreply.github.com>
A second live process holding a session's socket made the per-frame reconcile retry the connect-probe and warn-log every iteration. Retries now back off (5 s in prod, 200 ms under test) keyed on the session id, so switching sessions is never delayed by another session's refusal. Signed-off-by: M-Maciej <130112810+M-Maciej@users.noreply.github.com>
…atforms The listener is nonblocking, and on macOS/FreeBSD an accepted socket inherits O_NONBLOCK from the listener (Linux accepted sockets are blocking). The connection handler assumes blocking reads, so on macOS a large request hit EAGAIN mid-frame, the handler dropped the connection, and the client's in-flight write failed with BrokenPipe — the oversized-request test failed exactly this way on macOS CI. Setting the accepted stream back to blocking (a no-op on Linux) makes the handler's bounded-read model hold on every platform. Signed-off-by: M-Maciej <130112810+M-Maciej@users.noreply.github.com>
The Windows CI gate (cargo test --no-run) denies unused imports and dead code under -D warnings. On non-unix targets the socket transport does not exist, so its imports, timing constants, and request/response types are unreachable there. Split the io/atomic imports and gate the five socket timing constants with cfg(unix), and mark the six protocol types (Request, Method, MessageParams, EmptyParams, ControlCommand, ResponseResult) with a scoped allow: they stay reachable in the portable protocol/parsing tests and on unix builds, and are only unreachable in the plain Windows lib build. The dead-code budget file is untouched. Signed-off-by: M-Maciej <130112810+M-Maciej@users.noreply.github.com>
…mbown#5533) Regenerated crates/tui/CHANGELOG.md via scripts/sync-changelog.sh. Signed-off-by: M-Maciej <130112810+M-Maciej@users.noreply.github.com>
…t module doc The module doc still carried the pre-filing placeholder numbering; the issue is filed as Hmbown#5533 and the changelog/PR text already use it. Signed-off-by: M-Maciej <130112810+M-Maciej@users.noreply.github.com>
|
Thanks @M-Maciej for taking the time to contribute. This repository is observing a maintainer-managed PR intake gate in dry-run mode, so this pull request is staying open. This note helps maintainers prepare the allowlist before any enforcement is considered. Please read |
Resolves the only conflict: main's `## [Unreleased]` grew a `### Added` list while this branch appended its control-socket bullet at the same insertion point. Kept both — main's list, with this PR's bullet appended — and regenerated crates/tui/CHANGELOG.md via scripts/sync-changelog.sh. No source files conflicted; every .rs change auto-merged.
|
Hi @M-Maciej — maintainer housekeeping on this PR, no code of yours was touched. What I found. Two separate things were keeping this un-judgeable:
I also prepended Heads-up on what CI will report.
Both are fixed by #5610, which is waiting to land. Anything else that goes red is worth your attention. Sorry this sat for so long without a real signal. Same treatment applied to #5592 and #5593. |
Opt-in Unix `[control_socket]` JSON-RPC at `<sessions-dir>/<id>/control.sock` (0600) with message/interrupt/ relaunch/status verbs. Authored by @M-Maciej; taken into the 0.9.12 integration branch with its authorship preserved. Adapted onto the #5586 extracted modules and the already-landed lifecycle outbox: doctor posture lives in doctor_cli.rs, config merge in config/merge.rs, and both `[lifecycle_outbox]` and `[control_socket]` tables are kept. The `relaunch` verb dispatches `/relaunch` through the existing command seam. Evidence: rustfmt --edition 2024 cargo test -p codewhale-config --lib --locked --offline -- lifecycle_outbox_toml control_socket_toml -> 3 passed cargo test -p codewhale-tui --lib --locked --offline -- control_socket doctor_reports_control_socket_posture tui_config_parses_control_socket_table -> 15 passed Co-Authored-By: M-Maciej <130112810+M-Maciej@users.noreply.github.com> Co-Authored-By: Grok 4.6 <noreply@anthropic.com>
/#5594 Replay tonight's M-Maciej PRs onto the integration commits that landed while the take was in flight (FEAT-019 /loop handler, image attach, computer-use fallbacks). No further adaptation. Co-Authored-By: Grok 4.6 <noreply@anthropic.com>
Take: - #5592 lifecycle outbox (`[lifecycle_outbox]`, opt-in JSONL + webhook) - #5593 /relaunch (save like /exit, Unix self-exec resume) - #5594 per-session control socket (`[control_socket]`, Unix JSON-RPC) Authorship of the original commits is preserved. Adapted onto the #5586 extracted modules (doctor_cli, exec_agent, cli_args, config/merge) and existing /loop locale keys rather than clobbering them. Did not merge origin/main. Co-Authored-By: M-Maciej <130112810+M-Maciej@users.noreply.github.com> Co-Authored-By: Grok 4.6 <noreply@anthropic.com> Entire-Checkpoint: 01M0W25Y7BH0J5342G4WRNQZJT
Closes #5533
Summary
The control surface for supervised operation: an opt-in, Unix-only,
newline-framed JSON-RPC socket per running session.
When enabled, the interactive TUI binds
<sessions-dir>/<session-id>/control.sock(mode 0600) for the runningsession, with four verbs:
message— structured user message through the composer dispatch path(queued under load instead of dropped).
interrupt— the extracted Esc cancel body, shared with the Esc key path.relaunch— dispatches the/relaunchslash-command path (seam, nomechanics duplicated here).
status— turn/goal snapshot answered by the socket thread.Robustness properties:
so a wedged client cannot stall the session.
live-bind refusal with exponential retry backoff (a refused takeover cannot
become a per-frame connect-probe and log flood).
accept()inheritsO_NONBLOCKfrom thenonblocking listener (Linux does not), so an oversized request could drop
the connection mid-write with
BrokenPipe. The accepted stream is setblocking after accept — a no-op on Linux, required on macOS.
Windows parses the config key but refuses to bind with a clear error; the
protocol/parsing tests compile and run on Windows.
Merge-order note
Merge order: after the cadence fix (a). The
relaunchverb dispatches/relaunchthrough the generic command seam — no compile dependency on(c); until (c) merges, the verb returns
command_error. Zero interactionwith the outbox in either direction.
Dead-code budget note (per the #5535 review)
The six protocol types (
Request,Method,MessageParams,EmptyParams,ControlCommand,ResponseResult) are reachable on Windows only from theportable protocol tests; the plain Windows lib build leaves them unreachable
and CI denies dead code there. Each carries a
cfg_attr(not(unix), allow(dead_code))with an inline comment explaining exactly that scope —per the review's "removed or justified inline" — and because they are the
same items the single-PR version of this work had flagged, the
scripts/dead-code-budget.jsonceiling is unchanged in this branch(v0.9.11's actual count sits well under the 448 ceiling).
Testing
<!-- Exact gate commands (including the clippy allow list) are in
CONTRIBUTING.md → "Pre-push verification". -->
cargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-features --locked(warning-free under the CI allow list)cargo test --workspace --all-features --lockedResults: clippy clean on stable 1.98.0 under the CONTRIBUTING.md allow
list; full suite 13,379 passed / 0 failed (same one pre-existing
environmental skip as above). Focused
control_socketsuite: 14/14(protocol parsing, typed errors, oversized-request rejection,
bind/takeover/backoff lifecycle, status snapshot).
Checklist
docs/CONFIGURATION.mddocuments the new table; Unreleased changelog entry; the module doc
references the filed Feature: the control surface for supervised operation #5533
exercised live against running sessions during development before split, but honestly I don't remember if all, maybe I'll recheck it tomorrow.
every commit is authored under
130112810+M-Maciej@users.noreply.github.comSigned-off-by(DCO)