Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3c399df
ci: add ci.yml, the lane planner and the Agent PR gate
AnthonyRonning Oct 26, 2026
6afc33f
ci: replace workflow shape tests with policy and contract tests; acti…
AnthonyRonning Oct 26, 2026
532ab2f
ci: run Agent verification, packaging rehearsal and nightly checks fr…
AnthonyRonning Oct 27, 2026
202b7f8
ci: move SDK, proxy, Research and service checks into ci.yml lanes
AnthonyRonning Oct 27, 2026
4184bdf
ci: run OpenSecret checks and the PR EIF candidate from ci.yml lanes
AnthonyRonning Oct 27, 2026
cac3de7
ci: finish the rebuild: signed-lane hygiene, hook on the planner, CI …
AnthonyRonning Oct 27, 2026
17722c0
ci: address review findings
AnthonyRonning Oct 27, 2026
8227458
ci: fix validation
AnthonyRonning Oct 27, 2026
99e64d4
ci: route agent-updates away from OpenSecret and drop the unused x86 …
AnthonyRonning Oct 27, 2026
1b22278
ci(opensecret): report EIF approval mismatches on master as pending, …
AnthonyRonning Oct 27, 2026
c1e4f22
ci: address follow-up review findings
AnthonyRonning Oct 27, 2026
36ee67a
fix(agent): allow two intentional Windows-only Clippy lints
AnthonyRonning Oct 27, 2026
417dc9b
ci: size Agent lane timeouts for cold runs and lint on 32 vCPU
AnthonyRonning Oct 27, 2026
17df448
ci: faster PR packaging, cached Research git deps, no store benchmark
AnthonyRonning Oct 27, 2026
d32ae1c
ci: align with #1316
AnthonyRonning Oct 27, 2026
4ed120a
ci: fold the Windows updater tests into ci.yml lanes
AnthonyRonning Oct 27, 2026
b2a6390
ci: address #1316 integration review
AnthonyRonning Oct 27, 2026
039c7b5
ci: run the Agent's native Windows test lanes on GitHub's windows-2025
AnthonyRonning Oct 27, 2026
9d909ac
fix(agent): satisfy Windows Clippy in the updater's Windows bundle code
AnthonyRonning Oct 27, 2026
6f02204
ci: run the Windows packaging rehearsal on GitHub's windows-2025
AnthonyRonning Oct 27, 2026
3e74486
ci: cold-run timeouts for Research Rust and Android; fast Windows reh…
AnthonyRonning Oct 27, 2026
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
21 changes: 13 additions & 8 deletions .agents/skills/develop-maple-agent/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,25 @@ just ci
and warning-denied workspace builds/tests. Use `just release` for optimized
build and performance evidence. Root `just agent-check`, `agent-build`, and
`agent-dev` enter this component environment. The repository pre-commit hook
runs `cargo fmt`, one workspace Clippy pass, and the workspace tests in this
shell when Agent files are staged; set `MAPLE_HOOK_FULL=1` for the complete
`just ci`. Root `nix flake check
runs `just ci-lint` and `just ci-test` (the `agent-lint` and `agent-test` CI
lanes) in this shell when Agent files are staged; `MAPLE_HOOK_FULL=1` adds
`just ci-extra`, the rest of `just ci`. Root `nix flake check
--no-update-lock-file` additionally validates workflow selection and security
contracts when CI, Nix, or routing changes.

Agent has its own Cargo and Nix lockfiles. CI selects Agent for proxy runtime
changes and for Rust SDK build inputs, both path dependencies it builds from
the tree, but not for TypeScript SDK changes. Component-only
changes should not unnecessarily select Research packaging. Maintain the root
selectors, their tests, `.github/workflows/agent-ci.yml`, and unsigned PR
selection in `.github/workflows/agent-desktop-build.yml` together. The desktop
workflow compares `master` with the last fully verified ancestor package run;
unrelated pushes skip packages, while manual master dispatch forces both profiles.
changes should not unnecessarily select Research packaging. Agent lanes live in
`scripts/ci/lanes.py`, with fixtures in `scripts/ci/test_plan.py`; root
`just lane <id>` runs one locally. PRs run `agent-lint`, `agent-test` and
`agent-macos`, plus Windows tests and clippy for Windows paths,
`agent-supply-chain` and `agent-provider` for dependency inputs, and an unsigned
Dev package rehearsal for packaging inputs. Labels `ci:windows`, `ci:package` and
`ci:full` add lanes when a change carries Windows or packaging risk its paths do not
show ([labels](../../../docs/ci.md#labels)); master adds the full macOS and Windows suites. On master,
`.github/workflows/agent-desktop-build.yml` builds signed packages for pushes
that change `AGENT_DESKTOP` inputs and on every manual dispatch.

Linux Nix packages use a pure source fileset rooted at the monorepo, including
the sibling SDK/proxy source and SDK assets. Validate it when adding a new local dependency
Expand Down
14 changes: 9 additions & 5 deletions .agents/skills/develop-maple-proxy/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,20 @@ nix develop --no-update-lock-file .#ci -c \
./scripts/ci/verify-local-rust-deps.sh
```

Root workflows own proxy Rust, daily supply-chain, non-publishing container,
and native-release rehearsal checks. `proxy/src/**`, `proxy/Cargo.toml`, and
The root `ci.yml` lanes `proxy`, `deps-proxy`, and `proxy-container` (both
architectures on `master`) run these checks, cargo-deny, and a non-publishing
container build; `just lane proxy` runs the first locally. `proxy-rust.yml`
rehearses the native release builds, and `ci.yml`'s nightly run re-checks
advisories.
`proxy/src/**`, `proxy/Cargo.toml`, and
unknown proxy build inputs are desktop application inputs; tests, examples,
docs, the standalone lockfile, and container-only files do not by themselves
route expensive Maple app builds. Rust SDK build inputs (`sdk/rust/Cargo.toml`,
`src/`, `build.rs`, `assets/`) select proxy and desktop checks too, because the
proxy and both desktops build `sdk/rust` from the tree. When a new
input changes either graph, update
`scripts/ci/change_detection.py`, its table-driven tests, and workflow paths in
the same change.
`scripts/ci/change_detection.py`, `scripts/ci/lanes.py`, and their table-driven
tests in the same change.

## Exercise the right runtime

Expand Down Expand Up @@ -115,7 +119,7 @@ cargo package --locked --manifest-path proxy/Cargo.toml

If the proxy references a new `maple-sdk` version, publish that SDK crate
first. Do not publish either crate from Maple's application Release workflow.
Root proxy container CI builds without pushing. After a successful stable Maple
CI's proxy container lanes build without pushing. After a successful stable Maple
Release, `.github/workflows/proxy-publish.yml` independently compares that
release's proxy version with the previous stable Maple Release. Unchanged
versions skip, including the unbackfilled 0.3.3 baseline. A strictly newer,
Expand Down
17 changes: 10 additions & 7 deletions .agents/skills/develop-opensecret-sdk/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ nix develop --no-update-lock-file -c bun run build
```

For tests without service fixtures, use the credential-free selection in
`.github/workflows/sdk-typescript.yml`. An unfiltered `bun test` also collects
`scripts/ci/build-sdk-publish.sh`. An unfiltered `bun test` also collects
integration tests that require a configured backend and test identities; use
`sdk-integration.yml` for that complete local-stack setup.
`scripts/ci/opensecret-sdk-e2e.sh` (the `os-sdk-e2e` lane) for that complete
local-stack setup.

For Rust work:

Expand All @@ -76,16 +77,18 @@ nix develop --no-update-lock-file -c bash -lc '
'
```

Run focused tests while iterating, then match the root path-scoped workflows:
`sdk-typescript.yml`, `sdk-rust.yml`, and `sdk-supply-chain.yml` as applicable.
Run focused tests while iterating, then match CI from the repository root with
`just lane sdk-ts`, `just lane sdk-rust`, and `just lane deps-sdk` as applicable
(lanes in `scripts/ci/lanes.py`).
The repository pre-commit hook runs `sdk/.githooks/pre-commit` in this shell
when SDK files are staged: the Rust format, Clippy, `--lib` tests, and docs,
and the TypeScript format, build, and top-level unit tests. It never runs the
integration suites or cargo-deny.
When the change reaches backend behavior, authentication, or the encrypted wire
contract, also match `sdk-integration.yml`. It starts disposable PostgreSQL and
the in-tree `services/opensecret/` backend from the same checkout, then tests
both SDK implementations without a hosted development server. Inspect backend
contract, also match the `os-sdk-e2e` lane (`scripts/ci/opensecret-sdk-e2e.sh`).
It migrates a disposable PostgreSQL, starts the in-tree `services/opensecret/`
backend from the same checkout, then tests both SDK implementations without a
hosted development server. Inspect backend
changes and run its component validation when the compatibility contract reaches
them; there is no separate integration revision to advance.

Expand Down
9 changes: 5 additions & 4 deletions .agents/skills/release-maple/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ commit, external effect, and authority provided by the user.
number. Its master-only manual dispatch forces a fresh build even without
app changes. After a duplicate-build rejection, dispatch this full workflow
from master; retrying only submission reuses the rejected IPA's build number.
- The independent `Maple Dev TestFlight` workflow also builds every master
push and uploads the distinct `cloud.opensecret.maple.dev` application for
- The independent `Maple Dev TestFlight` workflow builds master pushes that
change Research web or iOS inputs, and every master dispatch, and uploads the
distinct `cloud.opensecret.maple.dev` application for
internal testing against the existing development services. Manual dispatch
accepts master only. Its app identity, credentials, artifact verification,
internal group setup, and retry contract are documented in
Expand Down Expand Up @@ -129,8 +130,8 @@ head_sha="$(printf '%s' "$preflight" | jq -r .head_sha)"
The script requires a clean current `master`, exact manifest version parity, a
newer version and unused tag, and successful executed master CI jobs.
CodeQL must run on the exact release commit; canonical master push, nightly,
or manual CodeQL runs qualify. If the nightly scan has not covered that commit,
run CodeQL manually from master before preflight. Other workflows still require
or manual CodeQL runs qualify. Every master push runs it; if that run is
missing or failed, run CodeQL manually from master before preflight. Other workflows still require
master-push evidence. Research test/build evidence may
come from an ancestor only when the complete Git tree diff contains exclusively
independent Agent, documentation, agent guidance, or release-gate test-harness
Expand Down
7 changes: 4 additions & 3 deletions .agents/skills/review-opensecret-security/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,12 @@ Load `$validate-opensecret` and run the tiers reached by the diff. Report local,
database, provider, client, build/artifact, and live evidence separately.

Local artifact builds and read-only PCR comparison are validation when in
scope. Root backend CI validates Rust, Nix checks/default binary, and SDK
compatibility. A separate ARM64 workflow compares dev/prod EIF
scope. Root backend CI validates Rust, Nix checks, and SDK compatibility. A
separate ARM64 workflow compares dev/prod EIF
measurements on explicit approved-PCR JSON edits in PRs, relevant master
changes, and manual runs. Do not require ordinary backend PRs to update
approvals, and do not suppress meaningful master mismatches. Master push/manual
approvals. A master mismatch is a pending-approval notice with the built PCRs;
do not hide it or turn it into a pass without that notice. Master push/manual
runs and same-repository PR comparisons receive OIDC for FlakeHub caching;
fork PRs and non-master manual refs use GitHub's branch-scoped cache without
OIDC. Review event/ref guards and the PR head-repository equality check,
Expand Down
7 changes: 5 additions & 2 deletions .agents/skills/validate-maple/references/automated-checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ Do not cite the pre-commit hook as complete proof: it runs only the format, lint

## Build the affected platform

Use these commands to mirror PR artifact builds. Then launch and smoke the result separately.
Use these commands to mirror PR artifact builds (the `research-desktop-*`, `research-android` and
`research-ios` lanes; `just lane <id>` runs one). Then launch and smoke the result separately.

These scripts deliberately hide local `.env*` files and compile fixed PR
endpoint profiles. Their artifacts are PR packaging evidence; they do not prove
Expand Down Expand Up @@ -151,9 +152,11 @@ Run on real Windows from Git Bash/MSYS:

### Android

Run on x86_64 Linux:
Run on x86_64 Linux. The PR lane builds one ABI in debug; the second command
rehearses the four-ABI release build that master runs signed:

```bash
nix develop --no-update-lock-file .#android -c ./scripts/ci/android-pr.sh
MAPLE_ANDROID_FAKE_SIGNING=1 MAPLE_ANDROID_WEB_ENVIRONMENT=pr nix develop --no-update-lock-file .#android -c ./scripts/ci/android-release.sh
```

Expand Down
25 changes: 14 additions & 11 deletions .agents/skills/validate-opensecret/references/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ not completion evidence.
## Tier 1: reproduce Rust CI

Initialize submodules when building or testing, then run the exact inner gates
from monorepo-root `.github/workflows/opensecret-ci.yml` through the pinned,
side-effect-disabled component Nix environment:
of the `os-rust` lane (`scripts/ci/lanes.py`; `just lane os-rust` from the
monorepo root) through the pinned, side-effect-disabled component Nix environment:

```sh
git -C ../.. submodule update --init --recursive -- services/opensecret/privatemode-public
Expand Down Expand Up @@ -134,9 +134,9 @@ manifest and lockfile select its SDK version and source; published pins are
the default, with local links supported under the
[consumer version policy](../../../../docs/sdk-publishing.md#consumer-version-policy).
Verify that selection before using an application build as evidence for an SDK
source edit. Root `sdk-integration.yml` runs both in-tree SDKs against
the backend in the same checkout with disposable PostgreSQL and loopback
configuration. That deterministic gate does not prove an application or live
source edit. The root `os-sdk-e2e` lane (`scripts/ci/opensecret-sdk-e2e.sh`)
runs both in-tree SDKs against the backend in the same checkout with disposable
PostgreSQL and loopback configuration. That deterministic gate does not prove an application or live
provider flow. Test browser Research and affected native paths independently;
an unavailable client runtime leaves that layer unverified.

Expand All @@ -156,14 +156,17 @@ nix build --no-link --no-write-lock-file '.?submodules=1#default'

PCR reference/history updates remain operator-controlled release work.
Read-only EIF construction and PCR comparison are validation when in scope.
Root backend CI runs applicable Nix checks and builds the
default backend binary. The separate root EIF approval workflow builds dev/prod
Root backend CI runs the component flake checks but does not build the
default package. The separate root EIF approval workflow builds dev/prod
and compares generated measurements on PRs that explicitly edit the four
approved PCR JSON files, relevant backend/TEE or approval changes to master,
and manual runs. Ordinary backend PRs do not require new PCR approvals; master
mismatches intentionally signal that the revision does not match its current
approvals. This repository's CI signs only in the reviewer-gated `OpenSecret EIF release`
workflow, never creates GitHub Releases, and never deploys the TEE service.
and manual runs. Ordinary backend PRs do not require new PCR approvals. A PR
fails on a mismatch only in an environment whose approval files it edits; a
master mismatch passes with a notice and the built PCRs, meaning the revision
awaits approval. The check script exits 3 for a mismatch and with another
non-zero status for a build failure. This repository's CI signs only in the
reviewer-gated `OpenSecret EIF release` workflow, never creates GitHub
Releases, and never deploys the TEE service.
Ordinary pull-request completion does not update PCR references.
Do not copy or sign values just to clear a validation failure; distinguish an
EIF build failure from a PCR mismatch. Use `docs/pcr-compatibility.md` for the
Expand Down
10 changes: 5 additions & 5 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#!/bin/sh
# Maple monorepo pre-commit hook.
#
# Classifies the staged paths with scripts/ci/hook_change_detection.py and runs
# each selected component's own .githooks/pre-commit inside that component's
# `scripts/ci/plan.py hook --staged` selects components from the ci.yml lane
# table (scripts/ci/lanes.py): a lane's own paths select its `hook` component.
# Each selected component's .githooks/pre-commit runs inside that component's
# Nix flake, so the tools match CI. Without Nix the same commands run from PATH
# with a warning. Only fast, hermetic checks run here (format, lint, type
# checks, unit tests); integration suites, cargo-deny, nix flake check and
Expand Down Expand Up @@ -48,9 +49,8 @@ for candidate in python3 /usr/bin/python3 /run/current-system/sw/bin/python3; do
done
[ -n "$PYTHON" ] || hook_fail "python3 is required to classify staged paths (it is in every Maple Nix shell)."

SELECTED=$(git diff --cached --name-only -z --diff-filter=ACMR \
| "$PYTHON" "$REPO_ROOT/scripts/ci/hook_change_detection.py" --selected) \
|| hook_fail "hook_change_detection.py failed."
SELECTED=$("$PYTHON" "$REPO_ROOT/scripts/ci/plan.py" hook --staged) \
|| hook_fail "scripts/ci/plan.py hook failed."
SELECTED=$(printf '%s\n' "$SELECTED" | tr '\n' ' ')

if [ -n "${MAPLE_HOOK_ONLY:-}" ]; then
Expand Down
16 changes: 7 additions & 9 deletions .githooks/repo-checks
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/bin/sh
# Repository-level pre-commit checks: workflow lint and CI script unit tests.
# Runs from the repository root inside the root flake's ci shell.
# CI equivalent: repository-checks.yml (nix flake check), minus the slow
# release-gates and toolchain checks.
# Repository-level pre-commit checks: workflow lint and the CI script tests.
# Runs from the repository root inside the root flake's ci shell whenever the
# staged paths select the ci.yml repo lane. That lane runs every root flake
# check (`just lane repo`); the release gates and toolchain checks stay there.

set -u
. "${MAPLE_HOOK_REPO_ROOT:?}/.githooks/lib/common.sh"
Expand All @@ -23,9 +23,7 @@ if [ -n "$workflows" ]; then
done
fi

if [ -n "$(hook_staged_matching '^(scripts/ci/|\.github/workflows/|flake\.(nix|lock)$)')" ]; then
hook_require python3
hook_run python3 -I -m unittest discover -s scripts/ci -p 'test_*.py'
fi
hook_require python3
hook_run python3 -I -m unittest discover -s scripts/ci -p 'test_*.py'

hook_log "Reminder: run 'nix flake check --no-update-lock-file' before pushing flake, workflow, or release changes."
hook_log "Reminder: run 'just lane repo' (nix flake check) before pushing flake, workflow, or release changes."
1 change: 1 addition & 0 deletions .github/actionlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ self-hosted-runner:
- blacksmith-8vcpu-windows-2025
- blacksmith-6vcpu-macos-26
- blacksmith-8vcpu-ubuntu-2404
- blacksmith-32vcpu-ubuntu-2404
70 changes: 70 additions & 0 deletions .github/actions/setup-agent-windows/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Set up the Agent's Windows toolchain
description: >-
Windows has no Nix: pinned Rust and MSVC, native Perl for the static crypto
build and a smoke-tested linker. Release builds also select the GPUI shader
compiler and install the pinned notice generator.
inputs:
release:
description: Also select fxc and install cargo-about, for release packages
default: "false"
runs:
using: composite
steps:
- name: Install pinned Windows Rust
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable action, exact toolchain below
with:
# Equal to AGENT_WINDOWS_RUST in scripts/ci/lanes.py; agent-lint checks
# that the Agent flake's rustc still reports this version.
toolchain: 1.98.0
components: clippy

- name: Select Windows MSVC and SDK
uses: ilammy/msvc-dev-cmd@a102174a2b586eec2ea151a69e6fd14404a8ce7c # v1.13.0
with:
arch: x64

- name: Select native Windows Perl for the static crypto build
shell: bash
run: |
native_perl=/c/Strawberry/perl/bin/perl.exe
if [ ! -x "$native_perl" ]; then
echo 'Native Windows Perl is missing from the runner' >&2
exit 1
fi
"$native_perl" -MLocale::Maketext::Simple -MIPC::Cmd -e 'die "OpenSSL requires native Windows Perl\n" unless $^O eq "MSWin32"; print "$^V\n"'
# Pin the native executable across later Git Bash steps and Rust's
# process resolver instead of depending on their PATH ordering.
native_perl_windows="$(cygpath -m "$native_perl")"
printf 'OPENSSL_SRC_PERL=%s\n' "$native_perl_windows" >> "$GITHUB_ENV"
# openssl-src uses NASM if available and otherwise selects no-asm.

- name: Pin and smoke-test the selected Windows linker
shell: bash
working-directory: apps/maple-agent
run: python3 ../../scripts/ci/agent_windows.py

- name: Select the SDK shader compiler for release GPUI
if: inputs.release == 'true'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
if (-not $env:WindowsSdkVerBinPath) { throw 'Selected Windows SDK binary path is missing' }
$fxc = Join-Path $env:WindowsSdkVerBinPath 'x64/fxc.exe'
if (-not (Test-Path -LiteralPath $fxc -PathType Leaf)) { throw "Windows SDK shader compiler is missing: $fxc" }
Write-Host "Selected HLSL compiler: $fxc"
"GPUI_FXC_PATH=$fxc" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8

- name: Install pinned Windows notice generator
if: inputs.release == 'true'
shell: bash
run: |
set -euo pipefail
(
cd "$RUNNER_TEMP"
curl --fail --location --show-error --silent \
https://github.com/EmbarkStudios/cargo-about/releases/download/0.9.0/cargo-about-0.9.0-x86_64-pc-windows-msvc.tar.gz \
--output cargo-about-0.9.0.tar.gz
printf '%s %s\n' 87daca52ad79980b42b2de6acdec84e143be41e2d4bbebedba0d0c90c49b055a cargo-about-0.9.0.tar.gz | sha256sum --check -
tar -xzf cargo-about-0.9.0.tar.gz
)
printf '%s\n' "$RUNNER_TEMP/cargo-about-0.9.0-x86_64-pc-windows-msvc" >> "$GITHUB_PATH"
15 changes: 15 additions & 0 deletions .github/actions/setup-nix/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Set up pinned Nix
description: >-
Install Determinate Nix 3.23.3 (pinned by commit) and verify the runtime,
exactly as the inline copies in the other workflows do.
runs:
using: composite
steps:
- name: Install pinned Nix environment
uses: DeterminateSystems/determinate-nix-action@96c5ee93a9dddf2830d31e1da6ec719077336c28 # v3.23.3
with:
source-tag: v3.23.3
github-token: ""
- name: Verify pinned Nix runtime
shell: bash
run: bash scripts/ci/verify-nix-runtime.sh
Loading
Loading