--- name: system-bridge-go-backend description: System Bridge Go backend conventions - error wrapping, graceful degradation for data modules, nil-pointer safety, context-aware Module.Update, structured slog logging, and errcheck-clean deferred cleanup. Use when writing or reviewing Go code in the system-bridge backend (data modules, HTTP/WebSocket APIs, CLI, discovery). --- # System Bridge Go Backend Project-specific Go patterns for the system-bridge backend. General Go style an agent already knows is intentionally omitted. ## Format and Lint - Always run `go fmt ./...` from the repo root after editing Go. - Before committing, run `mise run lint:all` (or `golangci-lint run ./...`). - CI runs golangci-lint. Watch `errcheck`: never leave unchecked error returns, especially deferred cleanup. All deferred `os.RemoveAll()` calls in tests must check the error. ## File Naming - Go files `lowercase.go`, tests `_test.go`. - OS-specific files use build-tag suffixes: `module_linux.go`, `module_windows.go`, `module_darwin.go`. - Avoid generic names like `utils.go`; be specific. ## Error Handling ```go // Wrap errors with context if err != nil { return cpuData, fmt.Errorf("error getting CPU count: %v", err) } // Handle at the appropriate level; degrade gracefully only where safe data, err := getData() if err != nil { slog.Error("Failed to get data", "error", err) return defaultData, nil } ``` - Always check and handle errors; wrap with `fmt.Errorf("context: %v", err)`. - Return errors up the stack; don't log-and-continue unless there is a clear reason. ## Graceful Degradation Data modules use best-effort strategies for optional metrics: ```go if temps, err := sensors.SensorsTemperatures(); err == nil { cpuData.Temperature = extractTemperature(temps) } if cpuData.Temperature == nil { if t := cm.ReadCPUTemperature(); t != nil { cpuData.Temperature = t } } ``` - **Use** for platform-specific features, optional enhancements, hardware-dependent data. - **Do not use** for critical functionality (auth, core API), data integrity (settings, state), or user-initiated actions. ## Structured Logging Use `log/slog` with structured fields, never string formatting: ```go slog.Info("Getting CPU data") slog.Error("Failed to fetch data", "module", moduleName, "error", err) // Avoid: slog.Info(fmt.Sprintf("Getting CPU data for %s", name)) ``` Levels: `Debug` (troubleshooting detail), `Info` (state changes), `Warn` (handled anomalies), `Error` (affects functionality without crashing). ## Nil-Pointer Safety Optional values are pointer fields. Always nil-check before dereferencing, and give pointer targets a stable scope: ```go if cpuData.Temperature != nil { useTemperature(*cpuData.Temperature) } temp := 75.0 cpuData.Temperature = &temp ``` ## Context Respect cancellation in `Module.Update`: ```go func (m Module) Update(ctx context.Context) (any, error) { data, err := cpu.CountsWithContext(ctx, true) if err != nil { return nil, err } select { case <-ctx.Done(): return nil, ctx.Err() default: } // ... } ``` ## Performance - Pre-allocate slices when size is known: `make([]types.PerCPU, 0, len(frequencies))`. - Use context timeouts for external calls; avoid frequent polling. - Pointers for large structs, values for small types.