diff --git a/docs/adr/0001-public-facade.md b/docs/adr/0001-public-facade.md new file mode 100644 index 0000000..c506407 --- /dev/null +++ b/docs/adr/0001-public-facade.md @@ -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. diff --git a/docs/roadmap/policy.md b/docs/roadmap/policy.md new file mode 100644 index 0000000..5faae9c --- /dev/null +++ b/docs/roadmap/policy.md @@ -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. diff --git a/ideas.md b/ideas.md new file mode 100644 index 0000000..d3f77b8 --- /dev/null +++ b/ideas.md @@ -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/.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)