## Step 19: Create PR/MR Recheck Step 18's PR/MR lookup and record it. Errors or ambiguous matches STOP publication. If the open PR/MR or title changed, repeat Step 18's identity/title preparation, then return here for a new lookup, fresh body and both redaction scans before publishing. ### Resolve Linked Spec before composing the body 1. Resolve the archive directory and branch: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-paths)" eval "$(~/.claude/skills/gstack/bin/gstack-slug)" CURRENT_BRANCH=$(git branch --show-current) SPEC_ARCHIVES="$GSTACK_STATE_ROOT/projects/$SLUG/specs" ``` 2. Read archive frontmatter as data, never shell source. Select an exact `spec_branch` match to `CURRENT_BRANCH`; among matches use the newest `spec_filed_at`. Never infer an issue number from a branch name. If no readable match or positive integer `spec_issue_number`, omit only `## Linked Spec` and continue composing the PR. Resolve ambiguous matches before linking an issue. 3. Compare that spec's acceptance criteria with Step 8's results. Only fully completed Step 8 plan scope permits `Closes #N`, with every spec criterion verified. Partial, deferred, failed, dropped or unverified scope uses `Linked to #N` and names the remaining work; never auto-close it. Include the archive filename and `spec_filed_at`, not a private absolute path. Send these fields through the same redaction scan. The PR/MR body should contain these sections (never reuse a prior run's body): ``` ## Summary ..HEAD --oneline`. Group every substantive commit by theme, excluding VERSION/CHANGELOG bookkeeping. Do not paste the commit list.> ## Test Coverage ## Pre-Landing Review ## Exploratory QA ## Design Review ## Eval Results ## Greptile Review ## Scope Drift ## Plan Completion ## Linked Spec ## Verification Results ## TODOS ## Documentation ## Test plan - [x] : 🤖 Generated with [Claude Code](https://claude.com/claude-code) ``` #### Redaction scan (PR body + title) — runs before create AND edit The PR body is world-readable on a public repo. Scan-at-sink before sending: write the composed body to a temp file, scan THAT file with the shared engine, and pass the same file to `gh`/`glab`. Wrap any Codex / Greptile / eval output sections in tool-attributed fences (` ```codex-review ` / ` ```greptile `) so the engine WARN-degrades the example credentials those tools quote instead of blocking the PR (a live-format credential inside the fence still blocks). Use Step 18's `NEW_TITLE` unchanged; its version prefix is already present. In a new shell, restore the saved literal title before this block. ```bash : "${NEW_TITLE:?Restore the saved Step 18 title before scanning}" REDACT_VIS=$(~/.claude/skills/gstack/bin/gstack-config get redact_repo_visibility 2>/dev/null) [ -z "$REDACT_VIS" ] && REDACT_VIS=$(gh repo view --json visibility -q .visibility 2>/dev/null | tr 'A-Z' 'a-z') REDACT_VIS="${REDACT_VIS:-unknown}" PR_BODY_FILE=$(mktemp) || { echo "ERROR: mktemp failed — cannot scan the PR body; refusing to create the PR unscanned." >&2; exit 1; } cat > "$PR_BODY_FILE" <<'PR_BODY_EOF' PR_BODY_EOF ~/.claude/skills/gstack/bin/gstack-redact --from-file "$PR_BODY_FILE" --repo-visibility "$REDACT_VIS" --self-email "$(git config user.email 2>/dev/null)" --json case $? in 0) ;; 3) echo "BLOCKED — credential in PR body. Rotate + redact, do not create the PR."; exit 1 ;; 2) echo "MEDIUM findings — confirm per finding (sterner on public) before proceeding." ;; *) echo "BLOCKED — PR body scan failed. Repair the scanner and repeat before publication."; exit 1 ;; esac printf '%s' "$NEW_TITLE" | ~/.claude/skills/gstack/bin/gstack-redact --repo-visibility "$REDACT_VIS" --json ``` Check both scan results: exit 0 permits publication; exit 2 requires AskUserQuestion per MEDIUM finding (PII offers `--auto-redact`); exit 3 blocks for HIGH findings. Exit 1 or any other error blocks until the scanner works and both scans pass. When visibility lookup is unavailable, including on GitLab, `unknown` uses the scanner's public-strict policy. For every create/edit command below, send the same scanned bytes. Never re-render the body. In a new shell, restore the literal `PR_BODY_FILE` path and `NEW_TITLE`. **Existing open PR/MR:** update using `gh pr edit --body-file "$PR_BODY_FILE"` (GitHub) or `glab mr update -d "$(cat "$PR_BODY_FILE")"` (GitLab). Update the title with the same scanned `NEW_TITLE`: `gh pr edit --title "$NEW_TITLE"` (or `glab mr update -t "$NEW_TITLE"`). **REST fallback (#1079):** if `gh pr edit` fails with the `repository.pullRequest.projectCards` GraphQL deprecation, do not re-ask for auth. Use the SAME scanned file: `PR_NUMBER=$(gh pr view --json number -q .number)`, then `gh api "repos/{owner}/{repo}/pulls/$PR_NUMBER" -X PATCH -F body=@"$PR_BODY_FILE"`; for the title use `gh api "repos/{owner}/{repo}/pulls/$PR_NUMBER" -X PATCH -f title="$NEW_TITLE"`. **Self-check:** re-fetch the title and assert it starts with `v$NEW_VERSION `. Retry once if wrong, then surface any failure. Print the existing URL and continue to Step 20; do not run the create commands below. **No open PR/MR, GitHub:** ```bash [ -s "$PR_BODY_FILE" ] || { echo "ERROR: scanned body file missing/empty — re-run the scan block." >&2; exit 1; } gh pr create --base --title "$NEW_TITLE" --body-file "$PR_BODY_FILE" rm -f "$PR_BODY_FILE" ``` **No open PR/MR, GitLab:** ```bash [ -s "$PR_BODY_FILE" ] || { echo "ERROR: scanned body file missing/empty — re-run the scan block." >&2; exit 1; } glab mr create -b -t "$NEW_TITLE" -d "$(cat "$PR_BODY_FILE")" rm -f "$PR_BODY_FILE" ``` **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. ---