This document defines the coding standards, testing expectations, and conventions for AI-assisted development on Cartographer. Any AI agent contributing to this codebase must follow these guidelines.
- Go 1.25+ with modules enabled.
- Linter:
golangci-lint— all code must pass with zero issues before committing. - Test framework:
testingstdlib +github.com/stretchr/testify(assert/require). - CLI framework: Cobra + Viper.
- CI: GitHub Actions runs lint, test, and build on every push and PR.
- Follow standard Go conventions:
gofmt,goimports, short variable names in small scopes, clear naming in exported APIs. - Prefer early returns over deep nesting.
- Keep functions focused — one responsibility per function. If a function exceeds ~50 lines, consider splitting it.
- Use
log.WithField/log.WithFieldsfor structured logging. Always include a"func"field. - Do not add comments that restate what the code does. Only comment on why something is done when the reason isn't obvious.
- Do not add docstrings, type annotations, or comments to code you didn't change.
- Target: 85%+ overall, no exported function below 80%.
- Run
go test -coverprofile=coverage.out ./...andgo tool cover -func=coverage.outto verify. - CI enforces lint, test, and build on every push.
- Unit tests go in
_test.gofiles alongside the code they test, in the same package (for internal functions) or_testpackage (for black-box testing of exported APIs). - Integration tests exercise the full pipeline: YAML parsing → dependency analysis → output generation. Use embedded YAML constants, not external fixture files.
- CLI integration tests invoke
cmd.RootCmd.Execute()withSetArgs()and capture output viabytes.BufferwithSetOut()/SetErr(). - Real-world tests model manifests after actual Helm chart patterns (e.g., Bitnami
app.kubernetes.io/*labels) to catch issues that synthetic tests miss.
- Every new feature must include tests that verify both positive (correct matches) and negative (correct non-matches) behavior.
- Use table-driven tests for functions with multiple input/output cases.
- Use
t.Run()subtests for logical grouping within a test function. - Use
requirefor preconditions that must hold (test aborts on failure). Useassertfor the actual assertions being tested. - Use
t.Helper()in test helper functions so failure messages point to the calling test. - Use
t.Cleanup()for teardown instead ofdeferin test helpers. - All output formats (DOT, Mermaid, JSON, PNG, SVG) must have integration tests for any new feature that affects graph output. Do not ship a feature tested in only one format.
TestFunctionNamefor basic unit tests.TestFunctionName_SpecificScenariofor scenario-specific tests.TestAnalyzeCommand_FeatureName_Formatfor CLI integration tests (e.g.,TestAnalyzeCommand_MatchExpressions_MermaidStdout).
cmd/— CLI commands (Cobra). Thin layer that parses flags and delegates topkg/.cmd/analyze/— Theanalyzesubcommand.cmd/version/— Theversionsubcommand. Version injected via-ldflagsat build time.pkg/dependency/— Core dependency analysis engine: graph building, handlers, output generation.pkg/helm/— Helm SDK integration for chart rendering.pkg/parser/— YAML parsing into Kubernetes unstructured objects.
When adding support for a new Kubernetes resource type:
- Add a handler function in
pkg/dependency/handlers.gofollowing the existing pattern (accept the resource, label index, and deps map). - Add the handler dispatch in
BuildDependencies()inpkg/dependency/dependency.go. - Add unit tests in
handlers_test.gowith inline unstructured objects. - Add integration tests in
dependency_test.gowith embedded YAML manifests. - Add CLI integration tests in
cmd/analyze/analyze_test.gocovering all output formats. - Update the "Supported Resource Types" table in
README.md.
- Services use flat
map[string]stringselectors — useLabelIndex.Match(). - NetworkPolicy and PDB use full
metav1.LabelSelector(matchLabels + matchExpressions) — useLabelIndex.MatchSelector(). - Always extract both
matchLabelsandmatchExpressionsfrom selector maps.
- Commit messages: Use conventional commits (
feat,fix,test,ci,docs,refactor). Include a scope in parentheses (e.g.,feat(dependency): add RBAC handler). - Commit body: Explain the why, not just the what. Keep the first line under 72 characters.
- Releases: Versioned via git tags. GoReleaser handles binary builds and Homebrew tap updates automatically on tag push.
- Tag messages: Include a one-line summary and a brief list of highlights.
- All tests must pass and lint must be clean before tagging a release.
- Do not introduce new dependencies without justification. Prefer stdlib where possible.
- Do not add feature flags or backward-compatibility shims — just change the code.
- Do not create helper utilities for one-time operations.
- Do not add error handling for scenarios that cannot happen.
- Do not modify files unrelated to the current task (no drive-by refactors).
- Do not create documentation files (README, etc.) unless explicitly requested.
- Do not skip pre-commit hooks or linting.
- Do not commit scratch files, temporary manifests, or debug output.