Developer tools architecture
Canonical architecture for contributor-facing tooling under internal/tools.
Intent
- Keep operational tooling explicit, testable, and maintainable.
- Make each tool easy to discover, run, and safely evolve.
- Avoid monolithic utility binaries with mixed concerns.
Boundary model
internal/tools is split by responsibility:
- Runtime helpers (
internal/tools/cli): shared signal and CLI helper seams. - Small one-shot tools: docs/check/report style commands (for example
i18ncheck,topologygen,eventdocgen). - Workflow tools: higher-complexity orchestration (
seed,scenario,maintenance,importer).
Tool packages should own business logic and remain transport-neutral. Command entrypoints (cmd/* or package main) should only parse flags, wire dependencies, and exit.
Workflow tools should also expose explicit internal seams:
- Keep parser/DSL loading isolated from runtime side effects (for example
internal/tools/scenario/script). - Keep system adapters and registry declarations in dedicated files/packages, separate from registry indexing/validation.
- Isolate persistence side effects behind injected interfaces (for example seed state loading/saving) so idempotency behavior is testable without filesystem coupling.
Command contract
Every tool should converge on the same shape:
Configtype for user-facing inputs.ParseConfig(*flag.FlagSet, []string) (Config, error)where the package is notmain.Run(context.Context, Config, stdout, stderr) error(orrun(args, stdout, stderr) errorfor packagemaintools).main()handlesos.Exitonly afterRun/runreturns.
Rules:
- Return errors from logic; avoid
os.Exitin non-mainfunctions. - Keep stdout machine-consumable where possible; route diagnostics to stderr.
- Define interfaces at consumption seams for external IO/process/network dependencies.
Testability rules
- Unit-test parsing, normalization, and report rendering without process exits.
- Keep integration tests focused on tool seam contracts (filesystem, process, gRPC, SQLite).
- Use dependency injection instead of mutable package globals for test doubles.
Ownership and docs
Durable tool behavior and architectural decisions must live in docs/. Temporary execution notes stay in .agents/plans/ and should be promoted or deleted when complete.
See also: Add a developer tool.