# AGENTS.md

## Project Summary

This project ports the NATS messaging server from Go to .NET 10 C#. The Go source (~130K LOC) is the reference at `golang/nats-server/`. Porting progress is tracked in an SQLite database (`porting.db`) managed by the PortTracker CLI tool.

## Folder Layout

```
natsnet/
├── golang/nats-server/                  # Go source (read-only reference)
├── dotnet/
│   ├── src/ZB.MOM.NatsNet.Server/       # Main server library
│   ├── src/ZB.MOM.NatsNet.Server.Host/  # Host entry point
│   └── tests/
│       ├── ZB.MOM.NatsNet.Server.Tests/              # Unit tests
│       └── ZB.MOM.NatsNet.Server.IntegrationTests/   # Integration tests
├── tools/NatsNet.PortTracker/           # CLI tracking tool
├── docs/standards/dotnet-standards.md   # .NET coding standards (MUST follow)
├── docs/plans/phases/                   # Phase instruction guides
├── reports/current.md                   # Latest porting status
├── porting.db                           # SQLite tracking database
└── porting-schema.sql                   # Database schema
```

## Build and Test

```bash
# Build the solution
dotnet build dotnet/

# Run all unit tests
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.Tests/

# Run filtered tests (by namespace/class)
dotnet test --filter "FullyQualifiedName~ZB.MOM.NatsNet.Server.Tests.Protocol" \
  dotnet/tests/ZB.MOM.NatsNet.Server.Tests/

# Run integration tests
dotnet test dotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/

# Generate porting report
./reports/generate-report.sh
```

## .NET Coding Standards

**MUST follow all rules in `docs/standards/dotnet-standards.md`.**

Critical rules (non-negotiable):

- .NET 10, C# latest, nullable enabled
- **xUnit 3** + **Shouldly** + **NSubstitute** for testing
- **NEVER use FluentAssertions or Moq** — these are forbidden
- PascalCase for public members, `_camelCase` for private fields
- File-scoped namespaces: `ZB.MOM.NatsNet.Server.[Module]`
- Use `CancellationToken` on all async signatures
- Use `ReadOnlySpan<byte>` on hot paths
- Test naming: `[Method]_[Scenario]_[Expected]`
- Test class naming: `[ClassName]Tests`
- Structured logging with `ILogger<T>` and `LogContext.PushProperty`

## PortTracker CLI

All tracking commands use this base:

```bash
dotnet run --project tools/NatsNet.PortTracker -- <command> --db porting.db
```

### Querying

| Command | Purpose |
|---------|---------|
| `report summary` | Show overall porting progress |
| `dependency ready` | List items ready to port (no unported deps) |
| `dependency blocked` | List items blocked by unported deps |
| `feature list --status <s>` | List features by status |
| `feature list --module <id>` | List features in a module |
| `feature show <id>` | Show feature details (Go source path, .NET target) |
| `test list --status <s>` | List tests by status |
| `test show <id>` | Show test details |
| `module list` | List all modules |
| `module show <id>` | Show module with its features and tests |

### Updating Status

| Command | Purpose |
|---------|---------|
| `feature update <id> --status <s>` | Update one feature |
| `feature batch-update --ids "1-10" --set-status <s> --execute` | Bulk update features |
| `test update <id> --status <s>` | Update one test |
| `test batch-update --ids "1-10" --set-status <s> --execute` | Bulk update tests |
| `module update <id> --status <s>` | Update module status |

### Audit Verification

Status updates are verified against Roslyn audit results. If the audit disagrees with your requested status, add `--override "reason"` to force it.

```bash
feature update 42 --status verified --override "manually verified logic"
```

### Audit Commands

| Command | Purpose |
|---------|---------|
| `audit --type features` | Dry-run audit of features against .NET source |
| `audit --type tests` | Dry-run audit of tests against test project |
| `audit --type features --execute` | Apply audit classifications to DB |
| `audit --type tests --execute` | Apply test audit classifications to DB |

### Valid Statuses

```
not_started → stub → complete → verified
                 └→ n_a (not applicable)
                 └→ deferred (blocked, needs server infra)
```

### Batch ID Syntax

`--ids` accepts: ranges `"100-200"`, lists `"1,5,10"`, or mixed `"1-5,10,20-25"`.

All batch commands default to dry-run. Add `--execute` to apply.

## Porting Workflow

### Finding Work

1. Query for features ready to port:

```bash
dotnet run --project tools/NatsNet.PortTracker -- dependency ready --db porting.db
```

2. Or find deferred/stub features in a specific module:

```bash
dotnet run --project tools/NatsNet.PortTracker -- feature list --module <id> --status deferred --db porting.db
```

3. To find tests that need implementing:

```bash
dotnet run --project tools/NatsNet.PortTracker -- test list --status stub --db porting.db
dotnet run --project tools/NatsNet.PortTracker -- test list --status deferred --db porting.db
```

### Implementing a Feature

1. **Claim it** — mark as stub before starting:

```bash
dotnet run --project tools/NatsNet.PortTracker -- feature update <id> --status stub --db porting.db
```

2. **Read the Go source** — use `feature show <id>` to get the Go file path and line numbers, then read the Go implementation.

3. **Write idiomatic C#** — translate intent, not lines:
   - Use `async`/`await`, not goroutine translations
   - Use `Channel<T>` for Go channels
   - Use `CancellationToken` for `context.Context`
   - Use `ReadOnlySpan<byte>` on hot paths
   - Use `Lock` (C# 13) for `sync.Mutex`
   - Use `ReaderWriterLockSlim` for `sync.RWMutex`

4. **Ensure it compiles** — run `dotnet build dotnet/`

5. **Mark complete**:

```bash
dotnet run --project tools/NatsNet.PortTracker -- feature update <id> --status complete --db porting.db
```

### Implementing a Unit Test

1. **Read the Go test** — use `test show <id>` to get Go source location.
2. **Read the corresponding .NET feature** to understand the API surface.
3. **Write the test** in `dotnet/tests/ZB.MOM.NatsNet.Server.Tests/` using xUnit 3 + Shouldly + NSubstitute.
4. **Run it**:

```bash
dotnet test --filter "FullyQualifiedName~TestClassName" \
  dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
```

5. **Mark verified** (if passing):

```bash
dotnet run --project tools/NatsNet.PortTracker -- test update <id> --status verified --db porting.db
```

### After Completing Work

1. Run affected tests to verify nothing broke.
2. Update DB status for all items you changed.
3. Check what's newly unblocked:

```bash
dotnet run --project tools/NatsNet.PortTracker -- dependency ready --db porting.db
```

4. Generate updated report:

```bash
./reports/generate-report.sh
```

## Go to .NET Translation Reference

| Go Pattern | .NET Equivalent |
|------------|-----------------|
| `goroutine` | `Task.Run` or `async`/`await` |
| `chan T` | `Channel<T>` |
| `select` | `Task.WhenAny` |
| `sync.Mutex` | `Lock` (C# 13) |
| `sync.RWMutex` | `ReaderWriterLockSlim` |
| `sync.WaitGroup` | `Task.WhenAll` or `CountdownEvent` |
| `atomic.Int64` | `Interlocked` methods on `long` field |
| `context.Context` | `CancellationToken` |
| `defer` | `try`/`finally` or `using` |
| `error` return | Exceptions or Result pattern |
| `[]byte` | `byte[]`, `ReadOnlySpan<byte>`, `ReadOnlyMemory<byte>` |
| `map[K]V` | `Dictionary<K,V>` or `ConcurrentDictionary<K,V>` |
| `interface{}` | `object` or generics |
| `time.Duration` | `TimeSpan` |
| `weak.Pointer[T]` | `WeakReference<T>` |
