Skip to content

fix(release): make v0.3.0 releasable and every install path truthful - #341

Open
Patel230 wants to merge 16 commits into
mainfrom
fix/release-pipeline
Open

Patel230 wants to merge 16 commits into
mainfrom
fix/release-pipeline

Conversation

@Patel230

@Patel230 Patel230 commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This prepares rho for its first release under the rho name, v0.3.0. After it merges, the release pipeline can actually publish a signed release, and every install path the README offers is either working or labelled "available from v0.3.0".

  • VERSION is 0.3.0. The Go proxy already serves v0.1.0, v0.1.1 and v0.2.0; those are hawk-era tags with module github.com/GrayCodeAI/hawk. go.mod now retracts that range.
  • release.yml had failed on all 10 runs. It now installs syft, signs checksums.txt inside GoReleaser (cosign keyless, bundle checksums.txt.sigstore.json), and verifies the published signature. It also refuses a tag that disagrees with VERSION or CHANGELOG.md, and takes the release notes from the CHANGELOG section. The Homebrew publisher (which would have failed), the unused packages: write permission and the sibling checkout are gone. GoReleaser now builds all six targets, windows/arm64 included.
  • install.sh:
    • uses the correct asset names
    • checks cosign against the exact release-workflow identity (no regexp)
    • always verifies the SHA-256
    • fails closed when cosign is present, warns clearly when it is absent, and RHO_REQUIRE_COSIGN=1 makes cosign mandatory
    • parses flags strictly
    • refuses releases from before the rename
  • The GitHub Action installs through install.sh with cosign required. Before, it ran curl | tar on the source tarball and built it with Go.
  • README install section is now truthful:
    • prebuilt installs are "available from v0.3.0"
    • go install …@main works today; @latest works from v0.3.0
    • Homebrew and npm are marked not available
  • npm packages are kept but marked private, with MIT licence and fixed assembly. flake.nix is removed (it could not evaluate). The ast-grep config is removed (it was never enforced). The systemd unit is fixed. The binary size budget now fails the build instead of only warning.
  • New docs/RELEASING.md: release checklist, tag protection, verification steps, and the status of each distribution channel. All claims that release-please manages releases are removed.

Required before tagging v0.3.0 (owner)

scripts/check-release-tag.sh runs both in make release-check and as the first step of release.yml. It refuses a tag unless CHANGELOG.md has a non-empty ## [0.3.0] — YYYY-MM-DD section. This PR leaves the notes under ## [Unreleased] because other campaign PRs are adding entries there in parallel. After the campaign PRs merge, a final release PR should rename [Unreleased] to [0.3.0] — <date> and add a new empty [Unreleased] (see docs/RELEASING.md). Then tag. If you tag without that step, the release job stops before building or publishing anything.

Findings addressed

ID Outcome
F002 README install section is truthful; install.sh uses rho_* names; the pipeline can produce them.
F018 VERSION 0.3.0; release-please claims removed; rho update compares the real version.
F019 Release pipeline fixed so a tag produces rho_* assets plus a signature; README says "available from v0.3.0"; install.sh refuses hawk-era releases (no hawk_* fallback).
F020 npm: MIT licence, "private": true, meta package and optionalDependencies version stamping, GoReleaser v2 _v8.0 paths; README no longer offers npm; npm/README.md added. Nothing is published (owner decision §1.13).
F021 brews stanza and tap-token env removed; goreleaser check now passes.
F022 syft installed with anchore/sbom-action/download-syft pinned by SHA.
F023 Signing moved into GoReleaser signs:, so nothing downloads into dist/; the post-publish check downloads into a temp dir.
F024 tag == VERSION guard with a CHANGELOG section check; compat matrix stable set to rho 0.3.0 / flux 0.0.1; flake removed (it hardcoded 0.0.1); npm stamps the version.
F025 README uses @main (checked today) and explains @latest; go.mod retracts [v0.1.0, v0.2.0]; tagging v0.3.0 is the owner's step.
F026 make release no longer publishes (replaced by release-check and release-snapshot). Editing the v0.2.0 release notes is left to the owner (see Follow-ups).
F027 / F211 / F263 flake.nix and the use flake .envrc removed; /.envrc gitignored.
F028 Already in place: tag ruleset protect-release-tags (id 24053191) covers refs/tags/v* with admin bypass. Documented in docs/RELEASING.md.
F029 / F260 install.sh: strict flag loop, exact --certificate-identity, fail-closed cosign, RHO_REQUIRE_COSIGN=1, SHA-256 always checked.
F030 SIZE_LIMIT_MB := 70 in the Makefile is the only budget; make size-check exits 1 above it; CI runs it.
F031 / F254 Action installs verified release archives through install.sh with cosign required; strips the leading v; validates inputs; release.yml no longer clones flux; stale ci.yml branch mapping removed.
F032 systemd unit: User=rho, StateDirectory/CacheDirectory with RHO_*_DIR, API key read from an EnvironmentFile instead of argv, Documentation points at the repo; troubleshooting guide updated.
F033 sgconfig.yaml, rules/ and the lefthook ast-grep hook removed (ast-grep 0.39.5 does not even discover sgconfig.yaml).
F259 packages: write removed.
F262 Same fix as F002/F019/F025.
F269 release-please claims removed from CONTRIBUTING, README, docs/versioning.md, docs/compatibility.md, the compat matrix, cmd/rho/main.go and the shared templates.
F270 CHANGELOG history note added; hawk-era sections relabelled [hawk …]; duplicate ### Changed merged; stale references annotated.
F282 windows/arm64 is now built (GoReleaser and the CI matrix), so the README claim is true.

Not reproduced / deferred

  • None of the findings were unreproducible. The unverified ones (F211, F254, F259, F260, F262, F263, F269, F270, F282) were all reproduced from source and live checks: gh api returns 404 for merlin, kestrel, shrike, swift, harrier and homebrew-tap; npm view @graycodeai/rho gives E404; the v0.2.0 assets are all hawk_*; go install …/cmd/rho@latest fails with "does not contain package".
  • F026 (partial): editing the v0.2.0 release notes is a change to a live release, which this campaign is not allowed to make. Left to the owner.

Verification

All of the following were run in this worktree on darwin/arm64:

  • GOWORK=off go build ./...: ok

  • GOWORK=off go vet ./...: ok

  • go run mvdan.cc/gofumpt@v0.10.0 -l $(git ls-files '*.go'): no output

  • GOWORK=off golangci-lint run ./... (v2.1.0): 0 issues

  • GOWORK=off make boundaries: all guards passed

  • GOWORK=off go test -race -count=1 -p 4 ./...: 146 packages ok. Four more failed with ENOSPC because the shared disk filled up during the run:

    • cmd failed to link ("No space left on device")
    • tests in internal/engine, internal/engine/git and internal/engine/project failed with "no space left on device"

    Rerunning exactly those four with go test -race -count=1 -p 1 passed, so all 150 test packages pass.

  • goreleaser check (v2.17.0): passes (base branch: "brews should not be used anymore", exit 1)

  • goreleaser release --snapshot --clean --skip=publish,sign (final config): succeeded. It produced:

    • six rho_<ver>_<os>_<arch> archives, including windows_arm64
    • the source archive
    • six SPDX SBOMs (syft 1.46.0)
    • checksums.txt with 13 entries
  • GitHub Actions on this PR: all 21 checks pass. That includes the new build (windows/arm64) job, the make size-check budget in build (linux/amd64), the race/coverage tests and markdownlint.

  • Signing was not exercised locally. It needs the Actions OIDC token, and cosign is not installed here.

  • go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.7 .github/workflows/release.yml .github/workflows/ci.yml: clean

  • shellcheck -s sh install.sh: clean

  • shellcheck scripts/check-release-tag.sh: clean

  • New tests (internal/testaudit: install script end to end with stub curl/uname/cosign, release config, tag guard, CHANGELOG structure, VERSION, go.mod retraction, systemd unit; cmd: version display). The install.sh tests fail against the old install.sh (10 of 10 functions).

  • Live checks:

    • install.sh and the extracted action step against GitHub today: "latest" is v0.2.0 and is refused as pre-rename; v0.3.0 gives a clear 404; malformed inputs exit 2.
    • go install github.com/GrayCodeAI/rho/cmd/rho@main with a clean GOBIN and proxy.golang.org: works.
    • @latest: fails, as expected, until v0.3.0 exists.
  • npm assembly, run on a scratch copy against the snapshot dist/: all six targets and the meta package came out at 0.3.0.

  • make size-check: 60 MiB passes; with SIZE_LIMIT_MB=50 it fails.

  • markdownlint-cli2 0.18.1: 0 errors on the changed Markdown files.

  • Not executed: the systemd unit under systemd (no systemd here; the semantics are from systemd.exec(5) and systemd.unit(5)) and any Nix evaluation (no Nix here).

Follow-ups

  • Owner: the final CHANGELOG [0.3.0] section (see above), then push tag v0.3.0; afterwards check the install paths with RHO_REQUIRE_COSIGN=1.
  • Owner: optionally edit the v0.2.0 release notes to mark it as the legacy hawk build. Decide whether to keep the orphan v0.1.1 tag (it has no release).
  • Branch protection on main still has no required reviews (required_pull_request_reviews: null). That is an owner decision and was not changed here.
  • After v0.3.0: add a scheduled CI job that runs install.sh against the latest release.
  • Homebrew, npm and Nix channels need the prerequisites listed in docs/RELEASING.md before they are offered.
  • docs/COMPETITIVE.md:64 still says rho "already ships script/brew/npm paths". That file belongs to RH-docs.

🤖 Generated with Claude Code

Patel230 and others added 16 commits September 27, 2026 03:46
The Go module proxy already serves v0.1.0, v0.1.1 and v0.2.0 for
github.com/GrayCodeAI/rho (hawk-era tags), so a VERSION of 0.0.1 made
source builds report a version below the published ones and could never
become @latest. The next rho release is v0.3.0.

- VERSION: 0.0.1 -> 0.3.0
- compatibility matrix "stable": rho 0.3.0 with flux 0.0.1 (the pinned
  go.mod dependency) instead of the never-released rho 0.0.1
- testaudit: VERSION must be SemVer without a leading "v" and >= 0.3.0

Refs: F018, F024

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README promises binaries for linux/darwin/windows on amd64/arm64, `make
build-all`, install.sh and the npm scaffolding (rho-win32-arm64) all
handle windows/arm64, but .goreleaser.yml ignored that target and the CI
build matrix excluded it, so no windows_arm64 archive was ever produced.

`GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build ./cmd/rho` succeeds,
so build the target instead of narrowing the promise:

- .goreleaser.yml: drop the windows/arm64 ignore (six archives)
- ci.yml: drop the matrix exclude so the target is compile-checked
- npm assemble script: drop the comments about the missing target

Refs: F282

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The brews stanza pushed a formula to GrayCodeAI/homebrew-tap, a
repository that does not exist, with HOMEBREW_TAP_TOKEN, a secret that
is not set. On the next tag GoReleaser would have created the GitHub
release and then failed in the brew publisher, leaving the run red and
the release unsigned. `brews` is also deprecated in the pinned
GoReleaser v2.17.0, so `goreleaser check` exited 1 on this config.

Per the owner decision the tap is not being created now: remove the
stanza and the unused HOMEBREW_TAP_TOKEN/TAP_GITHUB_TOKEN env wiring.
`goreleaser check` now validates the config. Re-adding Homebrew
(homebrew_casks) is documented as a follow-up in docs/RELEASING.md.

Refs: F021

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tag-triggered workflow had never completed (10 of 10 runs failed):

- GoReleaser's `sboms:` stanza shells out to syft, which the runner does
  not have ("exec: syft: executable file not found"). Install it with
  anchore/sbom-action/download-syft pinned by SHA.
- The cosign step ran `gh release download ... -D dist/` into the dist/
  directory GoReleaser had just written, so it failed on the existing
  dist/checksums.txt before signing anything. Signing now happens inside
  GoReleaser (`signs:`, cosign keyless over checksums.txt, emitting the
  Sigstore bundle checksums.txt.sigstore.json) so the signature is
  uploaded with the release. cosign-installer is bumped to v4.1.2
  (cosign v3), whose sign-blob requires --bundle. A final step
  re-downloads the published files and verifies them with the exact
  certificate identity install.sh uses.
- `packages: write` was unused (no images or packages are published).
- The checkout-flux step cloned sibling repos the build never used
  (go.mod pins published flux v0.0.1); the job now sets GOWORK=off and
  builds only from go.mod/go.sum, with the local-replace guard.
- New scripts/check-release-tag.sh refuses a tag that is not
  "v" + VERSION or whose CHANGELOG.md has no non-empty section for it,
  and extracts that section as the release notes (--release-notes), so
  published notes match the curated changelog instead of an
  auto-generated list of every commit since the root.
- Builds use -trimpath; /dist/ is gitignored.

The shared templates are regenerated from the fixed files.
`goreleaser check` passes and `goreleaser release --snapshot --clean
--skip=publish,sign` builds six rho_* archives, six SPDX SBOMs and
checksums.txt locally (signing needs the Actions OIDC token and was not
exercised). testaudit now pins these invariants.

Refs: F022, F023, F024, F031, F259

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
install.sh could not install anything and was weaker than it claimed:

- It fetched rho_<ver>_* from releases/latest (v0.2.0), whose assets
  are hawk_*: every install 404ed. Releases before v0.3.0 predate the
  rename, so they are now refused with a clear message and no download
  (no hawk_* fallback: those archives contain a `hawk` binary).
- Signature: it looked for checksums.txt.sig/.cert, which no release
  has, and matched the certificate with an unanchored regexp built from
  the unescaped version ("anchor and escape" said the comment). It now
  verifies the Sigstore bundle checksums.txt.sigstore.json that the
  release workflow uploads, with the exact --certificate-identity
  https://github.com/<repo>/.github/workflows/release.yml@refs/tags/v<ver>.
  When cosign is present, a missing or invalid signature aborts. When it
  is absent the script prints a prominent warning and reports
  "signature: NOT verified" in the summary; RHO_REQUIRE_COSIGN=1 makes
  cosign mandatory.
- The SHA-256 check always runs (sha256sum, shasum or openssl) and
  requires exactly one exact-name entry in checksums.txt.
- Flags are parsed in a loop: --version/--prefix in any order, the
  --flag=value forms, --help, and exit 2 on unknown or empty flags
  (previously `--version X --prefix Y` silently ignored the prefix).
- The version is validated before use, unsupported OS/arch fail fast,
  temp files are cleaned on exit/signals, and RHO_INSTALL_REPO (validated
  owner/repo) lets a fork install its own signed releases.

internal/testaudit/install_script_test.go runs the real script end to
end against a fake release served by stub curl/uname/cosign, with PATH
restricted so the host's tools cannot change the outcome.

Refs: F019, F029, F260

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The composite action downloaded the GitHub "latest" release's source
tarball with `curl -sL | tar` (no -f, no checksum, no signature) and ran
`go build ./cmd/rho` on it. Today that tarball is the hawk-era v0.2.0
tree, which has no cmd/rho, so the action failed for every default
invocation; for pinned versions it prepended a second "v" to inputs
documented as "v1.2.3"; and a `@main` pin tried to download tag "vmain".

The install step now:

- installs cosign (sigstore/cosign-installer, pinned SHA) and runs the
  repository's install.sh at the action's own ref with
  RHO_REQUIRE_COSIGN=1, so the rho_<ver>_<os>_<arch> archive is checked
  against the signed checksums.txt (exact release-workflow identity)
  and its SHA-256 before anything runs; Go is no longer required;
- resolves the version as: rho-version input (0.3.0, v0.3.0 or latest),
  else the release tag the action is pinned to, else the latest release
  via `gh release view` with the github-token input;
- validates rho-repo (owner/repo) and the version before use.

Releases before v0.3.0 are refused with a clear message by install.sh.
Also drops the stale feat/ecosystem-wiring-pr branch mapping in ci.yml.

Checked locally by extracting the step: a @main pin resolves latest
(v0.2.0) and is refused as pre-rename, rho-version 0.3.0 fails with a
clear 404 until that release exists, and malformed rho-version/rho-repo
exit 2.

Refs: F031, F254

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every documented install path failed for a new user:

- `curl .../main/install.sh | sh` requested rho_0.2.0_* assets from the
  latest release, which only has hawk_* assets (404);
- `brew install graycodeai/tap/rho`: GrayCodeAI/homebrew-tap does not exist;
- `npm install -g @graycodeai/rho`: nothing is published (E404);
- `go install .../cmd/rho@latest`: @latest is the hawk-era v0.2.0, which
  has no cmd/rho ("does not contain package").

The Install section now says prebuilt installs are available from
v0.3.0 (the first rho-named release) and points to the source build
until then; shows the script from the immutable v0.3.0 tag (tags are
protected) with what it verifies and RHO_REQUIRE_COSIGN=1; lists the
release assets; and states plainly that Homebrew and npm packages do not
exist. The contributor `go install` line uses @main, verified today with
a clean GOBIN against proxy.golang.org, and explains why @latest fails
until v0.3.0 is tagged.

Refs: F002, F019, F025, F262

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`npm install -g @graycodeai/rho` is E404: nothing is published, there is
no publish job, and the owner decided not to create the npm org now. The
scaffolding also had defects that would have broken a publish:

- every package.json said "license": "Apache-2.0" while the project is
  MIT (LICENSE, goreleaser);
- the assemble script stamped only the sub-packages, leaving the meta
  package and its optionalDependencies at 0.0.0-development, so every
  install would report "platform package not installed";
- it looked for dist/rho_<os>_arm64/ but GoReleaser v2 writes
  dist/rho_<os>_arm64_v8.0/, so the arm64 targets always failed.

Changes: license MIT everywhere; "private": true on all seven packages
so `npm publish` refuses until a real channel exists; the assemble
script takes the version from VERSION (or RHO_NPM_VERSION), validates
it, stamps the meta package and its optionalDependencies, and uses the
real GoReleaser v2 directory names. npm/README.md states the channel is
not available and lists what a publish needs. The README no longer
advertises npm.

Checked on a scratch copy against the local snapshot dist/: all six
targets assembled at 0.3.0 and the meta package pins 0.3.0.

Refs: F020

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
flake.nix declared inputs for five GitHub repositories that no longer
exist (merlin, kestrel, shrike, swift, harrier), so `nix build` and
`nix develop` failed while fetching inputs. Even with those removed it
would have built with vendorHash = null and GONOSUMCHECK=1 (no
dependency integrity), hardcoded version 0.0.1, and no flake.lock.
Nothing in CI evaluated it and the README does not offer Nix.

Nix is not installed where this change was prepared and CI never ran it,
so a fixed flake (real vendorHash, flake.lock, `nix flake check` in CI)
could not be verified. Rather than ship another unverified install path, remove
flake.nix and the `use flake` .envrc that only loaded it; /.envrc is now
gitignored so a local direnv file (which `make test-live` may read keys
from) is never committed. Re-adding a verified flake is recorded as a
follow-up in docs/RELEASING.md.

Refs: F027, F211, F263

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CI "size check" only emitted a ::warning above 98 MB and never
failed; `make size-check` warned at a different threshold (80 MB, "matching
CI"), and both comments claimed the binary was ~95 MB. The stripped
linux/amd64 build of ./cmd/rho is 62,492,834 bytes (59.6 MiB) today, so
any regression short of +38 MB passed silently.

The budget now lives in one place, SIZE_LIMIT_MB := 70 in the Makefile
(~10 MiB headroom), `make size-check` exits 1 above it (and no longer
needs bc or platform-specific stat flags), and the CI build job runs
`make size-check` on linux/amd64 instead of its own copy of the logic.

Verified: `make size-check` passes at 60 MiB and fails with
SIZE_LIMIT_MB=50.

Refs: F030

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dening

packaging/systemd/rho-daemon.service could not work as written:

- ProtectHome=true makes /home, /root and /run/user inaccessible, yet
  the unit granted ReadWritePaths=%h/.rho/state and ReadOnlyPaths=%h/.rho.
  In a system unit %h is always /root (systemd.unit(5)), and rho does not
  keep state in ~/.rho anyway (it uses the user config dir).
- The docs said the unit runs as user `rho`, but it had no User=, so it
  ran as root.
- `--api-key ${RHO_DAEMON_API_KEY}` put the key on the command line
  (visible in ps) although the daemon already reads the variable.
- Documentation= pointed at https://docs.rho.ai, which does not resolve.

The unit now runs as User=rho with StateDirectory=rho/CacheDirectory=rho
and points HOME, RHO_CONFIG_DIR, RHO_STATE_DIR and RHO_CACHE_DIR there,
keeps ProtectSystem=strict/ProtectHome=true/PrivateTmp, loads the API key
from an optional root-owned EnvironmentFile, and links the rho
troubleshooting guide, whose systemd section now documents the user,
paths, log file and how to grant project directories.

internal/testaudit/packaging_test.go parses the unit and fails on paths
hidden by ProtectHome, a missing User=, state outside StateDirectory, an
API key on ExecStart, or non-repository documentation links. The unit was
not started under systemd here (no systemd/systemd-analyze on this
machine); the ProtectHome and %h semantics are from systemd.exec(5) and
systemd.unit(5).

Refs: F032

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sgconfig.yaml pointed testDirs at a tests/ directory that does not
exist, and the setup never enforced anything:

- ast-grep (0.39.5, checked locally) only discovers sgconfig.yml, so
  `sg scan`/`sg test` in the repo root fail with "No ast-grep project
  configuration is found"; with `-c sgconfig.yaml`, `sg test` runs 0
  tests;
- the lefthook hook ran `sg scan` only when sg happened to be
  installed, piped through `head`, so it could never block a commit;
- CI never ran it;
- its single rule (no fmt.Println outside tests) contradicts the CLI,
  which prints user output with fmt.Println in ~178 places; the real
  guard for internal/ is testaudit's TestNoRawFmtPrintInInternal.

Delete sgconfig.yaml, rules/ and the lefthook hook rather than imply an
enforcement that does not exist.

Refs: F033

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CONTRIBUTING.md, README.md, docs/versioning.md, docs/compatibility.md,
the compatibility matrix, cmd/rho/main.go and the shared templates all
said release-please bumps VERSION, writes CHANGELOG.md and tags releases,
and told contributors never to edit either file. No release-please
config, manifest or workflow exists; releases are tags pushed by hand
and CHANGELOG entries are hand-written.

- New docs/RELEASING.md: the release PR checklist (VERSION, CHANGELOG
  section, compatibility matrix, README install URL), tagging, what
  release.yml does, how to verify a release, the status of every
  distribution channel (Homebrew, npm and Nix explicitly not available,
  with what each would need), and v0.3.0 specifics (hawk-era tags,
  retraction, "Latest" release).
- Documents the existing tag ruleset protect-release-tags (id 24053191):
  creation/update/deletion/non-fast-forward of refs/tags/v* restricted,
  repository admin role as the only bypass.
- Every release-please claim is replaced with the manual process; the PR
  checklist now asks for an [Unreleased] CHANGELOG entry instead of
  forbidding CHANGELOG edits.
- `make release` no longer runs `goreleaser release --clean` from a
  workstation (an unsigned publish with a personal token, which is how
  the hawk-era v0.2.0 assets were produced); it explains the tag-driven
  process and exits 1. New `make release-check` (the release.yml guard)
  and `make release-snapshot` (goreleaser check + snapshot build, no
  publish or signing) replace it for local validation.

Refs: F026, F028, F269

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CHANGELOG.md listed [0.2.0] (2026-07-13) above [0.4.0] and [0.3.0]
(May 2026), so versions were not monotonic, and it already had a
"## [0.3.0]" heading for hawk-era work although v0.3.0 is the next rho
release (the release guard would have accepted that stale section as the
v0.3.0 notes). [Unreleased] had two "### Changed" lists.

- A short history note explains that tags v0.1.0, v0.1.1 and v0.2.0 were
  built from the github.com/GrayCodeAI/hawk module and that v0.3.0 is
  the first rho release.
- Historical sections keep their text but are relabelled
  "[hawk 0.2.0]" (the v0.2.0 tag) and "[hawk X.Y.Z, untagged]" (earlier
  numbering that was never tagged), so no heading can be mistaken for a
  rho version.
- "Flux v0.2.1" is annotated as the eyrie version of the time, and the
  internal/jsonc, internal/providers and internal/permissions/verdict.go
  bullets are marked "(since removed)".
- The two [Unreleased] "### Changed" lists are merged, and this PR's
  release, install, packaging and removal changes are recorded.

testaudit's TestChangelogStructure requires a single leading
[Unreleased], release headings of the form "## [X.Y.Z] — YYYY-MM-DD" in
descending order and not above VERSION, "[hawk …]" for historical
entries, and unique subsection headings per section.

The "## [0.3.0] — <date>" section itself is written in the release PR
right before tagging (docs/RELEASING.md); until then
`make release-check` and release.yml refuse the v0.3.0 tag.

Refs: F270

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`go install github.com/GrayCodeAI/rho/cmd/rho@vX.Y.Z` builds without
ldflags, so main.Version stays "dev". DisplayVersion then fell back to
any VERSION file in the working directory or its parents (so running rho
inside another project could print that project's version) and
otherwise "dev"; `rho update` compared the raw "dev" string, which never
parses, and always reported an update.

DisplayVersion now prefers, after the ldflags version, the main module
version from debug.ReadBuildInfo when it is a clean release (e.g.
v0.3.0 -> "0.3.0"); "(devel)", pseudo-versions (@main, untagged commits)
and +dirty builds still fall back to the VERSION file, then "dev".
`rho update` uses DisplayVersion, so go-install and source builds compare
their real version.

Tests stub the build info for a tagged module, each development form,
and the ldflags precedence.

Refs: F018, F025

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
proxy.golang.org serves v0.1.0, v0.1.1 and v0.2.0 for
github.com/GrayCodeAI/rho, but those tags were cut before the rename:
their go.mod declares module github.com/GrayCodeAI/hawk and they contain
no cmd/rho, so `go install .../cmd/rho@latest` fails with "does not
contain package" and tooling (pkg.go.dev, dependabot) lists them as rho
versions. The proxy keeps them forever, so retract the range with a
rationale; retractions take effect from the first release that carries
this go.mod (v0.3.0), after which @latest is v0.3.0 and the old
versions are marked retracted.

`go mod tidy -diff`, `go mod verify` and the replace-directive guard
pass; a testaudit test keeps the range retracted and ensures VERSION is
never inside it.

Refs: F025

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant