ci.specsesh.com
CI runs as your spec is built
Every change in Specsesh is a spec and a task list. Specsesh Code builds it one task at a time, and CI runs each time a piece closes: a quick selective pass for a minor task, the full suite for a major task line, and the full suite again at the end of the spec.
1. Theme major task line
2. Verify last line
Merge
Three points, one run each
A minor task closes
Task 1.2 is done and its group still has open tasks. CI runs your quick task command, and only the acceptance checks of the scenarios and requirements that task links to.
- Scope
selective- Runs
setup, thentask(ortest)- Branch
stack/<change>/t<n>
A major task line closes
The last open task of group 1 is done. CI runs your full suite and every acceptance check on that layer, so the next line starts from a green base.
- Scope
full- Runs
setup, thentest- Branch
stack/<change>/t<n>
The spec is finished
The change's last task is done. Code restacks it onto main as a release candidate, and CI runs the full suite there before you publish.
- Scope
full- Runs
setup, thenspec(ortest)- Branch
release/<change>
Only the biggest point runs: the last task of a group gets the full suite instead of a selective pass, and the last task of the spec gets the candidate run. After you merge, CI runs setup, then your merge key, as the Smoke run on main, plus read-only (GET) checks.
Which key runs when
| Moment | Your commands | Specsesh adds |
|---|---|---|
| Selective minor task closes | setup, then task (or test) | The acceptance checks of what that task links to |
| Full suite task group closes | setup, then test | Every acceptance check, OpenSpec validation |
| Full suite · candidate spec finished | setup, then spec (or test) | Every acceptance check, OpenSpec validation |
| Smoke merged | setup, then merge | Read-only (GET) checks |
Your pipeline
Commands are top-level keys named for when they run. test, the full suite, is the one required key; leave out task and minor tasks run test. The smallest file:
test: go test ./...
setup: npm ci
test:
- npm run lint
- npm test
task: npx vitest run {!@ci.selected_tests}
merge: npm run smoke
env:
CGO_ENABLED: "0"
setup: go build ./...
test:
- go vet ./...
- go test ./...
task: go test -run '{!@ci.test_pattern}' {!@ci.changed_go_packages}
setup: python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
test: .venv/bin/python -m pytest -q
task: .venv/bin/python -m pytest -q {!@ci.selected_tests}
spec:
- .venv/bin/python -m pytest -q
- name: e2e
run: .venv/bin/python -m pytest -q tests/e2e
timeout: 600
Every command runs in Specsesh's build image, which already has Node 22 (npm, pnpm, Yarn, Bun), Python 3.11 (pip, venv), Go 1.27 and make/gcc, so there's no image or toolchain to choose.
Templates Specsesh fills
Write {!@ci.name} anywhere in a command. Specsesh fills it in before the command runs. Lists are shell-quoted and joined with spaces, so file names with spaces are safe.
| Template | Filled with |
|---|---|
{!@ci.changed_files} | files the closing task (or group, or change) added or modified, against its base |
{!@ci.changed_dirs} | the directories of the changed files, once each |
{!@ci.changed_go_packages} | the Go packages holding changed .go files, as ./path |
{!@ci.selected_tests} | the test files the picker chose for the closing task |
{!@ci.test_pattern} | the picked test names as one regex, for go test -run or vitest -t |
{!@ci.scope} | selective or full |
{!@ci.change} | the change id |
{!@ci.task} | the task that closed; empty at merge |
{!@ci.base} | the commit the diff is taken against |
{!@ci.branch} | the branch being tested |
If a list template comes out empty (no test was picked, no Go file changed), that command is skipped and the run notes it: it is never run unfiltered by accident. If the test picker can't answer, a task run falls back to test, so a minor task is never left untested.
Defined once in @specsesh/ontology/pipeline (CI_CADENCE, KEYS_FOR, CHECKS_FOR, CI_TEMPLATES); Specsesh Code runs it and this page reads it. What each key runs is your project's .specsesh/ci.yaml.