# 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 <feature-id> --db porting.db`
   - `sed -n '<start>,<end>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 "<reason>"` 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 <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:

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): <specific dependency/runtime blocker>`
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 <id>`
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.<MethodName>" \
  --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~<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**

```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.

