Update Check (run first)
_UPD=$(~/.claude/skills/upstack/bin/upstack-update-check 2>/dev/null || .claude/skills/upstack/bin/upstack-update-check 2>/dev/null || true)
[ -n "$_UPD" ] && echo "$_UPD" || true
If output shows UPGRADE_AVAILABLE <old> <new>: read ~/.claude/skills/upstack/skills/upgrade/SKILL.md and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If JUST_UPGRADED <from> <to>: tell user "Running upstack v{to} (just updated!)" and continue.
Ship PR
Three-in-one: documentation, versioning, and PR creation. No confirmation pauses once the version is decided.
Pre-flight (abort if any fail)
- Working tree is clean.
- On a feature branch (not main/master). If on main/master, auto-create a branch named after the work (e.g.,
feat/short-descriptionorfix/short-descriptionbased on the changes) and switch to it before continuing. - All commits are pushed (or will be pushed).
ghCLI is available and authenticated.- Tests pass.
If any check fails (other than #2, which is auto-resolved): report and stop. Do not ask to continue.
Step 1: Documentation (do not skip)
Review and update these. Report each as UPDATED or NO CHANGE NEEDED (with reason):
- README.md — does it reflect the new feature/fix?
- CHANGELOG.md — will be updated in Step 2, but note the entry now.
- Any
docs/folder or inline doc comments in changed files. - CLAUDE.md — if conventions or workflow changed.
Keep docs close to the code — update existing doc files rather than creating new ones where possible. At minimum, review each item above and report your finding.
Step 2: Version + Changelog
AskUserQuestion — the ONLY interactive step: "Current version: [X.Y.Z]. Changes suggest [type]. Which version?
- Patch (X.Y.Z+1) — bug fixes
- Minor (X.Y+1.0) — new features, backward compatible
- Major (X+1.0.0) — breaking changes
- Skip version bump"
Update VERSION, package.json, or pyproject.toml (whichever exists). Update CHANGELOG.md.
Step 3: PR Creation (no pauses from here)
Screenshot Check (run before creating or updating the PR)
- If the diff includes files in UI/frontend packages AND
evidence/screenshots/is empty or missing: STOP and warn the user. Recommend running /validate or capturing screenshots now. - If screenshots exist: continue and embed them in the PR body using absolute GitHub blob URLs.
- Organize screenshots by feature or mode (e.g., "Docker mode" vs "Sprites mode").
- Commit version + changelog + docs:
chore: bump to vX.Y.Z - Push branch to remote.
- Check for existing PR:
gh pr list --head "$(git branch --show-current)" --json number --jq '.[0].number'- If PR exists: Skip
gh pr create— the push already updated the PR. Optionally rungh pr comment <number> --body "..."summarizing the new changes. - If no PR exists: Create PR via
gh pr create. PR description includes:- Feature summary
- Screenshots (see Screenshot Embedding below)
- API examples from
evidence/api/or link to collection - Closes / Addresses section (MANDATORY — always include, even if empty):
- TODOS.md items addressed by this PR (e.g., "Completes P1-2: Fix auth token refresh")
- Linear ticket references using
Closes ENG-xxxkeyword format so Linear's GitHub integration auto-tracks PR completion - Codebase TODO/FIXME comments resolved (file:line references)
- If none exist, write "No existing tickets or TODOs addressed."
- If PR exists: Skip
- Mark completed items in TODOS.md as done (change
- [ ]to- [x]).
Screenshot Embedding
When embedding screenshots in the PR body, use ABSOLUTE GitHub blob URLs, not relative paths. Relative paths do not render in PR descriptions.
Format:

Construct the URL from:
gh repo view --json nameWithOwner --jq '.nameWithOwner'→ owner/repogit branch --show-current→ branch name- The screenshot filename in
evidence/screenshots/
Do NOT use relative paths like  — these are BROKEN in PR descriptions.
On Failure
Report which step completed and which failed. Do NOT attempt to rollback.
Linear Integration
Scan TODOS.md for Linear ticket references (patterns: [ENG-xxx](url), LINEAR: ENG-xxx, or bare ENG-xxx identifiers).
PR body keywords (primary mechanism)
Include Closes ENG-xxx in the PR description for each relevant ticket. Linear's GitHub integration detects these keywords and:
- Links the PR to the Linear issue automatically
- Moves the issue to "Done" when the PR merges
CLI fallback (when Linear CLI is authenticated)
# Check if linear CLI is available and authenticated
which linear 2>/dev/null && linear me 2>/dev/null
If authenticated, for each ticket referenced in TODOS.md:
linear issue update <issue-id> --add-link "<pr-url>"
If the CLI command fails: warn but do not block. The PR body keywords are the primary tracking mechanism.