- Update all 7 phase docs with source/target location references (golang/ for Go source, dotnet/ for .NET version) - Rename NATS.Server to ZB.MOM.NatsNet.Server in phase 4-7 docs - Update solution layout to dotnet/src/ and dotnet/tests/ structure - Create CLAUDE.md with project summary and phase links - Update .gitignore: track porting.db, add standard .NET patterns - Add golang/nats-server as git submodule - Add reports/generate-report.sh and pre-commit hook - Add documentation_rules.md to version control
9.2 KiB
Phase 6: Initial Porting
Port Go code to .NET 10 C#, working through the dependency graph bottom-up. This is the main implementation phase where the actual code is written.
Objective
Implement every non-N/A module, feature, and test in the porting database. Work from leaf nodes (items with no unported dependencies) upward through the dependency graph. Keep the database current as work progresses.
Prerequisites
- Phase 5 complete: all mappings verified, no collisions, naming validated
- .NET solution structure created:
dotnet/src/ZB.MOM.NatsNet.Server/ZB.MOM.NatsNet.Server.csprojdotnet/src/ZB.MOM.NatsNet.Server.Host/ZB.MOM.NatsNet.Server.Host.csprojdotnet/tests/ZB.MOM.NatsNet.Server.Tests/ZB.MOM.NatsNet.Server.Tests.csprojdotnet/tests/ZB.MOM.NatsNet.Server.IntegrationTests/ZB.MOM.NatsNet.Server.IntegrationTests.csproj
- Library dependencies (NuGet packages) added per Phase 3 mappings
- Verify readiness:
dotnet run --project tools/NatsNet.PortTracker -- phase check 5 --db porting.db
Source and Target Locations
- Go source code is located in the
golang/folder (specificallygolang/nats-server/) - .NET ported version is located in the
dotnet/folder
Porting Workflow
This is the core loop. Repeat until all items are complete.
Step 1: Find ready items
Query for items whose dependencies are all ported (status complete, verified, or n_a):
dotnet run --project tools/NatsNet.PortTracker -- dependency ready --db porting.db
This returns modules and features that have no unported dependencies. Start with these.
Step 2: Pick an item and mark as stub
Choose an item from the ready list. Mark it as stub to signal work has begun:
dotnet run --project tools/NatsNet.PortTracker -- feature update <id> --status stub --db porting.db
Step 3: Create the skeleton
In the .NET project, create the class and method skeleton based on the mapping:
- Look up the mapping:
dotnet run --project tools/NatsNet.PortTracker -- feature show <id> --db porting.db - Create the file at the correct path under
dotnet/src/ZB.MOM.NatsNet.Server/following the namespace hierarchy - Add the class declaration, method signature, and a
throw new NotImplementedException()body
For batch scaffolding of an entire module:
dotnet run --project tools/NatsNet.PortTracker -- feature update 0 --status stub \
--all-in-module <module_id> --db porting.db
Step 4: Implement the logic
Reference the Go source code. The database stores the Go file path and line number for each feature:
dotnet run --project tools/NatsNet.PortTracker -- feature show <id> --db porting.db
The output includes Go File, Go Line, and Go LOC fields. Open the Go source at those coordinates and translate the logic to C#.
Key translation patterns:
| Go pattern | .NET equivalent |
|---|---|
goroutine + channel |
Task + Channel<T> or async/await |
sync.Mutex |
lock statement or SemaphoreSlim |
sync.RWMutex |
ReaderWriterLockSlim |
sync.WaitGroup |
Task.WhenAll or CountdownEvent |
defer |
try/finally or using/IDisposable |
interface{} / any |
object or generics |
[]byte |
byte[], ReadOnlySpan<byte>, or ReadOnlyMemory<byte> |
map[K]V |
Dictionary<K,V> or ConcurrentDictionary<K,V> |
error return |
Exceptions or Result<T> pattern |
panic/recover |
Exceptions (avoid Environment.FailFast for recoverable cases) |
select on channels |
Task.WhenAny or Channel<T> reader patterns |
context.Context |
CancellationToken |
io.Reader/Writer |
Stream, PipeReader/PipeWriter |
Step 5: Mark complete
Once the implementation compiles and the basic logic is in place:
dotnet run --project tools/NatsNet.PortTracker -- feature update <id> --status complete --db porting.db
Step 6: Run targeted tests
If tests exist for this feature, run them:
dotnet test --filter "FullyQualifiedName~ZB.MOM.NatsNet.Server.Tests.Protocol" \
dotnet/tests/ZB.MOM.NatsNet.Server.Tests/
Fix any failures before moving on.
Step 7: Check what is now unblocked
Completing items may unblock others that depend on them:
dotnet run --project tools/NatsNet.PortTracker -- dependency ready --db porting.db
Return to Step 2 with the newly available items.
DB Update Discipline
The porting database must stay current. Update status at every transition:
# Starting work on a feature
dotnet run --project tools/NatsNet.PortTracker -- feature update 42 --status stub --db porting.db
# Feature implemented
dotnet run --project tools/NatsNet.PortTracker -- feature update 42 --status complete --db porting.db
# Batch scaffolding for all features in a module
dotnet run --project tools/NatsNet.PortTracker -- feature update 0 --status stub \
--all-in-module 3 --db porting.db
# Module fully ported (all its features are complete)
dotnet run --project tools/NatsNet.PortTracker -- module update 3 --status complete --db porting.db
# Check progress
dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db
Status transitions follow this progression:
not_started -> stub -> complete -> verified (Phase 7)
\-> n_a (if determined during porting)
Never skip stub -- it signals that work is in progress and prevents duplicate effort.
Porting Order Strategy
Start with leaf modules
Leaf modules have no dependencies on other unported modules. They are safe to port first because nothing they call is missing.
# These are the leaves — port them first
dotnet run --project tools/NatsNet.PortTracker -- dependency ready --db porting.db
Typical leaf modules include:
- Utility/helper code (string manipulation, byte buffer pools)
- Constants and enums
- Configuration types (options, settings)
- Error types and codes
Then work upward
After leaves are done, modules that depended only on those leaves become ready. Continue up the dependency graph:
Leaf utilities -> Protocol types -> Parser -> Connection handler -> Server
Port tests alongside features
When porting a feature, also port its associated tests in the same pass. This provides immediate validation:
# List tests for a feature
dotnet run --project tools/NatsNet.PortTracker -- test list --module <module_id> --db porting.db
# After porting a test
dotnet run --project tools/NatsNet.PortTracker -- test update <id> --status complete --db porting.db
Progress Tracking
Check overall progress regularly:
# Summary stats
dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db
# What is still blocked
dotnet run --project tools/NatsNet.PortTracker -- dependency blocked --db porting.db
# Phase-level check
dotnet run --project tools/NatsNet.PortTracker -- phase check 6 --db porting.db
Handling Discoveries During Porting
During implementation, you may find:
Items that should be N/A
If a feature turns out to be unnecessary in .NET (discovered during implementation):
dotnet run --project tools/NatsNet.PortTracker -- feature set-na <id> \
--reason "Go-specific memory management, handled by .NET GC" --db porting.db
Missing dependencies
If the Go analyzer missed a dependency:
# The dependency is tracked in the DB via the dependencies table
# For now, just ensure the target is ported before continuing
dotnet run --project tools/NatsNet.PortTracker -- dependency show feature <id> --db porting.db
Design changes
If the .NET design needs to differ from the original mapping (e.g., splitting a large Go function into multiple .NET methods), update the mapping:
dotnet run --project tools/NatsNet.PortTracker -- feature map <id> \
--class "NewClassName" \
--method "NewMethodName" \
--db porting.db
Tips
- Keep the build green. The solution should compile after each feature is completed. Do not leave unresolved references.
- Write idiomatic C#. Do not transliterate Go line-by-line. Use .NET patterns (async/await, LINQ, Span, dependency injection) where they produce cleaner code.
- Use
CancellationTokeneverywhere. The Go code usescontext.Contextpervasively -- mirror this withCancellationTokenparameters. - Prefer
ReadOnlySpan<byte>for hot paths. The NATS parser processes bytes at high throughput. Use spans and avoid allocations in the critical path. - Do not port Go comments verbatim. Translate the intent into C# XML doc comments where appropriate.
- Run
dotnet buildfrequently. Catch compile errors early rather than accumulating them.
Completion Criteria
- All non-N/A modules have status
completeor better - All non-N/A features have status
completeor better - All non-N/A tests have status
completeor better - The solution compiles without errors:
dotnet build dependency blockedreturns no items (or only items waiting for Phase 7 verification)report summaryshows the expected completion counts
Related Documentation
- Phase 5: Mapping Verification -- the verified mappings this phase implements
- Phase 7: Porting Verification -- targeted testing of the ported code