Files
notarius/docs/roadmap/implementation.md

19 KiB
Raw Blame History

Source-Only Release Implementation Plan

Purpose

Implement the target state defined by Source-Only Releases: immutable source tags with checked-in release notes, a diagnostic version interface, strong shared candidate checks, validation-only tag CI, Linux support, best-effort macOS compilation, and no packaged binaries or Windows support.

This plan is ordered. Each numbered stage is one implementation prompt for a gpt-5.6-terra coding agent. Complete and validate one stage before beginning the next. Preserve all unrelated worktree changes, follow every policy under docs/policy/, and update current-behavior documentation in the same stage as the behavior it describes.

Do not create or push a release tag while implementing this plan. Do not invent a release note for v0.1.0, v0.2.0, or v0.3.0. The first real release under the completed procedure will add its own note in a separate release operation.

Decisions Fixed For Implementation

  • Releases are stable vMAJOR.MINOR.PATCH source tags on main; prereleases are unsupported initially.
  • Tags are lightweight and immutable after publication.
  • No release binaries, archives, checksums, signatures, containers, package-manager entries, or Gitea release objects are produced.
  • Linux is supported. Release checks compile Linux amd64 and arm64 with CGO_ENABLED=0.
  • macOS is best-effort. Release checks compile Darwin amd64 and arm64 with CGO_ENABLED=0, without promising runtime CI or packaged output.
  • Windows is unsupported and must not be added to build checks.
  • Release notes begin with the first release made under the new procedure; historical tags are left untouched.
  • notarius --version is informational. Receipt and artifact contracts remain authoritative for downstream compatibility.
  • One checked-in POSIX shell command owns substantive source-candidate checks. The release procedure and tag CI call it rather than maintaining duplicate test/build matrices.
  • Tag CI validates only. Pre-publication local guards remain mandatory because tag CI cannot prevent an already-pushed tag.

Stage 1: Add Build Version Resolution And --version

Goal

Add a small, testable build-information boundary and expose the public root version flag without affecting existing command behavior.

Implementation

  1. Create internal/buildinfo with an exported link-time string variable named Override and an exported resolver such as Version() (string, error). Keep this package independent of CLI and application packages.
  2. Resolve the displayed value using this precedence:
    1. a nonempty Override;
    2. the main-module version returned by runtime/debug.ReadBuildInfo; then
    3. the literal development.
  3. Accept a release value only when it matches the complete stable SemVer tag form ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$. Whitespace, prerelease/build suffixes, pseudo-versions, and arbitrary text are not release versions. A nonempty invalid linker override is an error; an empty, (devel), pseudo-version, or otherwise non-release main-module version falls back to development.
  4. Add --version to the root dispatch in internal/cli. It is valid only as the sole argument, writes exactly notarius <resolved-version>\n to stdout, writes nothing to stderr, and exits zero. Additional arguments are a syntax error using the existing exit-2 and stderr conventions. An invalid linker override is a runtime/build error using exit 1 and stderr.
  5. Add the flag to root usage without changing the existing behavior of empty arguments, help spellings, or subcommands.
  6. Update docs/cli.md as the canonical public contract and docs/internal/cli.md as the implementation owner. State that tagged go install builds can obtain the main-module tag from Go build information, controlled builds may inject gitea.maximumdirect.net/eric/notarius/internal/buildinfo.Override, and an ordinary unversioned checkout reports development.

Tests

  • Add table-driven internal/buildinfo tests for valid stable versions, leading-zero rejection, whitespace, prerelease/build suffixes, pseudo- versions, override precedence, invalid nonempty override, and development fallback. Test the pure resolution decision rather than trying to mutate process build information.
  • Extend the CLI command-contract tests to cover exact stdout/stderr/exit behavior for --version, extra arguments, and unchanged help/unknown-command behavior.
  • Do not snapshot the whole usage document solely for the new line; assert the stable semantic fragments already owned by the CLI contract tests.

Validation

go test ./internal/buildinfo ./internal/cli
go test ./...
go vet ./...
go build ./cmd/notarius
go run ./cmd/notarius --version

Build a temporary Linux host binary with:

go build -trimpath \
  -ldflags '-X gitea.maximumdirect.net/eric/notarius/internal/buildinfo.Override=v0.0.0' \
  -o /path/to/temp/notarius ./cmd/notarius

and verify that its output is exactly notarius v0.0.0.

Acceptance Criteria

  • The public and internal documentation matches the implemented version behavior.
  • Ordinary builds print notarius development.
  • A valid linker override prints the exact stable tag.
  • Invalid linker content cannot masquerade as a release version.
  • Existing CLI commands, help, streams, and exit classes remain unchanged.

Stage 2: Add One Reusable Source-Candidate Checker

Goal

Create one repository-owned, offline validation command used identically by a maintainer and release CI.

Implementation

  1. Create executable POSIX shell script scripts/check-release-source.sh. Require exactly one positional argument containing a stable SemVer tag. The script must locate and enter the repository root from its own checked-in path so callers cannot accidentally validate another working directory.
  2. Use set -eu, quote all expansions, reject invalid versions before using them in paths or linker arguments, and use a freshly created temporary directory for cross-build output. Install a cleanup trap scoped only to that resolved temporary directory.
  3. Keep release-note, branch, remote, clean-worktree, and tag-existence guards out of this script. Those publication-specific checks belong in docs/release.md and tag CI. This script owns the substantive source checks that both workflows share.
  4. Implement these checks in a clear fail-fast order:
    • require the expected Notarius module path in go.mod;
    • reject tracked go.work or go.work.sum, an existing vendor directory, and any replace directive in go.mod;
    • GOWORK=off go test -count=1 ./...;
    • GOWORK=off go test -race -count=1 ./...;
    • GOWORK=off go vet ./...;
    • GOWORK=off go build ./...;
    • GOWORK=off go mod tidy -diff;
    • require no output from gofmt -l for tracked Go files;
    • git diff --check and git diff --cached --check;
    • validate examples/dnd-minimal.config.yml and examples/dnd-complete.config.yml for pipeline dnd-session using the built or go run Notarius command;
    • build ./cmd/notarius with CGO_ENABLED=0 for Linux amd64 and arm64 and Darwin amd64 and arm64; and
    • inject the supplied tag through internal/buildinfo.Override in every cross-build.
  5. Execute the built command and verify exact --version output when the current host GOOS/GOARCH matches one of the four targets. Do not attempt to execute a foreign target.
  6. Do not compile for Windows, write output beneath the repository, contact an LLM provider, require credentials, or mutate tracked files.

Tests And Validation

  • Run the script with v0.0.0 as a synthetic build version. It does not require or create a corresponding release note or Git tag.
  • Exercise its cheap argument guards separately with missing, extra, malformed, prerelease, and leading-zero versions. These failures must occur before Go tests or builds begin.
  • Confirm temporary outputs are removed on success and ordinary command failure. Do not add a large shell-test framework solely for this script; retain focused automated tests only if they protect a realistic failure that is not more clearly covered by executing the checker itself.
./scripts/check-release-source.sh v0.0.0
git status --short

Acceptance Criteria

  • One command runs every substantive source-candidate check required by the feature roadmap.
  • The command is deterministic, offline, credential-free, POSIX-compatible, fail-fast, and safe with temporary paths.
  • The Linux and Darwin target matrix succeeds and Windows is absent.
  • Version injection and host-binary reporting are checked as part of the same matrix.
  • Successful execution leaves the worktree and index unchanged.

Stage 3: Add Validation-Only Tag CI

Goal

Independently validate every newly pushed release tag without publishing or mutating release state.

Implementation

  1. Add .woodpecker/release.yml triggered only by tag events.
  2. Use the Go 1.25.5 container image to match the version currently declared by go.mod. When the declared Go version changes in a future release, the release pipeline image and release documentation must be reviewed in the same change.
  3. In one validation step:
    • read the candidate version only from CI_COMMIT_TAG;
    • require the stable SemVer form fixed above;
    • require a nonempty docs/releases/$CI_COMMIT_TAG.md;
    • require the exact heading # Notarius $CI_COMMIT_TAG;
    • require exact ## Summary, ## Compatibility, ## Upgrade, and ## Changes headings; and
    • invoke ./scripts/check-release-source.sh "$CI_COMMIT_TAG".
  4. Do not include a release plugin, API token, artifact upload, Gitea release creation, archive/checksum step, Windows target, tag mutation, or retry that could overwrite published state.
  5. Keep the CI file thin: note/tag guards belong in it, while the substantive source checks remain in the shared script.

Validation

  • Review the YAML trigger and commands against the repository's Woodpecker syntax and the established Weatherreporter tag pipeline structure.
  • Confirm every invoked path exists and the script is executable.
  • Run the shared checker locally with v0.0.0.
  • Search the new pipeline for release-plugin configuration, upload commands, secrets, Windows targets, and mutation commands; none may be present.
  • Run git diff --check.

Do not push a synthetic tag merely to test this stage. The first real release will exercise the remote trigger; local source validation and review provide the pre-release confidence boundary.

Acceptance Criteria

  • Every stable release tag triggers the validation pipeline.
  • Missing or malformed version-matched release notes fail before the expensive source checks.
  • CI calls the same substantive checker used locally.
  • The pipeline cannot publish binaries, releases, checksums, or other assets and requires no release secret.

Stage 4: Establish The Canonical Release Procedure And Documentation Policy

Goal

Make the complete source-release workflow executable by a maintainer without undocumented knowledge, and give release documentation an explicit canonical home.

Implementation

  1. Create docs/release.md, adapted to Notarius's source-only model. It must define:
    • stable SemVer selection and the pre-v1 compatibility policy;
    • the exact docs/releases/<tag>.md template and validation guards;
    • invocation of ./scripts/check-release-source.sh "$RELEASE_VERSION";
    • manual review of changed Markdown links and unintended repository files;
    • committing and pushing the release note and current documentation before tagging;
    • recording RELEASE_COMMIT from HEAD^{commit};
    • a copyable guard that requires main, a clean worktree/index, disabled Go workspace use, an exact match between RELEASE_COMMIT and origin/main, a matching note, and an unused local and remote tag;
    • explicit lightweight-tag creation against RELEASE_COMMIT;
    • pushing only refs/tags/$RELEASE_VERSION:refs/tags/$RELEASE_VERSION;
    • remote tag and tagged-note verification;
    • a fresh go install ...@"$RELEASE_VERSION" or fresh exact-tag checkout verification, including exact --version output; and
    • immutable-tag failure and correction policy.
  2. The procedure must say that the tag and checked-in note are the release and that tag CI is validation-only. It must explicitly exclude binaries, archives, checksums, signatures, containers, package-manager publication, Gitea release objects, Windows, and retrospective notes for existing tags.
  3. Document private-module installation through standard GOPRIVATE and Git authentication mechanisms without including credentials or private environment dumps. Do not make one maintainer's credential setup part of the release contract.
  4. Update docs/policy/documentation.md:
    • add canonical-owner rows for docs/release.md and docs/releases/;
    • state that release notes are historical summaries, not current-state contract owners;
    • state that the checked-in note at the immutable tag is the release record; and
    • require current canonical docs to change with behavior rather than using release notes as substitutes.
  5. Update docs/development.md with a release-preparation/tagging/verification routing row pointing to docs/release.md and the relevant policies.
  6. Do not create an empty placeholder release note or a retrospective note. docs/releases/ first becomes tracked when the next actual release note is prepared.

Validation

  • Follow every local Markdown link added or changed in this stage.
  • Execute every non-destructive candidate-validation command that does not require an actual new release note, remote tag, or publication.
  • Compare the procedure line by line with the shared checker and CI so their tag syntax, note headings, target matrix, and validation ownership agree.
  • Confirm the procedure never uses git push --tags, moves a published tag, uploads an asset, or embeds credentials.
  • Run git diff --check.

Acceptance Criteria

  • docs/release.md is sufficient to prepare, guard, tag, publish, verify, and recover from a source release.
  • Release procedure and release-note ownership are explicit and nonduplicative.
  • The procedure calls the shared checker rather than restating its full command matrix.
  • No historical or placeholder release note is introduced.

Stage 5: Align Platform, Installation, And Operational Documentation

Goal

Make the supported-platform and source-installation story discoverable in the canonical current-state documents without duplicating the release procedure.

Implementation

  1. Update README.md with a concise source-installation section. Show go install gitea.maximumdirect.net/eric/notarius/cmd/notarius@<tag> as a version-pinned pattern and retain the existing minimal product quickstart. Link release maintainers to docs/release.md rather than reproducing its guards.
  2. Update docs/operations.md with operator-facing source deployment facts: Linux support, the Go version declared by go.mod, version pinning, exact tag builds, and notarius --version as a diagnostic. Link command semantics to docs/cli.md and maintainer publication mechanics to docs/release.md.
  3. Update docs/policy/architecture.md with the durable platform and distribution invariants: supported Linux deployment, best-effort macOS development, unsupported Windows, and source-only distribution. Keep tag commands and release mechanics out of architecture.
  4. Review docs/internal/overview.md navigation after adding internal/buildinfo. Add only the smallest component entry needed if the current inventory would otherwise omit a meaningful implemented boundary; do not inflate build information into a subsystem.
  5. Confirm docs/roadmap/future.md no longer lists the active documented release-process work. Retain packaged alpha artifacts as deferred work; the source-only release feature does not permanently reject reconsideration.
  6. Review the completed feature roadmap against the implementation and correct only genuine target-state inconsistencies. Do not convert it into a changelog or duplicate docs/release.md.

Validation

  • Verify all added and changed local Markdown links.
  • Confirm commands agree with the implemented CLI and declared module path.
  • Confirm no current-state document claims that Notarius publishes binary assets or supports Windows.
  • Run:
go run ./cmd/notarius --version
go run ./cmd/notarius help
git diff --check

Acceptance Criteria

  • Users can discover how to install a pinned source release.
  • Operators can identify the deployed version and understand the support boundary.
  • Architecture records durable platform/distribution policy without owning maintainer release commands.
  • Packaged artifacts remain clearly deferred rather than accidentally promised or permanently prohibited.

Stage 6: Final Release-System Verification

Goal

Review the implemented feature as one system and prove that code, documentation, shared validation, and CI converge on the same release model.

Verification Work

  1. Inspect all commits associated with Stages 15 and compare the result with docs/roadmap/source-releases.md and this plan.
  2. Run the shared candidate checker with synthetic version v0.0.0. This is a build identity only; do not create a note or tag for it.
  3. Independently run the repository-wide baseline checks if any are not already performed by the shared checker:
go test ./...
go vet ./...
go build ./cmd/notarius
  1. Verify exact development and injected release output:
    • ordinary checkout: notarius development;
    • injected v0.0.0: notarius v0.0.0;
    • invalid linker override: nonzero exit, no false release identity.
  2. Confirm both maintained D&D configurations validate offline.
  3. Verify all added or changed local Markdown links and run git diff --check.
  4. Audit .woodpecker/release.yml and docs/release.md for agreement on tag form, note location/headings, Go image/version expectations, and shared checker use.
  5. Confirm the repository contains no generated release binary, distribution directory, checksum file, release credential, Windows target, release plugin, synthetic release note, or new tag.
  6. Review tests under the repository testing policy. Retain behavior-level coverage for the public version contract and important validation guards; remove redundant tests that merely duplicate the shared checker or CI text.
  7. Report any remaining divergence as a concrete finding. Fix only in-scope release-feature defects discovered during this verification; do not expand into packaged distribution or unrelated cleanup.

Acceptance Criteria

  • All feature-roadmap acceptance criteria are met except creation of the first real post-procedure release, which is intentionally a separate operator action.
  • Local validation and tag CI share one substantive source checker.
  • Version reporting, source installation, platform policy, documentation ownership, tag guards, and immutable failure handling are internally consistent.
  • The full test suite, vet, build, configuration validation, four-target cross- build matrix, Markdown link review, and whitespace checks pass.
  • The worktree contains no release side effects beyond the intended source, automation, and documentation changes.