Skip to content

feat: execute bounded WebSocket sessions - #23

Merged
copyleftdev merged 4 commits into
mainfrom
feat/websocket-session-executor
Aug 8, 2026
Merged

copyleftdev merged 4 commits into
mainfrom
feat/websocket-session-executor

Conversation

@copyleftdev

@copyleftdev copyleftdev commented Aug 8, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • execute sealed WebSocket action plans with strict next-event matching
  • enforce exact frame, message, byte, idle, action, close, and total budgets across ws and wss
  • handle fragmentation, UTF-8, ping/pong, client/server close, cancellation, and terminal observations
  • bound JSON-schema diagnostics used by WebSocket JSON expectations

Validation

  • scripts/gates.sh
  • cargo test -p kahea-exec websocket -- --nocapture
  • cargo clippy -p kahea-exec --all-targets -- -D warnings

Closes #12

Summary by CodeRabbit

  • New Features

    • Added WebSocket execution with cancellation, message expectations, JSON/schema validation, and ordered actions.
    • Added traffic accounting and configurable limits for inbound and outbound data.
    • Added support for fragmented messages, control frames, connection closure, and persisted execution outcomes.
  • Bug Fixes

    • Improved timeout handling across total, phase, idle, and cancellation scenarios.
    • Validation now stops after 64 failures for faster, more predictable results.
    • Improved handling of protocol violations, invalid data, and handshake failures.

@copyleftdev
copyleftdev marked this pull request as ready for review August 8, 2026 08:18
@coderabbitai

coderabbitai Bot commented Aug 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@copyleftdev, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 161eb3df-db86-44a8-87d1-d1134dfc2da3

📥 Commits

Reviewing files that changed from the base of the PR and between 146d31e and 7f451b6.

📒 Files selected for processing (1)
  • crates/kahea-exec/src/websocket.rs
📝 Walkthrough

Walkthrough

The PR adds public WebSocket execution and cancellation APIs. It implements bounded frame and byte accounting, ordered actions, message expectations, close handling, deadline causes, cancellation, and persisted terminal observations. Schema validation stops after 64 failures.

Changes

WebSocket execution

Layer / File(s) Summary
Public APIs and validation contracts
crates/kahea-exec/src/lib.rs, crates/kahea-exec/src/websocket.rs
The crate exports WebSocket execution and cancellation APIs. Connection setup validates plans and seals. Schema validation stops after 64 failures.
Wire accounting and handshake setup
crates/kahea-exec/src/websocket.rs
The transport and handshake track frames and bytes. Frame, byte, protocol, and buffered-input limits are enforced. Failure observations retain counters.
Session execution and terminal control
crates/kahea-exec/src/websocket.rs
The executor runs ordered actions, handles expectations and close frames, applies total, phase, idle, and cancellation deadlines, and persists terminal observations.
Execution behavior validation
crates/kahea-exec/src/websocket.rs
Integration tests cover ordered actions, fragmentation, control frames, expectations, budgets, JSON and schema matching, deadline precedence, and cancellation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Plan
  participant execute_websocket
  participant WebSocketConnection
  participant Peer
  participant ObservationStore
  Plan->>execute_websocket: execute plan with options
  execute_websocket->>WebSocketConnection: connect with cancellation
  WebSocketConnection->>Peer: perform WebSocket upgrade
  execute_websocket->>WebSocketConnection: execute ordered actions
  Peer-->>WebSocketConnection: return frames and close events
  WebSocketConnection->>ObservationStore: persist terminal observation
Loading

Possibly related issues

  • copyleftdev/kahea issue 6 — Covers the deterministic WebSocket executor, bounded sessions, protocol handling, and cancellation.
  • copyleftdev/kahea issue 14 — Covers exposing and invoking the canonical WebSocket execution APIs.
  • copyleftdev/kahea issue 15 — Covers the canonical executor and finite-session behavior required for workflow integration.

Possibly related PRs

  • copyleftdev/kahea#19 — Defines related WebSocket session execution, accounting, deadlines, and validation semantics.
  • copyleftdev/kahea#20 — Introduces related WebSocket plan, action, limit, and observation contracts.
  • copyleftdev/kahea#22 — Adds the preceding WebSocket execution API and connection implementation.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: bounded WebSocket session execution.
Linked Issues check ✅ Passed The changes implement bounded action-driven WebSocket sessions with matching, budgets, control frames, cancellation, cleanup, terminal results, and tests for issue #12.
Out of Scope Changes check ✅ Passed The public API exports and bounded schema-validation behavior support the WebSocket execution objectives; no unrelated changes are identified.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/websocket-session-executor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/kahea-exec/src/websocket.rs (1)

2029-2032: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Apply the cancellation retry loop to flush.

remaining() caps the returned duration at 25 ms whenever cancellation is set. read and write absorb that cap with a retry loop and re-check the real deadline. flush does not. A cancellable session whose socket buffer stalls for more than 25 ms therefore fails with io::ErrorKind::TimedOut before the action, idle, or total deadline elapses. socket_error then reports a timeout cause for a session that had budget left.

flush runs after ping and close handling at lines 810, 855, and 918, so cancellable sessions reach this path.

🐛 Proposed fix to retry `flush` until the real deadline elapses
     fn flush(&mut self) -> io::Result<()> {
-        self.stream.set_write_timeout(Some(self.remaining()?))?;
-        self.stream.flush().map_err(normalize_timeout)
+        loop {
+            self.stream.set_write_timeout(Some(self.remaining()?))?;
+            match self.stream.flush().map_err(normalize_timeout) {
+                Ok(()) => return Ok(()),
+                Err(error)
+                    if error.kind() == io::ErrorKind::TimedOut && self.cancellation.is_some() =>
+                {
+                    self.remaining()?;
+                }
+                Err(error) => return Err(error),
+            }
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/kahea-exec/src/websocket.rs` around lines 2029 - 2032, Update the
flush method to retry after cancellation-induced timeout errors, rechecking the
actual remaining deadline on each attempt like the existing read and write
loops. Preserve immediate propagation of non-timeout errors and return a timeout
only when the real deadline is exhausted.
🧹 Nitpick comments (1)
crates/kahea-exec/src/websocket.rs (1)

2885-2929: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for max_frame_bytes and invalid UTF-8.

This test covers max_inbound_frames. Two enforcement paths from the linked issue stay untested:

  • max_frame_bytes: WireParser::observe rejects a frame whose declared payload exceeds limits.max_frame_bytes at line 137. No test drives that branch, so the length-prefix parsing for the 126 and 127 extended-length forms is unverified.
  • Invalid UTF-8 in a text frame: socket_error maps WebSocketError::Utf8 to ProtocolViolation at line 1115. No test drives that mapping.

Both cases are cheap to add with the existing raw-Frame test server pattern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/kahea-exec/src/websocket.rs` around lines 2885 - 2929, Extend the
websocket enforcement tests around the existing max_inbound_frames scenario to
add raw-Frame coverage for max_frame_bytes, including payload lengths using both
126- and 127-byte extended-length encodings, and assert the resulting
budget/terminal behavior. Add a separate raw text-frame case containing invalid
UTF-8 and assert socket_error maps it to
WebSocketTerminalCause::ProtocolViolation, reusing the existing test-server and
observation patterns.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/kahea-exec/src/websocket.rs`:
- Around line 815-851: Update the ExpectPong and ExpectBinary branches in the
websocket action handling to map decode_sealed_base64 failures to the same
ExecError::InvalidSeal result used by SendBinary and Ping. Remove the
ProtocolViolation mapping for these local sealed-payload decode failures while
preserving successful payload comparisons and expectation-failed behavior.

---

Outside diff comments:
In `@crates/kahea-exec/src/websocket.rs`:
- Around line 2029-2032: Update the flush method to retry after
cancellation-induced timeout errors, rechecking the actual remaining deadline on
each attempt like the existing read and write loops. Preserve immediate
propagation of non-timeout errors and return a timeout only when the real
deadline is exhausted.

---

Nitpick comments:
In `@crates/kahea-exec/src/websocket.rs`:
- Around line 2885-2929: Extend the websocket enforcement tests around the
existing max_inbound_frames scenario to add raw-Frame coverage for
max_frame_bytes, including payload lengths using both 126- and 127-byte
extended-length encodings, and assert the resulting budget/terminal behavior.
Add a separate raw text-frame case containing invalid UTF-8 and assert
socket_error maps it to WebSocketTerminalCause::ProtocolViolation, reusing the
existing test-server and observation patterns.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d3313c17-87e6-4522-9c08-eb474a7ab068

📥 Commits

Reviewing files that changed from the base of the PR and between 18ad1c2 and 17ab2a8.

📒 Files selected for processing (2)
  • crates/kahea-exec/src/lib.rs
  • crates/kahea-exec/src/websocket.rs

Comment thread crates/kahea-exec/src/websocket.rs
@copyleftdev
copyleftdev merged commit d5fabd0 into main Aug 8, 2026
13 checks passed
@copyleftdev
copyleftdev deleted the feat/websocket-session-executor branch August 8, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

websocket: execute bounded deterministic message sessions

1 participant