# Batch 26 (WebSocket) Implementation Plan > **For Codex:** REQUIRED SUB-SKILL: Use `executeplan` to implement this plan task-by-task. **Goal:** Port and verify Batch 26 WebSocket behavior end-to-end (37 features, 86 tests) from `server/websocket.go` and mapped Go tests without introducing stubs or fake test passes. **Architecture:** Implement websocket protocol logic in focused websocket components, wire runtime behavior through `ClientConnection` and `NatsServer` partials, and execute ID-driven feature/test verification loops with hard build/test/status gates. Do low-level frame primitives first, then HTTP upgrade/options/listener integration, then mapped tests (including explicit benchmark classification handling). **Tech Stack:** .NET 10, C# latest, xUnit 3, Shouldly, NSubstitute, PortTracker CLI, SQLite (`porting.db`) **Design doc:** `docs/plans/2026-02-27-batch-26-websocket-design.md` --- ## Batch Scope - Batch: `26 - WebSocket` - Dependencies: Batches `16`, `18` (must be complete before execution) - Features: `37` - Tests: `86` ### Feature Groups (max ~20 each) 1. **Group A (20 features):** `3506,3507,3509,3510,3511,3512,3513,3514,3515,3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3541` 2. **Group B (17 features):** `3526,3527,3528,3529,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3542,3543` --- ## MANDATORY VERIFICATION PROTOCOL > **NON-NEGOTIABLE:** Every feature and test in this batch must follow this protocol. Skipping any step is a plan violation. ### What Counts as a Real Feature/Test 1. **Real feature**: - Behavior implemented in runtime code (not placeholder return/no-op/throw stub). - Mapped to the Go method intent for that feature ID. - Covered by at least one related passing test. 2. **Real test**: - Uses Arrange/Act/Assert. - Calls relevant production code. - Has meaningful Shouldly assertions. - Is not a placeholder pattern. ### Per-Feature Verification Loop (REQUIRED for each feature ID) 1. Read mapped Go method location and surrounding logic: - `dotnet run --project tools/NatsNet.PortTracker -- feature show --db porting.db` - `sed -n ',p' golang/nats-server/server/websocket.go` 2. Write/adjust C# implementation for only that behavior slice. 3. Build immediately: - `dotnet build dotnet/` 4. Run related targeted tests (single method/class filter first). 5. Record evidence (feature ID, Go line(s), test(s) run, pass summary) before status update. ### Stub Detection Check (REQUIRED after each feature/test mini-batch) Run both scans and resolve all hits before continuing: ```bash # Production code stub scan rg -n --pcre2 "(NotImplementedException|TODO: session 23|TODO: websocket|// TODO|// PLACEHOLDER|=>\\s*throw\\s+new\\s+NotImplementedException)" \ dotnet/src/ZB.MOM.NatsNet.Server/WebSocket \ dotnet/src/ZB.MOM.NatsNet.Server/ClientConnection.cs \ dotnet/src/ZB.MOM.NatsNet.Server/NatsServer*.cs # Empty/trivial method body scan in touched websocket-related files rg -n --pcre2 "^\\s*(public|private|internal|protected).*\\)\\s*\\{\\s*\\}\\s*$" \ dotnet/src/ZB.MOM.NatsNet.Server/WebSocket \ dotnet/src/ZB.MOM.NatsNet.Server/ClientConnection.cs \ dotnet/src/ZB.MOM.NatsNet.Server/NatsServer*.cs # Test stub scan rg -n --pcre2 "(Assert\\.True\\(true\\)|Assert\\.Pass\\(|NotImplementedException|// TODO|// PLACEHOLDER|ShouldBe\\(true\\);\\s*$)" \ dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog ``` ### Build Gate (REQUIRED after each feature group) After Group A and Group B, full build must pass: ```bash dotnet build dotnet/ --verbosity minimal ``` If build fails, no status updates are allowed. ### Test Gate (REQUIRED before marking features verified) 1. Run all related tests for the feature group. 2. Confirm `Failed: 0`. 3. Only after test gate passes can feature IDs move to `verified`. Example: ```bash dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \ --filter "FullyQualifiedName~WebSocketHandlerTests" --verbosity normal ``` ### Status Update Protocol (REQUIRED) 1. Use max **15 IDs per `batch-update`** command. 2. Never update status without captured evidence for those exact IDs. 3. Keep feature and test updates separate. 4. If audit rejects update and behavior is truly implemented, use `--override ""` with explicit evidence note. Examples: ```bash dotnet run --project tools/NatsNet.PortTracker -- \ feature batch-update --ids "3506-3517,3518" --set-status complete --db porting.db --execute dotnet run --project tools/NatsNet.PortTracker -- \ test batch-update --ids "3075,3076,3077,3078,3079,3080,3082,3083,3084,3085,3086,3087,3088,3089,3093" \ --set-status verified --db porting.db --execute ``` ### Checkpoint Protocol Between Tasks (REQUIRED) Between every task below: 1. Run full build: - `dotnet build dotnet/ --verbosity minimal` 2. Run full unit tests: - `dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --verbosity normal` 3. If regressions exist, fix before next task. 4. Commit checkpoint: - `git add ` - `git commit -m ""` --- ## ANTI-STUB GUARDRAILS (NON-NEGOTIABLE) ### Forbidden Patterns Any appearance below in touched Batch 26 feature/test methods is disallowed: 1. `throw new NotImplementedException(...)` 2. Empty method body `{ }` for mapped feature methods 3. Trivial constant-return placeholder (`return true;`, `return null;`, `return default;`) where real behavior is required 4. `Assert.True(true)` or equivalent always-pass assertions 5. `// TODO`, `// PLACEHOLDER`, `TODO: session 23` in mapped code paths 6. Test methods that only assert non-null on unrelated constants without exercising websocket behavior ### Hard Limits 1. Max `~20` feature IDs per implementation group (already enforced by Group A/B). 2. Max `15` IDs per status batch update. 3. Max `1` feature group status finalization cycle at a time (finish Group A fully before Group B verification updates). 4. No marking `verified` for features until related tests pass. 5. No marking tests `verified` without direct pass evidence. ### If You Get Stuck (REQUIRED) 1. Do **not** leave stubs. 2. Mark blocked feature/test IDs as `deferred` with explicit reason. 3. Add a code comment near deferred code/test: - `// DEFERRED(batch26): ` 4. Continue with next unblocked ID. 5. Revisit deferred IDs only after blocker is resolved. --- ### Task 1: Dependency and Batch Readiness Gate **Files:** - Modify: none - Validate context: `porting.db`, batch metadata **Step 1: Verify dependencies are complete** Run: ```bash dotnet run --project tools/NatsNet.PortTracker -- batch show 16 --db porting.db dotnet run --project tools/NatsNet.PortTracker -- batch show 18 --db porting.db dotnet run --project tools/NatsNet.PortTracker -- batch ready --db porting.db ``` Expected: batch 26 is ready only after 16/18 are complete. **Step 2: Start batch 26** Run: ```bash dotnet run --project tools/NatsNet.PortTracker -- batch start 26 --db porting.db ``` Expected: start succeeds; no dependency error. **Step 3: Materialize execution manifest** Run: ```bash dotnet run --project tools/NatsNet.PortTracker -- batch show 26 --db porting.db ``` Expected: explicit list of 37 features + 86 tests used as task checklist. **Step 4: Commit checkpoint** ```bash git add porting.db git commit -m "chore(batch26): start websocket batch after dependency gate" ``` --- ### Task 2: Implement Feature Group A (20 features, frame/core path) **Feature IDs:** - `3506,3507,3509,3510,3511,3512,3513,3514,3515,3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3541` **Files:** - Modify: `dotnet/src/ZB.MOM.NatsNet.Server/WebSocket/WebSocketTypes.cs` - Modify: `dotnet/src/ZB.MOM.NatsNet.Server/WebSocket/WebSocketConstants.cs` - Modify: `dotnet/src/ZB.MOM.NatsNet.Server/ClientConnection.cs` - Add or modify focused websocket helper files under: `dotnet/src/ZB.MOM.NatsNet.Server/WebSocket/` - Test: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Impltests.cs` **Step 1: Write/port failing tests for Group A behaviors** Start with tests covering: control-frame detection, masking/unmasking, close message creation, frame header generation, read/decompression paths, close enqueue, outbound collapse. Run: ```bash dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \ --filter "FullyQualifiedName~WebSocketHandlerTests.WSIsControlFrame_ShouldSucceed|FullyQualifiedName~WebSocketHandlerTests.WSUnmask_ShouldSucceed" ``` Expected: initially fail until implementation lands. **Step 2: Implement minimal code per feature using per-feature loop** For each feature ID in Group A, execute the mandatory loop (Go source -> C# -> build -> related tests). **Step 3: Run Group A build and focused tests** Run: ```bash dotnet build dotnet/ --verbosity minimal dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \ --filter "FullyQualifiedName~WebSocketHandlerTests" --verbosity normal ``` Expected: no failures in mapped Group A coverage. **Step 4: Run mandatory stub detection checks** Run all scans from the MANDATORY VERIFICATION PROTOCOL section. Expected: zero matches in touched methods/tests. **Step 5: Update feature statuses (chunked <=15 IDs)** Example chunks: ```bash dotnet run --project tools/NatsNet.PortTracker -- \ feature batch-update --ids "3506,3507,3509,3510,3511,3512,3513,3514,3515,3516,3517,3518,3519,3520,3521" \ --set-status complete --db porting.db --execute dotnet run --project tools/NatsNet.PortTracker -- \ feature batch-update --ids "3522,3523,3524,3525,3541" --set-status complete --db porting.db --execute ``` **Step 6: Commit checkpoint** ```bash git add dotnet/src/ZB.MOM.NatsNet.Server/WebSocket dotnet/src/ZB.MOM.NatsNet.Server/ClientConnection.cs porting.db git commit -m "feat(batch26): implement websocket frame/core feature group A" ``` --- ### Task 3: Implement Feature Group B (17 features, upgrade/options/server integration) **Feature IDs:** - `3526,3527,3528,3529,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3542,3543` **Files:** - Modify: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.cs` - Modify: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Init.cs` - Modify: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs` - Modify: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Lifecycle.cs` - Modify: websocket source files in `dotnet/src/ZB.MOM.NatsNet.Server/WebSocket/` - Test: websocket/backlog test files listed in later tasks **Step 1: Write failing tests for upgrade/origin/options behavior** Focus on IDs mapped to: - `TestWSUpgradeConnDeadline`, `TestWSCompressNegotiation`, `TestWSSetHeader`, `TestWSSetOriginOptions`. **Step 2: Implement feature-by-feature with mandatory loop** For each Group B feature: - Read Go source section from `websocket.go`. - Implement in .NET with lock/lifecycle parity. - Build and run targeted tests before next feature. **Step 3: Remove related integration stubs** Eliminate websocket placeholder behavior in: - readiness checks - listener lists - websocket server shutdown path - websocket connect URL propagation - websocket client creation path **Step 4: Build + focused tests + stub scans** Run: ```bash dotnet build dotnet/ --verbosity minimal dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \ --filter "FullyQualifiedName~WebSocketHandlerTests" --verbosity normal ``` **Step 5: Update feature statuses (chunked <=15 IDs)** ```bash dotnet run --project tools/NatsNet.PortTracker -- \ feature batch-update --ids "3526,3527,3528,3529,3530,3531,3532,3533,3534,3535,3536,3537,3538,3539,3540" \ --set-status complete --db porting.db --execute dotnet run --project tools/NatsNet.PortTracker -- \ feature batch-update --ids "3542,3543" --set-status complete --db porting.db --execute ``` **Step 6: Commit checkpoint** ```bash git add dotnet/src/ZB.MOM.NatsNet.Server/NatsServer*.cs dotnet/src/ZB.MOM.NatsNet.Server/WebSocket porting.db git commit -m "feat(batch26): implement websocket upgrade/options/server feature group B" ``` --- ### Task 4: Port WebSocket Functional Tests (22 tests in `websocket_test.go`) **Test IDs:** - `3075,3076,3077,3078,3079,3080,3082,3083,3084,3085,3086,3087,3088,3089,3093,3097,3098,3099,3102,3113,3117,3132` **Files:** - Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Impltests.cs` **Step 1: Port test-by-test from Go source** For each test ID: 1. `test show ` 2. Read Go body in `websocket_test.go` 3. Port behavior to Shouldly assertions 4. Run single test Single-test command template: ```bash dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \ --filter "FullyQualifiedName~ZB.MOM.NatsNet.Server.Tests.ImplBacklog.WebSocketHandlerTests." \ --verbosity normal ``` **Step 2: Run class-level gate** ```bash dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \ --filter "FullyQualifiedName~WebSocketHandlerTests" --verbosity normal ``` **Step 3: Stub scan + assertion quality check** ```bash rg -n "(Assert\\.True\\(true\\)|NotImplementedException|// TODO|// PLACEHOLDER)" \ dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Impltests.cs ``` **Step 4: Update test statuses in chunks <=15** Use chunks with pass evidence recorded for each chunk. **Step 5: Commit checkpoint** ```bash git add dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Impltests.cs porting.db git commit -m "test(batch26): port websocket functional tests" ``` --- ### Task 5: Port Cross-Module Functional Tests Dependent on WebSocket (11 tests) **Test IDs:** - `409,465,466,1353,1902,1903,1975,1986,2371,2384,2488` **Files:** - Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamFileStoreTests.Impltests.cs` - Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsConsumerTests.Impltests.cs` - Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs` - Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs` - Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs` - Create if missing: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeProxyTests.Impltests.cs` **Step 1: Ensure mapped class files exist** Create missing files/classes before test-porting if absent (especially `LeafNodeProxyTests`). **Step 2: Port tests ID-by-ID with single-test loop** Use the same per-test loop and evidence policy as Task 4. **Step 3: Run focused class gates** Run each touched class with `dotnet test --filter "FullyQualifiedName~"`. **Step 4: Update statuses in <=15 chunks** Mark `verified` only with evidence. If blocked by missing infra, mark `deferred` and add `// DEFERRED(batch26): `. **Step 5: Commit checkpoint** ```bash git add dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog porting.db git commit -m "test(batch26): port cross-module websocket-dependent tests" ``` --- ### Task 6: Resolve Benchmark-Mapped Test IDs (53 tests) **Benchmark test IDs:** - `154,274,275,276,277,3134,3135,3136,3137,3138,3139,3140,3141,3142,3143,3144,3145,3146,3147,3148,3149,3150,3151,3152,3153,3154,3155,3156,3157,3158,3159,3160,3161,3162,3163,3164,3165,3166,3167,3168,3169,3170,3171,3172,3173,3174,3175,3176,3177,3179,3180,3181,3182` **Files:** - Modify/create: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Impltests.cs` - Create if missing: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/CoreBenchmarks.Impltests.cs` - Create if missing: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/PublishBenchmarks.Impltests.cs` **Step 1: Benchmark decision gate per ID** For each benchmark ID choose exactly one: 1. Convert to deterministic functional test with real assertions and mark `verified`. 2. Mark `n_a` (benchmark-only/perf harness requirement) with explicit reason. 3. Mark `deferred` if runtime infra blocker exists. **Step 2: Run tests for converted benchmark IDs** Use class-level and method-level gates as in previous tasks. **Step 3: Apply status updates in <=15 chunks** Separate commands by status (`verified`, `n_a`, `deferred`). **Step 4: Commit checkpoint** ```bash git add dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog porting.db git commit -m "test(batch26): resolve benchmark-mapped websocket test IDs" ``` --- ### Task 7: Final Verification, Status Closure, and Batch Completion **Files:** - Modify: `porting.db` - Optional report artifacts: `reports/current.md` (if generator updates it) **Step 1: Full verification run** ```bash dotnet build dotnet/ --verbosity minimal dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --verbosity normal dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ --verbosity normal ``` **Step 2: Promote complete feature IDs to verified** Use chunked updates with evidence references. **Step 3: Audit and reconcile** ```bash dotnet run --project tools/NatsNet.PortTracker -- audit --type features --db porting.db dotnet run --project tools/NatsNet.PortTracker -- audit --type tests --db porting.db ``` **Step 4: Complete batch** ```bash dotnet run --project tools/NatsNet.PortTracker -- batch complete 26 --db porting.db ``` Expected: completion succeeds only when all batch items are in valid terminal statuses. **Step 5: Generate report and commit** ```bash ./reports/generate-report.sh git add porting.db reports/ git commit -m "feat(batch26): complete websocket batch with verified implementation and tests" ``` --- ## Execution Notes 1. If `dotnet` is not on `PATH`, use absolute binary (for this machine): `/usr/local/share/dotnet/dotnet`. 2. Always keep evidence for each status update chunk (test output snippet + feature/test IDs). 3. Never move a feature to `verified` before its related tests are passing. 4. Never convert a blocked item into a stub just to make progress.