# 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}-" "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 merge** → `gh 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= issue=# pr=<#N|none> 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 ```