# Go Plugin Usage Guide ## Overview The ArchGuard Go plugin provides architecture analysis for Go projects with two-tier interface detection: 1. **Primary**: gopls semantic analysis (95%+ accuracy) 2. **Fallback**: Name-based structural matching (75%+ accuracy) The plugin **automatically degrades gracefully** when gopls is unavailable, ensuring reliable operation in all environments. ## Prerequisites ### Required - Node.js >= 18 - tree-sitter-go (installed automatically) ### Optional (Enhanced Accuracy) - **gopls** - Go language server for semantic analysis - **Go toolchain** - Required to install gopls **Install gopls**: ```bash # Install gopls go install golang.org/x/tools/gopls@latest # Verify installation which gopls gopls version ``` **Without gopls**: Plugin works perfectly, using name-based matching (same as Phase 2.A baseline). ## Reliability: gopls Timeout Budget & Graceful Degradation gopls startup and workspace analysis on a very large Go module can stall indefinitely (first-run module resolution, broken toolchains, huge dependency graphs). To guarantee `archguard analyze --lang go` never hangs the whole run, every gopls-dependent stage is bounded by a **timeout budget**: - The budget covers gopls startup + workspace load, including the `gopls version` probe and the LSP `initialize` handshake (both previously unbounded). - When the budget is exceeded, ArchGuard **cancels the gopls operation, reaps the gopls child process, emits a loud warning, and continues with tree-sitter-only analysis** (package/struct/interface relations without the gopls call graph). Analysis always completes — it is never stalled by gopls. - Degraded output is **explicitly marked**: the ArchJSON carries `metadata.goGoplsDegraded: true` and `metadata.goGoplsDegradedReason`, and `metadata.goGoplsAvailable` reports whether gopls contributed. - After a timeout, gopls is **poison-pill disabled for the rest of the process** — it is never re-spawned within the same CLI/MCP process, so a known-bad gopls cannot reintroduce the stall. The poisoned state is surfaced in the gopls diagnostics. ### Configuring the budget | Mechanism | Key | Default | |-----------|-----|---------| | Environment variable (takes precedence) | `ARCHGUARD_GOPLS_TIMEOUT_MS` | — | | Resolved config (any form — `archguard.config.json`, `--config `, programmatic) | `atlas.goplsTimeoutMs` | `120000` (120s) | **Precedence**: `ARCHGUARD_GOPLS_TIMEOUT_MS` (env) > `atlas.goplsTimeoutMs` (resolved config) > `120000` (default). The `atlas.goplsTimeoutMs` value is consumed from the resolved configuration that ArchGuard loads — it honours `archguard.config.json` at cwd, a custom `--config `, programmatic configs, and `archguard.config.js`. No environment variable workaround is needed for custom config paths. ```bash # Give gopls 3 minutes on a very large module: ARCHGUARD_GOPLS_TIMEOUT_MS=180000 archguard analyze --lang go -s ./my-project # Or fail fast in CI (degrade to tree-sitter after 5s): ARCHGUARD_GOPLS_TIMEOUT_MS=5000 archguard analyze --lang go -s ./my-project ``` ```json // archguard.config.json { "atlas": { "goplsTimeoutMs": 180000 } } ``` Invalid or non-positive values fall through to the next source in the precedence chain (env → config file → 120s default), so a malformed override can never disable the bound. ### Degraded output example ```json { "version": "1.1", "language": "go", "metadata": { "goGoplsAvailable": false, "goGoplsDegraded": true, "goGoplsDegradedReason": "gopls timed out (gopls startup exceeded budget of 120000ms)" } } ``` ## Usage ### Basic Usage ```bash # Build ArchGuard npm run build # Analyze Go project (auto-detects gopls) node dist/cli/index.js analyze -s ./your-go-project ``` ### Expected Output **With gopls available**: ``` ✓ gopls detected - using semantic analysis ✓ Parsing Go files... ✓ Interface detection: gopls + fallback ✓ Generated architecture diagram ``` **Without gopls** (graceful degradation): ``` ⚠ gopls not available - using fallback matcher ✓ Parsing Go files... ✓ Interface detection: name-based structural matching ✓ Generated architecture diagram ``` ## Supported Features ### Entities - ✅ Structs (with fields and methods) - ✅ Interfaces (with method signatures) - ✅ Functions (standalone, package-level) - ✅ Packages ### Relations - ✅ Interface implementations (via gopls or name-based matching) - ✅ Method ownership (struct ↔ method) - 🔄 Dependencies (from imports) - Coming in Phase 2.C ### Confidence Scoring - **gopls source**: 0.99 (semantic type checking) - **inferred source**: 1.0 (structural name matching) ## Interface Detection ### How It Works The plugin uses a two-tier strategy: 1. **Try gopls** (if available): ```typescript // Query gopls LSP for semantic implementations const implementations = await goplsClient.getImplementations( 'MyInterface', 'file.go', lineNumber ); ``` 2. **Fall back to name-based matching**: ```typescript // Check if struct has all interface methods by name if (struct.methods.includes('Start') && struct.methods.includes('Stop')) { // Likely implements Runner interface } ``` ### Example Given this Go code: ```go type Runner interface { Start() Stop() } type Service struct { Name string } func (s *Service) Start() { } func (s *Service) Stop() { } ``` **Detection**: - gopls: Semantic verification → `Service` implements `Runner` (confidence: 0.99) - Fallback: Name matching → `Service` has `Start()` and `Stop()` (confidence: 1.0) **Result**: `Service` → implements → `Runner` (always detected) ## ArchJSON Output ```json { "version": "1.0", "language": "go", "entities": [ { "id": "main.Service", "name": "Service", "type": "struct", "package": "main", "methods": ["Start", "Stop"] }, { "id": "main.Runner", "name": "Runner", "type": "interface", "package": "main", "methods": ["Start", "Stop"] } ], "relations": [ { "id": "impl-0", "type": "implementation", "source": "main.Service", "target": "main.Runner", "confidence": 0.99, "inferenceSource": "gopls" } ] } ``` ## Troubleshooting ### gopls Not Found **Symptom**: ``` ⚠ gopls not available, using fallback interface matcher ``` **Solution**: 1. Install gopls: `go install golang.org/x/tools/gopls@latest` 2. Add to PATH: `export PATH=$PATH:$(go env GOPATH)/bin` 3. Verify: `which gopls` **Impact**: Plugin works fine, uses name-based matching (75%+ accuracy) ### gopls Initialization Timeout **Symptom**: ``` ⚠ Go analysis degraded — gopls timed out (gopls startup exceeded budget of 120000ms). Falling back to tree-sitter-only interface matching; gopls call-graph layers will be missing. ``` This is the reliability bound doing its job: gopls did not come up within the configured budget, so ArchGuard cancelled it, reaped the process, and continued with tree-sitter-only analysis. The run is **not** hung — it completes with degraded output marked by `metadata.goGoplsDegraded: true`. **Solution**: 1. Raise the budget for very large modules: `ARCHGUARD_GOPLS_TIMEOUT_MS=180000` (or `atlas.goplsTimeoutMs` in `archguard.config.json`). 2. Check gopls version: `gopls version` (should be recent) 3. Verify Go module: ensure `go.mod` exists and dependencies resolve (`go mod download`); first-run module resolution is a common cause of stalls. **Impact**: Falls back to name-based matching; gopls is poison-pill disabled for the rest of the process (not retried). Restart the CLI/MCP process to re-arm gopls after fixing the environment. ### No Implementations Detected **Symptom**: Interface implementations not showing in diagram **Checklist**: 1. ✓ Methods have matching names (case-sensitive) 2. ✓ Struct has ALL interface methods 3. ✓ Methods are exported (start with capital letter) 4. ✓ Files are being parsed (check `sourceFiles` in output) **Example Issue**: ```go // Won't detect: case mismatch type Runner interface { Start() } type Service struct {} func (s *Service) start() {} // lowercase 'start' != 'Start' // Will detect: exact match func (s *Service) Start() {} // uppercase 'Start' == 'Start' ``` ## Performance ### Small Projects (<10 files) - With gopls: ~2-5 seconds - Without gopls: <1 second ### Medium Projects (10-50 files) - With gopls: ~5-10 seconds - Without gopls: 1-3 seconds ### Large Projects (50+ files) - With gopls: ~10-30 seconds (first run, cached after) - Without gopls: 3-10 seconds **Note**: gopls initialization is one-time per workspace. ## API Usage ### Programmatic Usage ```typescript import { GoPlugin } from '@archguard/core'; const plugin = new GoPlugin(); await plugin.initialize({}); // Parse entire project (uses gopls if available) const result = await plugin.parseProject('/path/to/go/project', {}); // Parse single file (name-based only) const singleResult = plugin.parseCode(goCode, 'file.go'); // Cleanup await plugin.dispose(); ``` ### Custom gopls Configuration The gopls startup budget is configurable via the `ARCHGUARD_GOPLS_TIMEOUT_MS` environment variable or `atlas.goplsTimeoutMs` in `archguard.config.json` (see [Reliability](#reliability-gopls-timeout-budget--graceful-degradation)). ```typescript // Programmatic construction still accepts explicit bounds: // new GoplsClient(goplsPath, perRequestTimeoutMs, startupBudgetMs) const goplsClient = new GoplsClient('/custom/path/gopls', 60000, 120000); await goplsClient.initialize(workspaceRoot); ``` ## Comparison: gopls vs Fallback | Aspect | With gopls | Without gopls | |--------|-----------|---------------| | Accuracy | 95%+ | 75%+ | | Speed (first run) | Slower (~5s overhead) | Fast | | Speed (subsequent) | Fast (cached) | Fast | | Validation | Semantic type checking | Name matching | | Embedded types | No (future) | No | | False positives | Very low | Low to medium | | Setup required | gopls binary | None | **Recommendation**: Install gopls for best accuracy, but fallback is reliable for most use cases. ## Known Limitations 1. **Embedded type promotion**: Not yet supported ```go type Base struct {} func (b *Base) Start() {} type Service struct { Base // Embedded - methods promoted } // Currently: Won't detect Service.Start() from Base // Future: Phase 2.C will add this support ``` 2. **Method signatures**: Name-only matching ```go type Runner interface { Start(ctx context.Context) error } type Service struct {} func (s *Service) Start() {} // Different signature // Currently: May falsely match (name matches) // With gopls: Would reject (signature mismatch) // Future: Add signature validation ``` 3. **Cross-package implementations**: Basic support ```go // Package A type Runner interface { Run() } // Package B type Service struct {} func (s *Service) Run() {} // Currently: Detected if both packages parsed together // Limitation: Single-file parsing won't see cross-package ``` ## FAQ ### Q: Do I need to install gopls? **A**: No, it's optional. The plugin works without gopls using name-based matching (75%+ accuracy). Install gopls for best results (95%+ accuracy). ### Q: Will it crash if gopls is missing? **A**: No. The plugin detects gopls availability and gracefully falls back to name-based matching. You'll see a warning, not an error. ### Q: Can I disable gopls even if installed? **A**: Not yet. Future enhancement will add configuration: ```json { "golang": { "enableGopls": false } } ``` ### Q: Does it work with Go modules? **A**: Yes. The plugin works with or without `go.mod`. gopls works better with proper Go modules. ### Q: What Go version is required? **A**: Any version supported by tree-sitter-go (1.11+). gopls requires Go 1.18+. ### Q: How do I see which detection method was used? **A**: Check `inferenceSource` in the ArchJSON output: - `"gopls"` - Semantic analysis via gopls - `"inferred"` - Name-based structural matching ## Best Practices 1. **Use Go modules**: Create `go.mod` for better gopls accuracy 2. **Keep gopls updated**: `go install golang.org/x/tools/gopls@latest` 3. **Export methods**: Ensure interface methods start with capital letter 4. **Match signatures**: Use same parameter/return types (future gopls validation) 5. **Check output**: Review `inferenceSource` to see detection method used ## Next Steps - ✅ Phase 2.A: Tree-sitter parsing (Complete) - ✅ Phase 2.B: gopls semantic analysis (Complete) - 🔄 Phase 2.C: Dependency extraction (Planned) - 🔄 Phase 3: Multi-language support (Planned) --- **Need help?** Check the [implementation summary](./proposals/phase-2b-gopls-implementation-summary.md) or open an issue.