Tiered CI/CD¶
Goal¶
Keep routine pull-request feedback cheap while preserving deliberate multi-platform and release acceptance at the points where that evidence is needed.
Prerequisites¶
- A pull request in the Colossus repository.
- Repository write permission to request
ci:full, or administrator permission to bootstrap the ruleset. - The local toolchain described in Source setup and test tiers.
Colossus separates fast pull-request feedback, deliberate pre-merge acceptance, and complete release validation. Expensive hosted runners are allocated only after a repository writer requests them for a reviewed commit or an annotated release tag is pushed.
flowchart LR
PR["Pull request update"] --> C["Fail-closed path classifier"]
C -->|"documentation only"| D["Documentation build"]
C -->|"code, CI, dependency, or unknown"| L["Linux workspace validation"]
C -->|"API or SDK"| SDK["SDK generation + language packages"]
C -->|"desktop renderer or bridge"| UI["Desktop renderer validation"]
SDK --> L
UI --> L
C -->|"dependency files"| S["Supply-chain policy"]
D --> PG["Colossus PR gate"]
L --> PG
S --> PG
PG --> R["Resolve human and automated review"]
R --> F["Writer applies ci:full"]
F --> E["Draft, actor, and current-head eligibility"]
E --> A["macOS ARM + Windows x64 + live security"]
A --> MG["Colossus pre-merge gate"]
MG --> M["Merge to main"]
M --> T["Annotated stable or approved prerelease tag"]
T --> V["Release readiness + six native targets"]
V -->|"stable"| SDKR["Immutable SDK candidate"]
V -->|"preview"| DPR["Unsigned Desktop previews"]
SDKR --> RG["Colossus release gate"]
DPR --> RG
RG --> DR["Draft GitHub Release for human approval"]
DR -->|"publish stable"| RP["Protected npm, PyPI, and Go publication"]
Tiers and cost ceilings¶
| Tier | Trigger | Hosted coverage | Stable gate | Planning ceiling |
|---|---|---|---|---|
| PR validation | Open, edit, reopen, synchronize, or mark ready | Linux and selected documentation/dependency jobs | Colossus PR gate |
$0.15 per update |
| Pre-merge acceptance | Apply ci:full |
macOS 14 ARM, Windows 2025 x64, bounded fuzzing, supply chain, Chroma, PostgreSQL, OCI, OPA, and mTLS | Colossus pre-merge gate |
$0.75 per final run |
| Release | Push an annotated stable or approved prerelease tag | Six CLI targets; stable SDK candidate or macOS/Windows Developer Preview packages | Colossus release gate |
$4.50 per release |
These ceilings are planning targets based on hosted-runner rates and observed durations,
not billing or runtime enforcement. A job timeout remains mandatory for every hosted job.
The four-core ubuntu-latest-m larger runner is reserved for the longest CPU-bound x64
Linux lane in each tier: complete PR validation, live OCI/OPA acceptance, release
readiness, and the x86_64 Linux release artifact. Short control jobs, documentation,
dependency inspection, service-backed integration tests, and bounded single-process
fuzzing stay on standard or slim runners so larger-runner capacity is not spent where it
does not materially shorten the critical path. The repository's
.github/actionlint.yaml registers the provisioned larger-runner name so local workflow
linting recognizes it.
Steps¶
Pull-request validation¶
The classifier fails closed. Documentation-only paths build the documentation site and skip Rust. Code, configuration, build, release, CI, renamed unknown paths, and unknown new paths run the complete Linux Rust gate. API and SDK paths additionally select SDK generation, compatibility, language tests, and release-package checks inside that job. Desktop application, launcher, and Rust SDK paths select renderer checks there and the native Tauri acceptance described below. Rust, npm, Go, and Python dependency manifests and lockfiles also run license, source, ban, and advisory policy, including the standalone desktop Cargo graph.
Advisory exceptions must name one exact RustSec ID in every scanner that reports the
finding, document why the affected path is unreachable, and name the upstream removal
condition. The current cargo-audit exception for RUSTSEC-2026-0253 is limited to
Tantivy 0.26.1's LruCache<usize, Block>: the advisory requires a panicking key
destructor, while usize has no destructor. cargo-deny does not report this
informational advisory, so its advisory policy remains unmodified.
Tantivy PR #3034 has already moved
the unreleased branch to patched lru 0.18.2; remove the exception when that Tantivy
release is available on crates.io. Ratatui's independent dependency path is already
locked to lru 0.18.2.
Pull-request classification, title validation, and aggregate gate decisions execute
the contract checked out from the PR base revision, never the proposed replacement
from the PR head. While these contracts first land, their one-time bootstrap fallback
selects every PR tier and requires every result to succeed. If a trusted base classifier
predates the SDK or desktop outputs, the workflow appends both selections as true so an
old base cannot silently skip either component. This prevents a CI-changing PR from
suppressing validation by weakening its own classifier or gate scripts.
The Rust job combines Conventional Commit validation, exact AppArmor installation, and
the repository-owned cargo xtask component checks. The Rust component covers
formatting, crate-root structure, locked metadata, Clippy, the complete workspace suite,
and fuzz-harness linting. When selected, the SDK component installs pinned Node.js,
Python, and Go toolchains for reproducible generation and packaging, while the Desktop
component checks the standalone native bridge formatting, installs the renderer
lockfile, and audits, tests, and builds the renderer. Desktop selection also exercises
the managed-sidecar protocol and host crates. The workflow retains trusted-base
classification and runner provisioning, but does not duplicate portable check recipes
or allocate macOS or Windows runners.
The aggregate gate accepts a skipped job only when the classifier explicitly marked
that job unnecessary; the stable seven-argument gate contract remains unchanged.
Documentation deployment is separate: pull requests build documentation in PR
validation, while main changes are deployed by the Documentation workflow.
Request pre-merge acceptance¶
Apply ci:full only after the PR is ready to merge:
- Make the branch current with
mainand wait forColossus PR gateon the current PR merge commit. - Resolve every human and automated review conversation and address actionable findings in code and tests.
- Mark the PR ready for review if it is still a draft.
-
As a repository writer, apply the label:
Eligibility is checked on a cheap Linux runner before macOS or Windows is allocated. It
rejects draft PRs, actors below write permission, and a missing or failed current-head PR
gate. The required pre-merge gate fails on failed, cancelled, or unexpectedly skipped
acceptance work. Separate macOS jobs keep the root native-debug graph from coexisting
with the standalone Tauri graph on the runner's bounded disk. The desktop job lints and
tests the standalone native bridge; runs pinned Chromium keyboard,
accessibility, high-contrast, drawer, approval, and 880×640 layout acceptance; deletes
its debug artifacts; then builds the bundled sidecar, CLI, and Tauri application into one
shared non-incremental release tree. The native job exercises the otherwise-ignored real sidecar
bootstrap/pinned-gRPC/guardian lifecycle and sandbox acceptance. Together they prove the
pruned locked build, then create an ad-hoc signed two-phase app bundle and verify the outer
seal plus final nested-binary manifest hashes. Ad-hoc signing
uses the explicit ADHOC team sentinel, tests structure only, and produces a runtime that
intentionally refuses to start Managed Local. Distributable builds embed the expected
10-character Apple Team ID, use Developer ID and notarization, and verify exact code
identifiers for the app, sidecar, and CLI.
The Windows lane runs renderer typechecking, renderer tests, and platform-sensitive
Desktop contract tests before installing Rust or starting native compilation. Those
independent checks continue into Windows native, worker, sandbox, binary preparation,
Desktop Clippy, and Desktop library-test acceptance, then report every failed outcome
together before the lane fails. Desktop checks run only when their required binaries were staged.
Portable formatting remains owned by the PR tier instead of being repeated on platform
runners.
Supply-chain acceptance audits both the root sidecar graph and the desktop's independent
lockfile.
Do not push a new commit while acceptance is running. A synchronize event cancels the
old run and removes ci:full; the old result cannot authorize the new head. After the new
PR gate passes, resolve any new review and apply the label again.
The Colossus pre-merge gate sentinel runs on every pre-merge workflow event. A new
commit or a label event other than ci:full therefore leaves a failing gate without
allocating the acceptance runners. Only a successful ci:full run on the current head
replaces that sentinel result; a skipped gate can never satisfy the ruleset.
Failure path¶
- If classification is wrong or empty, fix the classifier or path contract; do not force a skipped gate through the aggregate job.
- If pre-merge eligibility fails, verify draft status, actor permission, branch currency, and the PR gate on the current merge commit before relabeling.
- If an acceptance job fails, diagnose that job, push the fix, wait for the new PR gate,
and reapply
ci:full. - If a release target fails, do not publish partial artifacts. Fix the source and create a new annotated tag according to the release policy.
Release flow¶
A release tag must be annotated, match either vX.Y.Z or vX.Y.Z-preview.N with
N > 0, point to a commit contained in main, and match both the workspace version and
changelog heading. Tag pushes run local release-readiness verification and exactly six
native CLI targets. Each CLI target combines its security acceptance, locked release
build, archive and checksum generation, clean installation, offline echo/audit, and
signed-bundle smoke.
A stable vX.Y.Z target additionally regenerates and tests the TypeScript, Python, and
Go SDKs, builds the exact npm tarball and Python wheel/source distribution, inspects
their intrinsic metadata, and binds them to the release commit with a manifest and
checksum set. Its aggregate gate requires every Desktop job to be skipped. It does not
read or require Apple, Tauri updater, or Authenticode credentials.
The stable SDK job compares the public API against the most recent stable tag reachable
from the release commit, falling back to that commit's parent for a first release. The
base is therefore fixed relative to the release commit, so rerunning an old tag after
main advances cannot report newer main APIs as removals.
An approved vX.Y.Z-preview.N target takes the mutually exclusive path: the stable SDK
candidate job is skipped, while credential-free macOS ARM and Windows x64 jobs package
the visibly unsigned Developer Preview described below. No unsigned Desktop package can
enter a stable core draft.
Developer Preview channel¶
vX.Y.Z-preview.N is the only credential-free tag path that may produce a runnable
Desktop; v0.10.1-preview.2 is the most recent example. It still runs all six CLI release
jobs. Its Desktop build uses the developer_preview channel,
COLOSSUS_DESKTOP_TEAM_ID=ADHOC, and the ad-hoc identity -; it never reads Apple signing
or notarization secrets. Packaging still verifies strict code signatures, fixed code
identifiers, the channel-bound sealed manifest, and the exact hashes of the bundled
sidecar and CLI.
The resulting Desktop archive is runnable for testing but is not Apple-notarized and its
ad-hoc signature does not establish publisher identity. The workflow names it
Colossus-Desktop-DEVELOPER-PREVIEW-vX.Y.Z-preview.N-aarch64-apple-darwin.zip, includes
an adjacent SHA-256 sidecar, sets GitHub prerelease metadata, and labels the draft
Colossus vX.Y.Z-preview.N - Developer Preview (Unnotarized). The native compile-time
channel also drives a persistent in-app warning. Production Desktop signing,
notarization, and update authority remain a separate release track from the stable core.
Within release.yml, only the draft job receives contents: write. After the selected
stable or preview contract passes, automation creates or updates a draft GitHub Release.
A human reviews and publishes it; the approved Developer Preview draft is already marked
as a GitHub prerelease. Publishing a stable draft triggers publish-sdk.yml, whose one
sdk-production job receives short-lived OIDC and tag authority only after protected
environment approval. It republishes the already-built candidate bytes rather than
rebuilding them. Manual dispatch is artifact-only and cannot create a release or mutate
a registry. A stable manual target validates the SDK path and skips Desktop; a preview
manual target uses the non-runnable validation_only Desktop channel:
Desktop update signing and channels¶
Only separately authorized stable Desktop builds advertise automatic updates. They require the repository variable
DESKTOP_UPDATE_PUBLIC_KEY, containing the one-line base64 Tauri updater public key,
plus the protected DESKTOP_UPDATE_PRIVATE_KEY secret and, when applicable,
DESKTOP_UPDATE_PRIVATE_KEY_PASSWORD. Unsigned Developer Preview and validation-only
builds reject update endpoints and keys and never produce updater artifacts; users
install later previews manually from GitHub Releases.
macOS packages the stable signed .app.tar.gz only after nested signing, outer signing,
notarization, stapling, and final bundle verification. The stable versioned draft
includes channel-scoped metadata whose platform entry carries that signature and an
immutable version-release URL.
When a human publishes a release that contains an independently produced stable.json,
desktop-update-channels.yml revalidates the tag, release kind, platform key, HTTPS URL,
and immutable release path before replacing that asset on the fixed
desktop-update-channels release. Stable core releases and Developer Previews contain no
such asset and skip this workflow. The native update client also uses the shared
additional-CA configuration and rejects HTTPS-to-HTTP redirects.
The application update signature is separate from platform publisher identity. Authenticode remains mandatory before a Windows package can enter the stable channel; the existing stable Windows release job remains absent and the release gate requires the unsigned Windows job to be skipped for stable tags.
Expected result¶
Routine PR updates allocate only selected Linux/documentation jobs, one deliberate final run provides representative pre-merge evidence, and release tags alone allocate all six CLI architecture jobs plus exactly one channel-specific extension: stable SDK candidates or unsigned Desktop Developer Previews. Registry publication and production Desktop authority remain independently protected. Each tier has one fail-closed aggregate check.
Bootstrap repository enforcement¶
The tracked ruleset starts in evaluation mode. After this change is merged, a repository
administrator uses the audited helper to create ci:full and apply the ruleset:
./scripts/ci/configure-repository.sh plan OWNER/REPOSITORY
./scripts/ci/configure-repository.sh evaluate OWNER/REPOSITORY
Exercise a documentation PR and a code PR, then run ci:full once. Confirm both stable
gate names, stale-label removal, resolved-conversation enforcement, and billing entries.
Only then activate protection:
The main ruleset requires a pull request with zero mandatory approvals, resolved review
conversations, an up-to-date branch, and both Colossus gates. It permits no bypass actors
and blocks direct pushes, deletion, and non-fast-forward updates. GitHub merge queues are
not part of this topology because they are unavailable for this private Team repository.
Verification¶
Run the change-selected local PR gate:
Local completion versus hosted tiers¶
Hosted tiering reduces repeated platform spending; it does not weaken the local completion
contract. Before handoff, run the focused tests needed while iterating, cargo xtask
check rust, and the change-selected cargo xtask pr gate described in
Source setup and test tiers. Release operators additionally run
./release/verify-release-readiness.sh.
Next step¶
For a normal contribution, resolve review and follow Request pre-merge acceptance. For repository rollout, follow Bootstrap repository enforcement without skipping the evaluation run.