fix(release): correctly promote and demote releases - #132
Merged
Conversation
Bare-boolean parsing dropped the unset form so promoting a prerelease silently no-op'd, and --latest was rejected as unknown. Forward both equals-forms to gh so the GitHub API actually applies the change. Co-authored-by: Cursor <cursoragent@cursor.com>
Confidence Score: 5/5The PR appears safe to merge, with the release boolean forwarding behavior covered through observable stateful CLI round trips. The changed parser retains bare and explicit boolean values, rejects conflicting repetitions before invoking Reviews (1): Last reviewed commit: "no-mistakes(document): Consolidate relea..." | Re-trigger Greptile |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Fix a real agent-ergonomics correctness bug:
gh-axi release edit <tag> --prerelease=false --latestsilently ACCEPTS the command (prints the tag) but does NOT apply the change - the release stayed prerelease=true and was not promoted to latest. Separately,gh-axi apiappeared GET-only so there was no gh-axi path to promote a release.Make
gh-axi release editcorrectly pass through--prerelease=false(the UNSET form, promoting a prerelease to a full release) and--latest[=true|false](mark/unmark repo latest) so promoting/demoting a release actually works. Handle the boolean-with-value forms correctly (--prerelease=false,--latest,--latest=false) - a common cause is a bare-boolean flag parser that treats--prereleaseas always-true and drops the=false, or that omits latest entirely. Verify the change ROUND-TRIPS against the GitHub API: after the edit, the API read-back shows prerelease=false and the release is the repo's latest.gh-axi apiwrite support - pick one and justify it: EITHER add a write path togh-axi api(--method/-XPATCH/POST/PUT/DELETE plus a--field/-f/--raw-fieldbody path, mirroringgh api), OR clearly DOCUMENT thatgh-axi apiis read-only (GET) by design and point users torelease edit(and typed subcommands) for writes - and make it FAIL LOUDLY with that guidance if a user passes a write method/body, never silently drop it. Prefer whichever is more consistent with gh-axi's existing surface and AXI ergonomics (agents must never get a silent no-op). Decision made during implementation: keep the existing write path (POST/PUT/PATCH/DELETE,-X,--field,--inputalready shipped); documenting read-only would regress that surface. Do not remove or weaken writes.Tests must be executable behavioral coverage that drives the real code path (CLI end-to-end against a mocked/faked gh or GitHub API layer), asserting OBSERVABLE behavior, not source text/snapshots:
release edit --prerelease=false --latest, the release reads prerelease=false AND is the repo latest (round-trip).--latest=false/--prerelease(set-true) also behave correctly (no regressions to the working forms).Do not weaken any auth handling - gh-axi delegates auth to
gh; keep that. Keep the change tight and well-tested. Public npm publish / release go-no-go is out of scope.What Changed
--prerelease,--draft, and--latestrelease edit flags, including explicit=falsevalues, while rejecting conflicting repetitions.Risk Assessment
✅ Low: The change is tightly scoped, correctly forwards release boolean value forms, and adds stateful CLI-level round-trip coverage for release edits and API writes.
Testing
Targeted command tests and public CLI end-to-end checks against a stateful fake GitHub backend confirmed persisted release promotion/demotion, boolean flag behavior, and API PATCH read-back. A reviewer-visible CLI transcript records the complete flow.
Evidence: Stateful public CLI release and API round-trip transcript
Source: Stateful public CLI release and API round-trip transcript
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
🔧 **Review** - 2 issues found → auto-fixed (2) ✅
test/commands/release.test.ts:411- Intent requires "CLI end-to-end against a mocked/faked gh or GitHub API layer" and an API read-back proving prerelease=false and that the release is repo latest. These tests call releaseCommand directly and use an in-process argv interpreter with a singlelatestboolean, so they neither execute the CLI/gh boundary nor read back/releases/latest. Likewise, test/commands/api.test.ts:64 returns the desired PATCH response unconditionally rather than performing a stateful write/read round-trip. Add executable CLI coverage using a fake gh/API whose persisted state is queried after each write.src/commands/release.ts:121- appendOptionalValueBoolFlag forwards the first--flag=valueoccurrence and returns, leaving any bare or additional valued occurrence silently ignored. Forrelease edit v1 --latest=false --latest, it sends only--latest=false, despite the final valid form requesting true. Reject repeated forms as conflicting, or consume and forward them with defined ordering, rather than accepting and dropping one.🔧 Fix: Reject conflicting repeated release boolean flags
1 error still open:
test/commands/release.test.ts:411- Required "CLI end-to-end against a mocked/faked gh or GitHub API layer" coverage is still absent. These tests invoke releaseCommand directly and mutate an in-process state object rather than executing the CLI and reading/releases/latest; test/commands/api.test.ts similarly returns a predetermined PATCH response without a stateful write/read round-trip. Add CLI-level tests backed by a stateful fake gh/API, then read back the edited release and latest endpoint to verify the persisted result.🔧 Fix: Add stateful CLI release and API round-trips
✅ Re-checked - no issues remain.
✅ **Test** - passed
✅ No issues found.
pnpm exec vitest run test/integration/release-api-roundtrip.integration.test.tspnpm exec vitest run test/commands/release.test.ts -t 'edit|boolean-with-value'Public CLI stateful-fake round-trip: initial reads,release edit v1.0.0 --prerelease=false --latest,--latest=false, bare--prerelease, API PATCH, and subsequent API GET read-backs✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.