Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .ci/predev.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# What predev must pass to move into dev: busbar-release's `dev` rung (crates/busbar-release-cli/
# ladder.json: gates inherit, oracle, conformance; verdict absolute), the 1.6.0 done-oracle, and the
# Q128 architecture audit (`cargo xtask q128 audit`). ModernCI names a branch file for the branch
# being moved, so the move predev -> dev reads this file. Shadow while busbar-release still decides.
# What predev must pass to move into dev: the `dev` rung (gates, oracle, conformance), the 1.6.0
# done-oracle, and the Q128 architecture audit (`cargo xtask q128 audit`). ModernCI names a branch
# file for the branch being moved, so the move predev -> dev reads this file, and ModernCI alone
# judges it: promote.yml, the busbar-release hop that judged it before, is deleted (OWNER
# 2026-10-12, "ModernCI only"; docs/design/BUSBAR-1.6.0.md Part 6, "BUSBAR-RELEASE RETIRED FROM
# BUSBAR"). Its green landing is DEV-GREEN.
to: dev
on_green: merge
# The owner's approval on the pull request holds the merge too; q128-audit gates the move on the
Expand All @@ -24,6 +26,9 @@ checks:
cargo xtask gate --all --format=tsv
baseline: target
rules: ["qa/*.toml"]
# The ceiling ratchet reads the merge-base (XTASK_CEILING_BASE), as in .ci/default.yaml: only a
# backend with the repo's history may run this check.
requires: [git-history]
oracle:
# busbar's own check tool, the pinned busbar-oracle (the backend's busbar-oracle tool sets
# BUSBAR_ORACLE_RUST_BIN): the judge is the one dev's oracle-rust.pin names, run only when its
Expand Down
12 changes: 4 additions & 8 deletions .github/required-status-checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,18 @@ Branch protection lives in repository rulesets. No script writes it.
| Branch | Ruleset | Required |
| --- | --- | --- |
| `predev` | 24945926 | ModernCI's `ci` check. ModernCI judges the PR (`.ci/default.yaml`) and lands it. |
| `dev` | 24340358 | `preflight` and `hop`, both from `.github/workflows/promote.yml` (the predev to dev promotion). |
| `dev` | 24340358 | ModernCI's `ci` check on the predev to dev move (`.ci/predev.yaml`). The ruleset still names `preflight` and `hop`, the jobs of the deleted `promote.yml`: the owner replaces them with `ci`. |
| `qa`, `main` | 24334334 | Owner-only; no status check stands in for the owner. |

Use the exact **check-run name** (the job's `name:`), not the workflow name. In `promote.yml`, `hop`
is the job id with no `name:`, so its context reads `hop`.

* `preflight`: formatting, `Cargo.lock` current, the generated C header current, on a free runner.
* `hop`: the pinned busbar-release engine plans the hop and admits the candidate (every registered
`cargo xtask gate`, plus the oracle and conformance where the rung asks for them).
Use the exact **check name** the judge posts. ModernCI posts one check, `ci`, per pull request head.

## Reusable workflows (plugin repos call these)

`plugin-ci.yml`, `plugin-consumer-verify.yml`, `plugin-repin.yml` and `plugin-release.yml` are
`workflow_call` only, called by the plugin repos' rendered callers (busbar-release `template/`,
`busbar-release plugin sync`). Their status bubbles up into the **caller's** job, so require the
caller's job in the plugin repo, never these directly.
caller's job in the plugin repo, never these directly. They stay until each plugin repo is
registered with ModernCI and judged by its own branch files.

## Branch hygiene

Expand Down
474 changes: 0 additions & 474 deletions .github/workflows/promote.yml

This file was deleted.

2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This page is the short version. Where the two differ, Part 6 wins.
both sides moved (theirs + ours − base; anything else stays a conflict):
`git config merge.ledger-count.driver "cargo xtask ledger-merge %O %A %B"`.

Builds, tests, clippy, the gates and the oracle run in CI (ModernCI for `predev` PRs; `.github/workflows/promote.yml` for the promotion).
Builds, tests, clippy, the gates and the oracle run in CI: ModernCI, for `predev` PRs and for every promotion (`.ci/*.yaml`).
3. Push the lane and open a PR into `predev`. ModernCI (required check `ci`) judges and lands it.
Then stop; CI is the proof and a red comes back as a task.
4. The PR body says what changed, why, and the ruling or issue it implements. No AI attribution.
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
</p>

<p align="center">
<a href="https://github.com/GetBusbar/busbar/actions/workflows/promote.yml"><img src="https://github.com/GetBusbar/busbar/actions/workflows/promote.yml/badge.svg" alt="promote"></a>
<a href="https://codecov.io/gh/GetBusbar/busbar"><img src="https://codecov.io/gh/GetBusbar/busbar/branch/main/graph/badge.svg" alt="Coverage"></a>
<a href="https://securityscorecards.dev/viewer/?uri=github.com/GetBusbar/busbar"><img src="https://api.securityscorecards.dev/projects/github.com/GetBusbar/busbar/badge" alt="OpenSSF Scorecard"></a>
<a href="https://www.bestpractices.dev/projects/14739"><img src="https://www.bestpractices.dev/projects/14739/badge" alt="OpenSSF Best Practices"></a>
Expand Down
56 changes: 21 additions & 35 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
# Releasing Busbar

The branch ladder is **predev → dev → qa → main**. Pull requests into `predev` are judged and landed by
ModernCI (required check `ci`, configured by [`.ci/default.yaml`](.ci/default.yaml)). The promotion
from `predev` to `dev` runs [`.github/workflows/promote.yml`](.github/workflows/promote.yml). The release engine lives in the private `GetBusbar/busbar-release` repository
(pinned outside this repo by the org variables `ENGINE_REF` and `ENGINE_SHA256`, set with
`busbar-release engine pin <sha>`; `workflow-rules` R16 holds promote.yml to a 40-hex check and a
sha256 verify of the engine at run time); busbar-release's `crates/busbar-release-train/ladder.json`
says what entering each rung needs.

| Rung entered (the PR's base) | What the hop runs | Consent |
|---|---|---|
| **`predev`** (from a `lane-*` branch) | every registered gate (`cargo xtask gate --list`, each by name) | none |
| **`dev`** | the gates, the oracle against the 1.5.5 golden, conformance | none |
| **`qa`** | the same, plus build + test, then the release binaries and the container | the owner (the org ruleset keeps `qa` locked) |
| **`main`** | the same, then publish | the owner (the org ruleset keeps `main` locked) |

A hop has two jobs, both in `promote.yml`:

* **`preflight`**, on a free runner: `cargo fmt --all -- --check`, `cargo metadata --locked`, and the
generated C header is current (`cargo xtask gate abi-header --write` leaves no diff).
* **`hop`**, on a Latchkey runner sized by the rung: the pinned engine plans the hop and admits the
candidate sha, and the verdict is uploaded as an artifact. When
the rung needs no consent and the repository variable `PROMOTE_BOARD` is `true`, the engine
fast-forwards the base branch to the exact sha it tested. Both promotions are **fast-forward
pushes**: linear history is what makes "main's HEAD *is* the qa commit" true.

> The entry builds a plan names (`release-binaries`, `container`, `publish`) are not yet ported into
> the engine: `promote.yml` fails a hop that plans one with that message rather than skipping it.
> Nothing is built or published until they land in busbar-release.
The branch ladder is **predev → dev → qa → main**, and ModernCI judges and moves every rung from
this repository's branch files (OWNER 2026-10-12, "ModernCI only"; the ruling is
[`docs/design/BUSBAR-1.6.0.md`](docs/design/BUSBAR-1.6.0.md) Part 6, "BUSBAR-RELEASE RETIRED FROM
BUSBAR"). A branch file is named for the branch being moved and says what the move must pass.

| Move | Branch file | What it runs | Consent |
|---|---|---|---|
| a `lane-*` branch into **`predev`** | [`.ci/default.yaml`](.ci/default.yaml) | preflight, every registered gate, clippy, compile, test, doctest, deletion matrix, `cargo deny`; no worse than predev (required check `ci`) | none |
| **`predev`** into **`dev`** | [`.ci/predev.yaml`](.ci/predev.yaml) | the gates, the oracle against the 1.5.5 golden, conformance, `scripts/verify-1.6.0-done.sh`, the Q128 audit | the owner's approval |
| **`dev`** into **`qa`** | `.ci/dev.yaml` | the same, plus build and test, the fuzz smoke and the signing gate, the soak, the release binaries and the container | the owner (the org ruleset keeps `qa` locked) |
| **`qa`** into **`main`** | `.ci/qa.yaml` | the same; on landing, the release is signed and published from the build that was judged, never rebuilt | the owner (the org ruleset keeps `main` locked) |

`.ci/dev.yaml` and `.ci/qa.yaml` are not written yet: until they land, nothing is built or published
from `qa` or `main`. The busbar-release hop (`promote.yml`, the engine pin) is deleted. busbar-release
remains the home of the oracle (`busbar-oracle`, pinned by `testing/shadow-oracle/oracle-rust.pin`),
the soak and the plugin fleet generator.

**The tag is an OUTPUT of a green release, never an input.** Nobody tags by hand; no workflow is
triggered by a `v*` tag (`cargo xtask gate workflow-rules`, rule R1). Docker Hub tag immutability
Expand All @@ -49,19 +37,17 @@ python3 .github/scripts/roll_changelog.py 1.6.0 CHANGELOG.md
```

That bumps `crates/busbar/Cargo.toml` + `Cargo.lock`, regenerates the committed OpenAPI schema, and
rolls `[Unreleased]` to `[1.6.0]` with today's date. It does **not** tag. Open the result as a pull
request against `dev` and let the hop run.
rolls `[Unreleased]` to `[1.6.0]` with today's date. It does **not** tag. Land it on `predev` like
any other change; it reaches `dev` with the next promotion.

## Moving a version up the ladder

Open a pull request whose base is the next rung (`dev` into `qa`, `qa` into `main`; a lane branch into `predev`). `promote.yml`
runs the hop on the PR; the run's check names are `preflight` and `hop`. A red hop is fixed
forward on the lower branch and the pull request re-run: every push re-runs the hop on the new sha.
ModernCI opens and judges each promotion (`modernci call moves` shows how each stands). A red move
is fixed forward on the lower branch: every push re-judges it on the new head.

## Branch protection this assumes

`qa` and `main` require the two contexts `preflight` and `hop` (plus the org ruleset's owner
consent).
`predev` requires ModernCI's check `ci`; `qa` and `main` take the owner's consent (the org ruleset).

Branch protection lives in repository rulesets, not in a script (see `.github/required-status-checks.md`).
Keep `enforce_admins: true`, `required_linear_history: true`, `allow_force_pushes: false` and
Expand Down
4 changes: 2 additions & 2 deletions docs/ci/actions-pins.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ PR that preserves the trailing tag comment.

## Every action currently in the tree

Every `uses:` in `.github/workflows/` (`promote.yml` and the reusable `plugin-*.yml` workflows) is one
of the nine distinct `(action, ref)` pairs above, pinned to a commit SHA with its tag kept as a
Every `uses:` in `.github/workflows/` (the reusable `plugin-*.yml` workflows; `promote.yml` is deleted)
is one of the distinct `(action, ref)` pairs above, pinned to a commit SHA with its tag kept as a
trailing comment. This table is the independent re-verification of that state. There is no
`.github/actions` composite action beyond `cargo-home`, and `R14` is proven able to catch a floating
tag inside one by `r14_reads_uses_out_of_a_composite_action_too_not_only_workflows` in
Expand Down
Loading