--- 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 STOP blocks advancement until the stated repair/resume route clears; without one, end this attempt. Answer each AskUserQuestion before continuing. Routine authorization never waives those gates or their required user decisions. **Routine work needs no confirmation:** include uncommitted changes, choose MICRO/PATCH under Step 12, draft CHANGELOG and commits, mark completed TODOs and auto-fix findings. When Step 7 coverage meets its target, report remaining gaps and verify generated tests without asking again. Step 15 commits those tests. **Route:** integrate (1–3) → test and review (4–11.5) → prepare the release (12–15) → verify frozen content (16) → push and publish (17–21). Every new invocation repeats Steps 1–16, including both reviews and the docs audit. Steps 12, 17 and 19 prevent duplicate bumps, pushes and PRs, never verification. ### Keep state between steps Keep one private Markdown **invocation record** outside the product tree and save its absolute path, with these headings so a paused run can resume: - **Release:** versions, `BUMP_LEVEL`, reviewed tree and attempt counts. - **Decisions:** each approval's finding, files and authorized action. Reuse it only for that same scope; a repair never resets or expands approvals. - **Reviews:** handles, original start tokens, terminal states, outputs and queued fixes. - **Checks:** command/label, result/counts, timestamp, log and consumed inputs. - **Documentation:** candidate/id, attempts used, accepted hashes or named blocked exception. - **Next steps:** one ordered work list, with the current step marked. A **receipt** is saved evidence of a check's command, result and consumed content. A review's **start token** is the opaque value returned by `gstack-review-log --start` before it reads the diff. Keep `REVIEW_START` for Step 9, a separate `PASS_START` for each Step 11 attempt, and `DESIGN_START` for design. Finish each pass with its original token; `--finish` stamps the binding fields automatically. Never borrow or replace a token. `gstack-wtree` prints a Git tree hash covering tracked and non-ignored untracked files, not a commit ID. Use `git diff ` to compare these snapshots. ### Ship control flow You, the **parent** running /ship, own advancement; children return evidence, not permission to proceed. Follow the saved work list: 1. Start with Steps 1–21 in order, including 6.5, 11.5 and 14.5. Advance only after the current item's gates clear. 2. Expand a repair into individual steps and insert them before the still-pending work. This replaces the current item, whose result stays in the record. Add its destination only if not already the next pending step. 3. For another repair, repeat rule 2 without discarding pending work. The saved list outranks ordinary next-step sentences inside a repair. A range never adds unlisted steps. **Example:** Step 11 fixes insert `9 → 10 → 11` before 11.5. A further Step 9 fix affecting 6–8 makes the list `5 → 6 → 7 → 8 → 9 → 10 → 11 → 11.5`. The unchanged release steps follow. STOP and AskUserQuestion gates still apply during repairs. Keep the same attempt counts throughout the invocation. A range ending at Step 14 does not enter Step 14.5. A range including Step 14.5 enters its existing audit decision, not an unconditional new launch; its initial-plus-ONE limit never resets. Permitted repairs continue in this invocation without restarting /ship. --- {{SECTION_INDEX:ship}} --- {{BASE_BRANCH_DETECT}} `` means the detected branch name for fetch/helper arguments; `origin/` is its remote-tracking ref for comparisons. Step 1 fetches it. {{GBRAIN_CONTEXT_LOAD}} ## Step 0.9: Apple target detection If the ask is App Store/TestFlight distribution, look for an `.xcodeproj`, `.xcworkspace`, or Swift app product. Read `Package.swift` and its entrypoint to distinguish an app from a library/CLI. If unclear, AskUserQuestion to identify the target and wait before choosing a release path. For a confirmed app, **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. Save the current branch as ``. 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 `-uall`). Uncommitted changes are always included; do not ask. 3. Run `git fetch origin ` before inspecting the diff. If fetch fails, STOP: report the error and restore access first. Then inspect `git diff origin/ --stat`, untracked files from status, and `git log origin/..HEAD --oneline`. 4. Display historical readiness using the dashboard below, then finish Step 1. Prior CLEAR reviews or dashboard skips never replace Step 9's gates. {{REVIEW_DASHBOARD}} An outside review with status `unverified` or `unavailable` is missing coverage, never a pass. For diffs >200 lines (`git diff origin/ --stat | tail -1`), recommend `/plan-eng-review` or `/autoplan`. For Design Review: run `source <(~/.claude/skills/gstack/bin/gstack-diff-scope 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 asking; Step 9 applies the review gates. --- ## Step 2: Distribution Pipeline Check Check distribution for new standalone artifacts (CLI binaries, packages, tools), not web services already deployed. 1. List candidate distribution paths: ```bash git diff origin/ --diff-filter=A --name-only | grep -E '(^|/)(cmd/[^/]+/main\.go|bin/[^/]+|Cargo\.toml|setup\.py|package\.json)$' | head -5 ``` Include matching untracked files from Step 1's status. Read each match: a new `package.json` or `Cargo.toml` alone is not a publishable artifact; also check existing manifests for newly declared binaries or package exports. Gate only when a new distributable is present. 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. **New artifact without a pipeline:** AskUserQuestion: "Users cannot download this artifact after merge without a release pipeline." - A) Add the platform's release workflow now - B) Defer with a P1 distribution TODO in Step 14 - C) Not needed: internal/web-only, covered by existing deployment 4. **If A:** Add packaging/publish configuration using repository CI conventions. Ask for unknown targets, registries or access first; never invent credentials. Recheck against the artifact and include the workflow in tests and review. Do not publish a release during `/ship`. 5. Otherwise continue without a pipeline. --- ## 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/ --no-edit ``` **Merge conflicts:** Auto-resolve simple ones (VERSION, schema.rb, CHANGELOG ordering). For complex or ambiguous conflicts, **STOP**, show the conflicting choices, use AskUserQuestion for the resolution, and wait for the answer before editing or continuing. **If already up to date:** Continue silently. **Ship policy:** `~/.claude/skills/gstack/bin/gstack-ship-policy show-effective --base ` (docs/ship-policy.md); its `ship:` lines bind Steps 12, 16 and history. If integration changes the artifact or distribution configuration inspected in Step 2, repeat Step 2 (decisions included) on the merged content, then Step 4. --- {{SECTION:tests}} {{SECTION:greptile-early}} {{SECTION:test-coverage}} {{SECTION:plan-completion}} {{SECTION:review-army}} {{SECTION:greptile}} {{SECTION:adversarial}} ## Step 11.5: Bind the reviews 1. **Select the two reviews.** Run `~/.claude/skills/gstack/bin/gstack-review-read`. Select this invocation's final Step 9.4 record (`skill:"review"`, `via:"ship"`) and Step 11 native record (`skill:"adversarial-review"`). Match each to its saved handle, original token and source; reject outside-provider or older records. 2. **Compare their content.** Require the native record's `review_binding.state` to be `verified`. All three snapshots must match: its `wtree`, Step 9.4's `review_binding.start_wtree` and `review_binding.end_wtree`. A mismatch or missing record/field blocks release preparation: report **Review records missing or mismatched** and insert `9 → 10 → 11 → 11.5` before Step 12. Bind the new records at 11.5. Never attach new tokens to old work. 3. **Preserve any QA exception.** A named probe-risk exception may leave Step 9.4's root `wtree` absent; item 2 still compares its start/end snapshots. Matching content does not mean failed or unrun probes passed. Keep Step 9.4's incomplete flags and the user's exception. 4. **Save the evidence.** Save both records and matching **reviewed tree** for Step 16. Continue to Step 12. ## Step 12: Version bump (auto-decide) Item 3 needs `BUMP_LEVEL`: reuse this invocation's saved level. Otherwise FRESH chooses it in item 2 and ALREADY_BUMPED derives it in item 1. `ship: queue_mode stamp-at-merge` replaces items 1–4. 1. **Classify state** — pure reader, never writes: ```bash bun --no-env-file --config=/dev/null run ~/.claude/skills/gstack/bin/gstack-version-bump classify --base ``` Save the JSON `baseVersion` as `BASE_VERSION`, then read `state` and dispatch: - **FRESH** → use the recorded level or choose it in item 2, then check the queue and write. - **ALREADY_BUMPED** → keep `NEW_VERSION=currentVersion`. If `BUMP_LEVEL` is missing, use the first changed component from `baseVersion` to `currentVersion` (major/minor/patch/micro; an absent fourth component is zero). Continue at item 3, not another automatic bump. - **DRIFT_STALE_PKG** → run `gstack-version-bump repair`, then reclassify. Success follows ALREADY_BUMPED, including its queue check; failure stops. Repair alone never re-bumps. - **DRIFT_UNEXPECTED** → STOP: package.json disagrees with VERSION while VERSION matches base. Reconcile the manual edit, then reclassify. - **NO_VERSION** → print `notice` verbatim and ship without a version change: skip the rest of Step 12 and Step 13's CHANGELOG entry, never create VERSION, use an unprefixed title in Step 18 and log `"version":null` in Step 20. - **Exit 2** → STOP and show stderr: a configured version file is missing, empty, unreadable or malformed. Fix it or its pin, then reclassify. Never substitute `0.0.0.0`. 2. **Decide the bump level** from the diff (agent judgment): - **MICRO**: <50 lines, trivial tweaks/config. **PATCH**: 50+ lines, no feature signal. - **MINOR**: ask for any feature signal (new route/page, migration, module) or 500+ lines. **MAJOR**: ask for milestones or breaking changes (`ship: bump patch` never asks). AskUserQuestion: recommended level with rationale, smaller level, or cancel. Wait; cancel stops before release writes or push and preserves existing work. Save lowercase `BUMP_LEVEL`. A claimed version may move the next available number forward, but cannot change the chosen MICRO/PATCH/MINOR/MAJOR level. 3. **Queue-aware pick:** ```bash QUEUE_JSON=$(bun --no-env-file --config=/dev/null run ~/.claude/skills/gstack/bin/gstack-next-version --base --bump "$BUMP_LEVEL" --current-version "$BASE_VERSION" 2>/dev/null || echo '{"offline":true}') CANDIDATE_VERSION=$(echo "$QUEUE_JSON" | jq -r '.version // empty') ``` **Qualify first:** require successful utility output and a nonempty valid version. `offline:false` qualifies; `offline:true` qualifies only with `fallback:"git"`. Offline output without that fallback, failure, malformed output or an empty version is unusable, even when it contains a version-looking string. **Incomplete** (`complete:false`): PRs in `unknown_claims` went unread; the pick may collide. Rerun item 3 once; still incomplete, AskUserQuestion: use it (recommended; Version Gate CI rereads the queue) or stop. - **Usable candidate:** print warnings and claimed queue. FRESH sets `NEW_VERSION=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. Check JSON `active_siblings` by `branch` and `version`; a sibling holding `>= NEW_VERSION` requires a choice: advance past it, or stop this attempt and sync. - **No usable candidate:** print queue-unverified. FRESH uses local `BUMP_LEVEL` arithmetic; ALREADY_BUMPED keeps `currentVersion`. Never use an empty candidate. 4. **Write the bump** (FRESH, or an approved rebump): ```bash bun --no-env-file --config=/dev/null run ~/.claude/skills/gstack/bin/gstack-version-bump write --version "$NEW_VERSION" --regen-digest --changelog CHANGELOG.md ``` It writes VERSION, manifest and lockfiles, and moves `## [Unreleased]` under NEW_VERSION for Step 13. Exit 2 wrote nothing: show stderr, stop. Exit 3 is a half-write: reclassify and `repair` DRIFT_STALE_PKG. `--regen-digest` runs `scripts/gen-agents-digest.ts` only when it and a committed `agents-digest/gstack-AGENTS.md` exist; if `agentsDigest` is false, run `bun --no-env-file --config=/dev/null scripts/gen-agents-digest.ts` and stage the digest with the bump. Before push, verify the committed digest matches generation for the selected VERSION. 5. **Record the release decision after a version was written**, including an approved ALREADY_BUMPED rebump. Skip unchanged versions and manifest-only repairs. ```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 a one-line `WHY` (scope or breaking-change signal); best-effort. {{SECTION:changelog}} ## Step 14: TODOS.md (auto-update) Read `~/.claude/skills/gstack/review/TODOS-format.md`. **1. Open or create:** Read root `TODOS.md`. An explicit "add TODO" choice authorizes creation with `# TODOS` and `## Completed`. Otherwise, if missing, ask: A) Create a component/priority-organized TODOS.md, B) Skip. Skip goes to item 5. **2. Organization:** Use component headings, `**Priority:**` P0–P4 and `## Completed` at the bottom. If disorganized, ask: A) Reorganize preserving all content (recommended), B) Leave as-is. **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 entries instead of duplicating them. **4. Detect completed TODOs:** Compare titles, files and behavior with `git diff origin/`, untracked files and `git log origin/..HEAD --oneline`. Move proven completions to `## Completed` with `**Completed:** vX.Y.Z (YYYY-MM-DD)`; leave uncertain items open. **5. Save the summary:** Report additions, deferrals, completions, remaining count and creation/reorganization. If creation was declined or a write failed, warn and carry unsaved follow-ups in Step 19's PR summary; never claim they were saved. TODO write failures are non-blocking. --- ## Step 14.5: Documentation audit (every ship) **Doc-sync invariant:** Every ship dispatches the /document-release subagent before final commit/verification/publication, including reruns, already-pushed branches, existing PRs and docs-only changes. No edits means an executed audit, not a skip; report the section's verified outcome. {{SECTION:documentation}} ## Step 15: Commit (bisectable chunks) Make bisectable commits; if already committed, continue to Step 16. Never create an empty commit. 1. Group changes with their tests, config/routes, views and Step 14.5 docs; migrations may stand alone. Under 50 lines across fewer than 4 files may be one commit. 2. Order dependencies first: infrastructure → models/services → controllers/views. Each commit works alone (no broken imports or missing code). Group VERSION + CHANGELOG + TODOS.md after the feature commits. 3. Use `: ` (feat/fix/chore/refactor/docs) and a brief body; fixing a user-named issue adds a `Fixes: #` trailer. Only the final VERSION/CHANGELOG commit gets the release version and co-author trailer. No Git tag: ```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.** Run stages 1–5 in order; recovery notes below name where to resume. Content changes during or after verification restart at stage 1 and complete all five stages before Step 17. Content-preserving commits keep evidence valid. ### 1. Finish writers and prepare outputs Inspect writer handles, including the docs child. Confirm terminal completion or termination before another writer runs; timeout or cancellation acknowledgment alone means STOP until confirmed. Find declared generation/build commands in project instructions, manifests, build files and CI. Run them and save results. If none exists, record not applicable and the inspected sources. A missing prerequisite or failed build stops shipping: report **Build failed or prerequisite missing**, with the command, error and needed repair. Never invent a substitute command. **If blocked:** Repair it, then repeat stage 1. After it passes, continue to stage 2; treat any content repair as a behavioral change there. ### 2. Choose the change route Capture the current tree with `~/.claude/skills/gstack/bin/gstack-wtree`. Inspect `git diff ` against the snapshot saved before Step 12. Missing snapshots block this comparison, regardless of HEAD equality. Classify the comparison in this order: 1. **Behavior, tests or build inputs changed:** Prompts/templates count as behavior. Insert `5–11.5 → 12–14 → 16` before the pending Step 17, then stop this step. This repair excludes Step 14.5 because the rebuild can change generated docs. Step 16 restarts at stage 1: rebuild and compare again before stage 3 decides documentation freshness. Further repairs use the same work list. 2. **Only authored docs or release metadata changed:** Keep Step 8's original child report and counts. Recheck affected plan items using their recorded verification and append current evidence to the invocation record. If a classification is no longer supported, run Step 8's audit and decision gates only, then return to Step 16 stage 1. Never edit the child's counts yourself. 3. **No changes, or the docs-only checks still support the plan:** Continue to stage 3 without a new code review. ### 3. Resolve documentation freshness Compare the base and hashes of the selected release paths, generated outputs and docs/templates with Step 14.5's saved values. A prior invocation's audit or risk decision never qualifies. | Outcome | Action | |---|---| | This invocation's accepted audit matches all inputs | Continue to stage 4. | | User-accepted named documentation risk covers the same approved scope and exact content, and unwaivable gates clear | Continue to stage 4; retain `Documentation: blocked`, its reason and incomplete scope. | | Missing, stale or blocked | Use recovery below. Never silently refresh hashes. | Report changed inputs, blockers and attempts used: - **An attempt remains, with changed inputs or an available repair:** insert `14.5 → 15 → 16` before Step 17. Use Blocked recovery with the existing count. Validate the outcome before Step 15, then restart Step 16 stage 1. - **Otherwise:** STOP unless the user accepts the specific named documentation risk and all unwaivable gates clear, under Step 14.5's Blocked recovery rules. Unchanged approved content goes to stage 4; repaired content goes to stage 1. Never run a third audit. Child return is not acceptance. ### 4. Verify the frozen candidate Freeze inputs through verification and push. Run declared docs/link/generated-file checks; report any that are unavailable. **Reuse a check when its inputs match.** Compare hashes or complete bytes of its saved and current consumed files, fixtures, dependencies and execution parameters. Changed or unknown dependencies require a rerun. For model judges, compare the complete expanded request, rubric, parameters and builder/runtime dependencies. Cite the original command, result/counts, timestamp and log, never resample. Mandatory reviews still run. **Check each test lane's receipt as well.** Use its actual Step 5 label/command: `--label --expect-cmd ''`. Inspect changes since the run; `--allow-paths` exempts only release metadata. A `package.json` version-only edit can qualify; scripts, dependencies and runtime configuration require live tests. Uncertain edits cannot be exempted. Docs, TODO edits, new/generated tests and fixes make evidence STALE even without a new code review. Use this example only once every allowed edit is release metadata: ```bash ~/.claude/skills/gstack/bin/gstack-evidence check --label tests --expect-cmd '' --label vitest --expect-cmd '' --max-age 24 --allow-paths CHANGELOG.md,VERSION,package.json,agents-digest/gstack-AGENTS.md ``` | Receipt result | Next action | |---|---| | FRESH (exit 0) | Cite the label, exit, timestamp, log and `tests_ran`. | | STALE/MISSING: changed content, command or age, or no proven run | Run `~/.claude/skills/gstack/bin/gstack-evidence run --label -- ''`, read the result and recheck once. Handle failures as described below. | | ZERO-RUN: the receipt's `tests_ran` is 0 | STALE-equivalent: the lane proved nothing. Use Step 5's ZERO-RUN triage (a failed lane), never FRESH, even with exit 0. | | `tests_ran: unknown` | Non-blocking: keep the exit-0 verdict, cite the lane as `count unavailable`. Never record it as ZERO-RUN or as a counted pass. | | Only receipt storage/readback failed | Independently prove unchanged final content, the same command and valid age from the successful run's evidence. Cite its exact command, exit, timestamp and log as **ledger unavailable**, never FRESH. Without that proof, use STALE/MISSING. | No test lanes: require Step 5's explicit untested-scope approval for final content, or run Steps 5–15, including the no-tests decision, then return to Step 16 stage 1. Report the gap, never FRESH; builds must pass. **New, changed or unwaived test failure:** STOP publication. Run Steps 5–15, starting with Step 5's triage, then return to Step 16 stage 1. This recovery also applies if a failure appears while reporting in stage 5. Reentry to Step 14.5 keeps its existing audit count; it does not authorize a third attempt. ### 5. Report, then push Commit only approved, verified release changes left uncommitted after Step 15, including generated outputs; use its grouping rules; never create an empty commit. Preserve unrelated user files. Paste build/docs/test results. Reuse waivers only for the same verified pre-existing failures and approved scope; cite the actual approval and failing counts, never FRESH or all-green. A new, changed or unwaived test failure uses stage 4's recovery before publication. Then the honesty inventory, two lines, from Step 9's latest `Gate edits:` line and its records: - For each gate finding: `gate`, path, disposition (`kept`/`restored`/`open`), `reason` and logger-stamped `actor`. If there are none, the coverage line (`none detected (...)`, `partial (...)` or `UNAVAILABLE — ...`). - Each test lane's `tests_ran` (N, ZERO-RUN or `count unavailable`). Write this report in the auditor's posture: this session's own gate edits are evidence for the reader to examine, not positions to defend; a kept gate states its reason and nothing more. Otherwise continue to Step 17. --- ## Step 17: Push **Credential pre-push guard — 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_STATE="missing" if [ -e "$_HOOK_PATH" ] || [ -L "$_HOOK_PATH" ]; then _HOOK_STATE="unmanaged" if [ -f "$_HOOK_PATH" ] && [ ! -L "$_HOOK_PATH" ] && grep -Fqx '# gstack-redact pre-push (managed)' "$_HOOK_PATH" 2>/dev/null; then _HOOK_STATE="managed" fi fi _HOOKS_DIR=$(git rev-parse --git-path hooks 2>/dev/null || echo "") _HOOKS_IN_GIT_DIR="no" _HOOKS_CONFIG_STATUS=0 git config --get core.hooksPath >/dev/null 2>&1 || _HOOKS_CONFIG_STATUS=$? if [ -n "$_HOOK_PATH" ] && [ -n "$_HOOKS_DIR" ] && [ "$_HOOKS_CONFIG_STATUS" = "1" ] && [ ! -L "$_HOOKS_DIR" ]; then _HOOKS_IN_GIT_DIR="yes" fi GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _PREPUSH_PROMPTED=$([ -f "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ] && echo "yes" || echo "no") if [ "$_REDACT_PREPUSH" = "true" ] && [ "$_HOOKS_IN_GIT_DIR" = "yes" ] && [ "$_HOOK_STATE" != "unmanaged" ]; then ~/.claude/skills/gstack/bin/gstack-redact install-prepush-hook || exit $? fi echo "REDACT_PREPUSH: $_REDACT_PREPUSH" echo "HOOK_STATE: $_HOOK_STATE" echo "HOOKS_IN_GIT_DIR: $_HOOKS_IN_GIT_DIR" echo "PREPUSH_PROMPTED: $_PREPUSH_PROMPTED" ``` Branch on the echoed values: 1. **`REDACT_PREPUSH: true`** — the block installs or refreshes managed hooks, preserving `pre-push.local` and complete stdin. On installer failure, STOP before pushing. `HOOKS_IN_GIT_DIR: no`: do not install; request manual integration. `HOOK_STATE: unmanaged`: ask consent only for a regular, non-symlink hook in the default directory without `pre-push.local`; otherwise request manual integration. Dangling symlinks are unmanaged. Never overwrite either policy. 2. **`REDACT_PREPUSH` not true AND `PREPUSH_PROMPTED: no`** — one-time offer (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 re-run the block and apply the same directory and unmanaged-hook rules above. If B: run `~/.claude/skills/gstack/bin/gstack-config set redact_prepush_hook false`. ALWAYS after either answer (never after a failed question; it must re-offer next time): ```bash GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" touch "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ``` 3. **Declined earlier** — continue without comment. **Open gate findings against an existing ready PR/MR:** when Step 9 left a gate finding `open`, look up an open PR/MR for `` before pushing (`gh pr list --head --state open --json number,isDraft`; GitLab `glab mr list --source-branch --output json`, reading `draft`). A lookup failure STOPs here. If one exists and is not a draft: 1. The preamble echoed `SESSION_KIND: spawned` or `headless`: **STOP before pushing.** Report the open gate findings (`gate`, path) and the PR/MR; no push, no publication claim. A later human dispositions them. 2. Otherwise AskUserQuestion once: A) Resolve the open items now — insert `9 → 10 → 11 → 11.5 → 12 → 13 → 14 → 14.5 → 15 → 16` before Step 17; B) Publish ready with the `Gate edits: N open` banner. Record B in the invocation record as a human-authorised publication; Step 19 reads it. Never convert a ready PR to a draft. A new PR or an existing draft needs no question: Step 19 creates or keeps the draft. **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/) || { 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" ``` `ALREADY_PUSHED`: skip the push, continue to Step 18. Otherwise push with upstream tracking: ```bash git push -u origin ``` **If the push fails, STOP.** No Step 19 or publication claim. Report the error: - **Non-fast-forward push:** fetch and inspect the remote, then merge under Step 3's conflict rules. Run Steps 5–16 before returning to Step 17. Never rewrite history. - **Authentication, hook or network failure:** repair the cause, then repeat Step 16 even with unchanged content before returning to Step 17. Never bypass failed guards. Never force-push. Only a successful push or verified `ALREADY_PUSHED` proceeds. Continue to Step 18. No documentation writer runs after push. --- ## Step 18: Prepare publication metadata First look up open PRs/MRs for `` on the detected platform: - GitHub: `gh pr list --head --state open --json number,title,url` - GitLab: `glab mr list --source-branch --output json` (defaults to open). An empty array means new; one match supplies the existing title/identity. Lookup failure or ambiguous matches **STOP** for resolution, never mean no PR. Save the result for Step 19's recheck. The pr-body section prepares the title from that result, then publishes; `--no-pr` or an automation lane takes the handoff section. {{SECTION:pr-body}} {{SECTION:handoff}} ## Step 20: Persist ship metrics Log metrics for `/retro` through `gstack-review-log`; it handles project/branch paths, JSON validation, storage and sync and takes **no path argument**. ```bash ~/.claude/skills/gstack/bin/gstack-review-log '{"skill":"ship","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","coverage_pct":COVERAGE_PCT,"coverage_schema":2,"coverage_pct_value":COVERAGE_PCT_VALUE,"weak_gaps":WEAK_GAPS,"tests_extended":TESTS_EXTENDED,"tests_rejected":TESTS_REJECTED,"regression_proof":REGRESSION_PROOF,"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**: Step 7 diagram's integer percentage; encode null/undetermined as -1 - **COVERAGE_PCT_VALUE**: Step 7's `coverage_pct_value` (the gate's X) as an integer, or `null` when missing or ignored - **WEAK_GAPS**, **TESTS_EXTENDED**, **TESTS_REJECTED**: counts of Step 7's `weak_gaps`, `tests_extended` and `tests_rejected` (0 when the key is missing or ignored) - **REGRESSION_PROOF**: `{"red_at_head":N,"base_green":N,"base_unavailable":N}` from Step 7, or `null` when missing - **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", set after Step 9 executes Step 8.1's verification list - **VERSION**: `NEW_VERSION` from Step 12; `null` (unquoted) under NO_VERSION The shell supplies the branch. Run it without confirmation. --- ## Step 21: Plan-tune discoverability nudge (first-successful-ship only) After a successful ship, show the non-blocking /plan-tune nudge once per machine: ```bash GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _NUDGE_MARKER="$GSTACK_STATE_ROOT/.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." mkdir -p "$GSTACK_STATE_ROOT" && touch "$_NUDGE_MARKER" fi ``` The marker or enabled question_tuning suppresses it; remove the marker to re-enable. --- ## Section self-check (before you finish) List the applicable Section index entries and confirm each Read. If you worked from memory, STOP, Read the section and redo that step. Use `gstack-version-bump`; never hand-roll VERSION/package.json writes. --- ## Important Rules Follow the numbered gates and their explicit exceptions. - **Never force push.** Use regular `git push` only. - **An early Greptile PR stays up.** A stop after Step 6.5 opened it leaves the draft open with one comment saying why (greptile-early section); never close it. - **Use the configured version file's format** (4-digit for VERSION); under NO_VERSION, never invent one. - **Step 7 generates coverage tests.** They must pass before committing. Never commit failing tests.