Skip to content

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:

  1. Make the branch current with main and wait for Colossus PR gate on the current PR merge commit.
  2. Resolve every human and automated review conversation and address actionable findings in code and tests.
  3. Mark the PR ready for review if it is still a draft.
  4. As a repository writer, apply the label:

    gh pr edit PR_NUMBER --add-label ci:full
    

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.

git tag -a vX.Y.Z -m "Colossus vX.Y.Z"
git push origin vX.Y.Z

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:

gh workflow run release.yml --ref BRANCH -f version=vX.Y.Z

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:

./scripts/ci/configure-repository.sh activate OWNER/REPOSITORY

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:

cargo xtask pr --base origin/main

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.