Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
ce99ae5
Rebase scripts/ci/detect_ci_change_areas.py workload-profile changes …
teamleaderleo Sep 21, 2026
7f97ae5
Rebase workload profile guards onto current main
teamleaderleo Sep 21, 2026
3976f4f
Carry fleet enrollment boundary onto current runner docs
teamleaderleo Sep 21, 2026
1ff2396
Rebase scripts/select-ci-xcode.sh workload-profile changes onto curre…
teamleaderleo Sep 21, 2026
a6cac9e
Rebase tests/test_ci_change_areas.py workload-profile changes onto cu…
teamleaderleo Sep 21, 2026
79372aa
Rebase tests/test_ci_xcode_selection_fast_path.sh workload-profile ch…
teamleaderleo Sep 21, 2026
1f819ac
Rebase docs/fleet-enrollment.md onto current main
teamleaderleo Sep 21, 2026
3a0516e
Rebase docs/workload-profiles.md onto current main
teamleaderleo Sep 21, 2026
8a5b2af
Rebase scripts/ci/cmux-workload-profiles.json onto current main
teamleaderleo Sep 21, 2026
82a9c42
Rebase scripts/ci/cmux_workload_profile.py onto current main
teamleaderleo Sep 21, 2026
a5fa129
Rebase scripts/ci/workloads/ci-guard.sh onto current main
teamleaderleo Sep 21, 2026
3fedcf3
Rebase scripts/ci/workloads/macos-app-host-test-shard.sh onto current…
teamleaderleo Sep 21, 2026
c8abfaa
Rebase scripts/ci/workloads/macos-compile-admission.sh onto current main
teamleaderleo Sep 21, 2026
af9f942
Rebase scripts/ci/workloads/macos-dev-check.sh onto current main
teamleaderleo Sep 21, 2026
82a9f81
Rebase tests/test_ci_workload_profiles.py onto current main
teamleaderleo Sep 21, 2026
cd47ea7
ci: publish workload result inside private state root
teamleaderleo Sep 21, 2026
7c31f6a
docs: keep workload results inside private state
teamleaderleo Sep 21, 2026
825790f
docs: keep fleet semantic result in private state
teamleaderleo Sep 21, 2026
0905ea7
docs: describe trusted workload result parents accurately
teamleaderleo Sep 21, 2026
cc9dca1
test dirty initialized submodule rejection
teamleaderleo Sep 21, 2026
b9c699d
workloads: serialize shared checkout build mutations
teamleaderleo Sep 21, 2026
51e551c
workloads: revalidate source and runtime inputs after execution
teamleaderleo Sep 21, 2026
06b619c
workloads: restore explicit materialized submodule fence
teamleaderleo Sep 21, 2026
a09aaa4
test: fence checkout and post-run identity drift
teamleaderleo Sep 21, 2026
0fa737f
Restore reviewed dirty-submodule status contract
teamleaderleo Sep 21, 2026
f320b59
Align submodule fixtures with explicit worktree validation
teamleaderleo Sep 21, 2026
3cca325
docs: require post-run fleet capability re-observation
teamleaderleo Sep 21, 2026
302a4a9
Keep fleet finalization aligned with merged Glaeda
teamleaderleo Sep 21, 2026
8839f6a
docs: bind fleet finalization to fresh post-run bootstrap
teamleaderleo Sep 21, 2026
a0b536c
docs: explain workload checkout lease and post-run revalidation
teamleaderleo Sep 21, 2026
4bd48bd
test: keep distinct toolchain integrity coverage
teamleaderleo Sep 21, 2026
a1efe1c
docs: bind fleet acceptance to Glaeda local execution
teamleaderleo Sep 21, 2026
1dc53de
docs: finish local acceptance runbook
teamleaderleo Sep 21, 2026
0808b8a
docs: fail fast during local fleet acceptance
teamleaderleo Sep 21, 2026
8220b9c
Decouple workload profiles from fleet activation
teamleaderleo Sep 21, 2026
8b05a9d
workloads: use canonical tagged reload for dev check
teamleaderleo Sep 21, 2026
7bf8f27
workloads: validate tagged dev identity through shared helper
teamleaderleo Sep 21, 2026
796e16e
test: enforce tagged developer workload build
teamleaderleo Sep 21, 2026
137f526
docs: describe tagged dev-check workload accurately
teamleaderleo Sep 21, 2026
8bd4eac
Activate merged Glaeda v2 fleet acceptance flow
teamleaderleo Sep 21, 2026
c9608cf
docs: gate fleet activation on Glaeda environment repair
teamleaderleo Sep 21, 2026
dfd4711
docs: activate fleet eligibility through repaired Glaeda boundary
teamleaderleo Sep 21, 2026
683d2dd
Merge main into codex/cmux-workload-profiles and resolve CI routing o…
teamleaderleo Sep 21, 2026
27b2dba
ci: settle workload groups before reaping
teamleaderleo Sep 21, 2026
bbe3e5e
ci: validate sanitized dev-check bundle path
teamleaderleo Sep 21, 2026
90b7268
test: cover unreaped settlement and tag slug
teamleaderleo Sep 21, 2026
d6ff999
Clean source-drift workload fixture
teamleaderleo Sep 21, 2026
beab041
test: mock unreaped child wait boundary
teamleaderleo Sep 21, 2026
32f9c28
ci: assign workload profile guards to CI group
teamleaderleo Sep 21, 2026
926bbad
Finalize persistent-Mac compile fallback routing (#13431)
teamleaderleo Sep 21, 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
129 changes: 128 additions & 1 deletion .github/workflows/ci-guards.yml

Large diffs are not rendered by default.

173 changes: 70 additions & 103 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ jobs:
compile_admitted: ${{ steps.admitted.outputs.compile_admitted }}
source_tree: ${{ steps.source-identity.outputs.tree }}
source_parent1: ${{ steps.source-identity.outputs.parent1 }}
source_identity_valid: ${{ steps.source-identity.outputs.valid }}
permissions:
actions: read
contents: read
Expand All @@ -70,14 +71,25 @@ jobs:
- name: Record GitHub-selected source identity
id: source-identity
run: |
set -euo pipefail
set -u
valid=false
tree=""
parent1=""
read -r commit parent1 parent2 extra <<EOF
$(git rev-list --parents -n1 HEAD)
EOF
[ "$commit" = "$GITHUB_SHA" ]
[ -z "${extra:-}" ]
echo "tree=$(git rev-parse 'HEAD^{tree}')" >> "$GITHUB_OUTPUT"
echo "parent1=${parent1:-}" >> "$GITHUB_OUTPUT"
if [ "$commit" = "$GITHUB_SHA" ] && [ -n "${parent1:-}" ] && [ -z "${extra:-}" ]; then
if tree="$(git rev-parse 'HEAD^{tree}')"; then
valid=true
fi
else
echo "Persistent routing source identity is unavailable; hosted admission remains authoritative." >&2
fi
{
echo "valid=$valid"
echo "tree=$tree"
echo "parent1=${parent1:-}"
} >> "$GITHUB_OUTPUT"

- name: Detect CI change areas
id: detect
Expand Down Expand Up @@ -292,7 +304,7 @@ jobs:
# cancellation authority live in persistent-macos-router.yml on main.
- name: Publish persistent Mac route request
id: persistent-route-request
if: ${{ github.event_name == 'pull_request' && steps.detect.outputs.macos == 'true' && steps.admitted.outputs.compile_admitted != 'true' && (vars.CI_PERSISTENT_MAC_COMPILE == 'pilot' || vars.CI_PERSISTENT_MAC_COMPILE == 'all' || vars.CI_PERSISTENT_MAC_COMPILE == 'on' || vars.CI_PERSISTENT_MAC_COMPILE == 'true' || vars.CI_PERSISTENT_MAC_COMPILE == '1') && github.event.pull_request.head.repo.full_name == github.repository && (github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.author_association == 'OWNER') }}
if: ${{ github.event_name == 'pull_request' && steps.source-identity.outputs.valid == 'true' && steps.detect.outputs.macos == 'true' && steps.admitted.outputs.compile_admitted != 'true' && (vars.CI_PERSISTENT_MAC_COMPILE == 'pilot' || vars.CI_PERSISTENT_MAC_COMPILE == 'all' || vars.CI_PERSISTENT_MAC_COMPILE == 'on' || vars.CI_PERSISTENT_MAC_COMPILE == 'true' || vars.CI_PERSISTENT_MAC_COMPILE == '1') && github.event.pull_request.head.repo.full_name == github.repository && (github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.author_association == 'OWNER') }}
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
Expand Down Expand Up @@ -2319,96 +2331,11 @@ jobs:
print(f"{name}: {data['result']}")
PY

persistent-mac-compile-route:
name: Persistent Mac compile route
needs:
- changes
- linux-preflight
if: ${{ !cancelled() && needs.changes.result == 'success' && needs.linux-preflight.result == 'success' && needs.changes.outputs.macos == 'true' && needs.changes.outputs.compile_admitted != 'true' && github.event_name == 'pull_request' && (vars.CI_PERSISTENT_MAC_COMPILE == 'pilot' || vars.CI_PERSISTENT_MAC_COMPILE == 'all' || vars.CI_PERSISTENT_MAC_COMPILE == 'on' || vars.CI_PERSISTENT_MAC_COMPILE == 'true' || vars.CI_PERSISTENT_MAC_COMPILE == '1') && github.event.pull_request.head.repo.full_name == github.repository && (github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.author_association == 'OWNER') }}
runs-on: ${{ vars.LINUX_RUNNER || 'blacksmith-4vcpu-ubuntu-2404' }}
timeout-minutes: 12
permissions:
actions: read
contents: read
pull-requests: read
outputs:
use_persistent: ${{ steps.route.outputs.use_persistent }}
fallback_reason: ${{ steps.route.outputs.fallback_reason }}
producer_run_id: ${{ steps.route.outputs.producer_run_id }}
artifact_id: ${{ steps.route.outputs.artifact_id }}
queue_to_start_seconds: ${{ steps.route.outputs.queue_to_start_seconds }}
producer_allocated_seconds: ${{ steps.route.outputs.producer_allocated_seconds }}
route_wall_seconds: ${{ steps.route.outputs.route_wall_seconds }}
source_tree: ${{ steps.source.outputs.tree }}
steps:
- name: Checkout route observer
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Bind the GitHub-selected source
id: source
env:
SOURCE_SHA: ${{ github.sha }}
SOURCE_TREE: ${{ needs.changes.outputs.source_tree }}
SOURCE_PARENT1: ${{ needs.changes.outputs.source_parent1 }}
run: |
set -euo pipefail
[ -n "$SOURCE_SHA" ] && [ -n "$SOURCE_TREE" ] && [ -n "$SOURCE_PARENT1" ]
{
echo "tree=$SOURCE_TREE"
echo "parent1=$SOURCE_PARENT1"
} >> "$GITHUB_OUTPUT"

- name: Observe persistent compile or use hosted fallback
id: route
env:
GH_TOKEN: ${{ github.token }}
CI_PERSISTENT_MAC_COMPILE: ${{ vars.CI_PERSISTENT_MAC_COMPILE }}
CI_PERSISTENT_MAC_COMPILE_COHORT: ${{ vars.CI_PERSISTENT_MAC_COMPILE_COHORT }}
CI_PERSISTENT_MAC_QUEUE_SECONDS: ${{ vars.CI_PERSISTENT_MAC_QUEUE_SECONDS }}
CI_PERSISTENT_MAC_EXECUTION_SECONDS: ${{ vars.CI_PERSISTENT_MAC_EXECUTION_SECONDS }}
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
HEAD_REF: ${{ github.head_ref }}
AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
SOURCE_TREE: ${{ steps.source.outputs.tree }}
SOURCE_PARENT1: ${{ steps.source.outputs.parent1 }}
run: |
set -euo pipefail
route_started="$(python3 -c 'import time; print(time.monotonic())')"
queue_seconds="${CI_PERSISTENT_MAC_QUEUE_SECONDS:-90}"
execution_seconds="${CI_PERSISTENT_MAC_EXECUTION_SECONDS:-480}"
python3 scripts/ci/persistent_mac_route.py \
--observe-only \
--event-name "$GITHUB_EVENT_NAME" \
--selector "$CI_PERSISTENT_MAC_COMPILE" \
--cohort "$CI_PERSISTENT_MAC_COMPILE_COHORT" \
--repository "$GITHUB_REPOSITORY" \
--pr-number "$PR_NUMBER" \
--head-repository "$HEAD_REPOSITORY" \
--head-ref "$HEAD_REF" \
--author-association "$AUTHOR_ASSOCIATION" \
--head-sha "$HEAD_SHA" \
--source-sha "$GITHUB_SHA" \
--source-tree "$SOURCE_TREE" \
--source-parent1 "$SOURCE_PARENT1" \
--run-id "$GITHUB_RUN_ID" \
--run-attempt "$GITHUB_RUN_ATTEMPT" \
--queue-seconds "$queue_seconds" \
--execution-seconds "$execution_seconds" \
--github-output "$GITHUB_OUTPUT"
route_finished="$(python3 -c 'import time; print(time.monotonic())')"
route_wall="$(python3 -c 'import sys; print(round(float(sys.argv[2])-float(sys.argv[1]), 6))' "$route_started" "$route_finished")"
echo "route_wall_seconds=$route_wall" >> "$GITHUB_OUTPUT"

macos-compile-admission:
name: macOS compile admission
needs:
- changes
- linux-preflight
- persistent-mac-compile-route
# Spend one macOS slot proving that the app-host test product compiles
# before starting the six test shards. The shards download this run's
# build products and run test-without-building, so a compiler failure
Expand All @@ -2420,6 +2347,7 @@ jobs:
permissions:
contents: read
actions: read
pull-requests: read
outputs:
artifact_id: ${{ steps.upload-products.outputs.artifact-id }}
artifact_digest: ${{ steps.upload-products.outputs.artifact-digest }}
Expand Down Expand Up @@ -2591,25 +2519,64 @@ jobs:
echo "- macOS runner minutes saved: $(show "$REUSE_MACOS_MINUTES_SAVED")"
} >> "$GITHUB_STEP_SUMMARY"

- name: Observe persistent Mac compile candidate
id: persistent-route
if: ${{ steps.reuse-products.outputs.hit != 'true' && github.event_name == 'pull_request' && needs.changes.outputs.source_identity_valid == 'true' && (vars.CI_PERSISTENT_MAC_COMPILE == 'pilot' || vars.CI_PERSISTENT_MAC_COMPILE == 'all' || vars.CI_PERSISTENT_MAC_COMPILE == 'on' || vars.CI_PERSISTENT_MAC_COMPILE == 'true' || vars.CI_PERSISTENT_MAC_COMPILE == '1') && github.event.pull_request.head.repo.full_name == github.repository && (github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.author_association == 'OWNER') }}
env:
GH_TOKEN: ${{ github.token }}
CI_PERSISTENT_MAC_COMPILE: ${{ vars.CI_PERSISTENT_MAC_COMPILE }}
CI_PERSISTENT_MAC_COMPILE_COHORT: ${{ vars.CI_PERSISTENT_MAC_COMPILE_COHORT }}
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
HEAD_REF: ${{ github.head_ref }}
AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
SOURCE_TREE: ${{ needs.changes.outputs.source_tree }}
SOURCE_PARENT1: ${{ needs.changes.outputs.source_parent1 }}
run: |
set -euo pipefail
route_started="$(python3 -c 'import time; print(time.monotonic())')"
python3 scripts/ci/persistent_mac_route.py \
--observe-only \
--ready-only \
--event-name "$GITHUB_EVENT_NAME" \
--selector "$CI_PERSISTENT_MAC_COMPILE" \
--cohort "$CI_PERSISTENT_MAC_COMPILE_COHORT" \
--repository "$GITHUB_REPOSITORY" \
--pr-number "$PR_NUMBER" \
--head-repository "$HEAD_REPOSITORY" \
--head-ref "$HEAD_REF" \
--author-association "$AUTHOR_ASSOCIATION" \
--head-sha "$HEAD_SHA" \
--source-sha "$GITHUB_SHA" \
--source-tree "$SOURCE_TREE" \
--source-parent1 "$SOURCE_PARENT1" \
--run-id "$GITHUB_RUN_ID" \
--run-attempt "$GITHUB_RUN_ATTEMPT" \
--github-output "$GITHUB_OUTPUT"
route_finished="$(python3 -c 'import time; print(time.monotonic())')"
route_wall="$(python3 -c 'import sys; print(round(float(sys.argv[2])-float(sys.argv[1]), 6))' "$route_started" "$route_finished")"
echo "route_wall_seconds=$route_wall" >> "$GITHUB_OUTPUT"

- name: Download persistent Mac compile product
id: persistent-download
if: steps.reuse-products.outputs.hit != 'true' && needs.persistent-mac-compile-route.outputs.use_persistent == 'true'
if: steps.reuse-products.outputs.hit != 'true' && steps.persistent-route.outputs.use_persistent == 'true'
continue-on-error: true
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
artifact-ids: ${{ needs.persistent-mac-compile-route.outputs.artifact_id }}
artifact-ids: ${{ steps.persistent-route.outputs.artifact_id }}
path: ${{ runner.temp }}/persistent-mac-compile
github-token: ${{ github.token }}
repository: ${{ github.repository }}
run-id: ${{ needs.persistent-mac-compile-route.outputs.producer_run_id }}
run-id: ${{ steps.persistent-route.outputs.producer_run_id }}

- name: Revalidate persistent Mac compile product
id: persistent-restore
if: steps.reuse-products.outputs.hit != 'true' && steps.persistent-download.outcome == 'success'
continue-on-error: true
env:
EXPECTED_SOURCE_SHA: ${{ github.sha }}
EXPECTED_SOURCE_TREE: ${{ needs.persistent-mac-compile-route.outputs.source_tree }}
EXPECTED_SOURCE_TREE: ${{ needs.changes.outputs.source_tree }}
run: |
set -euo pipefail
[ -z "$(git status --porcelain --untracked-files=all)" ] || {
Expand Down Expand Up @@ -2874,11 +2841,11 @@ jobs:
id: admission-metrics
if: always() && !cancelled()
env:
ROUTE_USE_PERSISTENT: ${{ needs.persistent-mac-compile-route.outputs.use_persistent }}
ROUTE_REASON: ${{ needs.persistent-mac-compile-route.outputs.fallback_reason }}
QUEUE_TO_START_SECONDS: ${{ needs.persistent-mac-compile-route.outputs.queue_to_start_seconds }}
PRODUCER_ALLOCATED_SECONDS: ${{ needs.persistent-mac-compile-route.outputs.producer_allocated_seconds }}
ROUTE_WALL_SECONDS: ${{ needs.persistent-mac-compile-route.outputs.route_wall_seconds }}
ROUTE_USE_PERSISTENT: ${{ steps.persistent-route.outputs.use_persistent }}
ROUTE_REASON: ${{ steps.persistent-route.outputs.fallback_reason }}
QUEUE_TO_START_SECONDS: ${{ steps.persistent-route.outputs.queue_to_start_seconds }}
PRODUCER_ALLOCATED_SECONDS: ${{ steps.persistent-route.outputs.producer_allocated_seconds }}
ROUTE_WALL_SECONDS: ${{ steps.persistent-route.outputs.route_wall_seconds }}
PERSISTENT_HIT: ${{ steps.persistent-restore.outputs.hit }}
PERSISTENT_CLASS: ${{ steps.persistent-restore.outputs.classification }}
PERSISTENT_METRICS: ${{ steps.persistent-restore.outputs.metrics }}
Expand Down Expand Up @@ -2948,9 +2915,9 @@ jobs:
"artifact_publication_seconds": number("PUBLICATION_SECONDS"),
"route_wall_seconds": number("ROUTE_WALL_SECONDS"),
"required_admission_runner_seconds": number("ADMISSION_SECONDS"),
"total_macos_compile_admission_seconds": (
(number("ROUTE_WALL_SECONDS") or 0.0) + (number("ADMISSION_SECONDS") or 0.0)
),
# Route observation runs inside the already-allocated hosted
# admission job, so ADMISSION_SECONDS already contains it.
"total_macos_compile_admission_seconds": number("ADMISSION_SECONDS"),
"persistent_runner_allocated_seconds": number("PRODUCER_ALLOCATED_SECONDS"),
"hosted_admission_runner_allocated_seconds": number("ADMISSION_SECONDS"),
"product_published": os.environ.get("PRODUCT_PUBLISHED") == "true",
Expand Down
28 changes: 23 additions & 5 deletions docs/ci-runners.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ after revalidating the Git revision/tree, Xcode, SDK, architecture,
`Package.resolved`, submodules, Glaeda lineage evidence, warning budget, and
early CLI probes. Any dispatch, queue, execution, download, or validation miss
falls through to the existing hosted compile in that same required job.
The PR workflow never receives Actions write authority: `changes` publishes a
small exact-source request artifact, the default-branch
`persistent-macos-router.yml` workflow validates it against the live PR and
owns producer dispatch/cancellation, and the PR-side route job observes producer
state with read-only Actions permission.
The required hosted macOS job is allocated without waiting for the persistent
producer. It restores any exact reusable product first, then observes the
producer with read-only Actions permission before deciding whether to consume
the persistent artifact or compile hosted. That observation is nonblocking:
the producer is consumed only when its compile is already complete at the
decision point; an absent, queued, or running producer falls through to hosted
compilation immediately. The PR workflow never receives
Actions write authority: `changes` publishes a small exact-source request
artifact, and the default-branch `persistent-macos-router.yml` workflow
validates it against the live PR and owns producer dispatch/cancellation.

The producer is `workflow_dispatch`-only and requires the
`cmux-persistent-compile` runner group plus the dedicated
Expand Down Expand Up @@ -185,6 +190,19 @@ The fleet-label guard allows Tart labels only as exact manual canary choices.
Required jobs continue to reference repository variables, so cutover and
break-glass remain configuration changes instead of workflow edits.

## CMUX-owned machine enrollment

Persistent CMUX hardware can be enrolled for repository-owned semantic workloads without becoming a direct required-CI runner. See [fleet-enrollment.md](fleet-enrollment.md).

The first reviewed role bindings are:

- `cmux_macos_native_build -> cmux.macos.dev-check@1`
- `cmux_linux_ci -> cmux.ci.guard@1`

CMUX owns those workload profiles and their pass/fail semantics through `scripts/ci/cmux_workload_profile.py`. Glaeda owns the machine enrollment record, candidate eligibility, local admission, and acceptance receipt that binds the exact canonical `cmux-workload-result/v1` bytes.

Enrollment does not register a GitHub runner or change repository runner variables. Required CI continues to use the policy above until a separately reviewed CI routing change promotes a fleet role.

## Direct physical-host runner boundary

Required GUI, test, Release, signing, and ordinary macOS jobs never route to
Expand Down
Loading
Loading