Generated design docs and implementation plans via Codex for: - Batch 23: Routes - Batch 24: Leaf Nodes - Batch 25: Gateways - Batch 26: WebSocket - Batch 27: JetStream Core - Batch 28: JetStream API - Batch 29: JetStream Batching - Batch 30: Raft Part 1 All plans include mandatory verification protocol and anti-stub guardrails. Updated batches.md with file paths and planned status.
18 KiB
Batch 26 (WebSocket) Implementation Plan
For Codex: REQUIRED SUB-SKILL: Use
executeplanto 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)
- Group A (20 features):
3506,3507,3509,3510,3511,3512,3513,3514,3515,3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3541 - 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
- 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.
- 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)
- Read mapped Go method location and surrounding logic:
dotnet run --project tools/NatsNet.PortTracker -- feature show <feature-id> --db porting.dbsed -n '<start>,<end>p' golang/nats-server/server/websocket.go
- Write/adjust C# implementation for only that behavior slice.
- Build immediately:
dotnet build dotnet/
- Run related targeted tests (single method/class filter first).
- 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:
# 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:
dotnet build dotnet/ --verbosity minimal
If build fails, no status updates are allowed.
Test Gate (REQUIRED before marking features verified)
- Run all related tests for the feature group.
- Confirm
Failed: 0. - Only after test gate passes can feature IDs move to
verified.
Example:
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
--filter "FullyQualifiedName~WebSocketHandlerTests" --verbosity normal
Status Update Protocol (REQUIRED)
- Use max 15 IDs per
batch-updatecommand. - Never update status without captured evidence for those exact IDs.
- Keep feature and test updates separate.
- If audit rejects update and behavior is truly implemented, use
--override "<reason>"with explicit evidence note.
Examples:
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:
- Run full build:
dotnet build dotnet/ --verbosity minimal
- Run full unit tests:
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --verbosity normal
- If regressions exist, fix before next task.
- Commit checkpoint:
git add <changed-files>git commit -m "<task checkpoint message>"
ANTI-STUB GUARDRAILS (NON-NEGOTIABLE)
Forbidden Patterns
Any appearance below in touched Batch 26 feature/test methods is disallowed:
throw new NotImplementedException(...)- Empty method body
{ }for mapped feature methods - Trivial constant-return placeholder (
return true;,return null;,return default;) where real behavior is required Assert.True(true)or equivalent always-pass assertions// TODO,// PLACEHOLDER,TODO: session 23in mapped code paths- Test methods that only assert non-null on unrelated constants without exercising websocket behavior
Hard Limits
- Max
~20feature IDs per implementation group (already enforced by Group A/B). - Max
15IDs per status batch update. - Max
1feature group status finalization cycle at a time (finish Group A fully before Group B verification updates). - No marking
verifiedfor features until related tests pass. - No marking tests
verifiedwithout direct pass evidence.
If You Get Stuck (REQUIRED)
- Do not leave stubs.
- Mark blocked feature/test IDs as
deferredwith explicit reason. - Add a code comment near deferred code/test:
// DEFERRED(batch26): <specific dependency/runtime blocker>
- Continue with next unblocked ID.
- 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:
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:
dotnet run --project tools/NatsNet.PortTracker -- batch start 26 --db porting.db
Expected: start succeeds; no dependency error.
Step 3: Materialize execution manifest
Run:
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
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:
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:
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:
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
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:
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)
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
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:
test show <id>- Read Go body in
websocket_test.go - Port behavior to Shouldly assertions
- Run single test
Single-test command template:
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
--filter "FullyQualifiedName~ZB.MOM.NatsNet.Server.Tests.ImplBacklog.WebSocketHandlerTests.<MethodName>" \
--verbosity normal
Step 2: Run class-level gate
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
--filter "FullyQualifiedName~WebSocketHandlerTests" --verbosity normal
Step 3: Stub scan + assertion quality check
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
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~<ClassName>".
Step 4: Update statuses in <=15 chunks
Mark verified only with evidence.
If blocked by missing infra, mark deferred and add // DEFERRED(batch26): <reason>.
Step 5: Commit checkpoint
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:
- Convert to deterministic functional test with real assertions and mark
verified. - Mark
n_a(benchmark-only/perf harness requirement) with explicit reason. - Mark
deferredif 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
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
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
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
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
./reports/generate-report.sh
git add porting.db reports/
git commit -m "feat(batch26): complete websocket batch with verified implementation and tests"
Execution Notes
- If
dotnetis not onPATH, use absolute binary (for this machine):/usr/local/share/dotnet/dotnet. - Always keep evidence for each status update chunk (test output snippet + feature/test IDs).
- Never move a feature to
verifiedbefore its related tests are passing. - Never convert a blocked item into a stub just to make progress.