Once a slice is green locally, make its tests run automatically in CI. This is the enterprise step that turns "passes on my machine" into "the team's main branch is protected by these tests." Keep it a deliberate, separate phase: CI config is infrastructure and deserves its own review.
For the anatomy of a workflow, caching, Playwright-in-CI specifics, and branch-protection guidance, read references/github-actions-guide.md. Ready-to-customise templates are in assets/workflows/.
Confirm exact, current
actions/*versions and runner images withfind-docs/ctx7— action major versions move.
Preconditions
- The slice's full suite (unit + e2e) passes locally first. Do not promote red or unrun tests to CI.
- A git remote on GitHub exists (
git remote -v). If not, tell the user CI will only take effect once the repo is pushed to GitHub; still write the workflow so it's ready.
Procedure
- Detect the stack, the project directory, and the test commands. Reuse the detection from
red-green-refactor'sreferences/test-strategy.md: the unit runner + command, and whether the outer loop is a Playwright e2e suite (web) or an integration test (CLI / HTTP API / service). Read the plan's Project directory field — if the app lives in a subfolder, the workflow must run there (see step 3). Use the project's own scripts (package.json,Makefile) as the source of truth for how tests are invoked.
-
Choose a template from
assets/workflows/:node-ci.yml— Node/TS unit tests (Vitest/Jest) + build.python-ci.yml— Python unit tests (pytest).playwright-e2e.yml— web end-to-end job: installs browsers with--with-deps, runs Playwright, uploadsplaywright-report/andtest-results/(the screenshots and videos) as artifacts.- Combine the unit template with the Playwright template, or merge into one workflow with two jobs. A web slice wants both a unit job and an e2e job. A non-web slice (CLI / API / service) skips the Playwright template entirely — its integration test is just another test run (often the same
npm test/pytest -qas the unit job, no browser steps); see the guide's "Non-web slices in CI" section.
-
Customise it to the real project: correct Node/Python version, the actual install + test commands, the e2e start command /
webServer(web only), and the trigger (push to any branch +pull_requesttargetingmain). Remove anything that doesn't apply. Don't leave template placeholders behind.If the app is in a subdirectory (plan's Project directory ≠
.): setdefaults.run.working-directoryto that path sorun:steps execute there, and — becauseuses:actions resolve from the repo root — prefix the subfolder oncache-dependency-pathand on anyupload-artifactpath:. The templates carry inline comments showing both; the guide's "Projects in a subdirectory" section explains why. Keep the test command identical to what ran locally from that directory. -
Write the file to
.github/workflows/with a clear name (e.g.ci.yml, orunit.yml+e2e.yml). Keep unit and e2e as separate jobs (or files) so a reviewer sees both signals distinctly. -
Validate. Check the YAML parses and the syntax is sound (a YAML lint, or
gh workflow view/actionlintif available). Sanity-check that the commands match how the tests actually run locally. Ifactis installed and the user wants a local dry-run, offer it — otherwise validation is static. -
Record & commit. Note the workflow file in the slice plan's Definition-of-Done. Commit with
ci: add GitHub Actions workflow running unit + e2e tests [slice NN]. Confirm before pushing — pushing is outward-facing;safe-prwill handle the push as part of opening the PR, so you can leave the commit local and hand off. -
Branch protection (advise, don't impose). Tell the user they can make these checks required on
mainvia the repo's branch-protection rules so PRs can't merge red. Offer thegh apicommand from the guide if they want it set up, but treat changing repo settings as something to confirm explicitly.
What "good CI for a slice" looks like
- Runs on push (fast feedback) and on pull_request → main (the gate).
- A unit job and an acceptance job, each reporting its own status check (a non-web slice may run both in one job if you don't need separate checks).
- For a web slice, the Playwright job installs browsers with
--with-deps, runs headless, and uploads the HTML report + screenshots + videos as artifacts even on failure (if: always()/if: ${{ !cancelled() }}) so reviewers andsafe-prcan reference them. A non-web slice runs its integration test with no browser steps, uploading any captured report/log if useful. - Correct working-directory + repo-root-relative
cache-dependency-path/artifact paths when the app is in a subdirectory. - Dependency caching so the pipeline is fast.
- No secrets committed; environment via repo/Actions secrets.
Hand-off
Once the workflow is committed and valid, recommend safe-pr to open the pull request — it will push the branch (triggering this workflow) and assemble the evidence-rich PR. If running under tdd-harness, return to the orchestrator.