ci: warm cmux-tui Testboxes from a main-controlled broker

blacksmith testbox warmup resolves the workflow definition and the hydrated
source from one --ref, so a lane that warms a candidate branch runs that
branch's copy of the workflow before begin-testbox writes the Testbox auth
token into the job. A candidate could therefore delete its own guards.

Hydrate main only. The first step refuses any ref except refs/heads/main, and
no repository code runs before the token. A candidate revision reaches the box
afterwards through blacksmith testbox run, which syncs a maintainer's worktree
onto the warm VM and needs Blacksmith org credentials that already grant box
access, so it moves no trust boundary.

The hydrated commit and the benchmarked commit are now deliberately different.
The stage helper checks the setup marker for VM identity, runner class, and
toolchain completeness instead of source equality, records the hydrated ref and
SHA under a new "hydration" block, and still fails closed when the active Rust,
Cargo, or Zig differs from what warmed the caches.

The lane no longer needs BLACKSMITH_TESTBOX_REVIEWED_REF or
BLACKSMITH_TESTBOX_REVIEWED_SHA; the environment needs a deployment branch rule
of exactly main.
This commit is contained in:
Lawrence Chen
2026-08-17 17:20:31 -07:00
parent 0fc34d7067
commit f4222dec61
9 changed files with 2335 additions and 0 deletions
+1
View File
@@ -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
@@ -0,0 +1,366 @@
name: cmux-tui Rust Testbox setup
# Main-controlled broker. This workflow runs only from refs/heads/main and
# hydrates only main, so no candidate branch can edit the guards that run
# before begin-testbox exposes its auth token, and no candidate build script
# executes inside this token-bearing job. A candidate revision reaches the
# Testbox later, through `blacksmith testbox run`, which synchronizes a
# maintainer's local worktree onto the already-warm VM.
on:
workflow_dispatch:
inputs:
testbox_id:
description: "Testbox session ID supplied by blacksmith testbox warmup"
required: true
type: string
permissions: {}
concurrency:
# A Testbox is a mutable shared workspace. Serialize every request for the
# same ID so two dispatches cannot corrupt one cache.
group: cmux-tui-testbox-${{ inputs.testbox_id }}
cancel-in-progress: false
jobs:
cmux-tui-rust:
name: cmux-tui Rust setup
runs-on: blacksmith-32vcpu-ubuntu-2404
environment:
# Configure this environment with required reviewers, no secrets, and a
# deployment branch rule of exactly `main`. Approval is evaluated before
# the first step, so it precedes begin-testbox.
name: blacksmith-testbox-trusted
permissions:
contents: read
# Hydration plus three sequential 20-minute bounded remote builds happen
# after setup. Keep the GitHub job alive long enough for all stages and
# cleanup; the Testbox itself still has its separate idle timeout.
timeout-minutes: 120
steps:
# Every check here is main-controlled. `blacksmith testbox warmup`
# resolves both this file and the hydrated source from the same --ref, so
# refusing any ref other than refs/heads/main is what keeps a candidate
# branch outside the trust boundary.
- name: Validate broker ref
env:
DISPATCH_REF: ${{ github.ref }}
EVENT_NAME: ${{ github.event_name }}
REPOSITORY: ${{ github.repository }}
TESTBOX_ID: ${{ inputs.testbox_id }}
shell: bash
run: |
set -euo pipefail
[[ "$REPOSITORY" == "manaflow-ai/cmux" ]] || {
echo "::error::this Testbox lane is only valid for manaflow-ai/cmux" >&2
exit 1
}
[[ "$EVENT_NAME" == "workflow_dispatch" ]] || {
echo "::error::Testbox setup must be dispatched manually, never from a PR event" >&2
exit 1
}
[[ "$DISPATCH_REF" == "refs/heads/main" ]] || {
echo "::error::this broker lane runs only from refs/heads/main, got $DISPATCH_REF; warm up with --ref main and sync the candidate through blacksmith testbox run" >&2
exit 1
}
[[ "$TESTBOX_ID" =~ ^tbx_[A-Za-z0-9_-]+$ ]] || {
echo "::error::malformed Testbox ID" >&2
exit 1
}
- name: Begin Testbox
uses: useblacksmith/begin-testbox@233448af4bfdc6fca509a7f0974411ac6d8a8043 # v2
with:
testbox_id: ${{ inputs.testbox_id }}
# main can move while a reviewer approves the deployment. Record which
# commit this job actually hydrates and fail closed if the dispatch ref
# changed under it.
- name: Revalidate broker identity after token exposure
env:
DISPATCH_REF: ${{ github.ref }}
DISPATCH_SHA: ${{ github.sha }}
shell: bash
run: |
set -euo pipefail
[[ "$DISPATCH_REF" == "refs/heads/main" ]] || {
echo "::error::broker ref changed during setup" >&2
exit 1
}
[[ "$DISPATCH_SHA" =~ ^[0-9a-f]{40}$ ]] || {
echo "::error::dispatch SHA is malformed" >&2
exit 1
}
printf 'hydrating main at %s\n' "$DISPATCH_SHA"
- name: Checkout hydration commit
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false
ref: ${{ github.sha }}
- name: Require exact checkout and clean source
env:
EXPECTED_SHA: ${{ github.sha }}
shell: bash
run: |
set -euo pipefail
actual_sha="$(git rev-parse HEAD)"
[[ "$actual_sha" == "$EXPECTED_SHA" ]] || {
echo "::error::checked out $actual_sha, expected hydration SHA $EXPECTED_SHA" >&2
exit 1
}
[[ -z "$(git status --porcelain=v1 --untracked-files=normal)" ]] || {
echo "::error::source checkout is dirty before hydration" >&2
git status --short >&2
exit 1
}
source_tree_sha="$(git rev-parse 'HEAD^{tree}')"
ghostty_entry="$(git ls-tree HEAD ghostty)"
[[ "$ghostty_entry" =~ ^160000[[:space:]]commit[[:space:]][0-9a-f]{40}[[:space:]]ghostty$ ]] || {
echo "::error::HEAD:ghostty is not a gitlink" >&2
exit 1
}
ghostty_gitlink_sha="$(git rev-parse 'HEAD:ghostty')"
printf 'source_sha=%s\nsource_tree_sha=%s\nghostty_gitlink_sha=%s\n' \
"$actual_sha" "$source_tree_sha" "$ghostty_gitlink_sha"
- name: Initialize Ghostty source submodule
shell: bash
run: |
set -euo pipefail
git submodule update --init --depth 1 ghostty
[[ "$(git -C ghostty rev-parse --show-toplevel)" == "$GITHUB_WORKSPACE/ghostty" ]] || {
echo "::error::ghostty did not initialize as its own submodule checkout" >&2
exit 1
}
ghostty_entry="$(git ls-tree HEAD ghostty)"
[[ "$ghostty_entry" =~ ^160000[[:space:]]commit[[:space:]][0-9a-f]{40}[[:space:]]ghostty$ ]] || {
echo "::error::HEAD:ghostty is not a gitlink" >&2
exit 1
}
expected_ghostty_sha="$(git rev-parse HEAD:ghostty)"
actual_ghostty_sha="$(git -C ghostty rev-parse HEAD)"
[[ "$actual_ghostty_sha" == "$expected_ghostty_sha" ]] || {
echo "::error::Ghostty checkout $actual_ghostty_sha does not match gitlink $expected_ghostty_sha" >&2
exit 1
}
[[ -z "$(git -C ghostty status --porcelain=v1 --untracked-files=normal)" ]] || {
echo "::error::Ghostty submodule is dirty after initialization" >&2
git -C ghostty status --short >&2
exit 1
}
test -f ghostty/build.zig.zon
- name: Install Linux build dependencies
shell: bash
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y clang libclang-dev pkg-config
- name: Cache Zig package downloads
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cache/zig
key: cmux-tui-zig-${{ hashFiles('ghostty/build.zig.zon', 'ghostty/build.zig.zon.json') }}
restore-keys: |
cmux-tui-zig-
- name: Install repository-pinned Zig
shell: bash
run: ./scripts/install-zig-ci.sh
- name: Fetch Ghostty Zig dependencies without compiling
working-directory: ghostty
shell: bash
run: |
set -euo pipefail
# `--fetch` hydrates the package cache and exits before a build.
"$CMUX_ZIG" build --fetch
- name: Cache Cargo registry and git dependencies
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: |
~/.cargo/registry
~/.cargo/git
key: cmux-tui-cargo-${{ runner.os }}-${{ hashFiles('cmux-tui/Cargo.lock', 'cmux-tui/rust-toolchain.toml') }}
restore-keys: |
cmux-tui-cargo-${{ runner.os }}-
- name: Set up repository-pinned cmux-tui Rust
uses: ./.github/actions/setup-cmux-tui-rust
- name: Fetch Cargo dependencies without compiling
working-directory: cmux-tui
shell: bash
run: |
set -euo pipefail
cargo fetch --locked
- name: Record runner, toolchain, and Ghostty identity
env:
SOURCE_SHA: ${{ github.sha }}
SOURCE_REF: ${{ github.ref }}
TESTBOX_ID: ${{ inputs.testbox_id }}
RUNNER_LABEL: blacksmith-32vcpu-ubuntu-2404
shell: bash
run: |
set -euo pipefail
source_tree_sha="$(git rev-parse 'HEAD^{tree}')"
ghostty_entry="$(git ls-tree HEAD ghostty)"
[[ "$ghostty_entry" =~ ^160000[[:space:]]commit[[:space:]][0-9a-f]{40}[[:space:]]ghostty$ ]] || exit 1
ghostty_gitlink_sha="$(git rev-parse 'HEAD:ghostty')"
ghostty_head_sha="$(git -C ghostty rev-parse HEAD)"
[[ "$SOURCE_SHA" == "$(git rev-parse HEAD)" ]] || exit 1
[[ "$ghostty_gitlink_sha" == "$ghostty_head_sha" ]] || exit 1
[[ -z "$(git status --porcelain=v1 --untracked-files=normal)" ]] || exit 1
[[ -z "$(git -C ghostty status --porcelain=v1 --untracked-files=normal)" ]] || exit 1
pushd cmux-tui >/dev/null
cargo metadata --locked --no-deps --format-version 1 > "$RUNNER_TEMP/cmux-tui-cargo-metadata.json"
test -s "$RUNNER_TEMP/cmux-tui-cargo-metadata.json"
RUST_TOOLCHAIN="$(rustup show active-toolchain)"
RUSTC_VERSION="$(rustc --version)"
CARGO_VERSION="$(cargo --version)"
popd >/dev/null
mkdir -p testbox-benchmark
SOURCE_TREE_SHA="$source_tree_sha"
GHOSTTY_GITLINK_SHA="$ghostty_gitlink_sha"
GHOSTTY_HEAD_SHA="$ghostty_head_sha"
ZIG_VERSION="$("$CMUX_ZIG" version)"
ZIG_PATH="$CMUX_ZIG"
RUNNER_UNAME="$(uname -a)"
RUNNER_CPU_COUNT="$(nproc)"
[[ "$RUNNER_ARCH" == "X64" && "$RUNNER_CPU_COUNT" == "32" ]] || {
echo "::error::expected x64 32-vCPU runner, got arch=$RUNNER_ARCH cpu_count=$RUNNER_CPU_COUNT" >&2
exit 1
}
CARGO_METADATA_SHA256="$(sha256sum "$RUNNER_TEMP/cmux-tui-cargo-metadata.json" | cut -d ' ' -f 1)"
RUST_TOOLCHAIN_FILE_SHA256="$(sha256sum cmux-tui/rust-toolchain.toml | cut -d ' ' -f 1)"
CARGO_LOCK_SHA256="$(sha256sum cmux-tui/Cargo.lock | cut -d ' ' -f 1)"
GHOSTTY_ZON_SHA256="$(sha256sum ghostty/build.zig.zon | cut -d ' ' -f 1)"
export SOURCE_TREE_SHA GHOSTTY_GITLINK_SHA GHOSTTY_HEAD_SHA RUST_TOOLCHAIN RUSTC_VERSION CARGO_VERSION ZIG_VERSION ZIG_PATH RUNNER_UNAME RUNNER_CPU_COUNT CARGO_METADATA_SHA256 RUST_TOOLCHAIN_FILE_SHA256 CARGO_LOCK_SHA256 GHOSTTY_ZON_SHA256
python3 - <<'PY' > testbox-benchmark/setup-identity.json
import json
import os
import platform
print(json.dumps({
"schema": 3,
"source": {
"ref": os.environ["SOURCE_REF"],
"commit_sha": os.environ["SOURCE_SHA"],
"tree_sha": os.environ["SOURCE_TREE_SHA"],
"ghostty_gitlink_sha": os.environ["GHOSTTY_GITLINK_SHA"],
"ghostty_head_sha": os.environ["GHOSTTY_HEAD_SHA"],
},
"broker": {
"workflow_ref": os.environ["GITHUB_REF"],
"workflow_sha": os.environ["SOURCE_SHA"],
},
"testbox": {
"id": os.environ["TESTBOX_ID"],
"setup_workflow_run_id": os.environ["GITHUB_RUN_ID"],
},
"runner": {
"label": os.environ["RUNNER_LABEL"],
"name": os.environ.get("RUNNER_NAME"),
"os": os.environ.get("RUNNER_OS"),
"arch": os.environ.get("RUNNER_ARCH"),
"hostname": platform.node(),
"uname": os.environ["RUNNER_UNAME"],
"cpu_count": int(os.environ["RUNNER_CPU_COUNT"]),
},
"toolchain": {
"rust_toolchain": os.environ["RUST_TOOLCHAIN"],
"rustc": os.environ["RUSTC_VERSION"],
"cargo": os.environ["CARGO_VERSION"],
"rust_toolchain_file_sha256": os.environ["RUST_TOOLCHAIN_FILE_SHA256"],
"cargo_lock_sha256": os.environ["CARGO_LOCK_SHA256"],
"cargo_metadata_sha256": os.environ["CARGO_METADATA_SHA256"],
"zig_path": os.environ["ZIG_PATH"],
"zig": os.environ["ZIG_VERSION"],
"ghostty_build_zig_zon_sha256": os.environ["GHOSTTY_ZON_SHA256"],
},
}, sort_keys=True, indent=2))
PY
test -s testbox-benchmark/setup-identity.json
cat testbox-benchmark/setup-identity.json
- name: Require clean hydrated source
shell: bash
run: |
set -euo pipefail
[[ -z "$(git status --porcelain=v1 --untracked-files=normal)" ]] || {
echo "::error::source became dirty during hydration" >&2
git status --short >&2
exit 1
}
[[ -z "$(git -C ghostty status --porcelain=v1 --untracked-files=normal)" ]] || {
echo "::error::Ghostty became dirty during hydration" >&2
git -C ghostty status --short >&2
exit 1
}
- name: Upload setup identity JSON
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: cmux-tui-testbox-setup-${{ github.run_id }}
path: testbox-benchmark/setup-identity.json
if-no-files-found: warn
retention-days: 14
# Missing `/tmp/.testbox` means begin-testbox could not register the VM.
# No auth token or API URL exists, so neither this workflow nor the pinned
# action can report hydration_failed. Fail explicitly; the CLI's bounded
# readiness wait and receipt-aware cleanup remain the only safe recovery.
- name: Require Testbox registration state
if: always()
shell: bash
run: |
set -euo pipefail
if [[ ! -d /tmp/.testbox ]]; then
echo "::error::begin-testbox produced no registration state; hydration failure cannot be reported from this runner" >&2
exit 1
fi
- name: Confirm Testbox registration and mark setup ready
if: success()
env:
EXPECTED_TESTBOX_ID: ${{ inputs.testbox_id }}
shell: bash
run: |
set -euo pipefail
state=/tmp/.testbox
rm -f /tmp/.testbox/cmux-tui-rust-setup-identity.json
for required_file in testbox_id installation_model_id auth_token api_url runner_host runner_ssh_port adopted_run_id ssh_public_key; do
test -s "$state/$required_file"
done
test -s "$state/working_directory"
test "$(<"$state/testbox_id")" = "$EXPECTED_TESTBOX_ID"
installation_model_id="$(<"$state/installation_model_id")"
[[ "$installation_model_id" =~ ^[0-9]+$ ]]
test -s "$state/ssh_public_key"
# begin-testbox deliberately continues after degraded phone-home.
# Validate the SSH handoff and install the identity marker before the
# pinned run-testbox action publishes ready. That action remains the
# sole readiness publisher, so a CLI run cannot race this marker.
marker="$state/cmux-tui-rust-setup-identity.json"
rm -f "$marker"
install -m 600 testbox-benchmark/setup-identity.json "$marker"
test -s "$marker"
# Keepalive is checked into this main-controlled workflow instead of
# importing an upstream composite that performs an unbounded duplicate
# ready curl. It reports
# hydration_failed on any failed setup or readiness path and bounds every
# phone-home request before entering its idle loop.
- name: Run trusted Testbox keepalive
if: always()
env:
JOB_STATUS: ${{ job.status }}
shell: bash
run: ./scripts/blacksmith-testbox-keepalive.sh
+4
View File
@@ -74,3 +74,7 @@ artifacts/
# tmux verbose debug logs (tmux -v) that land in the cwd
tmux-*.log
# Remote-only Blacksmith cmux-tui benchmark output
/testbox-benchmark/
/.cmux-scratch/
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 2 || ! "$1" =~ ^[0-9]+$ ]]; then
echo "usage: $0 <timeout-seconds> <command> [args...]" >&2
exit 64
fi
seconds="$1"
shift
# Prefer GNU coreutils when present, while keeping the operator workflow
# usable on macOS hosts that have neither `timeout` nor `gtimeout`.
for candidate in gtimeout timeout; do
if command -v "$candidate" >/dev/null && "$candidate" --version 2>&1 | grep -q 'GNU coreutils'; then
exec "$candidate" --kill-after=5s "${seconds}s" "$@"
fi
done
exec python3 - "$seconds" "$@" <<'PY'
import os
import signal
import subprocess
import sys
seconds = float(sys.argv[1])
argv = sys.argv[2:]
if not argv:
raise SystemExit("missing command")
process = subprocess.Popen(argv, start_new_session=True)
def terminate(_signum, _frame):
try:
os.killpg(process.pid, signal.SIGTERM)
process.wait(timeout=5)
except (ProcessLookupError, subprocess.TimeoutExpired):
try:
os.killpg(process.pid, signal.SIGKILL)
except ProcessLookupError:
pass
process.wait()
raise SystemExit(128 + _signum)
signal.signal(signal.SIGINT, terminate)
signal.signal(signal.SIGTERM, terminate)
try:
raise SystemExit(process.wait(timeout=seconds))
except subprocess.TimeoutExpired:
terminate(signal.SIGTERM, None)
PY
+586
View File
@@ -0,0 +1,586 @@
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
# This helper is intentionally remote-only. The local benchmark plan invokes it
# through `blacksmith testbox run`; the environment flag is only a first guard.
# The Blacksmith kernel marker and /tmp/.testbox state below are the stronger
# signals that this command is running in the prepared Testbox VM.
if [[ "${CMUX_TESTBOX_REMOTE:-}" != "1" ]]; then
echo "refusing to run outside a Blacksmith Testbox (set CMUX_TESTBOX_REMOTE=1 only in the remote command)" >&2
exit 64
fi
if [[ ! -r /proc/cmdline ]] || ! grep -Eq '(^|[[:space:]])metadata_port=[^[:space:]]+' /proc/cmdline; then
echo "refusing to run without the Blacksmith Testbox metadata marker" >&2
exit 64
fi
if [[ $# -ne 3 ]]; then
echo "usage: CMUX_TESTBOX_REMOTE=1 CMUX_TESTBOX_ID=tbx_... $0 {first-clean|incremental-noop|changed-file} <source-sha> <ghostty-gitlink-sha>" >&2
exit 64
fi
stage="$1"
expected_source_sha="$2"
expected_ghostty_sha="$3"
case "$stage" in
first-clean|incremental-noop|changed-file) ;;
*)
echo "unsupported benchmark stage: $stage" >&2
exit 64
;;
esac
for value_name in expected_source_sha expected_ghostty_sha; do
value="${!value_name}"
if [[ ! "$value" =~ ^[0-9a-f]{40}$ ]]; then
echo "$value_name must be a lowercase 40-character commit SHA" >&2
exit 64
fi
done
testbox_id="${CMUX_TESTBOX_ID:-}"
if [[ ! "$testbox_id" =~ ^tbx_[A-Za-z0-9_-]+$ ]]; then
echo "CMUX_TESTBOX_ID must identify the claimed Testbox" >&2
exit 64
fi
state_dir=/tmp/.testbox
if [[ ! -d "$state_dir" || ! -s "$state_dir/auth_token" || ! -f "$state_dir/testbox_id" ]]; then
echo "refusing to run without the Testbox state files" >&2
exit 64
fi
state_testbox_id="$(tr -d '\r\n' <"$state_dir/testbox_id")"
if [[ "$state_testbox_id" != "$testbox_id" ]]; then
echo "Testbox state belongs to $state_testbox_id, expected $testbox_id" >&2
exit 66
fi
repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"
ghostty_root="$repo_root/ghostty"
if [[ ! -f cmux-tui/Cargo.toml || ! -f ghostty/build.zig.zon ]]; then
echo "cmux-tui and its Ghostty source submodule must be initialized" >&2
exit 65
fi
if [[ "$(git -C ghostty rev-parse --show-toplevel 2>/dev/null || true)" != "$ghostty_root" ]]; then
echo "ghostty is not an initialized submodule checkout" >&2
exit 65
fi
ghostty_entry="$(git ls-tree HEAD ghostty)"
if [[ ! "$ghostty_entry" =~ ^160000[[:space:]]commit[[:space:]][0-9a-f]{40}[[:space:]]ghostty$ ]]; then
echo "HEAD:ghostty is not a gitlink" >&2
exit 65
fi
expected_tree_sha="$(git rev-parse "${expected_source_sha}^{tree}")"
if ! command -v timeout >/dev/null; then
echo "timeout is required for bounded remote builds" >&2
exit 65
fi
setup_identity_path="$state_dir/cmux-tui-rust-setup-identity.json"
if [[ ! -s "$setup_identity_path" ]]; then
echo "refusing to run without a successful Testbox setup identity marker" >&2
exit 65
fi
setup_run_id="$(tr -d '\r\n' <"$state_dir/adopted_run_id")"
if [[ ! "$setup_run_id" =~ ^[0-9]+$ ]]; then
echo "invalid Testbox setup workflow run ID" >&2
exit 65
fi
# The broker hydrates main, while this stage benchmarks whatever revision the
# operator synchronized onto the box. Those two commits are deliberately
# allowed to differ, so the marker is checked for VM identity, runner class,
# and toolchain completeness, not for source equality. The active-toolchain
# gate further down is what still refuses a candidate whose pinned Rust or Zig
# would leave the hydrated caches cold and the timings incomparable.
verify_setup_identity() {
python3 - "$setup_identity_path" "$testbox_id" "$setup_run_id" <<'PY'
import json
import pathlib
import re
import sys
path, expected_testbox, expected_run_id = sys.argv[1:]
try:
record = json.loads(pathlib.Path(path).read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
raise SystemExit(f"invalid setup identity marker: {error}")
source = record.get("source", {})
testbox = record.get("testbox", {})
runner = record.get("runner", {})
toolchain = record.get("toolchain", {})
errors = []
if not re.fullmatch(r"[0-9a-f]{40}", str(source.get("commit_sha", ""))):
errors.append("setup hydration commit is missing or malformed")
if not re.fullmatch(r"[0-9a-f]{40}", str(source.get("tree_sha", ""))):
errors.append("setup hydration tree is missing or malformed")
if not re.fullmatch(r"[0-9a-f]{40}", str(source.get("ghostty_gitlink_sha", ""))):
errors.append("setup hydration Ghostty gitlink is missing or malformed")
if source.get("ghostty_head_sha") != source.get("ghostty_gitlink_sha"):
errors.append("setup hydration Ghostty checkout does not match its own gitlink")
if source.get("ref") != "refs/heads/main":
errors.append(f"setup hydration ref {source.get('ref')!r} is not refs/heads/main")
if testbox.get("id") != expected_testbox:
errors.append("setup Testbox ID mismatch")
if str(testbox.get("setup_workflow_run_id")) != expected_run_id:
errors.append("setup workflow run ID mismatch")
if runner.get("label") != "blacksmith-32vcpu-ubuntu-2404" or runner.get("arch") != "X64" or runner.get("cpu_count") != 32:
errors.append("setup runner identity mismatch")
if not toolchain.get("rust_toolchain") or not toolchain.get("rustc") or not toolchain.get("cargo") or not toolchain.get("zig"):
errors.append("setup toolchain identity is incomplete")
if errors:
for error in errors:
print(error, file=sys.stderr)
raise SystemExit(66)
PY
}
verify_setup_identity
setup_rust_toolchain="$(python3 - "$setup_identity_path" <<'PY'
import json
import pathlib
import sys
record=json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
print(record["toolchain"]["rust_toolchain"])
PY
)"
setup_rustc="$(python3 - "$setup_identity_path" <<'PY'
import json
import pathlib
import sys
record=json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
print(record["toolchain"]["rustc"])
PY
)"
setup_cargo="$(python3 - "$setup_identity_path" <<'PY'
import json
import pathlib
import sys
record=json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
print(record["toolchain"]["cargo"])
PY
)"
setup_zig="$(python3 - "$setup_identity_path" <<'PY'
import json
import pathlib
import sys
record=json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
print(record["toolchain"]["zig"])
PY
)"
hydrated_source_sha="$(python3 - "$setup_identity_path" <<'PY'
import json
import pathlib
import sys
record=json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
print(record["source"]["commit_sha"])
PY
)"
hydrated_source_ref="$(python3 - "$setup_identity_path" <<'PY'
import json
import pathlib
import sys
record=json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
print(record["source"]["ref"])
PY
)"
benchmark_dir="$repo_root/testbox-benchmark"
command -v flock >/dev/null || {
echo "flock is required for serialized Testbox stages" >&2
exit 65
}
mkdir -p "$benchmark_dir"
# Testbox can acknowledge a run while its remote shell is still flushing
# output. Serialize stages and hold the lock through all artifact writes so a
# subsequent run cannot overwrite a prior stage's timing file.
exec 9>"$benchmark_dir/.stage.lock"
flock -x 9
log_path="$benchmark_dir/$stage.log"
time_path="$benchmark_dir/$stage.time"
json_path="$benchmark_dir/$stage.json"
pre_identity_path="$benchmark_dir/.$stage.pre-identity.json"
post_identity_path="$benchmark_dir/.$stage.post-identity.json"
changed_file="cmux-tui/crates/cmux-tui/src/main.rs"
changed_backup=""
changed_backup_sha256=""
changed_backup_size=""
restore_changed_file() {
if [[ -n "$changed_backup" ]]; then
if [[ ! -f "$changed_backup" ]]; then
echo "changed-file backup disappeared: $changed_backup" >&2
return 1
fi
if [[ "$(wc -c <"$changed_backup")" != "$changed_backup_size" ||
"$(sha256sum "$changed_backup" | cut -d ' ' -f 1)" != "$changed_backup_sha256" ]]; then
echo "changed-file backup failed integrity verification" >&2
return 1
fi
if ! cp "$changed_backup" "$repo_root/$changed_file"; then
return 1
fi
if [[ "$(wc -c <"$repo_root/$changed_file")" != "$changed_backup_size" ||
"$(sha256sum "$repo_root/$changed_file" | cut -d ' ' -f 1)" != "$changed_backup_sha256" ]]; then
echo "restored source failed integrity verification" >&2
return 1
fi
if ! rm -f "$changed_backup"; then
return 1
fi
changed_backup=""
fi
}
# Always restore the deliberately changed source, including when Cargo exits
# non-zero. Do not let cleanup replace the build result unless restoration
# itself fails.
# shellcheck disable=SC2329 # invoked indirectly by the signal/EXIT traps
finish_source() {
local result=$?
if [[ -n "$changed_backup" ]]; then
if ! restore_changed_file; then
echo "failed to restore $changed_file" >&2
(( result == 0 )) && result=67
fi
fi
exit "$result"
}
# Signals must remain failures even when they arrive after a successful command.
# The EXIT trap performs the actual restoration exactly once.
# shellcheck disable=SC2329 # invoked indirectly by signal traps
interrupt_source() {
local signal_name="$1"
trap - TERM INT HUP
case "$signal_name" in
TERM) exit 143 ;;
INT) exit 130 ;;
HUP) exit 129 ;;
esac
}
trap 'interrupt_source TERM' TERM
trap 'interrupt_source INT' INT
trap 'interrupt_source HUP' HUP
trap finish_source EXIT
clean_status() {
local top_status ghostty_status
top_status="$(git status --porcelain=v1 --untracked-files=normal)"
if [[ -n "$top_status" ]]; then
printf '%s\n' "top-level source is dirty:" "$top_status" >&2
return 1
fi
ghostty_status="$(git -C ghostty status --porcelain=v1 --untracked-files=normal)"
if [[ -n "$ghostty_status" ]]; then
printf '%s\n' "Ghostty submodule is dirty:" "$ghostty_status" >&2
return 1
fi
}
capture_identity() {
python3 - "$expected_source_sha" "$expected_tree_sha" "$expected_ghostty_sha" "$testbox_id" "$repo_root" <<'PY'
import json
import os
import pathlib
import platform
import subprocess
import sys
expected_source_sha, expected_tree_sha, expected_ghostty_sha, testbox_id, repo_root = sys.argv[1:]
repo = pathlib.Path(repo_root)
ghostty = repo / "ghostty"
def run(command, cwd=repo):
return subprocess.check_output(command, cwd=cwd, text=True, stderr=subprocess.STDOUT).strip()
def optional_file(path):
try:
return path.read_text(encoding="utf-8").strip()
except OSError:
return None
def status(cwd=repo):
return subprocess.check_output(
["git", "status", "--porcelain=v1", "--untracked-files=normal"],
cwd=cwd,
text=True,
).splitlines()
source_sha = run(["git", "rev-parse", "HEAD"])
source_tree_sha = run(["git", "rev-parse", "HEAD^{tree}"])
ghostty_gitlink_sha = run(["git", "rev-parse", "HEAD:ghostty"])
ghostty_head_sha = run(["git", "-C", "ghostty", "rev-parse", "HEAD"])
record = {
"commit_sha": source_sha,
"tree_sha": source_tree_sha,
"expected_commit_sha": expected_source_sha,
"expected_tree_sha": expected_tree_sha,
"dirty_files": status(),
"ghostty": {
"gitlink_sha": ghostty_gitlink_sha,
"expected_gitlink_sha": expected_ghostty_sha,
"head_sha": ghostty_head_sha,
"dirty_files": status(ghostty),
},
"testbox_id": testbox_id,
"testbox_state": {
"adopted_run_id": optional_file(pathlib.Path("/tmp/.testbox/adopted_run_id")),
"runner_host": optional_file(pathlib.Path("/tmp/.testbox/runner_host")),
"runner_ssh_port": optional_file(pathlib.Path("/tmp/.testbox/runner_ssh_port")),
},
}
print(json.dumps(record, sort_keys=True))
PY
}
verify_identity() {
local identity_path="$1"
python3 - "$identity_path" "$expected_source_sha" "$expected_tree_sha" "$expected_ghostty_sha" "$testbox_id" <<'PY'
import json
import pathlib
import sys
path, expected_source_sha, expected_tree_sha, expected_ghostty_sha, expected_testbox_id = sys.argv[1:]
record = json.loads(pathlib.Path(path).read_text(encoding="utf-8"))
errors = []
if record.get("commit_sha") != expected_source_sha:
errors.append(f"source commit {record.get('commit_sha')} != {expected_source_sha}")
if record.get("expected_commit_sha") != expected_source_sha:
errors.append("source expectation was not recorded")
if record.get("tree_sha") != expected_tree_sha:
errors.append(f"source tree {record.get('tree_sha')} != {expected_tree_sha}")
if record.get("expected_tree_sha") != expected_tree_sha:
errors.append("source tree expectation was not recorded")
if record.get("dirty_files"):
errors.append("top-level source is dirty")
ghostty = record.get("ghostty", {})
if ghostty.get("gitlink_sha") != expected_ghostty_sha:
errors.append(f"Ghostty gitlink {ghostty.get('gitlink_sha')} != {expected_ghostty_sha}")
if ghostty.get("expected_gitlink_sha") != expected_ghostty_sha:
errors.append("Ghostty expectation was not recorded")
if ghostty.get("head_sha") != expected_ghostty_sha:
errors.append(f"Ghostty checkout {ghostty.get('head_sha')} != {expected_ghostty_sha}")
if ghostty.get("dirty_files"):
errors.append("Ghostty submodule is dirty")
if record.get("testbox_id") != expected_testbox_id:
errors.append("Testbox identity mismatch")
if errors:
for error in errors:
print(f"source guard: {error}", file=sys.stderr)
raise SystemExit(66)
PY
}
# Verify the immutable source and submodule before every stage. The changed-file
# stage is allowed to become dirty only after this check and must be clean again
# before its JSON record is emitted.
clean_status
capture_identity >"$pre_identity_path"
verify_identity "$pre_identity_path"
runner_label="blacksmith-32vcpu-ubuntu-2404"
zig_bin="${CMUX_ZIG:-$(command -v zig)}"
pushd cmux-tui >/dev/null
rust_toolchain="$(rustup show active-toolchain)"
rustc_version="$(rustc --version)"
cargo_version="$(cargo --version)"
popd >/dev/null
zig_version="$("$zig_bin" version)"
[[ "$rust_toolchain" == "$setup_rust_toolchain" && "$rustc_version" == "$setup_rustc" && "$cargo_version" == "$setup_cargo" && "$zig_version" == "$setup_zig" ]] || {
echo "active Rust/Cargo/Zig toolchain differs from the setup identity marker" >&2
exit 66
}
export ZIG="$zig_bin"
rust_toolchain_file_sha256="$(sha256sum cmux-tui/rust-toolchain.toml | cut -d ' ' -f 1)"
cargo_lock_sha256="$(sha256sum cmux-tui/Cargo.lock | cut -d ' ' -f 1)"
ghostty_zon_sha256="$(sha256sum ghostty/build.zig.zon | cut -d ' ' -f 1)"
case "$stage" in
first-clean)
rm -rf "$repo_root/cmux-tui/target"
;;
changed-file)
if [[ ! -f "$repo_root/$changed_file" ]]; then
echo "changed-file target is missing: $changed_file" >&2
exit 65
fi
backup_candidate="$(mktemp "${TMPDIR:-/tmp}/cmux-tui-testbox-source.XXXXXX")"
if ! cp "$repo_root/$changed_file" "$backup_candidate"; then
rm -f "$backup_candidate"
echo "failed to create a source backup" >&2
exit 67
fi
backup_sha256="$(sha256sum "$backup_candidate" | cut -d ' ' -f 1)"
backup_size="$(wc -c <"$backup_candidate" | tr -d '[:space:]')"
[[ "$backup_size" =~ ^[0-9]+$ && "$backup_size" -gt 0 ]] || {
rm -f "$backup_candidate"
echo "source backup is empty" >&2
exit 67
}
changed_backup="$backup_candidate"
changed_backup_sha256="$backup_sha256"
changed_backup_size="$backup_size"
printf '\n// Blacksmith Testbox changed-file timing marker.\n' >>"$repo_root/$changed_file"
;;
esac
start_epoch="$(python3 -c 'import time; print(time.time())')"
rm -f "$time_path" "$log_path" "$json_path" "$post_identity_path"
set +e
(
cd "$repo_root/cmux-tui"
timeout --kill-after=30s 20m \
/usr/bin/time -p -o "$time_path" cargo build -p cmux-tui --locked
) >"$log_path" 2>&1
build_status=$?
set -e
end_epoch="$(python3 -c 'import time; print(time.time())')"
restore_status=0
if ! restore_changed_file; then
echo "failed to restore $changed_file" >&2
restore_status=67
fi
post_identity_status=0
if ! capture_identity >"$post_identity_path"; then
echo "failed to capture post-stage source identity" >&2
post_identity_status=66
printf '{}\n' >"$post_identity_path"
fi
if (( post_identity_status == 0 )); then
if ! verify_identity "$post_identity_path"; then
post_identity_status=66
fi
fi
if (( restore_status != 0 )); then
final_status="$restore_status"
elif (( post_identity_status != 0 )); then
final_status="$post_identity_status"
else
final_status="$build_status"
fi
python3 - "$stage" "$start_epoch" "$end_epoch" "$build_status" "$final_status" "$time_path" "$pre_identity_path" "$post_identity_path" "$changed_file" "$expected_source_sha" "$expected_tree_sha" "$expected_ghostty_sha" "$testbox_id" "$runner_label" "$rust_toolchain" "$rustc_version" "$cargo_version" "$zig_bin" "$zig_version" "$rust_toolchain_file_sha256" "$cargo_lock_sha256" "$ghostty_zon_sha256" "$hydrated_source_ref" "$hydrated_source_sha" >"$json_path" <<'PY'
import datetime as dt
import json
import os
import pathlib
import platform
import sys
(
stage,
start,
end,
build_status,
final_status,
time_path,
pre_identity_path,
post_identity_path,
changed_file,
expected_source_sha,
expected_tree_sha,
expected_ghostty_sha,
testbox_id,
runner_label,
rust_toolchain,
rustc_version,
cargo_version,
zig_bin,
zig_version,
rust_toolchain_file_sha256,
cargo_lock_sha256,
ghostty_zon_sha256,
hydrated_source_ref,
hydrated_source_sha,
) = sys.argv[1:]
def read_json(path):
try:
return json.loads(pathlib.Path(path).read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return {}
remote_time = {}
try:
for line in pathlib.Path(time_path).read_text(encoding="utf-8").splitlines():
key, _, value = line.partition(" ")
if key in {"real", "user", "sys"}:
remote_time[f"time_{key}_seconds"] = float(value)
except OSError:
pass
pre = read_json(pre_identity_path)
post = read_json(post_identity_path)
record = {
"schema": 3,
"stage": stage,
"command": "cargo build -p cmux-tui --locked",
"build_exit_code": int(build_status),
"exit_code": int(final_status),
"ok": int(final_status) == 0,
"started_at": dt.datetime.fromtimestamp(float(start), dt.timezone.utc).isoformat(),
"finished_at": dt.datetime.fromtimestamp(float(end), dt.timezone.utc).isoformat(),
"wall_seconds": round(float(end) - float(start), 3),
"source": {
"expected_commit_sha": expected_source_sha,
"expected_tree_sha": expected_tree_sha,
"before": pre,
"after": post,
"changed_file": changed_file if stage == "changed-file" else None,
"restored": stage != "changed-file" or not post.get("dirty_files"),
},
"ghostty": {
"expected_gitlink_sha": expected_ghostty_sha,
"before_gitlink_sha": pre.get("ghostty", {}).get("gitlink_sha"),
"before_head_sha": pre.get("ghostty", {}).get("head_sha"),
"after_gitlink_sha": post.get("ghostty", {}).get("gitlink_sha"),
"after_head_sha": post.get("ghostty", {}).get("head_sha"),
},
"testbox": {
"id": testbox_id,
"adopted_run_id": pre.get("testbox_state", {}).get("adopted_run_id"),
},
# What the broker warmed the caches from. It is main, and it is normally a
# different commit from the benchmarked revision above.
"hydration": {
"ref": hydrated_source_ref,
"commit_sha": hydrated_source_sha,
"matches_benchmarked_source": hydrated_source_sha == expected_source_sha,
},
"runner": {
"label": runner_label,
"hostname": platform.node(),
"arch": platform.machine(),
"cpu_count": os.cpu_count(),
"uname": " ".join(platform.uname()),
"host_from_testbox_state": pre.get("testbox_state", {}).get("runner_host"),
},
"toolchain": {
"rust_toolchain": rust_toolchain,
"rustc": rustc_version,
"cargo": cargo_version,
"rust_toolchain_file_sha256": rust_toolchain_file_sha256,
"cargo_lock_sha256": cargo_lock_sha256,
"zig_path": zig_bin,
"zig": zig_version,
"ghostty_build_zig_zon_sha256": ghostty_zon_sha256,
},
**remote_time,
}
print(json.dumps(record, sort_keys=True))
PY
cat "$log_path"
printf '\n--- /usr/bin/time -p (%s) ---\n' "$stage"
if [[ -f "$time_path" ]]; then
cat "$time_path"
else
echo "time output unavailable" >&2
fi
printf '\n--- structured timing (%s) ---\n' "$stage"
cat "$json_path"
exit "$final_status"
+368
View File
@@ -0,0 +1,368 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 4 ]]; then
echo "usage: $0 <testbox-id> <evidence-directory> <ownership-token> <PREVIEW|STOP:preview-sha>" >&2
exit 64
fi
testbox_id="$1"
evidence_dir="$2"
ownership_token="$3"
operator_confirmation="$4"
if [[ ! "$testbox_id" =~ ^tbx_[A-Za-z0-9_-]+$ ]]; then
echo "invalid Testbox ID: $testbox_id" >&2
exit 64
fi
if [[ ! "$ownership_token" =~ ^[0-9a-f]{32}$ ]]; then
echo "ownership token must be a 32-character lowercase hex value" >&2
exit 64
fi
if [[ "$operator_confirmation" != "PREVIEW" && ! "$operator_confirmation" =~ ^STOP:[0-9a-f]{64}$ ]]; then
echo "confirmation must be PREVIEW or STOP:<64-character preview SHA>" >&2
exit 64
fi
mkdir -p "$evidence_dir"
sha256_file() {
if command -v sha256sum >/dev/null; then
sha256sum "$1" | awk '{print $1}'
elif command -v shasum >/dev/null; then
shasum -a 256 "$1" | awk '{print $1}'
else
echo "sha256sum or shasum is required for cleanup preview hashing" >&2
return 65
fi
}
bounded_command() {
scripts_dir="$(CDPATH='' cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
"$scripts_dir/blacksmith-bounded-command.sh" "$@"
}
receipt_path="$evidence_dir/testbox-receipt.json"
if [[ ! -s "$receipt_path" ]]; then
echo "refusing cleanup without the warmup ownership receipt: $receipt_path" >&2
exit 65
fi
python3 - "$receipt_path" "$testbox_id" "$ownership_token" <<'PY'
import json
import pathlib
import sys
receipt_path, expected_id, expected_token = sys.argv[1:]
try:
receipt = json.loads(pathlib.Path(receipt_path).read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
raise SystemExit(f"invalid warmup ownership receipt: {error}")
if receipt.get("testbox_id") != expected_id:
raise SystemExit("cleanup ID does not match the warmup ownership receipt")
if receipt.get("confirmation_token") != expected_token:
raise SystemExit("ownership token does not match the warmup ownership receipt")
for field in ("workflow", "job", "source_ref", "source_sha", "source_tree_sha", "ghostty_gitlink_sha"):
if not receipt.get(field):
raise SystemExit(f"warmup ownership receipt is missing {field}")
PY
receipt_workflow="$(python3 - "$receipt_path" <<'PY'
import json
import pathlib
import sys
print(json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))["workflow"])
PY
)"
receipt_job="$(python3 - "$receipt_path" <<'PY'
import json
import pathlib
import sys
print(json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))["job"])
PY
)"
receipt_ref="$(python3 - "$receipt_path" <<'PY'
import json
import pathlib
import sys
print(json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))["source_ref"])
PY
)"
receipt_source_sha="$(python3 - "$receipt_path" <<'PY'
import json
import pathlib
import sys
print(json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))["source_sha"])
PY
)"
receipt_source_tree_sha="$(python3 - "$receipt_path" <<'PY'
import json
import pathlib
import sys
print(json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))["source_tree_sha"])
PY
)"
receipt_ghostty_sha="$(python3 - "$receipt_path" <<'PY'
import json
import pathlib
import sys
print(json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))["ghostty_gitlink_sha"])
PY
)"
# Parse either the table emitted by `list/status --id` or a summary response.
# The parser validates context only for a row containing this exact ID. It does
# not rely on fixed whitespace columns, because queued rows may have an empty IP.
parse_cli_output() {
local log_path="$1"
python3 - "$log_path" "$testbox_id" "$receipt_workflow" "$receipt_job" "$receipt_ref" <<'PY'
import pathlib
import re
import sys
path, expected_id, expected_workflow, expected_job, expected_ref = sys.argv[1:]
text = pathlib.Path(path).read_text(encoding="utf-8", errors="replace")
for line in text.splitlines():
fields = line.split()
if not fields or fields[0] != expected_id:
continue
if len(fields) < 2:
raise SystemExit(66)
status = fields[1].lower()
# Blacksmith CLI releases have emitted both ID/STATUS/REPO/WORKFLOW/CREATED
# and ID/STATUS/IP/WORKFLOW/JOB/REF/... schemas. Require the exact workflow
# in either schema; when job/ref columns exist, require those too.
try:
workflow_index = fields.index(expected_workflow, 2)
except ValueError:
raise SystemExit(66)
trailing = fields[workflow_index + 1:]
# Known schemas either end after CREATED (no job/ref columns) or expose
# JOB and REF immediately after WORKFLOW. If either expected field appears,
# require the exact pair in the exact order; reject all ambiguous contexts.
if len(trailing) >= 2 and trailing[:2] == [expected_job, expected_ref]:
pass
elif expected_job in trailing or expected_ref in trailing:
raise SystemExit(66)
elif len(trailing) not in (1, 2):
raise SystemExit(66)
print(status)
raise SystemExit(0)
# Some CLI versions use a summary such as `[tbx_...] Status: ready`.
if re.search(rf"\b{re.escape(expected_id)}\b", text):
match = re.search(r"\bstatus\s*:?\s*([A-Za-z_]+)", text, re.IGNORECASE)
if match:
print(match.group(1).lower())
raise SystemExit(0)
raise SystemExit(3)
PY
}
is_terminal() {
case "$1" in
completed|stopped|cancelled|failed|terminated|hydration_failed) return 0 ;;
*) return 1 ;;
esac
}
is_active() {
case "$1" in
ready|running|hydrating|in_progress|queued) return 0 ;;
*) return 1 ;;
esac
}
is_known_absence() {
grep -Eiq '(not found|already[[:space:]]+(stopped|completed)|hydration_failed|HTTP[[:space:]]+404|status[[:space:]]+code[[:space:]]+404|HTTP[[:space:]]+409|status[[:space:]]+code[[:space:]]+409)' "$1"
}
inventory_log="$evidence_dir/list-before-stop.log"
set +e
bounded_command 20 blacksmith testbox list --all >"$inventory_log" 2>&1
inventory_status=$?
set -e
if (( inventory_status != 0 )); then
echo "failed to capture the Testbox inventory before cleanup; refusing stop" >&2
exit "$inventory_status"
fi
inventory_row_present=0
set +e
parse_cli_output "$inventory_log" >/dev/null
inventory_parse_status=$?
set -e
case "$inventory_parse_status" in
0) inventory_row_present=1 ;;
3) : ;;
66) echo "inventory ownership context differs from the warmup receipt; refusing cleanup" >&2; exit 66 ;;
*) echo "could not parse the Testbox inventory; refusing cleanup" >&2; exit "$inventory_parse_status" ;;
esac
status_log="$evidence_dir/status-before-stop.log"
set +e
bounded_command 20 blacksmith testbox status --id "$testbox_id" >"$status_log" 2>&1
status_command_status=$?
set -e
status_value=""
status_absent=0
if (( status_command_status == 0 )); then
set +e
status_value="$(parse_cli_output "$status_log")"
status_parse_status=$?
set -e
case "$status_parse_status" in
0) ;;
3) echo "status omitted the owned Testbox $testbox_id; refusing cleanup" >&2; exit 66 ;;
66) echo "status ownership context differs from the warmup receipt; refusing cleanup" >&2; exit 66 ;;
*) echo "could not parse status for owned Testbox $testbox_id; refusing cleanup" >&2; exit "$status_parse_status" ;;
esac
elif is_known_absence "$status_log"; then
status_absent=1
else
echo "failed to inspect owned Testbox $testbox_id before cleanup; refusing stop" >&2
exit "$status_command_status"
fi
if (( status_absent == 0 )) && is_active "$status_value" && (( inventory_row_present == 0 )); then
echo "owned Testbox is active but absent from the inventory; refusing cleanup" >&2
exit 66
fi
if (( status_absent == 0 )) && ! is_active "$status_value" && ! is_terminal "$status_value"; then
echo "unknown status for owned Testbox $testbox_id; refusing cleanup" >&2
exit 66
fi
preview_path="$evidence_dir/cleanup-preview.json"
python3 - "$preview_path" "$testbox_id" "${status_value:-absent}" "$inventory_row_present" "$receipt_workflow" "$receipt_job" "$receipt_ref" "$receipt_source_sha" "$receipt_source_tree_sha" "$receipt_ghostty_sha" <<'PY'
import json
import pathlib
import sys
(path, testbox_id, status, inventory_present, workflow, job, ref,
source_sha, source_tree_sha, ghostty_sha) = sys.argv[1:]
payload = {
"schema": 1,
"testbox_id": testbox_id,
"status": status,
"inventory_row_present": bool(int(inventory_present)),
"workflow": workflow,
"job": job,
"source_ref": ref,
"source_sha": source_sha,
"source_tree_sha": source_tree_sha,
"ghostty_gitlink_sha": ghostty_sha,
}
out = pathlib.Path(path)
out.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
out.chmod(0o600)
PY
preview_sha="$(sha256_file "$preview_path" | awk '{print $1}')"
printf 'Testbox cleanup preview: id=%s status=%s inventory_row=%s workflow=%s job=%s ref=%s\n' \
"$testbox_id" "${status_value:-absent}" "$inventory_row_present" "$receipt_workflow" "$receipt_job" "$receipt_ref"
printf 'Preview SHA: %s\n' "$preview_sha"
if [[ "$operator_confirmation" == "PREVIEW" ]]; then
echo "Review the preview, then rerun with the same token and STOP:$preview_sha to authorize stop." >&2
exit 75
fi
expected_preview_sha="${operator_confirmation#STOP:}"
if [[ "$operator_confirmation" != "PREVIEW" && "$expected_preview_sha" != "$preview_sha" ]]; then
echo "current cleanup preview differs from the supplied confirmation; refusing stop" >&2
exit 67
fi
stop_log="$evidence_dir/stop.log"
list_log="$evidence_dir/list-after-stop.log"
cleanup_status=0
poll_deadline=$((SECONDS + 120))
poll_attempt=0
if (( status_absent == 1 )) || is_terminal "$status_value"; then
printf 'Testbox %s is already terminal or absent; no stop request needed\n' "$testbox_id" >"$stop_log"
else
set +e
bounded_command 20 blacksmith testbox stop --id "$testbox_id" >"$stop_log" 2>&1
stop_status=$?
set -e
if (( stop_status != 0 )); then
if is_known_absence "$stop_log"; then
printf 'stop reached a known terminal or absent state for %s; continuing\n' "$testbox_id" >&2
else
echo "failed to stop Testbox $testbox_id; see $stop_log" >&2
cleanup_status=$stop_status
fi
fi
fi
# Poll the ID-specific endpoint until cancellation propagates. Never treat a
# different row in the global inventory as proof that this ID is terminal.
while :; do
poll_attempt=$((poll_attempt + 1))
: >"$status_log"
set +e
bounded_command 20 blacksmith testbox status --id "$testbox_id" >"$status_log" 2>&1
status_command_status=$?
set -e
if (( status_command_status == 0 )); then
set +e
status_value="$(parse_cli_output "$status_log")"
status_parse_status=$?
set -e
if (( status_parse_status != 0 )); then
echo "could not parse post-stop status for $testbox_id; see $status_log" >&2
(( cleanup_status == 0 )) && cleanup_status=66
break
fi
if is_terminal "$status_value"; then
break
fi
if ! is_active "$status_value"; then
echo "unknown post-stop status for $testbox_id; see $status_log" >&2
(( cleanup_status == 0 )) && cleanup_status=66
break
fi
elif is_known_absence "$status_log"; then
break
else
echo "failed to inspect Testbox $testbox_id after cleanup; see $status_log" >&2
(( cleanup_status == 0 )) && cleanup_status=$status_command_status
break
fi
if (( SECONDS >= poll_deadline )); then
echo "Testbox $testbox_id is still active after bounded cleanup polling" >&2
(( cleanup_status == 0 )) && cleanup_status=1
break
fi
sleep_seconds=$((poll_attempt < 6 ? poll_attempt * 2 : 10))
sleep "$sleep_seconds"
done
set +e
bounded_command 20 blacksmith testbox list --all >"$list_log" 2>&1
list_status=$?
set -e
if (( list_status != 0 )); then
echo "failed to list Testboxes after stopping $testbox_id; see $list_log" >&2
(( cleanup_status == 0 )) && cleanup_status=$list_status
else
set +e
listed_status="$(parse_cli_output "$list_log")"
listed_parse_status=$?
set -e
case "$listed_parse_status" in
0)
if is_active "$listed_status"; then
echo "Testbox $testbox_id is still active after cleanup; see $list_log" >&2
(( cleanup_status == 0 )) && cleanup_status=1
elif ! is_terminal "$listed_status"; then
echo "unknown status for Testbox $testbox_id in inventory: $listed_status" >&2
(( cleanup_status == 0 )) && cleanup_status=66
fi
;;
3) ;;
66)
echo "Testbox $testbox_id ownership changed in final inventory; see $list_log" >&2
(( cleanup_status == 0 )) && cleanup_status=66
;;
*)
echo "could not parse final Testbox inventory; see $list_log" >&2
(( cleanup_status == 0 )) && cleanup_status=$listed_parse_status
;;
esac
fi
if (( cleanup_status != 0 )); then
exit "$cleanup_status"
fi
printf 'verified Testbox %s is no longer active\n' "$testbox_id"
+108
View File
@@ -0,0 +1,108 @@
#!/usr/bin/env bash
set -euo pipefail
# This is the trusted-main replacement for the upstream keepalive composite.
# It deliberately reads the Testbox token only after GitHub has evaluated the
# protected environment and the workflow's main/repository guard.
state=/tmp/.testbox
job_status="${JOB_STATUS:-failure}"
if [[ ! -d "$state" ]]; then
if [[ "$job_status" == "success" ]]; then
echo "Testbox validation passed, but no registration state was returned" >&2
exit 1
fi
echo "Testbox registration state is absent after a failed setup; no phone-home is possible" >&2
exit 0
fi
for required_file in testbox_id installation_model_id auth_token api_url runner_host runner_ssh_port adopted_run_id working_directory; do
test -s "$state/$required_file" || {
echo "missing Testbox state file: $state/$required_file" >&2
exit 1
}
done
testbox_id="$(<"$state/testbox_id")"
installation_model_id="$(<"$state/installation_model_id")"
auth_token="$(<"$state/auth_token")"
api_url="$(<"$state/api_url")"
runner_host="$(<"$state/runner_host")"
runner_ssh_port="$(<"$state/runner_ssh_port")"
working_directory="$(<"$state/working_directory")"
adopted_run_id="$(<"$state/adopted_run_id")"
[[ "$testbox_id" =~ ^tbx_[A-Za-z0-9_-]+$ ]]
[[ "$installation_model_id" =~ ^[0-9]+$ ]]
phone_home() {
local status="$1"
local payload
payload="$(jq -n \
--arg testbox_id "$testbox_id" \
--arg runner_host "$runner_host" \
--arg runner_ssh_port "$runner_ssh_port" \
--arg working_directory "$working_directory" \
--arg adopted_run_id "$adopted_run_id" \
--arg status "$status" \
--argjson installation_model_id "$installation_model_id" \
'{testbox_id: $testbox_id, installation_model_id: $installation_model_id, status: $status, ip_address: $runner_host, ssh_port: $runner_ssh_port, working_directory: $working_directory, adopted_run_id: $adopted_run_id, metadata: {}}')"
curl --fail --silent --show-error --connect-timeout 2 --max-time 10 \
-X POST "$api_url/api/testbox/phone-home" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $auth_token" \
--data "$payload" >/dev/null
}
phone_home_with_retry() {
local status="$1"
local attempt
for attempt in 1 2 3 4 5; do
if phone_home "$status"; then
return 0
fi
if (( attempt < 5 )); then
sleep $((attempt * 2))
fi
done
return 1
}
if [[ "$job_status" != "success" ]]; then
if ! phone_home_with_retry hydration_failed; then
echo "warning: could not report hydration_failed" >&2
fi
echo "Testbox hydration failed; no ready state was published" >&2
exit 0
fi
if ! phone_home_with_retry ready; then
echo "ready phone-home failed after bounded retries" >&2
phone_home_with_retry hydration_failed || echo "warning: could not report hydration_failed" >&2
exit 1
fi
printf 'Testbox ready: %s (%s)\n' "$testbox_id" "$runner_host"
idle_timeout_minutes="10"
if [[ -s "$state/idle_timeout" ]]; then
idle_timeout_minutes="$(cat "$state/idle_timeout")"
fi
[[ "$idle_timeout_minutes" =~ ^[0-9]+$ ]] || idle_timeout_minutes=10
last_activity="$(date +%s)"
idle_timeout_seconds=$((idle_timeout_minutes * 60))
while :; do
sleep 30
now="$(date +%s)"
if ss -tnp 2>/dev/null | grep -Eq ":${runner_ssh_port}([^0-9]|$)"; then
last_activity="$now"
elif [[ -f "$HOME/.testbox-last-activity" ]]; then
marker_mtime="$(stat -c %Y "$HOME/.testbox-last-activity" 2>/dev/null || stat -f %m "$HOME/.testbox-last-activity")"
if [[ "$marker_mtime" -gt "$last_activity" ]]; then
last_activity="$marker_mtime"
fi
fi
if (( now - last_activity >= idle_timeout_seconds )); then
phone_home_with_retry completed || echo "warning: could not report completed" >&2
exit 0
fi
done
+380
View File
@@ -0,0 +1,380 @@
---
name: blacksmith-testbox
description: >
Provision and reuse a trusted Blacksmith Testbox for cmux-tui Rust builds,
capture remote timings, download raw evidence, and clean up safely. Never run
cargo, rustc, or Zig builds on the local Mac.
---
# cmux-tui Blacksmith Testbox
This lane is Linux-only. It uses
`.github/workflows/cmux-tui-testbox-warmup.yml`, job
`cmux-tui-rust`, on `blacksmith-32vcpu-ubuntu-2404`. The workflow is a
setup-only entrypoint for a reusable Testbox. It refuses every ref except
`refs/heads/main`, checks out and verifies that commit, initializes the
`ghostty` source submodule, installs Linux C/LLVM headers, installs the
repository-pinned Zig and Rust toolchains, fetches Zig and Cargo dependencies,
records runner/toolchain/Ghostty identity in JSON, and then hands control back
to Testbox. It does not run Rust tests or Rust compilation during warmup.
`zig build --fetch` only hydrates Zig packages and exits before compilation.
The repository's single Rust toolchain source is
`cmux-tui/rust-toolchain.toml`. The workflow invokes
`./.github/actions/setup-cmux-tui-rust`, so a workflow-specific Rust version
must never be added.
## Hard safety and trust boundary
This is a main-controlled broker lane. `useblacksmith/begin-testbox` writes
`/tmp/.testbox/auth_token` into the job, and `permissions: contents: read` does
not stop any later step from reading it. The lane therefore holds one rule
above all others: **nothing a candidate branch can edit ever runs inside that
job.**
`blacksmith testbox warmup` resolves the workflow file and the hydrated source
from the same `--ref`, so the two cannot be separated. The lane resolves that
by hydrating `main` only. The first step fails any ref other than
`refs/heads/main`, and every guard, the pinned `begin-testbox` SHA, and the
keepalive all come from `main`. A candidate revision never becomes the workflow
definition, and its `build.zig`, `rust-toolchain.toml`, and composite actions
never execute in the token-bearing job.
A candidate reaches the Testbox afterwards, through `blacksmith testbox run`,
which synchronizes a maintainer's local worktree onto the warm VM. That command
runs as an authenticated Blacksmith organization member who could already read
the box, so it moves no trust boundary. It does mean the box holds one
operator's revision at a time: a Testbox ID belongs to one worktree and one
operator.
Before using the lane, a repository administrator must create the
`blacksmith-testbox-trusted` GitHub environment, configure required reviewers
(or an equivalent manual approval rule), disable administrator bypass, leave
the environment secret set empty, and set its deployment branch rule to exactly
`main` with no wildcard and no fork rule. GitHub evaluates that approval and
branch rule before the job's first step, so both precede `begin-testbox`. The
approval is what authorizes spending a 32 vCPU box, because the code path is
already fixed by `main`.
This lane needs no `BLACKSMITH_TESTBOX_REVIEWED_REF` or
`BLACKSMITH_TESTBOX_REVIEWED_SHA` environment variable. Those pins existed to
make a candidate-controlled workflow safe. Delete them if they are still
configured; the broker's `refs/heads/main` guard replaces them, and it cannot
be edited from a pull request.
If the environment is deleted, renamed, or loses its exact `main` branch rule,
disable the lane and stop rather than changing the workflow to proceed. The
workflow cannot manufacture those controls, so configuration drift makes the
lane unavailable rather than safe.
The helper retains the `CMUX_TESTBOX_REMOTE=1` guard for accidental local
launches, and additionally requires the Blacksmith VM kernel metadata marker
and matching `/tmp/.testbox` state. The environment flag remains caller
controlled and is not an authentication mechanism.
* Never run `cargo`, `rustc`, `rustup`, `zig build`, or another Rust/Zig build
command on Lawrence's Mac. This includes local fallback builds and local
test commands.
* Run every Blacksmith CLI command from the root of the intended isolated
worktree. The CLI synchronizes that directory with `rsync --delete`; it can
delete remote files that are not represented locally.
* Put remote build commands inside `blacksmith testbox run`. The benchmark
helper also requires the Testbox VM guard, so an accidental local launch
exits before invoking a compiler.
* Use this Testbox only for Linux-compatible cmux-tui work. Use hosted macOS
workflows for Swift, Xcode, XCTest, GUI, and app-host verification.
## Authentication and CLI installation
Check authentication without printing credentials:
```bash
blacksmith auth whoami
blacksmith --version
```
Use the repository or organization-approved, pinned Blacksmith CLI artifact or
package-manager version. Record its version in the evidence. Do not install a
mutable remote script with `curl ... | sh`; if the approved pinned artifact is
not available, stop and ask the tooling owner rather than weakening this lane.
The repository does not currently pin a checksum-verified CLI artifact, so CLI
provenance is a trusted-lane operational limitation: do not silently upgrade or
substitute a version, and retain `blacksmith --version` with each evidence set.
## Exact source contract
Two commits matter and they are normally different. The **hydration commit** is
`main`, and it is what the broker warmed the Cargo registry, Zig cache, and
toolchains from. The **benchmarked commit** is your local HEAD, and it reaches
the box through `blacksmith testbox run`. The stage helper records both and
sets `hydration.matches_benchmarked_source` in each stage JSON.
Do not pass a commit SHA to `blacksmith testbox warmup --ref`. Blacksmith and
GitHub reject a raw SHA with HTTP 422 (`No ref found`), and this lane accepts
only `main` anyway.
Your local HEAD must be committed and clean, because every remote stage
verifies HEAD, its tree, the `ghostty` gitlink, the initialized Ghostty HEAD,
and clean status before it builds. Pushing that commit is not required for the
build, since the sync carries the objects, but push it anyway so the evidence
names a fetchable revision. Initialize the public Ghostty submodule once, then
run this preflight from the clean worktree root:
```bash
set -euo pipefail
git submodule update --init ghostty
cd "$(git rev-parse --show-toplevel)"
SOURCE_REF="$(git symbolic-ref --short HEAD)"
if [[ ! "$SOURCE_REF" =~ ^[A-Za-z0-9._/-]+$ || "$SOURCE_REF" == *..* || "$SOURCE_REF" == */ || "$SOURCE_REF" == *//* ]]; then
echo "HEAD must name a plain branch" >&2
exit 1
fi
SOURCE_SHA="$(git rev-parse HEAD)"
ghostty_entry="$(git ls-tree HEAD ghostty)"
[[ "$ghostty_entry" =~ ^160000[[:space:]]commit[[:space:]][0-9a-f]{40}[[:space:]]ghostty$ ]] || {
echo "HEAD:ghostty is not a gitlink" >&2
exit 1
}
GHOSTTY_SHA="$(git rev-parse HEAD:ghostty)"
[[ "$SOURCE_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$GHOSTTY_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$(git -C ghostty rev-parse HEAD)" == "$GHOSTTY_SHA" ]]
[[ -z "$(git status --porcelain=v1 --untracked-files=normal)" ]]
[[ -z "$(git -C ghostty status --porcelain=v1 --untracked-files=normal)" ]]
BROKER_SHA="$(git ls-remote --exit-code --heads https://github.com/manaflow-ai/cmux.git refs/heads/main | awk 'NR == 1 { print $1 }')"
printf 'source_ref=%s\nsource_sha=%s\nghostty_gitlink_sha=%s\nbroker_main_sha=%s\n' \
"$SOURCE_REF" "$SOURCE_SHA" "$GHOSTTY_SHA" "$BROKER_SHA"
```
`main` can move after this check, which only changes cache warmth, never
correctness: the stage helper reads the hydration commit back out of the setup
marker and records it. A moved or dirty benchmarked checkout still fails
closed.
The hydrated toolchain is a hard gate. If your branch pins a different Rust or
Zig than `main`, the stage helper exits 66 rather than reporting a timing
against caches it did not warm. Rebase onto `main` or warm a fresh box from a
`main` that carries the same pins.
## Warmup and identity capture
Warm from `main`. The broker refuses every other ref, and your candidate does
not belong here; it arrives later through the sync:
```bash
WORKFLOW=.github/workflows/cmux-tui-testbox-warmup.yml
JOB=cmux-tui-rust
blacksmith testbox warmup "$WORKFLOW" \
--ref main \
--job "$JOB" \
--idle-timeout 30
```
Save the returned `tbx_...` ID. One ID belongs to one worktree and one
operator. The workflow concurrency group keys every setup request by Testbox
ID, including requests for different source SHAs. Source identity is validated
separately, and each remote stage holds `testbox-benchmark/.stage.lock` through
its build and artifact writes. Blacksmith's CLI exposes no pre-rsync lease, so
that remote lock cannot serialize the CLI's initial sync. One Testbox ID must
therefore have one owning worktree/operator; do not issue concurrent `run` or
download commands from independent clients. This is an explicit trusted-lane
limitation, not a claim of multi-client Testbox isolation.
Wait for setup and capture the exact run identity:
```bash
blacksmith testbox status --id "$TBX" --wait --wait-timeout 15m
```
The setup job's `setup-identity.json` artifact and each stage helper's
pre-build JSON record are the identity transcripts. The helper verifies the
Testbox VM marker, claimed Testbox ID, the hydration marker's own consistency,
the runner class, and then the synchronized source commit/tree, Ghostty
gitlink/checkout, and clean status before it invokes Cargo, repeating the
source checks after the build. Keep the setup artifact URL or download it into `$OUT`;
it records runner/toolchain/Ghostty identity independently of the stage helper.
The setup JSON contains the workflow run, the hydrated `main` ref/SHA/tree,
Ghostty gitlink and checkout SHA, runner label/architecture/CPU identity, and pinned
Rust/Zig/toolchain-file metadata. A successful setup also copies that JSON to
`/tmp/.testbox/cmux-tui-rust-setup-identity.json`; the stage helper refuses a
missing or mismatched marker, so a failed hydration cannot be benchmarked.
Never print or download `/tmp/.testbox/auth_token`.
## Remote benchmark stages
The detailed, receipt-producing orchestration in `benchmark.md` is the required
entry point for a complete benchmark. It creates the unique `OUT_ROOT`, receipt,
cleanup token, setup artifact capture, and cleanup preview state. Do not copy
only this stage loop into an ad hoc shell without those prerequisites.
Before each stage, recompute `SOURCE_SHA` and `GHOSTTY_SHA` and repeat the
clean pushed-branch preflight, including the protected reviewed ref/SHA pins.
Pass the expected values as validated arguments;
the helper does not trust the remote checkout or a caller-supplied expected SHA
without comparing it to Git metadata:
```bash
# Run this orchestration block in Bash, not an interactive zsh session.
run_stage() {
local stage="$1"
local run_status download_status=0
set +e
printf -v remote_command \
'CMUX_TESTBOX_REMOTE=1 CMUX_TESTBOX_ID=%q %q %q %q %q' \
"$TBX" ./scripts/blacksmith-cmux-tui-testbox-stage.sh \
"$stage" "$SOURCE_SHA" "$GHOSTTY_SHA"
./scripts/blacksmith-bounded-command.sh 1500 \
blacksmith testbox run --id "$TBX" --debug \
"$remote_command" >"$OUT/$stage.run.log" 2>&1
run_status=$?
set -e
cat "$OUT/$stage.run.log"
# Download immediately. Blacksmith's next rsync may delete or replace remote
# files, so a one-time download after all stages is insufficient.
: >"$OUT/$stage.download.log"
for suffix in json time log; do
if ! ./scripts/blacksmith-bounded-command.sh 120 \
blacksmith testbox download --id "$TBX" \
"testbox-benchmark/$stage.$suffix" "$OUT/raw/$stage.$suffix" \
>>"$OUT/$stage.download.log" 2>&1; then
download_status=1
fi
done
cat "$OUT/$stage.download.log"
if (( run_status != 0 )); then
return "$run_status"
fi
return "$download_status"
}
```
The helper supports exactly `first-clean`, `incremental-noop`, and
`changed-file`. Each remote Cargo build is bounded to 20 minutes with a
30-second kill grace period. It records a schema-2 JSON object for each stage
containing:
* expected and observed source commit/tree identity before and after the build;
* expected and observed Ghostty gitlink and initialized submodule HEAD;
* clean/dirty file lists and source restoration status;
* Testbox ID and adopted workflow run ID;
* runner label, hostname, architecture, CPU count, and `uname`; the setup
workflow fails closed unless the actual runner is x64 with 32 CPUs;
* active Rust toolchain, `rustc`, Cargo, Zig, lockfile/toolchain hashes, and
Ghostty package-manifest hash; and
* Cargo exit status, `/usr/bin/time -p` values, and CLI transcript timing.
`first-clean` removes only the remote `cmux-tui/target` directory before a
`cargo build -p cmux-tui --locked`. `incremental-noop` repeats that command
without changing source. `changed-file` appends a comment to
`cmux-tui/crates/cmux-tui/src/main.rs`, builds, and restores the original bytes
before emitting its final record. A dirty or mismatched source before any
stage, after restoration, or in the Ghostty submodule aborts the stage.
After all successful downloads, aggregate and verify the records:
```bash
python3 - "$OUT" "$SOURCE_SHA" "$GHOSTTY_SHA" "$TBX" <<'PY'
import json
import pathlib
import subprocess
import sys
out = pathlib.Path(sys.argv[1])
expected_source, expected_ghostty, testbox_id = sys.argv[2:]
expected_tree = subprocess.check_output(
["git", "rev-parse", f"{expected_source}^{{tree}}"], text=True
).strip()
required = {"first-clean", "incremental-noop", "changed-file"}
records = []
for path in sorted((out / "raw").glob("*.json")):
record = json.loads(path.read_text(encoding="utf-8"))
records.append(record)
if {record.get("stage") for record in records} != required:
raise SystemExit("timing evidence is missing one or more benchmark stages")
for record in records:
if record.get("testbox", {}).get("id") != testbox_id:
raise SystemExit(f"{record.get('stage')} has the wrong Testbox ID")
source = record.get("source", {})
if source.get("expected_commit_sha") != expected_source or source.get("expected_tree_sha") != expected_tree:
raise SystemExit(f"{record.get('stage')} has the wrong expected source identity")
for side in ("before", "after"):
snapshot = source.get(side, {})
if snapshot.get("commit_sha") != expected_source or snapshot.get("tree_sha") != expected_tree:
raise SystemExit(f"{record.get('stage')} has the wrong {side} source SHA")
if snapshot.get("dirty_files"):
raise SystemExit(f"{record.get('stage')} has dirty top-level source")
ghostty = snapshot.get("ghostty", {})
if ghostty.get("gitlink_sha") != expected_ghostty or ghostty.get("head_sha") != expected_ghostty:
raise SystemExit(f"{record.get('stage')} has mismatched Ghostty identity")
if ghostty.get("dirty_files"):
raise SystemExit(f"{record.get('stage')} has dirty Ghostty source")
if not record.get("ok"):
raise SystemExit(f"{record.get('stage')} did not complete successfully")
with (out / "timings.json").open("w", encoding="utf-8") as handle:
json.dump({"schema": 2, "source_sha": expected_source, "ghostty_gitlink_sha": expected_ghostty, "testbox_id": testbox_id, "stages": records}, handle, indent=2, sort_keys=True)
handle.write("\n")
PY
```
Keep `raw/*.json`, `raw/*.time`, `raw/*.log`, every `*.run.log` and download
log, the setup artifact, and the source manifest in a new, unique
`.cmux-scratch/` evidence directory. Never reuse a prior SHA-only directory;
refuse to overwrite historical records. Do not add credentials or private keys.
## Fail-safe cleanup
Always download before cleanup. Use the checked-in cleanup helper rather than
ignoring errors with `|| true`. After an independent operator decides the exact
box may be destroyed, pass the ownership token generated with the warmup receipt
and the literal `STOP`; never print the token:
```bash
CLEANUP_TOKEN="${CLEANUP_TOKEN:-}"
[[ "$CLEANUP_TOKEN" =~ ^[0-9a-f]{32}$ ]] || {
echo "use the ownership token emitted by the warmup receipt" >&2
exit 64
}
scripts/blacksmith-testbox-cleanup.sh "$TBX" "$OUT" "$CLEANUP_TOKEN" PREVIEW
# Review cleanup-preview.json, then rerun with STOP:<sha256(cleanup-preview.json)>.
```
It records a pre-stop status preview, stop result, post-stop status, and
`list --all` output. The preview must match the receipt's workflow, job, and
branch before any stop is attempted. Cleanup is destructive and requires a fresh `STOP:<sha256(cleanup-preview.json)>`
confirmation after reviewing the current receipt-bound preview.
It verifies that the specific Testbox ID is terminal or absent from the active
inventory, accepts the known terminal states `completed`, `stopped`, `cancelled`,
`failed`, `terminated`, and `hydration_failed`, plus a 409 saying the box is
already stopped or completed, and polls for up to two minutes while cancellation
propagates. Other stop, status, or list failures remain failures.
Put it in an `EXIT` trap only after an independent operator exports
`CONFIRM_TESTBOX_STOP_SHA` containing the SHA-256 of a separately reviewed
`cleanup-preview.json`; otherwise preserve the benchmark's original exit status
and leave the box for manual cleanup. The detailed benchmark writes a
receipt and ownership token for the exact ID returned by warmup; cleanup refuses an ID
or token that is not bound to that receipt. If warmup fails before returning an
ID, retain before/after inventory but do not automatically stop a box, because
an inventory diff cannot prove ownership across concurrent operators. Reconcile
that orphan manually through the Blacksmith control plane.
## Timing interpretation
The benchmark reports two clocks. The local CLI transcript measures sync,
transport, queueing, and the remote command. The downloaded `/usr/bin/time -p`
record measures the remote `cargo build -p cmux-tui --locked` command. Compare
remote `real` or `time_real_seconds` values for build performance, and retain
CLI wall time when evaluating Testbox overhead.
`first-clean` is target-clean but dependency-warm: warmup runs `cargo fetch` and
`zig build --fetch`, and the workflow may restore registry, git, and Zig caches.
`incremental-noop` measures a second build on the same VM. `changed-file` is a
controlled source change on that same VM. These are deliberately different
from a cold-VM benchmark.
The existing 32-vCPU evidence at
`.cmux-scratch/blacksmith-testbox-e40704611ac35f4ffa153/` remains historical
provenance for setup SHA `e40704611ac35f0e3a806841a9eae383f4ffa153`, Testbox
`tbx_01kzxebn91nhatkv4ygevh06vs`, and workflow run `31696013711`. Its raw
records and cleanup result must not be rewritten when validating this hardening
change.
+473
View File
@@ -0,0 +1,473 @@
# cmux-tui Testbox timing plan
Run this plan from the root of the isolated cmux worktree. It never invokes a
Rust tool on the local Mac. Every `cargo`, `rustc`, and `zig` command below is
inside a quoted command passed to `blacksmith testbox run`, or inside the
setup-only GitHub job on the remote Linux runner.
## Fixed lane contract
| Item | Value |
| --- | --- |
| Workflow | `.github/workflows/cmux-tui-testbox-warmup.yml` |
| Job | `cmux-tui-rust` |
| Runner | `blacksmith-32vcpu-ubuntu-2404` |
| Protected environment | `blacksmith-testbox-trusted` |
| Rust source of truth | `cmux-tui/rust-toolchain.toml`, via `./.github/actions/setup-cmux-tui-rust` |
| Remote build helper | `scripts/blacksmith-cmux-tui-testbox-stage.sh` |
| Cleanup helper | `scripts/blacksmith-testbox-cleanup.sh` |
| Remote output | `testbox-benchmark/` |
A repository administrator must configure the protected environment with
required reviewers, no secrets, administrator bypass disabled, and a deployment
branch rule of exactly `main` before this plan is usable. The lane needs no
environment variables: the workflow refuses any ref except `refs/heads/main`,
so `main` alone decides what code runs in the token-bearing job. Verify that
configuration before each run, and stop instead of treating the environment
name as a guard if it drifts.
`begin-testbox` exposes its auth token to commands in the Testbox, so
`contents: read` is not a trust boundary and the token is not sandboxed. The
repository does not currently pin a checksum-verified Blacksmith CLI artifact;
that is a trusted-lane operational limitation. Use only the organization-
approved CLI, record `blacksmith --version`, and stop rather than silently
substituting a version.
The warmup job only checks out `main`, initializes `ghostty`,
installs Linux headers/tools, installs the pinned Zig and Rust toolchains,
fetches Cargo and Zig dependencies, and records JSON identity. `zig build
--fetch` is the only build-system operation in warmup, and it exits before
compiling. Rust builds happen only in the three explicit benchmark runs.
The current Blacksmith catalog reports the requested x64 label as 32 vCPU and
121.6 GB, while the ARM label with the same vCPU count reports 96 GB. Keep the
requested `blacksmith-32vcpu-ubuntu-2404` label unless repository Linux
constraints make x64 impossible, and record the catalog result with the run.
## Exact benchmarked source and Ghostty identity
Warmup hydrates `main`. This section pins the separate commit you benchmark,
which `blacksmith testbox run` synchronizes onto the warm box. A raw commit SHA
is not a supported warmup ref (HTTP 422, `No ref found`), and this lane accepts
only `main` regardless. Carry the benchmarked SHA as an assertion:
```bash
set -euo pipefail
git submodule update --init ghostty
cd "$(git rev-parse --show-toplevel)"
SOURCE_REF="$(git symbolic-ref --short HEAD)"
if [[ ! "$SOURCE_REF" =~ ^[A-Za-z0-9._/-]+$ || "$SOURCE_REF" == *..* || "$SOURCE_REF" == */ || "$SOURCE_REF" == *//* ]]; then
echo "HEAD must name a supported pushed branch ref" >&2
exit 1
fi
SOURCE_SHA="$(git rev-parse HEAD)"
SOURCE_TREE_SHA="$(git rev-parse 'HEAD^{tree}')"
ghostty_entry="$(git ls-tree HEAD ghostty)"
[[ "$ghostty_entry" =~ ^160000[[:space:]]commit[[:space:]][0-9a-f]{40}[[:space:]]ghostty$ ]] || {
echo "HEAD:ghostty is not a gitlink" >&2
exit 1
}
GHOSTTY_SHA="$(git rev-parse HEAD:ghostty)"
[[ -n "$SOURCE_REF" ]]
[[ "$SOURCE_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$GHOSTTY_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$(git -C ghostty rev-parse HEAD)" == "$GHOSTTY_SHA" ]]
[[ -z "$(git status --porcelain=v1 --untracked-files=normal)" ]]
[[ -z "$(git -C ghostty status --porcelain=v1 --untracked-files=normal)" ]]
remote_sha="$(git ls-remote --exit-code origin "refs/heads/$SOURCE_REF" | awk 'NR == 1 { print $1 }')"
[[ "$remote_sha" == "$SOURCE_SHA" ]] || {
echo "push the exact clean branch head before warming Testbox" >&2
exit 1
}
EVIDENCE_ROOT="$PWD/.cmux-scratch"
if [[ -e "$EVIDENCE_ROOT/blacksmith-testbox-$SOURCE_SHA" ]]; then
RUN_SUFFIX="$(date -u +%Y%m%dT%H%M%SZ)-$$"
else
RUN_SUFFIX="initial-$$"
fi
OUT_ROOT="$EVIDENCE_ROOT/blacksmith-testbox-$SOURCE_SHA-$RUN_SUFFIX"
if [[ -e "$OUT_ROOT" ]]; then
echo "evidence directory already exists; choose a new run path: $OUT_ROOT" >&2
exit 1
fi
mkdir -p "$OUT_ROOT/raw"
python3 - "$SOURCE_REF" "$SOURCE_SHA" "$SOURCE_TREE_SHA" "$GHOSTTY_SHA" > "$OUT_ROOT/source.json" <<'PY'
import json
import sys
ref, sha, tree, ghostty = sys.argv[1:]
print(json.dumps({
"source_ref": ref,
"source_sha": sha,
"source_tree_sha": tree,
"ghostty_gitlink_sha": ghostty,
}, indent=2, sort_keys=True))
PY
assert_source_unchanged() {
local current_ref current_sha current_tree current_ghostty remote_sha ghostty_entry
current_ref="$(git symbolic-ref --short HEAD)"
current_sha="$(git rev-parse HEAD)"
current_tree="$(git rev-parse 'HEAD^{tree}')"
ghostty_entry="$(git ls-tree HEAD ghostty)"
current_ghostty="$(git rev-parse HEAD:ghostty)"
remote_sha="$(git ls-remote --exit-code origin "refs/heads/$SOURCE_REF" | awk 'NR == 1 { print $1 }')"
if [[ "$current_ref" != "$SOURCE_REF" || "$current_sha" != "$SOURCE_SHA" ||
"$current_tree" != "$SOURCE_TREE_SHA" || "$current_ghostty" != "$GHOSTTY_SHA" ||
! "$ghostty_entry" =~ ^160000[[:space:]]commit[[:space:]][0-9a-f]{40}[[:space:]]ghostty$ ||
"$remote_sha" != "$SOURCE_SHA" ||
-n "$(git status --porcelain=v1 --untracked-files=normal)" ||
-n "$(git -C ghostty status --porcelain=v1 --untracked-files=normal)" ]]; then
echo "source branch, tree, Ghostty gitlink, remote head, or clean status changed" >&2
return 1
fi
}
assert_source_unchanged
```
`assert_source_unchanged` runs before every stage below. If the branch moved,
the worktree became dirty, or the Ghostty pointer changed, stop the box and
start a new evidence directory. Do not silently substitute the new SHA.
## Warmup and setup identity
Use the branch ref, not `SOURCE_SHA`, in warmup:
```bash
WORKFLOW=.github/workflows/cmux-tui-testbox-warmup.yml
JOB=cmux-tui-rust
OUT_ROOT="${OUT_ROOT:?set by the exact-source preflight above}"
OUT="$OUT_ROOT"
TBX=""
warmup_testbox_id=""
cleanup_token=""
before_list_status=125
mkdir -p "$OUT/raw"
cleanup() {
local result=$?
local cleanup_status=0
local after_list_status=125
trap - EXIT
if [[ -n "$TBX" && -n "$cleanup_token" && -n "${CONFIRM_TESTBOX_STOP_SHA:-}" ]]; then
set +e
scripts/blacksmith-testbox-cleanup.sh "$TBX" "$OUT" "$cleanup_token" "STOP:${CONFIRM_TESTBOX_STOP_SHA}"
cleanup_status=$?
set -e
else
# Without the CLI receipt there is no proof that a newly listed box belongs
# to this invocation. Report inventory, but never stop another operator's box.
set +e
./scripts/blacksmith-bounded-command.sh 60 \
blacksmith testbox list --all >"$OUT/list-after-warmup-failure.log" 2>&1
after_list_status=$?
set -e
if (( after_list_status != 0 )); then
cleanup_status="$after_list_status"
echo "could not capture post-failure Testbox inventory" >&2
else
cleanup_status=1
echo "warmup returned no owned Testbox receipt; no automatic stop was attempted" >&2
fi
fi
if (( result == 0 && cleanup_status != 0 )) && [[ -n "${CONFIRM_TESTBOX_STOP_SHA:-}" ]]; then
result="$cleanup_status"
fi
exit "$result"
}
trap cleanup EXIT
blacksmith auth whoami
blacksmith --version >"$OUT/blacksmith-version.txt"
cat "$OUT/blacksmith-version.txt"
blacksmith runners catalog >"$OUT/runner-catalog.json"
set +e
./scripts/blacksmith-bounded-command.sh 60 \
blacksmith testbox list --all >"$OUT/list-before-warmup.log" 2>&1
before_list_status=$?
set -e
cat "$OUT/list-before-warmup.log"
if (( before_list_status != 0 )); then
echo "refusing to warm a Testbox without a baseline inventory" >&2
exit "$before_list_status"
fi
set +e
./scripts/blacksmith-bounded-command.sh 1200 \
blacksmith testbox warmup "$WORKFLOW" \
--ref main \
--job "$JOB" \
--idle-timeout 30 \
>"$OUT/warmup.log" 2>&1
warmup_status=$?
set -e
cat "$OUT/warmup.log"
if (( warmup_status != 0 )); then
# Do not parse IDs from a failed CLI transcript. It may contain a stale ID
# from an error message, and cleanup is intentionally receipt-bound.
exit "$warmup_status"
fi
set +e
warmup_testbox_id="$(python3 - "$OUT/warmup.log" <<'PY'
import re
import sys
text = open(sys.argv[1], encoding="utf-8").read()
ids = re.findall(r"\btbx_[A-Za-z0-9_-]+\b", text)
if not ids:
raise SystemExit("warmup output did not contain a Testbox ID")
print(ids[-1])
PY
)"
parse_status=$?
set -e
if (( parse_status != 0 )); then
exit "$parse_status"
fi
umask 077
set +e
cleanup_token="$(python3 - "$OUT/testbox-receipt.json" "$warmup_testbox_id" "$WORKFLOW" "$JOB" "$SOURCE_REF" "$SOURCE_SHA" "$SOURCE_TREE_SHA" "$GHOSTTY_SHA" <<'PY'
import datetime as dt
import json
import pathlib
import secrets
import sys
path, testbox_id, workflow, job, source_ref, source_sha, source_tree, ghostty_sha = sys.argv[1:]
token = secrets.token_hex(16)
path = pathlib.Path(path)
path.write_text(json.dumps({
"schema": 1,
"testbox_id": testbox_id,
"workflow": workflow,
"job": job,
"source_ref": source_ref,
"source_sha": source_sha,
"source_tree_sha": source_tree,
"ghostty_gitlink_sha": ghostty_sha,
"confirmation_token": token,
"created_at": dt.datetime.now(dt.timezone.utc).isoformat(),
}, indent=2, sort_keys=True) + "\n", encoding="utf-8")
path.chmod(0o600)
print(token)
PY
)"
receipt_status=$?
set -e
if (( receipt_status != 0 )); then
echo "could not create the warmup ownership receipt" >&2
exit "$receipt_status"
fi
TBX="$warmup_testbox_id"
printf 'Testbox ID: %s\n' "$TBX" | tee "$OUT/testbox-id.txt"
if (( warmup_status != 0 )); then
exit "$warmup_status"
fi
set +e
blacksmith testbox status --id "$TBX" --wait --wait-timeout 15m \
>"$OUT/status-ready.log" 2>&1
status_ready=$?
set -e
cat "$OUT/status-ready.log"
if (( status_ready != 0 )); then
exit "$status_ready"
fi
```
The workflow validates that the dispatch ref exactly matches the protected
reviewed branch, that `github.sha` exactly matches the protected reviewed SHA,
and that the remote branch still resolves to that SHA. If a direct GitHub
dispatch supplies the optional `source_sha` input, it is an assertion and must
equal the protected SHA. The Blacksmith CLI path needs no arbitrary workflow
input: it supplies `testbox_id`, while `--ref` selects the explicitly reviewed
branch and `github.sha` carries the pinned candidate.
The workflow concurrency group serializes setup requests by Testbox ID, even
when source SHAs differ. The remote `flock` begins after Blacksmith's rsync, so
it protects stage/build/artifact writes only. Blacksmith exposes no pre-rsync
lease; one Testbox ID must have one owning worktree/operator, and independent
clients must not issue concurrent `run` or download commands. This is an
explicit trusted-lane limitation.
Do not issue a separate interpolated identity command. The setup job's
`setup-identity.json` artifact and each stage helper's pre-build JSON record are
the identity transcripts. They describe different commits on purpose: the setup
artifact names the hydrated `main`, and the stage record names the benchmarked
revision you synchronized. The helper verifies the Testbox VM marker, claimed
Testbox ID, the hydration marker's own internal consistency and runner class,
and then the synchronized source commit/tree, Ghostty gitlink/checkout, and
clean status before it invokes Cargo, repeating the source checks after the
build. Each stage JSON carries a `hydration` block with the warmed ref and
commit plus `matches_benchmarked_source`, which is normally `false`. Keep the
setup artifact URL or download it into `$OUT`. A successful setup copies the
same JSON to `/tmp/.testbox/cmux-tui-rust-setup-identity.json`; the stage helper
rejects a missing or malformed marker, so failed hydration cannot be
benchmarked. The active Rust, Cargo, and Zig versions must still equal the
hydrated ones, so a branch that repins its toolchain stops the run instead of
reporting a cold-cache timing.
## Three remote build timings
The helper creates one structured JSON record, one raw Cargo log, and one raw
`/usr/bin/time -p` file per stage. Each remote Cargo build is bounded to 20
minutes with a 30-second kill grace period, and the outer CLI invocation is
bounded to 25 minutes so rsync, SSH, or control-plane hangs cannot bypass the
benchmark's cleanup path. It verifies source and submodule identity before the
stage, exports and records the exact Zig binary used by Cargo, holds a remote
`flock` through all writes, restores the controlled changed file from an
integrity-checked backup, and verifies clean identity again.
```bash
# Run this orchestration block in Bash, not an interactive zsh session.
run_stage() {
local stage="$1"
local run_status download_status=0
set +e
printf -v remote_command \
'CMUX_TESTBOX_REMOTE=1 CMUX_TESTBOX_ID=%q %q %q %q %q' \
"$TBX" ./scripts/blacksmith-cmux-tui-testbox-stage.sh \
"$stage" "$SOURCE_SHA" "$GHOSTTY_SHA"
./scripts/blacksmith-bounded-command.sh 1500 blacksmith testbox run --id "$TBX" --debug \
"$remote_command" >"$OUT/$stage.run.log" 2>&1
run_status=$?
set -e
cat "$OUT/$stage.run.log"
# rsync --delete can remove remote output before the next run. Download each
# stage immediately, before starting another stage.
: >"$OUT/$stage.download.log"
for suffix in json time log; do
if ! ./scripts/blacksmith-bounded-command.sh 120 \
blacksmith testbox download --id "$TBX" \
"testbox-benchmark/$stage.$suffix" "$OUT/raw/$stage.$suffix" \
>>"$OUT/$stage.download.log" 2>&1; then
download_status=1
fi
done
cat "$OUT/$stage.download.log"
if (( run_status != 0 )); then
return "$run_status"
fi
return "$download_status"
}
benchmark_status=0
for stage in first-clean incremental-noop changed-file; do
if ! assert_source_unchanged; then
benchmark_status=1
break
fi
if ! run_stage "$stage"; then
benchmark_status=1
break
fi
done
if (( benchmark_status != 0 )); then
exit "$benchmark_status"
fi
```
`first-clean` is target-clean but dependency-warm. `incremental-noop` repeats
the exact build on the same VM. `changed-file` appends a comment to
`cmux-tui/crates/cmux-tui/src/main.rs`, builds, and restores the original bytes.
The local worktree is never mutated by the helper.
Verify the downloaded records before accepting timings:
```bash
python3 - "$OUT" "$SOURCE_SHA" "$GHOSTTY_SHA" "$TBX" <<'PY'
import json
import pathlib
import subprocess
import sys
out = pathlib.Path(sys.argv[1])
expected_source, expected_ghostty, testbox_id = sys.argv[2:]
expected_tree = subprocess.check_output(
["git", "rev-parse", f"{expected_source}^{{tree}}"], text=True
).strip()
required = {"first-clean", "incremental-noop", "changed-file"}
records = [json.loads(path.read_text(encoding="utf-8")) for path in sorted((out / "raw").glob("*.json"))]
if {record.get("stage") for record in records} != required:
raise SystemExit("expected exactly three stage records")
for record in records:
stage = record.get("stage")
runner = record.get("runner", {})
if runner.get("arch") != "x86_64" or runner.get("cpu_count") != 32:
raise SystemExit(f"{stage}: wrong runner identity {runner}")
if record.get("testbox", {}).get("id") != testbox_id:
raise SystemExit(f"{stage}: wrong Testbox ID")
source_record = record.get("source", {})
if source_record.get("expected_commit_sha") != expected_source or source_record.get("expected_tree_sha") != expected_tree:
raise SystemExit(f"{stage}: wrong expected source identity")
for side in ("before", "after"):
source = source_record.get(side, {})
if source.get("commit_sha") != expected_source or source.get("tree_sha") != expected_tree or source.get("dirty_files"):
raise SystemExit(f"{stage}: source mismatch or dirty {side} checkout")
ghostty = source.get("ghostty", {})
if ghostty.get("gitlink_sha") != expected_ghostty or ghostty.get("head_sha") != expected_ghostty or ghostty.get("dirty_files"):
raise SystemExit(f"{stage}: Ghostty mismatch or dirty {side} checkout")
if not record.get("ok"):
raise SystemExit(f"{stage}: build failed")
with (out / "timings.json").open("w", encoding="utf-8") as handle:
json.dump({"schema": 2, "source_sha": expected_source, "ghostty_gitlink_sha": expected_ghostty, "testbox_id": testbox_id, "stages": records}, handle, indent=2, sort_keys=True)
handle.write("\n")
PY
```
## Cleanup and evidence
Download all raw files and `timings.json` before cleanup. Then, after an
operator explicitly decides this exact box may be destroyed, call the fail-safe
helper. It previews the exact receipt context, preserves an already-completed
409, polls cancellation to a bounded deadline, fails on other stop/status/list
errors, and verifies this exact Testbox ID is no longer active:
```bash
cleanup_token="${cleanup_token:-}"
[[ "$cleanup_token" =~ ^[0-9a-f]{32}$ ]] || {
echo "use the confirmation token emitted by the warmup receipt" >&2
exit 64
}
scripts/blacksmith-testbox-cleanup.sh "$TBX" "$OUT" "$cleanup_token" PREVIEW
# Review cleanup-preview.json, then rerun with STOP:<sha256(cleanup-preview.json)>.
```
A shell `EXIT` trap may call that helper only when an independent operator has
exported `CONFIRM_TESTBOX_STOP_SHA` with the SHA-256 of a separately reviewed
`cleanup-preview.json`; otherwise it preserves the benchmark status, records
inventory, and leaves the box for explicit manual cleanup.
Warmup writes `testbox-receipt.json` and an ownership token bound to the exact
returned ID; cleanup refuses a mismatched ID or token. If warmup fails before
returning an ID, retain before/after inventories but do not automatically stop a
box, because an inventory diff cannot prove ownership across concurrent
operators. Reconcile that orphan manually through the
Blacksmith control plane. Keep both inventories and their command statuses,
plus warmup/status/identity transcripts, every stage run and download
transcript, raw JSON/time/log files, runner catalog, setup identity artifact,
cleanup logs, the receipt, and the final source manifest in the new, unique
`.cmux-scratch/` directory. Never reuse a prior SHA-only directory or overwrite
historical records. Never store credentials, private keys, or
`/tmp/.testbox/auth_token`.
Record these fields alongside `timings.json`:
1. Exact source branch, full source SHA/tree SHA, Ghostty gitlink SHA, and the
clean-status result before each stage.
2. Requested runner label and catalog output. The setup job rejects any
actual architecture or CPU count other than x64 and 32.
3. Blacksmith CLI version, Testbox ID, setup workflow run/job IDs, identity run
ID, and each stage run/sync ID from raw transcripts.
4. Whether the comparison was target-clean, registry/git-cache warm,
Zig-cache warm, or a genuinely cold VM. Warmup deliberately hydrates
dependencies, so `first-clean` is target-cold and dependency-warm.
5. Cleanup stop/status/list output and whether the specific ID was absent from
the active inventory.
The historical 32-vCPU evidence at
`.cmux-scratch/blacksmith-testbox-e40704611ac35f4ffa153/` remains unchanged and
must never be selected as a writable `OUT` directory:
setup SHA `e40704611ac35f0e3a806841a9eae383f4ffa153`, Testbox
`tbx_01kzxebn91nhatkv4ygevh06vs`, workflow run `31696013711`, first-clean
`161.47s`, incremental no-op `8.28s`, changed-file `9.13s`, and cleanup with no
active box. Do not rewrite it while validating this hardening change.
Prior hosted cmux-tui correctness runs without Cargo durations are provenance,
not performance comparisons. Prior Blacksmith macOS Swift/Xcode artifacts use
a different OS, architecture, runner SKU, cache state, and workload, so they
are context rather than a Rust baseline.