Contributing
Several projects build on cad-agent at the same time (the J501 enclosure, Gyropup, the AGX Thor CAN boards, and others), often with AI agents working in parallel. A change to cad-agent can break all of them, so every change goes through a branch, a pull request, CI and a review.
Branches
| Branch | What it is | Who writes to it |
|---|---|---|
main |
Released toolkit, tagged vX.Y.Z. |
Only by merging a release PR from dev. |
dev |
Integration, and the repository's default branch. Everything here passed CI and review. | Only by merging a reviewed PR. |
feat/<topic>, fix/<topic>, ci/<topic>, docs/<topic>, research/<topic>, test/<topic>, refactor/<topic>, release/vX.Y.Z, vendor-update/<name>-<sha>, dependabot/… |
One piece of work, branched from dev. |
Its author, in its own worktree. |
main and dev are protected: no direct pushes (admins included), no force pushes, the required checks below must
pass and every review conversation must be resolved. Agents may merge into both, through the gates.
Gates
Required checks, all automated. Nobody, agent or person, merges around them.
Drawing diagram…
Scroll sideways to see the full diagram →
Diagram source
flowchart TD
F["feat/ fix/ ci/ docs/ …<br/>from dev"] -->|"CI · branch-flow · review"| D["dev"]
D -->|"CI · branch-flow · review<br/>· release"| M["main"]
M -->|"release.yml"| R["tag vX.Y.Z · GitHub release<br/>site deploy"]
classDef b fill:#fffdf5,stroke:#173d3c,color:#173d3c
classDef r fill:#ffe1cc,stroke:#bd3a12,color:#173d3c
class F,D,M b
class R r
| Required check | dev |
main |
What it asks |
|---|---|---|---|
core (3.10), core (3.12), full |
✓ | ✓ | The test suite with required dependencies only, and with every extra |
browser (chromium), browser (webkit) |
✓ | ✓ | Web kit and website suites in two browser engines |
cad-gates/<base>/branch-flow |
✓ | ✓ | dev takes feat/ fix/ ci/ docs/ research/ test/ refactor/ release/ vendor-update/ dependabot/ branches (forks: the outside-contribution checks); main takes dev only |
cad-gates/<base>/review |
✓ | ✓ | The latest verdict from a reviewer with write access is pass, on the PR's head commit |
cad-gates/main/release |
✓ | pyproject.toml version above main's, and a dated ## vX.Y.Z (YYYY-MM-DD) entry in CHANGELOG.md |
|
| Conversations resolved | ✓ | ✓ | Every review thread answered and resolved |
- The verdict. It is the last line of the reviewer's summary review:
<!-- cad-review verdict=pass sha=<40-character head sha> -->(orverdict=block). It is written by thecad-pr-reviewersubagent, or by a reviewer following thecad-pr-reviewskill, who then re-runs the gates:gh workflow run gates.yml -R Valpatel/cad-agent -f pr=<n>. - Who can review. Only reviewers with write access count, and dismissed reviews never count. All our agents work through one account, so GitHub cannot tell author from reviewer: an author never writes a verdict on its own PR.
- How the gates run.
gates.ymlruns onpull_request_target: the default branch's (dev's) copy of the workflow and oftools/ci_gates.pydecides, so a PR cannot pass the gates by editing them, and PRs intomain(which come fromdev) are judged by the same copy. The PR is only read as data, never checked out or run. Results are commit statuses named after the base, so a PR intodevcannot supplymain's checks. A fork's own workflows run with a read-only token, so they cannot post statuses; anyone with write access can, which is why write access is the trust boundary. Fork workflows wait for approval; the intake skill (#120) reads a fork's workflow changes before approving them. - When they run. On every push to the PR, when the reviewer re-runs them after a verdict, and every 15 minutes for all open PRs (a review cannot trigger them). A run for one PR posts
pendingfirst, so a crash leaves the PR blocked rather than green. The sweep posts only statuses that change: GitHub keeps at most 1000 statuses per commit and name, and refuses new ones after that. - Just before merging, run
python3 tools/ci_gates.py all --repo Valpatel/cad-agent --pr <n>from an up-to-datedevcheckout. It reads the reviews live and must exit 0, so a block posted since the last run stops the merge. - GitHub settings the gates need (owner): the
cad-gates/…checks required ondevandmainwith GitHub Actions as their source; an Actions event policy for.github/workflows/gates.ymlthat allows all three of its events,pull_request_target,scheduleandworkflow_dispatch(GitHub disablespull_request_targetby default in public repos from 2026-12-02, and a policy lists allowed events, so one left out stops the sweep or the re-run); and the deploy token as aproductionenvironment secret. - Pins. Workflow actions are pinned by commit SHA, and Dependabot proposes updates weekly.
Workflow
Agents: install the workflow kit (cad-agent agents install, docs/agent-workflow.md) and
follow cad-work-tracking: an issue per change, an early draft PR with Closes #<issue>, and pushes as you go.
- Issue. Find or open the GitHub issue the change belongs to, with a checklist of done / next / how to resume.
- Worktree from
dev. Never edit a checkout another session may be using:git fetch origin git worktree add --no-track ../cad-agent-<topic> -b feat/<topic> origin/dev cd ../cad-agent-<topic> && git push -u origin feat/<topic> # the branch tracks itself, not dev - Commit and push small, focused changes with tests (
tests/) as you go, not in one batch at the end. Keep the package generic: anything specific to one product stays in that product's repository. A new docs or research page,cad-agentcommand, skill, agent orlessons/folder also gets its line indocs/areas.yaml, the topic tree (docs/vision.md):tests/test_areas.pyfails until it does. When two open PRs cross, the one that merges second adds the line, after mergingdevand re-running CI (devdoes not require branches to be up to date). - Open a PR into
devearly, as a draft if it is unfinished (gh pr create --draft --base dev), withCloses #<issue>; fill in the template, including what projects using cad-agent must change. - CI (
.github/workflows/ci.yml) runscoreon Python 3.10 and 3.12 (required dependencies only; optional features must skip cleanly) andfullon 3.12 (CAD, FEA, robot and site extras, imported loudly before the tests; thecadandsimextras need 3.11 or newer);webkit.ymlruns the browser suites andgates.ymlthe gates. CI'ssupply-chainjob runs the offline supply-chain report: a PR that adds a dependency, vendored file or catalogue entry pastes its rows (python3 tools/resource_audit.py rows --base origin/dev --online) under "Supply chain". - Review. Every PR gets a review from a person or from a separate review agent that did not write the change,
posted as PR comments (the
cad-pr-reviewersubagent, or a separate Codex session with that skill), ending with the verdict marker that thereviewgate reads. The author answers or fixes each comment and resolves it. - Merge into
dev(squash or merge commit). Then, before removing the worktree, repoint anything installed from it:pip show -f cad-agent(or thedirect_url.jsonit lists) in each project venv tells you wherecadagentcomes from. Reinstall those from thedevcheckout below, thengit worktree remove ../cad-agent-<topic>.
Using cad-agent from a project
- Released: a checkout of
main(or a tag), installed editable:pip install -e "<checkout>[cad,fea]". - Latest reviewed: a dedicated worktree that only tracks
origin/dev(git worktree add ../cad-agent-dev origin/dev, updated withgit -C ../cad-agent-dev pull --ff-only). Never edit in it. - A feature under test: that feature's worktree, while its PR is open. Say so in the PR.
A project that needs a new toolkit feature opens a cad-agent PR for it; it does not patch its local copy.
Releases
Shipping to main is part of the development loop, not an occasional event. When dev has merged work that people
or projects would notice (a command, a check, a fix, a site change), cut a release; while work is landing, at least
once a day. main should never be more than a day behind dev. Agents cut and merge releases under the same gates as
any PR; nobody has to ask for one. Use a patch release (--patch) for fixes, docs and the site, and a minor release
(--minor) when projects must change something (the CHANGELOG's "Projects must change") or when it adds a feature,
such as a new command or check, even one that asks nothing of projects.
On a
release/vX.Y.Zbranch fromdev:- bring in
main's release merges (git merge origin/main), so the release PR is up to date withmain; - run
python3 tools/cut_release.py --patch(or--minor). It bumpsversioninpyproject.toml, folds the fragments inchangelog.d/into the CHANGELOG's## Unreleasedentry and deletes them, dates the entry as## vX.Y.Z (YYYY-MM-DD), opens a new empty## Unreleased, and checks the result against CI's release gate; - check that the entry covers every PR merged since the last release, with what projects must change.
PR it into
devand merge.- bring in
Open a PR from
devintomaintitledRelease vX.Y.Z. It needs CI,cad-gates/main/release, and a review whose pass verdict is on the PR's head commit. Merge it with a merge commit (not squash or rebase), somainanddevkeep a common history.release.ymlthen:- tags
vX.Y.Zon the merge commit; - publishes the GitHub release from the CHANGELOG entry;
- deploys cad-agent.valpatel.com from that commit, if the owner has set the
productionsecret below. Until then the site job warns "site not deployed" and exits 0: that is a skipped deploy, not a working one. Deploy by hand frommainand checkhttps://cad-agent.valpatel.com/version.json.
The deploy uses the
productionenvironment, which onlymainmay deploy to. It needs that environment'sCLOUDFLARE_API_TOKENsecret, set by the owner withgh secret set CLOUDFLARE_API_TOKEN --env production, and itsCLOUDFLARE_ACCOUNT_IDvariable.- tags
Move the projects' pins (
requirements*.txt) to the tag, each in its own PR.