# 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 --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 --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~" --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 "" --set-status stub --db porting.db --execute /usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \ feature batch-update --ids "" --set-status complete --db porting.db --execute /usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \ feature batch-update --ids "" --set-status verified --db porting.db --execute /usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \ test batch-update --ids "" --set-status stub --db porting.db --execute /usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \ test batch-update --ids "" --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 --status --db porting.db --override "verification evidence: " ``` ### 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 --status deferred --db porting.db \ --override "blocked: " /usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \ test update --status deferred --db porting.db \ --override "blocked: " ``` 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?