Skip to content

M8 H1: owner-runnable power-pull rig + storage_probe calibration (§14.8 H1 / D1)#22

Open
guyo13 wants to merge 2 commits into
mainfrom
claude/gifted-pasteur-gfr9th
Open

M8 H1: owner-runnable power-pull rig + storage_probe calibration (§14.8 H1 / D1)#22
guyo13 wants to merge 2 commits into
mainfrom
claude/gifted-pasteur-gfr9th

Conversation

@guyo13

@guyo13 guyo13 commented Jun 26, 2026

Copy link
Copy Markdown
Owner

What this is

Builds the M8 H1 power-pull rig — the last open M8 gate and the only one that fundamentally needs physical hardware: a real mains power cut on storage that genuinely loses un-synced data, ≥50 consecutive cycles with zero acked-LSN loss (invariant D1). This is orchestration + a workflow_dispatch CI job + one test-only calibration bin + docs around the already-proven power_pull_workload/power_pull_verify binaries.

H1 stays OPEN-pending-owner-run. This PR is for code review of the harness. The agent never self-certifies H1 — the owner triggers it on the wired rig, observes ≥50 PASS with the H2 probe proven + evidence on #18, and closes #18.

Commits

  • ed4e833 — the rig: scripts/m8/h1-cycle.sh, .github/workflows/m8-h1.yml, .github/actionlint.yaml, docs.
  • 6204a75 — designer feedback: src/bin/storage_probe.rs as the calibration instrument + per-path §5 verdicts.

Pieces

  • scripts/m8/h1-cycle.shdeploy / calibrate / cycle / run / config. The §3.4 vacuous-pass calibration gate runs first; the cycle loop drives the target over ssh, cuts via a pluggable smart-plug local API, restores, boot-waits, and runs power_pull_verify on the target against the recovered WAL (after scping that cycle's fresh capture). Emits the §5 evidence ledger.
  • src/bin/storage_probe.rs (test-only; auto publish-excluded by the existing exclude = ["src/bin"], like crash_child) — the §3.4 calibration instrument. write-unsynced-marker writes via a plain write(2) with no fdatasync — the WAL's data path minus the sync — so the calibration measures loss on the same kernel write path the WAL uses (a shell echo could mis-measure). verify-marker-gone: exit 0 = gone / 1 = survived.
  • .github/workflows/m8-h1.ymlworkflow_dispatch-only, runs-on: [self-hosted, h1-rig] (the owner's never-cut controller laptop). Cross-compiles the aarch64 bins, deploys, runs calibration + cycles, uploads the §5 artifact, posts to M8/H1: power-pull durability — ≥50 cycles, zero acked loss (D1) #18 (dispatch-gated). Loud-skips (OPEN, not green) if the rig Variables are unset or the target is unreachable.

Honesty rails

  • Vacuous-pass guard is mandatory & first — no cycle counts until storage_probe proves the DUT loses un-synced data on a real cut; a surviving marker is a HARD abort.
  • Distinct exit codes / §5 verdict on every path: 0=PASS / 1=FAIL (verdict=FAIL, D1 loss) / 2=INCONCLUSIVE / 3=VACUOUS (verdict=OPEN). verdict=PASS only when h2_probe proved loss and fail==0.
  • Valid cut = real mains interruption (smart plug); sysrq-b/reboot/shutdown are explicitly NOT valid.
  • A FAIL on consumer media is most likely a lying device, not a WAL bug (§3.6) — the ledger records the DUT medium + the acked LSN so it's attributable.

Smart-plug driver

Pluggable via H1_PLUG_TYPE: shelly (Gen2/Gen3/Plus RPC /rpc/Switch.Set — default; the owner's Shelly Plug S Gen3, aliases shelly-gen2/shelly-gen3), shelly-gen1 (/relay/0), tasmota (/cm?cmnd=Power). H1_PLUG_DRY_RUN=1 for no-hardware dry runs.

Verified in-sandbox

  • storage_probe: build + cargo +1.85.0 MSRV + fmt/clippy clean; exit semantics (0 gone / 1 survived / 2 usage).
  • All four h1-cycle.sh verdict paths driven end-to-end with stubbed ssh/scp/socat — PASS(0), FAIL(1→verdict=FAIL), vacuous(3→verdict=OPEN), and the unreachable-rig loud-skip — each emitting a valid §5 ledger.
  • shellcheck + actionlint + bash -n clean; cargo clippy --all-targets -D warnings, cargo fmt --check, full cargo test green (LazyFS/hardware suites remain #[ignore]).
  • Cannot run here: the actual power-pull cycles (no cuttable target / smart plug) — the script + workflow print loud OPEN banners and never fake green.

Refs §14.8 H1 / D1 / #18.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VW9DW3Lu7dVmargSY1gbZk


Generated by Claude Code

claude added 2 commits June 26, 2026 05:59
Build the H1 power-pull automation — the last open M8 gate and the only one
that fundamentally needs physical hardware (a real mains cut on storage that
genuinely loses un-synced data, >=50 consecutive PASS with zero acked-LSN loss,
D1). No src/ change: this is orchestration + CI + docs around the proven
power_pull_workload/power_pull_verify bins, storage-check.sh, and evidence.sh.

- scripts/m8/h1-cycle.sh: deploy/calibrate/cycle/run/config. The §3.4
  vacuous-pass calibration GATE runs first (un-synced marker must be GONE after
  a real cut, else abort loudly — no cycle counts). Cycle loop drives the target
  over ssh, cuts via a pluggable smart-plug local API, restores, and verifies
  every acked LSN survived. INCONCLUSIVE never counts; a FAIL stops the run;
  verdict=PASS only when h2_probe proved loss AND fail==0. Emits the §5 ledger.
- Smart-plug driver: shelly (Gen2/Gen3/Plus RPC — default, the Shelly Plug S
  Gen3; aliases shelly-gen2/gen3), shelly-gen1, tasmota. H1_PLUG_DRY_RUN for
  no-hardware dry runs.
- .github/workflows/m8-h1.yml: workflow_dispatch-only, runs-on
  [self-hosted, h1-rig]; cross-compiles aarch64 bins, deploys, runs the
  calibration + cycle loop, uploads the §5 evidence artifact, posts to #18
  (dispatch-gated sign-off). Loud-skips (OPEN, not green) if the rig is unwired
  or unreachable.
- .github/actionlint.yaml: declare the h1-rig custom runner label.
- docs: runbook H1 "Automated rig" + "Rig setup" (Pi 3 read-only overlay,
  dedicated DUT partition, BeagleBone-eMMC + USB-SSD media, controller wiring,
  smart-plug table); infra-plan §3.2/§3.4 corrected to storage-check.sh
  probe-write/probe-verify (no separate storage_probe binary); README +
  CLAUDE.md status.

H1 stays OPEN-pending-owner-run: the owner triggers m8-h1.yml on the wired rig,
observes >=50 PASS with the H2 probe proven + evidence on #18, and closes #18.
The agent never self-certifies H1.

Verified: shellcheck + actionlint clean, bash -n, plug-URL/config/evidence
dry-runs, the abort path emits a valid ABORTED ledger, cargo build of the bins.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VW9DW3Lu7dVmargSY1gbZk
…D1 / #18)

Apply the designer's approve-with-conditions feedback on the H1 rig.

Delta A — build src/bin/storage_probe.rs as the §3.4 calibration instrument
(reverse the shell-probe choice). The "no src/ change" rule guards against
durability-logic drift, not against the measurement instrument being real code.
The calibration must prove the DUT loses un-synced data on the SAME kernel write
path the WAL uses, so the marker is written via a plain write(2) with NO
fdatasync (the WAL's data path minus the durability step); a shell echo could
differ subtly and mis-measure the very thing this gate exists to catch. It does
NOT use the WAL append (pure in-memory until commit — would never reach the
cache). Test-only, auto publish-excluded by the existing exclude=["src/bin"],
like crash_child. Subcommands write-unsynced-marker (exit 0) and
verify-marker-gone (0 = gone/honest, 1 = survived/vacuous). h1-cycle.sh calibrate
now uses it for the loss-probe; storage-check.sh classify stays as the
deny-by-default FS/cache check. deploy + the workflow cross-compile + scp it
alongside power_pull_{workload,verify}. infra-plan §3.2/§3.4 keep the binary as
originally written.

Delta B — per-path §5 verdict + hard, distinct-code calibration abort. The prior
EXIT trap emitted ABORTED for every non-clean exit, mislabeling a D1 FAIL.
Replaced with finish()/VERDICT emitting the ledger exactly once on every terminal
path, with distinct exit codes: 0=PASS, 1=FAIL (verdict=FAIL), 2=INCONCLUSIVE/
infra (verdict=INCONCLUSIVE), 3=VACUOUS calibration (verdict=OPEN, marker
survived — a HARD abort before any cycle counts). The workflow maps 1/2/3 to
distinct error annotations.

Delta C — runbook makes the calibration-instrument choice and pin-downs 1-4
explicit (verify-on-target-over-ssh + scp transport; per-cycle fresh capture;
three-way outcome; evidence-on-every-path), plus an H2-section pointer so the
manual shell probe and the storage_probe binary don't read as contradictory.

Verified: storage_probe build + cargo +1.85.0 MSRV + fmt/clippy clean and its
exit semantics (0/1/2); all four h1-cycle.sh verdict paths driven end-to-end with
stubbed ssh/scp/socat (PASS=0, FAIL=1, vacuous=3/verdict=OPEN, unreachable
loud-skip), each emitting a valid §5 ledger; shellcheck + actionlint clean;
cargo test green (LazyFS/hardware suites remain #[ignore]).

H1 stays OPEN-pending-owner-run; the owner triggers m8-h1.yml on the wired rig,
observes >=50 PASS with the H2 probe proven + evidence on #18, and closes #18.
The agent never self-certifies H1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VW9DW3Lu7dVmargSY1gbZk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

M8/H1: power-pull durability — ≥50 cycles, zero acked loss (D1)

2 participants