mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-27 15:11:47 +02:00
* v1.89.1.0 fix: remove continuous checkpoint commits and repair validation blockers * fix: clarify shipping and engineering review recovery * fix: interpret native no-change review descriptions * test: separate descendant readiness from timeout delivery
489 lines
26 KiB
Cheetah
489 lines
26 KiB
Cheetah
---
|
||
name: ship
|
||
preamble-tier: 4
|
||
version: 1.0.0
|
||
description: |
|
||
Ship workflow: detect + merge base branch, run tests, review diff, bump VERSION,
|
||
update CHANGELOG, commit, push, create PR. Use when asked to "ship", "deploy",
|
||
"push to main", "create a PR", "merge and push", or "get it deployed".
|
||
Proactively invoke this skill (do NOT push/PR directly) when the user says code
|
||
is ready, asks about deploying, wants to push code up, or asks to create a PR. (gstack)
|
||
allowed-tools:
|
||
- Bash
|
||
- Read
|
||
- Write
|
||
- Edit
|
||
- Grep
|
||
- Glob
|
||
- Agent
|
||
- AskUserQuestion
|
||
- WebSearch
|
||
sensitive: true
|
||
triggers:
|
||
- ship it
|
||
- create a pr
|
||
- push to main
|
||
- deploy this
|
||
---
|
||
|
||
{{PREAMBLE}}
|
||
|
||
{{THIRD_PARTY_ACTIONS}}
|
||
|
||
# Ship: Fully Automated Ship Workflow
|
||
|
||
Run `/ship` through to the PR URL. This request authorizes routine work without confirmation; explicit safety and user-decision gates still apply.
|
||
|
||
**Follow every STOP and AskUserQuestion gate**, including:
|
||
- On the base branch (abort)
|
||
- Merge conflicts that can't be auto-resolved (stop, show conflicts)
|
||
- In-branch test failures (pre-existing failures are triaged, not auto-blocking)
|
||
- Pre-landing review finds ASK items that need user judgment
|
||
- Prior Learnings needs its first-time cross-project setting (Step 8)
|
||
- MINOR or MAJOR version bump needed (ask — see Step 12)
|
||
- Greptile review comments that need user decision (complex fixes, false positives)
|
||
- AI-assessed coverage below target (see Step 7 for minimum/target decisions)
|
||
- Plan items NOT DONE or UNVERIFIABLE (see Step 8)
|
||
- Plan verification failures (see Step 8.1)
|
||
- TODOS.md missing and user wants to create one (ask — see Step 14)
|
||
- TODOS.md disorganized and user wants to reorganize (ask — see Step 14)
|
||
|
||
**Never stop for:**
|
||
- Uncommitted changes (always include them)
|
||
- Version bump choice (auto-pick MICRO or PATCH — see Step 12)
|
||
- CHANGELOG content (auto-generate from diff)
|
||
- Commit message approval (auto-commit)
|
||
- Multi-file changesets (auto-split into bisectable commits)
|
||
- TODOS.md completed-item detection (auto-mark)
|
||
- Auto-fixable review findings (dead code, N+1, stale comments — fixed automatically)
|
||
- Test coverage gaps within target threshold (generate, verify, then commit with Step 15; flag any remaining gaps in the PR body)
|
||
|
||
**Re-run behavior (idempotency):**
|
||
Every invocation repeats verification: tests, coverage, plan completion, both
|
||
reviews, VERSION/CHANGELOG, TODOS and doc-sync. Only *actions* are idempotent:
|
||
- Step 12: If VERSION already bumped, skip the bump but still read the version
|
||
- Step 17: If already pushed, skip the push command
|
||
- Step 19: If PR exists, update the body instead of creating a new PR
|
||
Prior execution never exempts verification.
|
||
|
||
---
|
||
|
||
{{SECTION_INDEX:ship}}
|
||
|
||
---
|
||
|
||
{{BASE_BRANCH_DETECT}}
|
||
|
||
`<base>` means the detected branch name for fetch/helper arguments;
|
||
`origin/<base>` is its remote-tracking ref for comparisons. Step 1 fetches it.
|
||
|
||
{{GBRAIN_CONTEXT_LOAD}}
|
||
|
||
## Step 0.9: Apple target detection
|
||
|
||
If the repo has an `.xcodeproj`, `.xcworkspace`, or Swift app package AND the ask
|
||
is App Store/TestFlight distribution, **STOP and Read
|
||
`~/.claude/skills/gstack/ship/sections/apple-release.md` FIRST**. Store distribution proceeds
|
||
through that adapter from the current branch, including a clean base branch.
|
||
The branch gate and repository-landing pipeline below apply ONLY to
|
||
repository-landing asks, including on Apple repos.
|
||
|
||
## Step 1: Pre-flight
|
||
|
||
1. Check the current branch. If on the base branch or the repo's default branch, **abort**: "You're on the base branch. Ship from a feature branch."
|
||
|
||
2. Run `git status` (never use `-uall`). Uncommitted changes are always included — no need to ask.
|
||
|
||
3. Run `git fetch origin <base>` before inspecting the diff. If fetch fails, STOP:
|
||
report the error and restore access before continuing. Then inspect
|
||
`git diff origin/<base> --stat`, untracked files from status, and
|
||
`git log origin/<base>..HEAD --oneline`.
|
||
|
||
4. Display historical review readiness. This preflight snapshot does not replace
|
||
Step 9's mandatory review or its blocker, ASK, and convergence gates — even
|
||
when prior reviews are CLEAR or the dashboard's global skip is enabled.
|
||
|
||
{{REVIEW_DASHBOARD}}
|
||
|
||
If Eng Review is not CLEAR, print its actual status and reason: "Eng Review: {status} — {reason}. Ship will run its pre-landing review in Step 9." For diffs >200 lines (`git diff origin/<base> --stat | tail -1`), recommend `/plan-eng-review` or `/autoplan` for architecture review.
|
||
|
||
If CEO Review is missing, mention as informational ("CEO Review not run — recommended for product changes") but do NOT block.
|
||
|
||
For Design Review: run `source <(~/.claude/skills/gstack/bin/gstack-diff-scope <base> 2>/dev/null)`. If `SCOPE_FRONTEND=true` and no design review exists, mention: "Design Review not run — Step 9 includes the lite check; consider /design-review for a full visual audit."
|
||
|
||
Continue to Step 2 without a preflight approval question. Apply the review gates when Step 9 runs.
|
||
|
||
---
|
||
|
||
## Step 2: Distribution Pipeline Check
|
||
|
||
If the diff introduces a new standalone artifact (CLI binary, library package, tool) — not a web
|
||
service with existing deployment — verify that a distribution pipeline exists.
|
||
|
||
1. Check if the diff adds a new `cmd/` directory, `main.go`, or `bin/` entry point:
|
||
```bash
|
||
git diff origin/<base> --name-only | grep -E '(cmd/.*/main\.go|bin/|Cargo\.toml|setup\.py|package\.json)' | head -5
|
||
```
|
||
Also inspect matching untracked files from Step 1's status.
|
||
|
||
2. If new artifact detected, check for a release workflow:
|
||
```bash
|
||
ls .github/workflows/ 2>/dev/null | grep -iE 'release|publish|dist'
|
||
grep -qE 'release|publish|deploy' .gitlab-ci.yml 2>/dev/null && echo "GITLAB_CI_RELEASE"
|
||
```
|
||
|
||
3. **If no release pipeline exists and a new artifact was added:** Use AskUserQuestion:
|
||
- "This PR adds a new binary/tool but there's no CI/CD pipeline to build and publish it.
|
||
Users won't be able to download the artifact after merge."
|
||
- A) Add a release workflow now (CI/CD release pipeline — GitHub Actions or GitLab CI depending on platform)
|
||
- B) Defer — add a P1 distribution TODO in Step 14
|
||
- C) Not needed — this is internal/web-only, existing deployment covers it
|
||
|
||
4. **If the user chooses A:** Add packaging and publish configuration using this repository's CI conventions. Ask for the intended distribution target if it is unknown; do not invent a registry or credentials. Include the new workflow in the tests and review below. Do not publish a release during `/ship`.
|
||
5. **If release pipeline exists:** Continue silently.
|
||
6. **If no new artifact detected:** Skip silently.
|
||
|
||
---
|
||
|
||
## Step 3: Merge the base branch (BEFORE tests)
|
||
|
||
Merge the base ref fetched in Step 1 so tests and reviews cover the integrated code:
|
||
|
||
```bash
|
||
git merge origin/<base> --no-edit
|
||
```
|
||
|
||
**If there are merge conflicts:** Try to auto-resolve if they are simple (VERSION, schema.rb, CHANGELOG ordering). If conflicts are complex or ambiguous, **STOP** and show them.
|
||
|
||
**If already up to date:** Continue silently.
|
||
|
||
---
|
||
|
||
{{SECTION:tests}}
|
||
|
||
{{SECTION:test-coverage}}
|
||
|
||
{{SECTION:plan-completion}}
|
||
|
||
{{SECTION:review-army}}
|
||
|
||
{{SECTION:greptile}}
|
||
|
||
{{SECTION:adversarial}}
|
||
|
||
## Step 12: Version bump (auto-decide)
|
||
|
||
Use **`gstack-version-bump`** for classify/write/repair and `gstack-next-version`
|
||
for slot selection. Bump level and queue collisions remain agent decisions.
|
||
|
||
1. **Classify state** — pure reader, never writes:
|
||
```bash
|
||
bun run ~/.claude/skills/gstack/bin/gstack-version-bump classify --base <base>
|
||
```
|
||
Save the JSON `baseVersion` as `BASE_VERSION`, then read `state` and dispatch:
|
||
- **FRESH** → do the bump (steps 2-4).
|
||
- **ALREADY_BUMPED** → keep `NEW_VERSION` at `currentVersion`. Use the recorded level for this release; if absent, compare `baseVersion` and `currentVersion` left to right: the first changed major/minor/patch/micro component supplies `BUMP_LEVEL` (a missing fourth component is zero). Then run step 3's queue check. This recovers the level, not permission to bump again.
|
||
- **DRIFT_STALE_PKG** → run `gstack-version-bump repair`, then reclassify. On success, follow **ALREADY_BUMPED**, including its queue check; on failure, STOP. Repair alone never re-bumps.
|
||
- **DRIFT_UNEXPECTED** → **STOP**. package.json disagrees with VERSION while VERSION matches base — a manual edit bypassed /ship. Reconcile manually, then re-run.
|
||
|
||
2. **Decide the bump level** from the diff (agent judgment):
|
||
- **MICRO**: <50 lines, trivial tweaks/config. **PATCH**: 50+ lines, no feature signals.
|
||
- **MINOR**: AskUserQuestion for any feature signal (new route/page, migration, new module), OR 500+ lines. **MAJOR**: AskUserQuestion for milestones or breaking changes. Offer the recommended level with rationale, a smaller level, or cancel; wait for the answer. Cancel ends this ship attempt before release writes or push; preserve existing work.
|
||
Save `BUMP_LEVEL` as lowercase `micro`, `patch`, `minor`, or `major`. Queue placement may advance the slot without changing the intended level.
|
||
|
||
3. **Queue-aware pick** (workspace-aware ship):
|
||
```bash
|
||
QUEUE_JSON=$(bun run ~/.claude/skills/gstack/bin/gstack-next-version --base <base> --bump "$BUMP_LEVEL" --current-version "$BASE_VERSION" 2>/dev/null || echo '{"offline":true}')
|
||
CANDIDATE_VERSION=$(echo "$QUEUE_JSON" | jq -r '.version // empty')
|
||
```
|
||
- **Usable candidate** (including `offline:true` with `fallback:"git"`): print warnings and any claimed queue. FRESH sets `NEW_VERSION` to `CANDIDATE_VERSION`. ALREADY_BUMPED compares it with `currentVersion`; if different, ask to rebump (refresh CHANGELOG/PR title) or keep current (CI rejects a collision). Only approval changes the existing version. An active sibling is a workspace listed in JSON `active_siblings`; use its `branch` and `version`. If one holds `>= NEW_VERSION`, ask to advance past it or stop this attempt and sync.
|
||
- **No usable candidate** (utility failure or empty result): print queue-unverified; FRESH sets `NEW_VERSION` using local `BUMP_LEVEL` arithmetic, while ALREADY_BUMPED keeps `currentVersion`. Do not use the candidate branch above.
|
||
|
||
4. **Write the bump** (FRESH, or an approved rebump):
|
||
```bash
|
||
bun run ~/.claude/skills/gstack/bin/gstack-version-bump write --version "$NEW_VERSION" --regen-digest
|
||
```
|
||
The CLI validates 4-digit `MAJOR.MINOR.PATCH.MICRO` (or 3-digit pinned semver), then writes VERSION, the manifest, and existing `package-lock.json` / `npm-shrinkwrap.json` files; it never creates lockfiles. Manifest resolution: `--package-json-path` → `.gstack/package-json-path` → `./package.json` (supports subdirectory packages). npm manifests/locks use the 3-digit translation (`1.67.0.0` → `1.67.0`); VERSION remains authoritative. Exit 3 means a half-write: reclassify and use `repair` for DRIFT_STALE_PKG.
|
||
|
||
`--regen-digest` executes repo code with the same privileges as Step 5: `scripts/gen-agents-digest.ts`, only when it and committed `agents-digest/gstack-AGENTS.md` both exist. Check `agentsDigest`: if false, run `bun scripts/gen-agents-digest.ts` and stage the digest with the bump before continuing. Its VERSION stamp is freshness-gated.
|
||
|
||
5. **Record the release decision** (skip if ALREADY_BUMPED):
|
||
```bash
|
||
~/.claude/skills/gstack/bin/gstack-decision-log '{"decision":"Ship NEW_VERSION (BUMP_LEVEL)","rationale":"WHY","scope":"repo","source":"skill","confidence":9}' 2>/dev/null || true
|
||
```
|
||
Substitute `NEW_VERSION`, `BUMP_LEVEL`, and one-line `WHY` (scope or breaking-change signal). Best-effort, non-interactive, non-blocking.
|
||
|
||
{{SECTION:changelog}}
|
||
|
||
## Step 14: TODOS.md (auto-update)
|
||
|
||
Persist approved follow-ups, then conservatively mark completed work.
|
||
|
||
Read `.claude/skills/review/TODOS-format.md` for the canonical format reference.
|
||
|
||
**1. Open or create:** Read root `TODOS.md`. An earlier explicit "add TODO" choice authorizes its creation with `# TODOS` and `## Completed`. Otherwise, if missing, ask: "Create a component/priority-organized TODOS.md?" Options: A) Create now, B) Skip. If B, continue to Step 15 with the outcome in the summary below.
|
||
|
||
**2. Organization:** Expect component headings, `**Priority:**` P0–P4 fields, and `## Completed` at the bottom. If disorganized, ask: A) Reorganize (recommended), B) Leave as-is. A preserves all content; B continues without restructuring.
|
||
|
||
**3. Add approved deferrals:**
|
||
- Step 2: add the approved distribution follow-up as P1 with the missing pipeline and affected artifact.
|
||
- Step 8: add each approved P1 plan deferral with `Deferred from plan: {plan file path}` and the missing work.
|
||
- Step 5: retain P0 test-failure entries already written; deduplicate by failure and source, adding missing approved entries with error output and branch.
|
||
Never turn dropped scope into TODOs or invent unapproved follow-ups. Reuse matching existing entries rather than duplicating them.
|
||
|
||
**4. Detect completed TODOs:** Match titles, files, and behavior against `git diff origin/<base>`, untracked files from status, and `git log origin/<base>..HEAD --oneline`. Only clear evidence earns completion; leave uncertain items open. Move completed items to `## Completed` and append `**Completed:** vX.Y.Z (YYYY-MM-DD)`.
|
||
|
||
**5. Save the summary:** Report added/deferred items, items marked complete, remaining count, and any creation/reorganization. If creation was declined or a write fails, warn and retain the unpersisted follow-ups in the Step 19 PR summary; never claim they were saved. A TODO write failure remains non-blocking.
|
||
|
||
---
|
||
|
||
## Step 15: Commit (bisectable chunks)
|
||
|
||
Create small, logical commits for `git bisect`. If all changes are already committed, continue to Step 16; never create an empty commit.
|
||
|
||
1. Group by coherent change. Keep each model/service/controller with its tests;
|
||
keep controller views together. Migrations may stand alone or accompany their
|
||
model; config/routes may accompany the feature they enable. A diff under
|
||
50 lines across fewer than 4 files may use one commit.
|
||
2. Order dependencies first: infrastructure → models/services → controllers/views.
|
||
Each commit must work independently, without broken imports or missing code.
|
||
VERSION + CHANGELOG + TODOS.md belong in the final commit.
|
||
3. Use `<type>: <summary>` (feat/fix/chore/refactor/docs) and a brief body.
|
||
Only the final VERSION/CHANGELOG commit gets the version tag and co-author trailer:
|
||
|
||
```bash
|
||
git commit -m "$(cat <<'EOF'
|
||
chore: bump version and changelog (vX.Y.Z.W)
|
||
|
||
{{CO_AUTHOR_TRAILER}}
|
||
EOF
|
||
)"
|
||
```
|
||
|
||
---
|
||
|
||
## Step 16: Verification Gate
|
||
|
||
**IRON LAW: NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE.**
|
||
|
||
Find generation/build commands in CLAUDE.md/AGENTS.md, package scripts, and build
|
||
configuration; run them first, skipping only when none are defined. A failed build blocks push. If it changes tracked files, inspect the
|
||
changes, run affected checks from Steps 6–11, refresh release facts, and commit
|
||
under Step 15 before returning here. Reuse unchanged results and actual approvals.
|
||
|
||
Then check test evidence against the final content:
|
||
|
||
```bash
|
||
~/.claude/skills/gstack/bin/gstack-evidence check --label tests --expect-cmd '<exact tests-lane command from Step 5>' --label vitest --expect-cmd '<exact vitest-lane command from Step 5>' --max-age 24 --allow-paths CHANGELOG.md,VERSION,package.json,agents-digest/gstack-AGENTS.md
|
||
```
|
||
|
||
Use only Step 5's actual lane labels and exact commands; `vitest` is an example.
|
||
If Step 4 explicitly declined testing and no lanes exist, report that gap instead
|
||
of inventing FRESH evidence. Build verification still applies.
|
||
|
||
The allow-list covers release bookkeeping, including Step 12's package/digest
|
||
version stamps. Behavioral package.json edits still require live tests despite
|
||
the path exemption. Do not add `TODOS.md` or generated tests to the allow-list:
|
||
Step 7 tests, review fixes, and Step 14 TODO edits intentionally make evidence STALE.
|
||
|
||
- **Every line FRESH (exit 0):** recorded runs passed on identical content except
|
||
the listed release files. Cite label, exit, timestamp, and log path; continue.
|
||
- **Any STALE/MISSING (exit non-zero):** inspect the reason before choosing recovery:
|
||
- **Content, command or age mismatch, or no passing live evidence:** rerun the
|
||
affected lanes on final content, wrapped as `~/.claude/skills/gstack/bin/gstack-evidence run --label <lane> -- '<command>'`.
|
||
Read results and recheck once. TODO edits and generated tests are content
|
||
changes, not ledger-only bookkeeping.
|
||
- **Ledger read/write failure only:** if a successful live run already covers
|
||
the unchanged final content, exact command and permitted age, cite its exit,
|
||
timestamp and log directly. Report ledger unavailable and continue, never
|
||
ledger FRESH. Do not rerun green suites solely because the ledger cannot save
|
||
or read its record. If unchanged content cannot be confirmed, STOP.
|
||
|
||
A failed CHECK identifies evidence to repair; it is not a test failure. The
|
||
required live RUN must pass, except for the explicit triage waiver below.
|
||
|
||
Paste build and rerun results. Later code, test, or build-input changes return
|
||
through this gate before pushing. Step 18 owns validation of its post-push
|
||
docs-only edits; follow repository-required checks there too. Do not claim an
|
||
earlier test run covered changed inputs.
|
||
|
||
**If tests fail here:** apply Step 5's triage. A prior explicit waiver remains valid
|
||
only for the same verified pre-existing failures and approved scope; cite that
|
||
approval and actual failing counts, never FRESH or all-green evidence. New,
|
||
changed, or unwaived failures STOP publication and return to Step 5.
|
||
|
||
Claiming work is complete without verification is dishonesty, not efficiency.
|
||
|
||
---
|
||
|
||
## Step 17: Push
|
||
|
||
**Credential pre-push guard (#1946) — run before the push:**
|
||
|
||
```bash
|
||
_REDACT_PREPUSH=$(~/.claude/skills/gstack/bin/gstack-config get redact_prepush_hook 2>/dev/null || echo "false")
|
||
_HOOK_PATH=$(git rev-parse --git-path hooks/pre-push 2>/dev/null || echo "")
|
||
_HOOK_INSTALLED="no"
|
||
[ -n "$_HOOK_PATH" ] && [ -f "$_HOOK_PATH" ] && grep -q "gstack-redact" "$_HOOK_PATH" 2>/dev/null && _HOOK_INSTALLED="yes"
|
||
# Never silently install into custom core.hooksPath (e.g. committed .husky/).
|
||
_HOOKS_DIR=$(git rev-parse --git-path hooks 2>/dev/null || echo "")
|
||
_GIT_DIR=$(git rev-parse --absolute-git-dir 2>/dev/null || echo "")
|
||
# Worktree hooks live under the common git dir. /nonexistent prevents a
|
||
# failed lookup from producing a match-all /* pattern.
|
||
_GIT_COMMON=$(cd "$(git rev-parse --git-common-dir 2>/dev/null || echo /nonexistent)" 2>/dev/null && pwd || echo /nonexistent)
|
||
_HOOKS_IN_GIT_DIR="no"
|
||
case "$_HOOKS_DIR" in
|
||
"$_GIT_DIR"/*|"$_GIT_COMMON"/*|hooks|.git/hooks) _HOOKS_IN_GIT_DIR="yes" ;;
|
||
esac
|
||
_PREPUSH_PROMPTED=$([ -f "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" ] && echo "yes" || echo "no")
|
||
echo "REDACT_PREPUSH: $_REDACT_PREPUSH"
|
||
echo "HOOK_INSTALLED: $_HOOK_INSTALLED"
|
||
echo "HOOKS_IN_GIT_DIR: $_HOOKS_IN_GIT_DIR"
|
||
echo "PREPUSH_PROMPTED: $_PREPUSH_PROMPTED"
|
||
```
|
||
|
||
Branch on the echoed values:
|
||
|
||
1. **`REDACT_PREPUSH: true` and `HOOK_INSTALLED: no` and `HOOKS_IN_GIT_DIR: yes`** —
|
||
consent already given; install silently (no question) and continue:
|
||
```bash
|
||
~/.claude/skills/gstack/bin/gstack-redact install-prepush-hook
|
||
```
|
||
If `HOOKS_IN_GIT_DIR: no` (husky or another committed hooks dir), do NOT
|
||
install silently — print one line: "redact pre-push guard not installed:
|
||
this repo uses a custom core.hooksPath; run
|
||
`gstack-redact install-prepush-hook` manually if you want it chained."
|
||
2. **`REDACT_PREPUSH` not true AND `PREPUSH_PROMPTED: no`** — one-time
|
||
offer (fires once EVER, machine-wide). AskUserQuestion:
|
||
|
||
> gstack can install a per-repo git pre-push hook that blocks pushes
|
||
> containing credentials (API keys, tokens, private keys). It's a
|
||
> guardrail, not enforcement — `GSTACK_REDACT_PREPUSH=skip` bypasses it.
|
||
> Install it for repos you ship from?
|
||
|
||
Options:
|
||
- A) Yes — install the credential guard (recommended)
|
||
- B) No — never ask again
|
||
|
||
If A: run `~/.claude/skills/gstack/bin/gstack-config set redact_prepush_hook true`
|
||
then `~/.claude/skills/gstack/bin/gstack-redact install-prepush-hook`.
|
||
If B: run `~/.claude/skills/gstack/bin/gstack-config set redact_prepush_hook false`.
|
||
ALWAYS (after either answer, but NOT if the question itself failed to
|
||
render — a failed AskUserQuestion must re-offer next time):
|
||
```bash
|
||
touch "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted"
|
||
```
|
||
3. **Anything else** (declined earlier, or already installed) — continue
|
||
without comment.
|
||
|
||
**Idempotency check:** Check if the branch is already pushed and up to date.
|
||
|
||
```bash
|
||
LOCAL=$(git rev-parse HEAD) || exit 1
|
||
REMOTE_REF=$(git ls-remote --heads origin refs/heads/<branch-name>) || {
|
||
echo "STATUS: BLOCKED — cannot verify remote branch; restore access before pushing"
|
||
exit 1
|
||
}
|
||
REMOTE=$(printf '%s\n' "$REMOTE_REF" | awk '{print $1}')
|
||
REMOTE=${REMOTE:-none}
|
||
echo "LOCAL: $LOCAL REMOTE: $REMOTE"
|
||
[ "$LOCAL" = "$REMOTE" ] && echo "ALREADY_PUSHED" || echo "PUSH_NEEDED"
|
||
```
|
||
|
||
If `ALREADY_PUSHED`, skip the push but continue to Step 18. Otherwise push with upstream tracking:
|
||
|
||
```bash
|
||
git push -u origin <branch-name>
|
||
```
|
||
|
||
**If the push fails, STOP.** Report its error; do not run Steps 18–19 or claim
|
||
publication. For a non-fast-forward rejection, fetch and inspect the remote branch,
|
||
merge its changes without rewriting history, and return to Step 5 through Step 16
|
||
before retrying. Resolve ambiguous conflicts with the user; never force-push.
|
||
For authentication, hook, or network failures, fix that cause, rerun affected checks
|
||
if content changed, then recheck Step 16 before retrying. Never bypass a failed guard.
|
||
Only a successful push or verified `ALREADY_PUSHED` proceeds.
|
||
|
||
Continue to mandatory Step 18 (dispatch /document-release), then Step 19 (create/update PR/MR). A push alone does not complete /ship.
|
||
|
||
---
|
||
|
||
**PR/MR title invariant (always applies — do not skip even if you don't open the section below):** Any PR or MR you create OR update in the next step MUST have a title that starts with `v$NEW_VERSION` (the version bumped in Step 12), in the format `v<NEW_VERSION> <type>: <summary>`. Never create or edit a PR/MR title without this prefix. Compute the correct title with the single source of truth helper: `~/.claude/skills/gstack/bin/gstack-pr-title-rewrite.sh "$NEW_VERSION" "<current title>"`. The full create/update procedure (idempotency, redaction scan, self-check) is in the section below.
|
||
|
||
**Doc-sync invariant (always applies — do not skip even if you don't open the section below):** Step 18 dispatches the /document-release subagent BEFORE the PR/MR is created or updated in Step 19. Never skip the dispatch itself; only a failed subagent is non-blocking (proceed to Step 19 without a `## Documentation` section).
|
||
|
||
{{SECTION:pr-body}}
|
||
|
||
## Step 20: Persist ship metrics
|
||
|
||
Log coverage and plan completion for `/retro` through `gstack-review-log`.
|
||
It resolves the project/branch, validates JSON, creates storage and queues sync.
|
||
It takes **no path argument**: hand-built `<branch>-reviews.jsonl` paths break
|
||
branches containing `/`.
|
||
|
||
```bash
|
||
~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"ship","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","coverage_pct":COVERAGE_PCT,"plan_items_total":PLAN_TOTAL,"plan_items_done":PLAN_DONE,"verification_result":"VERIFY_RESULT","version":"VERSION","branch":"'"$(git rev-parse --abbrev-ref HEAD)"'"}'
|
||
```
|
||
|
||
Substitute from earlier steps:
|
||
- **COVERAGE_PCT**: coverage percentage from Step 7 diagram (integer, or -1 if undetermined)
|
||
- **PLAN_TOTAL**: total plan items extracted in Step 8 (0 if no plan file)
|
||
- **PLAN_DONE**: count of DONE + CHANGED items from Step 8 (0 if no plan file)
|
||
- **VERIFY_RESULT**: "pass", "fail", or "skipped" from Step 8.1
|
||
- **VERSION**: from the VERSION file
|
||
|
||
The branch name is filled in by the shell — there is no `BRANCH` placeholder to
|
||
substitute.
|
||
|
||
This step is automatic — never skip it, never ask for confirmation.
|
||
|
||
---
|
||
|
||
## Step 21: Plan-tune discoverability nudge (first-successful-ship only)
|
||
|
||
Plan-tune cathedral T15. After a successful ship, surface /plan-tune once
|
||
per machine. Single line, non-blocking, marker-gated so it never re-fires.
|
||
|
||
```bash
|
||
_NUDGE_MARKER="$HOME/.gstack/.plan-tune-nudge-shown"
|
||
_QT=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false")
|
||
if [ ! -f "$_NUDGE_MARKER" ] && [ "$_QT" = "false" ]; then
|
||
echo ""
|
||
echo "gstack can learn from your AskUserQuestion answers. Run /plan-tune to opt in"
|
||
echo "— it captures which prompts you find valuable vs noisy and (with hooks installed)"
|
||
echo "auto-decides your never-ask preferences."
|
||
touch "$_NUDGE_MARKER"
|
||
fi
|
||
```
|
||
|
||
If the marker exists, OR question_tuning is already on, the nudge is a
|
||
no-op. The marker guarantees at-most-once per machine. To re-enable:
|
||
`rm ~/.gstack/.plan-tune-nudge-shown` before next ship.
|
||
|
||
---
|
||
|
||
## Section self-check (before you finish)
|
||
|
||
You ran a carved skill. For your situation, list every section the Section index
|
||
named as applying, and confirm you issued a Read for each one. If you executed any
|
||
of those steps from memory without reading its section, you skipped the source of
|
||
truth — STOP, Read it now, and redo that step. Deterministic version work goes
|
||
through `gstack-version-bump`; never hand-roll the VERSION/package.json write.
|
||
|
||
---
|
||
|
||
## Important Rules
|
||
|
||
- **Never skip tests.** If tests fail, stop.
|
||
- **Never skip the pre-landing review.** If checklist.md is unreadable, stop.
|
||
- **Never force push.** Use regular `git push` only.
|
||
- **Never ask for trivial confirmations** (e.g., "ready to push?", "create PR?"). DO stop for: version bumps (MINOR/MAJOR), pre-landing review findings (ASK items), and Codex structured review [P1] findings (large diffs only).
|
||
- **Always use the 4-digit version format** from the VERSION file.
|
||
- **Date format in CHANGELOG:** `YYYY-MM-DD`
|
||
- **Split commits for bisectability** — each commit = one logical change.
|
||
- **TODOS.md completion detection must be conservative.** Only mark items as completed when the diff clearly shows the work is done.
|
||
- **Use Greptile reply templates from greptile-triage.md.** Every reply includes evidence (inline diff, code references, re-rank suggestion). Never post vague replies.
|
||
- **Never push without fresh verification evidence.** If code changed after Step 5 tests, re-run before pushing.
|
||
- **Step 7 generates coverage tests.** They must pass before committing. Never commit failing tests.
|
||
- **The goal is: user says `/ship`, next thing they see is the review + PR URL + auto-synced docs.**
|