Skip to content

feat: add event-handler WebSocket support (issue #555) - #593

Merged
logbie merged 3 commits into
mainfrom
claude/websockets-wfl-5nl9lh
Jul 9, 2026
Merged

feat: add event-handler WebSocket support (issue #555)#593
logbie merged 3 commits into
mainfrom
claude/websockets-wfl-5nl9lh

Conversation

@logbie

@logbie logbie commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Implement real-time WebSocket servers with natural-language syntax:

listen for websockets on port 8080 as chat_server
on websocket connect to chat_server as conn:
send websocket message "Welcome!" to conn
end on
on websocket message from chat_server as msg:
broadcast websocket message body of msg to chat_server
end on

WebSockets reuse the HTTP server's single-threaded design: warp runs each
socket in background tokio tasks that push connect/message/disconnect events
over a channel, and the interpreter dispatches to the registered
on websocket ... handler blocks while the program is inside a wait.

Changes:

  • Parser/AST: listen-for-websockets, on-websocket handler blocks, send and
    broadcast statements; no new keywords (kept message as an identifier).
  • Interpreter: WflWebSocketServer, per-connection warp task, event pump hooked
    into wait for <duration>, handler dispatch, send/broadcast; close server
    now also closes a WebSocket server and its connections.
  • Fix property of object access (body of msg, method of request): it
    parsed as a call and errored at runtime; added an object-field fallback in
    FunctionCall evaluation so natural property reads resolve.
  • Analyzer/typechecker/transpiler wired for the new statements.
  • Tests: tests/websocket_test.rs (real tokio-tungstenite client, echo +
    broadcast) and TestPrograms/websocket_echo_server.wfl (CI-safe).
  • Docs: WebSocket section in Docs/04-advanced-features/web-servers.md; dev diary.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_019Mp4HoighK1ALD1BxdMcBk

Summary by CodeRabbit

  • New Features

    • Added WebSocket server support, including connect/message/disconnect handlers, messaging, broadcasting, and graceful shutdown.
    • Added a WebSocket echo/broadcast demo program.
  • Bug Fixes

    • Improved WebSocket event handling so queued events dispatch during wait periods.
  • Documentation

    • Expanded the Web Servers guide with full WebSocket syntax and an example; updated the learning checklist.
  • Tests

    • Added end-to-end WebSocket integration coverage (including disconnect and close-server behaviors) and refreshed related CI notes.

Implement real-time WebSocket servers with natural-language syntax:

  listen for websockets on port 8080 as chat_server
  on websocket connect to chat_server as conn:
      send websocket message "Welcome!" to conn
  end on
  on websocket message from chat_server as msg:
      broadcast websocket message body of msg to chat_server
  end on

WebSockets reuse the HTTP server's single-threaded design: warp runs each
socket in background tokio tasks that push connect/message/disconnect events
over a channel, and the interpreter dispatches to the registered
`on websocket ...` handler blocks while the program is inside a `wait`.

Changes:
- Parser/AST: listen-for-websockets, on-websocket handler blocks, send and
  broadcast statements; no new keywords (kept `message` as an identifier).
- Interpreter: WflWebSocketServer, per-connection warp task, event pump hooked
  into `wait for <duration>`, handler dispatch, send/broadcast; `close server`
  now also closes a WebSocket server and its connections.
- Fix `property of object` access (`body of msg`, `method of request`): it
  parsed as a call and errored at runtime; added an object-field fallback in
  FunctionCall evaluation so natural property reads resolve.
- Analyzer/typechecker/transpiler wired for the new statements.
- Tests: tests/websocket_test.rs (real tokio-tungstenite client, echo +
  broadcast) and TestPrograms/websocket_echo_server.wfl (CI-safe).
- Docs: WebSocket section in Docs/04-advanced-features/web-servers.md; dev diary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Mp4HoighK1ALD1BxdMcBk
Copilot AI review requested due to automatic review settings July 9, 2026 02:36
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5584323c-dba6-453f-860f-8a84d153942b

📥 Commits

Reviewing files that changed from the base of the PR and between baf2522 and 4cafa99.

📒 Files selected for processing (5)
  • CLAUDE.md
  • src/analyzer/mod.rs
  • src/interpreter/mod.rs
  • src/typechecker/mod.rs
  • tests/websocket_test.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/typechecker/mod.rs
  • src/interpreter/mod.rs

📝 Walkthrough

Walkthrough

This PR adds WebSocket support to WFL: new syntax, AST nodes, semantic checks, runtime event dispatch, docs, demo programs, and end-to-end tests.

Changes

WebSocket feature implementation

Layer / File(s) Summary
AST contracts and dependencies
src/parser/ast.rs, Cargo.toml
Adds WsHandlerEvent, WebSocket statement variants, and the new WebSocket-related dependencies.
Parser support for WebSocket statements
src/parser/mod.rs, src/parser/stmt/web.rs
Adds parsing for WebSocket listeners, handlers, send/broadcast statements, and WebSocket message operands.
Analyzer, typechecker, and transpiler handling
src/analyzer/mod.rs, src/analyzer/static_analyzer.rs, src/typechecker/mod.rs, src/transpiler/javascript.rs
Adds WebSocket-aware name resolution, usage tracking, type checking, and transpiler rejection.
Interpreter WebSocket runtime
src/interpreter/mod.rs
Adds WebSocket server/connection runtime support, event pumping during wait, send/broadcast execution, shutdown handling, and function-call fallback behavior.
Docs, dev diary, and demo test programs
CLAUDE.md, Docs/04-advanced-features/web-servers.md, Dev diary/2026-07-08-websockets-issue-555.md, TestPrograms/web_server_websocket_test.wfl, TestPrograms/websocket_echo_server.wfl
Updates WebSocket docs, documentation policy, the dev diary, the existing CI-SKIP note, and adds a new demo program.
End-to-end WebSocket integration tests
tests/websocket_test.rs
Adds WebSocket integration helpers and tests for echo, broadcast, disconnect, and close-server flows.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WflProgram
  participant Interpreter
  participant WsConnectionRegistry
  participant ClientSocket

  WflProgram->>Interpreter: wait for duration
  Interpreter->>Interpreter: pump_websocket_events
  ClientSocket->>Interpreter: connect/message/disconnect event
  Interpreter->>Interpreter: dispatch_ws_event
  Interpreter->>WsConnectionRegistry: enqueue outbound frame
  WsConnectionRegistry->>ClientSocket: send text/close frame
Loading

Possibly related PRs

  • WebFirstLanguage/wfl#169: Extends the same interpreter wait and server-lifecycle paths that this PR uses for WebSocket event pumping and shutdown.
🚥 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 summarizes the main change: adding WebSocket event-handler support for issue #555.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 claude/websockets-wfl-5nl9lh

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: baf25221ae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/analyzer/mod.rs Outdated
Comment on lines +1736 to +1738
let ws_properties = ["id", "ip", "body", "sender"];
for prop in ws_properties {
self.action_parameters.insert(prop.to_string());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Let websocket fields override scoped variables

If a program already has a variable named body, id, ip, or sender in an outer scope (an earlier HTTP wait for request also creates body implicitly), the documented body of msg/id of conn syntax still fails semantic analysis: analyze_expression resolves the callee to that non-function symbol before consulting action_parameters, reports '<name>' is not a function, and the CLI aborts before the new runtime property fallback can run. The WebSocket field allowance needs to take precedence over non-function symbols while analyzing handler bodies.

Useful? React with 👍 / 👎.

Comment thread src/interpreter/mod.rs Outdated

let event_obj = build_ws_event_object(&event);
let child_env = Environment::new_child_env(&handler_env);
if let Err(msg) = child_env.borrow_mut().define(&binding, event_obj) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Shadow outer names for websocket event bindings

When the handler binding name already exists in the captured environment, for example store conn as ... before on websocket connect ... as conn, Environment::define rejects the binding because it checks parent scopes. The analyzer models this as a scoped handler variable, and other parameter-like runtime bindings use shadowing/direct definition, so the first matching event aborts the surrounding wait with a runtime error instead of running the handler.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (5)
tests/websocket_test.rs (2)

97-152: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing test coverage for on websocket disconnect and close server with WebSocket servers.

The PR objectives mention connect, message, and disconnect events, but only connect and message are tested. Adding a test for the disconnect handler and verifying close server properly closes WebSocket servers and their connections would strengthen coverage for the new feature.

As per coding guidelines, TDD is mandatory for new features in Rust code.

🤖 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 `@tests/websocket_test.rs` around lines 97 - 152, Add missing WebSocket
coverage in tests/websocket_test.rs by extending the existing websocket test
suite with a case that exercises the `on websocket disconnect` handler and
another that verifies `close server` shuts down the WebSocket server and
terminates active connections. Use the existing helpers like `start_ws_server`,
`connect`, and `next_text`, and place the new assertions alongside
`websocket_connect_and_echo` and `websocket_broadcast_reaches_all_clients` so
the new behavior is covered end-to-end.

Source: Coding guidelines


25-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid leaking TempDir — return it instead.

std::mem::forget(dir) permanently leaks the temp directory and its files. Since kill_on_drop(true) already ties the child's lifetime to the test scope, returning the TempDir alongside the Child ensures it is cleaned up when the test ends or panics, with no behavioral change.

♻️ Proposed refactor
 async fn start_ws_server(program: &str) -> (Child, u16) {
+async fn start_ws_server(program: &str) -> (Child, u16, tempfile::TempDir) {
     let dir = tempfile::tempdir().expect("tempdir");
     let prog_path = dir.path().join("ws_server.wfl");
     std::fs::write(&prog_path, program).expect("write program");
-    // Keep the temp dir alive for the process lifetime by leaking it; the OS
-    // reclaims it when the test process exits.
-    std::mem::forget(dir);

     // ...

-    (child, port)
+    (child, port, dir)
 }

Then update both call sites:

-    let (mut child, port) = start_ws_server(ECHO_PROGRAM).await;
+    let (mut child, port, _dir) = start_ws_server(ECHO_PROGRAM).await;
-    let (mut child, port) = start_ws_server(BROADCAST_PROGRAM).await;
+    let (mut child, port, _dir) = start_ws_server(BROADCAST_PROGRAM).await;
🤖 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 `@tests/websocket_test.rs` around lines 25 - 27, The test helper is leaking the
TempDir via std::mem::forget(dir), which should be removed. Update the helper
that creates the temp workspace so it returns the TempDir together with the
Child, and keep both owned by the caller instead of dropping the directory
manually. Then update the two websocket test call sites to hold on to the
returned TempDir alongside the child process so cleanup happens automatically
through normal scope drop.
src/interpreter/mod.rs (2)

7315-7336: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Optional: per-event receiver re-locking. Each dispatched event rebuilds recv_futs and re-acquires every server's event_receiver lock before select_all. This is O(servers) lock churn per event. Fine for a handful of servers, but if you expect high message throughput you could drain multiple ready events per poll (e.g. loop on try_recv after the select wakes) before rebuilding.

🤖 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 `@src/interpreter/mod.rs` around lines 7315 - 7336, The event polling in the
receiver loop rebuilds recv_futs and re-locks every server event_receiver on
each dispatched event, causing O(servers) lock churn. Update the select_all flow
in the receiver-handling block to drain additional ready events after one
wakeup, preferably by looping on try_recv or similar before rebuilding the
futures, while keeping dispatch_ws_event and the existing timeout behavior
intact.

143-147: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

WsRegisteredHandler captures the environment with a strong Rc, unlike FunctionValue/EventHandler (which use Weak).

Because the handler set lives in web_socket_servers for the whole program, the captured env (and everything it transitively holds) is retained for the program's lifetime rather than being released when the registering scope exits. This is harmless for top-level handlers but deviates from the codebase's Weak-capture convention. Consider Weak + upgrade-at-dispatch if you want handler lifetimes to track their defining scope.

🤖 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 `@src/interpreter/mod.rs` around lines 143 - 147, WsRegisteredHandler is
holding the captured Environment with a strong Rc, which keeps the defining
scope alive for the whole program. Update WsRegisteredHandler to store a Weak
reference like FunctionValue and EventHandler, then upgrade it at dispatch time
in the websocket handler path; if the environment is gone, skip or fail the
callback gracefully.
src/typechecker/mod.rs (1)

2052-2072: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Server operand type isn't statically validated for WebSocket handler/broadcast.

WebSocketHandlerStatement.server and BroadcastWebSocketMessageStatement.server are the same kind of server-key operand that check_server_expression_type already validates for WaitForRequestStatement, StopAcceptingConnectionsStatement, and CloseServerStatement (interpreter's resolve_ws_server_key requires Text). These two arms only call infer_expression_type, so a wrongly-typed server expression is caught only at runtime instead of statically, unlike its siblings.

♻️ Proposed fix
             Statement::WebSocketHandlerStatement { server, body, .. } => {
                 // The server operand and handler body are checked; the handler's
                 // bound variable resolves as an object at runtime (gradual typing
                 // keeps member access like `content of msg` permissive).
-                self.infer_expression_type(server);
+                self.check_server_expression_type(server, *line, *column);
                 for stmt in body {
                     self.check_statement_types(stmt);
                 }
             }
             Statement::SendWebSocketMessageStatement {
                 message, target, ..
             } => {
                 self.infer_expression_type(message);
                 self.infer_expression_type(target);
             }
             Statement::BroadcastWebSocketMessageStatement {
                 message, server, ..
             } => {
                 self.infer_expression_type(message);
-                self.infer_expression_type(server);
+                self.check_server_expression_type(server, *line, *column);
             }

Note: WebSocketHandlerStatement's destructuring uses .. for line/column; these would need to be bound explicitly to pass to check_server_expression_type.

🤖 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 `@src/typechecker/mod.rs` around lines 2052 - 2072, The WebSocket server
operand is only inferred, not validated, in `WebSocketHandlerStatement` and
`BroadcastWebSocketMessageStatement`, unlike the other server-key statements.
Update the `check_statement_types` match arms to call
`check_server_expression_type` for `server` in both cases, using the existing
`check_server_expression_type` helper and the
`WebSocketHandlerStatement`/`BroadcastWebSocketMessageStatement` symbols to
locate the change. For `WebSocketHandlerStatement`, bind the needed position
fields explicitly instead of `..` so you can pass them into the checker
consistently.
🤖 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 `@src/analyzer/mod.rs`:
- Around line 1704-1754: The WebSocket handler setup in
analyzer::analyze_statement is removing shared action_parameters entries without
tracking what was already present, which can delete enclosing action parameters
that are still valid. Update the Statement::WebSocketHandlerStatement branch to
remember whether binding and each ws_properties entry were already in
action_parameters before inserting them, then only remove the names that this
handler actually added after analyzing body. Keep the fix localized to the
handler-scoping logic so outer parameters remain available after the handler
finishes.

In `@src/interpreter/mod.rs`:
- Around line 7368-7372: The websocket handler binding setup in
dispatch_ws_event is using define, which can fail when conn/msg already exist in
an outer scope instead of shadowing them. Update the binding insertion on the
child Environment to use define_direct for the event binding so websocket
handlers behave consistently with the other handler bindings and do not bubble a
collision out as a RuntimeError.

In `@tests/websocket_test.rs`:
- Around line 138-139: The websocket broadcast test uses a fixed
tokio::time::sleep to wait for client b to register, which can race under CI
load. Update BROADCAST_PROGRAM to include a connect handler that emits a
readiness message, and change the test flow in websocket_test.rs to wait for
both clients to receive that message before a sends the broadcast. This removes
the timing dependency and makes the synchronization deterministic using the
existing connect/broadcast test setup.

---

Nitpick comments:
In `@src/interpreter/mod.rs`:
- Around line 7315-7336: The event polling in the receiver loop rebuilds
recv_futs and re-locks every server event_receiver on each dispatched event,
causing O(servers) lock churn. Update the select_all flow in the
receiver-handling block to drain additional ready events after one wakeup,
preferably by looping on try_recv or similar before rebuilding the futures,
while keeping dispatch_ws_event and the existing timeout behavior intact.
- Around line 143-147: WsRegisteredHandler is holding the captured Environment
with a strong Rc, which keeps the defining scope alive for the whole program.
Update WsRegisteredHandler to store a Weak reference like FunctionValue and
EventHandler, then upgrade it at dispatch time in the websocket handler path; if
the environment is gone, skip or fail the callback gracefully.

In `@src/typechecker/mod.rs`:
- Around line 2052-2072: The WebSocket server operand is only inferred, not
validated, in `WebSocketHandlerStatement` and
`BroadcastWebSocketMessageStatement`, unlike the other server-key statements.
Update the `check_statement_types` match arms to call
`check_server_expression_type` for `server` in both cases, using the existing
`check_server_expression_type` helper and the
`WebSocketHandlerStatement`/`BroadcastWebSocketMessageStatement` symbols to
locate the change. For `WebSocketHandlerStatement`, bind the needed position
fields explicitly instead of `..` so you can pass them into the checker
consistently.

In `@tests/websocket_test.rs`:
- Around line 97-152: Add missing WebSocket coverage in tests/websocket_test.rs
by extending the existing websocket test suite with a case that exercises the
`on websocket disconnect` handler and another that verifies `close server` shuts
down the WebSocket server and terminates active connections. Use the existing
helpers like `start_ws_server`, `connect`, and `next_text`, and place the new
assertions alongside `websocket_connect_and_echo` and
`websocket_broadcast_reaches_all_clients` so the new behavior is covered
end-to-end.
- Around line 25-27: The test helper is leaking the TempDir via
std::mem::forget(dir), which should be removed. Update the helper that creates
the temp workspace so it returns the TempDir together with the Child, and keep
both owned by the caller instead of dropping the directory manually. Then update
the two websocket test call sites to hold on to the returned TempDir alongside
the child process so cleanup happens automatically through normal scope drop.
🪄 Autofix (Beta)

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

Run ID: fcece197-77e7-4c80-9350-a02b6933ceec

📥 Commits

Reviewing files that changed from the base of the PR and between 516c509 and baf2522.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • Cargo.toml
  • Dev diary/2026-07-08-websockets-issue-555.md
  • Docs/04-advanced-features/web-servers.md
  • TestPrograms/web_server_websocket_test.wfl
  • TestPrograms/websocket_echo_server.wfl
  • src/analyzer/mod.rs
  • src/analyzer/static_analyzer.rs
  • src/interpreter/mod.rs
  • src/parser/ast.rs
  • src/parser/mod.rs
  • src/parser/stmt/web.rs
  • src/transpiler/javascript.rs
  • src/typechecker/mod.rs
  • tests/websocket_test.rs

Comment thread src/analyzer/mod.rs
Comment thread src/interpreter/mod.rs
Comment thread tests/websocket_test.rs Outdated
…tests

Correctness fixes from PR #593 review (Codex + CodeRabbit):

- Interpreter: use `define_direct` for the handler event binding so a
  same-named outer variable (`store conn as ...` before `on websocket connect
  ... as conn`) is shadowed instead of aborting the handler with a redefinition
  error.
- Analyzer: a handler property name (`id`/`ip`/`body`/`sender`) now resolves as
  an `of`-form property read even when an outer scope defines a non-function
  symbol of the same name (e.g. an earlier HTTP `wait for request` binds `body`),
  instead of failing analysis with "is not a function".
- Analyzer: only remove the `action_parameters` entries this handler actually
  added, so a same-named enclosing action parameter survives the handler.
- Typechecker: validate the server operand of `on websocket ...` and
  `broadcast websocket message ... to <server>` with `check_server_expression_type`,
  matching the other server-key statements.

Tests:
- Replace the fixed 200ms sleep in the broadcast test with a deterministic
  readiness handshake (per-client connect greeting) to avoid CI flakiness.
- Add disconnect-handler and close-server end-to-end coverage.
- Echo test now uses outer `conn`/`body` variables that collide with the handler
  binding and a property name, exercising the two shadowing fixes above.
- Return the temp dir from the helper instead of leaking it with mem::forget.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Mp4HoighK1ALD1BxdMcBk
Copilot AI review requested due to automatic review settings July 9, 2026 03:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Add a mandatory rule to CLAUDE.md that any change altering user-facing
behavior (syntax, keywords, statements, stdlib, CLI flags, config) must
ship its documentation — guide page, keyword references, a validated
example, and a Dev Diary note — in the same change. A feature is not
complete until its docs are written.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Mp4HoighK1ALD1BxdMcBk
Copilot AI review requested due to automatic review settings July 9, 2026 04:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@logbie
logbie merged commit da86008 into main Jul 9, 2026
15 of 16 checks passed
@logbie
logbie deleted the claude/websockets-wfl-5nl9lh branch July 9, 2026 05:46
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.

3 participants