← Ledger


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

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