From f76b599e2d7cbb47515d4b76840b1accb80060c8 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 23 Feb 2026 14:27:04 -0500 Subject: [PATCH] docs: add strict full go parity implementation plan --- ...6-02-23-nats-strict-full-go-parity-plan.md | 703 ++++++++++++++++++ 1 file changed, 703 insertions(+) create mode 100644 docs/plans/2026-02-23-nats-strict-full-go-parity-plan.md diff --git a/docs/plans/2026-02-23-nats-strict-full-go-parity-plan.md b/docs/plans/2026-02-23-nats-strict-full-go-parity-plan.md new file mode 100644 index 0000000..b3c8f2d --- /dev/null +++ b/docs/plans/2026-02-23-nats-strict-full-go-parity-plan.md @@ -0,0 +1,703 @@ +# NATS Strict Full Go Parity Implementation Plan + +> **For Codex:** REQUIRED SUB-SKILL: Use `executeplan` to implement this plan task-by-task. + +**Goal:** Port all remaining NATS functionality gaps from Go to .NET at capability level (not table-status level), with behavior-complete implementation, unit/integration coverage, and synchronized parity documentation. + +**Architecture:** Execute in dependency layers: inter-server fabric first, then MQTT wire/runtime parity, then JetStream runtime state machines, then storage/RAFT/cluster governance, then operational parity and docs synchronization. Every capability closes only when Behavior + Tests + Docs are complete. + +**Tech Stack:** .NET 10, C# 14, xUnit 3, Shouldly, System.IO.Pipelines, ASP.NET Core monitoring endpoints, custom protocol parsers, integration socket fixtures. + +--- + +**Execution guardrails** +- Use `@test-driven-development` for each task. +- If expected contracts and observed runtime behavior diverge, switch to `@systematic-debugging` before changing production code. +- Keep one commit per task. +- Run `@verification-before-completion` before final parity status updates. + +### Task 1: Add Strict Capability Inventory Guardrail + +**Files:** +- Create: `docs/plans/2026-02-23-nats-strict-full-go-parity-map.md` +- Create: `tests/NATS.Server.Tests/Parity/NatsStrictCapabilityInventoryTests.cs` +- Create: `tests/NATS.Server.Tests/Parity/NatsCapabilityInventory.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public void Strict_capability_inventory_has_no_open_items_marked_done_without_behavior_and_tests() +{ + var report = NatsCapabilityInventory.Load( + "docs/plans/2026-02-23-nats-strict-full-go-parity-map.md"); + report.InvalidRows.ShouldBeEmpty(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~NatsStrictCapabilityInventoryTests" -v minimal` +Expected: FAIL because inventory map and validator are not yet present. + +**Step 3: Write minimal implementation** + +```csharp +public sealed record CapabilityRow(string Capability, string Behavior, string Tests, string Docs); +public IReadOnlyList InvalidRows => Rows.Where(r => r.Behavior != "done" && r.Docs == "closed").ToArray(); +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~NatsStrictCapabilityInventoryTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add docs/plans/2026-02-23-nats-strict-full-go-parity-map.md tests/NATS.Server.Tests/Parity/NatsStrictCapabilityInventoryTests.cs tests/NATS.Server.Tests/Parity/NatsCapabilityInventory.cs +git commit -m "test: add strict nats capability inventory guardrail" +``` + +### Task 2: Enforce Account-Scoped Remote Delivery + +**Files:** +- Modify: `src/NATS.Server/NatsServer.cs` +- Modify: `src/NATS.Server/Auth/Account.cs` +- Test: `tests/NATS.Server.Tests/Routes/RouteAccountScopedDeliveryTests.cs` +- Test: `tests/NATS.Server.Tests/Gateways/GatewayAccountScopedDeliveryTests.cs` +- Test: `tests/NATS.Server.Tests/LeafNodes/LeafAccountScopedDeliveryTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Remote_message_delivery_uses_target_account_sublist_not_global_sublist() +{ + await using var fx = await InterServerAccountDeliveryFixture.StartAsync(); + var report = await fx.PublishAndObserveAsync(); + report.CrossAccountLeak.ShouldBeFalse(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~AccountScopedDeliveryTests" -v minimal` +Expected: FAIL because remote delivery currently resolves against global account only. + +**Step 3: Write minimal implementation** + +```csharp +private void DeliverRemoteMessage(string account, string subject, string? replyTo, ReadOnlyMemory payload) +{ + var target = GetOrCreateAccount(account); + var result = target.SubList.Match(subject); + // deliver using result... +} +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~AccountScopedDeliveryTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/NatsServer.cs src/NATS.Server/Auth/Account.cs tests/NATS.Server.Tests/Routes/RouteAccountScopedDeliveryTests.cs tests/NATS.Server.Tests/Gateways/GatewayAccountScopedDeliveryTests.cs tests/NATS.Server.Tests/LeafNodes/LeafAccountScopedDeliveryTests.cs +git commit -m "feat: enforce account-scoped remote delivery semantics" +``` + +### Task 3: Make Remote Interest Propagation Idempotent Under Reconnects + +**Files:** +- Modify: `src/NATS.Server/Routes/RouteConnection.cs` +- Modify: `src/NATS.Server/Routes/RouteManager.cs` +- Modify: `src/NATS.Server/Gateways/GatewayConnection.cs` +- Modify: `src/NATS.Server/LeafNodes/LeafConnection.cs` +- Modify: `src/NATS.Server/Subscriptions/SubList.cs` +- Test: `tests/NATS.Server.Tests/Routes/RouteInterestIdempotencyTests.cs` +- Test: `tests/NATS.Server.Tests/Gateways/GatewayInterestIdempotencyTests.cs` +- Test: `tests/NATS.Server.Tests/LeafNodes/LeafInterestIdempotencyTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Duplicate_RSplus_or_reconnect_replay_does_not_double_count_remote_interest() +{ + await using var fx = await RouteInterestFixture.StartAsync(); + var count = await fx.ReplayInterestFramesAsync(); + count.ShouldBe(1); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~InterestIdempotencyTests" -v minimal` +Expected: FAIL due duplicate remote-interest accumulation. + +**Step 3: Write minimal implementation** + +```csharp +// Key remote interest by (remoteServerId, account, subject, queue) +// and treat replay as upsert rather than increment. +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~InterestIdempotencyTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/Routes/RouteConnection.cs src/NATS.Server/Routes/RouteManager.cs src/NATS.Server/Gateways/GatewayConnection.cs src/NATS.Server/LeafNodes/LeafConnection.cs src/NATS.Server/Subscriptions/SubList.cs tests/NATS.Server.Tests/Routes/RouteInterestIdempotencyTests.cs tests/NATS.Server.Tests/Gateways/GatewayInterestIdempotencyTests.cs tests/NATS.Server.Tests/LeafNodes/LeafInterestIdempotencyTests.cs +git commit -m "feat: make inter-server interest propagation idempotent" +``` + +### Task 4: Harden Gateway Reply Remap and Leaf Loop-Marker Transparency + +**Files:** +- Modify: `src/NATS.Server/Gateways/ReplyMapper.cs` +- Modify: `src/NATS.Server/LeafNodes/LeafLoopDetector.cs` +- Modify: `src/NATS.Server/NatsServer.cs` +- Test: `tests/NATS.Server.Tests/GatewayAdvancedRemapRuntimeTests.cs` +- Test: `tests/NATS.Server.Tests/LeafNodes/LeafLoopTransparencyRuntimeTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Transport_internal_reply_and_loop_markers_never_leak_to_client_visible_subjects() +{ + await using var fx = await GatewayLeafMarkerFixture.StartAsync(); + var leak = await fx.PublishObserveLeakAsync(); + leak.ShouldBeFalse(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~RemapRuntimeTests|FullyQualifiedName~LoopTransparencyRuntimeTests" -v minimal` +Expected: FAIL if markers can leak on edge routing paths. + +**Step 3: Write minimal implementation** + +```csharp +if (ReplyMapper.TryRestoreGatewayReply(replyTo, out var restored)) replyTo = restored; +if (LeafLoopDetector.TryUnmark(subject, out var unmarked)) subject = unmarked; +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~RemapRuntimeTests|FullyQualifiedName~LoopTransparencyRuntimeTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/Gateways/ReplyMapper.cs src/NATS.Server/LeafNodes/LeafLoopDetector.cs src/NATS.Server/NatsServer.cs tests/NATS.Server.Tests/GatewayAdvancedRemapRuntimeTests.cs tests/NATS.Server.Tests/LeafNodes/LeafLoopTransparencyRuntimeTests.cs +git commit -m "feat: harden gateway reply remap and leaf loop transparency" +``` + +### Task 5: Replace Line-Based MQTT With Packet-Level Parser/Writer + +**Files:** +- Create: `src/NATS.Server/Mqtt/MqttPacketReader.cs` +- Create: `src/NATS.Server/Mqtt/MqttPacketWriter.cs` +- Modify: `src/NATS.Server/Mqtt/MqttProtocolParser.cs` +- Test: `tests/NATS.Server.Tests/Mqtt/MqttPacketParserTests.cs` +- Test: `tests/NATS.Server.Tests/Mqtt/MqttPacketWriterTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public void Connect_packet_fixed_header_and_remaining_length_parse_correctly() +{ + var packet = MqttPacketReader.Read(ConnectPacketBytes.Sample); + packet.Type.ShouldBe(MqttControlPacketType.Connect); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~MqttPacketParserTests|FullyQualifiedName~MqttPacketWriterTests" -v minimal` +Expected: FAIL because packet-level parser/writer do not yet exist. + +**Step 3: Write minimal implementation** + +```csharp +var first = buffer[0]; +var type = (MqttControlPacketType)(first >> 4); +var remainingLength = DecodeRemainingLength(buffer[1..], out var consumed); +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~MqttPacketParserTests|FullyQualifiedName~MqttPacketWriterTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/Mqtt/MqttPacketReader.cs src/NATS.Server/Mqtt/MqttPacketWriter.cs src/NATS.Server/Mqtt/MqttProtocolParser.cs tests/NATS.Server.Tests/Mqtt/MqttPacketParserTests.cs tests/NATS.Server.Tests/Mqtt/MqttPacketWriterTests.cs +git commit -m "feat: implement mqtt packet-level parser and writer" +``` + +### Task 6: Implement MQTT Session and QoS Acknowledgement Runtime + +**Files:** +- Modify: `src/NATS.Server/Mqtt/MqttConnection.cs` +- Modify: `src/NATS.Server/Mqtt/MqttListener.cs` +- Modify: `src/NATS.Server/MqttOptions.cs` +- Test: `tests/NATS.Server.Tests/Mqtt/MqttSessionRuntimeTests.cs` +- Test: `tests/NATS.Server.Tests/Mqtt/MqttQosAckRuntimeTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Qos1_publish_receives_puback_and_redelivery_on_session_reconnect_when_unacked() +{ + await using var fx = await MqttSessionFixture.StartAsync(); + var report = await fx.RunQosScenarioAsync(); + report.PubAckObserved.ShouldBeTrue(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~MqttSessionRuntimeTests|FullyQualifiedName~MqttQosAckRuntimeTests" -v minimal` +Expected: FAIL because current MQTT runtime lacks session/QoS state machine behavior. + +**Step 3: Write minimal implementation** + +```csharp +if (packet.Qos == 1) +{ + _pendingPublishes[packet.PacketId] = packet; + await SendPubAckAsync(packet.PacketId, ct); +} +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~MqttSessionRuntimeTests|FullyQualifiedName~MqttQosAckRuntimeTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/Mqtt/MqttConnection.cs src/NATS.Server/Mqtt/MqttListener.cs src/NATS.Server/MqttOptions.cs tests/NATS.Server.Tests/Mqtt/MqttSessionRuntimeTests.cs tests/NATS.Server.Tests/Mqtt/MqttQosAckRuntimeTests.cs +git commit -m "feat: implement mqtt session and qos ack runtime semantics" +``` + +### Task 7: Implement MQTT Auth/TLS/Keepalive Contract Integration + +**Files:** +- Modify: `src/NATS.Server/Mqtt/MqttConnection.cs` +- Modify: `src/NATS.Server/Mqtt/MqttListener.cs` +- Modify: `src/NATS.Server/NatsServer.cs` +- Modify: `src/NATS.Server/Auth/AuthService.cs` +- Test: `tests/NATS.Server.Tests/Mqtt/MqttAuthIntegrationTests.cs` +- Test: `tests/NATS.Server.Tests/Mqtt/MqttKeepAliveTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Invalid_mqtt_credentials_or_keepalive_timeout_close_session_with_protocol_error() +{ + await using var fx = await MqttAuthFixture.StartAsync(); + var result = await fx.RunInvalidAuthAndKeepAliveScenarioAsync(); + result.ConnectionClosed.ShouldBeTrue(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~MqttAuthIntegrationTests|FullyQualifiedName~MqttKeepAliveTests" -v minimal` +Expected: FAIL because MQTT auth and keepalive are not fully enforced. + +**Step 3: Write minimal implementation** + +```csharp +if (!TryAuthenticateMqtt(connectPacket, out _)) + return await CloseWithReasonAsync("mqtt auth failed", ct); +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~MqttAuthIntegrationTests|FullyQualifiedName~MqttKeepAliveTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/Mqtt/MqttConnection.cs src/NATS.Server/Mqtt/MqttListener.cs src/NATS.Server/NatsServer.cs src/NATS.Server/Auth/AuthService.cs tests/NATS.Server.Tests/Mqtt/MqttAuthIntegrationTests.cs tests/NATS.Server.Tests/Mqtt/MqttKeepAliveTests.cs +git commit -m "feat: enforce mqtt auth tls and keepalive semantics" +``` + +### Task 8: Implement Distinct JetStream Retention Runtime Semantics + +**Files:** +- Modify: `src/NATS.Server/JetStream/StreamManager.cs` +- Modify: `src/NATS.Server/JetStream/ConsumerManager.cs` +- Test: `tests/NATS.Server.Tests/JetStream/JetStreamRetentionRuntimeStrictParityTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Limits_interest_and_workqueue_retention_diverge_by_runtime_contract() +{ + await using var fx = await JetStreamRetentionStrictFixture.StartAsync(); + var report = await fx.RunPolicyMatrixAsync(); + report.PolicyViolations.ShouldBeEmpty(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamRetentionRuntimeStrictParityTests" -v minimal` +Expected: FAIL because WorkQueue/Interest currently share limits retention path. + +**Step 3: Write minimal implementation** + +```csharp +switch (stream.Config.Retention) +{ + case RetentionPolicy.Interest: ApplyInterestRetentionWithConsumerInterest(stream, consumerState); break; + case RetentionPolicy.WorkQueue: ApplyWorkQueueRetentionWithAckState(stream, consumerState); break; + default: ApplyLimitsRetention(stream, nowUtc); break; +} +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamRetentionRuntimeStrictParityTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/JetStream/StreamManager.cs src/NATS.Server/JetStream/ConsumerManager.cs tests/NATS.Server.Tests/JetStream/JetStreamRetentionRuntimeStrictParityTests.cs +git commit -m "feat: implement strict retention runtime parity for jetstream" +``` + +### Task 9: Harden JetStream Consumer Ack/Backoff/Replay/Flow State Machine + +**Files:** +- Modify: `src/NATS.Server/JetStream/Consumers/AckProcessor.cs` +- Modify: `src/NATS.Server/JetStream/Consumers/PullConsumerEngine.cs` +- Modify: `src/NATS.Server/JetStream/Consumers/PushConsumerEngine.cs` +- Modify: `src/NATS.Server/JetStream/ConsumerManager.cs` +- Test: `tests/NATS.Server.Tests/JetStream/JetStreamConsumerStateMachineStrictParityTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Ack_redelivery_backoff_and_replay_timing_follow_monotonic_consumer_state_machine_rules() +{ + await using var fx = await ConsumerStateMachineFixture.StartAsync(); + var report = await fx.RunLongScenarioAsync(); + report.InvariantViolations.ShouldBeEmpty(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamConsumerStateMachineStrictParityTests" -v minimal` +Expected: FAIL on pending-floor/replay/backoff edge cases. + +**Step 3: Write minimal implementation** + +```csharp +if (config.MaxDeliver > 0 && deliveries > config.MaxDeliver) TerminatePending(sequence); +if (config.AckPolicy == AckPolicy.All) AdvanceAckFloor(sequence); +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamConsumerStateMachineStrictParityTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/JetStream/Consumers/AckProcessor.cs src/NATS.Server/JetStream/Consumers/PullConsumerEngine.cs src/NATS.Server/JetStream/Consumers/PushConsumerEngine.cs src/NATS.Server/JetStream/ConsumerManager.cs tests/NATS.Server.Tests/JetStream/JetStreamConsumerStateMachineStrictParityTests.cs +git commit -m "feat: harden jetstream consumer state machine parity" +``` + +### Task 10: Harden JetStream Mirror/Source Runtime Semantics + +**Files:** +- Modify: `src/NATS.Server/JetStream/StreamManager.cs` +- Modify: `src/NATS.Server/JetStream/MirrorSource/MirrorCoordinator.cs` +- Modify: `src/NATS.Server/JetStream/MirrorSource/SourceCoordinator.cs` +- Test: `tests/NATS.Server.Tests/JetStream/JetStreamMirrorSourceStrictRuntimeTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Mirror_source_transform_and_cross_account_filters_follow_runtime_contract() +{ + await using var fx = await MirrorSourceStrictFixture.StartAsync(); + var report = await fx.RunScenarioAsync(); + report.InvariantViolations.ShouldBeEmpty(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamMirrorSourceStrictRuntimeTests" -v minimal` +Expected: FAIL for cross-account and filter-transform edge combinations. + +**Step 3: Write minimal implementation** + +```csharp +if (!SourceAccountAuthorized(sourceConfig, message.Account)) return; +if (!FilterMatches(sourceConfig, message.Subject)) return; +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamMirrorSourceStrictRuntimeTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/JetStream/StreamManager.cs src/NATS.Server/JetStream/MirrorSource/MirrorCoordinator.cs src/NATS.Server/JetStream/MirrorSource/SourceCoordinator.cs tests/NATS.Server.Tests/JetStream/JetStreamMirrorSourceStrictRuntimeTests.cs +git commit -m "feat: complete jetstream mirror source strict runtime parity" +``` + +### Task 11: Implement FileStore Durable Invariants and Recovery Contract + +**Files:** +- Modify: `src/NATS.Server/JetStream/Storage/FileStore.cs` +- Modify: `src/NATS.Server/JetStream/Storage/FileStoreBlock.cs` +- Test: `tests/NATS.Server.Tests/JetStream/JetStreamFileStoreRecoveryStrictParityTests.cs` +- Test: `tests/NATS.Server.Tests/JetStream/JetStreamFileStoreInvariantTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Filestore_recovery_preserves_sequence_subject_index_and_integrity_after_prune_and_restart_cycles() +{ + var report = await FileStoreStrictFixture.RunRecoveryCycleAsync(); + report.InvariantViolations.ShouldBeEmpty(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamFileStoreRecoveryStrictParityTests|FullyQualifiedName~JetStreamFileStoreInvariantTests" -v minimal` +Expected: FAIL on restart/prune/index invariant checks. + +**Step 3: Write minimal implementation** + +```csharp +PersistManifestVersioned(); +ValidateSequenceAndSubjectIndexOnLoad(); +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamFileStoreRecoveryStrictParityTests|FullyQualifiedName~JetStreamFileStoreInvariantTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/JetStream/Storage/FileStore.cs src/NATS.Server/JetStream/Storage/FileStoreBlock.cs tests/NATS.Server.Tests/JetStream/JetStreamFileStoreRecoveryStrictParityTests.cs tests/NATS.Server.Tests/JetStream/JetStreamFileStoreInvariantTests.cs +git commit -m "feat: enforce filestore durability and recovery invariants" +``` + +### Task 12: Implement RAFT Quorum/NextIndex/Snapshot/Membership Runtime Semantics + +**Files:** +- Modify: `src/NATS.Server/Raft/RaftNode.cs` +- Modify: `src/NATS.Server/Raft/RaftReplicator.cs` +- Modify: `src/NATS.Server/Raft/RaftTransport.cs` +- Modify: `src/NATS.Server/Raft/RaftSnapshotStore.cs` +- Test: `tests/NATS.Server.Tests/Raft/RaftStrictConsensusRuntimeTests.cs` +- Test: `tests/NATS.Server.Tests/Raft/RaftStrictConvergenceRuntimeTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Quorum_and_nextindex_rules_gate_commit_visibility_and_snapshot_catchup_convergence() +{ + var report = await RaftStrictFixture.RunConsensusScenarioAsync(); + report.InvariantViolations.ShouldBeEmpty(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~RaftStrictConsensusRuntimeTests|FullyQualifiedName~RaftStrictConvergenceRuntimeTests" -v minimal` +Expected: FAIL due simplified in-memory quorum and convergence behavior. + +**Step 3: Write minimal implementation** + +```csharp +if (!HasQuorum(appendAcks)) return CommitRejected(); +nextIndex[follower] = ComputeNextIndexOnMismatch(followerState); +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~RaftStrictConsensusRuntimeTests|FullyQualifiedName~RaftStrictConvergenceRuntimeTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/Raft/RaftNode.cs src/NATS.Server/Raft/RaftReplicator.cs src/NATS.Server/Raft/RaftTransport.cs src/NATS.Server/Raft/RaftSnapshotStore.cs tests/NATS.Server.Tests/Raft/RaftStrictConsensusRuntimeTests.cs tests/NATS.Server.Tests/Raft/RaftStrictConvergenceRuntimeTests.cs +git commit -m "feat: implement strict raft consensus and convergence parity" +``` + +### Task 13: Implement JetStream Meta/Replica Governance Runtime Contracts + +**Files:** +- Modify: `src/NATS.Server/JetStream/Cluster/JetStreamMetaGroup.cs` +- Modify: `src/NATS.Server/JetStream/Cluster/StreamReplicaGroup.cs` +- Modify: `src/NATS.Server/JetStream/StreamManager.cs` +- Test: `tests/NATS.Server.Tests/JetStream/JetStreamMetaGovernanceStrictParityTests.cs` +- Test: `tests/NATS.Server.Tests/JetStream/JetStreamReplicaGovernanceStrictParityTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Meta_and_replica_governance_actions_reflect_committed_state_transitions() +{ + await using var fx = await JetStreamGovernanceFixture.StartAsync(); + var report = await fx.RunStepdownPlacementScenarioAsync(); + report.InvariantViolations.ShouldBeEmpty(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamMetaGovernanceStrictParityTests|FullyQualifiedName~JetStreamReplicaGovernanceStrictParityTests" -v minimal` +Expected: FAIL due placeholder governance behavior. + +**Step 3: Write minimal implementation** + +```csharp +public void StepDown() => _leaderId = ElectNextLeader(); +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamMetaGovernanceStrictParityTests|FullyQualifiedName~JetStreamReplicaGovernanceStrictParityTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/JetStream/Cluster/JetStreamMetaGroup.cs src/NATS.Server/JetStream/Cluster/StreamReplicaGroup.cs src/NATS.Server/JetStream/StreamManager.cs tests/NATS.Server.Tests/JetStream/JetStreamMetaGovernanceStrictParityTests.cs tests/NATS.Server.Tests/JetStream/JetStreamReplicaGovernanceStrictParityTests.cs +git commit -m "feat: implement jetstream governance runtime parity semantics" +``` + +### Task 14: Replace Synthetic Profiling and Close Runtime Option Drift + +**Files:** +- Modify: `src/NATS.Server/Monitoring/PprofHandler.cs` +- Modify: `src/NATS.Server/Monitoring/MonitorServer.cs` +- Modify: `src/NATS.Server/NatsServer.cs` +- Modify: `src/NATS.Server/Configuration/ConfigReloader.cs` +- Test: `tests/NATS.Server.Tests/Monitoring/PprofRuntimeParityTests.cs` +- Test: `tests/NATS.Server.Tests/ConfigRuntimeParityTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public async Task Profiling_endpoint_returns_runtime_profile_artifacts_and_config_options_map_to_runtime_behavior() +{ + var report = await OperationalParityFixture.RunAsync(); + report.InvariantViolations.ShouldBeEmpty(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~PprofRuntimeParityTests|FullyQualifiedName~ConfigRuntimeParityTests" -v minimal` +Expected: FAIL due synthetic profiling output and option-runtime mismatches. + +**Step 3: Write minimal implementation** + +```csharp +public byte[] CaptureCpuProfile(int seconds) => _runtimeProfiler.Capture(seconds); +``` + +**Step 4: Run test to verify it passes** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~PprofRuntimeParityTests|FullyQualifiedName~ConfigRuntimeParityTests" -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add src/NATS.Server/Monitoring/PprofHandler.cs src/NATS.Server/Monitoring/MonitorServer.cs src/NATS.Server/NatsServer.cs src/NATS.Server/Configuration/ConfigReloader.cs tests/NATS.Server.Tests/Monitoring/PprofRuntimeParityTests.cs tests/NATS.Server.Tests/ConfigRuntimeParityTests.cs +git commit -m "feat: add runtime profiling parity and close config runtime drift" +``` + +### Task 15: Final Verification and Documentation Synchronization + +**Files:** +- Modify: `differences.md` +- Modify: `docs/plans/2026-02-23-jetstream-remaining-parity-map.md` +- Modify: `docs/plans/2026-02-23-jetstream-remaining-parity-verification.md` +- Modify: `docs/plans/2026-02-23-nats-strict-full-go-parity-map.md` +- Modify: `tests/NATS.Server.Tests/DifferencesParityClosureTests.cs` + +**Step 1: Write the failing test** + +```csharp +[Fact] +public void Differences_and_strict_capability_maps_have_no_claims_without_behavior_and_test_evidence() +{ + var report = StrictParityDocInspector.Load("differences.md", "docs/plans/2026-02-23-nats-strict-full-go-parity-map.md"); + report.DriftRows.ShouldBeEmpty(); +} +``` + +**Step 2: Run test to verify it fails** + +Run: `dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~DifferencesParityClosureTests|FullyQualifiedName~NatsStrictCapabilityInventoryTests" -v minimal` +Expected: FAIL until docs are synchronized with implemented behavior. + +**Step 3: Update parity docs and evidence maps** + +Run: `rg -n "remaining|incomplete|baseline|stub|placeholder" differences.md docs/plans/2026-02-23-nats-strict-full-go-parity-map.md` +Expected: only intentionally deferred items remain and each has explicit blocker rationale. + +**Step 4: Run full verification** + +Run: `dotnet test -v minimal` +Expected: PASS. + +**Step 5: Commit** + +```bash +git add differences.md docs/plans/2026-02-23-jetstream-remaining-parity-map.md docs/plans/2026-02-23-jetstream-remaining-parity-verification.md docs/plans/2026-02-23-nats-strict-full-go-parity-map.md tests/NATS.Server.Tests/DifferencesParityClosureTests.cs +git commit -m "docs: synchronize strict full go parity evidence and status" +``` + +--- + +## Plan Notes + +- Prefer small, behavior-focused deltas in each task. +- Do not mark any capability as closed based on type signatures, placeholders, or route registration alone. +- Keep runtime integration fixtures deterministic and timeout-bounded to avoid flaky parity claims.