Files
EVOLV/.claude/skills/ship-it/iterate.md
znetsixe 6ff262e96e chore(skills): add workflow chain — grill-me → prd → prd-to-issues → ship-it
Four workflow skills that take a feature from fuzzy idea to merged code.
Two human-in-the-loop phases (grill-me, prd), one mostly-together (prd-to-issues
files only on explicit 'create'), and one AFK (ship-it).

  grill-me        TOGETHER  pressure-test the idea with hard interview questions
  prd             TOGETHER  synthesize PRD; gaps stay explicit, not papered over
  prd-to-issues   MOSTLY    thin vertical-slice issues with coverage matrix +
                            per-issue Slice check; self-audits before showing
  ship-it         AFK       shell loop ships each slice end-to-end with one
                            commit per issue, status streams to terminal,
                            Ctrl-C-able, survives session close

Vertical-slice principle throughout: every issue cuts end-to-end through every
integration layer (no horizontal "do all the DB work first" issues). The
AFK loop only ships against acceptance criteria already locked in by the PRD
phase — autonomous code never runs against undefined contracts.

ship-it tracker support: gh (GitHub) and tea (Gitea). For this repo, set
SHIP_IT_TRUNK=development to override the main default.

See .claude/skills/README.md for the full how-to and a worked example.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 16:27:15 +02:00

5.1 KiB

ship-it iterate — one issue, end-to-end

You are running ONE iteration of the ship-it AFK loop. Implement, verify, and ship exactly one issue, then exit. The outer shell loop will pick the next one.

Mode: AFK. Do not ask questions. If the issue is genuinely undecidable from its body + linked PRD + grilling notes already in the issue or repo, drop a comment on the issue with the specific question, label it needs-decision, and exit with status=needs-decision. Do not guess at user intent.

Variables provided below this prompt: ISSUE_NUMBER, TRACKER_CLI (gh or tea), TRUNK_BRANCH, REPO_ROOT.

Steps

  1. Read the issue.

    • GitHub: gh issue view $ISSUE_NUMBER --json number,title,body,labels
    • Gitea: tea issues $ISSUE_NUMBER --output json
    • Parse: Slice — layers touched, Scope, Acceptance criteria, Slice check, Notes, linked PRD path.
    • If Acceptance criteria is missing or non-testable → exit status=needs-decision with reason "acceptance criteria not testable".
  2. Branch from latest trunk. git fetch origin && git switch -c "slice/${ISSUE_NUMBER}-<short-kebab-slug>" "origin/$TRUNK_BRANCH"

  3. Write the failing e2e test first. Anchored at the OUTERMOST layer named in Slice — layers touched (HTTP endpoint, UI smoke, dashboard query, log assertion — whatever the acceptance criterion observes). Run it. Confirm it fails for the right reason. If you can't write an e2e test for this slice, that's a sign the acceptance criterion isn't really observable end-to-end → exit status=needs-decision.

  4. Implement layer by layer. Walk the Slice — layers touched list. Make the minimal change at each layer to satisfy the slice — do not gold-plate, do not refactor adjacent code, do not "improve" things outside scope. Re-run the e2e test after each layer change.

  5. Run the broader test suite. Catch regressions caused by the slice. Fix any test that was green before and is now red — do not skip or mark tests. If a test was already red before your changes, leave it (note in PR body).

  6. Outermost-layer smoke check. The 30-second-demo check: hit the endpoint with curl, query the dashboard, tail the log, load the page. Observe what the acceptance criterion observes. Capture the output (curl response body, log snippet, query result) — you'll paste it into the PR body as evidence.

  7. Commit. One commit per slice (or a tight series — no WIP commits, no fixup commits, no "address review" before review exists). Read the repo's recent git log to match commit style. Message ends with Closes #${ISSUE_NUMBER}.

  8. Push and open PR.

    • GitHub: git push -u origin HEAD && gh pr create --fill
    • Gitea: git push -u origin HEAD && tea pr create --title "..." --description "..."
    • PR body must include:
      • Each acceptance criterion as a checked - [x] line.
      • The smoke-check evidence (curl output / log snippet / screenshot path) in a fenced block.
      • Closes #${ISSUE_NUMBER} (so the issue auto-closes on merge).
  9. Wait for CI and decide merge.

    • Poll: gh pr checks --watch (or tea pr status).
    • All green + branch protection allows direct mergegh pr merge --squash --delete-branch. Verify the merge commit landed on trunk.
    • All green + branch protection requires human review → leave PR open. Comment Ready for review — all acceptance criteria verified, smoke check passed. on the issue. Exit status=shipped with the PR number.
    • Red CI → one fix-and-push cycle. Read the failing log, fix the actual cause (do not skip the test). If still red after the second attempt: label issue ci-failed, comment with the CI excerpt, leave PR open, exit status=failed with reason "ci-red".
  10. Return to trunk. git switch $TRUNK_BRANCH && git pull --ff-only. If the slice was merged, run the smoke check one more time against integrated trunk. If it fails there → revert the merge, label regression, exit status=failed with reason "regression-on-trunk".

Boundaries

  • Never force-push, never rewrite shared history, never delete branches you didn't create.
  • Never bypass branch protection (--admin) or skip CI hooks (--no-verify).
  • Never auto-merge a PR whose CI is red or pending.
  • Never close an issue without the outermost-layer smoke check passing.
  • Never modify CI/CD config, IaC, or production data unless the slice's layers touched explicitly names that layer.
  • Never invent acceptance criteria. If they're vague, label needs-decision.
  • Never assign issues or change milestones.

Final output line

The shell loop greps for this exact line to determine outcome. Print it as the LAST line before exiting, on its own line, no decoration:

ITERATION_RESULT: status=<shipped|failed|needs-decision> issue=#<N> pr=<#N|none> reason=<short single-line reason>

Examples:

ITERATION_RESULT: status=shipped issue=#142 pr=#287 reason=merged-to-main
ITERATION_RESULT: status=shipped issue=#143 pr=#288 reason=open-for-review
ITERATION_RESULT: status=failed issue=#144 pr=#289 reason=ci-red-after-retry
ITERATION_RESULT: status=needs-decision issue=#145 pr=none reason=acceptance-criteria-not-testable