develop is the trunk; main holds released code and moves by fast-forward at a release

CI runs on merges to develop (and on fork pull requests); the release workflow builds the draft
from a push to main. AGENTS.md, CONTRIBUTING.md and docs/releasing.md follow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-14 08:32:27 -04:00
1 parent 9469ac0853
commit fe7d536719
5 files changed
+46 -41

No files matched your search

+8 -7
View File
@@ -1,7 +1,8 @@
name: CI
# CI runs once per merge, on main (AGENTS.md, "Workflow for every change"): the core team's pull
# requests are checked locally on the crates they touch and merged; a red main is fixed forward.
# CI runs once per merge on `develop`, the trunk (AGENTS.md, "Workflow for every change"): the core
# team's pull requests are checked locally on the crates they touch and merged; a red trunk is
# fixed forward. `main` holds released code only (docs/releasing.md).
# Pull requests from forks (outside contributors) run the full gate, once the maintainer approves
# the run, so the review sees it.
on:
@@ -13,7 +14,7 @@ on:
- 'docs/features.md'
- 'docs/oracle.md'
push:
branches: [main]
branches: [develop]
# A merge that only touches prose starts no run. The generated pages are checked by the jobs
# (features.md by `cargo xtask ci`, oracle.md by the oracle job), so they still count.
# (Later patterns override earlier ones.)
@@ -24,7 +25,7 @@ on:
- 'docs/features.md'
- 'docs/oracle.md'
schedule:
# Nightly: the heavy tests on main.
# Nightly, on the default branch (develop): the heavy tests.
- cron: '23 3 * * *'
workflow_dispatch:
@@ -54,7 +55,7 @@ jobs:
targets: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
save-if: ${{ github.ref == 'refs/heads/develop' }}
- run: cargo xtask ci
heavy:
@@ -68,7 +69,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}
save-if: ${{ github.ref == 'refs/heads/develop' }}
- run: cargo xtask test --heavy
oracle:
@@ -85,7 +86,7 @@ jobs:
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
key: oracle
save-if: ${{ github.ref == 'refs/heads/main' }}
save-if: ${{ github.ref == 'refs/heads/develop' }}
- run: cargo xtask oracle
- name: docs/oracle.md is current
run: git diff --exit-code docs/oracle.md
+11 -10
View File
@@ -1,11 +1,12 @@
# Release builds (docs/releasing.md), after SolidCraft's and VectorCraft's release.yml
# (MIT OR Apache-2.0; see ATTRIBUTION.md).
#
# A push to `release` (fast-forwarded from main) builds every package and creates or updates the
# A push to `main` (fast-forwarded from a verified `develop` commit; main holds released code only)
# builds every package and creates or updates the
# DRAFT GitHub Release "FieldCraft v<version>", with SHA256SUMS.txt and generated notes. The
# version is [workspace.package] version in Cargo.toml; a suffix (0.2.0-rc.1) makes a
# pre-release. A person verifies the draft and publishes it; this workflow never publishes,
# and pushes to main or tags never start it.
# and pushes to develop or tags never start it.
#
# macOS universal (arm64 + x86_64) .dmg with FieldCraft.app, the CLI as a .zip packaging/macos
# Linux x86_64 (glibc 2.35+) .tar.gz and .AppImage packaging/linux
@@ -15,17 +16,17 @@
# otherwise (the release notes say which). Linux and Windows builds are not signed.
#
# Dry run: `gh workflow run release.yml --ref <branch>` builds everything and uploads the
# `release-assets` artifact without creating a release. `-f draft=true` on `release` retries
# `release-assets` artifact without creating a release. `-f draft=true` on `main` retries
# the draft.
name: Release
on:
push:
branches: [release]
branches: [main]
workflow_dispatch:
inputs:
draft:
description: "Create/update the draft release (release branch only; off: build artifacts only)"
description: "Create/update the draft release (main only; off: build artifacts only)"
type: boolean
default: false
@@ -60,8 +61,8 @@ jobs:
echo "::error::Cargo.toml must contain a version like 1.2.3 or 1.2.3-rc.1, not '$VERSION'"
exit 1
fi
if [ "$REQUEST_DRAFT" = true ] && [ "$GITHUB_REF" != refs/heads/release ]; then
echo "::error::Draft releases are built from the release branch only"
if [ "$REQUEST_DRAFT" = true ] && [ "$GITHUB_REF" != refs/heads/main ]; then
echo "::error::Draft releases are built from main only"
exit 1
fi
case "$VERSION" in *-*) pre=true ;; *) pre=false ;; esac
@@ -86,7 +87,7 @@ jobs:
with:
key: release-macos-universal
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
save-if: ${{ github.ref == 'refs/heads/release' }}
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Import the signing certificate (skipped without APPLE_CERTIFICATE)
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
@@ -129,7 +130,7 @@ jobs:
with:
key: release-linux-x86_64
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
save-if: ${{ github.ref == 'refs/heads/release' }}
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Build and package (tar.gz, AppImage)
run: packaging/linux/package.sh
- name: Check the packages
@@ -167,7 +168,7 @@ jobs:
with:
key: release-windows-x64
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
save-if: ${{ github.ref == 'refs/heads/release' }}
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Build and package (zip)
shell: pwsh
run: ./packaging/windows/package.ps1
+12 -10
View File
@@ -99,7 +99,7 @@ People trust FieldCraft with their models; a crash loses their work. **This outr
1. Work in your own worktree on a branch, with your own `CARGO_TARGET_DIR`. While you work:
`cargo check` and the tests of the crates you touch (`cargo test -p <crate>`). Commit after
each working step (a layer that compiles with its tests passing), not one big commit per PR.
Every commit lands on `main` as it is, so each one must build and pass its tests and say
Every commit lands on `develop` as it is, so each one must build and pass its tests and say
what it does: `git bisect` walks them one by one.
2. When the change is done: review it once with `/simplify`, then `cargo fmt --all` and
`cargo clippy -p <crate> --all-targets -- -D warnings` for each crate you touched, once.
@@ -108,11 +108,12 @@ People trust FieldCraft with their models; a crash loses their work. **This outr
oracle cases or solver numerics change (`cargo xtask oracle`).
3. Fold every fix into the commit it fixes, never a separate "fmt", "clippy", "review fixes" or
"regenerate features.md" commit: `git commit --fixup <sha>`, then
`git rebase -i --autosquash origin/main` (`GIT_SEQUENCE_EDITOR=true` keeps it
`git rebase -i --autosquash origin/develop` (`GIT_SEQUENCE_EDITOR=true` keeps it
non-interactive). A regenerated page goes into the commit that changed what it lists.
4. Rebase onto the latest `origin/main` (never merge `main` into a branch), rerun the tests of
the touched crates, push, open the PR and merge it with a rebase merge, which keeps `main`
linear and every commit bisectable. `main` is protected (CODEOWNERS review, linear history,
4. Branch from and rebase onto the latest `origin/develop`, the trunk (never merge `develop` into
a branch), rerun the tests of
the touched crates, push, open the PR and merge it with a rebase merge, which keeps `develop`
linear and every commit bisectable. `develop` and `main` are protected (CODEOWNERS review, linear history,
rebase merges only); the core team merges as the maintainer with
`gh pr merge <N> --rebase --admin --delete-branch`, run as its own command. Outside
contributors' pull requests wait for the maintainer's review (CONTRIBUTING.md). Never squash.
@@ -120,20 +121,21 @@ People trust FieldCraft with their models; a crash loses their work. **This outr
5. Never run the full gate locally: no `cargo xtask ci`, no `cargo clippy --workspace`, no
`cargo xtask wasm` or `oracle` unless step 2 calls for it. Never watch CI. GitHub CI runs
`cargo xtask ci` (fmt, assets, licences, layers, clippy -D warnings, tests, wasm), the
oracle, the heavy tests and the advisories once per merge on `main`; the coordinator
oracle, the heavy tests and the advisories once per merge on `develop`; the coordinator
watches it and sends a failure back to the change that caused it, which is fixed forward
at once.
- **Keep the main gate fast** (under ~3 minutes on CI). A test that takes more than a few seconds
- **Keep the trunk gate fast** (under ~3 minutes on CI). A test that takes more than a few seconds
(convergence studies, the whole corpus, cross-checks between solvers) is
`#[ignore = "heavy: cargo xtask test --heavy (CI heavy job)"]`, with a fast representative
test left in the main gate. The `heavy` CI job runs `cargo xtask test --heavy` (every ignored
test, benchmarks included, and the doc tests) on every merge to `main`, nightly and on
test left in the trunk gate. The `heavy` CI job runs `cargo xtask test --heavy` (every ignored
test, benchmarks included, and the doc tests) on every merge to `develop`, nightly and on
demand (`gh workflow run CI --ref <branch>`). Hostile-input tests run commands on sessions with small
work caps (`Session::limits`) so refusals are instant; never lower the production caps for a
test.
- **Publishing**: `origin` is github.com/jyviko/fieldcraft. Changes land through pull requests
as the workflow above describes; the coordinator reads every merged diff and sends findings
back as fix-forward work. Never force-push `main`. Never commit `plan/`, `.agent-files/`,
back as fix-forward work. Never force-push `develop` or `main`; `main` holds released code and moves only by
fast-forward at a release (docs/releasing.md). Never commit `plan/`, `.agent-files/`,
`worktrees/` or `target*/`; check `git ls-files` before pushing.
## Running
+4 -2
View File
@@ -18,7 +18,8 @@ Issues labelled **in progress** are being worked on by the core team; please don
## Making the change
1. Branch from `main`, keep a pull request to one topic.
1. Branch from `develop` (the trunk; `main` holds released code only) and open the pull request
against `develop`. Keep it to one topic.
2. Before pushing: `cargo fmt --all`, and `cargo clippy -p <crate> --all-targets -- -D warnings`
and `cargo test -p <crate>` for the crates you touched. CI runs the full gate on your pull
request once the maintainer approves the run.
@@ -30,6 +31,7 @@ Issues labelled **in progress** are being worked on by the core team; please don
By contributing you agree your work is licensed under MIT OR Apache-2.0.
`main` is protected: changes land only through pull requests that the maintainer has reviewed,
`develop` and `main` are protected: changes land on `develop` only through pull requests that the
maintainer has reviewed,
as rebase merges (keep each commit building and passing its tests). Be kind: see the
[code of conduct](CODE_OF_CONDUCT.md).
+11 -12
View File
@@ -1,9 +1,9 @@
# Releasing FieldCraft
Releases are deliberate. Development lands on `main`; pushing the chosen commit to the `release`
branch builds every package and creates a **draft** GitHub Release; a maintainer verifies the
draft and publishes it. Pushes to `main` and tags never start a release, and nothing bumps the
version automatically. The workflow is `.github/workflows/release.yml`; the packaging scripts
Releases are deliberate. Development lands on `develop`, the trunk; `main` holds released code
only. Fast-forwarding `main` to the chosen, verified `develop` commit builds every package and
creates a **draft** GitHub Release; a maintainer verifies the draft and publishes it. Pushes to
`develop` and tags never start a release, and nothing bumps the version automatically. The workflow is `.github/workflows/release.yml`; the packaging scripts
are in `packaging/` and run the same way on a developer machine.
## What a release contains
@@ -57,22 +57,21 @@ certificate is added.
notes); `-rc.N` for a release candidate. `1.0` is the owner's decision.
2. Set `[workspace.package] version` in the root `Cargo.toml` and run
`cargo update --workspace --offline`, which changes only the workspace crates' versions in
`Cargo.lock`. Land it on `main` through a pull request like any change, and let CI on `main`
pass. Note the commit to release.
`Cargo.lock`. Land it on `develop` through a pull request like any change, and let CI on
`develop` pass. Note the commit to release.
## Build the draft
```sh
git fetch origin
git switch release # the first time: git switch -c release origin/main
git merge --ff-only origin/main
git push origin release
git push origin <commit>:main # fast-forward main to the verified develop commit
```
Only fast-forward `release` and never force-push it. The run builds the three platforms; when
`main` holds released code only: it moves only by fast-forwarding to a verified `develop`
commit (a maintainer push; the branch is protected), never by merges, rewrites or force-pushes. The run builds the three platforms; when
all succeed, the last job checks that every file is there, writes `SHA256SUMS.txt` and the notes,
and creates the draft `FieldCraft v<version>` targeting the pushed commit. Pushing
`release` again with the same version rebuilds and replaces the draft's files (its notes are
`main` again with the same version rebuilds and replaces the draft's files (its notes are
kept). The job refuses a version whose tag exists or whose release is already published: bump
the version instead.
@@ -98,6 +97,6 @@ Publishing is a maintainer's call; an agent preparing a release stops at the dra
`gh workflow run release.yml -R jyviko/fieldcraft --ref <branch>` builds every package and
uploads them with the notes as the `release-assets` artifact of the run, without creating a
release. `-f draft=true` creates or updates the draft, and is accepted on `release` only.
release. `-f draft=true` creates or updates the draft, and is accepted on `main` only.
Locally, `packaging/macos/package.sh --arch aarch64` (or `x86_64`, `universal`) and
`packaging/linux/package.sh` write to `dist/release/`.