CI

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.

Spec · add-dark-modeproposal.md · specs/ui/theme/spec.md · tasks.md
planned into a task list ↓

1. Theme major task line

1.1↓Selectivestack/add-dark-mode/t1
1.2↓Selectivestack/add-dark-mode/t2
1.3↓Full suitestack/add-dark-mode/t3 · closes group 1

2. Verify last line

2.1↓Full suite · candidaterelease/add-dark-mode · end of spec

Merge

main↓Smokeafter the merge

Three points, one run each

Selective

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, then task (or test)
Branch
stack/<change>/t<n>
Full suite

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, then test
Branch
stack/<change>/t<n>
Full suite · candidate

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, then spec (or test)
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

MomentYour commandsSpecsesh adds
Selective minor task closessetup, then task (or test)The acceptance checks of what that task links to
Full suite task group closessetup, then testEvery acceptance check, OpenSpec validation
Full suite · candidate spec finishedsetup, then spec (or test)Every acceptance check, OpenSpec validation
Smoke mergedsetup, then mergeRead-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 ./...
Node
setup: npm ci
test:
  - npm run lint
  - npm test
task: npx vitest run {!@ci.selected_tests}
merge: npm run smoke
Go
env:
  CGO_ENABLED: "0"
setup: go build ./...
test:
  - go vet ./...
  - go test ./...
task: go test -run '{!@ci.test_pattern}' {!@ci.changed_go_packages}
Python
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.

TemplateFilled 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.