Open a pull request a senior engineer can approve with confidence, because the evidence is right there. This is the final phase of a slice: build (red-green-refactor) → CI (tdd-ci) → PR. Treat opening a PR as an outward-facing action — be careful and confirm before pushing.
-
Produce the evidence by actually running the tests. Show real run output, not claims. Always capture the unit-test summary (counts, pass/fail). Then, depending on the slice's boundary:
- Web slice: run the e2e suite with Playwright configured to capture screenshots + video (
video: 'on') + HTML report (see the red-green-refactor test-strategy reference). The PR must include a screenshot and a recording of the passing acceptance run — if none was produced, re-run with video on before continuing.
- Non-web slice (CLI / API / service): capture two transcripts to files — (a) the test-run output (e.g.
npm test / pytest -q), and (b) a real invocation through the boundary (the CLI run with its stdout + exit code, or the HTTP request + response). Redirect them to files so the collector can attach them, e.g. npm test > test-run.txt 2>&1 and node src/cli.js 2 3 > cli-demo.txt 2>&1.
Run tests from the slice's project directory if the app lives in a subfolder (see the plan's Project directory field).
-
Copy the evidence in (--copy-only). Run the collector from the repo root in copy-only mode — it copies the artifacts into docs/tdd-evidence/<feature>/<NN-slice>/ and scans them for secrets, but does not write the PR body yet. (Two phases matter: the body pins URLs to the current commit, so the evidence must be committed before the body is generated — otherwise the links point at a commit that doesn't contain the files. See step 5.) Plain Node, so the same invocation works on Windows/macOS/Linux:
Web slice — point --report-dir/--results-dir at the project dir if the app is in a subfolder (the collector resolves them from the repo root):
node "${CLAUDE_SKILL_DIR}/scripts/collect-evidence.mjs" --feature <feature-slug> --slice <NN-slice-slug> --report-dir <project-dir>/playwright-report --results-dir <project-dir>/test-results --copy-only
Non-web slice (--type cli|api|service, one or more --transcript):
node "${CLAUDE_SKILL_DIR}/scripts/collect-evidence.mjs" --feature <feature-slug> --slice <NN-slice-slug> --type cli --transcript test-run.txt --transcript cli-demo.txt --copy-only
By default it drops raw traces (*.zip) and HAR files (which often carry auth tokens); pass --include-traces only if you need them and have checked them.
-
Review the evidence for secrets — BEFORE committing anything. Read the collector's output: if it reports SECRETS SUSPECTED, open the named files and remove or redact any tokens, cookies, passwords, or env dumps (Playwright traces, HAR captures, and HTML reports are the usual culprits). This evidence is about to be committed and pushed and cannot be un-published once in history. Do not proceed until it is clean. Keep the project's raw test-results/ and playwright-report/ out of git via .gitignore — but anchor those patterns to the project dir (e.g. sandbox/web-adder/test-results/) or add !docs/tdd-evidence/**, so the unanchored patterns don't also ignore the committed copies under docs/tdd-evidence/ (a silent-empty-evidence trap).
-
Commit the cleaned evidence. Two statements (Windows PowerShell does not support &&):
git add docs/tdd-evidence/<feature>/<NN-slice>/
git commit -m "docs(<feature>): test evidence [slice NN]"
Confirm it actually committed (git show --stat HEAD) — if the folder is empty, your .gitignore swallowed it (step 3).
-
Generate the PR body (--body-only), now pinned to the evidence commit. Re-run the collector in body-only mode — it does not re-copy; it builds PR_BODY.md from the committed evidence, pinning every link to the current HEAD (the commit you just made), so the links resolve:
node "${CLAUDE_SKILL_DIR}/scripts/collect-evidence.mjs" --feature <feature-slug> --slice <NN-slice-slug> [--type cli] --body-only --template "${CLAUDE_SKILL_DIR}/assets/pr-body-template.md" --out PR_BODY.md
For a web slice it embeds screenshots and links the recording/report; for a non-web slice it embeds each transcript as a fenced code block (capped via --max-transcript-lines, default 200) and links the full file. Private repos: the collector auto-detects visibility (gh repo view) and, on a private repo, renders screenshots as clickable blob links instead of inline ![]() embeds — because raw.githubusercontent.com doesn't render for private repos. Override with --public/--private if detection is wrong. Then open PR_BODY.md and fill the remaining <placeholders> from the slice plan (description, what changed, how to review, the unit-test summary, risk notes, plan path); tick the checklist items that hold; be honest about anything partial. The PR body itself is git-ignored (regenerable), so it isn't committed.
-
Confirm, then push. Show the user the PR title, the body, and the branch you will push, and get explicit confirmation (this is outward-facing). Then push:
git push -u origin feat/<feature-slug>/<NN-slice-slug>
This also triggers the CI workflow. Never force-push.
-
Open the PR into main (single line so it pastes cleanly in any shell):
gh pr create --base main --head feat/<feature-slug>/<NN-slice-slug> --title "feat(<feature-slug>): <slice goal> [slice NN]" --body-file PR_BODY.md
Never target a base other than main unless the user explicitly asks.
-
Record the result. Put the PR URL into the slice plan's status log and the feature README's slice table, and tick the Definition-of-Done. Optionally note that CI is now running on the PR and they can require those checks before merge (see tdd-ci).
-
Report to the user (required — this is how branches stay tidy). End the run with an explicit, scannable summary so nothing is silently left behind:
- New branch created: name the branch you cut and pushed (
feat/<feature-slug>/<NN-slice-slug>), and that it now exists both locally and on origin.
- PR opened: the title and URL, into
main.
- Cleanup reminder: state plainly that this slice left a feature branch behind, and that once the PR is merged or closed they should run
safe-cleanup to retire the now-stale local branch (it reports first and confirms before deleting, and records recovery SHAs). This matters for repo hygiene — every slice adds a branch, so they accumulate fast.
Surface this every time you open a PR, even mid-pipeline under tdd-harness — a one-time reminder at the end of a multi-slice session is easy to miss. Keep it short, but never skip the branch name or the cleanup nudge.
A PR whose description proves the slice works: the behaviour described in plain language, the failing-then-passing acceptance test, the unit-test summary, a checklist, a link back to the execution plan, and modality-appropriate evidence of it working — embedded screenshots plus a linked recording and HTML report for a web slice, or the embedded test-run and real-invocation transcripts for a CLI/API/service slice. That is "everything a developer needs to review it and know the desired feature was built."