Skip to content

Go Language Integration ​

Architecture and implementation details for Go support in n-dx.


Overview ​

Go is the first non-JavaScript/TypeScript language supported by n-dx. The integration spans all three core packages (SourceVision, Rex, Hench) and enables the full ndx init → analyze → plan → work workflow on Go codebases.

The implementation follows a key design principle: Go support extends existing systems through configuration and dispatch, not duplication. The zone detection algorithm, PRD management, and agent execution loop are unchanged. Language-specific behavior is isolated to parsers, classifiers, and guard templates.


File Inventory ​

Go files are classified using the language registry (packages/lang-kit/src/go.ts).

File Role Classification ​

PatternRoleExample
*.go (not test/generated)sourceinternal/handler/user.go
*_test.gotestinternal/handler/user_test.go
*_gen.go, *.pb.go, wire_gen.go, mock_*.gogeneratedproto/api.pb.go
go.mod, go.sum, .golangci.yml, Makefileconfiggo.mod
testdata/ contentsassettestdata/users.json

Skip Directories ​

Go projects skip vendor/ (equivalent to node_modules/). Standard Go directories like internal/, cmd/, and pkg/ are not skipped — they contain source code.


Import Graph ​

Files: packages/lang-go/src/go-pack.ts and go-resolver.ts (the Go language pack, @n-dx/lang-go), packages/sourcevision/src/language/pack-loader.ts (how sourcevision finds it), packages/sourcevision/src/analyzers/go-imports.ts (the regex fallback)

Parser Design ​

Go imports come from the Go language pack, @n-dx/lang-go, which installs by default as an optional dependency of @n-dx/sourcevision and is loaded at run time: a tree-sitter grammar built to WASM, the queries under its queries/go/, and a resolver that reads every go.mod, go.work and vendor/modules.txt in the project. Neither needs a Go toolchain. Where the pack's output differs from the regex parser's, the differences are deliberate and listed in the header of go-resolver.ts: nested modules resolve against their own go.mod, go.work modules and local replaces are internal, and a required module nested under the project's path is third-party. Both parsers keep an import of a directory holding no inventoried .go file as an external package rather than an edge to a node that does not exist; the pack attributes it to its require, the regex parser to no module.

When the pack is not installed or cannot run — the grammar is not built or will not load, a query does not compile, or the build files cannot be read — the imports phase reports it once and takes Go edges from the regex parser instead, so a Go repository never drops to inventory-only. Both parsers handle all import syntax variants:

VariantSyntaxExample
Singleimport "pkg"import "fmt"
Groupedimport ( ... )import ( "fmt"; "net/http" )
Aliasedimport alias "pkg"import chi "github.com/go-chi/chi/v5"
Blankimport _ "pkg"import _ "github.com/lib/pq"
Dotimport . "pkg"import . "testing"

The regex parser strips comments (// and /* */) before extraction and ignores import-like text inside string literals.

Import Classification ​

Every import is classified into one of three categories. The table gives the regex parser's rules, which use the root go.mod only; the pack applies the same categories per module, as described above.

CategoryRuleEdge TypeExample
InternalStarts with module pathImportEdge (file → directory)github.com/myapp/internal/handler → edge to internal/handler
StdlibNo dot in first path segmentExternalImport with stdlib: prefixfmt → stdlib:fmt
Third-partyHas dot, doesn't start with module pathExternalImportgithub.com/go-chi/chi/v5

File-to-Package Edge Semantics ​

This is the most important architectural difference from JS/TS:

JS/TS:  src/handler/user.ts  →  src/service/user-service.ts     (file → file)
Go:     handler/user.go      →  internal/service                  (file → directory)

Go imports target packages (directories), not files. A single import "module/internal/service" imports all exported symbols from all .go files in that directory. The edge target in imports.json is a relative directory path.

Consequences:

  • Zone detection operates at package granularity for Go (vs file granularity for JS/TS)
  • Files within the same Go package always cluster together in one zone
  • All imports carry ImportType: "static" (Go has no dynamic imports)
  • Symbols are recorded as ["*"] (Go imports entire packages)

Module Path Resolution ​

The go.mod file is read once at the start of analysis to extract the module path. For each import:

  1. Check if the import path starts with the module path
  2. If yes → strip the module path prefix to get the relative directory
  3. If no → classify as stdlib or third-party

Example with module github.com/example/myapp:

Import: "github.com/example/myapp/internal/handler"
Strip:  "github.com/example/myapp/"
Result: edge to "internal/handler"

Zone Detection ​

Go projects feed into the same Louvain community detection algorithm as JS/TS. The algorithm is language-agnostic — it operates on graph nodes (files) and edges (imports).

Edge Resolution ​

Go import edges target directories, but zone file maps contain individual file paths. The resolveEdgeTarget() function bridges this gap by expanding directory targets to all files in that directory:

Edge: handler/user.go → internal/service
Resolves to: handler/user.go → [internal/service/user.go, internal/service/order.go]

This enables accurate zone crossing computation and coupling/cohesion metrics.

Zone Naming ​

Go's package structure maps naturally to zone names. The zone naming algorithm skips generic segments (internal, pkg, src, lib):

File PathZone NameReasoning
internal/handler/user.goHandlerinternal skipped → handler
internal/service/user.goServiceinternal skipped → service
pkg/response/json.goResponsepkg skipped → response
cmd/api/main.goAPIcmd preserved → clustering decides

Real-World Validation ​

Tested against PocketBase (843 files):

MetricValue
Import edges756
External packages125
Zones26
Zone crossings49
Hub detectioncore imported by 5 zones
Bidirectional couplinginfrastructure-tools ↔ s3-blob-driver flagged

Route Detection ​

File: packages/sourcevision/src/analyzers/go-route-detection.ts

Regex-based detection of HTTP route registrations across 6 Go frameworks:

FrameworkPatternExample
net/httphttp.HandleFunc("/path", handler)Stdlib
chir.Get("/path", handler)Lowercase methods
ginr.GET("/path", handler)Uppercase methods
echoe.GET("/path", handler)Uppercase methods
fiberapp.Get("/path", handler)Mixed case
gorilla/muxr.HandleFunc("/path").Methods("GET")Method chaining

The detector strips Go comments before matching, preserves path parameters as-is ({id}, :id), and groups routes by common prefix. Output uses the existing ServerRoute and ServerRouteGroup types — no schema changes required.


Archetype Classification ​

Go-specific archetype signals are scoped using the languages: ["go"] field:

ArchetypeGo Signals
entrypointmain.go (0.9), /cmd/ directory (0.7)
typestypes.go (0.9), models.go (0.8), entities.go (0.8)
route-handlerhandler.go (0.8), /handler/ (0.8), /handlers/ (0.8)
test-helper/testdata/ (0.9), /testutil/ (0.8)

Frontend-specific archetypes (component, hook, page, route-module) are scoped to ["typescript", "javascript"] and do not fire in Go projects.


Rex Scanner Support ​

File: packages/rex/src/analyze/scanners.ts

go.mod Parsing ​

parseGoMod() extracts structured data from go.mod:

  • Module path
  • Go version requirement
  • Dependencies (from require block)
  • Replace directives

scanGoMod() produces Rex proposals from this data (e.g., dependency update tasks, Go version upgrade tasks).

Go-Aware Scanning ​

  • vendor/ added to skip directories
  • go.mod and go.sum added to skip config files (not scanned for doc content)
  • _test.go recognized by isTestFile()

Hench Agent Support ​

Guard Configuration ​

typescript
const GO_GUARD_DEFAULTS = {
  // Shared by every language: n-dx state, .git, every ecosystem's
  // dependency directories (vendor/**, node_modules/**, …), credential files.
  blockedPaths: guardBaselineForLanguage("go").blockedPaths,
  allowedCommands: ["go", "make", "git", "golangci-lint"],
  commandTimeout: 30000,
  maxFileSize: 1048576,
};

Selected via guardDefaultsForLanguage("go") when the manifest language is Go.

Test Runner ​

Go test detection and execution:

  • Pattern: /_test\.go$/
  • Runner: go test ./... (all packages) or go test ./internal/handler/... (scoped)
  • Specific test: go test -run TestName ./pkg/...

Language Context ​

buildGoLanguageContext() provides Go-specific guidance to the agent brief:

  • Go testing conventions (go test, table-driven tests, testdata/)
  • Go build commands (go build, go vet, golangci-lint run)
  • Go naming conventions (exported = PascalCase, unexported = camelCase)
  • Go project structure conventions (cmd/, internal/, pkg/)

Test Fixture ​

Location: packages/sourcevision/tests/fixtures/go-project/

A minimal Go project modeling a layered HTTP API:

go-project/
  go.mod                           # Module: github.com/example/go-project
  cmd/api/
    main.go                        # Entry point
  internal/
    handler/user.go                # HTTP handlers (chi routes)
    handler/user_test.go           # Handler tests
    service/user.go                # Business logic
    service/user_test.go           # Service tests
    repository/user.go             # Data access
    repository/user_test.go        # Repository tests
    middleware/auth.go             # Auth middleware
    middleware/logging.go          # Logging middleware
    config/config.go              # Configuration loading
  pkg/response/json.go            # Shared response utilities
  testdata/users.json             # Test fixtures

Exercises: entry point detection, standard Go layout, all archetype categories, test/generated file classification, import graph construction, route detection, and zone boundary formation.

A mixed-language fixture (tests/fixtures/mixed-go-ts/) also exists for multi-language detection testing.


Known Limitations ​

Parser Limitations ​

  • Regex-based — no full AST. Build-tag-gated imports (//go:build linux) are extracted regardless of platform. CGo imports (import "C") are classified as stdlib.
  • No symbol extraction — all imports record symbols: ["*"] because Go imports entire packages. This is accurate to Go semantics but means the import graph cannot distinguish which specific exports are used.

Zone Detection Limitations ​

  • Package-level granularity — zones align with Go packages. A 50-file package appears as a single cluster. Sub-package splitting requires function-level call graph analysis (Phase 4).
  • Intra-package cohesion — Go files in the same package never import each other, so cohesion is computed from shared outgoing edges and proximity, not internal edges.

Hench Limitations ​

  • Guard template is manual — the go-project template must be selected during ndx init or configured in .hench/config.json. Auto-detection from go.mod during hench run is not implemented.
  • No Makefile target scanning — make is in the allowed commands but Hench doesn't discover available targets from Makefile.

Not Implemented (Phase 4) ​

  • Function-level call graph via go/ast or gopls
  • Interface and struct cataloging
  • Go-specific findings (large interfaces, package naming violations, stuttering)
  • go.work multi-module workspace support

Released under the Elastic License 2.0.