# Batch 10 (OCSP Cache + JS Events) Implementation Plan

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

**Goal:** Implement and verify Batch 10 (`20` features + `38` tests) for OCSP response-cache behavior and JetStream advisory publishing, while keeping externally blocked tests deferred with explicit evidence.

**Architecture:** Implement Batch 10 in three feature groups (1/13/6 IDs) and two test-resolution groups (candidate-verify + blocked-defer). Use strict per-feature verification loops, stub detection, and chunked status updates so every transition to `complete`/`verified` is evidence-backed.

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

---

I'm using `writeplan` to create the implementation plan.

**Design doc:** `docs/plans/2026-02-27-batch-10-ocsp-cache-js-events-design.md`

## Batch 10 Working Set

Batch facts:

- Batch ID: `10`
- Depends on: `9`
- Features: `20`
- Tests: `38`
- Go files: `server/ocsp_responsecache.go`, `server/jetstream_events.go`

Environment note:

- In this workspace, use `/usr/local/share/dotnet/dotnet` (not `dotnet`) for CLI commands.

Feature groups (all <= 20 IDs):

- **F1 (1):** `1959`
- **F2 (13):** `2472,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495`
- **F3 (6):** `2496,2497,2498,2499,2500,2501`

Test groups:

- **T1 candidate verify (8):** `1537,1538,1607,1625,1682,2442,2807,2894`
- **T2 dependency-blocked (30):** `641,684,1407,1837,1934,1946,1978,2002,2005,2042,2049,2050,2051,2052,2055,2058,2059,2060,2061,2062,2063,2177,2250,2487,2810,2812,2839,2862,3103,3112`

---

## MANDATORY VERIFICATION PROTOCOL

> NON-NEGOTIABLE: Every task in this plan must follow this protocol.

### Per-Feature Verification Loop (required for every feature ID)

1. Read mapping + Go source:
```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- feature show <feature_id> --db porting.db
```
2. Read exact Go implementation in `golang/nats-server/server/jetstream_events.go` or `golang/nats-server/server/ocsp_responsecache.go`.
3. Write/adjust C# implementation in mapped .NET file.
4. Build immediately:
```bash
/usr/local/share/dotnet/dotnet build dotnet/
```
5. Run related focused tests for that feature group.
6. Only after build + focused tests pass, add ID to promotion candidate list.

### Stub Detection Check (required after every feature/test group)

Run all checks before status updates:

```bash
# Placeholder/stub markers in touched code
rg -n "NotImplementedException|TODO|PLACEHOLDER|throw new NotSupportedException" \
  dotnet/src/ZB.MOM.NatsNet.Server \
  dotnet/tests/ZB.MOM.NatsNet.Server.Tests

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

# Suspicious trivial returns in touched production files
for f in $(git diff --name-only dotnet/src/ZB.MOM.NatsNet.Server -- '*.cs'); do
  rg -n "=>\s*(default|null|true|false);$" "$f"
done
```

Any hit blocks promotion to `complete` or `verified` until fixed or explicitly deferred.

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

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

Required: `0` compile errors.

### Test Gate (required before marking features verified)

All tests related to current feature group must pass before moving feature IDs to `verified`.

Focused test commands are defined inside each task. If any test fails, do not promote.

### Status Update Protocol (required)

- Maximum `15` IDs per `feature batch-update` or `test batch-update` command.
- Required evidence per update command:
  - latest build gate output
  - latest focused test gate output
  - latest stub detection output
- Required flow:
  - Feature IDs: `deferred -> stub -> complete -> verified`
  - Test IDs: `deferred -> stub -> verified` or remain `deferred` with blocker reason

Command templates:

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

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

### Checkpoint Protocol Between Tasks (required)

Between every task boundary (Task 1 -> Task 2 -> ...):

1. Full build:
```bash
/usr/local/share/dotnet/dotnet build dotnet/
```
2. Full unit test run:
```bash
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ --verbosity normal
```
3. Commit checkpoint:
```bash
git add <touched-files> porting.db
git commit -m "feat(batch10): <task checkpoint summary>"
```

---

## ANTI-STUB GUARDRAILS

### Forbidden Patterns

Do not mark as `verified` if any mapped method/test includes:

- `throw new NotImplementedException(...)`
- empty mapped method bodies (`{ }`)
- `TODO` or placeholder comments in method/test bodies
- fake pass assertions (`Assert.True(true)`, equivalent no-op assertions)
- tests that only assert non-behavioral values (for example, not-null only with no behavior)

### Hard Limits

- Max `20` feature IDs per implementation task group.
- Max `15` IDs per status update command.
- No cross-group bulk verification in one command cycle.
- Build gate is mandatory after each feature group.
- Test gate is mandatory before any feature moves to `verified`.
- Checkpoint commit is mandatory between tasks.

### If You Get Stuck

Do not stub and do not fake-pass.

1. Leave item as `deferred`.
2. Record specific blocker reason (dependency ID, missing runtime path, or infra need).
3. Continue with next unblocked item.

Commands:

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

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test update <id> --status deferred --db porting.db \
  --override "blocked: <specific reason>"
```

---

### Task 1: Preflight and Batch Start

**Files:**

- Modify: `porting.db`

**Step 1: Verify dependency batch state**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- batch show 9 --db porting.db
```
Expected: Batch 9 is complete enough to start Batch 10.

**Step 2: Start Batch 10**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- batch start 10 --db porting.db
```
Expected: Batch 10 transitions to in-progress.

**Step 3: Baseline dependency evidence for Batch 10 tests**

```bash
cat <<'SQL' | sqlite3 -header -column porting.db
WITH t AS (
  SELECT ut.id, ut.name
  FROM unit_tests ut
  JOIN batch_tests bt ON bt.test_id=ut.id
  WHERE bt.batch_id=10
), unresolved AS (
  SELECT d.source_id AS test_id,
         SUM(CASE WHEN f.status NOT IN ('complete','verified','n_a') THEN 1 ELSE 0 END) AS unresolved_total,
         SUM(CASE WHEN f.status NOT IN ('complete','verified','n_a')
                   AND f.id NOT IN (SELECT feature_id FROM batch_features WHERE batch_id=10)
                  THEN 1 ELSE 0 END) AS unresolved_outside_batch
  FROM dependencies d
  JOIN features f ON d.target_type='feature' AND d.target_id=f.id
  WHERE d.source_type='unit_test' AND d.source_id IN (SELECT id FROM t)
  GROUP BY d.source_id
)
SELECT * FROM unresolved ORDER BY test_id;
SQL
```
Expected: `T1` IDs have `unresolved_outside_batch=0`; `T2` IDs have `>0`.

**Step 4: Run checkpoint protocol and commit preflight evidence**

---

### Task 2: Implement Feature Group F1 (`1959`) JetStream Advisory Publish

**Files:**

- Create: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.JetStreamEvents.cs`
- Create: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/NatsServerJetStreamEventsTests.cs`
- Modify: `porting.db`

**Step 1: Mark feature `1959` as `stub`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature update 1959 --status stub --db porting.db
```

**Step 2: Write failing tests for advisory decision tree**

Cover:

- null account falls back to `SystemAccount()`
- no interest returns false without send
- marshal/send errors return false
- successful send returns true

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

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

**Step 4: Implement `PublishAdvisory` parity in new partial class**

**Step 5: Re-run focused tests and require pass**

Use same command as Step 3.

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

**Step 7: Promote feature `1959`**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature update 1959 --status complete --db porting.db

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature update 1959 --status verified --db porting.db
```

**Step 8: Run checkpoint protocol and commit**

---

### Task 3: Implement Feature Group F2 (`2472,2484-2495`) Local Cache Core + Stats + Compression

**Files:**

- Modify: `dotnet/src/ZB.MOM.NatsNet.Server/Auth/Ocsp/OcspTypes.cs`
- Create: `dotnet/src/ZB.MOM.NatsNet.Server/Auth/Ocsp/OcspHandler.cs`
- Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/OcspResponseCacheTests.cs`
- Create: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/OcspResponseCacheParserTests.cs`
- Modify: `porting.db`

**Step 1: Mark F2 IDs as `stub` (single command, 13 IDs <= 15)**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2472,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495" \
  --set-status stub --db porting.db --execute
```

**Step 2: Add failing tests for local cache behavior**

Cover:

- stats adjust on put/replace/delete
- hit->miss adjustment when preserving revoked entries
- `Online/Type/Config/Stats` semantics
- compress/decompress round-trip
- `NewOCSPResponseCacheConfig` defaults

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

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

**Step 4: Implement F2 methods with per-feature verification loop**

**Step 5: Re-run focused tests and require pass**

Use same command as Step 3.

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

**Step 7: Promote F2 IDs in two transitions (`complete`, then `verified`)**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2472,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495" \
  --set-status complete --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2472,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495" \
  --set-status verified --db porting.db --execute
```

**Step 8: Run checkpoint protocol and commit**

---

### Task 4: Implement Feature Group F3 (`2496,2497,2498,2499,2500,2501`) Load/Save + Server Wiring + Parser

**Files:**

- Modify: `dotnet/src/ZB.MOM.NatsNet.Server/Auth/Ocsp/OcspTypes.cs`
- Modify: `dotnet/src/ZB.MOM.NatsNet.Server/Auth/Ocsp/OcspHandler.cs`
- Create: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.OcspResponseCache.cs`
- Modify: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Init.cs`
- Modify: `dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Lifecycle.cs`
- Create: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/NatsServerOcspCacheTests.cs`
- Modify: `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Auth/OcspResponseCacheParserTests.cs`
- Modify: `porting.db`

**Step 1: Mark F3 IDs as `stub` (6 IDs)**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2496,2497,2498,2499,2500,2501" --set-status stub --db porting.db --execute
```

**Step 2: Add failing tests for load/save lifecycle + parser + server init/start/stop**

Cover:

- load from absent/corrupt/valid cache file
- save no-op when clean + atomic write when dirty
- parser type conversion + minimum save interval clamp
- server init chooses no-op/local cache correctly
- start/stop wiring calls cache lifecycle

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

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

**Step 4: Implement F3 methods with per-feature verification loop**

**Step 5: Re-run focused tests and require pass**

Use same command as Step 3.

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

**Step 7: Promote F3 IDs (`complete`, then `verified`)**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2496,2497,2498,2499,2500,2501" --set-status complete --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  feature batch-update --ids "2496,2497,2498,2499,2500,2501" --set-status verified --db porting.db --execute
```

**Step 8: Run checkpoint protocol and commit**

---

### Task 5: Resolve Batch 10 Tests (T1 verify, T2 defer with reason)

**Files:**

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

**Step 1: Mark T1 + T2 IDs as `stub` in chunks <= 15**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "1537,1538,1607,1625,1682,2442,2807,2894" --set-status stub --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "641,684,1407,1837,1934,1946,1978,2002,2005,2042,2049,2050,2051,2052,2055" \
  --set-status stub --db porting.db --execute

/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "2058,2059,2060,2061,2062,2063,2177,2250,2487,2810,2812,2839,2862,3103,3112" \
  --set-status stub --db porting.db --execute
```

**Step 2: Port and verify T1 tests one-by-one using per-test loop**

For each T1 test ID:

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- test show <test_id> --db porting.db
/usr/local/share/dotnet/dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ \
  --filter "FullyQualifiedName~<ExactMethodName>" --verbosity normal
```
Expected: each verified method shows `Passed: 1`.

**Step 3: Promote T1 verified tests in one chunk (8 IDs <= 15)**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test batch-update --ids "1537,1538,1607,1625,1682,2442,2807,2894" --set-status verified --db porting.db --execute
```

**Step 4: For T2, prove unresolved external dependencies and keep deferred**

```bash
cat <<'SQL' | sqlite3 -header -column porting.db
WITH t AS (
  SELECT ut.id, ut.name
  FROM unit_tests ut
  JOIN batch_tests bt ON bt.test_id=ut.id
  WHERE bt.batch_id=10
    AND ut.id IN (641,684,1407,1837,1934,1946,1978,2002,2005,2042,2049,2050,2051,2052,2055,
                  2058,2059,2060,2061,2062,2063,2177,2250,2487,2810,2812,2839,2862,3103,3112)
)
SELECT t.id, t.name, f.id AS blocked_feature_id, f.name AS blocked_feature_name, f.status
FROM t
JOIN dependencies d ON d.source_type='unit_test' AND d.source_id=t.id AND d.target_type='feature'
JOIN features f ON f.id=d.target_id
WHERE f.status NOT IN ('complete','verified','n_a')
  AND f.id NOT IN (SELECT feature_id FROM batch_features WHERE batch_id=10)
ORDER BY t.id, f.id;
SQL
```

Then keep each blocked test deferred with reason:

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- \
  test update <blocked_test_id> --status deferred --db porting.db \
  --override "blocked: unresolved dependency feature <id> (<name>)"
```

**Step 5: Run stub detection + build gate + test gate + checkpoint commit**

---

### Task 6: Final Batch 10 Verification and Attempted Closure

**Files:**

- Modify: `porting.db`
- Generate: `reports/current.md`

**Step 1: 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
```
Expected: build passes; no unexpected test regressions.

**Step 2: Global stub audit for touched scope**

```bash
rg -n "NotImplementedException|TODO|PLACEHOLDER|Assert\.True\(true\)" \
  dotnet/src/ZB.MOM.NatsNet.Server \
  dotnet/tests/ZB.MOM.NatsNet.Server.Tests
```
Expected: no matches in Batch 10 touched code/tests.

**Step 3: Verify batch/test visibility**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- batch show 10 --db porting.db
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db
```

**Step 4: Try completing batch**

```bash
/usr/local/share/dotnet/dotnet run --project tools/NatsNet.PortTracker -- batch complete 10 --db porting.db
```
If blocked by deferred T2 tests, keep batch in-progress with documented blockers (correct behavior).

**Step 5: Generate report and final commit**

```bash
./reports/generate-report.sh
git add dotnet/src/ZB.MOM.NatsNet.Server \
        dotnet/tests/ZB.MOM.NatsNet.Server.Tests \
        porting.db reports/
git commit -m "feat(batch10): implement ocsp cache and js advisory with verified gates"
```
