894 lines
52 KiB
Markdown
894 lines
52 KiB
Markdown
# ReleaseKit staged implementation plan
|
||
|
||
Status: Proposed. All stages are pending; this document introduces no implemented
|
||
features or published contracts.
|
||
|
||
Date: 2026-10-05
|
||
|
||
## Purpose and baseline
|
||
|
||
This plan gives a Luna coding agent bounded implementation assignments, in
|
||
execution order, for the target state in the [feature roadmap](releasekit.md).
|
||
The roadmap owns product intent and selected release conventions. This document
|
||
owns implementation steps, dependencies, acceptance evidence, and progress.
|
||
|
||
The baseline is ReleaseKit commit `6c2695f`. It contains documentation, adopted
|
||
policies, licensing notices, and [ADR 0001](../adr/0001-public-facade.md). It has
|
||
no Go module, packages, executable, tests, or publication automation. The
|
||
worktree was clean when this plan was prepared. The available tools were Go
|
||
1.26.8 on linux/amd64 and Git 2.53.0; these observations are not minimum support
|
||
requirements.
|
||
|
||
The six sibling checkouts provide design evidence; this implementation sequence
|
||
changes ReleaseKit only. Consumer migrations and measurement of deleted consumer
|
||
infrastructure belong to later adoption work. Consult code through the knowledge
|
||
graph, indexing a repository when necessary. Documents, scripts, and CI
|
||
configuration may be read directly under the discovery instructions.
|
||
|
||
## Scope and completion
|
||
|
||
Deliver the complete initial library, executable, configuration, shared build
|
||
and delivery mechanics, Woodpecker examples, and ReleaseKit's own prepared
|
||
release wiring. Hosted Gitea release pages are part of the selected standard
|
||
for every project, including source-only projects. Source-only delivery uses
|
||
the committed release note and zero binary assets; it needs no binary build or
|
||
checksum manifest.
|
||
|
||
Proceed through all stages using the local checkout and controlled fixtures.
|
||
No stage depends on an intermediate release, published tool/runtime pin,
|
||
consumer pilot, external service action, or human review. Ordinary automated
|
||
development checks accompany implementation under the testing policy.
|
||
|
||
After the complete initial implementation, the maintainer will review the
|
||
architecture, address discovered issues, and then choose the broader testing
|
||
plan and next steps. Consumer adoption, live integration testing, secret
|
||
provisioning, CI activation, and actual publication are outside this sequence.
|
||
|
||
## Execution instructions
|
||
|
||
- Implement stages in integer order. Each stage is one assignment; finish its
|
||
acceptance checks and local commit before starting the next stage. Record
|
||
completed work and evidence here. Read the stage's dependencies and relevant
|
||
decisions before editing.
|
||
- Start with the [developer guide](../development.md) and applicable policies.
|
||
Architecture, testing, documentation, and release policies apply to the core
|
||
implementation. Read the OSS policy when incorporating third-party material.
|
||
- Follow the [architecture policy](../policy/architecture.md) and ADR 0001.
|
||
Add exported declarations as their behavior is implemented, with GoDoc.
|
||
Implement the selected contract with proportionate internal collaborators.
|
||
- The design choices below specify this draft's recommended implementation.
|
||
Apply the selected scope and decisions throughout the sequence. Later
|
||
agent-authored code or tests provide evidence of implementation, rather than
|
||
independent authority to strengthen the contract.
|
||
- For each stage, run focused behavioral checks for its changed risks and the
|
||
established offline default suite. Reuse unchanged evidence where appropriate.
|
||
Stage acceptance checks are development checks; they add no publication gates
|
||
beyond the release policy and a repository's selected candidate checks.
|
||
- Use temporary repositories, local bare remotes, test programs, and local HTTP
|
||
servers. Keep fixtures independent of the six consumer checkouts and real
|
||
services. Temporary fixture module replacements may connect test programs to
|
||
this checkout; keep them outside committed production module configuration.
|
||
- Update canonical documentation with each implemented responsibility. Create
|
||
consumer, CLI, configuration, integration, and internal documents when they
|
||
have substantive content. Keep future behavior and stage progress here.
|
||
- Keep the implementation self-contained. Build the tool from this checkout;
|
||
temporary fixture modules may use local replacements. Integration templates
|
||
accept an operator-selected immutable tool pin for later deployment, without
|
||
requiring one to complete this plan or claiming that a release already exists.
|
||
- Complete stages with local implementation commits and controlled checks.
|
||
Prepare runnable automation and deployment instructions without activating
|
||
live workflows. Distinguish implemented integration from verified live use.
|
||
Finish the whole sequence before the maintainer's architecture review; add no
|
||
intermediate approval or external-action checkpoint.
|
||
|
||
## Implementation decisions
|
||
|
||
### Module, runtime access, and public ownership
|
||
|
||
Use one module, `gitea.maximumdirect.net/eric/ReleaseKit`, preserving the case
|
||
advertised by the forge's Go import metadata. Set the initial Go language
|
||
baseline to `go 1.25.0`, matching the oldest consumer module line. Use APIs
|
||
available on that baseline. Actual execution uses a prepared local toolchain
|
||
capable of building the consumer; Narratio already needs Go 1.26. There is no
|
||
mandatory multi-toolchain release matrix.
|
||
|
||
The supported packages are the root `releasekit` facade and `buildinfo`. Put
|
||
the executable at `cmd/releasekit`. Start with standard-library implementations
|
||
and the installed Git and Go executables. Introduce internal packages as their
|
||
responsibilities land: runtime information, process execution, release engine,
|
||
Git operations, builds, Gitea transport, and CLI adaptation. Put command-owned
|
||
parsing and file-format helpers under `cmd/releasekit/internal/cli`; keep facade
|
||
calls in the `cmd/releasekit` controller. Those helpers return their own settings
|
||
for explicit conversion by the controller and import neither public facade.
|
||
The executable therefore consumes the supported APIs while library internal
|
||
packages retain inward dependencies. There are no public transport clients or
|
||
assembly APIs.
|
||
|
||
The initial runtime API is `buildinfo.Version() string`. It returns an injected
|
||
release identifier when supplied; otherwise it uses the running executable's
|
||
main-module version when present and different from `(devel)`, then `dev` when
|
||
that metadata is unavailable. This supports source installs as well as explicit
|
||
release builds. It describes the application version, not the ReleaseKit
|
||
dependency version. [Go build information](https://pkg.go.dev/runtime/debug#ReadBuildInfo)
|
||
documents the main-module metadata available to implement this fallback.
|
||
|
||
Keep the injected string private in the internal runtime implementation. Keep
|
||
the linker symbol identifier in a small internal contract used by the builder.
|
||
The builder and runtime implementation change together. Consumers call the
|
||
accessor; their configuration supplies no linker symbol. The runtime import
|
||
graph uses the standard library and small runtime implementation, independent
|
||
of orchestration. A tool/runtime version mismatch must be exposed by the native
|
||
version smoke check; do not introduce an exact dependency-version equality gate.
|
||
|
||
### Orchestration API
|
||
|
||
Implement these root operations as the corresponding stages land:
|
||
|
||
| Operation | Request and result responsibilities |
|
||
| --- | --- |
|
||
| `Check(ctx, ReleaseRequest)` | Capture and validate a local committed candidate; return its commit and version. This diagnostic operation does not query publication services, create a tag, or claim remote readiness. |
|
||
| `Publish(ctx, ReleaseRequest)` | Validate once, check authoritative state, create and publish the selected annotated tag, and return publication state and retained recovery facts. |
|
||
| `Build(ctx, BuildRequest)` | Build the selected targets with the requested version, verify a native version invocation, and return source identity and generated artifacts. |
|
||
| `Deliver(ctx, DeliveryRequest)` | Verify tagged source and supplied artifact identity, create or find a hosted release, compare content, and add missing assets with explicit partial or uncertain outcomes. |
|
||
|
||
Each operation returns a facade-owned result and an error. Requests carry the
|
||
repository directory explicitly. `ReleaseRequest` also carries the display
|
||
product name, version, selected remote, optional validation callback, optional
|
||
build specification, and optional output writer. The remote defaults to
|
||
`origin`; publication uses `main`. The callback receives a facade-owned
|
||
`Candidate` containing directory, captured commit, and selected version. A nil
|
||
callback selects the default Go test invocation. A supplied callback replaces
|
||
that invocation with the repository's complete selected validation; it is
|
||
called once. The CLI adapts configured commands into this callback.
|
||
|
||
A build specification carries main package, binary name, and targets. Each
|
||
facade-owned `Target` contains Go OS and architecture strings. A build request
|
||
adds version and output directory. A delivery request identifies version,
|
||
product, repository directory, Gitea destination, and optional build result.
|
||
The Gitea destination carries base URL, owner, repository name, and caller-supplied
|
||
token. Library code obtains no credentials from ambient configuration. Before
|
||
the first remote write, an inspection failure reports that this invocation has
|
||
not attempted mutation. Reserve uncertain-mutation classification and exit 3
|
||
for writes whose authoritative outcome remains unresolved.
|
||
|
||
Publication results identify version, candidate commit, captured endpoint,
|
||
phase, and tag object when created. Use distinct states for `not_attempted`,
|
||
`not_published`, `published`, `conflict`, and `unknown`. These describe this
|
||
invocation; a pre-existing remote release is not described as unpublished.
|
||
Delivery results identify release URL and the matching, uploaded, pending, and
|
||
uncertain assets, with `not_attempted`, `complete`, `partial`, `conflict`, or
|
||
`unknown` state. Ordinary errors preserve useful causes through wrapping.
|
||
Document exactly when nonzero results accompany errors. Add only the named
|
||
types needed to represent these responsibilities; field spelling and internal
|
||
package layout can follow idiomatic Go without changing the decisions here.
|
||
|
||
### CLI and configuration
|
||
|
||
Use standard-library flag parsing and JSON configuration. The default profile
|
||
is `<repository>/releasekit.json`; `--config` selects another file. Configuration
|
||
paths are resolved from the selected repository, whose default is the current
|
||
directory. A profile supplies product identity, remote, candidate commands,
|
||
optional build specification, and optional Gitea delivery declaration. The
|
||
command supplies the version rather than persisting it in the profile.
|
||
|
||
Use this eventual profile shape; add each optional responsibility only when its
|
||
stage implements it:
|
||
|
||
```json
|
||
{
|
||
"product": "Scriptorium",
|
||
"remote": "origin",
|
||
"checks": [{"args": ["sh", "scripts/check-release-candidate.sh"]}],
|
||
"build": {
|
||
"main_package": "./cmd/scriptorium",
|
||
"binary_name": "scriptorium",
|
||
"targets": ["linux/amd64", "linux/arm64"]
|
||
},
|
||
"delivery": {
|
||
"base_url": "https://gitea.maximumdirect.net",
|
||
"owner": "eric",
|
||
"repository": "scriptorium",
|
||
"token_env": "GITEA_RELEASE_TOKEN"
|
||
}
|
||
}
|
||
```
|
||
|
||
This is a planned example, not an existing file or working invocation. Command
|
||
entries also accept an `env` map. Run argument vectors directly, without
|
||
implicit shell parsing. A repository that needs shell syntax selects `sh` or
|
||
`bash` explicitly. Run commands in the captured repository directory and supply
|
||
`RELEASEKIT_VERSION` and `RELEASEKIT_CANDIDATE` for candidate-specific checks.
|
||
Those captured values take precedence over conflicting command environment
|
||
entries. Inherit the remaining environment and overlay explicitly owned values
|
||
without mutating the caller process.
|
||
|
||
An absent or empty check list selects `go test -count=1 ./...`; a nonempty list
|
||
is the repository's complete validation sequence. Decode profile fields
|
||
strictly so unsupported build or delivery settings cannot silently disappear
|
||
from the selected checks. Validate required values for the operation being run;
|
||
source-only checks and publication need neither a main package nor credentials.
|
||
Hosted page delivery requires a destination and token for source-only profiles
|
||
as well as binary-producing profiles.
|
||
The CLI loads the token named by `delivery.token_env`, defaulting to
|
||
`RELEASEKIT_GITEA_TOKEN`, only for delivery. Build receipt identity covers resolved
|
||
build inputs; delivery credentials, destination, and unrelated check settings
|
||
are excluded from that identity.
|
||
|
||
The eventual command forms are:
|
||
|
||
```text
|
||
releasekit version
|
||
releasekit check [--repo DIR] [--config FILE] VERSION
|
||
releasekit publish [--repo DIR] [--config FILE] VERSION
|
||
releasekit build [--repo DIR] [--config FILE] [--output DIR] VERSION
|
||
releasekit deliver [--repo DIR] [--config FILE] [--assets DIR] VERSION
|
||
```
|
||
|
||
Flags precede the positional version. `version` prints `releasekit <version>`.
|
||
Use exit 0 for completed success, 1 for a known failure or conflict, 2 for usage
|
||
or invalid configuration, and 3 for an uncertain remote mutation. Library
|
||
outcomes retain more detail. Send validation progress and diagnostics to stderr;
|
||
stdout presents the command's final result. Signals cancel the operation through
|
||
its context. There is no interactive confirmation loop.
|
||
|
||
### Candidate checks and Git publication
|
||
|
||
Parse stable versions using the roadmap's exact syntax. Validate decimal
|
||
components as strings without introducing a machine-integer size limit. Read
|
||
the release note from the captured commit at `docs/releases/<version>.md`. Its
|
||
first line must be a Markdown H1 containing the configured product name and
|
||
exact version as complete title elements. Preserve the note content for the annotated tag.
|
||
The shared tool imposes no additional note section inventory.
|
||
|
||
`Check` checks a clean worktree and index on `main`, captures HEAD, checks the
|
||
committed note and unused local version, runs validation and selected builds,
|
||
and rechecks local mutable state. `Publish` adds authoritative endpoint and
|
||
remote branch/tag checks before validation and immediately before publication.
|
||
Cleanliness includes untracked files; ordinary Git ignore rules still apply.
|
||
Snapshot resolved request values and CLI profile content for the invocation so
|
||
configuration is not reloaded into different checks midway through the run.
|
||
Source changes remain subject to the ordinary candidate rechecks. The Go API
|
||
accepts caller-supplied values without requiring a particular profile file.
|
||
|
||
Resolve the selected remote's effective push destination using Git's own remote
|
||
configuration semantics. A missing destination or more than one distinct
|
||
effective publication destination is an actionable configuration error because
|
||
this operation needs one authoritative endpoint. A separate fetch URL is
|
||
supported. Inspect the publication endpoint,
|
||
rather than assuming the selected remote's normal fetch URL is authoritative.
|
||
|
||
Use a transient, command-scoped named remote with explicit read and push URLs
|
||
and mirror mode disabled. Verify that both effective URLs equal the captured
|
||
publication destination after Git URL rewriting. Reuse that scoped remote for
|
||
queries and push; persist no new remote configuration. Retain normal Git
|
||
credential and transport settings, carrying remote-specific transport settings
|
||
into the scoped remote as needed. If rewriting prevents establishing this
|
||
equality, report the unresolved destination before mutation.
|
||
Recheck the original remote resolution before pushing. Test ordinary
|
||
`insteadOf`, `pushInsteadOf`, and explicit push-URL cases using local remotes.
|
||
[Git's remote documentation](https://git-scm.com/docs/git-remote) describes the
|
||
effective URL lookup on which this relies.
|
||
|
||
Use exact ref names in remote queries. Require authoritative `main` to equal
|
||
the captured candidate and the selected tag to be absent. Create the annotated
|
||
tag from the committed note with signing disabled for this invocation, inspect
|
||
its object type and peeled commit, then freeze its object ID. Push
|
||
`<tag-object-id>:refs/tags/<version>` with automatic following of tags and mirror
|
||
defaults disabled. Publish no branch updates, unrelated tags, or force updates.
|
||
|
||
A successful push returns immediately as published. After a failed or ambiguous
|
||
push, make one authoritative tag query: an exact object match confirms success;
|
||
absence confirms not published at observation; a different object is conflict;
|
||
an unavailable or uninterpretable answer is unknown. Retain the local tag in
|
||
all failed or uncertain cases. Caller cancellation after push starts also needs
|
||
this bounded reconciliation attempt using a separate finite recovery context.
|
||
Use a 30-second recovery deadline as an operational default, not a guarantee
|
||
that the endpoint will respond. Cancellation before push does not initiate it.
|
||
|
||
The last remote branch check is an observation before the tag push. This design
|
||
provides no transaction locking remote `main` against concurrent writers. Do not
|
||
add branch pushes or assert stronger concurrency guarantees in tests.
|
||
|
||
### Go execution, builds, and assets
|
||
|
||
For ReleaseKit-owned candidate Go commands and builds, use prepared dependencies
|
||
and a local toolchain. Set child environment values `GOWORK=off`, `GOPROXY=off`,
|
||
`GONOPROXY=none`, `GOSUMDB=off`, and `GOTOOLCHAIN=local`. Set module mode to
|
||
readonly, preserving a repository's deliberately selected testing flags.
|
||
ReleaseKit-owned builds replace ambient `GOFLAGS` with `-mod=readonly` and use
|
||
their explicit build arguments; custom validation commands retain their
|
||
deliberately configured flags.
|
||
Preparation of caches and tools occurs separately. These settings do not
|
||
sandbox arbitrary repository callbacks or establish a general network firewall.
|
||
|
||
Build with `CGO_ENABLED=0`, `-mod=readonly`, `-trimpath`, `-buildvcs=false`, and
|
||
linker flags `-s -w` plus the shared version injection. This selects the pure-Go
|
||
build convention already used by the current asset builders. Additional cgo
|
||
toolchains remain outside this implementation. The injected string must use a
|
||
linker-supported initializer. [Go's linker documentation](https://pkg.go.dev/cmd/link)
|
||
defines the `-X` mechanism.
|
||
|
||
Initially verify linux/amd64, linux/arm64, and darwin/arm64. Represent targets
|
||
as values rather than a closed enum; other explicit Go OS/architecture pairs
|
||
can be passed to the prepared compiler and may fail according to its support.
|
||
This establishes no native-execution promise for other platforms.
|
||
|
||
Asset names are `<binary-name>-<version>-<goos>-<goarch>`, with `.exe` when the
|
||
requested Go OS needs it. Binary names and target components must be nonempty
|
||
filename components without path separators, backslashes, CR, or LF, so names
|
||
stay in the output directory and have unambiguous manifest entries. Normalize
|
||
repeated target pairs to one target, retaining their first position. Distinguish
|
||
the display product name used by notes from this binary name. Preserve selected
|
||
target order for progress; generate manifests in filename order.
|
||
|
||
For nonempty targets, invoke the built application with `version` and require
|
||
`<binary-name> <version>` plus a final newline. Reuse a target artifact when it
|
||
matches `GOHOSTOS/GOHOSTARCH`; otherwise build a temporary native executable
|
||
with the same flags and injection. Determine the host independently of ambient
|
||
`GOOS/GOARCH`. Native smoke binaries are temporary and are not extra assets.
|
||
An empty target set performs no Go build or version invocation. Source-only
|
||
projects may retain an explicitly selected application check; no binary check
|
||
is silently imposed on them.
|
||
|
||
Candidate build checks use a temporary output directory outside the worktree
|
||
and discard it. Artifact builds record the commit, version, main package,
|
||
targets, compiler identity, and generated filename/hash pairs. Return these
|
||
facts in `BuildResult`. The CLI persists them in an internal local receipt,
|
||
`.releasekit-build.json`, alongside the generated files after a successful
|
||
complete build and smoke check. A partial build returns progress without a
|
||
receipt authorizing delivery. This receipt is for tool coordination, with no
|
||
public format or attestation promise. It contains no credentials and is not a
|
||
hosted asset. Build output defaults to `dist`; create
|
||
the directory as needed, regenerate owned local filenames, and preserve
|
||
unrelated files. Report generated files on partial local failure and clean up
|
||
only tool-owned temporary paths.
|
||
|
||
Delivery uses a clean checkout whose HEAD equals the selected local tag's peeled
|
||
commit; detached HEAD is valid. Read the note from that commit and snapshot the
|
||
selected settings. Repository-local tracked profiles come from that source;
|
||
explicit caller-supplied settings are identified in the build result. Verify
|
||
the hosted repository already has the same tag targeting that commit before
|
||
creating a release. Existing lightweight historical tags may be inspected and
|
||
delivered; new publication follows the annotated-tag standard. Do not require
|
||
current remote `main` to remain at an old release commit during delivery retries.
|
||
|
||
The CLI's `deliver --assets DIR` consumes a verified build receipt and checks
|
||
file hashes, selected filenames, and source/build-input identity before remote
|
||
mutation. When no directory is supplied, build into a temporary directory from
|
||
the tagged checkout and then deliver. With zero targets, hosted delivery creates
|
||
the required note page without a binary manifest or build receipt. An absent
|
||
build specification is equivalent to zero targets for source-only delivery.
|
||
Neither mode reruns the candidate test suite or changes source-publication status.
|
||
|
||
### Hosted delivery and Woodpecker
|
||
|
||
Use a small internal standard-library Gitea REST client. On 2026-10-05 the host's
|
||
[version endpoint](https://gitea.maximumdirect.net/api/v1/version) reported Gitea
|
||
1.25.4; its [served API schema](https://gitea.maximumdirect.net/swagger.v1.json)
|
||
confirmed release-by-tag lookup, JSON release creation, asset listing, and
|
||
multipart attachment upload. Implement the operations described here against
|
||
local fixtures and record them in the integration guide. Verification against
|
||
the deployed host belongs to later live testing and is not needed to complete
|
||
this sequence. This observation is not an all-version Gitea compatibility promise.
|
||
|
||
Read the tag through `/api/v1/repos/{owner}/{repo}/tags/{tag}` and verify its
|
||
`commit.sha`. Use the repository API prefix for each following operation. Find
|
||
the release through `/releases/tags/{tag}`. Create it through
|
||
`POST /releases` with the existing tag, captured commit, title from the note's
|
||
H1, complete committed note as body, and stable, non-draft settings. A found
|
||
release must match the note body and tag and be stable/non-draft; retain its
|
||
existing title, including a historical title chosen by other tooling. A content
|
||
conflict is reported; its body, title, assets, and tag are not edited. Existing
|
||
historical pages remain intact.
|
||
|
||
List attachments through `/releases/{id}/assets`. Identify expected filenames
|
||
and stream their downloads to compare SHA-256 content; size and name alone do
|
||
not prove a match. Use multipart field `attachment` and the filename query
|
||
parameter for missing uploads. The client obtains no automatic tag-creation
|
||
responsibility. Keep authorization on the configured service origin when
|
||
following attachment URLs or redirects; never include token values in receipts
|
||
or diagnostics.
|
||
|
||
For nonempty binary sets, generate `SHA256SUMS` with lowercase hashes, two spaces,
|
||
filenames, and LF-terminated lines, sorted by filename. It describes the complete
|
||
declared binary set and excludes itself and the private receipt. An existing
|
||
manifest must match that expected content. Fill missing assets covered by that
|
||
manifest only with matching bytes. A manifest describing a different or smaller
|
||
set cannot be replaced to extend that version. Leave unrelated attachments
|
||
untouched.
|
||
|
||
Inspect all known declared asset and manifest conflicts before uploading.
|
||
Upload missing binaries in filename order, then a missing manifest. HTTP writes
|
||
are separate operations. After an ambiguous creation or upload, re-read the
|
||
affected resource once; classify matching content as completed, conflict as
|
||
conflict, and inaccessible or still-indeterminate content as unknown. Handle
|
||
concurrent creation by finding and verifying the created resource. Do not
|
||
blindly retry writes or delete attachments. Record partial progress so an
|
||
operator can rerun against the same tag and retained assets.
|
||
|
||
Woodpecker uses tag-triggered repository-local YAML with a pinned Go image
|
||
and an explicit tool preparation step. Provide source-only and binary-producing
|
||
examples. Consumer templates accept an operator-selected immutable ReleaseKit
|
||
source pin for deployment; template preparation and local execution use the
|
||
implementation checkout and require no published pin. Prepare consumer module
|
||
caches separately, then run builds and delivery with offline Go execution.
|
||
This honors ReleaseKit's source-only distribution; a dedicated ReleaseKit
|
||
container image would require a separate deliverable decision. Use a Go image
|
||
that satisfies the repository's module requirements.
|
||
|
||
ReleaseKit's own prepared workflow builds `cmd/releasekit` from its tagged
|
||
checkout using the local toolchain and delivers its hosted note page. It needs
|
||
no previously released executable and produces no distributed binary assets.
|
||
Prepare this workflow locally; activation belongs to the later next-step plan.
|
||
|
||
Use `CI_COMMIT_TAG` and `CI_COMMIT_SHA` to bind the workflow to tagged source,
|
||
with an explicit tag fetch when the checkout omits local tag refs. For binary
|
||
profiles, the build step writes to ignored `dist` and the delivery step shares
|
||
the workspace. Source-only workflows call delivery directly without an artifact
|
||
build step. Supply the Gitea token only to delivery from a Woodpecker secret.
|
||
Document operator substitution of image digests and source pins for eventual
|
||
consumer deployment. Pipeline preparation may access dependency services;
|
||
candidate validation and builds use the prepared caches. CI does not call
|
||
`publish`, push refs, or gate source completion. The supported retry is a rerun
|
||
from the same tagged source with content comparison; reproducible bytes across
|
||
arbitrary toolchains or elapsed time are not promised.
|
||
|
||
## Stage 1: Establish the Go module and development baseline
|
||
|
||
Status: Complete. Depends on the baseline only.
|
||
|
||
1. Add the module with the selected path and language baseline. Add root package
|
||
documentation for the orchestration facade and establish ordinary Go layout.
|
||
2. Keep committed module state independent of sibling checkouts and workspaces.
|
||
Start without external runtime dependencies or a generic plugin framework.
|
||
3. Add the working offline developer test command and initial implemented-package
|
||
inventory. Update README to its actual implemented boundary.
|
||
|
||
Acceptance: module loading and `go test ./...` work with the prepared local
|
||
toolchain and network disabled. Review module identity and licensing notices.
|
||
Do not invent tests merely to assert that an empty package exists.
|
||
|
||
Completion evidence: added the Go 1.25.0 module and documented root facade,
|
||
updated the README and developer guide to describe the implemented boundary and
|
||
offline test command, and confirmed module loading and `go test ./...` with
|
||
network access disabled. The module has no external dependencies; existing
|
||
LGPL licensing and attribution notices remain consistent.
|
||
|
||
## Stage 2: Implement the runtime version facade
|
||
|
||
Status: Complete. Depends on Stage 1.
|
||
|
||
1. Implement `buildinfo.Version` and its internal injected-string and main-module
|
||
fallback logic. Keep runtime state private and the public accessor small.
|
||
2. Add the internal injection-symbol contract without importing orchestration
|
||
into the runtime path. Test injected precedence and missing/development
|
||
metadata through stable internal seams and a small executable fixture.
|
||
3. Add `cmd/releasekit` with a working `version` command using this accessor.
|
||
Record its implemented command contract and the runtime consumer guidance.
|
||
|
||
Acceptance: a source-built fixture reports `dev`; a fixture linked with a stable
|
||
version reports that version. Fallback metadata uses the executable main module.
|
||
CLI version output is correct and no runtime orchestration initialization occurs.
|
||
|
||
Completion evidence: implemented `buildinfo.Version`, its private injected
|
||
string and main-module fallback, and the internal linker-symbol contract. Unit
|
||
tests cover injection precedence and missing, development, and stable main
|
||
module metadata. Built and ran a separate consumer executable from source and
|
||
with an injected stable version using network-disabled Go commands; both
|
||
reported the selected value. CLI tests cover its version line, and the CLI's
|
||
dependency closure contains no orchestration package. Added runtime consumer
|
||
guidance and documented the command contract.
|
||
|
||
## Stage 3: Protect public contracts and dependency direction
|
||
|
||
Status: Complete. Depends on Stage 2.
|
||
|
||
1. Add a compiled-export boundary checker for both facades using `go list` export
|
||
data and `go/types`. Inspect exported signatures, fields, callbacks, aliases,
|
||
generic constraints, and pointer/value method sets recursively.
|
||
2. Assert facade ownership of public named types. Permit deliberately selected
|
||
standard-library contracts. Protect the private-state forwarding approach
|
||
against leaked internal types and promoted implementation methods.
|
||
3. Add import-direction checks: internal production packages use internal
|
||
contracts; the runtime facade's dependency closure excludes orchestration,
|
||
process, build, Gitea, and CLI implementation.
|
||
4. Use small negative fixtures proving that the checker catches a leaked type
|
||
and promoted internal method, and accepts a private implementation field.
|
||
|
||
Acceptance: actual exports pass and meaningful negative fixtures fail. Tests
|
||
inspect contracts and imports, with no fixed inventory of future internal
|
||
package names. Extend these checks as subsequent exports land.
|
||
|
||
Completion evidence: added `internal/apicheck`, which loads compiled exports
|
||
from `go list -deps -export` and inspects signatures, fields, aliases, generic
|
||
constraints, and pointer and value method sets through `go/types`. The checks
|
||
pass for both public facades and enforce internal import direction and the
|
||
runtime dependency boundary. Negative fixtures detect leaked types, aliases,
|
||
callbacks, generic constraints, and promoted implementation methods; a private
|
||
implementation field and selected standard-library contracts pass. Added the
|
||
checker command and internal package inventory to the canonical developer docs.
|
||
|
||
## Stage 4: Implement profile loading and command adaptation
|
||
|
||
Status: Complete. Depends on Stage 3.
|
||
|
||
1. Implement JSON product, remote, and command settings under an internal CLI
|
||
adapter. Add strict field decoding, directory resolution, required-value
|
||
diagnostics, direct argument execution, and per-child environment overlays.
|
||
2. Implement an internal cancellable process runner with directory, argument,
|
||
environment, stdout, stderr, and exit-error support. Use `os/exec` directly;
|
||
expose no public runner abstraction solely for test assembly.
|
||
3. Add CLI parsing infrastructure and selected usage/exit behavior. Advertise
|
||
commands only when their following stages implement them.
|
||
4. Add small profile examples and `docs/config.md` for implemented fields.
|
||
|
||
Acceptance: paths and arguments containing spaces remain separate arguments;
|
||
selected environment values reach the child without changing process globals;
|
||
missing executables, invalid configuration, and cancellation report useful
|
||
errors. Tests use controlled local child commands and temporary directories.
|
||
|
||
Completion evidence: added strict profile decoding, repository-relative and
|
||
absolute profile resolution, required product and command diagnostics, and
|
||
direct check argument adaptation. Added a cancellable `os/exec` runner with
|
||
working directory, inherited environment plus child-only overlays, stdout,
|
||
stderr, and preserved exit errors. Tests cover spaced paths and arguments,
|
||
environment isolation, missing executables, exit failures, cancellation, and
|
||
invalid profiles. Added a profile example and canonical CLI, configuration, and
|
||
internal package documentation.
|
||
|
||
## Stage 5: Capture and validate one local candidate
|
||
|
||
Status: Complete. Depends on Stage 4.
|
||
|
||
1. Implement stable-version syntax, clean `main` and index/worktree inspection,
|
||
captured HEAD, profile snapshot/note loading, title identity, and unused local
|
||
tag checks. Preserve immutable candidate facts in internal values.
|
||
2. Implement default offline Go validation and callback adaptation. Bind the
|
||
callback to the captured candidate; pass captured version/commit to CLI
|
||
commands. Call the complete validation callback once, stopping on failure.
|
||
3. Recheck branch, HEAD, and cleanliness after validation. Retain the captured
|
||
settings throughout the operation. Implement the public `Check` operation
|
||
and local diagnostic `check` command.
|
||
4. Document that local check success provides local candidate evidence and makes
|
||
no authoritative remote availability claim.
|
||
|
||
Acceptance: temporary repositories cover invalid versions, missing or mismatched
|
||
committed notes, dirty tracked/untracked state, existing local tags, detached
|
||
HEAD, callback failure, and a callback changing HEAD or the worktree. Count a
|
||
successful callback once. No test needs a publication service, and `Check`
|
||
creates no tag or remote ref.
|
||
|
||
Evidence: `internal/candidate` validates stable versions and committed note
|
||
identity, captures a clean `main` commit, runs either the offline default or the
|
||
configured callback once, and rechecks local mutable state. The public
|
||
`releasekit.Check` API and `releasekit check` command provide the local
|
||
diagnostic. Temporary Git repositories cover the acceptance cases; offline
|
||
`go test -count=1 ./...` and `go run ./internal/apicheck` pass.
|
||
|
||
## Stage 6: Resolve and inspect the authoritative endpoint
|
||
|
||
Status: Complete. Depends on Stage 5.
|
||
|
||
1. Implement effective push-URL capture, command-scoped remote configuration,
|
||
matching read/write resolution, and endpoint rechecks described above.
|
||
2. Implement exact remote main/tag queries with distinct present, absent,
|
||
conflicting, and unreadable results. Query failures never establish absence.
|
||
3. Compose the pre-validation and pre-publication remote guards around captured
|
||
local state. Require main equality, rather than ancestor-only publication.
|
||
|
||
Acceptance: local remotes cover a separate fetch endpoint, explicit push URL,
|
||
ordinary URL rewrites, multiple push destinations, missing main, conflicting
|
||
tags, main ahead/behind the candidate, endpoint changes, and remote failure.
|
||
Queries leave local tags, persistent remote configuration, and remote refs intact.
|
||
|
||
Evidence: `internal/git` captures effective push destinations through Git,
|
||
rejects multiple distinct destinations, verifies scoped URL rewriting, and
|
||
queries exact main and version-tag refs. A candidate check requires main to
|
||
equal the captured commit and the version tag to be absent. Local bare remote
|
||
tests cover separate fetch/push URLs, both rewrite modes, missing main, an
|
||
existing tag, main ahead and behind, endpoint changes, unreadable remote state,
|
||
and unchanged persistent config and refs.
|
||
|
||
## Stage 7: Create and push the exact annotated tag
|
||
|
||
Status: Pending. Depends on Stage 6.
|
||
|
||
1. Implement public publication request/result values and explicit conversion
|
||
into the engine. Validate once between the complete preflight guards.
|
||
2. Create an annotated tag from captured note bytes, inspect type and peeled
|
||
commit, capture its object ID, and push only that object to the selected ref.
|
||
3. Recheck mutable local state and endpoint before the push, including the local
|
||
tag still identifying the captured object. A changed local tag prevents this
|
||
invocation from proceeding; cleanup remains deliberate recovery.
|
||
4. On successful push, return published state without another remote read-back.
|
||
On push error, retain the tag and return unknown until Stage 8 adds resolution.
|
||
|
||
Acceptance: a local bare remote receives the expected tag object and commit;
|
||
its main and unrelated refs do not change. Follow-tags and mirror configuration
|
||
cannot expand publication. Note content, tag identity, and one-validation
|
||
behavior are checked through the public facade. Restrict stage trials to test
|
||
remotes while failure handling is incomplete.
|
||
|
||
## Stage 8: Resolve rejected and ambiguous publication
|
||
|
||
Status: Pending. Depends on Stage 7.
|
||
|
||
1. Add the one-query reconciliation path and public error/result classification.
|
||
Preserve the original push cause when resolution does not confirm success.
|
||
2. Give post-push cancellation a separate bounded recovery context. Retain local
|
||
tag/object facts when the read fails or recovery is cancelled/times out.
|
||
3. Add real rejected-push tests using bare-remote hooks. Simulate lost
|
||
acknowledgments at the subprocess boundary after a real successful push.
|
||
Separate known absence, conflicting object, and unavailable-query cases.
|
||
4. Cover a remote branch change during validation and local tag mutation before
|
||
push. Keep concurrency promises at the observed guard boundaries.
|
||
|
||
Acceptance: exact remote object match succeeds despite a reported push error;
|
||
missing, conflicting, and unavailable remote tags have distinct outcomes. Failed
|
||
pushes issue one reconciliation query, successful pushes issue none, and no
|
||
recovery force-update or automatic tag deletion occurs.
|
||
|
||
## Stage 9: Expose publication and document ReleaseKit bootstrap
|
||
|
||
Status: Pending. Depends on Stage 8.
|
||
|
||
1. Wire the complete public publisher into `releasekit publish`, with selected
|
||
progress output, final result, exit classification, and signal cancellation.
|
||
2. Add ReleaseKit's source-only profile and one short maintainer entry script.
|
||
The profile selects the offline default suite; no inherited race, lint,
|
||
documentation, image, or platform gates are added.
|
||
3. Create `docs/release.md` now that publication works. Name authoritative
|
||
`origin` at the ReleaseKit forge URL and `main`; document preparation,
|
||
successful-push completion, exact-object recovery, local tag retention, and
|
||
deliberate cleanup of proven unpublished tags.
|
||
4. Document first-release execution from the captured candidate source using
|
||
the local Go toolchain, before a released ReleaseKit executable exists.
|
||
Include immutable release-note licensing footer construction and dependency
|
||
preparation separate from validation.
|
||
5. Update current development documentation and the policy-adoption roadmap's
|
||
bootstrap status to the implemented boundary. Preserve pending deliverables.
|
||
|
||
Acceptance: an end-to-end CLI fixture publishes to a local remote through a
|
||
committed profile and returns the expected exit/output behavior. Runbook commands
|
||
are exercised using a temporary repository configured like ReleaseKit. This
|
||
stage prepares self-publication and does not publish a real ReleaseKit release.
|
||
|
||
## Stage 10: Build selected targets with shared version injection
|
||
|
||
Status: Pending. Depends on Stage 9.
|
||
|
||
1. Implement public build values and internal build execution using the fixed
|
||
flags, version symbol, filename convention, and prepared Go environment.
|
||
2. Build explicit OS/architecture pairs and verify the application `version`
|
||
invocation using a host artifact or temporary native build. Preserve
|
||
caller-owned output files and report partial generated output on failure.
|
||
3. Return source identity, artifact paths, filenames, targets, hashes, and
|
||
compiler identity. Keep byte reproducibility and cross-platform execution
|
||
promises outside the result contract.
|
||
4. Add a temporary consumer module importing only `buildinfo` and exposing the
|
||
canonical version command. Its local replacement is fixture-only.
|
||
|
||
Acceptance: the fixture compiles for the three initial targets and a native
|
||
execution reports the injected version. An ambient cross-target environment
|
||
does not select the smoke host. Empty targets perform no build or smoke test;
|
||
missing main packages, build failures, incorrect version output, unsafe binary
|
||
names, and cancellation fail with useful partial results.
|
||
|
||
## Stage 11: Integrate candidate builds and artifact command
|
||
|
||
Status: Pending. Depends on Stage 10.
|
||
|
||
1. Add build profile fields, CLI `build`, and the private artifact receipt.
|
||
Bind receipts to source/build-input identity and generated hashes. Validate
|
||
data on reload; leave the receipt outside the public API and hosted asset list.
|
||
2. Integrate selected builds into `Check` and `Publish` after the single candidate
|
||
callback. Use disposable output outside the worktree for candidate builds.
|
||
Recheck mutable state after the entire validation/build sequence.
|
||
3. Add ignored `dist` output and source-only and binary-producing examples.
|
||
Document the API, profile target selection, version invocation, and build
|
||
output ownership.
|
||
4. Refresh facade and runtime dependency checks with the new values.
|
||
|
||
Acceptance: source-only publication invokes no builder; selected build failure
|
||
prevents tag creation; selected builds inject the exact candidate version;
|
||
callback validation runs once. CLI artifacts and receipts agree on filenames,
|
||
hashes, commit, and resolved build settings. Generated validation output does
|
||
not dirty the candidate. Continue directly to hosted delivery implementation;
|
||
no publication or consumer pilot is needed to complete this stage.
|
||
|
||
## Stage 12: Find and create Gitea release pages
|
||
|
||
Status: Pending. Depends on Stage 11.
|
||
|
||
1. Implement the small internal REST transport using the served API operations,
|
||
explicit destination/token inputs, context cancellation, and structured
|
||
failure mapping. Use standard HTTP and JSON packages.
|
||
2. Verify the hosted tag targets the captured source commit. Find a release by
|
||
tag, create a missing stable release from the committed note, and verify
|
||
existing note body/tag and stable, non-draft state before accepting it.
|
||
Preserve the existing title.
|
||
3. Handle create conflicts and lost acknowledgments by one lookup and content
|
||
comparison. Never edit the existing page to make it match.
|
||
4. Add `docs/integrations/gitea.md` describing implemented API interactions,
|
||
credential supply, source binding, and failure semantics.
|
||
|
||
Acceptance: `httptest.Server` covers found, missing, created, conflicting,
|
||
unauthorized, malformed, cancelled, and ambiguous-create results. A missing or
|
||
wrong hosted tag prevents page creation. Existing historical titles are retained
|
||
when the body, tag, and release state match. Fixture assertions cover actual
|
||
request paths, payloads, and authentication without using real credentials.
|
||
|
||
## Stage 13: Inspect asset and manifest content
|
||
|
||
Status: Pending. Depends on Stage 12.
|
||
|
||
1. Generate the expected binary manifest from the declared build result. Add
|
||
attachment discovery and streaming hash comparison of existing assets.
|
||
2. Compare existing `SHA256SUMS` with the complete declared manifest. Identify
|
||
matching, missing, conflicting, and unverifiable content separately.
|
||
3. Inspect all declared conflicts before any upload and preserve unrelated
|
||
attachments. Keep download authorization confined to the configured origin.
|
||
|
||
Acceptance: equal name/size with different bytes is conflict; matching content
|
||
is accepted; a full matching manifest can cover missing binaries; a subset or
|
||
different manifest is conflict; unreadable content is not treated as missing.
|
||
Source-only delivery generates no binary manifest. No write endpoint is used
|
||
by inspection tests.
|
||
|
||
## Stage 14: Upload missing assets and report partial delivery
|
||
|
||
Status: Pending. Depends on Stage 13.
|
||
|
||
1. Add multipart uploads for missing binaries in filename order, followed by a
|
||
missing manifest. Record completed work and pending asset identities.
|
||
2. Resolve ambiguous uploads by one resource read and content comparison.
|
||
Handle concurrent matching uploads without creating replacement content;
|
||
detect duplicate/conflicting attachment names as conflicts.
|
||
3. Implement rerun behavior that accepts existing matching files, adds missing
|
||
ones, and stops on conflicts. Preserve every existing page and attachment.
|
||
4. Add tests where the first binary succeeds, a later write fails, and a second
|
||
invocation completes only the missing assets; also cover a lost manifest
|
||
acknowledgment and failure of the reconciliation read.
|
||
|
||
Acceptance: partial and unknown outcomes retain exact completed/pending facts;
|
||
retries do not overwrite or delete content; no blind repeat POST is made after
|
||
an ambiguous acknowledgment. Published manifest constraints hold across reruns.
|
||
|
||
## Stage 15: Expose tagged-source delivery through the facade and CLI
|
||
|
||
Status: Pending. Depends on Stage 14.
|
||
|
||
1. Implement `Deliver`, its facade-owned request/result values, and conversions.
|
||
Bind source note, resolved build settings, tag commit, and hosted repository
|
||
as specified.
|
||
2. Add delivery profile fields, environment-name token loading in the CLI, and
|
||
`deliver` with an optional verified assets directory. With no directory,
|
||
build declared tagged-source artifacts in temporary storage before uploading.
|
||
An absent build specification or empty target set selects page-only delivery:
|
||
skip the builder, native smoke, receipt requirement, and manifest generation.
|
||
3. Accept detached tagged checkouts and historical tags without requiring old
|
||
remote main equality. Verify receipt identity/hashes before supplied assets
|
||
are used. Keep the library credential input explicit.
|
||
4. Add current consumer/CLI guidance for source status, delivery status,
|
||
retained assets, and retry commands. Extend public boundary checks.
|
||
|
||
Acceptance: facade/CLI fixtures cover both page-only delivery and binary delivery
|
||
from tagged source to a local server, with content-aware reruns. Page-only
|
||
delivery preserves the complete note, needs no main package or receipt, and
|
||
performs no Go build or asset upload. Wrong commit, stale build settings or
|
||
receipt, tampered assets, and missing credentials prevent relevant writes.
|
||
Candidate tests and Git publication are not run by delivery. A delivery failure
|
||
leaves the existing source tag intact and reports only delivery failure.
|
||
|
||
## Stage 16: Provide the common Woodpecker integration
|
||
|
||
Status: Pending. Depends on Stage 15.
|
||
|
||
1. Add maintained source-only and binary-producing tag-pipeline templates.
|
||
Parameterize the Go image and immutable ReleaseKit source pin for eventual
|
||
deployment, with explicit installation/cache preparation and a delivery
|
||
secret. Document required template inputs rather than inventing a published
|
||
pin. Create no new ReleaseKit image-distribution requirement.
|
||
2. Bind tag and commit variables to the checkout, fetch the selected tag when
|
||
necessary, share generated assets between steps, and keep output ignored.
|
||
3. Document preparation versus offline execution, asynchronous status, and
|
||
rerunning the same immutable tag. The source-only template delivers the note
|
||
page directly; the binary template first builds its declared assets. Neither
|
||
workflow executes the publisher.
|
||
4. Exercise both command sequences locally with a tool built from this checkout,
|
||
fixture repositories, and a local server. Substitute the local executable for
|
||
the templates' installation step during development checks. Check YAML using
|
||
locally available tooling or static inspection; no live runner or new
|
||
external validator is needed to complete the stage.
|
||
|
||
Acceptance: the maintained commands reach common delivery for both profile types
|
||
and common builds for declared binary targets, with the selected version and
|
||
commit. Secrets enter only the delivery step. Agent-performed static YAML checks
|
||
and local command evidence are recorded accurately. No published tool pin,
|
||
container pull, configured secret, or live runner is a stage prerequisite.
|
||
CI success is not a source release gate. The
|
||
[Woodpecker environment reference](https://woodpecker-ci.org/docs/usage/environment)
|
||
owns the runner-provided tag and commit variables.
|
||
|
||
## Stage 17: Prepare ReleaseKit's own hosted-page delivery
|
||
|
||
Status: Pending. Depends on Stage 16.
|
||
|
||
1. Extend ReleaseKit's source-only profile with its Gitea destination and token
|
||
environment name. Keep the build target set empty and retain its selected
|
||
offline candidate suite. No distributed executable or binary manifest is
|
||
introduced.
|
||
2. Prepare a repository-specific Woodpecker workflow under the maintained
|
||
examples, using the tagged checkout to build the local release tool and call
|
||
page-only delivery. Keep it outside active CI discovery during this sequence.
|
||
Document where to place it for later activation and how to supply its secret.
|
||
3. Update the release policy's deliverable declaration and `docs/release.md`
|
||
together to identify the hosted note page, automation responsibility, and
|
||
page-delivery retry command. Source completion remains the exact tag push.
|
||
4. Exercise the prepared command sequence against a temporary tagged repository
|
||
and local HTTP server. Retain the note's complete body, including licensing
|
||
footer, on the hosted page.
|
||
|
||
Acceptance: ReleaseKit's source-only example creates a hosted page and accepts
|
||
an identical rerun without building consumer binaries, generating a manifest,
|
||
requiring a receipt, or consulting a previously released tool. Delivery failure
|
||
leaves source status unchanged. Workflow preparation requires no real tag,
|
||
credentials, remote push, CI activation, or maintainer checkpoint.
|
||
|
||
## Stage 18: Complete the initial implementation and handoff documentation
|
||
|
||
Status: Pending. Depends on Stage 17.
|
||
|
||
1. Map each initial capability in the feature roadmap to its implemented owner
|
||
and existing behavioral evidence. Record the concrete package and dependency
|
||
inventory, both supported facades, and their implemented responsibilities in
|
||
the canonical internal and consumer documentation.
|
||
2. Finish current API, CLI, configuration, integration, example, development,
|
||
and release documentation. Check links and consistency with actual behavior;
|
||
keep licensing and attribution accurate for incorporated material.
|
||
3. Run the established offline default suite and relevant existing boundary
|
||
checks for the assembled implementation. Resolve implementation defects
|
||
within this scope without adding a new permanent release matrix or live
|
||
testing requirement.
|
||
4. Record the completed initial implementation and evidence in this plan.
|
||
Identify prepared integrations whose live operation remains unverified and
|
||
hand off the implementation for the maintainer's subsequent architecture
|
||
review. Keep consumer migrations and their anticipated code savings marked
|
||
as future adoption work.
|
||
|
||
Acceptance: the initial library and executable provide check, publication,
|
||
build, runtime version access, page-only and binary delivery, partial/uncertain
|
||
recovery, and prepared Woodpecker integration. The documentation describes the
|
||
implemented boundary accurately. Completion depends on local implementation
|
||
and development evidence; architectural review, a broader testing plan,
|
||
consumer migrations, and release/deployment decisions follow this sequence.
|
||
|
||
## Coverage of the feature roadmap
|
||
|
||
| Initial target responsibility | Implementing stages |
|
||
| --- | --- |
|
||
| Two public facades and small runtime dependencies | 1–3, extended in 10–11 and 15 |
|
||
| Stable version and committed note identity | 5 |
|
||
| Candidate capture, one validation, mutable guards | 5–8, 11 |
|
||
| Authoritative endpoint and remote ref state | 6–8 |
|
||
| Annotated object publication and failure recovery | 7–9 |
|
||
| ReleaseKit source-only bootstrap/runbook | 9, 17 |
|
||
| Shared version injection and selected cross-builds | 2, 10–11 |
|
||
| Native smoke, empty target sets, consistent names | 10–11 |
|
||
| Hosted page/note creation, including source-only projects | 12, 15, 17 |
|
||
| Full checksums, comparison, partial/additive retries | 13–15 |
|
||
| Prepared Woodpecker workflows for both delivery profiles | 16–17 |
|
||
| Complete initial implementation and canonical documentation | 18 |
|
||
|
||
Consumer infrastructure reduction and convergence on the shared standard remain
|
||
feature-roadmap adoption goals. Their migration sequence, real-world testing,
|
||
and measured savings will be determined after the initial architecture review.
|
||
|
||
## Open Questions
|
||
|
||
None block this initial implementation sequence. The maintainer has selected
|
||
hosted pages for source-only projects and deferred pilots, releases, consumer
|
||
adoption, and broader testing decisions until after implementation and the
|
||
subsequent architecture review. Actual release pins and deployment credentials
|
||
are inputs to later adoption; no stage requires them.
|