From 0436e08fc1e7bc05c3a63aa4e3940f99d54b2fab Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 27 Feb 2026 05:17:35 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20add=20FeatureClassifier=20=E2=80=94=20h?= =?UTF-8?q?euristic-based=20feature=20classification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reports/current.md | 2 +- reports/report_2dd2321.md | 35 +++++++ .../Audit/FeatureClassifier.cs | 91 +++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 reports/report_2dd2321.md create mode 100644 tools/NatsNet.PortTracker/Audit/FeatureClassifier.cs diff --git a/reports/current.md b/reports/current.md index ea1a213..50cdb85 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-27 10:16:59 UTC +Generated: 2026-02-27 10:17:36 UTC ## Modules (12 total) diff --git a/reports/report_2dd2321.md b/reports/report_2dd2321.md new file mode 100644 index 0000000..50cdb85 --- /dev/null +++ b/reports/report_2dd2321.md @@ -0,0 +1,35 @@ +# NATS .NET Porting Status Report + +Generated: 2026-02-27 10:17:36 UTC + +## Modules (12 total) + +| Status | Count | +|--------|-------| +| verified | 12 | + +## Features (3673 total) + +| Status | Count | +|--------|-------| +| unknown | 3394 | +| verified | 279 | + +## Unit Tests (3257 total) + +| Status | Count | +|--------|-------| +| deferred | 2680 | +| n_a | 187 | +| verified | 390 | + +## Library Mappings (36 total) + +| Status | Count | +|--------|-------| +| mapped | 36 | + + +## Overall Progress + +**868/6942 items complete (12.5%)** diff --git a/tools/NatsNet.PortTracker/Audit/FeatureClassifier.cs b/tools/NatsNet.PortTracker/Audit/FeatureClassifier.cs new file mode 100644 index 0000000..75591c3 --- /dev/null +++ b/tools/NatsNet.PortTracker/Audit/FeatureClassifier.cs @@ -0,0 +1,91 @@ +namespace NatsNet.PortTracker.Audit; + +/// +/// Classifies features by inspecting the SourceIndexer for their .NET implementation status. +/// Priority: n_a lookup -> method-not-found -> stub detection -> verified. +/// +public sealed class FeatureClassifier +{ + public record ClassificationResult(string Status, string Reason); + + public record FeatureRecord( + long Id, + string DotnetClass, + string DotnetMethod, + string GoFile, + string GoMethod); + + private readonly SourceIndexer _indexer; + + // N/A lookup: (goMethod pattern) -> reason + // Checked case-insensitively against go_method + private static readonly Dictionary NaByGoMethod = new(StringComparer.OrdinalIgnoreCase) + { + ["Noticef"] = ".NET uses Microsoft.Extensions.Logging", + ["Debugf"] = ".NET uses Microsoft.Extensions.Logging", + ["Tracef"] = ".NET uses Microsoft.Extensions.Logging", + ["Warnf"] = ".NET uses Microsoft.Extensions.Logging", + ["Errorf"] = ".NET uses Microsoft.Extensions.Logging", + ["Fatalf"] = ".NET uses Microsoft.Extensions.Logging", + }; + + // N/A lookup: go_file + go_method patterns + private static readonly List<(Func Match, string Reason)> NaPatterns = + [ + // Signal handling — .NET uses IHostApplicationLifetime + (f => f.GoMethod.Equals("handleSignals", StringComparison.OrdinalIgnoreCase), ".NET uses IHostApplicationLifetime"), + (f => f.GoMethod.Equals("processSignal", StringComparison.OrdinalIgnoreCase), ".NET uses IHostApplicationLifetime"), + ]; + + public FeatureClassifier(SourceIndexer indexer) + { + _indexer = indexer; + } + + /// + /// Classify a single feature. Returns status and reason. + /// + public ClassificationResult Classify(FeatureRecord feature) + { + // 1. N/A lookup — check go_method against known patterns + if (NaByGoMethod.TryGetValue(feature.GoMethod, out var naReason)) + return new ClassificationResult("n_a", naReason); + + foreach (var (match, reason) in NaPatterns) + { + if (match(feature)) + return new ClassificationResult("n_a", reason); + } + + // 2. Handle comma-separated dotnet_class (e.g. "ClosedRingBuffer,ClosedClient") + var classNames = feature.DotnetClass.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var methodName = feature.DotnetMethod; + + // Try each class name + foreach (var className in classNames) + { + var methods = _indexer.Lookup(className, methodName); + if (methods.Count > 0) + { + // Found the method — classify based on body analysis + // Use the "best" match: prefer non-stub over stub + var best = methods.OrderByDescending(m => m.StatementCount).First(); + + if (best.IsStub) + return new ClassificationResult("stub", $"Body is throw NotImplementedException at {Path.GetFileName(best.FilePath)}:{best.LineNumber}"); + + if (best.IsPartial) + return new ClassificationResult("stub", $"Partial implementation with NotImplementedException at {Path.GetFileName(best.FilePath)}:{best.LineNumber}"); + + return new ClassificationResult("verified", $"Method found with {best.StatementCount} statement(s) at {Path.GetFileName(best.FilePath)}:{best.LineNumber}"); + } + } + + // 3. Method not found — check if any class exists + var anyClassFound = classNames.Any(c => _indexer.HasClass(c)); + if (anyClassFound) + return new ClassificationResult("deferred", "Class exists but method not found"); + + return new ClassificationResult("deferred", "Class not found in .NET source"); + } +}