From 2628e1cb66835e6f5ac3d7f0eac7e7ad4b4a84db Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Fri, 14 Aug 2026 00:29:50 -0700 Subject: [PATCH 1/5] ci: use native Blacksmith Testbox actions --- .github/actionlint.yaml | 1 + .github/workflows/cmux-tui-testbox.yml | 86 ++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 .github/workflows/cmux-tui-testbox.yml diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml index a9ac6b01ed9..4402dc219bd 100644 --- a/.github/actionlint.yaml +++ b/.github/actionlint.yaml @@ -19,4 +19,5 @@ self-hosted-runner: # Linux: Blacksmith primary (LINUX_RUNNER), WarpBuild overflow fallback. - blacksmith-4vcpu-ubuntu-2404 - blacksmith-8vcpu-ubuntu-2404 + - blacksmith-32vcpu-ubuntu-2404 - warp-ubuntu-latest-x64-4x diff --git a/.github/workflows/cmux-tui-testbox.yml b/.github/workflows/cmux-tui-testbox.yml new file mode 100644 index 00000000000..f4eb8560df7 --- /dev/null +++ b/.github/workflows/cmux-tui-testbox.yml @@ -0,0 +1,86 @@ +name: cmux-tui Testbox + +on: + workflow_dispatch: + inputs: + testbox_id: + description: "Testbox session ID supplied by Blacksmith warmup" + required: false + default: "" + type: string + +# Candidate source is absent during hydration. After this job reports ready, +# the Blacksmith CLI syncs candidate changes into this same vendor Testbox job. +# Candidate code can then read Testbox-scoped state by Testbox design. This job +# gets no cmux repository secret, OIDC permission, GitHub environment, or GitHub +# token permission. The job timeout and `blacksmith testbox stop --id ` +# bound spend. Stronger hostile-code token isolation needs a Blacksmith +# primitive and is not claimed here. +permissions: {} + +jobs: + cmux-tui-testbox: + name: cmux-tui-testbox + if: github.ref == 'refs/heads/main' + runs-on: blacksmith-32vcpu-ubuntu-2404 + timeout-minutes: 120 + steps: + - name: Checkout trusted main + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.sha }} + fetch-depth: 1 + persist-credentials: false + submodules: false + + - name: Begin Testbox + uses: useblacksmith/begin-testbox@ad4fbed427d8251a90e589c0886dd3d07ec7727f + with: + testbox_id: ${{ inputs.testbox_id }} + + - name: Init Ghostty submodule + run: git submodule update --init --depth 1 ghostty + + - name: Install Linux build dependencies + run: | + sudo apt-get update + sudo apt-get install -y clang libclang-dev pkg-config + + - name: Install Zig + run: ./scripts/install-zig-ci.sh + + - name: Set up pinned Rust + uses: ./.github/actions/setup-cmux-tui-rust + + # The CLI uses rsync deletion, and its current managed-file contract does + # not prove that an ignored workspace target survives. Keep the target and + # Cargo cache outside the sync root. Candidate commands can override this + # Cargo configuration, so it is a performance setting, not a boundary. + - name: Configure persistent Cargo target + shell: bash + run: | + set -euo pipefail + cargo_target=/opt/cmux-testbox/cargo-target + cargo_config="$HOME/.cargo/config.toml" + legacy_cargo_config="$HOME/.cargo/config" + sudo install -d -m 0755 -o "$(id -u)" -g "$(id -g)" "$cargo_target" + mkdir -p "$HOME/.cargo" + if [[ -e "$cargo_config" || -e "$legacy_cargo_config" ]]; then + echo "::error::refusing to overwrite existing global Cargo configuration" >&2 + exit 1 + fi + printf '[build]\ntarget-dir = "%s"\n' "$cargo_target" > "$cargo_config" + chmod 0600 "$cargo_config" + + - name: Warm cmux-tui Cargo build + working-directory: cmux-tui + run: cargo build -p cmux-tui --locked + + - name: Verify persistent Cargo target + run: | + test -x /opt/cmux-testbox/cargo-target/debug/cmux-tui + test ! -e cmux-tui/target + + - name: Run Testbox + if: always() + uses: useblacksmith/run-testbox@3f60ff9ceb2c10c3feefa87dc0c6490cffae059d From 75a2bb90044ae2445964a6cd9f183cb24b2e62cb Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Fri, 14 Aug 2026 00:36:20 -0700 Subject: [PATCH 2/5] ci: checkout Testbox main anonymously --- .github/workflows/cmux-tui-testbox.yml | 52 ++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cmux-tui-testbox.yml b/.github/workflows/cmux-tui-testbox.yml index f4eb8560df7..6ee376dbb69 100644 --- a/.github/workflows/cmux-tui-testbox.yml +++ b/.github/workflows/cmux-tui-testbox.yml @@ -25,19 +25,55 @@ jobs: runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: 120 steps: - - name: Checkout trusted main - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - ref: ${{ github.sha }} - fetch-depth: 1 - persist-credentials: false - submodules: false + - name: Require protected main dispatch + shell: bash + env: + EXPECTED_MAIN_SHA: ${{ github.sha }} + run: | + set -euo pipefail + if [[ ! "$EXPECTED_MAIN_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::github.sha must be a lowercase 40-character commit ID" >&2 + exit 1 + fi + if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then + echo "::error::refusing Testbox dispatch from $GITHUB_REF" >&2 + exit 1 + fi + # Begin writes the Testbox state before checkout and repository setup. The + # pinned final action uses that state to report a later setup failure. - name: Begin Testbox uses: useblacksmith/begin-testbox@ad4fbed427d8251a90e589c0886dd3d07ec7727f with: testbox_id: ${{ inputs.testbox_id }} + - name: Checkout trusted main without a repository token + shell: bash + env: + EXPECTED_MAIN_SHA: ${{ github.sha }} + run: | + set -euo pipefail + git -c init.defaultBranch=main init . + git remote add origin https://github.com/manaflow-ai/cmux.git + GIT_ASKPASS=/bin/false GIT_TERMINAL_PROMPT=0 \ + timeout --signal=TERM --kill-after=10s 180s \ + git -c credential.helper= -c http.extraHeader= \ + -c http.lowSpeedLimit=1024 -c http.lowSpeedTime=30 \ + fetch --no-tags --depth=1 origin "$EXPECTED_MAIN_SHA" + + fetched_main_sha="$(git rev-parse --verify 'FETCH_HEAD^{commit}')" + if [[ "$fetched_main_sha" != "$EXPECTED_MAIN_SHA" ]]; then + echo "::error::anonymous fetch did not return the dispatched main commit" >&2 + exit 1 + fi + git checkout --detach "$fetched_main_sha" + test "$(git rev-parse --verify HEAD)" = "$EXPECTED_MAIN_SHA" + test -z "$(git symbolic-ref -q HEAD || true)" + if git config --local --get-regexp '(^|\.)http\..*extraheader$' >/dev/null 2>&1; then + echo "::error::anonymous checkout retained an HTTP authorization header" >&2 + exit 1 + fi + - name: Init Ghostty submodule run: git submodule update --init --depth 1 ghostty @@ -82,5 +118,7 @@ jobs: test ! -e cmux-tui/target - name: Run Testbox + # The pinned action reads job.status and reports hydration_failed when + # any setup step after Begin fails. if: always() uses: useblacksmith/run-testbox@3f60ff9ceb2c10c3feefa87dc0c6490cffae059d From 3a0de81ecfb635e41fc5a6ceafc573d85c569c50 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Fri, 14 Aug 2026 00:49:59 -0700 Subject: [PATCH 3/5] ci: scope Testbox broker to protected tag --- .github/workflows/cmux-tui-testbox.yml | 75 +++++++++++++++++--------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/.github/workflows/cmux-tui-testbox.yml b/.github/workflows/cmux-tui-testbox.yml index 6ee376dbb69..507aed7dbb5 100644 --- a/.github/workflows/cmux-tui-testbox.yml +++ b/.github/workflows/cmux-tui-testbox.yml @@ -9,33 +9,34 @@ on: default: "" type: string -# Candidate source is absent during hydration. After this job reports ready, -# the Blacksmith CLI syncs candidate changes into this same vendor Testbox job. -# Candidate code can then read Testbox-scoped state by Testbox design. This job -# gets no cmux repository secret, OIDC permission, GitHub environment, or GitHub -# token permission. The job timeout and `blacksmith testbox stop --id ` -# bound spend. Stronger hostile-code token isolation needs a Blacksmith -# primitive and is not claimed here. +# External prerequisite: create cmux-testbox-broker-v1 at the reviewed merge +# commit, then protect that exact tag from update and deletion. Dispatch with +# `--ref cmux-testbox-broker-v1`. github.sha identifies the broker tag commit; +# it does not select source for hydration. +# +# After this job reports ready, the Blacksmith CLI syncs candidate changes into +# this tag-scoped vendor Testbox job. Candidate code can read Testbox state and +# tag-job Actions runtime and results credentials. It can read any default-branch +# cache available to tag runs and consume shared cache quota. GitHub cache scope +# prevents a cache created by this tag from being restored by main. This job gets +# no cmux repository secret, OIDC permission, GitHub environment, or GITHUB_TOKEN +# permission. The 120-minute timeout and explicit +# `blacksmith testbox stop --id ` command bound spend. Stronger hostile-code +# runtime isolation needs a Blacksmith primitive and is not claimed here. permissions: {} jobs: cmux-tui-testbox: name: cmux-tui-testbox - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/tags/cmux-testbox-broker-v1' runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: 120 steps: - - name: Require protected main dispatch + - name: Require protected broker tag dispatch shell: bash - env: - EXPECTED_MAIN_SHA: ${{ github.sha }} run: | set -euo pipefail - if [[ ! "$EXPECTED_MAIN_SHA" =~ ^[0-9a-f]{40}$ ]]; then - echo "::error::github.sha must be a lowercase 40-character commit ID" >&2 - exit 1 - fi - if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then + if [[ "$GITHUB_REF" != "refs/tags/cmux-testbox-broker-v1" ]]; then echo "::error::refusing Testbox dispatch from $GITHUB_REF" >&2 exit 1 fi @@ -49,25 +50,49 @@ jobs: - name: Checkout trusted main without a repository token shell: bash - env: - EXPECTED_MAIN_SHA: ${{ github.sha }} run: | set -euo pipefail + repository_url=https://github.com/manaflow-ai/cmux.git + main_ref=refs/heads/main + git_network_config=( + -c credential.helper= + -c http.extraHeader= + -c http.lowSpeedLimit=1024 + -c http.lowSpeedTime=30 + ) + + main_listing="$( + GIT_ASKPASS=/bin/false GIT_TERMINAL_PROMPT=0 \ + timeout --signal=TERM --kill-after=10s 60s \ + git "${git_network_config[@]}" \ + ls-remote --exit-code --refs "$repository_url" "$main_ref" + )" + main_records=() + mapfile -t main_records <<< "$main_listing" + if [[ "${#main_records[@]}" -ne 1 ]]; then + echo "::error::public main did not resolve to exactly one ref" >&2 + exit 1 + fi + IFS=$'\t' read -r main_sha resolved_ref extra <<< "${main_records[0]}" + if [[ ! "$main_sha" =~ ^[0-9a-f]{40}$ || "$resolved_ref" != "$main_ref" || -n "$extra" ]]; then + echo "::error::public main ref has an invalid identity" >&2 + exit 1 + fi + git -c init.defaultBranch=main init . - git remote add origin https://github.com/manaflow-ai/cmux.git + git remote add origin "$repository_url" GIT_ASKPASS=/bin/false GIT_TERMINAL_PROMPT=0 \ timeout --signal=TERM --kill-after=10s 180s \ - git -c credential.helper= -c http.extraHeader= \ - -c http.lowSpeedLimit=1024 -c http.lowSpeedTime=30 \ - fetch --no-tags --depth=1 origin "$EXPECTED_MAIN_SHA" + git "${git_network_config[@]}" \ + fetch --no-tags --depth=1 origin "$main_sha" fetched_main_sha="$(git rev-parse --verify 'FETCH_HEAD^{commit}')" - if [[ "$fetched_main_sha" != "$EXPECTED_MAIN_SHA" ]]; then - echo "::error::anonymous fetch did not return the dispatched main commit" >&2 + if [[ "$fetched_main_sha" != "$main_sha" ]]; then + echo "::error::anonymous fetch did not return resolved public main" >&2 exit 1 fi git checkout --detach "$fetched_main_sha" - test "$(git rev-parse --verify HEAD)" = "$EXPECTED_MAIN_SHA" + test "$(git rev-parse --verify HEAD)" = "$main_sha" test -z "$(git symbolic-ref -q HEAD || true)" if git config --local --get-regexp '(^|\.)http\..*extraheader$' >/dev/null 2>&1; then echo "::error::anonymous checkout retained an HTTP authorization header" >&2 From 3b0a3e7491fe774d1c57183bd51d3882fd347d89 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Fri, 14 Aug 2026 01:04:12 -0700 Subject: [PATCH 4/5] ci: validate protected Testbox dispatch --- .github/workflows/cmux-tui-testbox.yml | 31 +++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/.github/workflows/cmux-tui-testbox.yml b/.github/workflows/cmux-tui-testbox.yml index 507aed7dbb5..c7d4c25a5c9 100644 --- a/.github/workflows/cmux-tui-testbox.yml +++ b/.github/workflows/cmux-tui-testbox.yml @@ -11,33 +11,44 @@ on: # External prerequisite: create cmux-testbox-broker-v1 at the reviewed merge # commit, then protect that exact tag from update and deletion. Dispatch with -# `--ref cmux-testbox-broker-v1`. github.sha identifies the broker tag commit; -# it does not select source for hydration. +# `--ref cmux-testbox-broker-v1`. Blacksmith Branch Protected Caches must be ON. +# github.sha identifies the broker tag commit; it does not select source for +# hydration. # # After this job reports ready, the Blacksmith CLI syncs candidate changes into # this tag-scoped vendor Testbox job. Candidate code can read Testbox state and # tag-job Actions runtime and results credentials. It can read any default-branch # cache available to tag runs and consume shared cache quota. GitHub cache scope -# prevents a cache created by this tag from being restored by main. This job gets -# no cmux repository secret, OIDC permission, GitHub environment, or GITHUB_TOKEN -# permission. The 120-minute timeout and explicit -# `blacksmith testbox stop --id ` command bound spend. Stronger hostile-code -# runtime isolation needs a Blacksmith primitive and is not claimed here. +# and Blacksmith Branch Protected Caches prevent main from restoring a cache +# created by this tag. This job gets no cmux repository secret, OIDC permission, +# GitHub environment, or GITHUB_TOKEN permission. The 120-minute timeout applies +# to each run. `blacksmith testbox stop --id ` ends one run early. Stronger +# hostile-code runtime isolation needs a Blacksmith primitive and is not claimed. permissions: {} jobs: cmux-tui-testbox: name: cmux-tui-testbox - if: github.ref == 'refs/tags/cmux-testbox-broker-v1' + if: github.ref == 'refs/tags/cmux-testbox-broker-v1' && github.ref_protected == true runs-on: blacksmith-32vcpu-ubuntu-2404 timeout-minutes: 120 steps: - - name: Require protected broker tag dispatch + - name: Validate protected broker dispatch shell: bash + env: + TESTBOX_ID: ${{ inputs.testbox_id }} run: | set -euo pipefail if [[ "$GITHUB_REF" != "refs/tags/cmux-testbox-broker-v1" ]]; then - echo "::error::refusing Testbox dispatch from $GITHUB_REF" >&2 + echo "::error::Testbox dispatch ref invariant failed" >&2 + exit 1 + fi + if [[ "${GITHUB_REF_PROTECTED:-}" != "true" ]]; then + echo "::error::Testbox dispatch protection invariant failed" >&2 + exit 1 + fi + if [[ -n "$TESTBOX_ID" && ! "$TESTBOX_ID" =~ ^tbx_[0-9a-hjkmnp-tv-z]{26}$ ]]; then + echo "::error::Testbox ID invariant failed" >&2 exit 1 fi From d95db1ef5bf8ab5802703f35ebd4a1c3fffb48de Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Fri, 14 Aug 2026 01:11:22 -0700 Subject: [PATCH 5/5] ci: skip Testbox final action on cancellation --- .github/workflows/cmux-tui-testbox.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cmux-tui-testbox.yml b/.github/workflows/cmux-tui-testbox.yml index c7d4c25a5c9..ce281fbea0d 100644 --- a/.github/workflows/cmux-tui-testbox.yml +++ b/.github/workflows/cmux-tui-testbox.yml @@ -9,11 +9,14 @@ on: default: "" type: string -# External prerequisite: create cmux-testbox-broker-v1 at the reviewed merge -# commit, then protect that exact tag from update and deletion. Dispatch with -# `--ref cmux-testbox-broker-v1`. Blacksmith Branch Protected Caches must be ON. -# github.sha identifies the broker tag commit; it does not select source for -# hydration. +# External prerequisites, in order: +# 1. Activate an exact-tag ruleset for cmux-testbox-broker-v1 that restricts +# creation, update, and deletion, with only a narrow temporary bypass. +# 2. Use that bypass to create the tag at the reviewed merge SHA. Remove the +# bypass, then verify the tag SHA and active rules. +# 3. Verify Blacksmith Branch Protected Caches is ON. Only then warm with +# `--ref cmux-testbox-broker-v1`. github.sha identifies the broker tag commit; +# it does not select source for hydration. # # After this job reports ready, the Blacksmith CLI syncs candidate changes into # this tag-scoped vendor Testbox job. Candidate code can read Testbox state and @@ -155,6 +158,6 @@ jobs: - name: Run Testbox # The pinned action reads job.status and reports hydration_failed when - # any setup step after Begin fails. - if: always() + # any setup step after Begin fails. Cancellation skips the final action. + if: ${{ !cancelled() }} uses: useblacksmith/run-testbox@3f60ff9ceb2c10c3feefa87dc0c6490cffae059d