Refocus release audit on developer effort

This commit is contained in:
2026-10-06 15:17:51 +00:00
parent e146a5c61e
commit 85b2b39fb6

View File

@@ -1,43 +1,41 @@
# ReleaseKit pre-release audit
Date: 2026-10-06. Reviewed source: `536a57916f6565ae8194c4563577dfcebd6cfb69`.
Status: findings and recommendations; no design changes adopted by this document.
Status: review findings and proposed changes, revised for the clarified product goals.
Audience: maintainers deciding the shared release standard before `v0.1.0`.
## Assessment
ReleaseKit is a sound foundation for the intended standard, but I recommend
resolving two reproduced correctness defects and one demonstrated build-standard
gap before tagging it. Its publication
and delivery separation, narrow public facades, standard artifact naming,
version injection, and content-aware delivery retries are worth retaining.
The implementation covers most of the mechanics in the
[product roadmap](releasekit.md). It does not yet demonstrate both
[architectural success criteria](../policy/architecture.md#success-criteria):
substantial removal of consumer infrastructure and adoption of one coherent
standard. Those require representative adoption evidence.
ReleaseKit's practical goals are to minimize application-owned release code,
duplication across the suite, and the time developers spend on release processes.
Shared conventions serve these goals by giving routine decisions one owner.
Applications supply product facts and product-specific checks; ReleaseKit owns
the procedure, sensible defaults, and recovery guidance.
The main architectural choices have concrete benefits: annotated tags preserve
the checked-in note and provide an exact object for recovery; requiring the
candidate on authoritative `main` keeps branch publication out of the release
transaction; and separate delivery prevents a hosting outage from redefining
source-release success. None needs replacement to achieve the stated goals.
Selected build targets and product-specific checks are appropriate variation;
independent implementations of the publication transaction are not.
The core direction is sound. Retain the publication/delivery separation, narrow
public facades, standard artifact naming, version injection, and content-aware
retries. Fix the reproduced validation and recovery defects in F1–F2 before
`v0.1.0`. Treat F3 as a focused improvement to internal build defaults.
The larger design opportunity is to make ReleaseKit own more of the ordinary
path: a standard validation baseline, a declared build environment, and shared
delivery orchestration usable equally through Go and the CLI. Today, consumers
can still supply an entire replacement validation process, repeat CI setup, and
assemble binary delivery themselves when using the Go API. These are supported
choices, not violations of the present contracts, but they weaken the stated
objective of minimizing variation.
Build-environment reproducibility, hardware-specific configurability, and
extensive build inspection are outside the product's optimization goals. Use the
available compatible Go toolchain and shared baseline settings. Evaluate new
configuration and deployment machinery by the application code and developer
work they save. Different toolchains may produce different bytes; existing
published content remains protected by digest comparison.
Compatibility with existing consumer scripts is not a reason to preserve these
choices. The recommendations below favor a coherent long-term standard, while
retaining product identity, target selection, compatibility decisions, and
product-specific behavioral checks with the consumer. They do not propose
mandatory new lint, race, signing, live-service, or approval gates.
The implementation covers most mechanics in the [product roadmap](releasekit.md).
Representative adoption should demonstrate the
[architectural success criteria](../policy/architecture.md#success-criteria)
through deleted release code, fewer duplicated checks, and fewer routine decisions.
F4–F6 identify opportunities to centralize validation, delivery, and CI setup.
The existing source transaction supports these goals. Annotated tags preserve
the checked-in note and provide an exact object for recovery. Requiring the
candidate on authoritative `main` keeps branch publication in preparation.
Separate delivery lets source publication finish independently of hosting.
Consumer migrations should adopt this common process and retain product identity,
target selection, compatibility decisions, and product-specific checks.
## Scope and evidence
@@ -45,13 +43,13 @@ Reviewed the development guide; agent, architecture, testing, documentation, and
release policies; facade ADR; roadmap; public declarations; CLI/configuration
and consumer guides; publication, build, delivery, Git and Gitea implementations;
and their relevant tests and workflow examples. The
[release policy](../policy/release.md) governs ReleaseKit's own releases; its
requirements are not automatically imposed on every consumer API operation.
[release policy](../policy/release.md) governs ReleaseKit's own releases.
The facade and integration contracts define consumer behavior.
Also inspected the release scripts, module declarations, and available delivery
YAML in the six sibling projects named by the roadmap. This was a read-only
sample of adoption needs, not an audit or migration of those repositories.
Their existing extra checks are evidence to evaluate, not requirements to copy.
YAML in the six sibling projects named by the roadmap. This read-only sample
informed the assessment of adoption needs.
Existing extra checks should be retained according to their product-specific value.
For example, PromptKit has product-specific executable examples, whereas
Narratio currently requires Go 1.26 and several peers require Go 1.25.5.
@@ -61,14 +59,13 @@ Validation on Linux/amd64 with Go 1.26.8:
passed on the reviewed checkout.
- `go run ./internal/apicheck` passed.
- Isolated probes against an archive of the reviewed commit reproduced F1–F3
below. They used temporary consumer repositories and local bare Git remotes;
no production refs, credentials, or hosted content were used.
below. They used temporary consumer repositories and local bare Git remotes.
The probes were investigative tests in a disposable checkout, not additions to
the default suite. Their setup and outcomes are recorded with each finding so
regressions can be written against the intended fixes. No live Gitea or
Woodpecker deployment, published-module installation, or minimum-Go-1.25 run was
performed. The test pass therefore does not establish those properties.
The investigative probes ran in a disposable checkout. Their setup and outcomes
are recorded with each finding for future regressions. Validation covered local
fixtures on Go 1.26.8. Live Gitea/Woodpecker integration, published-module
installation, and minimum-Go-1.25 compatibility remain unverified. These results
belong to the original source review; this revision updates its recommendations.
## Goal coverage
@@ -77,10 +74,10 @@ performed. The test pass therefore does not establish those properties.
| One standard source-publication transaction | Substantially implemented in [Publish](../../internal/publish/publish.go): captured candidate, authoritative `main`, annotated tag, exact object/ref push, and bounded recovery. F2 is a correctness exception. |
| One validation of the intended candidate | Callback/default validation plus mutable-state rechecks are implemented in [candidate](../../internal/candidate/check.go). F1 can silently narrow the default suite; F4 concerns the selected customization model. |
| Consumer-selected binaries or source-only releases | [Build](../../internal/build/build.go) supports target pairs and empty sets. Source-only delivery creates no binary manifest. This is the right distinction. |
| Shared runtime version, build flags, naming, and smoke check | Implemented through [buildinfo](../../buildinfo/version.go), the builder, and external consumer build tests. F3 leaves important build inputs ambient. |
| Shared runtime version, build flags, naming, and smoke check | Implemented through [buildinfo](../../buildinfo/version.go), the builder, and external consumer build tests. F3 identifies an opportunity for predictable internal defaults. |
| Immutable hosted contents and recoverable partial delivery | [Delivery](../../internal/delivery/delivery.go) and [Gitea](../../internal/gitea/) compare notes and asset bytes, reject conflicts, and add missing assets. F7 identifies an outcome-reporting inconsistency. |
| Public contract ownership and small runtime dependency graph | Root and runtime facades follow [ADR 0001](../adr/0001-public-facade.md); compiled boundary checks pass. Keep these boundaries. |
| Thin application integration and less repeated infrastructure | Plausible but not demonstrated by consumer adoption. F5–F6 identify remaining orchestration and setup duplication. |
| Thin application integration and less repeated infrastructure | Representative consumer adoption remains to be evaluated. F5–F6 identify remaining orchestration and setup duplication. |
| Ready deployment path | Prepared templates and local fixtures exist. Immutable deployment pins, credentials, activated workflows, and live verification remain explicitly open in the roadmap. |
## Findings
@@ -104,27 +101,26 @@ The [Go command documentation](https://pkg.go.dev/cmd/go#hdr-Environment_variabl
confirms that `GOFLAGS` supplies defaults to commands that recognize its flags.
**Consequence.** A normal developer setting used for focused testing can cause a
release to pass without the complete default suite. The caller need not edit
source or intentionally bypass validation.
release to pass after skipping the default suite. The reproduction uses an
unchanged committed candidate and an ordinary shell setting.
**Recommendation.** Make the standard validation command own its effective Go
flags and host target, including values supplied through Go's persistent
configuration. Preserve caches and legitimate dependency preparation settings.
Use one internal Go-environment policy shared with the builder where appropriate;
do not give validation the builder's `CGO_ENABLED=0` blindly. A standard native
test run and a static release build have different needs. Apply the same baseline
to ReleaseKit's own profile. Retain a regression that proves a failing test
cannot disappear under an ambient filter. F4 and Open Question 1 address how
Share internal Go-environment handling where the semantics match. Preserve
the settings appropriate to native tests and static release builds separately.
Apply the same baseline to ReleaseKit's own profile. Retain a regression showing
that a failing test remains effective under an ambient filter. F4 and Open Question 1 address how
intentional product-specific checks should compose with this baseline.
### F2 — High: automatic push recovery can query a different endpoint and report false absence
**Evidence.** [reconcilePush](../../internal/publish/publish.go) calls
`Endpoint.QueryTag` using the captured URL and command-scoped remote.
[QueryTag](../../internal/git/tag.go) does not call
`verifyScopedURLs`, although [Resolve/Recheck](../../internal/git/remote.go)
use that verification before publication. Git still applies current URL rewrite
configuration to the captured literal URL. The manual recovery instructions in
[QueryTag](../../internal/git/tag.go) runs the query directly.
[Resolve/Recheck](../../internal/git/remote.go) call `verifyScopedURLs` before
publication. Git still applies current URL rewrite configuration to the captured
literal URL. The manual recovery instructions in
[the runbook](../release.md#resolve-failures) already require a URL-equality check.
The automatic path lacks the equivalent check.
@@ -137,25 +133,21 @@ shows the exact tag object returned in `PublicationResult` is present.
**Consequence.** The result gives incorrect recovery advice after an ambiguous
push. It converts a query to the wrong endpoint into proof that the intended
endpoint lacks the published tag. This contradicts an existing recovery
contract; it is not a request for broader filesystem or transport hardening.
endpoint lacks the published tag. This contradicts the existing recovery
contract for the authoritative endpoint.
**Recommendation.** Within the existing bounded recovery context, verify that the
captured endpoint still resolves to itself before the one authoritative tag
query. If verification fails, preserve the push error and local tag and return
`unknown`. Do not resolve the remote name anew and substitute its current
endpoint. Cover both the successful-publication/false-absence reproduction and
an ordinary unchanged-endpoint recovery. No extra read-back gate is needed after
a successful push.
`unknown`. Keep recovery bound to the captured endpoint. Cover the
successful-publication/false-absence reproduction and ordinary recovery against
an unchanged endpoint. Preserve successful push as the completion point.
### F3 — High for binary adoption: target identity omits effective build inputs
### F3 — Internal improvement: ambient CPU settings change binary compatibility
**Evidence.** `goEnvironment` in [internal/build/build.go](../../internal/build/build.go)
standardizes several Go settings but inherits architecture feature variables,
`GOEXPERIMENT`, and the persistent Go environment file. Build identity records
GOOS/GOARCH and compiler text, but not those effective inputs. The
[receipt](../../cmd/releasekit/internal/cli/receipt.go) and public `BuildResult`
cannot distinguish their effects.
standardizes several Go settings and inherits architecture feature variables,
`GOEXPERIMENT`, and the persistent Go environment file.
**Reproduction.** Build the same committed consumer and `v1.2.3` for
`linux/amd64` twice, once with `GOAMD64=v1` and once with `GOAMD64=v2`.
@@ -165,23 +157,20 @@ records the respective feature levels. Go documents these as different target
microarchitecture requirements in its
[environment reference](https://pkg.go.dev/cmd/go#hdr-Environment_variables).
**Consequence.** Identical declared targets can require different CPUs and yield
conflicting bytes during delivery retries. Existing content comparison correctly
refuses replacement, but cannot make an undeclared build environment repeatable.
A clean Git checkout does not resolve this; `SourceVerified` correctly makes no
attestation claim. This is a gap in the desired shared build standard, rather
than a violation of a documented promise of fixed CPU baselines or reproducible
bytes. The missing selection should be made deliberately before binary adoption.
**Consequence.** A developer's ambient CPU setting can narrow a binary's hardware
compatibility. Different build inputs can also produce a digest conflict during
a delivery retry. Existing digest comparison correctly preserves published bytes.
This finding supports predictable defaults; its earlier high-priority assessment
placed too much weight on build-environment standardization.
**Recommendation.** Select documented baseline architecture features for the
supported targets and control the relevant environment and Go configuration
file. Choose the release toolchain explicitly and preserve that selection with
the tagged declaration or immutable delivery environment. If custom feature
levels are supported, declare them and include them in artifact/build identity.
Do not silently derive them from a developer shell. Reuse the selected inputs
for candidate builds and tagged delivery. See Open Question 2 for the tradeoff
between a fixed baseline and richer declared variants. This recommendation does
not promise universally reproducible or hermetic builds.
**Recommendation.** Use ordinary baseline CPU settings and shared build flags
internally, with the available compatible Go toolchain. Control incidental
settings that affect expected compatibility or validation. Keep the application
profile focused on its package, binary name, and targets. Exact toolchain
pinning and detailed build metadata remain optional operational choices.
When a retry requires the original bytes, preserve and reuse those artifacts.
Open Question 2 compares local toolchain use with a centrally maintained build
environment for consumers that benefit from shared setup.
### F4 — Design priority: adding a custom check replaces the entire standard suite
@@ -193,43 +182,41 @@ replacement `Validate` callback.
**Consequence.** Adding one product-specific example check requires the consumer
to remember and reproduce the standard test command, environment, and ordering.
The profile becomes a small alternative release program. This relocates shared
policy instead of fully centralizing it. Conversely, simply adding mandatory
default tests to the current behavior would run them twice for existing check
scripts that already run the suite.
The profile becomes a small release program that repeats shared policy. Migrating
to built-in tests requires removing their invocation from existing custom scripts
to preserve one suite run.
**Recommendation.** Before first adoption, select a baseline-plus-additional-checks
model, with a narrow explicit way to express a justified alternative test
invocation when necessary. Migrate redundant suite invocations out of consumer
scripts. Keep only checks for actual product behavior, such as PromptKit's
examples. Do not preserve every consumer's race/vet/build loop merely to avoid
breaking their old workflow. This is a proposed contract change, not a bug fix;
Open Question 1 records a viable replacement-callback alternative.
**Recommendation.** Run the shared offline suite once, followed by optional
product-specific checks. Move redundant suite invocations out of consumer scripts.
Retain checks for actual product behavior, such as PromptKit's examples. Add
alternative test-invocation settings when a concrete consumer requirement
justifies them. This proposal changes the current customization contract; Open
Question 1 records the replacement-callback alternative.
### F5 — Design priority: the CLI owns reusable tagged-build delivery orchestration
**Evidence.** [runDelivery](../../cmd/releasekit/main.go) chooses a temporary
output directory, invokes the public builder, converts receipts, and then calls
`Deliver`. The [public delivery request](../../delivery.go) accepts an already
produced `BuildResult`, not a build declaration. A Go consumer wanting the normal
build-and-deliver operation must reproduce orchestration that CLI consumers get
for free. The default CLI path also builds and runs the native smoke executable
produced `BuildResult`. A Go consumer wanting the normal build-and-deliver
operation must reproduce orchestration already implemented by the CLI. The
default CLI path also builds and runs the native smoke executable
before `Deliver` verifies that HEAD matches the selected local tag.
**Consequence.** Temporary storage, build sequencing, tagged-source preflight,
and cleanup have more than one potential owner. An invalid tagged checkout can
consume a complete cross-build before it is rejected. The CLI does use supported
facades, but owns more domain coordination than the architecture's parsing,
presentation, and exit-status responsibility suggests.
consume a complete cross-build before it is rejected. The CLI owns domain coordination
alongside its parsing, presentation, and exit-status responsibilities.
**Recommendation.** Put the ordinary verify-tagged-source, build-selected-targets,
and deliver sequence behind the orchestration facade. Verify the local tagged
source before expensive compilation, then retain the necessary checks before
remote effects. Let the CLI adapt JSON, credentials, and output to that operation.
Use the same build declaration for candidate validation and delivery. Decide
whether caller-provided builds remain an advanced supported mode, as discussed
in Open Question 3. Keep the private receipt as local coordination data if that
mode remains; do not promote it into a provenance or portable-attestation format.
whether a concrete consumer needs caller-provided builds, as discussed in
Open Question 3. Keep separate building useful for local development. The ordinary
consumer workflow should complete delivery in one invocation; advanced artifact
handoffs can use private coordination data when needed.
### F6 — Adoption priority: shared deployment still depends on copied mechanics
@@ -241,31 +228,27 @@ the CLI already supports combined delivery. ReleaseKit's
[own workflow](../../examples/woodpecker/releasekit-source-delivery.yml) has a
separate first-release bootstrap. Consumer pins remain placeholders as documented.
**Consequence.** Core library duplication is reduced, but routine CI mechanics
still become consumer-owned copies that can drift. A chosen ReleaseKit version
and Go toolchain also need a clear installation/update convention for both local
publication and CI delivery. Merely requiring every application to invent a
wrapper would leave part of the roadmap's original problem unsolved.
**Consequence.** Copied CI mechanics require maintenance in each consumer and
can drift. A shared installation and update convention would simplify local
publication and CI delivery.
**Recommendation.** Provide a maintained, versioned integration entry point and
one default delivery sequence for both source-only and binary profiles. Keep
product declarations, immutable tool/toolchain pins, and secret references in
consumer configuration. Keep network-enabled dependency preparation separate
from offline validation/builds. Splitting build and delivery is justified when
it establishes an actual credential or artifact handoff boundary, not simply
because both commands exist. See Open Question 4 for packaging alternatives.
ReleaseKit's bootstrap can remain special until the first version is available.
**Recommendation.** Provide one maintained integration entry point and one
default delivery sequence for source-only and binary profiles. Consumers select
the ReleaseKit version, supply product settings, and provision credentials.
Centralize installation and update instructions. Choose source installation or a
maintained image according to the setup and maintenance effort each saves.
Keep dependency preparation separate from offline validation/builds. A split
build/delivery flow remains useful for an actual credential boundary or artifact
handoff. See Open Question 4 for packaging alternatives.
The current workflow tests are useful but narrower than their names may imply.
`TestWoodpeckerDeliveryCommandSequences` in
[the CLI workflow tests](../../cmd/releasekit/woodpecker_test.go) executes
equivalent commands;
[the ReleaseKit workflow test](../../cmd/releasekit/releasekit_delivery_workflow_test.go)
similarly reconstructs the sequence. They do not execute the maintained YAML
through Woodpecker or exercise the pinned installation step. A YAML drift can
therefore leave those tests green. Validate the actual selected integration at
adoption, and keep a small check tied to the maintained artifact. This is
integration-development evidence, not a new per-release live-service gate.
similarly reconstructs the sequence. Execution of the maintained YAML and
installation step remains unverified. A YAML drift can leave these tests green.
Validate the selected integration during adoption and keep a small check tied to
the maintained artifact. Use that evidence to maintain the shared integration.
### F7 — Medium: delivery asset classifications overlap inconsistently
@@ -278,19 +261,18 @@ can also remain pending. The existing
[upload_test.go](../../internal/gitea/upload_test.go) explicitly expects the
same asset in both pending and uncertain collections.
**Consequence.** Public consumers cannot treat the collections as consistent
categories. The CLI can present one file as both pending and uncertain, while a
**Consequence.** Overlapping categories require special handling by consumers.
The CLI can present one file as both pending and uncertain, while a
slightly different failure reports it only as uncertain. The overall unknown
state is conservative, so this is not a false-success defect, but it makes
recovery reporting and automation less clear.
state remains conservative. Consistent categories would simplify recovery
reporting and automation.
**Recommendation.** Prefer mutually exclusive current-outcome categories:
confirmed matching, uploaded, known pending, conflicting, and uncertain. Move an
asset out of pending when its outcome becomes uncertain or conflicting, and
clarify the facade GoDoc. Alternatively, explicitly define pending as a superset
of all unfinished assets and implement that rule consistently; see Open
Question 5. Update the behavioral tests to protect the selected meaning instead
of preserving inconsistent branch bookkeeping.
Question 5. Update behavioral tests to protect the selected meaning.
### F8 — Maintainability: artifact rules have several implementation owners
@@ -310,137 +292,114 @@ why divergent Go-environment ownership matters beyond cosmetic duplication.
**Recommendation.** Give internal build/artifact rules one owner and reuse them
from receipt and delivery adapters. Share Go invocation/environment construction
where the semantics actually match. Keep explicit facade conversions required
by the ADR; replacing them with public aliases would trade a little duplication
for a larger compatibility burden. Do not introduce a generic workflow engine,
plugin registry, or public Git/Gitea implementation packages to solve these
localized problems.
where the semantics match. Keep the explicit facade conversions required
by the ADR. Focus refactoring on the small internal helpers that own these rules.
## Recommended standard and release sequencing
Retain the existing source transaction: committed stable-version note, clean
`main`, candidate already on authoritative `main`, one validation, selected-target
build verification, exact annotated-tag publication, and bounded ambiguity
recovery. Source publication finishes independently of hosted delivery. Keep
additive, content-checked delivery and lightweight runtime version access.
build verification, exact annotated-tag publication, and bounded recovery.
Source publication finishes independently of hosted delivery. Keep additive,
content-checked delivery and lightweight runtime version access.
Strengthen that standard around a small committed declaration: product identity,
selected binary targets, explicit toolchain/build choices, delivery destination,
and product-specific checks added to the shared validation baseline. The ordinary
consumer integration should be a pinned tool invocation plus that declaration
and secret provisioning. Authoring compatibility notes and choosing the version
remain product decisions; ReleaseKit need not guess them from commit messages.
Make ordinary integration a versioned tool invocation plus a small product
profile and credential provisioning. The profile supplies product identity,
selected binaries and targets, delivery destination, and product-specific checks.
ReleaseKit owns the standard suite, routine build settings, delivery sequence,
and recovery reporting. Product developers choose versions and write
compatibility notes. Evaluate each proposed setting by the work it saves them.
Recommended order:
1. Fix F1–F3 and retain focused behavioral regressions. F1–F2 affect the first
source release itself; F3 should be resolved before advertising standard
binary adoption.
2. Decide Open Questions 1–3 and 5 before settling the first public API and
configuration contracts. Implement the selected changes, including F7, with
their canonical GoDoc/configuration/consumer documentation. Consolidate F8
while touching those owners.
3. Prove one representative source-only integration with an extra product check
and one binary integration using the selected common workflow. Identify the
actual scripts and duplicated tests removable from each. Temporary local
adoption trials can precede a published module; do not make sibling checkouts
dependencies of ReleaseKit's normal build or default suite.
4. Select the packaging direction in Open Question 4, prepare immutable pins and
credentials, and test the actual workflow/service integration separately.
First-source-release bootstrap is legitimate; live artifact availability
still does not become a source-publication completion gate.
5. Prepare the `v0.1.0` note and follow the existing maintainer procedure once the
selected pre-release work is complete. This audit does not tag or publish.
1. Fix F1–F2 and retain focused behavioral regressions before tagging `v0.1.0`.
2. Settle the validation and delivery API choices in Open Questions 1 and 3.
Adopt clear result categories under Question 5. Implement the selected
changes with their canonical documentation and consolidate F8 in those owners.
3. Address F3 through internal compatibility defaults, using Open Question 2 to
keep build configuration proportionate to actual consumer needs.
4. Exercise one source-only consumer with an extra product check and one binary
consumer through the common integration. Measure removed scripts, duplicated
tests, setup steps, and recurring release decisions. Keep ReleaseKit's default
suite independent of sibling checkouts.
5. Choose the simplest shared installation and update path under Open Question 4.
Verify the maintained integration during deployment preparation. Follow the
existing maintainer procedure for the release note and source publication;
report hosted delivery separately.
Not every possible release feature belongs in this first version. There is no
review evidence requiring a general multi-module release engine, prerelease
channels, multi-program bundles, arbitrary packaging hooks, mandatory signatures,
or stronger source attestations. Add such capabilities for an adopted need,
without weakening the ordinary shared path to accommodate hypothetical cases.
Feature expansion should follow demonstrated consumer needs and measurable
reductions in application maintenance or developer effort.
## Open Questions
### 1. Should product checks supplement or replace standard validation?
**Recommended:** Run the shared offline suite once, followed by declared
product-specific checks. Where a product truly needs a different invocation of
that same suite, expose a narrow explicit selection with documented semantics,
not an unrestricted implicit replacement. This centralizes the shared obligation
while allowing meaningful product checks and avoiding duplicate tests. Remove
old suite invocations when migrating custom scripts.
**Recommended:** Run the shared offline Go suite once, followed by optional
product-specific checks. ReleaseKit owns the command and its routine environment
settings. Migrate repeated suite invocations out of application scripts. Add
alternative invocation settings when an actual consumer requirement establishes
their value. This gives developers one ordinary validation path to understand.
**Viable alternative:** Keep the current replacement callback/command list and
make each product own one complete validation command. This is idiomatic for a
small Go orchestration library and accommodates unusual test layouts without an
expanding configuration schema. It is optimal if validation is intentionally a
consumer boundary; it is less aligned with the user's goal of maximal shared
standardization. Adoption guidance and tests would need to establish the common
baseline rather than assuming the library enforces it.
**Viable alternative:** Keep a replacement callback or complete validation
command for products with unusual test layouts. This is a simple composition
boundary and can accommodate existing product test infrastructure. It assigns
more release responsibility to the application, so its value depends on the
specific consumer need.
### 2. How much of the build environment belongs in release identity?
### 2. How should ReleaseKit choose build settings?
**Recommended:** Standard baseline feature levels and flags, plus an explicitly
selected toolchain preserved with the release inputs or an immutable build-image
pin. Control ambient feature/experiment settings and persistent Go configuration.
Keep cache locations and dependency access during preparation operational.
Different products can select toolchains meeting their modules' requirements;
standardization is the selection mechanism, not forcing every product onto an
inadequate Go version. This minimizes accidental variants and makes retries
explainable without claiming hermeticity.
**Recommended:** Use the available compatible Go toolchain, ordinary baseline CPU
settings, and shared build flags. Handle incidental environment settings internally
so developers can rely on the selected target's expected compatibility. Keep
application configuration small. Toolchain-dependent byte differences are
acceptable; digest checks preserve existing published content, and retained
artifacts support retries requiring the same bytes.
**Viable alternative:** Declare a richer target variant including CPU features,
build tags, and other justified options, and encode variant identity in artifact
names, receipts, and manifests. That supports genuine hardware-specific products
correctly but adds public API and validation cost. Use it when such variants are
actual deliverables. Continuing to inherit unrecorded settings is not an equally
good alternative for a standardized release builder.
**Viable alternative:** A shared CI image can supply a centrally selected Go
version and build environment when this makes installation and support easier.
Maintainers own image updates; applications use the shared integration. This is
useful where a common environment reduces operational work. Its value should be
measured against image maintenance and the suite's differing Go requirements.
Explicit hardware variants can be added for a demonstrated product need.
### 3. Should the library own build-and-deliver, or accept only completed builds?
**Recommended:** Make declared tagged-source build-and-deliver the normal facade
operation, with source verification and temporary storage owned internally.
The CLI and Go callers then share one implementation. Retain prebuilt input only
if a real split-stage consumer needs it, with the current digest/source checks
and a clear lifecycle. A plain request struct and straightforward orchestration
are sufficient; no public state-machine framework is needed.
**Recommended:** Provide one facade operation for tagged-source verification,
selected-target builds, temporary storage, checksums, uploads, and cleanup.
Use it from the CLI so both interfaces share the procedure. Retain separate
building for local development and support prebuilt delivery where a concrete
handoff requirement justifies it. This minimizes consumer orchestration code.
**Viable alternative:** Keep `Build` and `Deliver` as the only primitives and
provide a canonical composition helper/example. This makes build environments
and credential isolation explicit and keeps the delivery operation narrow.
It is appropriate when most consumers intentionally hand artifacts between
separate stages, but asks ordinary Go callers to assemble more release logic.
For portable stage handoff, deliberately design a portable artifact description;
the current absolute-path receipt is intentionally local.
**Viable alternative:** Keep separate build and delivery primitives with a
maintained composition helper. This suits consumers that require credential
isolation between stages or need to retain artifacts for later delivery. Those
consumers accept responsibility for the artifact lifecycle in exchange for the
operational separation they need.
### 4. How should the shared executable and CI integration be distributed?
**Recommended:** A versioned shared CI runner/entry point, packaged with a known
ReleaseKit executable and compatible Go/Git environment, selected by immutable
image digest. Consumers provide the profile, selected image/toolchain, and secret
reference. Use one maintained flow for source-only and binary delivery. This
removes repeated installer/setup shell from applications and gives integration
fixes a versioned owner. It requires deliberately adopting image distribution,
which the current source-only release policy does not yet declare; local
maintainers can still install the versioned Go command.
**Recommended:** Choose the simplest maintained entry point for installation,
updates, and delivery. Begin with versioned source installation if it keeps
consumer integration small. Centralize reusable setup and provide one clear
upgrade procedure. Assess packaging by the consumer code and recurring developer
work it removes.
**Viable alternative:** Keep source-only tool distribution through pinned
`go install` and maintain a small canonical bootstrap/template. It needs less
hosting infrastructure and naturally supports different Go versions. Prefer
calling one combined delivery operation where credential isolation permits it.
The cost is repeated setup and template-update work in consumers; test the real
template and provide an explicit pin/update convention. Do not add executable
binary distribution merely to remove a few shell lines if this alternative
proves adequately small in adoption trials.
**Viable alternative:** Publish a maintained CI image containing ReleaseKit and
a compatible Go/Git environment when it substantially reduces setup. The image
provides a ready-to-run integration and centralizes environment maintenance.
It also creates an image publication and update responsibility, which should be
reflected in the release procedure. Either packaging choice should give
applications a small configuration and a routine invocation.
### 5. Are delivery result collections exclusive categories or overlapping views?
### 5. How should delivery results support simple recovery?
**Recommended:** Exclusive outcome categories, with pending meaning known work
remaining and uncertain meaning remote effect unresolved. A caller can render
or act on each category without deduplicating it against the others. Overall
state continues to prevent automatic retries after uncertain writes.
**Recommended:** Use exclusive outcome categories: matching, uploaded, known
pending, conflicting, and uncertain. Present ordinary callers with completion or
a clear failure and recovery instruction. Keep asset details available for
diagnostics. Consistent categories simplify implementation and interpretation.
**Viable alternative:** Pending includes every unfinished declaration, with
conflicting/uncertain as diagnostic subsets. This can simplify a progress UI,
but must be defined explicitly and applied in every branch. Consumers must then
subtract blocked subsets before deriving retry work. Either is maintainable;
the present mixture is not.
conflicting/uncertain as diagnostic subsets. This can simplify a progress UI
when defined explicitly and applied in every branch. Consumers must then
subtract blocked subsets before deriving retry work. Apply the chosen
meaning consistently across all outcomes.