DocsContributingDevelopment workflow

Development workflow

Generate, lint, test, build, validate deployment, and release changes.

Install tools and synchronize dependencies

make tools
make dependencies

make tools installs the repository's Go generators and linter into .bin; Buf itself must also be installed and available on PATH. make dependencies runs make generate and then go mod tidy -v. Generation removes and recreates pkg/proto, updates buf.lock, and compiles the protobuf contracts. Keep proto/, pkg/proto/go/, buf.lock, go.mod, and go.sum changes together when applicable.

Run the narrower target when only protobuf output needs verification:

make generate

After changing a Go interface with a //go:generate directive, regenerate mocks:

make mockgen

Test

make test/short
make test/integration

make test/short runs every unit suite with the race detector; Docker-backed integration tests self-skip under -short. make test/integration selects every TestIntegration* test and needs a running Docker daemon: repository packages under internal/repository, the command ledger in internal/pkg/commandidempotency (both provisioned with Testcontainers), and the container workflow tests in internal/pkg/kind/container (which drive the host Docker daemon directly).

Integration package binaries use independent Testcontainers/Ryuk sessions through a Python launcher. Local runs default to INTEGRATION_PARALLELISM=1; CI uses four packages at a time. Set this value to match the Docker host's CPU and memory. The launcher requires Python and preserves test arguments, exit codes and signals. Race detection, fresh execution and every assertion remain enabled; tests are not retried.

Integration tests live as *_integration_test.go files. Testcontainers-backed suites share a single bootstrap, internal/pkg/testkit, that starts one container per requested service in the package's TestMain (for example testkit.Run(m, testkit.WithPostgres(), testkit.WithKafka())), applies the embedded migrations and index setup, and terminates everything when the test binary finishes. The suite reuses the production clients and runners from the sibling internal/pkg packages: postgres.Migrate, clickhouse.Migrate, meilisearch.SetupIndexes and kafka.EnsureTopics. Tests therefore exercise the exact code paths services use without re-implementing migrations. Integration tests self-skip under -short or when Docker is unavailable (testkit.RequireDocker provides the same gate for direct-daemon suites), keeping make test/short fast on every machine.

Lint and build

make lint
make build/all
cd dashboard && npm test && npm run lint && npm run build
cd ../static && npm run check

Use npm ci in both frontend workspaces when reproducing CI from their committed lockfiles. npm run check in static/ validates the MDX/OpenAPI registry, lints, type-checks, and performs the static Next.js export.

Run Go lint as ./.bin/golangci-lint, which is what make lint invokes. make tools installs a build of it there, and GO_BIN defaults to that directory. A golangci-lint found on PATH is usually a different build of a different Go toolchain; when it predates the Go version in go.mod it aborts with can't load config: the Go language version ... is lower than the targeted Go version, which reads like a config bug but is only a toolchain mismatch. To lint a subset, pass package patterns to the pinned binary: ./.bin/golangci-lint run ./internal/pkg/kafka/....

Dashboard route modules should remain thin. Keep domain components, hooks, and API response types under dashboard/src/features/<domain>; reserve dashboard/src/components/ui for reusable shadcn primitives and dashboard/src/lib for infrastructure shared by multiple features.

Dependency updates

Keep Go modules and tools, both npm lockfiles, Docker base images, Compose and Kubernetes service images, integration-test fixtures, and CI toolchains aligned. Use stable versions supported by their plugins and framework dependencies; updating only a direct package can leave an unsupported toolchain. Regenerate protobuf output and run the existing checks after changing its generators.

Review certificate verification in the runtime and operating system as well as SDK dependencies. Rebuild images to pick up refreshed CA packages and restart processes to activate them. See Certificates and security for the Node trust-store policy.

Kubernetes changes

Render and client-dry-run both overlays after changing infra/k8s, scripts/k8s, deployment configuration, image arguments, ports, probes, or Secrets:

make k8s/render/local
make k8s/render/production
make k8s/dry-run/local
make k8s/dry-run/production
scripts/k8s/setup.sh --mode local --dry-run
scripts/k8s/setup.sh --mode production --context <context> --dry-run

The render targets work offline. The kubectl client dry runs still use API discovery and therefore need a reachable selected cluster; full OpenAPI schema validation likewise needs a live target cluster or a separate validator such as kubeconform. The setup-script dry runs additionally exercise prerequisite, existing-Secret inspection, and Secret preparation logic against the selected kubectl context.

Documentation changes

Add or edit MDX under static/content/docs, update static/docs.config.ts, and update static/content/openapi.yaml when the public HTTP contract changes. Run npm run generate:llms after changing any of those canonical sources; npm run validate:llms rejects stale llms.txt or llms-full.txt artifacts, and the static build runs this drift check automatically. Keep the concise repository references under docs/, README.md, infra/k8s/README.md, and landing-page copy aligned when the same behavior is described there. From static/, npm run validate:docs rejects unregistered pages, duplicate slugs, missing source references, broken docs links, and invalid OpenAPI.

Releases

Git tags matching v* trigger service image release jobs after lint, build, generated-protobuf verification, and tests complete. The static site validates and deploys from main through GitHub Pages.