forwarding and conversions connect the public contracts to internal
representations.
Git operations, Go toolchain execution, Gitea integration, and orchestration
bookkeeping belong under Go's `internal/` boundary. Dependency direction runs
from the facades into internal implementation, with internal collaborators using
contracts defined inside that boundary. Implementation packages can evolve
while the supported public contracts retain their compatibility obligations.
The `buildinfo` facade keeps runtime version access lightweight. Applications
read their embedded version through its supported accessor. ReleaseKit's builder
and runtime implementation own the linker symbol and injection mechanism
together.
The `buildinfo` facade follows three constraints:
- Its public API exposes only runtime information justified by consumer needs.
- Its implementation uses the standard library and a small internal dependency
graph independent of release orchestration.
- Dependency checks protect that boundary, and build-and-run integration tests
verify that the builder injects the selected version and the public accessor
reports it correctly.
The release executable calls the orchestration facade and handles command
parsing, output, and exit codes. Library errors and outcomes report meaningful
publication state, including partial progress and uncertainty. Go declarations
and GoDoc own exact public API contracts; additional public packages and
extension interfaces require a deliberate decision grounded in durable consumer
needs.
Compiled-export and dependency checks will protect facade ownership.
External-package behavioral tests will protect error identity, value ownership,
callback lifetime, and observable outcomes across forwarding and conversions.
## Shared tool boundary
Provide a versioned ReleaseKit executable backed by a Go library. Existing
shell entry points can become short invocations of that executable. A library
alone would still require a caller in every shell-driven repository.
ReleaseKit should own:
- stable-version parsing and committed-note identity checks;
- authoritative Git endpoint resolution, candidate capture, clean `main` and
remote-branch agreement checks, and unused local/remote tag checks;
- one validation invocation bound to the captured candidate, followed by cheap
rechecks before publication;
- annotated-tag creation and verification, explicit tag-object push, and one
bounded authoritative-ref check after a failed or ambiguous push;
- a standard version accessor in the public `buildinfo` facade, backed by
release-build injection of the selected tag;
- a cross-build API that accepts the application's main package and selected
targets, initially Linux amd64, Linux arm64, and macOS arm64 (Go's
`darwin/arm64`), including an empty set for source-only projects;
- consistent release build flags and asset naming, plus a host-runnable build
that checks the executable's reported version; and
- Git publication tests using temporary local remotes, including remote
advancement, rejected pushes, lost acknowledgments, and unavailable remotes.
Each repository should retain its product name, compatibility decisions,
candidate-specific checks, main package, selected build targets, delivery
declaration, and a small test of its ReleaseKit wiring. For example, PromptKit
exercises its consumer examples. Applications would adapt their existing
version output to use the `buildinfo` accessor; one agreed version command
or flag would let ReleaseKit smoke-test a host-runnable build the same way.
Cross-built binaries would use the same injection mechanism, but compilation
alone would not establish that they run correctly on another platform.
ReleaseKit should run the selected checks and builds without silently adding
binary gates to source-only projects.
The build API should own the mechanics rather than require each application to
provide a custom linker symbol or shell loop. The initial target list reflects
the current projects, not a permanent limit on future platforms. It should
support both candidate cross-build checks and artifact builds from the tagged
source, using the same version and target selection.
## Woodpecker and Gitea delivery
A common tag-triggered Woodpecker workflow can run ReleaseKit against the
tagged source. Repository-local YAML should declare the pinned tool image and
credentials; each repository's ReleaseKit configuration should declare its
main package and target set. ReleaseKit can own version injection, cross-builds,
checksum generation, and delivery. The source-only repositories can declare
zero binary assets.
For projects that declare a hosted Gitea release, a shared delivery command
could create or find the release for the tag, use the checked-in note, and add
the declared assets and checksum manifest. A retry should inspect existing
assets, verify matching content, add only missing items, and stop on a conflict.
An existing checksum manifest must remain consistent with any later addition.
The release and its attachments are separate remote operations, so partial
delivery must be expected and reported clearly.
Audita, Narratio, and Scriptorium currently use the Woodpecker release plugin
with `file-exists: skip` and `overwrite: false`. Those settings avoid deliberate
replacement by filename, but they do not prove that a rebuilt file has the
same bytes as a previously published file. Content comparison would be a
reason to implement a shared Gitea delivery client rather than rely solely on
the plugin's skip behavior.
Requiring a hosted Gitea release for PromptKit, ConfigKit, or Notarius would
add a deliverable and credentials to their current source-only policies. That
decision can be made separately from standardizing delivery for projects that
already publish hosted assets.
## Failure states
ReleaseKit should report source publication and artifact delivery separately:
| Event | Result |
| --- | --- |
| Candidate validation or a pre-push guard fails | No release was published; correct and validate the candidate again. |
| Tag push reports success | Source release is complete; delivery may still be pending. |
| Tag push fails or its acknowledgment is lost | Query the authoritative ref once. Confirm success only if the exact intended tag object is present on the intended commit; otherwise report failed or unknown status and retain the local tag. |
| Woodpecker or Gitea delivery fails after tag publication | Keep the source release successful. Retry missing delivery from the immutable tag after checking existing content. |
| Published source or content needs correction | Publish a new version; never replace content under the published version. |