diff --git a/reports/current.md b/reports/current.md
index e0a6d28..ea1a213 100644
--- a/reports/current.md
+++ b/reports/current.md
@@ -1,6 +1,6 @@
# NATS .NET Porting Status Report
-Generated: 2026-02-27 10:16:08 UTC
+Generated: 2026-02-27 10:16:59 UTC
## Modules (12 total)
diff --git a/reports/report_c5c6fbc.md b/reports/report_c5c6fbc.md
new file mode 100644
index 0000000..ea1a213
--- /dev/null
+++ b/reports/report_c5c6fbc.md
@@ -0,0 +1,35 @@
+# NATS .NET Porting Status Report
+
+Generated: 2026-02-27 10:16:59 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/SourceIndexer.cs b/tools/NatsNet.PortTracker/Audit/SourceIndexer.cs
new file mode 100644
index 0000000..85b6443
--- /dev/null
+++ b/tools/NatsNet.PortTracker/Audit/SourceIndexer.cs
@@ -0,0 +1,201 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace NatsNet.PortTracker.Audit;
+
+///
+/// Parses .cs files using Roslyn syntax trees and builds a lookup index
+/// of (className, memberName) -> list of MethodInfo.
+///
+public sealed class SourceIndexer
+{
+ public record MethodInfo(
+ string FilePath,
+ int LineNumber,
+ int BodyLineCount,
+ bool IsStub,
+ bool IsPartial,
+ int StatementCount);
+
+ // Key: (className lowercase, memberName lowercase)
+ private readonly Dictionary<(string, string), List> _index = new();
+
+ public int FilesIndexed { get; private set; }
+ public int MethodsIndexed { get; private set; }
+
+ ///
+ /// Recursively parses all .cs files under
+ /// (skipping obj/ and bin/) and populates the index.
+ ///
+ public void IndexDirectory(string sourceDir)
+ {
+ var files = Directory.EnumerateFiles(sourceDir, "*.cs", SearchOption.AllDirectories)
+ .Where(f =>
+ {
+ var rel = Path.GetRelativePath(sourceDir, f);
+ return !rel.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}")
+ && !rel.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}")
+ && !rel.StartsWith($"obj{Path.DirectorySeparatorChar}")
+ && !rel.StartsWith($"bin{Path.DirectorySeparatorChar}");
+ });
+
+ foreach (var file in files)
+ {
+ IndexFile(file);
+ FilesIndexed++;
+ }
+ }
+
+ ///
+ /// Looks up all method declarations for a given class and member name.
+ /// Case-insensitive. Returns empty list if not found.
+ ///
+ public List Lookup(string className, string memberName)
+ {
+ var key = (className.ToLowerInvariant(), memberName.ToLowerInvariant());
+ return _index.TryGetValue(key, out var list) ? list : [];
+ }
+
+ ///
+ /// Returns true if the class exists anywhere in the index (any member).
+ ///
+ public bool HasClass(string className)
+ {
+ var lower = className.ToLowerInvariant();
+ return _index.Keys.Any(k => k.Item1 == lower);
+ }
+
+ private void IndexFile(string filePath)
+ {
+ var source = File.ReadAllText(filePath);
+ var tree = CSharpSyntaxTree.ParseText(source, path: filePath);
+ var root = tree.GetCompilationUnitRoot();
+
+ foreach (var typeDecl in root.DescendantNodes().OfType())
+ {
+ var className = typeDecl.Identifier.Text.ToLowerInvariant();
+
+ // Methods
+ foreach (var method in typeDecl.Members.OfType())
+ {
+ var info = AnalyzeMethod(filePath, method.Body, method.ExpressionBody, method.GetLocation());
+ AddToIndex(className, method.Identifier.Text.ToLowerInvariant(), info);
+ }
+
+ // Properties (get/set are like methods)
+ foreach (var prop in typeDecl.Members.OfType())
+ {
+ var info = AnalyzeProperty(filePath, prop);
+ AddToIndex(className, prop.Identifier.Text.ToLowerInvariant(), info);
+ }
+
+ // Constructors — index as class name
+ foreach (var ctor in typeDecl.Members.OfType())
+ {
+ var info = AnalyzeMethod(filePath, ctor.Body, ctor.ExpressionBody, ctor.GetLocation());
+ AddToIndex(className, ctor.Identifier.Text.ToLowerInvariant(), info);
+ }
+ }
+ }
+
+ private MethodInfo AnalyzeMethod(string filePath, BlockSyntax? body, ArrowExpressionClauseSyntax? expressionBody, Location location)
+ {
+ var lineSpan = location.GetLineSpan();
+ var lineNumber = lineSpan.StartLinePosition.Line + 1;
+
+ if (expressionBody is not null)
+ {
+ // Expression-bodied: => expr;
+ var isStub = IsNotImplementedExpression(expressionBody.Expression);
+ return new MethodInfo(filePath, lineNumber, 1, IsStub: isStub, IsPartial: false, StatementCount: isStub ? 0 : 1);
+ }
+
+ if (body is null || body.Statements.Count == 0)
+ {
+ // No body or empty body
+ return new MethodInfo(filePath, lineNumber, 0, IsStub: true, IsPartial: false, StatementCount: 0);
+ }
+
+ var bodyLines = body.GetLocation().GetLineSpan();
+ var bodyLineCount = bodyLines.EndLinePosition.Line - bodyLines.StartLinePosition.Line - 1; // exclude braces
+
+ var statements = body.Statements;
+ var hasNotImplemented = statements.Any(s => IsNotImplementedStatement(s));
+ var meaningfulCount = statements.Count(s => !IsNotImplementedStatement(s));
+
+ // Pure stub: single throw NotImplementedException
+ if (statements.Count == 1 && hasNotImplemented)
+ return new MethodInfo(filePath, lineNumber, bodyLineCount, IsStub: true, IsPartial: false, StatementCount: 0);
+
+ // Partial: has some logic AND a NotImplementedException
+ if (hasNotImplemented && meaningfulCount > 0)
+ return new MethodInfo(filePath, lineNumber, bodyLineCount, IsStub: false, IsPartial: true, StatementCount: meaningfulCount);
+
+ // Real logic
+ return new MethodInfo(filePath, lineNumber, bodyLineCount, IsStub: false, IsPartial: false, StatementCount: meaningfulCount);
+ }
+
+ private MethodInfo AnalyzeProperty(string filePath, PropertyDeclarationSyntax prop)
+ {
+ var lineSpan = prop.GetLocation().GetLineSpan();
+ var lineNumber = lineSpan.StartLinePosition.Line + 1;
+
+ // Expression-bodied property: int Foo => expr;
+ if (prop.ExpressionBody is not null)
+ {
+ var isStub = IsNotImplementedExpression(prop.ExpressionBody.Expression);
+ return new MethodInfo(filePath, lineNumber, 1, IsStub: isStub, IsPartial: false, StatementCount: isStub ? 0 : 1);
+ }
+
+ // Auto-property: int Foo { get; set; } — this is valid, not a stub
+ if (prop.AccessorList is not null && prop.AccessorList.Accessors.All(a => a.Body is null && a.ExpressionBody is null))
+ return new MethodInfo(filePath, lineNumber, 0, IsStub: false, IsPartial: false, StatementCount: 1);
+
+ // Property with accessor bodies — check if any are stubs
+ if (prop.AccessorList is not null)
+ {
+ var hasStub = prop.AccessorList.Accessors.Any(a =>
+ (a.ExpressionBody is not null && IsNotImplementedExpression(a.ExpressionBody.Expression)) ||
+ (a.Body is not null && a.Body.Statements.Count == 1 && IsNotImplementedStatement(a.Body.Statements[0])));
+ return new MethodInfo(filePath, lineNumber, 0, IsStub: hasStub, IsPartial: false, StatementCount: hasStub ? 0 : 1);
+ }
+
+ return new MethodInfo(filePath, lineNumber, 0, IsStub: false, IsPartial: false, StatementCount: 1);
+ }
+
+ private static bool IsNotImplementedExpression(ExpressionSyntax expr)
+ {
+ // throw new NotImplementedException(...)
+ if (expr is ThrowExpressionSyntax throwExpr)
+ return throwExpr.Expression is ObjectCreationExpressionSyntax oc
+ && oc.Type.ToString().Contains("NotImplementedException");
+ // new NotImplementedException() — shouldn't normally be standalone but handle it
+ return expr is ObjectCreationExpressionSyntax oc2
+ && oc2.Type.ToString().Contains("NotImplementedException");
+ }
+
+ private static bool IsNotImplementedStatement(StatementSyntax stmt)
+ {
+ // throw new NotImplementedException(...);
+ if (stmt is ThrowStatementSyntax throwStmt && throwStmt.Expression is not null)
+ return throwStmt.Expression is ObjectCreationExpressionSyntax oc
+ && oc.Type.ToString().Contains("NotImplementedException");
+ // Expression statement containing throw expression
+ if (stmt is ExpressionStatementSyntax exprStmt)
+ return IsNotImplementedExpression(exprStmt.Expression);
+ return false;
+ }
+
+ private void AddToIndex(string className, string memberName, MethodInfo info)
+ {
+ var key = (className, memberName);
+ if (!_index.TryGetValue(key, out var list))
+ {
+ list = [];
+ _index[key] = list;
+ }
+ list.Add(info);
+ MethodsIndexed++;
+ }
+}