0004 — Phase 5 Go submodule conventions (hearth/)
- Date: 2026-05-04
- Status: Accepted
- Supersedes: none
- Reinforces: 0001-redesign-bootstrap.md §2 (HEARTH language: Go), 0002-rename-fleet-to-hearth.md
Context
Phase 5 (docs/specs/2026-05-04-rocky-phase-5.md) introduces the third language in the Rocky superproject: Go, in the new rocky-hq/hearth submodule. The redesign spec resolved the language choice (§Why Go for HEARTH) but left the per-language conventions — module path, license, toolchain, CI shape, codegen pattern — open. Without those locked, sub-phases 5a–5e will drift on style, tooling, and test discipline; the same problem the Python (Phase 3a) and TypeScript (Phase 2) submodules each settled in their own bootstrap PRs.
This decision settles the Go conventions before 5a starts coding.
The user-facing constraint guiding this decision: mirror existing patterns (kahn-hq Go projects + the Phase 4 contracts/ CI shape). No bespoke Go tooling for Rocky; we adopt the standard ecosystem defaults that the operator already runs in adjacent repos.
Decision
The conventions below apply to rocky-hq/hearth and to any future Go submodule of rocky-hq (none planned through Phase 7).
Module + repo
- Module path:
github.com/rocky-hq/hearth. No vanity import path; the GitHub URL is the canonical import path. Standard Go ecosystem default. - License: MIT (per redesign-spec §Licensing — "
console,ralph,hearth,algo→ MIT"). Distinct fromcontracts/Apache-2.0; HEARTH does not redistribute schemas. - Go version pin:
go 1.25ingo.mod(matches the toolchain on the operator's box and kahn-hq's current pin). Bumps require a one-line note in this decision's "Amendments" section, not a new decision.
Toolchain
- Build:
go build ./...(no Makefile; no Mage; no Bazel). - Test:
go test ./...for unit. Integration tests gatedROCKY_HEARTH_INTEGRATION=1per spec §13. - Format:
gofumpt(stricter thangofmt; matches kahn-hq). - Lint:
golangci-lintwith the kahn-hq baseline.golangci.ymlcopied verbatim intohearth/at 5a (no Rocky-specific deviations on day one). Future deviations recorded in this decision's amendments. - Dependency management: standard
go mod+go.sum. No vendoring (Go module proxy is canonical).go mod tidyruns in CI as part oflint.
Repo layout
Standard Go layout per spec §4:
cmd/<binary>/main.gofor entrypoints.internal/for non-exported packages (everything that isn't the public Driver interface or generated types).test/integration/for integration tests; unit tests live alongside the code (*_test.gonext to source).- No
pkg/directory. (The historicalpkg/convention is discouraged in modern Go;internal/plus the module root cover both private and public packages.)
CI shape
GitHub Actions, three jobs (mirrors the contracts/console job naming):
lint—gofumpt -l .exits clean;go vet ./...;golangci-lint run;go mod tidyproduces no diff.test—go test ./... -race -count=1(race detector on, no test caching). Unit only — no Docker daemon assumed.integration— runs only whenROCKY_HEARTH_INTEGRATION=1. Uses GitHub Actions' built-in Docker daemon. Required on PRs touchinginternal/driver/localdocker/**ortest/integration/**(path-filtered trigger).
Branch protection on main requires lint and test to pass; integration is required when triggered.
Cross-language types (codegen path)
Per Phase-5 spec D5 + §7:
contracts/(Phase 4 D1 locked zod-as-source) emits Go bindings viaquicktypeagainstdist/schemas.jsoninto a sibling Go module atcontracts/go/.hearth/importsgithub.com/rocky-hq/contracts/go/hearthas a regulargo.moddep, pinned by Git tag (e.g.v0.2.0).- Hearth never hand-writes a struct that exists in contracts. Drift is caught by the
go-parityjob in the contracts repo (Phase-5 spec §7).
This pattern extends naturally to any Phase-6+ Go consumer: import the same contracts/go/<subpath> module.
Versioning + tags
- SemVer pre-1.0 (matches contracts; matches the rest of Rocky during foundation phases).
- First release tag:
v0.1.0at the close of 5c (when LocalDocker is real). 5a's bootstrap commit lands onmainun-tagged. - Breaking interface changes to
driver.Driverbump minor; additive changes (new methods with default implementations) bump patch.
Distribution
- Single static binary built per platform via
go build(no CGO). The hearth binary is the deployable artifact; no Docker image of hearth itself is published in Phase 5 (the binary runs alongside the console process in self-host or as a sidecar in cloud). - No
go install-able CLI surface in Phase 5. The binary is invoked over its JSON-over-HTTP RPC; nohearthuser-facing command. Phase 6 may add one for Kustomize manifest emission.
Documentation
hearth/README.md— what HEARTH is + quickstart (go run ./cmd/hearthagainstFakeDriver).hearth/CLAUDE.md— per push-down policy 0001 §3, build/test/runtime instructions live here, NOT in parent CLAUDE.md.- Per-package
doc.gofiles for anyinternal/package with non-obvious responsibilities (Driver interface contract, server lifecycle).
Consequences
- Sub-phases 5a–5e proceed without per-PR style debates. A new Go submodule lands in a single afternoon at the same quality bar as the Python and TS submodules.
- kahn-hq compatibility. Operators familiar with kahn-hq's Go style read hearth without a context switch. The shared
.golangci.ymlbaseline is the explicit lever for keeping that true. - Codegen direction is locked. zod → JSON Schema → quicktype → Go. No per-Go-consumer codegen; no protobuf tooling drag. Phase-6 drivers (Kustomize, DevarnoCloud) inherit this.
- No vanity import path means no extra DNS or static-site hosting overhead. If we ever need a vanity path, it lands as an amendment, not a new decision.
- No GoReleaser / cross-compile pipeline in Phase 5. Distribution is build-from-source for self-host (binary built by the user's
go build) and a cloud-side build for the managed deployment. A GoReleaser CD pipeline can land in Phase 6/7 once there are actual cloud-build SLAs to honour. - What this decision does NOT do: mandate a specific HTTP framework (
net/httpis enough; if the server module wantschiorecholater, that's a hearth-internal call), a specific logger (slogfrom stdlib is the obvious default), or a specific Docker SDK (github.com/docker/docker/clientis the spec recommendation in §9 but not foreclosed here).
Amendments
(None yet — append future tightenings as ### YYYY-MM-DD: <change> subsections.)
References
- Phase 5 spec:
docs/specs/2026-05-04-rocky-phase-5.md - Redesign spec language choice:
docs/specs/2026-05-02-rocky-system-redesign.md§Why Go for HEARTH (lines 154–166) - Push-down policy:
docs/decisions/0001-redesign-bootstrap.md§3 - Cross-repo conventions:
docs/shared/conventions.md(Go line filled in alongside this decision) - kahn-hq Go style baseline: operator's
~/code/workspace/kahn-hq(referenced for.golangci.ymlbaseline at 5a; not re-vendored) - Phase 4 contracts CI shape:
docs/specs/2026-05-03-rocky-phase-4.md§9