Document ReleaseKit proposal, policy roadmap, and facade architecture

This commit is contained in:
2026-10-05 14:26:42 +00:00
parent 3cbb3f3406
commit 4f914f10cf
3 changed files with 440 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
# ADR 0001: Own public contracts through facades
## Status
Accepted. Implementation pending.
## Date
2026-10-05
## Context
ReleaseKit is being designed as an opinionated Go library with a release
executable. Its responsibilities include publication, cross-builds, version
injection, and artifact delivery. Consuming applications also need lightweight
access to the version embedded in their binaries.
ConfigKit and PromptKit demonstrate a facade approach in which public packages
own consumer contracts and delegate to internal implementation. This gives
maintainers room to change internal structure while preserving supported type
identity, method sets, and behavior. ReleaseKit needs that boundary from its
first implementation.
## Decision
The root `releasekit` package is the public facade for release orchestration. A
separate public `buildinfo` package is the facade for runtime version information.
Each package exposes a cohesive set of operations and values justified by
consumer needs.
Each facade defines its public named types and owns their documented behavior.
Exported signatures, fields, callbacks, and method sets use facade-owned types
and deliberately selected public dependencies. Handles retain implementation
state in private fields and expose explicitly selected methods. Forwarding and
conversion code translates between public contracts and internal representations.
Implementation packages live under Go's `internal/` boundary. Dependency
direction runs from public facades into internal implementation. Contracts shared
between implementation packages belong inside that boundary. Git operations, Go
toolchain execution, Gitea integration, and orchestration bookkeeping remain
implementation responsibilities, with package layout chosen for cohesion.
The `buildinfo` facade has a lightweight dependency graph suitable for application
runtime imports. Its version accessor is a supported public contract. ReleaseKit's
builder and runtime implementation own the linker symbol and injection mechanism
together, allowing applications to obtain their version through the accessor.
The release executable invokes the supported orchestration facade and owns
command parsing, output, and exit codes. The library returns consumer-facing
errors and outcomes that describe 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 a
durable consumer need. Compiled-export and dependency checks protect facade
ownership; external-package behavioral tests protect consumer semantics.
## Alternatives considered
- Keep implementation in public packages: reduces forwarding work and makes
implementation declarations part of the surface contributors must maintain.
- Use aliases or embedded implementation types: reduces conversion code while
coupling consumer type identity and method sets to implementation types.
- Provide one facade for orchestration and runtime version access: offers one
import path and a shared dependency graph for both responsibilities.
- Expose a public package for each implementation component: enables direct
component reuse and expands the compatibility surface across Git, build, and
delivery machinery.
## Consequences
Consumers depend on deliberate contracts in `releasekit` and `buildinfo`.
Internal algorithms, collaborators, and package boundaries can evolve while
those contracts remain stable. Both public packages participate in ReleaseKit's
versioning and compatibility obligations.
Explicit forwarding and conversions add maintenance work. They must preserve
error identity, value ownership, callback lifetime, and observable outcomes.
Boundary checks and representative consumer tests provide evidence for those
properties as implementation proceeds.
The separate `buildinfo` facade supports runtime use with a small dependency
surface. Its integration with the builder requires tests that verify the selected
release version is injected and reported correctly.

156
docs/roadmap/policy.md Normal file
View File

@@ -0,0 +1,156 @@
# Adopt the policy framework
Status: Proposed; adoption has not started.
Date: 2026-10-05
## Purpose and scope
Adopt Standard Repository Policies into ReleaseKit as locally maintained rules
for an opinionated Go library and its proposed release executable. The adoption
should preserve the project's existing licensing choice, support deliberate
standardization of release behavior, and keep documentation accurate about what
has been implemented.
This roadmap covers policy adoption, contributor navigation, documentation
ownership, licensing notices, and the preparation of ReleaseKit's own release
procedure. Product implementation and consumer migrations belong to the separate
[ReleaseKit proposal](../../ideas.md).
## Upstream baseline and references
Use upstream commit `d84a38ebab26f048cafbe59a2246700c35d29843`. The local
`../policy` checkout and upstream `main` matched that revision during the review.
PromptKit and ConfigKit adopted the same baseline.
Follow the [adoption guide at the selected revision](https://gitea.maximumdirect.net/eric/policy/src/commit/d84a38ebab26f048cafbe59a2246700c35d29843/docs/adoption.md).
Copy applicable policies locally and customize them. Retain each file's exact
upstream provenance; later upstream changes become local rules through deliberate
review and adoption. Git history is sufficient to record this process.
Useful library adaptations include
[ConfigKit's architecture policy](../../../ConfigKit/docs/policy/architecture.md),
[PromptKit's architecture policy](../../../promptkit/docs/policy/architecture.md),
and their documentation, testing, and attribution policies. Use these as examples
of adaptation rather than copying their product-specific requirements.
## Policy adaptations
Adopt all seven policies under `docs/policy/`:
| Policy | Base version | ReleaseKit adaptation |
| --- | --- | --- |
| `meta.md` | 1.0 | Retain local authority, per-document adoption provenance, and upstream update navigation. |
| `agents.md` | 1.3 | Retain proportionate implementation and local commit practices. Add the libraries' clarification that implementation, tests, and agent-authored plans do not independently establish new contracts. |
| `architecture.md` | 1.3 | Define the library, proposed executable, consumer responsibilities, external effects, and dependency boundaries. |
| `testing.md` | 1.3 | Retain behavioral, deterministic, offline testing. Prioritize publication integrity, ambiguous outcomes, partial delivery, cross-builds, and version injection as those features are implemented. |
| `documentation.md` | 1.11 | Assign exact exported Go contracts to declarations and GoDoc, workflows to consumer guides, and proposals to roadmap documents. Add CLI and integration owners when those interfaces exist. |
| `oss.md` | 1.8 | Preserve the library licensing choice, apply the attribution format, and identify separately licensed copied policy material. |
| `release.md` | 1.4 | Govern releases of ReleaseKit itself, with explicit deliverables and justified checks. Distinguish its maintainer procedure from the release standard offered to consumers. |
## Proposed architectural constraints
The public facade decision is accepted in
[ADR 0001](../adr/0001-public-facade.md). Policy adoption will incorporate that
decision alongside the remaining proposed constraints below.
Evaluate ReleaseKit against both of ConfigKit's relevant success criteria:
applications should delete substantial infrastructure, and consumers should adopt
one consistent set of deliberately selected conventions.
- ReleaseKit owns the selected publication, version injection, build, and delivery
conventions. Consumers supply product identity, main package, selected targets,
and application-specific checks.
- Consumers use the root `releasekit` orchestration facade and the lightweight
`buildinfo` runtime facade. Both packages own their public contracts and
delegate through private state and explicit conversions to internal
implementation. ReleaseKit builds and tests independently
of the consuming repositories and does not depend on their private packages.
- Git, filesystem, Go toolchain, and Gitea effects have proportionate boundaries
and occur through explicit operations. Package layout and abstractions should
serve those boundaries without requiring an application framework.
- The library returns meaningful errors and publication outcomes. The executable
handles command parsing, output, and exit codes.
- The `buildinfo` facade provides runtime version access through a lightweight
dependency graph. ReleaseKit's builder and runtime implementation own version
injection together.
Architecture policy may govern future development. Concrete package inventory,
API semantics, and implemented behavior must be documented when they exist.
## Adoption stages
### 1. Copy and tailor policies
- [ ] Copy the seven policies from the selected baseline.
- [ ] Preserve each `Base Policy Version`, set `Last Updated` to the actual
adoption date, and add an `Upstream Source` link to that file at the full commit.
- [ ] Customize the policies for ReleaseKit and remove their `Base Policy
Template` sections.
- [ ] Adapt upstream `agents/AGENTS.md` into root `AGENTS.md`, preserving the
existing knowledge-graph discovery instructions.
- [ ] Create `docs/development.md` with contributor orientation and the selective
policy reading guide. Route tasks to applicable policies instead of requiring
the entire suite for every task.
### 2. Establish documentation ownership
- [ ] Move the substantive contents of `ideas.md` to
`docs/roadmap/releasekit.md`, preserving the proposal, and retain `ideas.md` as a
navigation pointer. Update this roadmap's proposal link in the same change.
- [ ] Update README with accurate current status and navigation.
- [ ] Define ownership for GoDoc, consumer guidance, architecture constraints,
internal implementation, release procedures, ADRs, and unfinished work.
- [ ] Create consumer, CLI, configuration, integration, and internal documents
when substantive implemented responsibilities justify them. Avoid empty owners
and speculative command or API references.
### 3. Complete notices and attribution
- [ ] Add root `COPYRIGHT` using the upstream library example and verified project
details. The recommended grant is `LGPL-3.0-or-later`, consistent with the two
library examples; preserve any established author choice.
- [ ] Verify the existing LGPLv3 and incorporated GPLv3 texts in `LICENSE` are
complete and faithful. Keep project-specific grant statements in `COPYRIGHT`.
- [ ] Add `docs/oss/attribution.md` with the supplied policy attribution entry,
describing the actual adapted policies, agent orientation, and reading guide.
- [ ] Copy the CC-BY-SA 4.0 text to `docs/oss/licenses/CC-BY-SA-4.0.txt` and identify
the copied material's separate licensing scope in `COPYRIGHT`.
- [ ] Add README links to the project grant and attribution document. Keep source
and notice links pinned to the adopted commit and reference examples upstream.
The library's selected license remains the project grant. A future executable
does not automatically acquire a different license merely because the upstream
policy has separate defaults for applications and libraries.
### 4. Define ReleaseKit's own release path
- [ ] Adopt the general versioning, compatibility, integrity, immutability, and
successful-tag-push completion rules.
- [ ] Select ReleaseKit's own deliverables explicitly. Supporting downstream
cross-builds does not by itself require ReleaseKit to distribute binaries.
- [ ] Add `docs/release.md` alongside the first working publication command,
documenting real prerequisites, authoritative remote, branch, validation,
publication, and failure recovery.
- [ ] Account for the first-release bootstrap before a released ReleaseKit tool
is available for its own publication.
- [ ] Add executable delivery declarations and target checks when corresponding
deliverables exist. Keep source completion and asynchronous delivery status
separate.
## Validation and completion
Review provenance, attribution scope, faithful license texts, template residue,
local links, whitespace, and consistency with implemented repository behavior.
Documentation-only adoption should use those checks; executable commands and
examples added later require appropriate execution checks.
Adoption is complete when the tailored policy set, contributor navigation,
documentation ownership, and licensing notices are coherent and verified. The
release-path stage may remain pending until publication is implemented; record
that status without documenting an unavailable command as usable.
Do not inherit PromptKit's or ConfigKit's extra release gates automatically.
Select additional checks only for identified ReleaseKit risks, recording their
justification in the adopted policy. Upstream synchronization, recurring license
approval, and CI polling are not additional adoption or release gates.

200
ideas.md Normal file
View File

@@ -0,0 +1,200 @@
# ReleaseKit: shared release process
This is a proposal based on a review of PromptKit, ConfigKit, Audita, Notarius,
Narratio, and Scriptorium on 2026-09-29. Product capabilities remain proposed.
The public facade architecture is accepted in
[ADR 0001](docs/adr/0001-public-facade.md), with implementation pending.
## Opportunity
All six repositories follow the same base release policy. Their release scripts
repeat stable-version parsing, candidate and release-note checks, local and
remote tag checks, validation of one captured commit, explicit Git publication,
and recovery guidance for uncertain pushes. They differ in publication approach,
tag type, validation commands, and artifact delivery. The mechanics of embedding
a release version and cross-building an application are further candidates for
standardization.
If the projects deliberately standardize publication approach and tag type,
ReleaseKit can own most of the repeated release-process code. The current six
repositories contain 942 lines of release shell scripts and 742 lines of
dedicated release tests, or 1,684 lines total. A directional estimate is that
roughly 1,000–1,250 lines could leave those repositories: about 450–500 lines
of publication scripts, 150–250 lines of checks and builders, and 400–500 lines
of repeated publication tests. ReleaseKit would gain its own implementation and
tests, so this is a reduction in local code, not a net line-count saving.
## Proposed publication standard
1. Prepare and commit a version-matched note at `docs/releases/<version>.md`.
2. Publish the candidate commit to authoritative `main` before invoking the
release command.
3. Run one command that checks the candidate and unused stable version, runs
that repository's offline candidate validation once, and rechecks mutable
state.
4. Create an annotated tag at the validated commit with a message derived from
the checked-in note. Push only that exact tag object to the authoritative
endpoint. A successful tag push completes the source release.
5. Treat hosted release pages and binary assets as asynchronous delivery with
their own status. Do not poll CI or require artifact availability to declare
the source release complete.
This would replace Audita's atomic branch-and-tag push, ConfigKit's and
Notarius's branch push during the release command, and Audita's, Notarius's,
and Narratio's lightweight tags. Those changes require updates to each
repository's adopted release policy and procedure; they are not merely code
cleanup.
## Architecture
[ADR 0001](docs/adr/0001-public-facade.md) owns the accepted facade decision.
The root `releasekit` package will provide the public facade for publication,
builds, and delivery. A separate public `buildinfo` package will provide the
lightweight facade for runtime version information.
Each facade owns its public named types and documented contracts. Public
operations, inputs, outcomes, errors, and callbacks express deliberately
selected consumer needs. Handles retain private implementation state; explicit
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. |
## First implementation slice
Pilot the common publication path with PromptKit and Scriptorium, which already
require `main` to equal the candidate and publish annotated tags. Develop the
pilot through the root `releasekit` facade, with implementation behind its
internal boundary and tests exercising the supported consumer contract. Their
different validation and remote configuration will test the proposed boundary.
Migrate the other projects after the shared Git contract is exercised against
temporary remotes. Add common delivery only after publication behavior is
settled, using one of the existing binary-publishing projects as the first
delivery pilot.
## Reviewed implementations
- [PromptKit release script](../promptkit/scripts/release.sh),
[checker](../promptkit/scripts/release-check.sh)
- [ConfigKit release script](../ConfigKit/scripts/release.sh),
[checker](../ConfigKit/scripts/release-check.sh)
- [Audita release script](../audita/scripts/release.sh),
[Woodpecker job](../audita/.woodpecker/release.yml)
- [Notarius release script](../notarius/scripts/release.sh),
[checker](../notarius/scripts/check-release-source.sh)
- [Narratio release script](../narratio/scripts/release.sh),
[Woodpecker job](../narratio/.woodpecker/release.yml)
- [Scriptorium release script](../scriptorium/scripts/release.sh),
[Woodpecker job](../scriptorium/.woodpecker/release.yml)