Stagecraft ships a CI workflow template that validates and publishes pipeline state on pull requests. It does not run the pipeline itself in CI. Running an LLM pipeline on every PR is expensive, and the pipeline is designed to be human-driven. CI's job is to surface the audit trail that local runs produced.
This is the F4 BACKLOG item.
- What the workflow does
- Install
- Required GitHub permissions
- What the PR review experience looks like
- Why not run the pipeline in CI?
- Autonomous jobs and the Docker runner
- Other CI systems
- Pinning + drift
- See also
When a PR touches pipeline/ or .devteam/:
- Checks out the target project at the PR's head commit.
- Checks out Stagecraft at a pinned version (configurable env var).
- Skips cleanly if the PR includes no gates. If
pipeline/gates/is empty or absent, the workflow short-circuits with a::notice::and exits green. - Validates every gate file with Stagecraft's validator. Exit codes propagate: 0 PASS/WARN (job stays green), 1 malformed (fail), 2 FAIL gate (fail), 3 ESCALATE (fail — surfaces needs-resolution).
- Verifies the authenticated gate chain (C6) with
devteam verify-chain. CI recomputes predecessor hashes and verifies HMACs whenDEVTEAM_SIGNING_SECRETis configured. Exit 1 means a hash break, invalid MAC, or signed-only policy violation. Legacy unsigned gates remain warnings unlesspipeline.require_signed_gates: trueis set. Blocking by design — an authenticated audit trail is only meaningful when verification failure stops promotion. - Posts each gate as a GitHub check run on the PR head commit via
scripts/pr-publish.js. PASS→success, WARN→neutral, FAIL/ESCALATE→failure. Reviewers see "15/18 stages passing" in the PR's status bar with click-through to per-stage detail. - Reproducibility drift check (advisory, doesn't block merge): runs
devteam reproduceon each gate, surfacing any drift between the recordedsystem_prompt_hashand what would render today.
Not invoking the LLM in CI keeps cost predictable and avoids running the pipeline without human oversight.
In your target project:
devteam ci install # drops the workflow into .github/workflows/
devteam ci install --force # overwrite an existing file
devteam ci install --out custom/path/ # redirect output (rare)
devteam ci show # print the template to stdout (preview)After install:
- Open
.github/workflows/stagecraft-pr-checks.ymland edit the two Stagecraft env vars at the top:STAGECRAFT_REPO: where to fetch Stagecraft from (e.g.your-org/stagecraft)STAGECRAFT_REF: which Stagecraft version/tag/sha to pin
- Optional authenticated enforcement: add a repository Actions secret named
DEVTEAM_SIGNING_SECRET, use the same protected secret while running/stamping Stagecraft, and setpipeline.require_signed_gates: truein.devteam/config.yml. Pull requests from forks do not receive repository secrets and therefore fail strict verification by design. - If using
openai-compatas your host: add the API-key env var named byapi_key_envin your config (e.g.OPENAI_API_KEY,FIREWORKS_API_KEY,FUELIX_API_KEY, orOPENROUTER_API_KEY) as a repository Actions secret.openai-compathas no CLI and noheadlessCommand— it calls the configured OpenAI-compatible API directly via HTTP, so CI only needs the key in the environment. - Commit + push the workflow file.
- Open a PR that touches
pipeline/gates/and watch the checks fire.
The workflow declares:
permissions:
contents: read
checks: write
pull-requests: writeThese are scoped to the workflow run. No PAT is needed. The runner's default GITHUB_TOKEN is used for posting check runs.
Without this workflow, a PR with a pipeline/gates/stage-04.json PASS gate looks identical to one with a FAIL gate. Reviewers have to read the JSON. With it:
✓ stagecraft pr-checks / validate-and-publish
✓ stage-01 requirements PASS (12s)
✓ stage-02 design PASS (47s)
✓ stage-03 clarification PASS (skipped, auto-folded)
✓ stage-04 build PASS (4 workstreams)
✓ stage-04a pre-review PASS
✗ stage-04b security-review FAIL ← visible in the PR status bar
Blockers:
- High-severity finding in src/backend/auth.ts:42
⚠ stage-04c red-team WARN ← yellow in the status bar
Warnings:
- 2 noted-for-followup findings
...
Each line in the PR's "Checks" tab links back to the gate detail. A reviewer can see at a glance which stages need attention.
Three reasons:
- Cost. A
full-track run is 5–60 minutes of LLM time. Running it on every PR, including draft PRs and force-pushes, multiplies that by PR volume. - Human-in-the-loop. Stagecraft's pipeline is designed for a human reading the prompt and decisions at each stage. Running it unattended in CI means agents make decisions that nobody sees until after the fact.
- Shape mismatch. A PR has already been built. Running
devteam stage buildagainst a PR doesn't fit the model.
The validate-and-publish path (this workflow) is the appropriate CI integration. If your team eventually wants to run a nano track on dependabot PRs, or an audit-quick on PRs touching sensitive paths, those belong in separate adjacent workflow files, not as a replacement for this one.
When you intentionally want CI or a scheduler to run model work, keep it in a
separate job from devteam ci install's validation workflow. The Docker runner
is the supported packaging path for that use case: it runs the normal
orchestrator as a non-root user against a mounted workspace and writes normal
pipeline/ artifacts back to the checkout.
- name: Build Stagecraft runner
run: docker build -f hosts/docker/Dockerfile -t stagecraft-runner:ci .
- name: Run low-risk pipeline
run: |
docker run --rm \
-v "$PWD:/workspace" \
--env-file .devteam/docker.env \
stagecraft-runner:ci run --cwd /workspace --track nano --budget-usd 10Generate .devteam/docker.env from CI secrets or use your platform's native
secret injection. Do not bake provider keys into the image. The runner preserves
normal devteam run exit codes, including the consequence ceiling and advisory
strictness, so CI scripts can handle success, halt, and advisory-blocked states
the same way they do outside Docker.
devteam ci install --ci <type> currently supports only github-actions. GitLab CI / Buildkite / CircleCI templates are BACKLOG candidates. The Stagecraft side (validator + pr-publish.js) is CI-agnostic; the templates just need to be written.
The template pins STAGECRAFT_REF: <version>. Update it on each Stagecraft release you want to adopt. Pinning matters because:
- Validator behavior could change (rare but possible).
scripts/pr-publish.js's output shape could evolve.- A floating ref (
@main) means your CI silently changes when Stagecraft does.
Update the workflow file in lockstep with Stagecraft upgrades.
Before running the pipeline autonomously in CI, record the track decision with devteam assess:
- name: Assess track
run: devteam assess --description "${{ github.event.pull_request.title }}" --confirm
# writes pipeline/track.json with source:"human" (--confirm) so devteam run
# proceeds without the unconfirmed-track guard triggering.By default, devteam run warns on an inferred track but never halts. To enforce the guard in CI, set autonomy.require_confirmed_track: true in .devteam/config.yml:
autonomy:
require_confirmed_track: trueWith the flag on, an inferred pipeline/track.json at medium or low confidence halts with unconfirmed-track instead of proceeding. High confidence proceeds. The flag is not tied to CI=true — it is an explicit opt-in so unrelated tooling running under a CI environment does not silently change track behavior.
CI pipeline pattern with track recording + strict guard:
- name: Assess track (record with human-confirm)
run: devteam assess --description "$FEATURE_DESC" --confirm
- name: Run pipeline
run: devteam run --json > run-summary.json
# With require_confirmed_track:true, --confirm above means source:"human" → no halt.
# Without --confirm, medium/low inferred would halt here.devteam run emits a loud advisory line on stderr and adds advisory_blockers_count
to the --json summary when unresolved follow-up items remain after
pipeline-complete. The exit code is 0 by default so existing
if devteam run; then merge pipelines are unaffected.
Teams that want CI to block on advisory findings can opt in with
--fail-on-advisory:
Lenient (default) — report advisory blockers, don't block merge:
- name: Run pipeline
run: devteam run --json > run-summary.json
# exits 0 even if advisory blockers remain; loud line appears in logsStrict — block merge on QA_BLOCKER or A11Y_FIX findings (exit 3):
- name: Run pipeline
run: devteam run --fail-on-advisory
# exits 3 if QA_BLOCKER or A11Y_FIX items remain; exits 0 otherwiseAll-class strict — also block on PEER_REVIEW_RISK:
- name: Run pipeline
run: devteam run --fail-on-advisory=all
# exits 3 if any blocker-class item (QA_BLOCKER, A11Y_FIX, PEER_REVIEW_RISK) remainsExit code 3 is distinct from exit 1 (a pipeline halt) so scripts can distinguish "pipeline didn't finish" from "pipeline finished but has outstanding advisories".
templates/ci/github-actions/stagecraft-pr-checks.yml— the workflow itself.scripts/pr-publish.js— what the workflow invokes for check-run posting.docs/BACKLOG.mdF4 — the BACKLOG entry this implements.docs/reproducibility.md— the drift check that runs as an advisory step.