Files
natsdotnet/docs/plans/2026-02-23-full-repo-remaining-parity-plan.md
2026-02-23 12:24:29 -05:00

39 KiB

Full-Repo Remaining Parity Implementation Plan

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

Goal: Close every currently unresolved Baseline / N / Stub parity row in differences.md with strict behavior-level parity and test-backed evidence.

Architecture: Use a truth-matrix workflow where each unresolved row is tracked by behavior, test, and docs state. Implement dependencies in layers: core server/protocol/sublist first, then auth/monitoring, then JetStream runtime/storage/RAFT/clustering, then docs synchronization. Rows move to Y only when behavior is implemented and validated by meaningful contract tests.

Tech Stack: .NET 10, C# 14, xUnit 3, Shouldly, ASP.NET Core minimal APIs, System.IO.Pipelines, System.Buffers, System.Text.Json.


Execution guardrails

  • Use @test-driven-development for every task.
  • If behavior diverges from protocol/runtime expectations, switch to @systematic-debugging before code changes.
  • Keep one commit per task.
  • Run @verification-before-completion before final status updates.

Task 1: Add Truth-Matrix Parity Guard and Fix Summary/Table Drift Detection

Files:

  • Modify: tests/NATS.Server.Tests/DifferencesParityClosureTests.cs
  • Create: tests/NATS.Server.Tests/Parity/ParityRowInspector.cs
  • Modify: differences.md

Step 1: Write the failing test

[Fact]
public void Differences_md_has_no_remaining_baseline_n_or_stub_rows_in_tracked_scope()
{
    var report = ParityRowInspector.Load("differences.md");
    report.UnresolvedRows.ShouldBeEmpty();
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~DifferencesParityClosureTests" -v minimal Expected: FAIL with unresolved rows list from table entries (not summary prose).

Step 3: Write minimal implementation

public sealed record ParityRow(string Section, string SubSection, string Feature, string DotNetStatus);
public IReadOnlyList<ParityRow> UnresolvedRows => Rows.Where(r => r.DotNetStatus is "N" or "Baseline" or "Stub").ToArray();

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~DifferencesParityClosureTests" -v minimal Expected: PASS once unresolved rows are fully closed at end of plan.

Step 5: Commit

git add tests/NATS.Server.Tests/DifferencesParityClosureTests.cs tests/NATS.Server.Tests/Parity/ParityRowInspector.cs differences.md
git commit -m "test: enforce row-level parity closure from differences table"

Task 2: Implement Profiling Endpoint (/debug/pprof) Support

Files:

  • Modify: src/NATS.Server/NatsServer.cs
  • Modify: src/NATS.Server/Monitoring/MonitorServer.cs
  • Create: src/NATS.Server/Monitoring/PprofHandler.cs
  • Test: tests/NATS.Server.Tests/Monitoring/PprofEndpointTests.cs

Step 1: Write the failing test

[Fact]
public async Task Debug_pprof_endpoint_returns_profile_index_when_profport_enabled()
{
    await using var fx = await MonitorFixture.StartWithProfilingAsync();
    var body = await fx.GetStringAsync("/debug/pprof");
    body.ShouldContain("profiles");
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~PprofEndpointTests" -v minimal Expected: FAIL with 404 or endpoint missing.

Step 3: Write minimal implementation

app.MapGet("/debug/pprof", (PprofHandler h) => Results.Text(h.Index(), "text/plain"));
app.MapGet("/debug/pprof/profile", (PprofHandler h, int seconds) => Results.File(h.CaptureCpuProfile(seconds), "application/octet-stream"));

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~PprofEndpointTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/NatsServer.cs src/NATS.Server/Monitoring/MonitorServer.cs src/NATS.Server/Monitoring/PprofHandler.cs tests/NATS.Server.Tests/Monitoring/PprofEndpointTests.cs
git commit -m "feat: add profiling endpoint parity support"

Task 3: Add Accept-Loop Reload Lock and Callback Error Hook Parity

Files:

  • Modify: src/NATS.Server/NatsServer.cs
  • Modify: src/NATS.Server/Configuration/ConfigReloader.cs
  • Create: src/NATS.Server/Server/AcceptLoopErrorHandler.cs
  • Test: tests/NATS.Server.Tests/Server/AcceptLoopReloadLockTests.cs
  • Test: tests/NATS.Server.Tests/Server/AcceptLoopErrorCallbackTests.cs

Step 1: Write the failing test

[Fact]
public async Task Accept_loop_blocks_client_creation_while_reload_lock_is_held()
{
    await using var fx = await AcceptLoopFixture.StartAsync();
    await fx.HoldReloadLockAsync();
    (await fx.TryConnectClientAsync(timeoutMs: 150)).ShouldBeFalse();
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~AcceptLoopReloadLockTests|FullyQualifiedName~AcceptLoopErrorCallbackTests" -v minimal Expected: FAIL because create-client path does not acquire reload lock and has no callback-based hook.

Step 3: Write minimal implementation

await _reloadMu.WaitAsync(ct);
try { await CreateClientAsync(socket, ct); }
finally { _reloadMu.Release(); }
_errorHandler?.OnAcceptError(ex, endpoint, delay);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~AcceptLoopReloadLockTests|FullyQualifiedName~AcceptLoopErrorCallbackTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/NatsServer.cs src/NATS.Server/Configuration/ConfigReloader.cs src/NATS.Server/Server/AcceptLoopErrorHandler.cs tests/NATS.Server.Tests/Server/AcceptLoopReloadLockTests.cs tests/NATS.Server.Tests/Server/AcceptLoopErrorCallbackTests.cs
git commit -m "feat: add accept-loop reload lock and error callback parity"

Task 4: Implement Dynamic Buffer Sizing and 3-Tier Output Buffer Pooling

Files:

  • Modify: src/NATS.Server/NatsClient.cs
  • Create: src/NATS.Server/IO/AdaptiveReadBuffer.cs
  • Create: src/NATS.Server/IO/OutboundBufferPool.cs
  • Test: tests/NATS.Server.Tests/IO/AdaptiveReadBufferTests.cs
  • Test: tests/NATS.Server.Tests/IO/OutboundBufferPoolTests.cs

Step 1: Write the failing test

[Fact]
public void Read_buffer_scales_between_512_and_65536_based_on_recent_payload_pattern()
{
    var b = new AdaptiveReadBuffer();
    b.RecordRead(512); b.RecordRead(4096); b.RecordRead(32000);
    b.CurrentSize.ShouldBeGreaterThan(4096);
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~AdaptiveReadBufferTests|FullyQualifiedName~OutboundBufferPoolTests" -v minimal Expected: FAIL because no adaptive model or 3-tier pool exists.

Step 3: Write minimal implementation

public int CurrentSize => Math.Clamp(_target, 512, 64 * 1024);
public IMemoryOwner<byte> Rent(int size) => size <= 512 ? _small.Rent(512) : size <= 4096 ? _medium.Rent(4096) : _large.Rent(64 * 1024);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~AdaptiveReadBufferTests|FullyQualifiedName~OutboundBufferPoolTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/NatsClient.cs src/NATS.Server/IO/AdaptiveReadBuffer.cs src/NATS.Server/IO/OutboundBufferPool.cs tests/NATS.Server.Tests/IO/AdaptiveReadBufferTests.cs tests/NATS.Server.Tests/IO/OutboundBufferPoolTests.cs
git commit -m "feat: add adaptive read buffers and outbound buffer pooling"

Task 5: Unify Inter-Server Opcode Semantics With Client-Kind Routing and Trace Initialization

Files:

  • Modify: src/NATS.Server/Protocol/NatsParser.cs
  • Modify: src/NATS.Server/Protocol/ClientCommandMatrix.cs
  • Modify: src/NATS.Server/NatsClient.cs
  • Modify: src/NATS.Server/Routes/RouteConnection.cs
  • Modify: src/NATS.Server/Gateways/GatewayConnection.cs
  • Modify: src/NATS.Server/LeafNodes/LeafConnection.cs
  • Test: tests/NATS.Server.Tests/Protocol/InterServerOpcodeRoutingTests.cs
  • Test: tests/NATS.Server.Tests/Protocol/MessageTraceInitializationTests.cs

Step 1: Write the failing test

[Fact]
public void Parser_dispatch_rejects_Aplus_for_client_kind_client_but_allows_for_gateway()
{
    var m = new ClientCommandMatrix();
    m.IsAllowed(ClientKind.Client, "A+").ShouldBeFalse();
    m.IsAllowed(ClientKind.Gateway, "A+").ShouldBeTrue();
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~InterServerOpcodeRoutingTests|FullyQualifiedName~MessageTraceInitializationTests" -v minimal Expected: FAIL due incomplete parser/dispatch trace-init parity.

Step 3: Write minimal implementation

if (!CommandMatrix.IsAllowed(kind, op))
    throw new ProtocolViolationException($"operation {op} not allowed for {kind}");
_traceContext = MessageTraceContext.CreateFromConnect(connectOpts);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~InterServerOpcodeRoutingTests|FullyQualifiedName~MessageTraceInitializationTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/Protocol/NatsParser.cs src/NATS.Server/Protocol/ClientCommandMatrix.cs src/NATS.Server/NatsClient.cs src/NATS.Server/Routes/RouteConnection.cs src/NATS.Server/Gateways/GatewayConnection.cs src/NATS.Server/LeafNodes/LeafConnection.cs tests/NATS.Server.Tests/Protocol/InterServerOpcodeRoutingTests.cs tests/NATS.Server.Tests/Protocol/MessageTraceInitializationTests.cs
git commit -m "feat: enforce inter-server opcode routing and trace initialization"

Task 6: Implement SubList Missing Features (Notifications, Local/Remote Filters, Queue Weight, MatchBytes)

Files:

  • Modify: src/NATS.Server/Subscriptions/SubList.cs
  • Modify: src/NATS.Server/Subscriptions/RemoteSubscription.cs
  • Modify: src/NATS.Server/Subscriptions/Subscription.cs
  • Test: tests/NATS.Server.Tests/SubList/SubListNotificationTests.cs
  • Test: tests/NATS.Server.Tests/SubList/SubListRemoteFilterTests.cs
  • Test: tests/NATS.Server.Tests/SubList/SubListQueueWeightTests.cs
  • Test: tests/NATS.Server.Tests/SubList/SubListMatchBytesTests.cs

Step 1: Write the failing test

[Fact]
public void MatchBytes_matches_subject_without_string_allocation_and_respects_remote_filter()
{
    var sl = new SubList();
    sl.MatchBytes("orders.created"u8.ToArray()).PlainSubs.Length.ShouldBe(0);
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~SubListNotificationTests|FullyQualifiedName~SubListRemoteFilterTests|FullyQualifiedName~SubListQueueWeightTests|FullyQualifiedName~SubListMatchBytesTests" -v minimal Expected: FAIL because APIs and behavior are missing.

Step 3: Write minimal implementation

public SubListResult MatchBytes(ReadOnlySpan<byte> subjectUtf8) => Match(Encoding.ASCII.GetString(subjectUtf8));
public event Action<InterestChange>? InterestChanged;
if (remoteSub.QueueWeight > 0) expanded.AddRange(Enumerable.Repeat(remoteSub, remoteSub.QueueWeight));

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~SubListNotificationTests|FullyQualifiedName~SubListRemoteFilterTests|FullyQualifiedName~SubListQueueWeightTests|FullyQualifiedName~SubListMatchBytesTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/Subscriptions/SubList.cs src/NATS.Server/Subscriptions/RemoteSubscription.cs src/NATS.Server/Subscriptions/Subscription.cs tests/NATS.Server.Tests/SubList/SubListNotificationTests.cs tests/NATS.Server.Tests/SubList/SubListRemoteFilterTests.cs tests/NATS.Server.Tests/SubList/SubListQueueWeightTests.cs tests/NATS.Server.Tests/SubList/SubListMatchBytesTests.cs
git commit -m "feat: add remaining sublist parity behaviors"

Task 7: Add Trie Fanout Optimization and Async Cache Sweep Behavior

Files:

  • Modify: src/NATS.Server/Subscriptions/SubList.cs
  • Create: src/NATS.Server/Subscriptions/SubListCacheSweeper.cs
  • Test: tests/NATS.Server.Tests/SubList/SubListHighFanoutOptimizationTests.cs
  • Test: tests/NATS.Server.Tests/SubList/SubListAsyncCacheSweepTests.cs

Step 1: Write the failing test

[Fact]
public async Task Cache_sweep_runs_async_and_prunes_stale_entries_without_write_locking_match_path()
{
    var fx = await SubListSweepFixture.BuildLargeCacheAsync();
    await fx.TriggerSweepAsync();
    fx.CacheCount.ShouldBeLessThan(fx.InitialCacheCount);
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~SubListHighFanoutOptimizationTests|FullyQualifiedName~SubListAsyncCacheSweepTests" -v minimal Expected: FAIL because sweep is currently inline and no high-fanout node optimization exists.

Step 3: Write minimal implementation

if (node.PlainSubs.Count > 256) node.EnablePackedList();
_sweeper.ScheduleSweep(_cache, generation);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~SubListHighFanoutOptimizationTests|FullyQualifiedName~SubListAsyncCacheSweepTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/Subscriptions/SubList.cs src/NATS.Server/Subscriptions/SubListCacheSweeper.cs tests/NATS.Server.Tests/SubList/SubListHighFanoutOptimizationTests.cs tests/NATS.Server.Tests/SubList/SubListAsyncCacheSweepTests.cs
git commit -m "feat: add trie fanout optimization and async cache sweep"

Task 8: Complete Route Parity (Account-Specific Routes, Topology Gossip, Route Compression)

Files:

  • Modify: src/NATS.Server/Routes/RouteConnection.cs
  • Modify: src/NATS.Server/Routes/RouteManager.cs
  • Modify: src/NATS.Server/Configuration/ClusterOptions.cs
  • Test: tests/NATS.Server.Tests/Routes/RouteAccountScopedTests.cs
  • Test: tests/NATS.Server.Tests/Routes/RouteTopologyGossipTests.cs
  • Test: tests/NATS.Server.Tests/Routes/RouteCompressionTests.cs

Step 1: Write the failing test

[Fact]
public async Task Route_connect_exchange_includes_account_scope_and_topology_gossip_snapshot()
{
    await using var fx = await RouteGossipFixture.StartPairAsync();
    var info = await fx.ReadRouteConnectInfoAsync();
    info.Accounts.ShouldContain("A");
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~RouteAccountScopedTests|FullyQualifiedName~RouteTopologyGossipTests|FullyQualifiedName~RouteCompressionTests" -v minimal Expected: FAIL because route handshake is still minimal text and no compression/account-scoped route model.

Step 3: Write minimal implementation

await WriteLineAsync($"CONNECT {JsonSerializer.Serialize(routeInfo)}", ct);
if (_options.Compression == RouteCompression.S2) payload = S2Codec.Compress(payload);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~RouteAccountScopedTests|FullyQualifiedName~RouteTopologyGossipTests|FullyQualifiedName~RouteCompressionTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/Routes/RouteConnection.cs src/NATS.Server/Routes/RouteManager.cs src/NATS.Server/Configuration/ClusterOptions.cs tests/NATS.Server.Tests/Routes/RouteAccountScopedTests.cs tests/NATS.Server.Tests/Routes/RouteTopologyGossipTests.cs tests/NATS.Server.Tests/Routes/RouteCompressionTests.cs
git commit -m "feat: complete route account gossip and compression parity"

Task 9: Complete Gateway and Leaf Advanced Semantics (Interest-Only, Hub/Spoke Mapping)

Files:

  • Modify: src/NATS.Server/Gateways/GatewayConnection.cs
  • Modify: src/NATS.Server/Gateways/GatewayManager.cs
  • Modify: src/NATS.Server/LeafNodes/LeafConnection.cs
  • Modify: src/NATS.Server/LeafNodes/LeafNodeManager.cs
  • Modify: src/NATS.Server/NatsServer.cs
  • Test: tests/NATS.Server.Tests/Gateways/GatewayInterestOnlyParityTests.cs
  • Test: tests/NATS.Server.Tests/LeafNodes/LeafHubSpokeMappingParityTests.cs

Step 1: Write the failing test

[Fact]
public async Task Gateway_interest_only_mode_forwards_only_subjects_with_remote_interest_and_reply_map_roundtrips()
{
    await using var fx = await GatewayInterestFixture.StartAsync();
    (await fx.ForwardedWithoutInterestCountAsync()).ShouldBe(0);
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~GatewayInterestOnlyParityTests|FullyQualifiedName~LeafHubSpokeMappingParityTests" -v minimal Expected: FAIL because advanced interest-only and leaf account remap semantics are incomplete.

Step 3: Write minimal implementation

if (!_interestTable.HasInterest(account, subject)) return;
var mapped = _hubSpokeMapper.Map(account, subject, direction);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~GatewayInterestOnlyParityTests|FullyQualifiedName~LeafHubSpokeMappingParityTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/Gateways/GatewayConnection.cs src/NATS.Server/Gateways/GatewayManager.cs src/NATS.Server/LeafNodes/LeafConnection.cs src/NATS.Server/LeafNodes/LeafNodeManager.cs src/NATS.Server/NatsServer.cs tests/NATS.Server.Tests/Gateways/GatewayInterestOnlyParityTests.cs tests/NATS.Server.Tests/LeafNodes/LeafHubSpokeMappingParityTests.cs
git commit -m "feat: complete gateway and leaf advanced parity semantics"

Task 10: Add Auth Extension Parity (Custom Interface, External Callout, Proxy Auth)

Files:

  • Modify: src/NATS.Server/Auth/AuthService.cs
  • Modify: src/NATS.Server/Auth/IAuthenticator.cs
  • Create: src/NATS.Server/Auth/ExternalAuthCalloutAuthenticator.cs
  • Create: src/NATS.Server/Auth/ProxyAuthenticator.cs
  • Modify: src/NATS.Server/NatsOptions.cs
  • Test: tests/NATS.Server.Tests/Auth/AuthExtensionParityTests.cs
  • Test: tests/NATS.Server.Tests/Auth/ExternalAuthCalloutTests.cs
  • Test: tests/NATS.Server.Tests/Auth/ProxyAuthTests.cs

Step 1: Write the failing test

[Fact]
public async Task External_callout_authenticator_can_allow_and_deny_with_timeout_and_reason_mapping()
{
    var result = await AuthExtensionFixture.AuthenticateViaExternalAsync("u", "p");
    result.Success.ShouldBeTrue();
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~AuthExtensionParityTests|FullyQualifiedName~ExternalAuthCalloutTests|FullyQualifiedName~ProxyAuthTests" -v minimal Expected: FAIL because extension points are not wired.

Step 3: Write minimal implementation

public interface IExternalAuthClient { Task<ExternalAuthDecision> AuthorizeAsync(ExternalAuthRequest req, CancellationToken ct); }
if (_options.ExternalAuth is { Enabled: true }) authenticators.Add(new ExternalAuthCalloutAuthenticator(...));

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~AuthExtensionParityTests|FullyQualifiedName~ExternalAuthCalloutTests|FullyQualifiedName~ProxyAuthTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/Auth/AuthService.cs src/NATS.Server/Auth/IAuthenticator.cs src/NATS.Server/Auth/ExternalAuthCalloutAuthenticator.cs src/NATS.Server/Auth/ProxyAuthenticator.cs src/NATS.Server/NatsOptions.cs tests/NATS.Server.Tests/Auth/AuthExtensionParityTests.cs tests/NATS.Server.Tests/Auth/ExternalAuthCalloutTests.cs tests/NATS.Server.Tests/Auth/ProxyAuthTests.cs
git commit -m "feat: add custom external and proxy authentication parity"

Task 11: Close Monitoring Parity Gaps (connz filters/details and missing identity/tls/proxy fields)

Files:

  • Modify: src/NATS.Server/Monitoring/ConnzHandler.cs
  • Modify: src/NATS.Server/Monitoring/Connz.cs
  • Modify: src/NATS.Server/Monitoring/VarzHandler.cs
  • Modify: src/NATS.Server/Monitoring/Varz.cs
  • Modify: src/NATS.Server/Monitoring/ClosedClient.cs
  • Test: tests/NATS.Server.Tests/Monitoring/ConnzParityFilterTests.cs
  • Test: tests/NATS.Server.Tests/Monitoring/ConnzParityFieldTests.cs
  • Test: tests/NATS.Server.Tests/Monitoring/VarzSlowConsumerBreakdownTests.cs

Step 1: Write the failing test

[Fact]
public async Task Connz_filters_by_user_account_and_subject_and_includes_tls_peer_and_jwt_metadata()
{
    await using var fx = await MonitoringParityFixture.StartAsync();
    var connz = await fx.GetConnzAsync("?user=u&acc=A&filter_subject=orders.*&subs=detail");
    connz.Conns.ShouldAllBe(c => c.Account == "A");
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~ConnzParityFilterTests|FullyQualifiedName~ConnzParityFieldTests|FullyQualifiedName~VarzSlowConsumerBreakdownTests" -v minimal Expected: FAIL because filters/fields are not fully populated.

Step 3: Write minimal implementation

if (!string.IsNullOrEmpty(opts.User)) conns = conns.Where(c => c.AuthorizedUser == opts.User).ToList();
if (!string.IsNullOrEmpty(opts.Account)) conns = conns.Where(c => c.Account == opts.Account).ToList();
if (!string.IsNullOrEmpty(opts.FilterSubject)) conns = conns.Where(c => c.Subs.Any(s => SubjectMatch.MatchLiteral(s, opts.FilterSubject))).ToList();
info.TlsPeerCertSubject = client.TlsState?.PeerSubject ?? "";
info.JwtIssuerKey = client.AuthContext?.IssuerKey ?? "";

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~ConnzParityFilterTests|FullyQualifiedName~ConnzParityFieldTests|FullyQualifiedName~VarzSlowConsumerBreakdownTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/Monitoring/ConnzHandler.cs src/NATS.Server/Monitoring/Connz.cs src/NATS.Server/Monitoring/VarzHandler.cs src/NATS.Server/Monitoring/Varz.cs src/NATS.Server/Monitoring/ClosedClient.cs tests/NATS.Server.Tests/Monitoring/ConnzParityFilterTests.cs tests/NATS.Server.Tests/Monitoring/ConnzParityFieldTests.cs tests/NATS.Server.Tests/Monitoring/VarzSlowConsumerBreakdownTests.cs
git commit -m "feat: close monitoring parity filters and field coverage"

Task 12: Complete JetStream Stream Runtime Feature Parity

Files:

  • Modify: src/NATS.Server/JetStream/Models/StreamConfig.cs
  • Modify: src/NATS.Server/JetStream/StreamManager.cs
  • Modify: src/NATS.Server/JetStream/Publish/JetStreamPublisher.cs
  • Modify: src/NATS.Server/JetStream/Publish/PublishPreconditions.cs
  • Modify: src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs
  • Modify: src/NATS.Server/JetStream/Validation/JetStreamConfigValidator.cs
  • Test: tests/NATS.Server.Tests/JetStream/JetStreamStreamRuntimeParityTests.cs
  • Test: tests/NATS.Server.Tests/JetStream/JetStreamStreamFeatureToggleParityTests.cs

Step 1: Write the failing test

[Fact]
public async Task Stream_runtime_enforces_retention_ttl_per_subject_max_msg_size_and_guard_flags_with_go_error_contracts()
{
    await using var fx = await JetStreamRuntimeFixture.StartWithStrictPolicyAsync();
    var ack = await fx.PublishAsync("orders.created", payloadSize: 2048);
    ack.ErrorCode.ShouldBe(10054);
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamStreamRuntimeParityTests|FullyQualifiedName~JetStreamStreamFeatureToggleParityTests" -v minimal Expected: FAIL due incomplete runtime semantics for remaining stream rows.

Step 3: Write minimal implementation

ApplyRetentionPolicy(stream, nowUtc); // Limits / Interest / WorkQueue behavior
ApplyPerSubjectCaps(stream);
if (config.Sealed || (isDelete && config.DenyDelete) || (isPurge && config.DenyPurge)) return Error(10052);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamStreamRuntimeParityTests|FullyQualifiedName~JetStreamStreamFeatureToggleParityTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/JetStream/Models/StreamConfig.cs src/NATS.Server/JetStream/StreamManager.cs src/NATS.Server/JetStream/Publish/JetStreamPublisher.cs src/NATS.Server/JetStream/Publish/PublishPreconditions.cs src/NATS.Server/JetStream/Api/Handlers/StreamApiHandlers.cs src/NATS.Server/JetStream/Validation/JetStreamConfigValidator.cs tests/NATS.Server.Tests/JetStream/JetStreamStreamRuntimeParityTests.cs tests/NATS.Server.Tests/JetStream/JetStreamStreamFeatureToggleParityTests.cs
git commit -m "feat: complete jetstream stream runtime parity"

Task 13: Complete JetStream Consumer Runtime Parity

Files:

  • Modify: src/NATS.Server/JetStream/Models/ConsumerConfig.cs
  • Modify: src/NATS.Server/JetStream/ConsumerManager.cs
  • 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/Api/Handlers/ConsumerApiHandlers.cs
  • Test: tests/NATS.Server.Tests/JetStream/JetStreamConsumerRuntimeParityTests.cs
  • Test: tests/NATS.Server.Tests/JetStream/JetStreamConsumerFlowReplayParityTests.cs

Step 1: Write the failing test

[Fact]
public async Task Consumer_runtime_honors_deliver_policy_ack_all_redelivery_max_deliver_backoff_flow_rate_and_replay_timing()
{
    await using var fx = await JetStreamConsumerRuntimeFixture.StartAsync();
    var result = await fx.RunScenarioAsync();
    result.UnexpectedTransitions.ShouldBeEmpty();
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamConsumerRuntimeParityTests|FullyQualifiedName~JetStreamConsumerFlowReplayParityTests" -v minimal Expected: FAIL while baseline behavior remains.

Step 3: Write minimal implementation

if (ackPolicy == AckPolicy.All) _ackState.AdvanceFloor(seq);
if (deliveries >= config.MaxDeliver) return DeliveryDecision.Drop;
if (config.FlowControl) enqueue(FlowControlFrame());

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamConsumerRuntimeParityTests|FullyQualifiedName~JetStreamConsumerFlowReplayParityTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/JetStream/Models/ConsumerConfig.cs src/NATS.Server/JetStream/ConsumerManager.cs 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/Api/Handlers/ConsumerApiHandlers.cs tests/NATS.Server.Tests/JetStream/JetStreamConsumerRuntimeParityTests.cs tests/NATS.Server.Tests/JetStream/JetStreamConsumerFlowReplayParityTests.cs
git commit -m "feat: complete jetstream consumer runtime parity"

Task 14: Complete JetStream Storage Backend Parity (Layout, Indexing, TTL, Compression, Encryption)

Files:

  • Modify: src/NATS.Server/JetStream/Storage/IStreamStore.cs
  • Modify: src/NATS.Server/JetStream/Storage/FileStoreOptions.cs
  • Modify: src/NATS.Server/JetStream/Storage/FileStoreBlock.cs
  • Modify: src/NATS.Server/JetStream/Storage/FileStore.cs
  • Modify: src/NATS.Server/JetStream/Storage/MemStore.cs
  • Test: tests/NATS.Server.Tests/JetStream/JetStreamFileStoreLayoutParityTests.cs
  • Test: tests/NATS.Server.Tests/JetStream/JetStreamFileStoreCryptoCompressionTests.cs

Step 1: Write the failing test

[Fact]
public async Task File_store_uses_block_index_layout_with_ttl_prune_and_optional_compression_encryption_roundtrip()
{
    await using var fx = await FileStoreParityFixture.StartAsync();
    await fx.AppendManyAsync(10000);
    (await fx.ValidateBlockAndIndexInvariantsAsync()).ShouldBeTrue();
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamFileStoreLayoutParityTests|FullyQualifiedName~JetStreamFileStoreCryptoCompressionTests" -v minimal Expected: FAIL because storage semantics are still simplified.

Step 3: Write minimal implementation

public sealed record SequencePointer(int BlockId, int Slot, long Offset);
if (_options.EnableCompression) bytes = S2Codec.Compress(bytes);
if (_options.EnableEncryption) bytes = _crypto.Encrypt(bytes, nonce);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamFileStoreLayoutParityTests|FullyQualifiedName~JetStreamFileStoreCryptoCompressionTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/JetStream/Storage/IStreamStore.cs src/NATS.Server/JetStream/Storage/FileStoreOptions.cs src/NATS.Server/JetStream/Storage/FileStoreBlock.cs src/NATS.Server/JetStream/Storage/FileStore.cs src/NATS.Server/JetStream/Storage/MemStore.cs tests/NATS.Server.Tests/JetStream/JetStreamFileStoreLayoutParityTests.cs tests/NATS.Server.Tests/JetStream/JetStreamFileStoreCryptoCompressionTests.cs
git commit -m "feat: complete jetstream storage backend parity"

Task 15: Complete Mirror/Source Parity (Mirror Consumer, Source Mapping, Cross-Account)

Files:

  • Modify: src/NATS.Server/JetStream/MirrorSource/MirrorCoordinator.cs
  • Modify: src/NATS.Server/JetStream/MirrorSource/SourceCoordinator.cs
  • Modify: src/NATS.Server/JetStream/StreamManager.cs
  • Modify: src/NATS.Server/Auth/Account.cs
  • Test: tests/NATS.Server.Tests/JetStream/JetStreamMirrorSourceRuntimeParityTests.cs

Step 1: Write the failing test

[Fact]
public async Task Mirror_source_runtime_enforces_cross_account_permissions_and_subject_mapping_with_sync_state_tracking()
{
    await using var fx = await MirrorSourceParityFixture.StartAsync();
    var sync = await fx.GetSyncStateAsync("AGG");
    sync.LastOriginSequence.ShouldBeGreaterThan(0);
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamMirrorSourceRuntimeParityTests" -v minimal Expected: FAIL due missing runtime parity semantics.

Step 3: Write minimal implementation

if (!_accountPolicy.CanMirror(sourceAccount, targetAccount)) return;
subject = _sourceTransform.Apply(subject);
_mirrorState.Update(originSequence, DateTime.UtcNow);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamMirrorSourceRuntimeParityTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/JetStream/MirrorSource/MirrorCoordinator.cs src/NATS.Server/JetStream/MirrorSource/SourceCoordinator.cs src/NATS.Server/JetStream/StreamManager.cs src/NATS.Server/Auth/Account.cs tests/NATS.Server.Tests/JetStream/JetStreamMirrorSourceRuntimeParityTests.cs
git commit -m "feat: complete mirror and source runtime parity"

Task 16: Complete RAFT Consensus Parity (Heartbeat, NextIndex, Snapshot Transfer, Membership)

Files:

  • Modify: src/NATS.Server/Raft/RaftRpcContracts.cs
  • Modify: src/NATS.Server/Raft/RaftTransport.cs
  • Modify: src/NATS.Server/Raft/RaftReplicator.cs
  • Modify: src/NATS.Server/Raft/RaftNode.cs
  • Modify: src/NATS.Server/Raft/RaftLog.cs
  • Modify: src/NATS.Server/Raft/RaftSnapshotStore.cs
  • Test: tests/NATS.Server.Tests/Raft/RaftConsensusRuntimeParityTests.cs
  • Test: tests/NATS.Server.Tests/Raft/RaftSnapshotTransferRuntimeParityTests.cs
  • Test: tests/NATS.Server.Tests/Raft/RaftMembershipRuntimeParityTests.cs

Step 1: Write the failing test

[Fact]
public async Task Raft_cluster_commits_with_next_index_backtracking_and_snapshot_install_for_lagging_follower()
{
    await using var cluster = await RaftRuntimeFixture.StartThreeNodeAsync();
    (await cluster.RunCommitAndCatchupScenarioAsync()).ShouldBeTrue();
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~RaftConsensusRuntimeParityTests|FullyQualifiedName~RaftSnapshotTransferRuntimeParityTests|FullyQualifiedName~RaftMembershipRuntimeParityTests" -v minimal Expected: FAIL under current hook-level behavior.

Step 3: Write minimal implementation

while (!AppendEntriesAccepted(follower, nextIndex[follower])) nextIndex[follower]--;
if (nextIndex[follower] <= snapshot.LastIncludedIndex) await transport.InstallSnapshotAsync(...);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~RaftConsensusRuntimeParityTests|FullyQualifiedName~RaftSnapshotTransferRuntimeParityTests|FullyQualifiedName~RaftMembershipRuntimeParityTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/Raft/RaftRpcContracts.cs src/NATS.Server/Raft/RaftTransport.cs src/NATS.Server/Raft/RaftReplicator.cs src/NATS.Server/Raft/RaftNode.cs src/NATS.Server/Raft/RaftLog.cs src/NATS.Server/Raft/RaftSnapshotStore.cs tests/NATS.Server.Tests/Raft/RaftConsensusRuntimeParityTests.cs tests/NATS.Server.Tests/Raft/RaftSnapshotTransferRuntimeParityTests.cs tests/NATS.Server.Tests/Raft/RaftMembershipRuntimeParityTests.cs
git commit -m "feat: complete raft runtime consensus parity"

Task 17: Complete JetStream Cluster Governance and Cross-Cluster JetStream Parity

Files:

  • Modify: src/NATS.Server/JetStream/Cluster/JetStreamMetaGroup.cs
  • Modify: src/NATS.Server/JetStream/Cluster/StreamReplicaGroup.cs
  • Modify: src/NATS.Server/JetStream/Cluster/AssetPlacementPlanner.cs
  • Modify: src/NATS.Server/NatsServer.cs
  • Modify: src/NATS.Server/Gateways/GatewayManager.cs
  • Test: tests/NATS.Server.Tests/JetStream/JetStreamClusterGovernanceRuntimeParityTests.cs
  • Test: tests/NATS.Server.Tests/JetStream/JetStreamCrossClusterRuntimeParityTests.cs

Step 1: Write the failing test

[Fact]
public async Task Jetstream_cluster_governance_applies_consensus_backed_placement_and_cross_cluster_replication()
{
    await using var fx = await JetStreamClusterRuntimeFixture.StartAsync();
    var result = await fx.CreateAndReplicateStreamAsync();
    result.Success.ShouldBeTrue();
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamClusterGovernanceRuntimeParityTests|FullyQualifiedName~JetStreamCrossClusterRuntimeParityTests" -v minimal Expected: FAIL while governance and cross-cluster paths are still partial.

Step 3: Write minimal implementation

await _metaGroup.ProposePlacementAsync(stream, replicas, ct);
await _replicaGroup.ApplyCommittedPlacementAsync(plan, ct);
if (message.Subject.StartsWith("$JS.CLUSTER.")) await _gatewayManager.ForwardJetStreamClusterMessageAsync(message, ct);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStreamClusterGovernanceRuntimeParityTests|FullyQualifiedName~JetStreamCrossClusterRuntimeParityTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/JetStream/Cluster/JetStreamMetaGroup.cs src/NATS.Server/JetStream/Cluster/StreamReplicaGroup.cs src/NATS.Server/JetStream/Cluster/AssetPlacementPlanner.cs src/NATS.Server/NatsServer.cs src/NATS.Server/Gateways/GatewayManager.cs tests/NATS.Server.Tests/JetStream/JetStreamClusterGovernanceRuntimeParityTests.cs tests/NATS.Server.Tests/JetStream/JetStreamCrossClusterRuntimeParityTests.cs
git commit -m "feat: complete jetstream cluster governance and cross-cluster parity"

Task 18: Implement MQTT Transport Parity Baseline-to-Feature Completion

Files:

  • Modify: src/NATS.Server/NatsServer.cs
  • Create: src/NATS.Server/Mqtt/MqttListener.cs
  • Create: src/NATS.Server/Mqtt/MqttConnection.cs
  • Create: src/NATS.Server/Mqtt/MqttProtocolParser.cs
  • Modify: src/NATS.Server/Configuration/MqttOptions.cs
  • Test: tests/NATS.Server.Tests/Mqtt/MqttListenerParityTests.cs
  • Test: tests/NATS.Server.Tests/Mqtt/MqttPublishSubscribeParityTests.cs

Step 1: Write the failing test

[Fact]
public async Task Mqtt_listener_accepts_connect_and_routes_publish_to_matching_subscription()
{
    await using var fx = await MqttFixture.StartAsync();
    var payload = await fx.PublishAndReceiveAsync("sensors.temp", "42");
    payload.ShouldBe("42");
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~MqttListenerParityTests|FullyQualifiedName~MqttPublishSubscribeParityTests" -v minimal Expected: FAIL because MQTT transport listener is not implemented.

Step 3: Write minimal implementation

_listener = new TcpListener(IPAddress.Parse(_opts.Host), _opts.Port);
while (!ct.IsCancellationRequested) _ = HandleAsync(await _listener.AcceptTcpClientAsync(ct), ct);
if (packet.Type == MqttPacketType.Publish) _router.ProcessMessage(topic, null, default, payload, mqttClientAdapter);

Step 4: Run test to verify it passes

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~MqttListenerParityTests|FullyQualifiedName~MqttPublishSubscribeParityTests" -v minimal Expected: PASS.

Step 5: Commit

git add src/NATS.Server/NatsServer.cs src/NATS.Server/Mqtt/MqttListener.cs src/NATS.Server/Mqtt/MqttConnection.cs src/NATS.Server/Mqtt/MqttProtocolParser.cs src/NATS.Server/Configuration/MqttOptions.cs tests/NATS.Server.Tests/Mqtt/MqttListenerParityTests.cs tests/NATS.Server.Tests/Mqtt/MqttPublishSubscribeParityTests.cs
git commit -m "feat: add mqtt transport parity implementation"

Task 19: Final Docs and Verification Closure

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

Step 1: Write the failing test

[Fact]
public void Differences_table_has_no_remaining_unresolved_rows_after_full_parity_execution()
{
    var report = ParityRowInspector.Load("differences.md");
    report.UnresolvedRows.ShouldBeEmpty();
}

Step 2: Run test to verify it fails

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~DifferencesParityClosureTests" -v minimal Expected: FAIL until all rows are updated from validated evidence.

Step 3: Write minimal implementation

## Summary: Remaining Gaps

### Full Repo
None in tracked scope after this plan; unresolved table rows are closed or explicitly blocked with evidence.

Step 4: Run verification gates

Run: dotnet test tests/NATS.Server.Tests --filter "FullyQualifiedName~JetStream|FullyQualifiedName~Raft|FullyQualifiedName~Route|FullyQualifiedName~Gateway|FullyQualifiedName~Leaf|FullyQualifiedName~SubList|FullyQualifiedName~Connz|FullyQualifiedName~Varz|FullyQualifiedName~Auth|FullyQualifiedName~Mqtt|FullyQualifiedName~DifferencesParityClosureTests" -v minimal Expected: PASS.

Run: dotnet test -v minimal Expected: PASS.

Step 5: Commit

git add differences.md docs/plans/2026-02-23-jetstream-remaining-parity-map.md docs/plans/2026-02-23-jetstream-remaining-parity-verification.md
git commit -m "docs: close full-repo parity gaps with verified evidence"