diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c8e321f..4409cb3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0445936..2964d8e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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", 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 ` 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 diff --git a/AGENTS.md b/AGENTS.md index 08f7792..ea9963b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 `). 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 --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 `, 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 --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 `). 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8398041..06fc4c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 --all-targets -- -D warnings` and `cargo test -p ` 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). diff --git a/docs/releasing.md b/docs/releasing.md index 81dfae7..0523f31 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -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 :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` 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 ` 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/`.