agentleFS
ayudayayuday/gstack/.agents/skills/gstack-ship/SKILL.md

ship

Skill0 starsChanged 7 months agoMITView on GitHub

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", or "merge and push". Proactively suggest when the user says code is ready or asks about deploying.

  • Commits and pushes

We split this skill into the rules it gives an agent

150 rules. 0 are common, 102 are distinctive, and 48 are very specific to this project.

  • 0

    Common

    Most of the 181 edited versions we read keep these.

  • 102

    Distinctive

    This author's own choices. Worth a look.

  • 48

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Very specific:

    If PROACTIVE is "false", do not proactively suggest gstack skills — only invoke them when the user explicitly asks.

    line 40
  2. Very specific:

    If output shows UPGRADE_AVAILABLE <old> <new>: read ~/.codex/skills/gstack/gstack-upgrade/SKILL.md and follow the "Inline upgrade flow" …

    line 43
  3. Very specific:

    If LAKE_INTRO is no: Before continuing, introduce the Completeness Principle.

    line 45
  4. Very specific:

    Only run open if the user says yes.

    line 55
  5. Very specific:

    If TEL_PROMPTED is no AND LAKE_INTRO is yes: After the lake intro is handled, ask the user about telemetry.

    line 57
  6. Very specific:

    If A: run ~/.codex/skills/gstack/bin/gstack-config set telemetry community

    line 69
  7. Distinctive:

    If B: ask a follow-up AskUserQuestion:

    line 71
  8. Very specific:

    If B→A: run ~/.codex/skills/gstack/bin/gstack-config set telemetry anonymous If B→B: run ~/.codex/skills/gstack/bin/gstack-config set …

    line 80
  9. Very specific:

    If B→B: run ~/.codex/skills/gstack/bin/gstack-config set telemetry off

    line 81
  10. Distinctive:

    Never let a noticed issue silently pass.

    line 137
  11. Very specific:

    Before building infrastructure, unfamiliar patterns, or anything the runtime might have a built-in — search first.

    line 141
  12. Distinctive:

    Eureka moment: When first-principles reasoning reveals conventional wisdom is wrong, name it: "EUREKA: Everyone does X because [assumption].

    line 148
  13. Distinctive:

    WebSearch fallback: If WebSearch is unavailable, skip the search step and note: "Search unavailable — proceeding with in-distribution …

    line 157
  14. Very specific:

    If _CONTRIB is true: you are in contributor mode.

    line 161
  15. Very specific:

    To file: write ~/.gstack/contributor-logs/{slug}.md with all sections below (do not truncate — include every section through the …

    line 169
  16. Distinctive:

    When completing a skill workflow, report status using one of:

    line 198
  17. Distinctive:

    If you have attempted a task 3 times without success, STOP and escalate.

    line 209
  18. Distinctive:

    If you are uncertain about a security-sensitive change, STOP and escalate.

    line 210
  19. Distinctive:

    If the scope of work exceeds what you can verify, STOP and escalate.

    line 211
  20. Very specific:

    After the skill workflow completes (success, error, or abort), log the telemetry event.

    line 223
  21. Distinctive:

    If you cannot determine the outcome, use "unknown".

    line 246
  22. Very specific:

    Check if a PR already exists for this branch: gh pr view --json baseRefName -q .baseRefName If this succeeds, use the printed branch name …

    line 253
  23. Distinctive:

    If this succeeds, use the printed branch name as the base branch.

    line 255
  24. Very specific:

    If no PR exists (command fails), detect the repo's default branch: gh repo view --json defaultBranchRef -q .defaultBranchRef.name

    line 257
  25. Distinctive:

    If both commands fail, fall back to main.

    line 260
  26. Distinctive:

    Commit message approval (auto-commit)

    line 286
  27. Distinctive:

    Test coverage gaps (auto-generate and commit, or flag in PR body)

    line 290
  28. Distinctive:

    Check the current branch. If on the base branch or the repo's default branch, abort: "You're on the base branch.

    line 296
  29. Very specific:

    Run git status (never use -uall).

    line 298
  30. Very specific:

    Run git diff <base>...HEAD --stat and git log <base>..HEAD --oneline to understand what's being shipped.

    line 300
  31. Distinctive:

    After completing the review, read the review log and config to display the dashboard.

    line 306
  32. Distinctive:

    CEO Review (optional): Use your judgment. Recommend it for big product/business changes, new user-facing features, or scope decisions.

    line 331
  33. Distinctive:

    Design Review (optional): Use your judgment. Recommend it for UI/UX changes.

    line 332
  34. Very specific:

    If \skip_eng_review\ config is \true\, Eng Review shows "SKIPPED (global)" and verdict is CLEARED

    line 339
  35. Distinctive:

    Staleness detection: After displaying the dashboard, check if any existing reviews may be stale:

    line 341
  36. Distinctive:

    If all reviews match the current HEAD, do not display any staleness notes

    line 345
  37. Distinctive:

    If the Eng Review is NOT "CLEAR":

    line 347
  38. Distinctive:

    If an override exists, display the dashboard and note "Review gate previously accepted — continuing." Do NOT ask again.

    line 354
  39. Distinctive:

    If no override exists,: use AskUserQuestion:

    line 356
  40. Distinctive:

    If CEO Review is missing, mention as informational ("CEO Review not run — recommended for product changes") but do NOT block

    line 360
  41. Distinctive:

    Read 2-3 existing test files to learn conventions (naming, imports, assertion style, setup patterns).

    line 413
  42. Very specific:

    If user picks H → write .gstack/no-test-bootstrap and continue without tests.

    line 421
  43. Distinctive:

    Use WebSearch to find current best practices for the detected runtime:

    line 427
  44. Distinctive:

    If WebSearch is unavailable, use this built-in knowledge table:

    line 431
  45. Very specific:

    If user picks C → write .gstack/no-test-bootstrap.

    line 453
  46. Distinctive:

    If multiple runtimes detected (monorepo) → ask which runtime to set up first, with option to do both sequentially.

    line 455
  47. Distinctive:

    Create minimal config file

    line 460
  48. Distinctive:

    Create directory structure (test/, spec/, etc.)

    line 461
  49. Distinctive:

    Create one example test matching the project's code to verify setup works

    line 462
  50. Distinctive:

    If package installation fails → debug once.

    line 464
  51. Very specific:

    For each file: Write one test that tests real behavior with meaningful assertions.

    line 472
  52. Distinctive:

    Run each test. Passes → keep.

    line 473
  53. Distinctive:

    Never import secrets, API keys, or credentials in test files.

    line 476
  54. Distinctive:

    If tests fail → debug once.

    line 485
  55. Very specific:

    If .github/ exists (or no CI detected — default to GitHub Actions): Create .github/workflows/test.yml with:

    line 495
  56. Distinctive:

    If non-GitHub CI detected → skip CI generation with note: "Detected {provider} — CI pipeline generation supports GitHub Actions only.

    line 502
  57. Distinctive:

    Test layers: Unit tests (what, where, when), Integration tests, Smoke tests, E2E tests

    line 512
  58. Distinctive:

    Run command and test directory

    line 520
  59. Distinctive:

    When writing new functions, write a corresponding test

    line 524
  60. Distinctive:

    When fixing a bug, write a regression test

    line 525
  61. Distinctive:

    When adding error handling, write a test that triggers the error

    line 526
  62. Distinctive:

    When adding a conditional (if/else, switch), write tests for BOTH paths

    line 527
  63. Distinctive:

    Never commit code that makes existing tests fail

    line 528
  64. Very specific:

    Only commit if there are changes.

    line 536
  65. Distinctive:

    Run both test suites in parallel:

    line 549
  66. Distinctive:

    After both complete, read the output files and check pass/fail.

    line 557
  67. Distinctive:

    If any test fails: Do NOT immediately stop. Apply the Test Failure Ownership Triage:

    line 559
  68. Distinctive:

    When tests fail, do NOT immediately stop.

    line 563
  69. Distinctive:

    In-branch: if: the failing test file itself was modified on this branch, OR the test output references code that was changed on this …

    line 575
  70. Distinctive:

    Likely pre-existing: if: neither the test file nor the code it tests was modified on this branch, AND the failure is unrelated to any …

    line 576
  71. Very specific:

    Check REPO_MODE from the preamble output.

    line 587
  72. Very specific:

    Commit the fix separately from the branch's changes: git commit -m "fix: pre-existing test failure in <test-file>"

    line 625
  73. Very specific:

    If TODOS.md exists, add the entry following the format in review/TODOS-format.md (or .agents/skills/gstack/review/TODOS-format.md).

    line 629
  74. Very specific:

    If TODOS.md does not exist, create it with the standard header and add the entry.

    line 630
  75. Distinctive:

    If these are different people, prefer the production code author — they likely introduced the regression.

    line 642
  76. Distinctive:

    Create a GitHub issue assigned to that person:

    line 643
  77. Very specific:

    If gh is not available or --assignee fails (user not in org, etc.), create the issue without assignee and note who should look at it in the …

    line 650
  78. Distinctive:

    After triage: If any in-branch failures remain unfixed, STOP.

    line 657
  79. Distinctive:

    If unsure which suites are affected, run ALL suites that could plausibly be impacted.

    line 697
  80. Distinctive:

    If multiple suites need to run, run them sequentially (each needs a test lane).

    line 707
  81. Distinctive:

    Before analyzing coverage, detect the project's test framework:

    line 731
  82. Distinctive:

    Read every changed file. For each one, trace how data flows through the code — don't just list functions, actually follow the execution:

    line 761
  83. Distinctive:

    Every function/method that was added or modified

    line 770
  84. Distinctive:

    Every conditional branch (if/else, switch, ternary, guard clause, early return)

    line 771
  85. Distinctive:

    Every error path (try/catch, rescue, error boundary, fallback)

    line 772
  86. Distinctive:

    Every call to another function (trace into it — does IT have untested branches?)

    line 773
  87. Distinctive:

    Every edge: what happens with null input?

    line 774
  88. Distinctive:

    Add these to your diagram alongside the code branches.

    line 795
  89. Distinctive:

    When checking each branch, also determine whether a unit test or E2E/integration test is the right tool:

    line 814
  90. Distinctive:

    IRON RULE: When the coverage audit identifies a REGRESSION — code that previously worked but the diff broke — a regression test is written …

    line 833
  91. Distinctive:

    When uncertain whether a change is a regression, err on the side of writing the test.

    line 840
  92. Distinctive:

    Fast path: All paths covered → "Step 3.4: All new code paths have test coverage ✓" Continue.

    line 890
  93. Distinctive:

    If test framework detected (or bootstrapped in Step 2.5):

    line 894
  94. Distinctive:

    Read 2-3 existing test files to match conventions exactly

    line 896
  95. Distinctive:

    Write tests that exercise the specific uncovered path with real assertions

    line 900
  96. Very specific:

    Run each test. Passes → commit as test: coverage for {feature}

    line 901
  97. Distinctive:

    If no test framework AND user declined bootstrap → diagram only, no generation.

    line 906
  98. Very specific:

    After producing the coverage diagram, write a test plan artifact so /qa and /qa-only can consume it:

    line 922
  99. Very specific:

    Read .agents/skills/gstack/review/checklist.md.

    line 957
  100. Very specific:

    Run git diff origin/<base> to get the full diff (scoped to feature changes against the freshly-fetched base branch).

    line 959
  101. Distinctive:

    Pass 2 (INFORMATIONAL): All remaining categories

    line 963
  102. Very specific:

    Check if the diff touches frontend files using gstack-diff-scope:

    line 967
  103. Very specific:

    Check for DESIGN.md: If DESIGN.md or design-system.md exists in the repo root, read it.

    line 977
  104. Very specific:

    Read .agents/skills/gstack/review/design-checklist.md: If the file cannot be read, skip design review with a note: "Design checklist not …

    line 979
  105. Distinctive:

    If 3 or fewer ASK items, you may use individual AskUserQuestion calls instead

    line 1010
  106. Very specific:

    If ANY fixes were applied: commit fixed files by name (git add <fixed-files> && git commit -m "fix: pre-landing review fixes"), then STOP …

    line 1013
  107. Distinctive:

    If no fixes applied (all ASK items skipped, or no issues found): continue to Step 4.

    line 1014
  108. Very specific:

    If no issues found: Pre-Landing Review: No issues found.

    line 1018
  109. Very specific:

    Read .agents/skills/gstack/review/greptile-triage.md and follow the fetch, filter, classify, and escalation detection steps.

    line 1026
  110. Distinctive:

    Before replying to any comment, run the Escalation Detection algorithm from greptile-triage.md to determine whether to use Tier 1 …

    line 1034
  111. Distinctive:

    VALID & ACTIONABLE: Use AskUserQuestion with:

    line 1038
  112. Very specific:

    If user chooses A: apply the fix, commit the fixed files (git add <fixed-files> && git commit -m "fix: address Greptile review — <brief …

    line 1042
  113. Distinctive:

    If user chooses C: reply using the False Positive reply template from greptile-triage.md (include evidence + suggested re-rank), save to …

    line 1043
  114. Distinctive:

    FALSE POSITIVE: Use AskUserQuestion:

    line 1049
  115. Distinctive:

    If user chooses A: reply using the False Positive reply template from greptile-triage.md (include evidence + suggested re-rank), save to …

    line 1055
  116. Distinctive:

    After all comments are resolved: If any fixes were applied, the tests from Step 3 are now stale.

    line 1059
  117. Very specific:

    Read the current VERSION file (4-digit format: MAJOR.MINOR.PATCH.MICRO)

    line 1067
  118. Very specific:

    Write the new version to the VERSION file.

    line 1080
  119. Very specific:

    Read CHANGELOG.md header to know the format.

    line 1086
  120. Very specific:

    Use git log <base>..HEAD --oneline to see every commit being shipped

    line 1089
  121. Very specific:

    Use git diff <base>...HEAD to see the full diff against the base branch

    line 1090
  122. Distinctive:

    If existing CHANGELOG entries on the branch already cover some commits, replace them with one unified entry for the new version

    line 1092
  123. Distinctive:

    Write concise, descriptive bullet points

    line 1098
  124. Very specific:

    Read .agents/skills/gstack/review/TODOS-format.md for the canonical format reference.

    line 1110
  125. Distinctive:

    If TODOS.md does not exist: Use AskUserQuestion:

    line 1114
  126. Very specific:

    If A: Create TODOS.md with a skeleton (# TODOS heading + ## Completed section).

    line 1117
  127. Distinctive:

    If B: Skip the rest of Step 5.5.

    line 1118
  128. Distinctive:

    Read TODOS.md and verify it follows the recommended structure:

    line 1122
  129. Distinctive:

    If A: Reorganize in-place following TODOS-format.md.

    line 1130
  130. Distinctive:

    If B: Continue to step 3 without restructuring.

    line 1131
  131. Distinctive:

    Use the diff and commit history already gathered in earlier steps:

    line 1137
  132. Distinctive:

    Be conservative: Only mark a TODO as completed if there is clear evidence in the diff.

    line 1146
  133. Distinctive:

    6. Defensive: If TODOS.md cannot be written (permission error, disk full), warn the user and continue.

    line 1155
  134. Distinctive:

    Goal: Create small, logical commits that work well with git bisect and help LLMs understand what changed.

    line 1163
  135. Distinctive:

    VERSION + CHANGELOG + TODOS.md: always in the final commit

    line 1171
  136. Distinctive:

    If the total diff is small (< 50 lines across < 4 files), a single commit is fine

    line 1179
  137. Distinctive:

    Only the final commit (VERSION + CHANGELOG) gets the version tag and co-author trailer:

    line 1186
  138. Distinctive:

    Before pushing, re-verify if code changed during Steps 4-6:

    line 1203
  139. Distinctive:

    Test verification: If ANY code changed after Step 3's test run (fixes from review findings, CHANGELOG edits don't count), re-run the test …

    line 1205
  140. Distinctive:

    Build verification: If the project has a build step, run it.

    line 1207
  141. Distinctive:

    If tests fail here: STOP. Do not push. Fix the issue and return to Step 3.

    line 1215
  142. Very specific:

    Create a pull request using gh:

    line 1233
  143. Very specific:

    After the PR is created, automatically sync project documentation.

    line 1280
  144. Very specific:

    Read the /document-release skill: cat ${CLAUDE_SKILL_DIR}/../document-release/SKILL.md

    line 1284
  145. Distinctive:

    Follow its instructions — it reads all .md files in the project, cross-references the diff, and updates anything that drifted (README, …

    line 1285
  146. Distinctive:

    If any docs were updated, commit the changes and push to the same branch:

    line 1288
  147. Distinctive:

    If no docs needed updating, say "Documentation is current — no updates needed."

    line 1292
  148. Distinctive:

    Never skip tests: If tests fail, stop.

    line 1301
  149. Distinctive:

    Never skip the pre-landing review: If checklist.md is unreadable, stop.

    line 1302
  150. Distinctive:

    Never force push: Use regular git push only.

    line 1303

“Common” is measured: kept by at least 6 in 10 of the 181 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly --><!-- Regenerate: bun run gen:skill-docs -->

Preamble (run first)

_UPD=$(~/.codex/skills/gstack/bin/gstack-update-check 2>/dev/null || .agents/skills/gstack/bin/gstack-update-check 2>/dev/null || true)
[ -n "$_UPD" ] && echo "$_UPD" || true
mkdir -p ~/.gstack/sessions
touch ~/.gstack/sessions/"$PPID"
_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ')
find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true
_CONTRIB=$(~/.codex/skills/gstack/bin/gstack-config get gstack_contributor 2>/dev/null || true)
_PROACTIVE=$(~/.codex/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true")
_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
echo "BRANCH: $_BRANCH"
echo "PROACTIVE: $_PROACTIVE"
source <(~/.codex/skills/gstack/bin/gstack-repo-mode 2>/dev/null) || true
REPO_MODE=${REPO_MODE:-unknown}
echo "REPO_MODE: $REPO_MODE"
_LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no")
echo "LAKE_INTRO: $_LAKE_SEEN"
_TEL=$(~/.codex/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true)
_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no")
_TEL_START=$(date +%s)
_SESSION_ID="$$-$(date +%s)"
echo "TELEMETRY: ${_TEL:-off}"
echo "TEL_PROMPTED: $_TEL_PROMPTED"
mkdir -p ~/.gstack/analytics
echo '{"skill":"ship","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}'  >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
for _PF in ~/.gstack/analytics/.pending-*; do [ -f "$_PF" ] && ~/.codex/skills/gstack/bin/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true; break; done

If PROACTIVE is "false", do not proactively suggest gstack skills — only invoke them when the user explicitly asks. The user opted out of proactive suggestions.

If output shows UPGRADE_AVAILABLE <old> <new>: read ~/.codex/skills/gstack/gstack-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 gstack v{to} (just updated!)" and continue.

If LAKE_INTRO is no: Before continuing, introduce the Completeness Principle. Tell the user: "gstack follows the Boil the Lake principle — always do the complete thing when AI makes the marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean" Then offer to open the essay in their default browser:

open https://garryslist.org/posts/boil-the-ocean
touch ~/.gstack/.completeness-intro-seen

Only run open if the user says yes. Always run touch to mark as seen. This only happens once.

If TEL_PROMPTED is no AND LAKE_INTRO is yes: After the lake intro is handled, ask the user about telemetry. Use AskUserQuestion:

Help gstack get better! Community mode shares usage data (which skills you use, how long they take, crash info) with a stable device ID so we can track trends and fix bugs faster. No code, file paths, or repo names are ever sent. Change anytime with gstack-config set telemetry off.

Options:

  • A) Help gstack get better! (recommended)
  • B) No thanks

If A: run ~/.codex/skills/gstack/bin/gstack-config set telemetry community

If B: ask a follow-up AskUserQuestion:

How about anonymous mode? We just learn that someone used gstack — no unique ID, no way to connect sessions. Just a counter that helps us know if anyone's out there.

Options:

  • A) Sure, anonymous is fine
  • B) No thanks, fully off

If B→A: run ~/.codex/skills/gstack/bin/gstack-config set telemetry anonymous If B→B: run ~/.codex/skills/gstack/bin/gstack-config set telemetry off

Always run:

touch ~/.gstack/.telemetry-prompted

This only happens once. If TEL_PROMPTED is yes, skip this entirely.

AskUserQuestion Format

ALWAYS follow this structure for every AskUserQuestion call:

  1. Re-ground: State the project, the current branch (use the _BRANCH value printed by the preamble — NOT any branch from conversation history or gitStatus), and the current plan/task. (1-2 sentences)
  2. Simplify: Explain the problem in plain English a smart 16-year-old could follow. No raw function names, no internal jargon, no implementation details. Use concrete examples and analogies. Say what it DOES, not what it's called.
  3. Recommend: RECOMMENDATION: Choose [X] because [one-line reason] — always prefer the complete option over shortcuts (see Completeness Principle). Include Completeness: X/10 for each option. Calibration: 10 = complete implementation (all edge cases, full coverage), 7 = covers happy path but skips some edges, 3 = shortcut that defers significant work. If both options are 8+, pick the higher; if one is ≤5, flag it.
  4. Options: Lettered options: A) ... B) ... C) ... — when an option involves effort, show both scales: (human: ~X / CC: ~Y)

Assume the user hasn't looked at this window in 20 minutes and doesn't have the code open. If you'd need to read the source to understand your own explanation, it's too complex.

Per-skill instructions may add additional formatting rules on top of this baseline.

Completeness Principle — Boil the Lake

AI-assisted coding makes the marginal cost of completeness near-zero. When you present options:

  • If Option A is the complete implementation (full parity, all edge cases, 100% coverage) and Option B is a shortcut that saves modest effort — always recommend A. The delta between 80 lines and 150 lines is meaningless with CC+gstack. "Good enough" is the wrong instinct when "complete" costs minutes more.
  • Lake vs. ocean: A "lake" is boilable — 100% test coverage for a module, full feature implementation, handling all edge cases, complete error paths. An "ocean" is not — rewriting an entire system from scratch, adding features to dependencies you don't control, multi-quarter platform migrations. Recommend boiling lakes. Flag oceans as out of scope.
  • When estimating effort, always show both scales: human team time and CC+gstack time. The compression ratio varies by task type — use this reference:
Task typeHuman teamCC+gstackCompression
Boilerplate / scaffolding2 days15 min~100x
Test writing1 day15 min~50x
Feature implementation1 week30 min~30x
Bug fix + regression test4 hours15 min~20x
Architecture / design2 days4 hours~5x
Research / exploration1 day3 hours~3x
  • This principle applies to test coverage, error handling, documentation, edge cases, and feature completeness. Don't skip the last 10% to "save time" — with AI, that 10% costs seconds.

Anti-patterns — DON'T do this:

  • BAD: "Choose B — it covers 90% of the value with less code." (If A is only 70 lines more, choose A.)
  • BAD: "We can skip edge case handling to save time." (Edge case handling costs minutes with CC.)
  • BAD: "Let's defer test coverage to a follow-up PR." (Tests are the cheapest lake to boil.)
  • BAD: Quoting only human-team effort: "This would take 2 weeks." (Say: "2 weeks human / ~1 hour CC.")

Repo Ownership Mode — See Something, Say Something

REPO_MODE from the preamble tells you who owns issues in this repo:

  • solo — One person does 80%+ of the work. They own everything. When you notice issues outside the current branch's changes (test failures, deprecation warnings, security advisories, linting errors, dead code, env problems), investigate and offer to fix proactively. The solo dev is the only person who will fix it. Default to action.
  • collaborative — Multiple active contributors. When you notice issues outside the branch's changes, flag them via AskUserQuestion — it may be someone else's responsibility. Default to asking, not fixing.
  • unknown — Treat as collaborative (safer default — ask before fixing).

See Something, Say Something: Whenever you notice something that looks wrong during ANY workflow step — not just test failures — flag it briefly. One sentence: what you noticed and its impact. In solo mode, follow up with "Want me to fix it?" In collaborative mode, just flag it and move on.

Never let a noticed issue silently pass. The whole point is proactive communication.

Search Before Building

Before building infrastructure, unfamiliar patterns, or anything the runtime might have a built-in — search first. Read ~/.codex/skills/gstack/ETHOS.md for the full philosophy.

Three layers of knowledge:

  • Layer 1 (tried and true — in distribution). Don't reinvent the wheel. But the cost of checking is near-zero, and once in a while, questioning the tried-and-true is where brilliance occurs.
  • Layer 2 (new and popular — search for these). But scrutinize: humans are subject to mania. Search results are inputs to your thinking, not answers.
  • Layer 3 (first principles — prize these above all). Original observations derived from reasoning about the specific problem. The most valuable of all.

Eureka moment: When first-principles reasoning reveals conventional wisdom is wrong, name it: "EUREKA: Everyone does X because [assumption]. But [evidence] shows this is wrong. Y is better because [reasoning]."

Log eureka moments:

jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true

Replace SKILL_NAME and ONE_LINE_SUMMARY. Runs inline — don't stop the workflow.

WebSearch fallback: If WebSearch is unavailable, skip the search step and note: "Search unavailable — proceeding with in-distribution knowledge only."

Contributor Mode

If _CONTRIB is true: you are in contributor mode. You're a gstack user who also helps make it better.

At the end of each major workflow step (not after every single command), reflect on the gstack tooling you used. Rate your experience 0 to 10. If it wasn't a 10, think about why. If there is an obvious, actionable bug OR an insightful, interesting thing that could have been done better by gstack code or skill markdown — file a field report. Maybe our contributor will help make us better!

Calibration — this is the bar: For example, $B js "await fetch(...)" used to fail with SyntaxError: await is only valid in async functions because gstack didn't wrap expressions in async context. Small, but the input was reasonable and gstack should have handled it — that's the kind of thing worth filing. Things less consequential than this, ignore.

NOT worth filing: user's app bugs, network errors to user's URL, auth failures on user's site, user's own JS logic bugs.

To file: write ~/.gstack/contributor-logs/{slug}.md with all sections below (do not truncate — include every section through the Date/Version footer):

# {Title}

Hey gstack team — ran into this while using /{skill-name}:

**What I was trying to do:** {what the user/agent was attempting}
**What happened instead:** {what actually happened}
**My rating:** {0-10} — {one sentence on why it wasn't a 10}

## Steps to reproduce
1. {step}

## Raw output

{paste the actual error or unexpected output here}


## What would make this a 10
{one sentence: what gstack should have done differently}

**Date:** {YYYY-MM-DD} | **Version:** {gstack version} | **Skill:** /{skill}

Slug: lowercase, hyphens, max 60 chars (e.g. browse-js-no-await). Skip if file already exists. Max 3 reports per session. File inline and continue — don't stop the workflow. Tell user: "Filed gstack field report: {title}"

Completion Status Protocol

When completing a skill workflow, report status using one of:

  • DONE — All steps completed successfully. Evidence provided for each claim.
  • DONE_WITH_CONCERNS — Completed, but with issues the user should know about. List each concern.
  • BLOCKED — Cannot proceed. State what is blocking and what was tried.
  • NEEDS_CONTEXT — Missing information required to continue. State exactly what you need.

Escalation

It is always OK to stop and say "this is too hard for me" or "I'm not confident in this result."

Bad work is worse than no work. You will not be penalized for escalating.

  • If you have attempted a task 3 times without success, STOP and escalate.
  • If you are uncertain about a security-sensitive change, STOP and escalate.
  • If the scope of work exceeds what you can verify, STOP and escalate.

Escalation format:

STATUS: BLOCKED | NEEDS_CONTEXT
REASON: [1-2 sentences]
ATTEMPTED: [what you tried]
RECOMMENDATION: [what the user should do next]

Telemetry (run last)

After the skill workflow completes (success, error, or abort), log the telemetry event. Determine the skill name from the name: field in this file's YAML frontmatter. Determine the outcome from the workflow result (success if completed normally, error if it failed, abort if the user interrupted).

PLAN MODE EXCEPTION — ALWAYS RUN: This command writes telemetry to ~/.gstack/analytics/ (user config directory, not project files). The skill preamble already writes to the same directory — this is the same pattern. Skipping this command loses session duration and outcome data.

Run this bash:

_TEL_END=$(date +%s)
_TEL_DUR=$(( _TEL_END - _TEL_START ))
rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true
~/.codex/skills/gstack/bin/gstack-telemetry-log \
  --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \
  --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null &

Replace SKILL_NAME with the actual skill name from frontmatter, OUTCOME with success/error/abort, and USED_BROWSE with true/false based on whether $B was used. If you cannot determine the outcome, use "unknown". This runs in the background and never blocks the user.

Step 0: Detect base branch

Determine which branch this PR targets. Use the result as "the base branch" in all subsequent steps.

  1. Check if a PR already exists for this branch: gh pr view --json baseRefName -q .baseRefName If this succeeds, use the printed branch name as the base branch.
  2. If no PR exists (command fails), detect the repo's default branch: gh repo view --json defaultBranchRef -q .defaultBranchRef.name
  3. If both commands fail, fall back to main.

Print the detected base branch name. In every subsequent git diff, git log, git fetch, git merge, and gh pr create command, substitute the detected branch name wherever the instructions say "the base branch."


Ship: Fully Automated Ship Workflow

You are running the /ship workflow. This is a non-interactive, fully automated workflow. Do NOT ask for confirmation at any step. The user said /ship which means DO IT. Run straight through and output the PR URL at the end.

Only stop for:

  • 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
  • MINOR or MAJOR version bump needed (ask — see Step 4)
  • Greptile review comments that need user decision (complex fixes, false positives)
  • TODOS.md missing and user wants to create one (ask — see Step 5.5)
  • TODOS.md disorganized and user wants to reorganize (ask — see Step 5.5)

Never stop for:

  • Uncommitted changes (always include them)
  • Version bump choice (auto-pick MICRO or PATCH — see Step 4)
  • 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 (auto-generate and commit, or flag in PR body)

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 diff <base>...HEAD --stat and git log <base>..HEAD --oneline to understand what's being shipped.
  4. Check review readiness:

Review Readiness Dashboard

After completing the review, read the review log and config to display the dashboard.

~/.codex/skills/gstack/bin/gstack-review-read

Parse the output. Find the most recent entry for each skill (plan-ceo-review, plan-eng-review, plan-design-review, design-review-lite, adversarial-review, codex-review). Ignore entries with timestamps older than 7 days. For the Adversarial row, show whichever is more recent between adversarial-review (new auto-scaled) and codex-review (legacy). For Design Review, show whichever is more recent between plan-design-review (full visual audit) and design-review-lite (code-level check). Append "(FULL)" or "(LITE)" to the status to distinguish. Display:

+====================================================================+
|                    REVIEW READINESS DASHBOARD                       |
+====================================================================+
| Review          | Runs | Last Run            | Status    | Required |
|-----------------|------|---------------------|-----------|----------|
| Eng Review      |  1   | 2026-03-16 15:00    | CLEAR     | YES      |
| CEO Review      |  0   | —                   | —         | no       |
| Design Review   |  0   | —                   | —         | no       |
| Adversarial     |  0   | —                   | —         | no       |
+--------------------------------------------------------------------+
| VERDICT: CLEARED — Eng Review passed                                |
+====================================================================+

Review tiers:

  • Eng Review (required by default): The only review that gates shipping. Covers architecture, code quality, tests, performance. Can be disabled globally with `gstack-config set skip_eng_review true` (the "don't bother me" setting).
  • CEO Review (optional): Use your judgment. Recommend it for big product/business changes, new user-facing features, or scope decisions. Skip for bug fixes, refactors, infra, and cleanup.
  • Design Review (optional): Use your judgment. Recommend it for UI/UX changes. Skip for backend-only, infra, or prompt-only changes.
  • Adversarial Review (automatic): Auto-scales by diff size. Small diffs (<50 lines) skip adversarial. Medium diffs (50–199) get cross-model adversarial. Large diffs (200+) get all 4 passes: Claude structured, Codex structured, Claude adversarial subagent, Codex adversarial. No configuration needed.

Verdict logic:

  • CLEARED: Eng Review has >= 1 entry within 7 days with status "clean" (or `skip_eng_review` is `true`)
  • NOT CLEARED: Eng Review missing, stale (>7 days), or has open issues
  • CEO, Design, and Codex reviews are shown for context but never block shipping
  • If `skip_eng_review` config is `true`, Eng Review shows "SKIPPED (global)" and verdict is CLEARED

Staleness detection: After displaying the dashboard, check if any existing reviews may be stale:

  • Parse the `---HEAD---` section from the bash output to get the current HEAD commit hash
  • For each review entry that has a `commit` field: compare it against the current HEAD. If different, count elapsed commits: `git rev-list --count STORED_COMMIT..HEAD`. Display: "Note: {skill} review from {date} may be stale — {N} commits since review"
  • For entries without a `commit` field (legacy entries): display "Note: {skill} review from {date} has no commit tracking — consider re-running for accurate staleness detection"
  • If all reviews match the current HEAD, do not display any staleness notes

If the Eng Review is NOT "CLEAR":

  1. Check for a prior override on this branch:
    source <(~/.codex/skills/gstack/bin/gstack-slug 2>/dev/null)
    grep '"skill":"ship-review-override"' ~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl 2>/dev/null || echo "NO_OVERRIDE"
    If an override exists, display the dashboard and note "Review gate previously accepted — continuing." Do NOT ask again.
  2. If no override exists, use AskUserQuestion:
    • Show that Eng Review is missing or has open issues
    • RECOMMENDATION: Choose C if the change is obviously trivial (< 20 lines, typo fix, config-only); Choose B for larger changes
    • Options: A) Ship anyway B) Abort — run /plan-eng-review first C) Change is too small to need eng 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 <(~/.codex/skills/gstack/bin/gstack-diff-scope <base> 2>/dev/null). If SCOPE_FRONTEND=true and no design review (plan-design-review or design-review-lite) exists in the dashboard, mention: "Design Review not run — this PR changes frontend code. The lite design check will run automatically in Step 3.5, but consider running /design-review for a full visual audit post-implementation." Still never block.
  3. If the user chooses A or C, persist the decision so future /ship runs on this branch skip the gate:
    source <(~/.codex/skills/gstack/bin/gstack-slug 2>/dev/null)
    echo '{"skill":"ship-review-override","timestamp":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","decision":"USER_CHOICE"}' >> ~/.gstack/projects/$SLUG/$BRANCH-reviews.jsonl
    Substitute USER_CHOICE with "ship_anyway" or "not_relevant".

Step 2: Merge the base branch (BEFORE tests)

Fetch and merge the base branch into the feature branch so tests run against the merged state:

git fetch origin <base> && 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.


Step 2.5: Test Framework Bootstrap

Test Framework Bootstrap

Detect existing test framework and project runtime:

# Detect project runtime
[ -f Gemfile ] && echo "RUNTIME:ruby"
[ -f package.json ] && echo "RUNTIME:node"
[ -f requirements.txt ] || [ -f pyproject.toml ] && echo "RUNTIME:python"
[ -f go.mod ] && echo "RUNTIME:go"
[ -f Cargo.toml ] && echo "RUNTIME:rust"
[ -f composer.json ] && echo "RUNTIME:php"
[ -f mix.exs ] && echo "RUNTIME:elixir"
# Detect sub-frameworks
[ -f Gemfile ] && grep -q "rails" Gemfile 2>/dev/null && echo "FRAMEWORK:rails"
[ -f package.json ] && grep -q '"next"' package.json 2>/dev/null && echo "FRAMEWORK:nextjs"
# Check for existing test infrastructure
ls jest.config.* vitest.config.* playwright.config.* .rspec pytest.ini pyproject.toml phpunit.xml 2>/dev/null
ls -d test/ tests/ spec/ __tests__/ cypress/ e2e/ 2>/dev/null
# Check opt-out marker
[ -f .gstack/no-test-bootstrap ] && echo "BOOTSTRAP_DECLINED"

If test framework detected (config files or test directories found): Print "Test framework detected: {name} ({N} existing tests). Skipping bootstrap." Read 2-3 existing test files to learn conventions (naming, imports, assertion style, setup patterns). Store conventions as prose context for use in Phase 8e.5 or Step 3.4. Skip the rest of bootstrap.

If BOOTSTRAP_DECLINED appears: Print "Test bootstrap previously declined — skipping." Skip the rest of bootstrap.

If NO runtime detected (no config files found): Use AskUserQuestion: "I couldn't detect your project's language. What runtime are you using?" Options: A) Node.js/TypeScript B) Ruby/Rails C) Python D) Go E) Rust F) PHP G) Elixir H) This project doesn't need tests. If user picks H → write .gstack/no-test-bootstrap and continue without tests.

If runtime detected but no test framework — bootstrap:

B2. Research best practices

Use WebSearch to find current best practices for the detected runtime:

  • "[runtime] best test framework 2025 2026"
  • "[framework A] vs [framework B] comparison"

If WebSearch is unavailable, use this built-in knowledge table:

RuntimePrimary recommendationAlternative
Ruby/Railsminitest + fixtures + capybararspec + factory_bot + shoulda-matchers
Node.jsvitest + @testing-libraryjest + @testing-library
Next.jsvitest + @testing-library/react + playwrightjest + cypress
Pythonpytest + pytest-covunittest
Gostdlib testing + testifystdlib only
Rustcargo test (built-in) + mockall—
PHPphpunit + mockerypest
ElixirExUnit (built-in) + ex_machina—

B3. Framework selection

Use AskUserQuestion: "I detected this is a [Runtime/Framework] project with no test framework. I researched current best practices. Here are the options: A) [Primary] — [rationale]. Includes: [packages]. Supports: unit, integration, smoke, e2e B) [Alternative] — [rationale]. Includes: [packages] C) Skip — don't set up testing right now RECOMMENDATION: Choose A because [reason based on project context]"

If user picks C → write .gstack/no-test-bootstrap. Tell user: "If you change your mind later, delete .gstack/no-test-bootstrap and re-run." Continue without tests.

If multiple runtimes detected (monorepo) → ask which runtime to set up first, with option to do both sequentially.

B4. Install and configure

  1. Install the chosen packages (npm/bun/gem/pip/etc.)
  2. Create minimal config file
  3. Create directory structure (test/, spec/, etc.)
  4. Create one example test matching the project's code to verify setup works

If package installation fails → debug once. If still failing → revert with git checkout -- package.json package-lock.json (or equivalent for the runtime). Warn user and continue without tests.

B4.5. First real tests

Generate 3-5 real tests for existing code:

  1. Find recently changed files: git log --since=30.days --name-only --format="" | sort | uniq -c | sort -rn | head -10
  2. Prioritize by risk: Error handlers > business logic with conditionals > API endpoints > pure functions
  3. For each file: Write one test that tests real behavior with meaningful assertions. Never expect(x).toBeDefined() — test what the code DOES.
  4. Run each test. Passes → keep. Fails → fix once. Still fails → delete silently.
  5. Generate at least 1 test, cap at 5.

Never import secrets, API keys, or credentials in test files. Use environment variables or test fixtures.

B5. Verify

# Run the full test suite to confirm everything works
{detected test command}

If tests fail → debug once. If still failing → revert all bootstrap changes and warn user.

B5.5. CI/CD pipeline

# Check CI provider
ls -d .github/ 2>/dev/null && echo "CI:github"
ls .gitlab-ci.yml .circleci/ bitrise.yml 2>/dev/null

If .github/ exists (or no CI detected — default to GitHub Actions): Create .github/workflows/test.yml with:

  • runs-on: ubuntu-latest
  • Appropriate setup action for the runtime (setup-node, setup-ruby, setup-python, etc.)
  • The same test command verified in B5
  • Trigger: push + pull_request

If non-GitHub CI detected → skip CI generation with note: "Detected {provider} — CI pipeline generation supports GitHub Actions only. Add test step to your existing pipeline manually."

B6. Create TESTING.md

First check: If TESTING.md already exists → read it and update/append rather than overwriting. Never destroy existing content.

Write TESTING.md with:

  • Philosophy: "100% test coverage is the key to great vibe coding. Tests let you move fast, trust your instincts, and ship with confidence — without them, vibe coding is just yolo coding. With tests, it's a superpower."
  • Framework name and version
  • How to run tests (the verified command from B5)
  • Test layers: Unit tests (what, where, when), Integration tests, Smoke tests, E2E tests
  • Conventions: file naming, assertion style, setup/teardown patterns

B7. Update CLAUDE.md

First check: If CLAUDE.md already has a ## Testing section → skip. Don't duplicate.

Append a ## Testing section:

  • Run command and test directory
  • Reference to TESTING.md
  • Test expectations:
    • 100% test coverage is the goal — tests make vibe coding safe
    • When writing new functions, write a corresponding test
    • When fixing a bug, write a regression test
    • When adding error handling, write a test that triggers the error
    • When adding a conditional (if/else, switch), write tests for BOTH paths
    • Never commit code that makes existing tests fail

B8. Commit

git status --porcelain

Only commit if there are changes. Stage all bootstrap files (config, test directory, TESTING.md, CLAUDE.md, .github/workflows/test.yml if created): git commit -m "chore: bootstrap test framework ({framework name})"



Step 3: Run tests (on merged code)

Do NOT run RAILS_ENV=test bin/rails db:migrate — bin/test-lane already calls db:test:prepare internally, which loads the schema into the correct lane database. Running bare test migrations without INSTANCE hits an orphan DB and corrupts structure.sql.

Run both test suites in parallel:

bin/test-lane 2>&1 | tee /tmp/ship_tests.txt &
npm run test 2>&1 | tee /tmp/ship_vitest.txt &
wait

After both complete, read the output files and check pass/fail.

If any test fails: Do NOT immediately stop. Apply the Test Failure Ownership Triage:

Test Failure Ownership Triage

When tests fail, do NOT immediately stop. First, determine ownership:

Step T1: Classify each failure

For each failing test:

  1. Get the files changed on this branch:
    git diff origin/<base>...HEAD --name-only
  2. Classify the failure:

    • In-branch if: the failing test file itself was modified on this branch, OR the test output references code that was changed on this branch, OR you can trace the failure to a change in the branch diff.
    • Likely pre-existing if: neither the test file nor the code it tests was modified on this branch, AND the failure is unrelated to any branch change you can identify.
    • When ambiguous, default to in-branch. It is safer to stop the developer than to let a broken test ship. Only classify as pre-existing when you are confident.

    This classification is heuristic — use your judgment reading the diff and the test output. You do not have a programmatic dependency graph.

Step T2: Handle in-branch failures

STOP. These are your failures. Show them and do not proceed. The developer must fix their own broken tests before shipping.

Step T3: Handle pre-existing failures

Check REPO_MODE from the preamble output.

If REPO_MODE is solo:

Use AskUserQuestion:

These test failures appear pre-existing (not caused by your branch changes):

[list each failure with file:line and brief error description]

Since this is a solo repo, you're the only one who will fix these.

RECOMMENDATION: Choose A — fix now while the context is fresh. Completeness: 9/10. A) Investigate and fix now (human: ~2-4h / CC: ~15min) — Completeness: 10/10 B) Add as P0 TODO — fix after this branch lands — Completeness: 7/10 C) Skip — I know about this, ship anyway — Completeness: 3/10

If REPO_MODE is collaborative or unknown:

Use AskUserQuestion:

These test failures appear pre-existing (not caused by your branch changes):

[list each failure with file:line and brief error description]

This is a collaborative repo — these may be someone else's responsibility.

RECOMMENDATION: Choose B — assign it to whoever broke it so the right person fixes it. Completeness: 9/10. A) Investigate and fix now anyway — Completeness: 10/10 B) Blame + assign GitHub issue to the author — Completeness: 9/10 C) Add as P0 TODO — Completeness: 7/10 D) Skip — ship anyway — Completeness: 3/10

Step T4: Execute the chosen action

If "Investigate and fix now":

  • Switch to /investigate mindset: root cause first, then minimal fix.
  • Fix the pre-existing failure.
  • Commit the fix separately from the branch's changes: git commit -m "fix: pre-existing test failure in <test-file>"
  • Continue with the workflow.

If "Add as P0 TODO":

  • If TODOS.md exists, add the entry following the format in review/TODOS-format.md (or .agents/skills/gstack/review/TODOS-format.md).
  • If TODOS.md does not exist, create it with the standard header and add the entry.
  • Entry should include: title, the error output, which branch it was noticed on, and priority P0.
  • Continue with the workflow — treat the pre-existing failure as non-blocking.

If "Blame + assign GitHub issue" (collaborative only):

  • Find who likely broke it. Check BOTH the test file AND the production code it tests:
    # Who last touched the failing test?
    git log --format="%an (%ae)" -1 -- <failing-test-file>
    # Who last touched the production code the test covers? (often the actual breaker)
    git log --format="%an (%ae)" -1 -- <source-file-under-test>
    If these are different people, prefer the production code author — they likely introduced the regression.
  • Create a GitHub issue assigned to that person:
    gh issue create \
      --title "Pre-existing test failure: <test-name>" \
      --body "Found failing on branch <current-branch>. Failure is pre-existing.\n\n**Error:**\n```\n<first 10 lines>\n```\n\n**Last modified by:** <author>\n**Noticed by:** gstack /ship on <date>" \
      --assignee "<github-username>"
  • If gh is not available or --assignee fails (user not in org, etc.), create the issue without assignee and note who should look at it in the body.
  • Continue with the workflow.

If "Skip":

  • Continue with the workflow.
  • Note in output: "Pre-existing test failure skipped: <test-name>"

After triage: If any in-branch failures remain unfixed, STOP. Do not proceed. If all failures were pre-existing and handled (fixed, TODOed, assigned, or skipped), continue to Step 3.25.

If all pass: Continue silently — just note the counts briefly.


Step 3.25: Eval Suites (conditional)

Evals are mandatory when prompt-related files change. Skip this step entirely if no prompt files are in the diff.

1. Check if the diff touches prompt-related files:

git diff origin/<base> --name-only

Match against these patterns (from CLAUDE.md):

  • app/services/*_prompt_builder.rb
  • app/services/*_generation_service.rb, *_writer_service.rb, *_designer_service.rb
  • app/services/*_evaluator.rb, *_scorer.rb, *_classifier_service.rb, *_analyzer.rb
  • app/services/concerns/*voice*.rb, *writing*.rb, *prompt*.rb, *token*.rb
  • app/services/chat_tools/*.rb, app/services/x_thread_tools/*.rb
  • config/system_prompts/*.txt
  • test/evals/**/* (eval infrastructure changes affect all suites)

If no matches: Print "No prompt-related files changed — skipping evals." and continue to Step 3.5.

2. Identify affected eval suites:

Each eval runner (test/evals/*_eval_runner.rb) declares PROMPT_SOURCE_FILES listing which source files affect it. Grep these to find which suites match the changed files:

grep -l "changed_file_basename" test/evals/*_eval_runner.rb

Map runner → test file: post_generation_eval_runner.rb → post_generation_eval_test.rb.

Special cases:

  • Changes to test/evals/judges/*.rb, test/evals/support/*.rb, or test/evals/fixtures/ affect ALL suites that use those judges/support files. Check imports in the eval test files to determine which.
  • Changes to config/system_prompts/*.txt — grep eval runners for the prompt filename to find affected suites.
  • If unsure which suites are affected, run ALL suites that could plausibly be impacted. Over-testing is better than missing a regression.

3. Run affected suites at EVAL_JUDGE_TIER=full:

/ship is a pre-merge gate, so always use full tier (Sonnet structural + Opus persona judges).

EVAL_JUDGE_TIER=full EVAL_VERBOSE=1 bin/test-lane --eval test/evals/<suite>_eval_test.rb 2>&1 | tee /tmp/ship_evals.txt

If multiple suites need to run, run them sequentially (each needs a test lane). If the first suite fails, stop immediately — don't burn API cost on remaining suites.

4. Check results:

  • If any eval fails: Show the failures, the cost dashboard, and STOP. Do not proceed.
  • If all pass: Note pass counts and cost. Continue to Step 3.5.

5. Save eval output — include eval results and cost dashboard in the PR body (Step 8).

Tier reference (for context — /ship always uses full):

TierWhenSpeed (cached)Cost
fast (Haiku)Dev iteration, smoke tests~5s (14x faster)~$0.07/run
standard (Sonnet)Default dev, bin/test-lane --eval~17s (4x faster)~$0.37/run
full (Opus persona)/ship and pre-merge~72s (baseline)~$1.27/run

Step 3.4: Test Coverage Audit

100% coverage is the goal — every untested path is a path where bugs hide and vibe coding becomes yolo coding. Evaluate what was ACTUALLY coded (from the diff), not what was planned.

Test Framework Detection

Before analyzing coverage, detect the project's test framework:

  1. Read CLAUDE.md — look for a ## Testing section with test command and framework name. If found, use that as the authoritative source.
  2. If CLAUDE.md has no testing section, auto-detect:
# Detect project runtime
[ -f Gemfile ] && echo "RUNTIME:ruby"
[ -f package.json ] && echo "RUNTIME:node"
[ -f requirements.txt ] || [ -f pyproject.toml ] && echo "RUNTIME:python"
[ -f go.mod ] && echo "RUNTIME:go"
[ -f Cargo.toml ] && echo "RUNTIME:rust"
# Check for existing test infrastructure
ls jest.config.* vitest.config.* playwright.config.* cypress.config.* .rspec pytest.ini phpunit.xml 2>/dev/null
ls -d test/ tests/ spec/ __tests__/ cypress/ e2e/ 2>/dev/null
  1. If no framework detected: falls through to the Test Framework Bootstrap step (Step 2.5) which handles full setup.

0. Before/after test count:

# Count test files before any generation
find . -name '*.test.*' -o -name '*.spec.*' -o -name '*_test.*' -o -name '*_spec.*' | grep -v node_modules | wc -l

Store this number for the PR body.

1. Trace every codepath changed using git diff origin/<base>...HEAD:

Read every changed file. For each one, trace how data flows through the code — don't just list functions, actually follow the execution:

  1. Read the diff. For each changed file, read the full file (not just the diff hunk) to understand context.
  2. Trace data flow. Starting from each entry point (route handler, exported function, event listener, component render), follow the data through every branch:
    • Where does input come from? (request params, props, database, API call)
    • What transforms it? (validation, mapping, computation)
    • Where does it go? (database write, API response, rendered output, side effect)
    • What can go wrong at each step? (null/undefined, invalid input, network failure, empty collection)
  3. Diagram the execution. For each changed file, draw an ASCII diagram showing:
    • Every function/method that was added or modified
    • Every conditional branch (if/else, switch, ternary, guard clause, early return)
    • Every error path (try/catch, rescue, error boundary, fallback)
    • Every call to another function (trace into it — does IT have untested branches?)
    • Every edge: what happens with null input? Empty array? Invalid type?

This is the critical step — you're building a map of every line of code that can execute differently based on input. Every branch in this diagram needs a test.

2. Map user flows, interactions, and error states:

Code coverage isn't enough — you need to cover how real users interact with the changed code. For each changed feature, think through:

  • User flows: What sequence of actions does a user take that touches this code? Map the full journey (e.g., "user clicks 'Pay' → form validates → API call → success/failure screen"). Each step in the journey needs a test.
  • Interaction edge cases: What happens when the user does something unexpected?
    • Double-click/rapid resubmit
    • Navigate away mid-operation (back button, close tab, click another link)
    • Submit with stale data (page sat open for 30 minutes, session expired)
    • Slow connection (API takes 10 seconds — what does the user see?)
    • Concurrent actions (two tabs, same form)
  • Error states the user can see: For every error the code handles, what does the user actually experience?
    • Is there a clear error message or a silent failure?
    • Can the user recover (retry, go back, fix input) or are they stuck?
    • What happens with no network? With a 500 from the API? With invalid data from the server?
  • Empty/zero/boundary states: What does the UI show with zero results? With 10,000 results? With a single character input? With maximum-length input?

Add these to your diagram alongside the code branches. A user flow with no test is just as much a gap as an untested if/else.

3. Check each branch against existing tests:

Go through your diagram branch by branch — both code paths AND user flows. For each one, search for a test that exercises it:

  • Function processPayment() → look for billing.test.ts, billing.spec.ts, test/billing_test.rb
  • An if/else → look for tests covering BOTH the true AND false path
  • An error handler → look for a test that triggers that specific error condition
  • A call to helperFn() that has its own branches → those branches need tests too
  • A user flow → look for an integration or E2E test that walks through the journey
  • An interaction edge case → look for a test that simulates the unexpected action

Quality scoring rubric:

  • ★★★ Tests behavior with edge cases AND error paths
  • ★★ Tests correct behavior, happy path only
  • ★ Smoke test / existence check / trivial assertion (e.g., "it renders", "it doesn't throw")

E2E Test Decision Matrix

When checking each branch, also determine whether a unit test or E2E/integration test is the right tool:

RECOMMEND E2E (mark as [→E2E] in the diagram):

  • Common user flow spanning 3+ components/services (e.g., signup → verify email → first login)
  • Integration point where mocking hides real failures (e.g., API → queue → worker → DB)
  • Auth/payment/data-destruction flows — too important to trust unit tests alone

RECOMMEND EVAL (mark as [→EVAL] in the diagram):

  • Critical LLM call that needs a quality eval (e.g., prompt change → test output still meets quality bar)
  • Changes to prompt templates, system instructions, or tool definitions

STICK WITH UNIT TESTS:

  • Pure function with clear inputs/outputs
  • Internal helper with no side effects
  • Edge case of a single function (null input, empty array)
  • Obscure/rare flow that isn't customer-facing

REGRESSION RULE (mandatory)

IRON RULE: When the coverage audit identifies a REGRESSION — code that previously worked but the diff broke — a regression test is written immediately. No AskUserQuestion. No skipping. Regressions are the highest-priority test because they prove something broke.

A regression is when:

  • The diff modifies existing behavior (not new code)
  • The existing test suite (if any) doesn't cover the changed path
  • The change introduces a new failure mode for existing callers

When uncertain whether a change is a regression, err on the side of writing the test.

Format: commit as test: regression test for {what broke}

4. Output ASCII coverage diagram:

Include BOTH code paths and user flows in the same diagram. Mark E2E-worthy and eval-worthy paths:

CODE PATH COVERAGE
===========================
[+] src/services/billing.ts
    │
    ├── processPayment()
    │   ├── [★★★ TESTED] Happy path + card declined + timeout — billing.test.ts:42
    │   ├── [GAP]         Network timeout — NO TEST
    │   └── [GAP]         Invalid currency — NO TEST
    │
    └── refundPayment()
        ├── [★★  TESTED] Full refund — billing.test.ts:89
        └── [★   TESTED] Partial refund (checks non-throw only) — billing.test.ts:101

USER FLOW COVERAGE
===========================
[+] Payment checkout flow
    │
    ├── [★★★ TESTED] Complete purchase — checkout.e2e.ts:15
    ├── [GAP] [→E2E] Double-click submit — needs E2E, not just unit
    ├── [GAP]         Navigate away during payment — unit test sufficient
    └── [★   TESTED]  Form validation errors (checks render only) — checkout.test.ts:40

[+] Error states
    │
    ├── [★★  TESTED] Card declined message — billing.test.ts:58
    ├── [GAP]         Network timeout UX (what does user see?) — NO TEST
    └── [GAP]         Empty cart submission — NO TEST

[+] LLM integration
    │
    └── [GAP] [→EVAL] Prompt template change — needs eval test

─────────────────────────────────
COVERAGE: 5/13 paths tested (38%)
  Code paths: 3/5 (60%)
  User flows: 2/8 (25%)
QUALITY:  ★★★: 2  ★★: 2  ★: 1
GAPS: 8 paths need tests (2 need E2E, 1 needs eval)
─────────────────────────────────

Fast path: All paths covered → "Step 3.4: All new code paths have test coverage ✓" Continue.

5. Generate tests for uncovered paths:

If test framework detected (or bootstrapped in Step 2.5):

  • Prioritize error handlers and edge cases first (happy paths are more likely already tested)
  • Read 2-3 existing test files to match conventions exactly
  • Generate unit tests. Mock all external dependencies (DB, API, Redis).
  • For paths marked [→E2E]: generate integration/E2E tests using the project's E2E framework (Playwright, Cypress, Capybara, etc.)
  • For paths marked [→EVAL]: generate eval tests using the project's eval framework, or flag for manual eval if none exists
  • Write tests that exercise the specific uncovered path with real assertions
  • Run each test. Passes → commit as test: coverage for {feature}
  • Fails → fix once. Still fails → revert, note gap in diagram.

Caps: 30 code paths max, 20 tests generated max (code + user flow combined), 2-min per-test exploration cap.

If no test framework AND user declined bootstrap → diagram only, no generation. Note: "Test generation skipped — no test framework configured."

Diff is test-only changes: Skip Step 3.4 entirely: "No new application code paths to audit."

6. After-count and coverage summary:

# Count test files after generation
find . -name '*.test.*' -o -name '*.spec.*' -o -name '*_test.*' -o -name '*_spec.*' | grep -v node_modules | wc -l

For PR body: Tests: {before} → {after} (+{delta} new) Coverage line: Test Coverage Audit: N new code paths. M covered (X%). K tests generated, J committed.

Test Plan Artifact

After producing the coverage diagram, write a test plan artifact so /qa and /qa-only can consume it:

source <(~/.codex/skills/gstack/bin/gstack-slug 2>/dev/null) && mkdir -p ~/.gstack/projects/$SLUG
USER=$(whoami)
DATETIME=$(date +%Y%m%d-%H%M%S)

Write to ~/.gstack/projects/{slug}/{user}-{branch}-ship-test-plan-{datetime}.md:

# Test Plan
Generated by /ship on {date}
Branch: {branch}
Repo: {owner/repo}

## Affected Pages/Routes
- {URL path} — {what to test and why}

## Key Interactions to Verify
- {interaction description} on {page}

## Edge Cases
- {edge case} on {page}

## Critical Paths
- {end-to-end flow that must work}

Step 3.5: Pre-Landing Review

Review the diff for structural issues that tests don't catch.

  1. Read .agents/skills/gstack/review/checklist.md. If the file cannot be read, STOP and report the error.
  2. Run git diff origin/<base> to get the full diff (scoped to feature changes against the freshly-fetched base branch).
  3. Apply the review checklist in two passes:
    • Pass 1 (CRITICAL): SQL & Data Safety, LLM Output Trust Boundary
    • Pass 2 (INFORMATIONAL): All remaining categories

Design Review (conditional, diff-scoped)

Check if the diff touches frontend files using gstack-diff-scope:

source <(~/.codex/skills/gstack/bin/gstack-diff-scope <base> 2>/dev/null)

If SCOPE_FRONTEND=false: Skip design review silently. No output.

If SCOPE_FRONTEND=true:

  1. Check for DESIGN.md. If DESIGN.md or design-system.md exists in the repo root, read it. All design findings are calibrated against it — patterns blessed in DESIGN.md are not flagged. If not found, use universal design principles.
  2. Read .agents/skills/gstack/review/design-checklist.md. If the file cannot be read, skip design review with a note: "Design checklist not found — skipping design review."
  3. Read each changed frontend file (full file, not just diff hunks). Frontend files are identified by the patterns listed in the checklist.
  4. Apply the design checklist against the changed files. For each item:
    • [HIGH] mechanical CSS fix (outline: none, !important, font-size < 16px): classify as AUTO-FIX
    • [HIGH/MEDIUM] design judgment needed: classify as ASK
    • [LOW] intent-based detection: present as "Possible — verify visually or run /design-review"
  5. Include findings in the review output under a "Design Review" header, following the output format in the checklist. Design findings merge with code review findings into the same Fix-First flow.
  6. Log the result for the Review Readiness Dashboard:
~/.codex/skills/gstack/bin/gstack-review-log '{"skill":"design-review-lite","timestamp":"TIMESTAMP","status":"STATUS","findings":N,"auto_fixed":M,"commit":"COMMIT"}'

Substitute: TIMESTAMP = ISO 8601 datetime, STATUS = "clean" if 0 findings or "issues_found", N = total findings, M = auto-fixed count, COMMIT = output of git rev-parse --short HEAD.

Include any design findings alongside the code review findings. They follow the same Fix-First flow below.

  1. Classify each finding as AUTO-FIX or ASK per the Fix-First Heuristic in checklist.md. Critical findings lean toward ASK; informational lean toward AUTO-FIX.
  2. Auto-fix all AUTO-FIX items. Apply each fix. Output one line per fix: [AUTO-FIXED] [file:line] Problem → what you did
  3. If ASK items remain, present them in ONE AskUserQuestion:
    • List each with number, severity, problem, recommended fix
    • Per-item options: A) Fix B) Skip
    • Overall RECOMMENDATION
    • If 3 or fewer ASK items, you may use individual AskUserQuestion calls instead
  4. After all fixes (auto + user-approved):
    • If ANY fixes were applied: commit fixed files by name (git add <fixed-files> && git commit -m "fix: pre-landing review fixes"), then STOP and tell the user to run /ship again to re-test.
    • If no fixes applied (all ASK items skipped, or no issues found): continue to Step 4.
  5. Output summary: Pre-Landing Review: N issues — M auto-fixed, K asked (J fixed, L skipped)

    If no issues found: Pre-Landing Review: No issues found.

Save the review output — it goes into the PR body in Step 8.


Step 3.75: Address Greptile review comments (if PR exists)

Read .agents/skills/gstack/review/greptile-triage.md and follow the fetch, filter, classify, and escalation detection steps.

If no PR exists, gh fails, API returns an error, or there are zero Greptile comments: Skip this step silently. Continue to Step 4.

If Greptile comments are found:

Include a Greptile summary in your output: + N Greptile comments (X valid, Y fixed, Z FP)

Before replying to any comment, run the Escalation Detection algorithm from greptile-triage.md to determine whether to use Tier 1 (friendly) or Tier 2 (firm) reply templates.

For each classified comment:

VALID & ACTIONABLE: Use AskUserQuestion with:

  • The comment (file:line or [top-level] + body summary + permalink URL)
  • RECOMMENDATION: Choose A because [one-line reason]
  • Options: A) Fix now, B) Acknowledge and ship anyway, C) It's a false positive
  • If user chooses A: apply the fix, commit the fixed files (git add <fixed-files> && git commit -m "fix: address Greptile review — <brief description>"), reply using the Fix reply template from greptile-triage.md (include inline diff + explanation), and save to both per-project and global greptile-history (type: fix).
  • If user chooses C: reply using the False Positive reply template from greptile-triage.md (include evidence + suggested re-rank), save to both per-project and global greptile-history (type: fp).

VALID BUT ALREADY FIXED: Reply using the Already Fixed reply template from greptile-triage.md — no AskUserQuestion needed:

  • Include what was done and the fixing commit SHA
  • Save to both per-project and global greptile-history (type: already-fixed)

FALSE POSITIVE: Use AskUserQuestion:

  • Show the comment and why you think it's wrong (file:line or [top-level] + body summary + permalink URL)
  • Options:
    • A) Reply to Greptile explaining the false positive (recommended if clearly wrong)
    • B) Fix it anyway (if trivial)
    • C) Ignore silently
  • If user chooses A: reply using the False Positive reply template from greptile-triage.md (include evidence + suggested re-rank), save to both per-project and global greptile-history (type: fp)

SUPPRESSED: Skip silently — these are known false positives from previous triage.

After all comments are resolved: If any fixes were applied, the tests from Step 3 are now stale. Re-run tests (Step 3) before continuing to Step 4. If no fixes were applied, continue to Step 4.


Step 4: Version bump (auto-decide)

  1. Read the current VERSION file (4-digit format: MAJOR.MINOR.PATCH.MICRO)
  2. Auto-decide the bump level based on the diff:
    • Count lines changed (git diff origin/<base>...HEAD --stat | tail -1)
    • MICRO (4th digit): < 50 lines changed, trivial tweaks, typos, config
    • PATCH (3rd digit): 50+ lines changed, bug fixes, small-medium features
    • MINOR (2nd digit): ASK the user — only for major features or significant architectural changes
    • MAJOR (1st digit): ASK the user — only for milestones or breaking changes
  3. Compute the new version:
    • Bumping a digit resets all digits to its right to 0
    • Example: 0.19.1.0 + PATCH → 0.19.2.0
  4. Write the new version to the VERSION file.

Step 5: CHANGELOG (auto-generate)

  1. Read CHANGELOG.md header to know the format.
  2. Auto-generate the entry from ALL commits on the branch (not just recent ones):
    • Use git log <base>..HEAD --oneline to see every commit being shipped
    • Use git diff <base>...HEAD to see the full diff against the base branch
    • The CHANGELOG entry must be comprehensive of ALL changes going into the PR
    • If existing CHANGELOG entries on the branch already cover some commits, replace them with one unified entry for the new version
    • Categorize changes into applicable sections:
      • ### Added — new features
      • ### Changed — changes to existing functionality
      • ### Fixed — bug fixes
      • ### Removed — removed features
    • Write concise, descriptive bullet points
    • Insert after the file header (line 5), dated today
    • Format: ## [X.Y.Z.W] - YYYY-MM-DD

Do NOT ask the user to describe changes. Infer from the diff and commit history.


Step 5.5: TODOS.md (auto-update)

Cross-reference the project's TODOS.md against the changes being shipped. Mark completed items automatically; prompt only if the file is missing or disorganized.

Read .agents/skills/gstack/review/TODOS-format.md for the canonical format reference.

1. Check if TODOS.md exists in the repository root.

If TODOS.md does not exist: Use AskUserQuestion:

  • Message: "GStack recommends maintaining a TODOS.md organized by skill/component, then priority (P0 at top through P4, then Completed at bottom). See TODOS-format.md for the full format. Would you like to create one?"
  • Options: A) Create it now, B) Skip for now
  • If A: Create TODOS.md with a skeleton (# TODOS heading + ## Completed section). Continue to step 3.
  • If B: Skip the rest of Step 5.5. Continue to Step 6.

2. Check structure and organization:

Read TODOS.md and verify it follows the recommended structure:

  • Items grouped under ## <Skill/Component> headings
  • Each item has **Priority:** field with P0-P4 value
  • A ## Completed section at the bottom

If disorganized (missing priority fields, no component groupings, no Completed section): Use AskUserQuestion:

  • Message: "TODOS.md doesn't follow the recommended structure (skill/component groupings, P0-P4 priority, Completed section). Would you like to reorganize it?"
  • Options: A) Reorganize now (recommended), B) Leave as-is
  • If A: Reorganize in-place following TODOS-format.md. Preserve all content — only restructure, never delete items.
  • If B: Continue to step 3 without restructuring.

3. Detect completed TODOs:

This step is fully automatic — no user interaction.

Use the diff and commit history already gathered in earlier steps:

  • git diff <base>...HEAD (full diff against the base branch)
  • git log <base>..HEAD --oneline (all commits being shipped)

For each TODO item, check if the changes in this PR complete it by:

  • Matching commit messages against the TODO title and description
  • Checking if files referenced in the TODO appear in the diff
  • Checking if the TODO's described work matches the functional changes

Be conservative: Only mark a TODO as completed if there is clear evidence in the diff. If uncertain, leave it alone.

4. Move completed items to the ## Completed section at the bottom. Append: **Completed:** vX.Y.Z (YYYY-MM-DD)

5. Output summary:

  • TODOS.md: N items marked complete (item1, item2, ...). M items remaining.
  • Or: TODOS.md: No completed items detected. M items remaining.
  • Or: TODOS.md: Created. / TODOS.md: Reorganized.

6. Defensive: If TODOS.md cannot be written (permission error, disk full), warn the user and continue. Never stop the ship workflow for a TODOS failure.

Save this summary — it goes into the PR body in Step 8.


Step 6: Commit (bisectable chunks)

Goal: Create small, logical commits that work well with git bisect and help LLMs understand what changed.

  1. Analyze the diff and group changes into logical commits. Each commit should represent one coherent change — not one file, but one logical unit.
  2. Commit ordering (earlier commits first):
    • Infrastructure: migrations, config changes, route additions
    • Models & services: new models, services, concerns (with their tests)
    • Controllers & views: controllers, views, JS/React components (with their tests)
    • VERSION + CHANGELOG + TODOS.md: always in the final commit
  3. Rules for splitting:
    • A model and its test file go in the same commit
    • A service and its test file go in the same commit
    • A controller, its views, and its test go in the same commit
    • Migrations are their own commit (or grouped with the model they support)
    • Config/route changes can group with the feature they enable
    • If the total diff is small (< 50 lines across < 4 files), a single commit is fine
  4. Each commit must be independently valid — no broken imports, no references to code that doesn't exist yet. Order commits so dependencies come first.
  5. Compose each commit message:
    • First line: <type>: <summary> (type = feat/fix/chore/refactor/docs)
    • Body: brief description of what this commit contains
    • Only the final commit (VERSION + CHANGELOG) gets the version tag and co-author trailer:
git commit -m "$(cat <<'EOF'
chore: bump version and changelog (vX.Y.Z.W)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Step 6.5: Verification Gate

IRON LAW: NO COMPLETION CLAIMS WITHOUT FRESH VERIFICATION EVIDENCE.

Before pushing, re-verify if code changed during Steps 4-6:

  1. Test verification: If ANY code changed after Step 3's test run (fixes from review findings, CHANGELOG edits don't count), re-run the test suite. Paste fresh output. Stale output from Step 3 is NOT acceptable.
  2. Build verification: If the project has a build step, run it. Paste output.
  3. Rationalization prevention:
    • "Should work now" → RUN IT.
    • "I'm confident" → Confidence is not evidence.
    • "I already tested earlier" → Code changed since then. Test again.
    • "It's a trivial change" → Trivial changes break production.

If tests fail here: STOP. Do not push. Fix the issue and return to Step 3.

Claiming work is complete without verification is dishonesty, not efficiency.


Step 7: Push

Push to the remote with upstream tracking:

git push -u origin <branch-name>

Step 8: Create PR

Create a pull request using gh:

gh pr create --base <base> --title "<type>: <summary>" --body "$(cat <<'EOF'
## Summary
<bullet points from CHANGELOG>

## Test Coverage
<coverage diagram from Step 3.4, or "All new code paths have test coverage.">
<If Step 3.4 ran: "Tests: {before} → {after} (+{delta} new)">

## Pre-Landing Review
<findings from Step 3.5 code review, or "No issues found.">

## Design Review
<If design review ran: "Design Review (lite): N findings — M auto-fixed, K skipped. AI Slop: clean/N issues.">
<If no frontend files changed: "No frontend files changed — design review skipped.">

## Eval Results
<If evals ran: suite names, pass/fail counts, cost dashboard summary. If skipped: "No prompt-related files changed — evals skipped.">

## Greptile Review
<If Greptile comments were found: bullet list with [FIXED] / [FALSE POSITIVE] / [ALREADY FIXED] tag + one-line summary per comment>
<If no Greptile comments found: "No Greptile comments.">
<If no PR existed during Step 3.75: omit this section entirely>

## TODOS
<If items marked complete: bullet list of completed items with version>
<If no items completed: "No TODO items completed in this PR.">
<If TODOS.md created or reorganized: note that>
<If TODOS.md doesn't exist and user skipped: omit this section>

## 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)
EOF
)"

Output the PR URL — then proceed to Step 8.5.


Step 8.5: Auto-invoke /document-release

After the PR is created, automatically sync project documentation. Read the document-release/SKILL.md skill file (adjacent to this skill's directory) and execute its full workflow:

  1. Read the /document-release skill: cat ${CLAUDE_SKILL_DIR}/../document-release/SKILL.md
  2. Follow its instructions — it reads all .md files in the project, cross-references the diff, and updates anything that drifted (README, ARCHITECTURE, CONTRIBUTING, CLAUDE.md, TODOS, etc.)
  3. If any docs were updated, commit the changes and push to the same branch:
    git add -A && git commit -m "docs: sync documentation with shipped changes" && git push
  4. If no docs needed updating, say "Documentation is current — no updates needed."

This step is automatic. Do not ask the user for confirmation. The goal is zero-friction doc updates — the user runs /ship and documentation stays current without a separate command.


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 3 tests, re-run before pushing.
  • Step 3.4 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.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.