DrumsBench

Experiments / EXP-011

First paid AWS host launch terminated by its own heartbeat backstop

Launch one bounded, self-cleaning EC2 host under the aws_run gates to run the baseline smoke, with an AWS-side terminate backstop that cannot leave a host running unattended.

failedRan and missed a correctness, isolation, reset or pre-registered performance bar. Kept on the record.
Date
2026-10-25
Hypothesis
A CloudWatch alarm on a heartbeat metric with missing data treated as breaching is a safe backstop that only fires when the orchestrator stops renewing it.
Arms
aws-host
Hardware
c8i.2xlarge (8 vCPU / 16 GiB, nested virtualization enabled), us-east-1
OS
Canonical Ubuntu 24.04 amd64 (official AMI resolved through SSM)
Workload
none reached; the host was terminated before bootstrap could report
Code
claude/ecstatic-archimedes-456go6 at 8f6ae5a
Limitation
The launch and its termination are author-claimed from a docstring, a test and commit messages; aws-runs/ is gitignored so no run-summary, steps or AWS-call log is on the branch, and STATUS.md on the same branch still says no paid launch has happened.

In one paragraph

aws_run is the tooling that is allowed to spend money: one EC2 host per phase, with a budget-derived lifetime, two independent kill switches and a tag-based teardown that is verified afterwards. The first real launch under these gates ended within minutes, not because the host failed but because one of the kill switches fired early. The CloudWatch alarm on the heartbeat metric was created with its terminate action enabled before any heartbeat datapoint existed; the alarm treated its twelve empty periods as breaching, went straight from INSUFFICIENT_DATA to ALARM at its first evaluation, and terminated the instance about a minute before the first heartbeat arrived. The backstop worked, against us. The ordering was changed so that a heartbeat is seeded first, the alarm is created with actions disabled, a healthy OK state is read back, and only then are actions enabled. All of this is author-claimed; no run artifact is on the branch.

Setup

What the gates are

Before any AWS mutation, run --execute requires:

Fixed inputs, operator-supplied and labelled as such on the branch: region us-east-1, instance type c8i.2xlarge (8 vCPU / 16 GiB, nested virtualization via --cpu-options NestedVirtualization=enabled), the official Canonical Ubuntu 24.04 AMI, one 4 vCPU / 4 GiB guest (design value), a 120 GiB root volume ("a guess"), an idle limit default of 3600 s (design value). Network: SSH from exactly one operator /32, port 5000 never opened, IMDSv2 required, no instance profile, encrypted gp3 root deleted on termination, shutdown behaviour terminate.

The two backstop layers

  1. AWS-managed: a CloudWatch alarm on drumsbench/Heartbeat (statistic Maximum, unit Count, period 300 s, 12 evaluation periods, missing data = breaching; all design values) whose action is arn:aws:automate:us-east-1:ec2:terminate. The orchestrator publishes a heartbeat only when the host's own progress heartbeat advanced or an operator --keepalive-file is fresh, and only while the estimated hours are inside the run budget. A healthy run is never cut off by elapsed time before the budget boundary.
  2. Host-side: a watchdog installed by cloud-init user data at boot with the budget-derived deadline and the idle limit, so there is no unguarded window before bootstrap.

The orchestrator's finally always tries evidence collection, then a bounded-retention snapshot if that fails, then terminates and verifies a tag-based teardown. Exit codes: 0 all passed including human review; 3 automated steps passed, human GUI review pending; 10 phase failed; 20 teardown not clean; 30 evidence not preserved; 2 refused.

ApplecontrollerMac mini, M2 Pro, 16 GBruns drumsctl + gatesAppleresult storageMac mini, M2 Pro, 16 GBlogs, never deletedresultsApplestudio-a, Mac StudioM4 Max, 16 cores, 48 GBDrums arm: macOS sessionsApplestudio-b, Mac StudioM4 Max, 16 cores, 48 GBbaseline and reproductionAWSAWSLinuxx86 hostAWS, nested virt for KVMLinux arms: QEMU/KVM,runc + CRIU, custom rootfsAnthropicBedrockfrontier agent(Claude) overAPI calls onlyone harness drives every arm. studio-b is run by a second personfor Gate 10: reproduction within 15% on the same four numbers.
The planned AWS stage: one c8i.2xlarge host in us-east-1 running the QEMU/KVM baseline guest under nested virtualization for the smoke, and a separate host for the shared-OS diagnostic. No matched comparison is made across hosts. Our own Mac Studios and minis are not involved in this record.

What ran

Author-claimed, from tests/test_aws_run.py (class BackstopInitTests), aws_run/provision.py (create_backstop) and commits 8f6ae5a, eb55440, 7e45267 (19:06 to 20:09 UTC): one real EC2 launch happened on 2026-10-25, with the instance id, run id and receipts in the gitignored aws-runs/ directory. A CloudWatch datapoint timestamp captured into a regression test puts the incident at about 19:09 UTC; that timestamp is a captured CLI payload, not a measured benchmark value. The branch's internal label for the launch is undefined and not published here.

Results

No measured result. The author's description of the incident, quoted from the test docstring: "an alarm created with actions enabled and no datapoint went INSUFFICIENT_DATA -> ALARM at its first evaluation (12 empty periods treated as breaching) and terminated the instance a minute before the first heartbeat arrived."

Two smaller findings from the same launch, both from commit messages: the real CLI prints the datapoint Timestamp as a JSON number, not an ISO string, which the readback parser had not accepted (eb55440); and the real payload carried Maximum, not Sum, so the Sum fallback was dropped and the regression test now runs the exact captured payload through the whole init guard (7e45267).

The fix

provision.create_backstop now documents and enforces this order ("ORDER (the first live launch showed why)"):

  1. Seed the heartbeat for exactly this instance id, before the alarm exists.
  2. Create the alarm with --no-actions-enabled and the terminate action attached but inert.
  3. Read back the configuration, the metric (exact dimension, Count, threshold, a current timestamp) and a healthy OK state, polling until they hold or a bounded timeout (600 s, design value) passes; on timeout fail closed and terminate.
  4. Only then enable-alarm-actions.
  5. Re-verify everything with actions enabled.

The state is never forced (no set-alarm-state) and nothing is slept on without a readback. The commit calls itself "REVIEW CANDIDATE (targeted tests pass; legacy full test_aws_run not re-run)".

Failures and surprises

Limitations

Reproduce

From the README's exact sequence (benchmark/):

python -m aws_run plan --run-budget-usd <usd> --ebs-usd-gb-month <p> --ipv4-usd-hour <p> --transfer-usd-gb <p> \
    --egress-gb-ceiling <gb> --snapshot-usd-gb-month <p> --snapshot-retention-days <days>
python -m aws_run preflight --aws-wrapper ./aws-as-profile --ssh-cidr <your.ip>/32 ...      # read-only AWS calls
python -m aws_run provenance-record --phase baseline --preflight-dir aws-runs/preflight --out provenance-baseline.json
python -m aws_run run --execute --phase baseline --aws-wrapper ./aws-as-profile --preflight-dir aws-runs/preflight \
    --reviewed-prereg-sha256 <hash> --provenance-file provenance-baseline.json --operator-read-original-grant '<id>'
python -m aws_run status --aws-wrapper ./aws-as-profile
python -m aws_run cleanup --aws-wrapper ./aws-as-profile --run-id <id> --confirm-terminate

A second launch with the reordered backstop, with run-summary.json, steps.jsonl and the alarm readback published (identifiers redacted), is what would turn this into a verified record of the backstop behaving.

Sources