Skip to content

chore: round-2 quality bundle — mandatory forward-upgrade leg, checkout preflight, polish sweep, honest baseline policy (#350, #353, #362, #354) - #365

Merged
VijitSingh97 merged 4 commits into
developfrom
chore/round2-quality
Aug 15, 2026
Merged

chore: round-2 quality bundle — mandatory forward-upgrade leg, checkout preflight, polish sweep, honest baseline policy (#350, #353, #362, #354)#365
VijitSingh97 merged 4 commits into
developfrom
chore/round2-quality

Conversation

@VijitSingh97

Copy link
Copy Markdown
Contributor

Four small issues that all raise the same release-quality bar, one branch.

#362 — e2e-real pre-flights its own checkout path. Found live yesterday: running the gate from a checkout under $HOME (mode 750) made the control phase fail six asserts, because the receiver's DynamicUser cannot traverse the home directory — it died in a restart loop with Permission denied. The phase read as a product failure when it was placement. require_traversable_checkout() now walks every ancestor to /, checks the others-execute bit, and dies immediately naming the blocking path and the /opt/rigforge-e2e remedy. Wired into require_linux_root() so a lone control re-run gets the same early failure, not just a fresh provision. Unit-tested with a faked stat — deliberately, because real directory modes vary by host (macOS mktemp -d is 700), which would have made a real-tree test host-dependent.

#350 — the forward upgrade leg is mandatory, not opt-in. The gate previously proved only the refusals (noop + forged-tag rollback) unless someone set an env var, so a broken fetch/rebuild path could pass. The leg now auto-derives the previous release tag, rewinds out-of-band (the control channel correctly refuses a downgrade POST, per D10), and drives a real forward upgrade back to current through the same wire/path-unit/oneshot chain as the other legs. _upgrade_cleanup restores the checkout on any exit — that repeatability is what makes it safe as a default. E2E_UPGRADE_TARGET keeps its explicit semantics; the new E2E_UPGRADE_SKIP_REASON requires and logs a reason, so a bypass is never silent.

#353 — six-item polish sweep. Verb-entry CURRENT_STEP fixed at the single shared dispatch site rather than per verb; _apply_plan now names all five reconciles apply() runs, with a drift guard mirroring the _setup_plan one; backup/restore/support_bundle got EXIT traps for their secret-bearing staging dirs; the appliance --runtime disable gap was empirically verified against real systemd 255 in a container — plain systemctl disable leaves a --runtime-enabled unit's /run symlink in place and is-enabled still says enabled-runtime, so all 12 disable sites now mirror their enables; two comment splices repaired; and the two near-duplicate API readers merged into one with a mode argument, preserving all four call sites' behavior.

Note for reviewers, in the spirit of not hiding a bruise: the trap work initially broke under set -u because a local staging var is already out of scope when the trap fires — caught by the full suite, fixed by making it a script-global like TUNE_TMP, reverified.

#354 — RELEASING.md promises what the fleet actually delivers. Checked the real baseline files: the dedicated bench rig has every recent release, one loaner was refreshed today, the rest stop at v1.11.1. The doc now states the real policy (record the rigs you actually re-tag; the bench rig every time, since the gate always runs there) instead of an unmet fleet-wide claim.

Evidence: make lint clean; suite 1668 → 1690 passed, 0 failed (+7 sections). Coverage could not be computed locally (the pinned static jq is amd64-only, this host is arm64) — CI's amd64 runner is the check.

Needs live-rig validation before the next cut: #350's new default path end to end, #362's real under-$HOME scenario, and #353's appliance disable paths.

Closes #350
Closes #353
Closes #354
Closes #362

🤖 Generated with Claude Code

VijitSingh97 and others added 4 commits August 15, 2026 16:11
…aging traps, appliance disable --runtime, comment splices, reader dedup

Six small debts from the 2026-08-14 audit, bundled per the issue:

1. CURRENT_STEP was only ever set inside main() (setup), so an unexpected
   failure in any other verb reported the stale "aborted while starting up"
   default. Set it once at dispatch time from the verb name itself — setup's
   own fine-grained per-phase steps still overwrite it a moment later.

2. _apply_plan's step 3 named 3 reconciles (autotune/API/firewall) while
   apply() actually runs 6 install_* calls, silently omitting watchdog and
   control from the printed plan. Named all of them; added the same
   name-vs-plan drift guard _setup_plan already has for main().

3. backup/restore/support_bundle's mktemp -d staging (config.json, tokens)
   leaked on a set -e abort — the explicit `rm -rf` cleanup lines were never
   reached. Gave all three the EXIT-trap treatment tune() got in #135.
   (The trap has to reference a script-global, not a `local`: a `local`
   staging var is out of scope by the time the trap fires on a normal
   return, and dies "unbound variable" under set -u — caught by the
   existing backup/restore/support-bundle black-box tests.)

4. Verified empirically against a real systemd (255, enable/disable
   round-trip in a container) that a plain `systemctl disable` only ever
   removes the /etc-side wants-symlink: a --runtime-enabled unit's /run
   symlink survives, and `is-enabled` still reports "enabled-runtime". Every
   disable call now passes ${ENABLE_RUNTIME:+...}, mirroring its enable
   counterpart.

5. Two comment splices: xmrig_already_built()'s docstring was sitting above
   the unrelated _sha256() instead of itself; a section-header banner
   (`--- Auto-tuning: search strategies & seeding ---`) had landed mid-
   sentence inside _hillclimb()'s docstring.

6. _read_api_summary and _xmrig_summary_json were near-identical /2/summary
   readers, differing only in an API_CMD hook and whether a curl failure
   propagates or is swallowed. Merged into one function with a
   propagate/swallow mode argument, preserving each call site's original
   behavior exactly.

Tests: tests/run.sh gains one section per item above (black-box + unit),
153 -> 158 sections. Full suite: 1685 passed, 0 failed (up from 1668
baseline). make lint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pt-in (#350)

tests/e2e-real.sh's upgrade phase only ever proved the two REFUSAL legs
(noop, forged-tag rollback) unless an operator remembered to set
E2E_UPGRADE_TARGET — so a broken fetch/rebuild path could pass the release
gate silently.

The forward leg is now on by default. It auto-derives the previous real
release tag and the current/installed one from `git tag` (the release this
gate is actually cutting has no tag yet at this point in RELEASING.md's
flow, so "current" stands in for it), rewinds the checkout to the previous
tag out of band (git checkout + `rigforge.sh upgrade`, the same two steps
_control_upgrade_do takes — D10's anti-rollback in control_upgrade() refuses
a downgrade POST, so this can't happen through the control channel itself),
then drives a real forward upgrade back to current through the same
wire/path-unit/oneshot chain the other legs use. _upgrade_cleanup restores
the checkout to exactly where the phase started on any exit, success or a
hard abort mid-leg — unlike the old opt-in leg (which permanently advanced
the checkout past "current"), this one is repeatable, which is what lets it
be the default.

E2E_UPGRADE_TARGET=vX.Y.Z still works as an explicit override (same
PERMANENT semantics as before — useful for a deliberate real deploy).
E2E_UPGRADE_SKIP_REASON="..." is the new escape hatch: it skips the leg but
requires (and logs) a reason, so a skip is never silent.

RELEASING.md's `upgrade` line updated to describe the new default and the
skip knob.

Needs live-rig validation — this whole leg (the rewind, the real
fetch/build through the control channel, the restore) has no automated
coverage; it can only run for real on the release rig. See #362's
require_traversable_checkout for the one piece of this file that IS
unit-tested via extraction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…amicUser (#362)

Found live (miner-2): running tests/e2e-real.sh from a checkout under $HOME
(mode 750) makes the control phase fail 6 asserts confusingly — the
receiver dies in a restart loop with "can't open file .../util/control-
server.py: Permission denied", because rigforge-control.service and
rigforge-api.service run as systemd DynamicUser and can't traverse the
home directory. The phase then reads as a product failure (receiver down,
POST 000, DONATION unchanged) when it's a harness-placement problem —
nothing pointed at the path.

require_traversable_checkout() walks every ancestor directory from the
checkout up to /, checking the "others" execute bit on each (root can
always stat regardless of permissions, so this never false-fails), and
dies immediately with the fix (move the checkout to /opt) if any of them
block traversal. Wired into require_linux_root(), which every phase already
calls first — so a lone `control` or `upgrade` re-run gets the same
immediate, honest failure, not just a fresh `provision`.

Split into its own function (rather than inlined in require_linux_root) so
it's unit-testable in isolation via the same extraction+eval technique
tests/run.sh already uses for rig_lock/_perf_judge: `stat` is faked, so the
test's result never depends on the real host's tmp/HOME permissions (which
turned out to vary by OS during development — macOS's own mktemp -d
default and its TMPDIR parent are both mode 700).

One line added to the header comment block per the issue's ask.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tually delivers (#354)

RELEASING.md said to record each rig's benchmark after every fleet
re-tag. In practice only miner-0 (the dedicated release-gate rig, per
tests/README.md) has a current entry every release; miner-1..7 stop at
v1.11.1 — three releases of fleet perf history the fleet was never
actually re-tagged for, or the recording step was skipped.

Rewrote the paragraph to describe the real policy instead of inventing
one nobody follows: record whichever rig(s) you actually re-tag, which in
practice means miner-0 every time (the gate always runs there) plus
whatever else happens to get touched that release. tests/perf-baselines/
legitimately carries gaps between releases for untouched rigs — that's
now stated as expected, not implied to be a lapse.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@VijitSingh97
VijitSingh97 merged commit fa130fb into develop Aug 15, 2026
9 checks passed
@VijitSingh97
VijitSingh97 deleted the chore/round2-quality branch August 15, 2026 21:37
@VijitSingh97 VijitSingh97 mentioned this pull request Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment