Skip to content

fix(appliance): commit gate consumes doctor --json, not just a curl (#852) - #864

Merged
VijitSingh97 merged 1 commit into
develop-v2from
fix/852-commit-gate
Aug 3, 2026
Merged

fix(appliance): commit gate consumes doctor --json, not just a curl (#852)#864
VijitSingh97 merged 1 commit into
develop-v2from
fix/852-commit-gate

Conversation

@VijitSingh97

Copy link
Copy Markdown
Collaborator

What

The A/B slot-commit gate in os/overlay/pithead-boot marked a slot good on a single curl https://localhost/ — any HTTP answer. "The dashboard answers" is a strict subset of "the stack is alive": a slot whose monerod/p2pool/tari crashed while caddy+dashboard keep serving still committed, defeating the case-2 self-heal the A/B design promises.

The plan always named the gate pithead doctor --json and claimed it "checks containers" — but doctor's Containers section only printed docker compose ps and never FAILed on a crashed revenue container. So wiring doctor's exit code alone would not have fixed the bug. This PR fixes doctor first, then uses it.

Health signal chosen

Commit now requires both: the localhost curl (proves the derived-config → caddy → dashboard chain) and pithead doctor --json exiting clean. doctor gains an honest revenue-container check.

Syncing vs crashed (the distinction that avoids bricking)

A new pure classifier revenue_container_verdict splits the revenue path by how it behaves during a days-long initial sync:

  • Chain nodes (monerod/tari + payout wallets) run throughout the sync — they are what is syncing — and their healthchecks are liveness probes (RPC answers / process alive / #718 scan-grace), so they report healthy from early on regardless of height. Down or unhealthy = a real crash → hold the commit.
  • Sync-gated miners (p2pool/xmrig-proxy) are deliberately stopped by the dashboard until the node syncs (#35), so a down miner is the expected steady state on a fresh box → fine, commit. Only a running-but-unhealthy miner is a fault.

So a still-syncing box commits (the #35 sync gate holds mining but the OS is good) while a genuinely broken one reverts. The boot loop retries, so a still-starting node just waits rather than committing on partial evidence.

Coverage

  • tests/stack (unit): revenue_container_verdict across syncing vs crashed states (incl. empty-.State cross-engine robustness) + the pithead-boot gate wiring.
  • tests/os (KVM battery): the provision reboot leg — where the real self-commit gate actually runs on a provisioned stack — now asserts the gate PASSES on the healthy still-syncing stack yet REFUSES once monerod is stopped (the mining-dead-but-serving slot a curl-only gate committed). This is the assertion whose absence let the curl-only gate ship green. (The update phase drives commit manually on an unprovisioned guest where pithead-boot never runs, so the gate decision is proven in provision, where it executes.)

Docs

Boot-contract step 4 (appliance-wizard), the plan's gate bullet (dual-distribution-plan), and the appliance-release phase table all now describe the real curl + doctor gate.

Closes #852

🤖 Generated with Claude Code

…852)

The A/B slot-commit gate in os/overlay/pithead-boot marked a slot good on a
single `curl https://localhost/` — any HTTP answer. "The dashboard answers" is
a strict subset of "the stack is alive": a slot whose monerod/p2pool/tari
crashed while caddy+dashboard keep serving still committed, defeating the
case-2 self-heal the A/B design promises.

The plan (dual-distribution-plan.md) always named the gate `pithead doctor
--json` and claimed it "checks containers" — but doctor's Containers section
only printed `docker compose ps` and never FAILed on a crashed revenue
container, so wiring doctor's exit code alone would not have fixed the bug.

- doctor now genuinely fails on a crashed revenue container. A new pure
  classifier `revenue_container_verdict` + `check_revenue_containers` split the
  revenue path by sync behaviour: chain nodes (monerod/tari + payout wallets)
  run throughout a days-long initial sync and report healthy early (liveness
  probes / #718 scan-grace), so a down/unhealthy one is a real crash; the
  sync-gated miners (p2pool/xmrig-proxy, stopped by the dashboard until the node
  syncs, #35) are expected down, so only a running-but-unhealthy miner is a
  fault. This keeps a slow initial sync from ever blocking the commit.
- pithead-boot now gates mark-good on the curl AND `pithead doctor --json`
  (curl first, cheap; doctor only has to pass once — a still-starting node just
  loops). A slot serving a dashboard while mining is dead no longer commits.
- docs reconciled: appliance-wizard boot contract step 4, the plan's gate
  bullet, and the appliance-release phase table now describe the real gate.

Coverage:
- tests/stack: unit tests for revenue_container_verdict (syncing vs crashed,
  incl. empty-.State robustness) and the pithead-boot gate wiring.
- tests/os: the provision reboot leg — where the real self-commit gate runs on
  a provisioned stack — now asserts the gate PASSES on the healthy still-syncing
  stack yet REFUSES once monerod is stopped (the mining-dead-but-serving slot a
  curl-only gate committed). This is the assertion whose absence let the
  curl-only gate ship green.

Closes #852

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@VijitSingh97
VijitSingh97 merged commit d6bf9aa into develop-v2 Aug 3, 2026
16 checks passed
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.

1 participant