A CI/CD harness that runs the PDCA quality cycle over Gramps (core + addons),
driven by the Claude CLI. One command takes a tracker issue from a brief all the
way to a human-signed-off, verified fix — Plan → Do → Check → sign-off → Act —
pausing only where a human belongs.
The driver is deterministic (state machine + gates + the C6 accept-guard are plain code); a model is invoked only at the five leaves. See PCDA/quality-cycle.md for the model and docs/INTEGRATION.md for the gramps specifics.
On a clean machine, one command sets up everything that can be scripted:
git clone https://github.com/<you>/gramps-testbed-v2 && cd gramps-testbed-v2
make bootstrap # venv + permissions + sibling forks + worktrees + engine
# image + an offline smoke test of the whole control flowmake bootstrap is idempotent — re-run it any time (MINIMAL=1 skips everything
needing network/docker; APT=1 lets it sudo-install missing core tools; SSH=1
clones the forks over SSH). make doctor reports every prerequisite
(OK / MISSING / UNAUTH / WARN + the command that fixes it) without changing
anything. What stays manual is exactly the credentials:
| doctor line | manual fix |
|---|---|
| claude CLI | curl -fsSL https://claude.ai/install.sh | bash, then run claude once (login + folder trust) |
| gh CLI | install, then gh auth login (publish/merge need it) |
| codex CLI (reviewer) | optional cross-vendor reviewer: install codex, codex login |
| docker | install; sudo usermod -aG docker $USER + re-login |
| system Chrome | only for Mantis scraping — the Chrome .deb, not the snap |
Under the hood that is: Python 3.11+ (the driver itself is stdlib-only), Docker
(the gramps test gates run in a container), and the sibling fork checkouts next to
this repo — ../gramps, ../addons-source, ../addons
(./engine/scripts/bootstrap-forks.sh creates them; make worktrees the
per-version validation worktrees).
make bootstrap # ONCE per machine (see Prerequisites)
make flow ID=13636 # run the whole cycle for tracker issue 13636Run make flow in a real terminal — the Plan, sign-off, publish and Act steps
open Claude interactively. The first interactive session asks once to trust the
project; accept it (this is Claude Code's folder trust, stored globally and
separate from make setup's permissions — it persists after one accept).
What happens, step by step (the cycle has four beats; review/sign-off/publish are steps within Check):
- Plan (interactive) — Claude reads the tracker row for the issue id from
the configured Mantis export (
pdca.toml [tracker].export_csv; override withCSV=…) and, with you, writesresults/issue_<id>/brief.md. For the full bug thread, run./engine/scripts/scrape-mantis.sh <id>first (optional) — the planner readsresults/issue_<id>/mantis-notes.jsonif present. - Do (headless) — Claude implements the brief:
patch.diff, the test,build-notes.md. - Check — the deterministic gates run (
→ … still workingheartbeats while they do), then a headless advisory reviewer. - Sign-off (Check step, interactive) — Claude reviews the result with you; you
clear the §6 items and decide
accept/iterate-do/iterate-plan. The driver records §9 under the C6 guard. - Publish (Check step, interactive, on accept) — Claude drafts the contribution
artifacts (commit-msg + PR description) and the driver opens a draft PR — the
closing work of Check.
NO_PUBLISH=1skips it; offlinerehearsedry-runs it. - Act (interactive, only with
ACT=1) — Claude reviews the frozen cycle and suggests process improvements if any are warranted.
When a Plan/sign-off/publish session ends, exit the Claude session (Ctrl-D) to let the flow continue.
Closing Check — contribute the fix. Once a fix is accepted, make publish ID=<id>
does the closing work of Check: a publisher leaf drafts commit-msg.txt +
pr-description.md (doc-16-conformant, validated by the T4 gate), then the driver
branches from upstream/<base>, applies the patch, commits, pushes, and opens a
draft PR — and stops. You review CI and mark it ready/merge yourself. Preview the
exact git/gh commands first with DRY=1. (This is Check's contribution arm, not a new
beat; make flow does not push for you.)
| Command | What it does |
|---|---|
make setup |
One-time: write the permission config so the interactive leaves don't prompt. |
make flow ID=<id> [CSV="<path>"] [NO_PUBLISH=1] [ACT=1] [BY=<name>] |
Run the full cycle for one issue. On an accept it opens a draft PR (NO_PUBLISH=1 stops at COMPLETE). CSV seeds Plan; ACT=1 runs Act; BY overrides §9 attribution (defaults to the project author). |
make flow CSV="<path>" |
Batch: one Plan session may brief several issues; then every in-flight bundle builds unattended and you sign off the cheap-first queue. Resumable — re-run it to pick up where you left off (it skips/authors at Plan, then drives whatever's outstanding). |
make batch IDS="<id> …" [NOACT=1] [BY=<name>] |
Drive specific already-briefed bundles by id through the full cycle (no Plan): Do→Check→sign-off (cheap-first across the set)→Act once at the end — make flow seeded by ids. Run it in a terminal. NOACT=1 stops after sign-off. Resumable — re-run to pick up whatever is still in flight. |
make publish ID=<id> [DRY=1] [BY=<name>] |
Closing work of Check: contribute an accepted fix as a draft PR (drafts the commit/PR artifacts, runs the T4 gate, branches from upstream, applies, commits, pushes, opens the draft). DRY=1 previews. Ready/merge stay yours. |
make rehearse ID=<id> [CSV="<path>"] |
Dry-run the same control flow with stub leaves + stub gates — no Claude, no Docker, instant. Watch PLANNED → … → COMPLETE before a real run. |
make status |
List every bundle and its state. |
make cli ARGS="<subcommand>" |
Any other gramps-pdca subcommand (e.g. signoff 13636 --accept). |
make / make check |
Self-test (full / fast offline). |
make install |
Optional: a real gramps-pdca console script in .venv/. |
If something looks stuck, it isn't — a headless claude -p and a Docker gate print
nothing until they finish, so the flow shows a … still working (NmSSs elapsed)
heartbeat. Let it run.
Configured in pdca.toml [[gates.checks]], single-sourced for the local
driver and CI (gramps-pdca gates / gramps-pdca gates --working-tree):
- C4-verify (gating) — the per-fix correctness check. Applies the bundle's
patch.diffand runs only its test, asserting it is red without the fix, green with it (engine/scripts/ubuntu/run-verify.sh). - T3-unit (advisory) — the whole gramps unit suite on the unmodified tree; informational baseline, not a per-fix gate.
The planner picks the brief template with you: ordinary fixes and most new functionality use templates/brief.md.tpl; a change big enough to warrant a design proposal (a GEPS) uses templates/design-proposal.md.tpl. Not every feature is a GEPS — the design proposal is the exception, authored at Plan.
src/pdca_harness/ the deterministic driver (state machine, gates, leaves, flow)
engine/ the gramps verification engine (Docker runners, lib, C4-verify)
templates/ brief / design-proposal / SUMMARY templates
results/issue_<id>/ one bundle per issue — the state IS the files here
pdca.toml project config: leaves, gates, tracker, paths
docs/INTEGRATION.md gramps concretizations
PCDA/ the generic model (reference docs)
.claude/agents/ the five leaf agents (planner, builder, reviewer, signoff, act)
- Iteration is built in.
iterate-dorebuilds against the same brief;iterate-planre-opens Plan. The flow loops untilCOMPLETE(bounded). - Nothing is pushed for you. The builder may open a draft PR for CI but can
never mark it ready/merge — enforced by
.claude/hooks/builder_guard.py. - Harness changes feed back upstream. This repo is rendered from a copier
template; changes to the generic machinery are filed as
enhancementissues on thepdca-harnesstemplate repo for propagation (or pulled in withcopier update).
Licensed under Apache-2.0 (LICENSE, NOTICE). The PDCA
harness this project was generated from is Apache-2.0, which carries no copyleft into
this repo. Contributions are gated on the Developer Certificate of Origin —
sign off with git commit -s (see CONTRIBUTING.md).