garrytan / garrytan/gstack

Proposal: platform-agnostic versioning for /ship (no-VERSION-file projects)

Open
#1,474 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
133k
Forks
19.9k
Avg merge
18h 46m
Merged PRs (30d)
26

Description

Hi — opening this as a design discussion (not a PR yet). I'm running gstack on a Python framework that manages release identity through git tags + `CHANGELOG.md` headers only — no `VERSION` file at the repo root. The release model is "accumulate in `## [Unreleased]`, periodic release cuts promote to a dated `## [X.Y.Z]` header" — pretty close to standard Keep a Changelog flow.

`/ship` is excellent and I want every PR to go through it. Two hard-fail points block that today:

1. **Step 9** STOPs when `.claude/skills/review/checklist.md` is missing. The skill is intentionally strict on this; my project just didn't have a project-local checklist.
2. **Step 12** `exit 1`s on the regex `^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$` because there's no 4-digit `VERSION` file. The queue-aware workspace-ship feature (`bin/gstack-next-version`) is also pinned to 4-digit input.

This is consistent with gstack's documented model (workspace-aware ship, VERSION as monotonic ordered identifier, every PR is its own release) but doesn't fit a project that ships releases via tags + CHANGELOG promotion only.

## What I proposed in my local fork

For my own unblock today I patched `ship/SKILL.md.tmpl` on a local branch with two strictly-additive guards:

**Step 9 fallback (missing-checklist):**
- If `.claude/skills/review/checklist.md` is missing, fall back to an inline default checklist covering universal categories (SQL safety, LLM trust boundary, concurrency/atomicity, error handling, auth/secrets, public surface hygiene, dependency risk) and warn. Existing behavior (STOP on unreadable-but-present file) preserved.

**Step 12 NO_VERSION_FILE mode:**
- Probe `[ ! -f VERSION ]` BEFORE the existing FRESH/ALREADY_BUMPED/DRIFT_STALE_PKG/DRIFT_UNEXPECTED state machine. If absent, AskUserQuestion: A) PR-level update (bullets to `## [Unreleased]`, no version bump, no header promotion) or B) release cut (operator provides new 3-digit semver, validated against the latest dated CHANGELOG header, `## [Unreleased]` promoted to `## [X.Y.Z] - `). The `CHANGELOG_VERSION` extraction uses an awk state machine that skips fenced code blocks (caught by an adversarial review round — `grep -m1 -E '^## \[[0-9]'` was picking up example versions inside ```` ``` ```` blocks in the `### BREAKING` callout demo).
- No-VERSION + no-CHANGELOG → explicit STOP with actionable error.
- `bin/gstack-next-version` is not called in NO_VERSION_FILE mode (it strictly requires 4-digit input).

**Step 13 CHANGELOG resolver — mode dispatch:**
- Top-of-resolver branch on `SHIP_MODE`. PR-level: validate `## [Unreleased]` exists and has bullets, STOP if not (operator owns CHANGELOG voice). Release-cut: promote `## [Unreleased]` to dated 3-digit header. Otherwise: existing auto-generate flow for VERSION-file projects.

**Step 19 PR title rule — mode-gated:**
- Default and release-cut mode keep the existing `v$NEW_VERSION` mandate + `bin/gstack-pr-title-rewrite.sh` self-check.
- PR-level mode drops the version prefix (every PR in PR-level mode would otherwise carry the same `v0.13.0` until the next release cut, misleading anyone scanning the PR list). Conventional Commit prefix is left alone.

## Why a proposal, not a PR

This is a meaningful change to a load-bearing skill (`ship` is 3K+ lines, very tuned). Before opening a PR I want your read on:

1. **Is this the right shape?** Detecting absence of a `VERSION` file feels like the lowest-friction signal — but you may prefer an explicit per-project opt-in via `gstack-config set versioning_mode tag-based` or similar. That's more explicit but adds setup friction.
2. **Should it route through `gstack-next-version`?** I excluded the util in NO_VERSION_FILE mode since it requires 4-digit input. If you'd want queue-aware shipping to work for tag+CHANGELOG projects too, the util would need 3-digit support (and the queue-claim semantics for projects where every PR doesn't bump VERSION need defining).
3. **Should Step 9 fallback move to a `.gstack-config` opt-in?** The current STOP-on-missing-checklist behavior is intentional discipline. Removing it without a knob may erode that discipline for projects that *should* have a checklist but don't.
4. **`document-release` subagent's depth-2 markdown heuristic** doesn't reach `docs/spec/*.md` or non-markdown files. For my project I documented an "Operator Manual Surface Check" in a release-runbook to cover that gap manually — but a project-local "doc-sync-map" sidecar that the subagent reads would be cleaner. Out of scope for this proposal but flagging for context.

I'm happy to open a PR if the shape is roughly right. The whole local patch is on a feature branch I'd rebase on current main; ~80 lines of `.tmpl` changes + 9 lines in `scripts/resolvers/utility.ts`. `bun test test/skill-validation.test.ts test/gen-skill-docs.test.ts` passes 707/707 with the patch.

## Context

- Project: https://github.com/dep0we/atomic-agents-stack (Python AI agent framework, 928 tests, pre-1.0)
- Dogfood PR using the patched /ship end-to-end: https://github.com/dep0we/atomic-agents-stack/pull/159
- Adversarial review caught the awk-vs-grep fenced-block regex bug pre-merge — that bug alone would have shipped wrong PR titles + accepted regressed version numbers, so this proposal is grateful for `codex_reviews:`-required + Opus-subagent-as-fallback discipline being core to the skill.

Thanks for gstack. Happy to discuss or close if you'd prefer this stay project-local.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.