# Batch 4 (Logging) Implementation Plan

> **For Codex:** REQUIRED SUB-SKILL: Use `executeplan` to implement this plan task-by-task.

**Goal:** Implement and verify Batch 4 logging parity (`11` features, `31` tests) from `server/log.go` with evidence-backed status updates and zero stub leakage.

**Architecture:** Add a dedicated `NatsServer` logging partial that ports the mapped `log.go` methods while preserving existing `ILogger`-based call sites. Implement in two feature groups (<=20 each), then port mapped tests in three class-based groups with strict per-feature and per-test verification loops. Use deferred-with-reason only when runtime blockers are proven.

**Tech Stack:** .NET 10, C# latest, xUnit 3, Shouldly, NSubstitute, PortTracker CLI, SQLite (`porting.db`)

**Design doc:** `docs/plans/2026-02-27-batch-4-logging-design.md`

---

## Batch 4 Working Set

Batch facts:

- Batch ID: `4`
- Features: `11`
- Tests: `31`
- Dependency: `Batch 1`
- Go file: `server/log.go`

Feature groups (max group size <= 20):

- **F1 Core Logger Wiring (5):** `2050,2052,2053,2054,2067`
- **F2 Error + Rate-Limit Helpers (6):** `2057,2058,2059,2061,2062,2063`

Test groups:

- **T1 Gateway + Route (11):** `622,623,643,678,2820,2826,2827,2828,2834,2847,2848`
- **T2 Leaf + MQTT (15):** `1906,1911,1916,1917,1922,1940,1947,1954,1971,1973,1977,1991,2000,2188,2270`
- **T3 Concurrency + WebSocket (5):** `2469,2506,3104,3110,3130`

Environment note:

- `dotnet` is not on PATH in this environment; use `/usr/local/share/dotnet/dotnet` in commands.

---

## MANDATORY VERIFICATION PROTOCOL

> **NON-NEGOTIABLE:** Every feature and every test in this plan must follow this protocol.

### Per-Feature Verification Loop (REQUIRED for each feature ID)

1. Read Go source mapping and intent:
   ```bash
   /usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- feature show <id> --db porting.db
   ```
2. Read the mapped `server/log.go` code region for the feature.
3. Write C# implementation for only that feature’s behavior.
4. Build immediately:
   ```bash
   /usr/local/share/dotnet/dotnet build dotnet/
   ```
5. Run related tests for the affected area (focused class/method filter).
6. Add the feature ID to a verified-candidates list only after build + related tests are green.

### Per-Test Verification Loop (REQUIRED for each test ID)

1. Read test mapping:
   ```bash
   /usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- test show <id> --db porting.db
   ```
2. Read Go test method lines and identify expected logging behavior.
3. Write/replace C# test with real Arrange/Act/Assert.
4. Run only that test and confirm discovery + pass:
   ```bash
   /usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
     --filter "FullyQualifiedName~<ExactTestMethod>" --verbosity normal
   ```
5. Add test ID to verified-candidates list only after focused test passes.

### Stub Detection Check (REQUIRED after each feature group and each test class group)

Run these checks on touched files before any status promotion:

```bash
# 1) Forbidden stub markers
grep -n -E "(NotImplementedException|TODO|PLACEHOLDER)" \
  dotnet/src/ZB.MOM.NatsNet.Server/NatsServer*.cs \
  dotnet/src/ZB.MOM.NatsNet.Server/Internal/NatsLogger.cs \
  dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/*.cs \
  dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/*.cs \
  dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/*.cs

# 2) Empty method bodies in changed C# files
for f in $(git diff --name-only -- '*.cs'); do
  grep -n -E "^\s*(public|private|internal|protected).*\)\s*\{\s*\}\s*$" "$f";
done
```

Any match blocks status updates until fixed or explicitly deferred with reason.

### Build Gate (REQUIRED after each feature group)

```bash
/usr/local/share/dotnet/dotnet build dotnet/
```

Required result: build succeeds with zero errors.

### Test Gate (REQUIRED before marking any Batch 4 feature as `verified`)

Run all related suites for the currently active groups and require `Failed: 0`:

```bash
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~NatsLoggerTests|FullyQualifiedName~ServerLoggerTests" --verbosity normal

/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~GatewayHandlerTests|FullyQualifiedName~RouteHandlerTests" --verbosity normal

/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~LeafNodeHandlerTests|FullyQualifiedName~MqttHandlerTests" --verbosity normal

/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~ConcurrencyTests1|FullyQualifiedName~ConcurrencyTests2|FullyQualifiedName~WebSocketHandlerTests" --verbosity normal
```

Rule: do not mark any feature `verified` until all related mapped tests for that feature group are passing. If blocked, keep feature at `complete` and defer blocked tests with explicit reason.

### Status Update Protocol (REQUIRED)

- Max `15` IDs per `feature batch-update` or `test batch-update` command.
- Evidence required for each update chunk:
  - Go source reviewed
  - build gate output
  - related focused test output
  - stub detection output
- Required state progression:
  - Features: `deferred/not_started -> stub -> complete -> verified`
  - Tests: `deferred/not_started -> stub -> verified` (or remain `deferred` with reason)

Command templates:

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "<max15ids>" --set-status stub --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "<max15ids>" --set-status complete --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "<max15ids>" --set-status verified --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "<max15ids>" --set-status stub --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "<max15ids>" --set-status verified --db porting.db --execute
```

If audit disagrees, attach override evidence:

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature update <id> --status <status> --db porting.db --override "verification evidence: <reason>"
```

### Checkpoint Protocol Between Tasks (REQUIRED)

After each task, before starting the next:

1. Run full build:
   ```bash
   /usr/local/share/dotnet/dotnet build dotnet/
   ```
2. Run full unit tests:
   ```bash
   /usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --verbosity normal
   ```
3. Record pass/fail totals.
4. Commit the completed task slice (including `porting.db`) before proceeding.

---

## ANTI-STUB GUARDRAILS (NON-NEGOTIABLE)

### Forbidden Patterns

Do not introduce these in feature or test code:

- `throw new NotImplementedException(...)`
- `TODO` / `PLACEHOLDER` markers in newly touched Batch 4 code
- Empty method bodies (`{ }`) for mapped feature methods
- Trivial always-pass assertions (`Assert.True(true)`, `Assert.Pass()`)
- Non-behavioral string/self checks used in place of production behavior
- Tests that never invoke production code

### Hard Limits

- Max `20` features per feature group.
- Max `15` IDs per status update command.
- Max `1` feature group status promotion per verification cycle.
- Mandatory build gate + related test gate before any `verified` promotion.
- Mandatory checkpoint commit between tasks.

### If You Get Stuck (REQUIRED BEHAVIOR)

Do not stub. Do not fake-pass.

1. Keep the blocked item `deferred`.
2. Add explicit reason with `--override`:
   ```bash
   /usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
     feature update <id> --status deferred --db porting.db \
     --override "blocked: <specific runtime/dependency reason>"

   /usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
     test update <id> --status deferred --db porting.db \
     --override "blocked: <specific runtime/dependency reason>"
   ```
3. Commit only proven work and continue with next unblocked ID.

---

### Task 1: Preflight and Batch Claim

**Files:**

- Modify: `porting.db`

**Step 1: Confirm dependency and batch readiness**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- batch show 1 --db porting.db
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- batch show 4 --db porting.db
```

**Step 2: Start batch 4**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- batch start 4 --db porting.db
```

**Step 3: Mark F1 as `stub`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2050,2052,2053,2054,2067" --set-status stub --db porting.db --execute
```

**Step 4: Checkpoint protocol + commit**

---

### Task 2: Implement Feature Group F1 (Core Logger Wiring)

**Files:**

- Create: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Logging.cs`
- Modify: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.cs`
- Modify: `dotnet/src/ZB.MOM.NatsNet.Server/Internal/NatsLogger.cs`
- Test: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/NatsLoggerTests.cs`
- Test: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/ServerLifecycleStubFeaturesTests.cs` (or dedicated new server logging test file if cleaner)
- Modify: `porting.db`

**Step 1: Write/expand failing tests for F1 mapped behaviors**

- `ConfigureLogger` option precedence behavior
- `SetLogger` delegates to `SetLoggerV2`
- `SetLoggerV2` flag + replacement semantics
- `ReOpenLogFile` behavior by log mode
- `ExecuteLogCall` no-op with nil logger

**Step 2: Run focused tests and confirm they fail first**

```bash
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~NatsLoggerTests|FullyQualifiedName~ServerLoggerTests" --verbosity normal
```

**Step 3: Implement F1 in `NatsServer.Logging.cs` with Go parity**

**Step 4: Run per-feature verification loop for `2050,2052,2053,2054,2067`**

**Step 5: Run stub detection check + build gate + focused test gate**

**Step 6: Promote F1 statuses to `complete` then `verified`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2050,2052,2053,2054,2067" --set-status complete --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2050,2052,2053,2054,2067" --set-status verified --db porting.db --execute
```

**Step 7: Checkpoint protocol + commit**

---

### Task 3: Implement Feature Group F2 (Errors + Rate-Limit Helpers)

**Files:**

- Modify: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Logging.cs`
- Modify: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.cs`
- Test: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Internal/NatsLoggerTests.cs`
- Test: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/ServerLifecycleStubFeaturesTests.cs` (or dedicated server logging test file)
- Modify: `porting.db`

**Step 1: Mark F2 as `stub`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2057,2058,2059,2061,2062,2063" --set-status stub --db porting.db --execute
```

**Step 2: Write/expand failing tests for F2**

- `Errors`, `Errorc`, `Errorsc` formatting parity
- `RateLimitFormatWarnf` dedupe-by-format
- `RateLimitWarnf` dedupe-by-rendered-statement
- `RateLimitDebugf` respects debug flag and dedupe

**Step 3: Run focused tests and confirm fail**

```bash
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~NatsLoggerTests|FullyQualifiedName~ServerLoggerTests" --verbosity normal
```

**Step 4: Implement F2 feature methods**

**Step 5: Run per-feature verification loop for `2057,2058,2059,2061,2062,2063`**

**Step 6: Run stub detection + build gate + focused test gate**

**Step 7: Promote F2 statuses to `complete` then `verified`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2057,2058,2059,2061,2062,2063" --set-status complete --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2057,2058,2059,2061,2062,2063" --set-status verified --db porting.db --execute
```

**Step 8: Checkpoint protocol + commit**

---

### Task 4: Port Test Group T1 (Gateway + Route, 11 tests)

**Files:**

- Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/GatewayHandlerTests.Impltests.cs`
- Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/RouteHandlerTests.Impltests.cs`
- Modify: `porting.db`

**Step 1: Mark T1 tests as `stub`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "622,623,643,678,2820,2826,2827,2828,2834,2847,2848" \
  --set-status stub --db porting.db --execute
```

**Step 2: For each test ID, run Per-Test Verification Loop**

**Step 3: Run class-level gates**

```bash
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~GatewayHandlerTests|FullyQualifiedName~RouteHandlerTests" --verbosity normal
```

**Step 4: Run stub detection for touched backlog files**

**Step 5: Promote T1 test IDs to `verified`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "622,623,643,678,2820,2826,2827,2828,2834,2847,2848" \
  --set-status verified --db porting.db --execute
```

**Step 6: Checkpoint protocol + commit**

---

### Task 5: Port Test Group T2 (Leaf + MQTT, 15 tests)

**Files:**

- Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs`
- Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MqttHandlerTests.Impltests.cs`
- Modify: `porting.db`

**Step 1: Mark T2 tests as `stub`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "1906,1911,1916,1917,1922,1940,1947,1954,1971,1973,1977,1991,2000,2188,2270" \
  --set-status stub --db porting.db --execute
```

**Step 2: Run Per-Test Verification Loop for each T2 ID**

**Step 3: Run class-level gates**

```bash
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~LeafNodeHandlerTests|FullyQualifiedName~MqttHandlerTests" --verbosity normal
```

**Step 4: Run stub detection for touched backlog files**

**Step 5: Promote T2 test IDs in max-15 chunks**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "1906,1911,1916,1917,1922,1940,1947,1954,1971,1973,1977,1991,2000,2188,2270" \
  --set-status verified --db porting.db --execute
```

**Step 6: Checkpoint protocol + commit**

---

### Task 6: Port Test Group T3 (Concurrency + WebSocket, 5 tests)

**Files:**

- Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs`
- Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs`
- Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Impltests.cs`
- Modify: `porting.db`

**Step 1: Mark T3 tests as `stub`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "2469,2506,3104,3110,3130" --set-status stub --db porting.db --execute
```

**Step 2: Run Per-Test Verification Loop for each T3 ID**

**Step 3: Run class-level gates**

```bash
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~ConcurrencyTests1|FullyQualifiedName~ConcurrencyTests2|FullyQualifiedName~WebSocketHandlerTests" \
  --verbosity normal
```

**Step 4: Run stub detection for touched backlog files**

**Step 5: Promote T3 test IDs to `verified`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "2469,2506,3104,3110,3130" --set-status verified --db porting.db --execute
```

**Step 6: Checkpoint protocol + commit**

---

### Task 7: Batch 4 Closure and Final Verification

**Files:**

- Modify: `porting.db`
- Optional: `reports/current.md` (if report regenerated)

**Step 1: Run final full verification gates**

```bash
/usr/local/share/dotnet/dotnet build dotnet/
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --verbosity normal
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ --verbosity normal
```

**Step 2: Validate batch state**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- batch show 4 --db porting.db
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- batch complete 4 --db porting.db
```

**Step 3: Generate status report**

```bash
./reports/generate-report.sh
```

**Step 4: Final checkpoint commit**

```bash
git add dotnet/src/ZB.MOM.NatsNet.Server dotnet/tests/ZB.MOM.NatsNet.Server.Tests porting.db reports/current.md
git commit -m "feat(batch4): implement logging features and mapped tests with verification evidence"
```

---

Plan complete and saved to `docs/plans/2026-02-27-batch-4-logging-plan.md`. Two execution options:

1. Subagent-Driven (this session) - dispatch a fresh subagent per task, review between tasks, fast iteration.
2. Parallel Session (separate) - open a new session with `executeplan`, batch execution with checkpoints.

Which approach?
