title: Phase 5b close — @rocky-hq/contracts@0.2.0 + hearth codegen consumption date: 2026-05-25 status: Accepted phase: 5b predecessor: docs/decisions/2026-05-24-phase-5a-close.md spec: docs/specs/2026-05-04-rocky-phase-5.md §6, §7 plan: docs/plans/2026-05-25-contracts-hearth-subpath-phase-5b.md
Phase 5b close
Shipped
rocky-hq/contractsPR #4:./hearthzod subpath (7 schemas mirroring spec §6 —Tier,DriverName,Status,ResourceCaps,ProvisioningProfile,DeploymentRef,HearthHatchEvent) + boundary parsers (parseProvisioningProfile,parseDeploymentRef,parseHearthHatchEvent) + sibling Go modulegithub.com/rocky-hq/contracts/gowith quicktype-generatedhearthpackage +go-parityCI job (regenerate + diff + Go round-trip tests). Taggedv0.2.0+go/v0.2.0at merged commit7cb7ad9.rocky-hq/hearthPR #3: pinnedgithub.com/rocky-hq/contracts/go@v0.2.0; deletedinternal/driver/types_local.go(Phase 5a placeholder); re-exported generated types + named string constants underpackage driverviainternal/driver/types.go; 5a fake-driver contract suite remains green. Merged at001a142.rocky-hq/rockyPR #57 (plan): plan merged atdf3a2bf.- Parent:
contractssubmodule pointer bumped to7cb7ad9;hearthsubmodule pointer bumped to001a142; CLAUDE.md SS-00 + SS-08 refreshed; MILESTONES.md 5b row closed.
Carry-forwards
1. Nested Go module tagging convention
Phase 5b shipped two release tags from the same commit: v0.2.0 (npm package tag, drives GitHub Packages publish) and go/v0.2.0 (Go module proxy tag — required because the Go module lives at contracts/go/, not the repo root; without the go/ prefix the module proxy returns unknown revision). Documented in contracts/docs/codegen.md. Future contracts version bumps must push both tags together. Consumers that go get github.com/rocky-hq/contracts/go@<tag> need the go/v* prefixed tag to resolve.
2. dedupeClassPairs codegen post-processor
quicktype emits <Name>Class peer structs when a top-level schema is also referenced inline by another schema. Phase 5b added a regex-based deduplification pass in contracts/scripts/build-go-bindings.mjs that strips these <Name>Class duplicates before writing go/hearth/types.go. If schemas grow such that other quicktype suffix patterns (<Name>1, <Name>Element, <Name>Union) start appearing, the dedupe post-processor needs extending accordingly.
3. gofmt pipe in build:go
npm run build:go pipes quicktype output through gofmt before writing the file. This keeps go/hearth/types.go in canonical Go format so git diff --exit-code go/ (the go-parity gate) stays clean across Go toolchain versions that may format differently.
4. Contracts visibility decision deferred
rocky-hq/contracts remains PRIVATE. hearth's CI carries GOPRIVATE=github.com/rocky-hq/contracts + a ROCKY_HQ_RO_TOKEN PAT secret for go mod download. The option to make contracts public was considered during 5b and explicitly deferred: the repo contains no secrets, but the operator chose to keep the source private for now. Revisit in Phase 6+ when additional public submodules (e.g., future public drivers) need to consume contracts without PAT auth.
5. Hearth parsers throw vs ralph ParseResult — deliberate divergence
The three boundary parsers in contracts/src/hearth/parsers.ts throw ZodError on invalid input (fail-fast, admin-RPC pattern). The ralph parsers in contracts/src/ralph/parsers.ts return ParseResult<T> (log-and-skip, SSE stream pattern). This divergence is intentional per the HEARTH design: admin provisioning paths should never silently swallow type errors. Documented in hearth/src/hearth/parsers.ts. Future hearth boundary code should follow the throw pattern; non-hearth / SSE-adjacent code should follow ralph's ParseResult<T> pattern.
6. Public-fork CI failure
hearth is a public repo. PRs opened from forks will fail at go mod download github.com/rocky-hq/contracts/go because the ROCKY_HQ_RO_TOKEN secret is not passed to fork builds by GitHub Actions policy. This is an accepted tradeoff (contracts remains private). Documented in hearth/.github/workflows/ci.yml. Mitigation options (making contracts public, or vendoring in hearth) deferred to Phase 6+.
Next
Phase 5c — LocalDocker driver in hearth (internal/driver/localdocker/) implementing the locked Driver interface against the Docker daemon, with integration tests via testcontainers-go gated by ROCKY_HEARTH_INTEGRATION=1. The fake_test.go contract suite from 5a is the compliance gate every driver must satisfy.
References
- Phase 5 spec:
docs/specs/2026-05-04-rocky-phase-5.md - Plan:
docs/plans/2026-05-25-contracts-hearth-subpath-phase-5b.md - Predecessor:
docs/decisions/2026-05-24-phase-5a-close.md - Contracts PR: https://github.com/rocky-hq/contracts/pull/4 (merged
7cb7ad9, tagsv0.2.0+go/v0.2.0) - Hearth PR: https://github.com/rocky-hq/hearth/pull/3 (merged
001a142) - Parent plan PR: https://github.com/rocky-hq/rocky/pull/57 (merged
df3a2bf)