## Step 18: Documentation sync (via subagent, before PR creation) **Dispatch /document-release as a subagent** using the Agent tool with `subagent_type: "general-purpose"`. The subagent gets a fresh context window — zero rot from the preceding 17 steps. It also runs the **full** `/document-release` workflow (with CHANGELOG clobber protection, doc exclusions, risky-change gates, named staging, race-safe PR body editing) rather than a weaker reimplementation. **Sequencing:** This step runs AFTER Step 17 (Push) and BEFORE Step 19 (Create PR). The PR is created once from final HEAD with the `## Documentation` section baked into the initial body. No create-then-re-edit dance. **Subagent prompt:** > You are executing the /document-release workflow after a code push. Read the full skill file `${HOME}/.claude/skills/gstack/document-release/SKILL.md` and execute its complete workflow end-to-end, including CHANGELOG clobber protection, doc exclusions, risky-change gates, and named staging. Do NOT attempt to edit the PR body — no PR exists yet. Branch: ``, base: ``. > > After completing the workflow, output a single JSON object on the LAST LINE of your response (no other text after it): > `{"files_updated":["README.md","CLAUDE.md",...],"commit_sha":"abc1234","pushed":true,"documentation_section":""}` > > If no documentation files needed updating, output: > `{"files_updated":[],"commit_sha":null,"pushed":false,"documentation_section":null}` **Parent processing:** 1. Parse the LAST line of the subagent's output as JSON. 2. Store `documentation_section` — Step 19 embeds it in the PR body (or omits the section if null). 3. If `files_updated` is non-empty, print: `Documentation synced: {files_updated.length} files updated, committed as {commit_sha}`. 4. If `files_updated` is empty, print: `Documentation is current — no updates needed.` **If the subagent fails or returns invalid JSON:** Print a warning and proceed to Step 19 without a `## Documentation` section. Do not block /ship on subagent failure. The user can run `/document-release` manually after the PR lands. --- ## Step 19: Create PR/MR **Idempotency check:** Check if a PR/MR already exists for this branch. **If GitHub:** ```bash gh pr view --json url,number,state -q 'if .state == "OPEN" then "PR #\(.number): \(.url)" else "NO_PR" end' 2>/dev/null || echo "NO_PR" ``` **If GitLab:** ```bash glab mr view -F json 2>/dev/null | jq -r 'if .state == "opened" then "MR_EXISTS" else "NO_MR" end' 2>/dev/null || echo "NO_MR" ``` If an **open** PR/MR already exists: **update** the PR body using `gh pr edit --body "..."` (GitHub) or `glab mr update -d "..."` (GitLab). Always regenerate the PR body from scratch using this run's fresh results (test output, coverage audit, review findings, adversarial review, TODOS summary, documentation_section from Step 18). Never reuse stale PR body content from a prior run. **Always update the PR title to start with `v$NEW_VERSION`.** PR titles use the workspace-aware format `v : ` — version ALWAYS first, no exceptions, no "custom title kept intentionally" escape hatch. The shared helper `bin/gstack-pr-title-rewrite.sh` is the single source of truth for the rule. 1. Read the current title: `CURRENT=$(gh pr view --json title -q .title)` (or `glab mr view -F json | jq -r .title`). 2. Compute the corrected title: `NEW_TITLE=$(~/.claude/skills/gstack/bin/gstack-pr-title-rewrite.sh "$NEW_VERSION" "$CURRENT")`. The helper handles three cases: title already correct (no-op), title has a different `v` prefix (replace it), or title has no version prefix (prepend one). 3. If `NEW_TITLE` differs from `CURRENT`, run `gh pr edit --title "$NEW_TITLE"` (or `glab mr update -t "$NEW_TITLE"`). 4. **Self-check:** re-fetch the title and assert it starts with `v$NEW_VERSION `. If it does not, retry the edit once. If still wrong, surface the failure to the user. This keeps the title truthful when Step 12's queue-drift detection rebumps a stale version, and forces the format on PRs that were created without it. Print the existing URL and continue to Step 20. If no PR/MR exists: create a pull request (GitHub) or merge request (GitLab) using the platform detected in Step 0. The PR/MR body should contain these sections: ``` ## Summary ..HEAD --oneline` to enumerate every commit. Exclude the VERSION/CHANGELOG metadata commit (that's this PR's bookkeeping, not a substantive change). Group the remaining commits into logical sections (e.g., "**Performance**", "**Dead Code Removal**", "**Infrastructure**"). Every substantive commit must appear in at least one section. If a commit's work isn't reflected in the summary, you missed it.> ## Test Coverage ## Pre-Landing Review ## Design Review ## Eval Results ## Greptile Review ## Scope Drift ## Plan Completion ## Linked Spec -$$, the spawned worktree IS where /ship runs). SPEC_FILE=$(grep -l "^spec_branch: $CURRENT_BRANCH$" "$SPEC_ARCHIVES"/*.md 2>/dev/null | head -1) [ -z "$SPEC_FILE" ] && exit # no spec; omit this section entirely SPEC_ISSUE=$(grep "^spec_issue_number:" "$SPEC_FILE" | cut -d' ' -f2) [ -z "$SPEC_ISSUE" ] && exit # spec archive exists but no issue number; omit # CONDITIONAL Closes #N (codex F4): only add when Plan Completion above is "complete". # If the plan completion gate from Step 8 reports any deferred or failed items, emit: # "Linked to #$SPEC_ISSUE (partial delivery — NOT auto-closing; close manually after follow-up)" # If Plan Completion is fully complete, emit: # "Closes #$SPEC_ISSUE" # and include the Closes #N line in the PR body so GitHub auto-closes on merge.> This PR delivers the spec at . Spec filed: > (partial delivery — not auto-closing). Deferred items: . Close # manually after follow-up lands.> ## Verification Results ## TODOS ## Documentation ## Test plan - [x] All Rails tests pass (N runs, 0 failures) - [x] All Vitest tests pass (N tests) 🤖 Generated with [Claude Code](https://claude.com/claude-code) ``` **If GitHub:** ```bash # PR title MUST start with v$NEW_VERSION — enforced on every run, no exceptions. # (See Step 19 idempotency block + bin/gstack-pr-title-rewrite.sh for the rule.) gh pr create --base --title "v$NEW_VERSION : " --body "$(cat <<'EOF' EOF )" ``` **If GitLab:** ```bash # MR title MUST start with v$NEW_VERSION — enforced on every run, no exceptions. # (See Step 19 idempotency block + bin/gstack-pr-title-rewrite.sh for the rule.) glab mr create -b -t "v$NEW_VERSION : " -d "$(cat <<'EOF' EOF )" ``` **If neither CLI is available:** Print the branch name, remote URL, and instruct the user to create the PR/MR manually via the web UI. Do not stop — the code is pushed and ready. **Output the PR/MR URL** — then proceed to Step 20. ---