Skip to content

ci: rebuild CI around one fast required check (ci-ok) - #1315

Merged
AnthonyRonning merged 21 commits into
masterfrom
ci/rebuild
Oct 27, 2026
Merged

AnthonyRonning merged 21 commits into
masterfrom
ci/rebuild

Conversation

@AnthonyRonning

@AnthonyRonning AnthonyRonning commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

What this does

This PR replaces Maple's pull-request CI with one workflow, ci.yml, and one required check, ci-ok.

  • One table, scripts/ci/lanes.py, defines every check (a "lane"): its paths, events, labels, runner, Nix shell, command and timeout.
  • scripts/ci/plan.py reads the diff and picks the lanes. ci-ok passes only when every picked lane passed.
  • The pre-commit hook and just lane <id> read the same table, so every lane also runs on a laptop.
  • Release, signing and publishing workflows are not touched. No OpenSecret EIF input changes either: the eif-dev, eif-prod and eif-preview derivations are identical to master's (proof below).

Why. Over 10-10..10-26 (6,000 runs):

  • An Agent PR waited 32 min at p50, and 63 min after PR #1297 (the dependency archive).
  • The time went to compiling on free 3-core macOS and 4-vCPU Windows runners, building the same Agent 2–3 times per platform, and restoring a 3 GB archive that made most jobs slower.
  • Each PR push started 13.5 workflows and showed about 48 checks, 18 of them skipped.
  • On master, cancel-in-progress cancelled Agent CI on 36 of 136 commits. With the red-by-design EIF approval status, only 40% of master commits had every workflow green.

Before and after

"Before" is measured from the 10-10..10-26 run data. "After" is an estimate; this PR's own run is the first measurement.

Before (measured) After (estimate)
PR to green, all PRs, p50 / p90 30 / 52 min about 7 / 12
Agent-only PR 32 / 52; 63 / 83 after #1297 about 6–7 / 10–12 (agent-test on 32 vCPU: a cold clippy + test build took 6m22s, against 11m25s on 8 vCPU)
OpenSecret PR 32 / 39 9–10 / 12 with EIF inputs, 6–7 otherwise
SDK PR 38 / 56 TS only 2–3; Rust SDK about 7 / 11
Research-only PR 2.3 / 16 3–4 / 6 (native paths 7–12)
Agent update service PR (services/agent-updates/) every OpenSecret lane, both EIF builds included agent-updates only
Workflows started per PR push 13.5 2 (ci.yml + the EIF approval workflow), +1 for a Pages/Auth preview
Check rows per PR push about 48 (18 skipped) about 20–25
Master commits with a full verdict 40% all green; Agent CI cancelled on 36 of 136 every master run is grouped per SHA and never cancelled
Master EIF/PCR approval status red on every revision whose PCRs are not approved yet a notice with the built PCRs; red only where an approval is actually being edited
New RustSec advisory turned every Agent PR red (9 Agent CI audit failures; RUSTSEC-2026-0404..0410 on 10-26) deps-* audits block only PRs that change that manifest, lockfile or deny.toml; otherwise the nightly opens one ci-nightly issue
Dev TestFlight every master push (88 of 136 rebuilt an identical app) only when Research web or iOS inputs change; dispatch always builds
CodeQL for a Research release a manual 40-min dispatch at release time runs on every master push, never cancelled, not part of ci-ok
GitHub Actions cache 12.07 GB (over the 10 GB cap), 4.5 GB of it from PRs PRs save no caches (one exception, unchanged from master: the fork-PR EIF approval check's branch-scoped Nix cache); heavy Rust caches live on Blacksmith (6.39 GB in GitHub's store today)
Agent macOS/Windows minutes on free runners about 4,000 per day on the PR path 0 on the PR path
Blacksmith spend baseline about −30 to −45% (estimate)

Tiers

Tier Trigger What runs Blocks
Pre-commit git commit the components the staged files select, from the same table (the Agent runs just ci-lint + just ci-test) locally
PR gate every PR, any base (each layer of a stack gets its own verdict) ci.yml: plan → selected lanes → ci-ok. Linux fmt/lint/tests per component, Agent macOS clippy, OpenSecret checks ci-ok
PR, by path or label packaging, native or Windows paths; labels ci:full, ci:package, ci:windows, ci:ios, ci:android, ci:research-dev-macos, ci:eif Agent packaging rehearsal (Linux and macOS: thin LTO, own nightly-refreshed cache, master keeps fat LTO; Windows: the unsigned Dev installer, installed and smoke-tested), PR #1316's native Windows updater tests and app typecheck (agent-windows-updater, same paths on PRs and master), Research iOS/Android/desktop, Agent Windows, EIF candidate yes, when selected
Master push push the selected lanes again, plus the full Agent suites on Linux, macOS and Windows; caches saved. The signed and evidence workflows (same names and jobs), CodeQL, the trusted EIF build never cancelled
Nightly schedule ci.yml nightly lanes, advisories included; nightly.yml: Bun audit and the SQLCipher provider suite on 3 OSes; one ci-nightly issue no
On demand dispatch or label ci.yml with lanes=<ids> or all —
Release release / dispatch unchanged owner clicks

Runners: Blacksmith runs everything someone waits on (8 vCPU Linux; 32 vCPU for agent-test; macOS 26 with Xcode 26.5; Windows 2025; ARM Linux for every trusted EIF build). Free GitHub runners take light lanes, attestation, Windows signing and the publishers. The repository variables CI_LINUX_RUNNER, CI_LINUX_HEAVY_RUNNER, CI_MACOS_RUNNER and CI_WINDOWS_RUNNER move a lane to another runner without a PR.

docs/ci.md is the how-to (lanes, labels, local runs, adding a lane). docs/repository-workflows.md is the reference (caches, concurrency, contracts, signed builds).

What is deleted

  • 22 workflows, whose checks are lanes now: agent-ci, agent-sqlcipher-supply-chain, agent-store-performance, agent-updates-tests, agent-windows-updater-tests, android-pr-build, desktop-pr-build, mobile-pr-build, opensecret-change-detection, opensecret-ci, sdk-integration, pages-tests, release-gates-tests, repository-checks, proxy-container, proxy-supply-chain, supply-chain, sdk-publish-tests, sdk-rust, sdk-supply-chain, sdk-typescript, updates-tests.
  • agent-windows-updater-tests.yml (added by Add signed Windows Agent updates and versioned installation #1316) is now the agent-windows-updater lane: the same 20 path filters minus its own file, the same two scripts unchanged (both always run; either failure fails the lane), the same Rust 1.98.0, MSVC, Perl and linker pins through .github/actions/setup-agent-windows, no cache, secret or environment, and its receipts kept as an artifact even on failure. Differences: it runs on blacksmith-8vcpu-windows-2025 instead of GitHub's windows-2025, master runs are no longer cancelled, and the typecheck no longer selects fxc (cargo check on the dev profile never compiles shaders). Add signed Windows Agent updates and versioned installation #1316's manual agent-windows-signature-probe.yml stays; its actions: read moved from the workflow to its one job.
  • The Agent dependency archive and its scripts (agent_cache*.py, agent_measure.py). It was slower than no cache everywhere it was measured.
  • Three selectors (agent_change_detection.py, agent_master_selection.py, hook_change_detection.py), replaced by the planner; their cases moved to test_plan.py.
  • About 2,400 lines of YAML-shape tests that pinned step counts, runner labels and job lists. test_contracts.py now asserts the contracts, and test_workflow_policy.py the rules (pinned actions, read-only permissions, timeouts, no PR cache writes).
  • Smaller: agent-desktop-build.yml's PR trigger (the rehearsal is now the agent-package-* lanes); six dead PR routes in change_detection.py; the root flake's 14 unused ci-* apps (its checks go from 13 to 7); docs/agent-ci-performance.md; the Linux fat-LTO job (a duplicate of the Linux bundle); the Agent store benchmark from CI entirely (the crate stays for local runs).
  • The x86_64 OpenSecret package build (nix build '.?submodules=1#default'), owner-approved (below).

Size: workflows 52 files / 9,134 lines → 32 / 6,815 (non-release lines 6,155 → 3,836). scripts/ci 119 / 29,689 → 108 / 25,852. The PR: 129 files, +5,750 / −12,138.

What is untouched

  • Release and publishers, byte-identical: release.yml, pages-publish, proxy-publish, updates-publish, zapstore-publish, sdk-publish-npm, sdk-publish-rust, agent-updates-publish, auth-pages-publish, opensecret-eif-release. Exactly one workflow is named Release, and every listener still finds its producer.
  • What the Agent update publisher reads: agent-desktop-build.yml keeps its path (so run_number is still the build number), every job name publisher.py needs (macOS, Linux and, since Add signed Windows Agent updates and versioned installation #1316, Windows: build, sign and verify), its artifact names, MAPLE_PACKAGE_BUILD_NUMBER: ${{ github.run_number }} on the Windows packaging and verification jobs, its master-only signing and GitHub-hosted attestation. test_contracts C2 runs master's own publisher.trust_run(…, linux=True, windows=True) against this workflow, with each job's if: and needs evaluated for a master push and a dispatch. Its builders now use exact-key caches on Blacksmith; the unsigned Windows build moved to Blacksmith, signing stays on GitHub windows-2025.
  • What release preflight reads: every job in ci_evidence.py keeps its file and id, runs on master push with the same push filters, and is never cancelled. ios-dev-testflight.yml keeps its name.
  • Signed lanes: the cache keys release.yml restores, the 13 environment names, and every release and publisher script (_common.sh, *-release.sh, web.sh, updates.sh, build-sdk-publish.sh, publisher.py, ci_evidence.py, test-release-gates.sh and the rest) are unchanged.
  • Toolchains: no flake.lock changed, no component flake changed, and the root dev shells are the same derivations.

OpenSecret changes the owner approved, and their proofs

The rule for this PR was "OpenSecret verbatim". The owner approved five exceptions:

  1. Agent update service PRs stop running OpenSecret. The OpenSecret selector did not know services/agent-updates/ (added later, in PR #1214), so it treated it as a new root and ran every OpenSecret lane, both EIF builds included. The prefix is now listed. A genuinely unknown directory (for example services/new-service/) still runs everything; a test covers both.

  2. The x86_64 #default package build is gone. Nothing consumed it, the shipped EIF is aarch64, and the other OpenSecret lanes compile and test the same code. os-flake-check keeps the component nix flake check.

  3. A master EIF/PCR mismatch is a notice, not red. check_opensecret_eif.sh now exits 3 for a PCR mismatch (and prints the built pcr.json); build and output failures still exit 1. In opensecret-eif.yml:

    • master push or dispatch: a mismatch is "approval pending", a notice with the built PCRs in the step summary, and the job passes;
    • pull request: a mode must match only if the PR edits that mode's approval files (pcrDev.json + pcrDevHistory.json, or pcrProd.json + pcrProdHistory.json), from the PR's merge base. The base and head SHAs come through env: and must be full commit ids; otherwise the job fails;
    • any other failure (exit not 0 or 3) still fails;
    • a manual run outside master still fails on a mismatch, as before.

    Only the last step of the eif and eif-trusted jobs changed, identically in both. Their if: conditions, runners, permissions, cache steps and matrix are identical to master. opensecret-eif-release.yml is byte-identical and never calls the check script.

  4. OpenSecret docs name the new lanes. services/opensecret/README.md and docs/nitro-deploy.md stop naming the deleted workflows, and the hook comment in services/opensecret/.githooks/pre-commit names the new lanes. The docs check now covers services/opensecret with no exception.

  5. Dependency audits (deps-*) no longer run on CI-only edits. They run when their manifest, lockfile or deny.toml changes, with ci:full, and nightly. A new advisory in someone else's crate should not turn a CI fix red.

Proof that the PCRs cannot change. nix eval of each EIF's derivation path (services/opensecret, submodules=1, aarch64-linux) gives the same result on master and on this branch:

Output master 109188c4 and this branch
eif-dev bwycsxmw56bs5qksr62pz8zr3s28zwd3
eif-prod hdzf3hidwamjqbg8214p31c08ha8xckw
eif-preview 060l38vsx63bjr87l6nxbsn4sv3r4srr

The same evaluation does see source changes: PR #1184 (OpenSecret src/ edits) moved eif-dev from x0pfcwdr… to bwycsxmw…. The EIF build, verify and publish scripts and the EIF fixtures are unchanged.

Validation done locally (at the final head)

  • Root nix flake check: all 7 checks pass (501 CI-script tests, 133 publisher tests, 158 Pages, 27 PCR-compatibility, the release gates, actionlint).
  • Tests: every scripts/ci test (501), the publisher tests (133, including Add signed Windows Agent updates and versioned installation #1316's Windows publisher tests), test_ci_evidence.py (21) and test-release-gates.sh (31/31) pass; the edited test modules also pass on Python 3.9.
  • Every commit passes plan.py lint and the scripts/ci suite, so bisect never lands on a red one. The exception is the first commit, which still carries master's own test_agent_workflows.py: its provenance test is locale-dependent (it fails under Nix bash with an empty LC_ALL on master too, and passes with LC_ALL=C); the next commit deletes it.
  • The EIF approval step runs for real in test_contracts against a fixture git history: 16 event, ref, SHA and mode cases × exit statuses 0, 1, 2, 3 and 42, including option-like and abbreviated SHAs.
  • plan.py lint: all 2,220 tracked files are owned or inert, and each ci.yml job's runner and timeout match its lane.
  • actionlint over all 32 workflows: clean.
  • Byte identity: the ten release and publisher workflows (including Add signed Windows Agent updates and versioned installation #1316's agent-updates-publish.yml), auth-pages-dev-publish.yml and all of services/agent-updates (including publisher.py) are identical to master, both at the base 109188c4 and at the current tip 1df4cf70.
  • The real pre-commit hook was run with scratch commits on an earlier commit of this branch (the hook and planner are unchanged since): docs-only runs nothing, a workflow edit runs actionlint and the CI tests, a broken workflow key is refused, an Agent edit runs the Agent lint and 2,277 tests.

Which lanes a PR gets (plan.py explain):

PR type Lanes
Agent only agent-lint, agent-test, agent-macos (+ packaging, supply-chain, Windows or the Windows updater tests when those paths change)
TS SDK only sdk-ts, os-sdk-e2e
Rust SDK Agent gate lanes, sdk-rust, proxy, research-rust, os-sdk-e2e
Proxy Agent gate lanes, proxy, research-rust
Research web / Rust / iOS-native research-web / research-rust / research-ios
OpenSecret backend os-rust, os-sdk-e2e, os-flake-check, os-eif
OpenSecret PCR approvals os-pcr
OpenSecret docs none
Agent update service only agent-updates
CI only (ci.yml, plan.py, lanes.py) every gate lane; no deps-* lane, no EIF build
A signed or CodeQL workflow repo
Docs only none (plan + ci-ok)
A path no lane owns, or a new service every lane

What only this PR's own run can prove

This PR edits ci.yml and the planner, so its run selects every gate lane (including agent-windows-updater), Agent packaging on Linux, macOS and Windows, Research Android and the EIF candidate (no deps-* lane). Everything runs cold, because no master cache exists yet. The run should show:

  • GitHub accepts ci.yml, the plan matrix and the ci-ok verdict over real job results.
  • Blacksmith provisions the 32 vCPU runner on first use; Windows 2025 has the Agent's toolchain; macOS 26 selects Xcode 26.5 for the Agent clippy and the packaging smoke.
  • Windows on Blacksmith, first runs. agent-package-windows builds the release-profile Windows binary (fxc, cargo-about, +crt-static), packages it with the run number as the build number, and passes verify --unsigned --smoke; master's Prod Windows build, which publisher.py now consumes, runs the same build on the same runner class. agent-windows-updater runs Add signed Windows Agent updates and versioned installation #1316's native tests, which so far passed only on GitHub's windows-2025: the reparse fixtures need the symlink privilege, one test expects an elevated Administrators owner, and the script checks the catalog signature of powershell.exe. Blacksmith's Windows jobs run as runneradmin on a windows25-full image, like GitHub's. If either lane fails on the runner rather than the code, set runner=Runner("windows-2025") on the lane and runs-on: windows-2025 on its job (and on agent-full-windows).
  • agent-windows (Clippy with -D warnings) lints Add signed Windows Agent updates and versioned installation #1316's roughly 4,000 lines of cfg(windows) code for the first time; the typecheck only ran cargo check.
  • os-sdk-e2e passes with its PostgreSQL service, os-helpers passes on ARM, and os-eif builds dev and prod. The candidate PCRs should equal the latest master EIF build, because the derivations are unchanged.
  • In the EIF approval workflow, changes reports eif=true and pcr_approvals=false, and both approval jobs skip.
  • The Pages preview, Auth Pages CI and proxy rehearsal run, because their files changed.

Adding ci:full and re-running also exercises the full Agent suites on macOS and Windows, the Research desktop, iOS and Dev-app lanes, and the deps lanes. Expect deps-research to fail there until the Research advisories are fixed (follow-up 4).

Only master can prove cache saves, never-cancelled master runs, the signed builds with their new caches, the Dev TestFlight gate skipping a non-Research push, CodeQL on push, the EIF approval notice and the nightly issue.

Before merging

  1. Re-confirm two owner decisions that Add signed Windows Agent updates and versioned installation #1316 reopened. Both were approved while Windows was not on the Agent feed. Add signed Windows Agent updates and versioned installation #1316 put it there: publisher.py now requires Build Windows binary (prod), Signed Windows (prod) and Verify downloaded Windows (prod) and the Prod Windows artifact (publication itself stays behind include_windows and AGENT_WINDOWS_UPDATES_ENABLED).
    • Agent windows-build on Blacksmith (signing stays on GitHub windows-2025). This matches macOS and Linux, whose Prod builds already compile on Blacksmith, and the publisher README's "compiled on the workflow's Blacksmith runners". It needs one change to a Add signed Windows Agent updates and versioned installation #1316 file: the manual signature probe (windows_signature_reference.py) required windows-2025 for all three Windows Prod jobs, and now also accepts blacksmith-8vcpu-windows-2025 for the build job only. Signing and download verification must still report windows-2025; test_contracts runs the probe against this workflow. To keep the probe strict instead, the build must go back to windows-2025 (the master Windows chain then stays at 53–90 min).
    • Windows packaging on PRs. It was label-only. On master every Agent PR built, installed and smoke-tested an unsigned Windows package; here agent-package-windows now runs when its own inputs change (apps/maple-agent/scripts/windows-*, build-release.sh, release-info.py, release-profiles.json, release_profile.rs, .github/actions/setup-agent-windows/, scripts/ci/agent_windows.py; 15 of 174 Agent commits on master over the last three months, about 9%) or with ci:package/ci:full. To go back to label-only, set its paths to () in lanes.py.
  2. Rebase once more. The branch is on 109188c4 (PR #1316). Master has since gained three Agent-only commits (PR #1320, staged Windows build validation in build-release.sh; PR #1309, local API controls; PR #1321, version bump). git merge-tree with 1df4cf70 is clean.

After merge, master's trusted EIF build runs once, because the workflow, check script and selector changed; a revision whose PCRs are not approved yet shows a notice, not red.

Owner follow-ups after merge

  1. Require ci-ok in the master ruleset (source: GitHub Actions; strict off; keep the review rules). Until then the old checks are gone and ci-ok is advisory. A few working days without a false red is a good point to switch it on.
  2. Enable auto-merge in the repository settings.
  3. Create the label ci:eif. The other CI labels already exist. Name a triage owner for the ci-nightly issue.
  4. Fix the Research RustSec advisories. deps-research fails on advisories published after its last green run: reported as zstd-sys and zvariant (RUSTSEC-2026-0370, RUSTSEC-2026-0372); a local run on 10-27 found 14 advisories in zstd 0.13.3, zstd-safe 7.2.4 and zstd-sys 2.0.16 (RUSTSEC-2026-0391, 0395–0406, 0410). This PR does not change Research dependencies; it needs a Research dependency PR.
  5. Rebase PR #1266 (Xcode 27.1 for the Apple lanes) onto this, and adopt the Metal Toolchain import from PR #1308 (the macOS 27 Blacksmith experiment). ci: build Apple lanes with Xcode 27.1 #1266 moved Agent macOS builds to GitHub's xcode-27 image (about 40 min) because Blacksmith's macOS 27 image lacks the Metal Toolchain. experiment: Agent macOS 27 builds on Blacksmith (do not merge) #1308 exports it on GitHub (36 s) and imports it on blacksmith-6vcpu-macos-27, where a cold Agent build passed in 15m50s. After the rebase, ci: build Apple lanes with Xcode 27.1 #1266's change becomes the macOS runner constant in lanes.py, its label in .github/actionlint.yaml, the Xcode pin, and the import step; most of its shape-test edits disappear.

Smaller decisions, no rush:

  • agent-supply-chain (cargo-deny) and sdk-ts (bun audit) still audit advisories whenever they are selected, CI edits included. Decide whether they should follow the deps rule.
  • change_detection.py has no services/agent-updates/ prefix, so an Agent update push to master still starts the signed Research builds. That was outside the approved OpenSecret scope.
  • A manual EIF dispatch outside master still fails on a mismatch, as before.
  • opensecret-eif.yml's eif job (fork PRs that edit approvals, manual runs outside master) is the one pull-request job that saves a cache: magic-nix-cache writes its Nix store to the branch-scoped GitHub cache, which master and other PRs cannot read. It is unchanged from master; the policy test now counts it as a writer and pins the exemption, and the cache docs name it. Disabling it (use-gha-cache: disabled) would make fork EIF checks build cold.
  • agent-test defaults to 32 vCPU; a 16 vCPU runner can be tried through CI_LINUX_HEAVY_RUNNER without a PR.
  • After the first master runs, optionally dispatch nightly.yml once and run opensecret-eif-release with dry_run.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Maple Auth development preview: https://5b731d43.maple-auth-dev.pages.dev

Commit: 3e744868c88cfacd275d3d5aa32c0348e4dd5759

Uses development services. Cloudflare Access applies. Provider OAuth is not configured for PR URLs; use the stable Auth Dev host for OAuth rehearsal.

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Maple development preview: https://c293e7af.maple-ca8.pages.dev

Commit: 3e744868c88cfacd275d3d5aa32c0348e4dd5759

Uses development API, billing, flags and PCR configuration. Cloudflare Access applies.

@AnthonyRonning

Copy link
Copy Markdown
Contributor Author

This PR's own runs (every gate lane, packaging, Research Android and the EIF candidate; all cold, since PRs never save caches).

Run 1 (40453021288) found four problems:

  • agent-lint and agent-supply-chain hit 15-minute timeouts on a cold cache. A lane that times out never saves its cache, so the timeouts now cover a cold run, and lint moved to 32 vCPU.
  • agent-windows found two Windows-only Clippy lints in existing Agent code. The old CI never ran Clippy on Windows. Both are intentional and now carry scoped allows (c8bd24cd); behaviour is unchanged.
  • os-helpers failed Trivy on CVE-2026-84782 (libssl3). That was fixed on master by #1318, and this branch is rebased onto it.

Run 2 (40455950361): all green, ci-ok success. Wall time was 40.6 min and runner time 203 min, both cold. Cold job times in minutes:

Lane Cold min
agent-package-linux (fat-LTO + Docker smoke) 40.2
research-android 29.6
agent-package-macos (incl. GUI smoke) 17.4
agent-lint (32 vCPU) 15.8
os-rust 13.9
agent-windows (Blacksmith) 10.8
os-eif dev / prod 9.4 / 1.6
agent-macos clippy 6.4
agent-test (32 vCPU) 5.0
research-rust 4.8
os-sdk-e2e (with Postgres) 4.0
every other lane 0.5–3.6

Ordinary PRs select far fewer lanes than this CI-infra PR does, and run warm once master has saved caches. Packaging and Android only run when their own paths change or on a label. Both EIF candidates were substituted from FlakeHub, so their outputs are identical to master's existing builds; the dev PCR0 equals pcrDev.json.

AnthonyRonning and others added 11 commits October 27, 2026 15:50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…vate the repo lane

The manual Windows signature probe's `actions: read` moves from the
workflow to its one job, as the read-only workflow-permissions policy
requires; the job's token is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…om ci.yml

Activate the remaining Agent lanes (full Linux/macOS/Windows suites,
supply chain, provider, store bench, PR packaging rehearsals), add
nightly.yml with one ci-nightly issue, make agent-desktop-build.yml
master-only with exact-key release caches and a Blacksmith Windows
builder, and delete agent-ci.yml, the SQLCipher and store workflows
and the dependency archive, cache-scope, measurement and selector
scripts.

test_windows_updater_workflow.py reads the Windows Rust pin from
setup-agent-windows, which windows-build now uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Activate the sdk-ts, sdk-rust, proxy, deps-{research,sdk,proxy},
proxy-container(-arm64), research-web, research-rust, research-ios,
research-android, research-desktop-{macos,linux,windows}, research-dev-macos,
auth, updates and agent-updates lanes. PR lanes restore only caches that
master writes and never save one. frontend-tests, web-build and rust-tests
become push-only evidence twins (rust-tests on Blacksmith, writing
ci-v1-research-rust); proxy-rust keeps only the release rehearsal. Delete the
twelve workflows these lanes replace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…docs

Signed Research workflows get job timeouts and job-level OIDC and
attestation scopes; Maple Dev TestFlight builds only for Research web or
iOS inputs (dispatch always builds); CodeQL runs on every master push,
per SHA, never cancelled. The temporary policy and contract allowlists
are gone.

The pre-commit hook selects components with `plan.py hook --staged`
from the lane table, and the Agent hook runs the agent-lint and
agent-test recipes. docs/ci.md is new, docs/repository-workflows.md is
rewritten, AGENTS.md gains CI rules for agents, and the unused flake
apps.ci-* outputs are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- os-sdk-e2e and os-helpers select on their own entry scripts; the
  OpenSecret selector routes inert paths too, so its routing is verbatim
- widen the Agent packaging selector to the scripts packaging calls, the
  Agent flake and (macOS) the Xcode pin; agent-windows runs the native
  Windows tests; acp-only and proxy-only clippy move into ci-lint
- os-eif: PRs to master only, plus a ci:eif label; os-flake-check builds
  the x86_64 package again; opensecret-eif.yml runs on every PR to master
- ci-ok calls `plan.py verdict`; lint checks special jobs' runners and
  timeouts; setup-bun saves no PR cache; agent-test on 32 vCPU
- ci.yml's nightly run covers deps and supply chain; the reporter needs
  today's scheduled run
- drop PR-only scripts from the signed-build selector; Auth push paths
  rehearse publisher edits again; docs fixes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep services/opensecret/ byte-identical to master, as the OpenSecret port
requires: restore its README, nitro-deploy.md and hook comment. The C7 docs
contract skips that subtree; its docs move with OpenSecret PRs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…package build

- opensecret_change_detection.py lists services/agent-updates/ as an
  independent root. Since #1214 every agent-updates change selected every
  OpenSecret check, including both EIF builds. Unknown roots still select
  every check.
- os-flake-check runs only the component `nix flake check`. Nothing consumed
  the x86_64 `#default` package: the shipped EIF is aarch64, and os-rust,
  os-sdk-e2e and the EIF lanes compile and test the same code.
- deps-opensecret follows its own Cargo.toml, Cargo.lock and deny.toml
  instead of the selector's `audit` output, which CI files also trigger. CI
  edits no longer select any deps lane, so new RustSec advisories surface in
  the nightly run instead of failing unrelated PRs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…not failed

check_opensecret_eif.sh exits 3 for a PCR mismatch and prints the built
pcr.json; usage, build and comparison failures keep their non-3 statuses
(a Nix build failure is now always 1, never 3).

opensecret-eif.yml's compare step, identical in eif and eif-trusted:
- master push or dispatch: a mismatch is a notice, with the built PCRs in
  the step summary, and the job passes;
- pull_request: a mode must match only if the PR's own diff (merge base to
  head, SHAs through env) edits that mode's pcr<Env>.json or
  pcr<Env>History.json; otherwise its mismatch is a notice;
- dispatch outside master: a mismatch fails, as before;
- any other non-zero status fails.
Job conditions, runners, permissions, cache steps and the matrix are
unchanged; opensecret-eif-release.yml does not call the script.

services/opensecret/README.md and docs/nitro-deploy.md now name ci.yml
instead of the deleted workflows and describe the new verdicts, so C7 no
longer skips services/opensecret/. The eif-dev and eif-prod derivations are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- opensecret-eif.yml: the EIF compare step accepts only full 40-hex PR
  base and head SHAs before it diffs the approval files. Anything else
  fails the job, so an option-like or abbreviated value cannot make an
  edited mode's mismatch look neutral. Both approval jobs keep the same
  step; test_contracts covers option-like and abbreviated SHAs.
- docs/ci.md: the advisory rule is scoped to the deps-* lanes, and names
  the two gate lanes that still audit advisories (agent-supply-chain,
  sdk-ts).
- services/opensecret/.githooks/pre-commit: the comment names ci.yml's
  os-rust and os-flake-check lanes and drops the removed package build.
  Comment lines only; the eif-dev, eif-prod and eif-preview drvPaths are
  unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AnthonyRonning and others added 10 commits October 27, 2026 15:50
The new Windows Clippy lane is the first CI job to lint the Agent's
cfg(windows) code. Both findings are intentional, so they get a scoped
allow with the reason; behaviour is unchanged:

- filesystem_kind joins "\" to the path prefix on purpose: joining a rooted
  path keeps the prefix, so C: becomes C:\ for GetDriveTypeW.
- The Windows-only hard-link cleanup test clears the read-only attribute,
  not Unix mode bits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first ci.yml run timed out agent-lint and agent-supply-chain at 15
minutes on a cold cache, and a lane that times out never saves its cache.
Timeouts now cover a cold run (lint and test 40, supply chain 30). The
lint lane joins agent-test on the 32 vCPU runner, since it is on the Agent
PR path whenever a dependency bump misses the cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Agent PR packaging rehearsals build with thin LTO and 16 codegen units. They
  prove the packaging scripts, not the optimizer; master's signed builds keep
  fat LTO. A profile change rebuilds every dependency, so the rehearsals keep
  their own cache, which ci.yml's nightly master run saves.
- research-android restores the Research Rust cache rust-tests.yml writes.
  Cargo fetches every git dependency in the lockfile for any target, so the
  Android build was spending most of its time fetching Goose and PDFOxide.
- The store benchmark leaves CI: no ci:perf lane and no nightly job. The
  crate and `just store-bench` remain for local runs.
- docs/ci.md, AGENTS.md and the Agent skill say when a label is worth adding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1316 (signed Windows Agent updates) landed while this branch moved the
Windows jobs. Carry its contracts over:

- agent-package-windows replaces the deleted PR-only windows-unsigned job,
  so it now exports MAPLE_PACKAGE_BUILD_NUMBER, which windows-release.py
  requires: the Actions run number, or 1 outside Actions (as
  package-release.sh does), so `just lane agent-package-windows` still runs
  locally.
- test_contracts C2 asks the publisher to trust a run with Windows too, and
  pins MAPLE_PACKAGE_BUILD_NUMBER to github.run_number on every job that
  packages or verifies Windows (the assertion #1316 added to the deleted
  test_agent_workflows.py).
- The manual signature probe checks that each Windows Prod job ran on
  windows-2025. The unsigned build now runs on Blacksmith, so the build job
  may also report blacksmith-8vcpu-windows-2025 (signing and verification
  still must be GitHub's). A new C2 contract runs the probe's producer check
  against this workflow's jobs and runners.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agent-windows-updater-tests.yml (from #1316) becomes the
agent-windows-updater lane, reported by ci-ok:

- Selected by the workflow's own paths on pull requests, the merge queue
  and master pushes alike, by ci:windows and ci:full, by CI edits, and
  nightly. Only the deleted workflow's own path is dropped.
- It runs scripts/ci/windows-app-typecheck.ps1, then
  scripts/ci/windows-updater-tests.ps1, unchanged, with pwsh from the
  repository root and the workflow's environment (RUSTFLAGS -D warnings,
  CARGO_TERM_COLOR never, no incremental build, no debug info). Both
  always run, as the two parallel jobs did, and either failure fails the
  lane.
- A special ci.yml job keeps both receipts and Cargo logs as one artifact
  even on failure. It uses setup-agent-windows, the pinned Rust, MSVC,
  native Perl and linker the workflow set up by hand, and restores no
  cache. The fxc selection is not carried over: GPUI compiles shaders only
  in builds without debug assertions, so a dev-profile cargo check never
  runs fxc.
- It runs on the Windows lane runner (Blacksmith, CI_WINDOWS_RUNNER to
  fail over) instead of GitHub's windows-2025. The native reparse fixtures
  need the symlink privilege, so this PR's own run is the first proof on
  Blacksmith.

test_windows_updater_workflow.py now tests the lane, the job and the
scripts instead of the workflow's YAML: no authority, the pinned toolchain
and no cache, selection through the planner, both scripts running (with a
stub pwsh) and the retained evidence. The script assertions are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- agent-package-windows also runs on its own Windows inputs: the Windows
  packaging scripts, build-release.sh (its Windows branch), release-info.py,
  the release profile files and the Windows toolchain. #1316 put Windows on
  the Agent feed, and on master every Agent PR built, installed and
  smoke-tested an unsigned Windows package; label-only selection assumed
  Windows was not published. ci:package and ci:full still add it.
- test_contracts C2 evaluates each agent-desktop-build.yml job's `if:` and
  `needs` for a master push and a dispatch, so a condition that skips a
  Prod job the publisher requires fails the contract. Bare true, false and
  null evaluate as GitHub's literals.
- A new C2 contract runs every lane that calls windows-release.py package
  or verify with stand-ins and checks they receive MAPLE_PACKAGE_BUILD_NUMBER:
  the Actions run number, or 1 outside Actions.
- C7 also checks quoted or bold workflow display names in docs; updates.md
  named the deleted Agent Windows Updater Tests workflow and now names the
  agent-windows-updater lane.
- magic-nix-cache-action counts as a cache writer unless use-gha-cache is
  disabled. opensecret-eif.yml's eif job (fork PRs that edit approvals,
  manual runs outside master) is the one exempt pull-request writer: no
  FlakeHub, no id-token, branch-scoped cache. The workflow is unchanged;
  the cache docs now name the exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first Blacksmith run of agent-windows-updater failed one native test:
the updater's storage validation rejects the test image because Blacksmith's
workspace inherits a foreign write ACE, which GitHub's workspace does not.
The validation is right, and #1316 proved these tests on windows-2025, so
agent-windows-updater and agent-full-windows (which runs the same tests) use
GitHub's image again. The Windows Clippy lane and the unsigned builds stay on
Blacksmith.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Windows Clippy lane is the first CI job to lint #1316's Windows-only
updater code. Five mechanical fixes, no behaviour change: three collapsible
ifs (two as let chains), a range check written with RangeInclusive::contains,
and PROCESSENTRY32W built with its size in the initializer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run 5 built, packaged and verified the unsigned Dev installer on Blacksmith,
then the installer smoke test (setup.exe /S into a temporary directory)
failed there. Master's install smoke for the same installer runs on GitHub's
windows-2025 (agent-desktop-build.yml verify-windows), which also passes the
updater's native tests, so the PR rehearsal runs there too. It no longer
restores master's Blacksmith-written release cache, which a GitHub runner
cannot read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…earsal

Run 6 cancelled research-rust at its 20-minute timeout: 14.7 minutes went to
Cargo fetching Goose's git history before master's Research cache exists.
research-rust and research-android get 45 minutes. The Windows packaging
rehearsal uses the same thin-LTO profile as the Linux and macOS ones; master's
signed Windows build keeps fat LTO.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AnthonyRonning
AnthonyRonning marked this pull request as ready for review October 27, 2026 16:42
@AnthonyRonning
AnthonyRonning merged commit 8643744 into master Oct 27, 2026
44 of 46 checks passed
@AnthonyRonning
AnthonyRonning deleted the ci/rebuild branch October 27, 2026 16:42

This branch was successfully deployed

2 active deployments
pages-pr-1315 — 3e744868 Deployed Oct 27, 2026 by github-actions[bot]
auth-pages-pr-1315 — 3e744868 Deployed Oct 27, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant