Hands mover: the node tree defaults to the Mac agent's own build, a digest readback per hand against the Mac hand, a 16-field guard on the override

Main's hold of 6 October 2026 (23:xx UK): the hands move only on the shipper's 'publish 2 live: digest <x>' line, each hand read back by
commit string and consensus digest. move-hand.sh binary now derives --node from the fork tree behind the Mac's node1 launchd agent
(igneum-wt-ship0315/vendor/igneum-node-0315 at f1ea7a38 tonight; the box's igneumd rebuilt from it is sha256 7f0bde70...), warns when the
Mac's override still has 13 fields (publish 2 writes the sixteen-field object when it restarts the hands), and after each hand's first
executing line prints the box binary's commit string and the hand's digest against the Mac hand's last digest (MATCH or DIFFER, DIFFER
stops the sequence). Dry run clean against the live Mac.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-06 22:51:06 +00:00
parent 0e6f5bce6b
commit 3e8e7a5dfc

View file

@ -36,6 +36,13 @@ while [ $# -gt 0 ]; do case "$1" in --go) GO=1; shift ;; --node) NODE_WT="$2"; s
say() { bs_log "$*"; }
run() { if [ "$GO" = 1 ]; then "$@"; else say "DRY RUN: $*"; fi; }
rssh() { "${ROOT_SSH[@]}" "$@"; }
# the digest readback (main, 6 Oct 2026 23:xx UK): the box hand's "Consensus params digest" against the Mac hand's last one
mac_digest() { grep 'Consensus params digest' "$HOME/Library/Logs/Igneum/$1.out" 2>/dev/null | tail -1 | grep -oE '[0-9a-f]{64}' | head -1; }
box_digest() { rssh "journalctl -u $1 --no-pager -o cat --since '10 min ago' | grep 'Consensus params digest' | tail -1" 2>/dev/null | grep -oE '[0-9a-f]{64}' | head -1; }
digest_readback() { # <unit> <mac hand log name>
local b m; b=$(box_digest "$1"); m=$(mac_digest "$2")
if [ -n "$b" ] && [ "$b" = "$m" ]; then say "$1 digest ${b:0:16}... MATCHES the Mac's $2 hand"; else say "$1 digest ${b:-none} against the Mac's ${m:-none}: DIFFER (stop and read the override before moving the next hand)"; return 1; fi
}
first_exec_line() { # <unit>: wait up to 180 s for the node's exec line, print it (the proof main wants) and the chain tip
local u="$1" line=""
for _ in $(seq 1 36); do
@ -58,7 +65,13 @@ mac_stop_agent() { # <label>: bootout the launchd agent (KeepAlive would resta
case "$MODE" in
binary)
[ -n "$NODE_WT" ] || bs_die "binary needs --node <fork worktree> (the release-0.3.15-node tree)"
if [ -z "$NODE_WT" ]; then
# default: the fork tree that built the hand running on the Mac now (the launchd agent's binary path), so the box runs
# the same commit the Mac did (6 Oct 2026: igneum-wt-ship0315/vendor/igneum-node-0315 at f1ea7a38)
macbin=$(plutil -p "$HOME/Library/LaunchAgents/network.igneum.devnet.node1.plist" 2>/dev/null | grep -oE '/[^"]*igneumd' | head -1)
NODE_WT=$(dirname "$macbin" | sed -E 's|/target[^/]*/release$||'); [ -f "$NODE_WT/Cargo.toml" ] || bs_die "no --node and no fork tree behind the Mac's node1 agent ($macbin)"
say "node tree from the Mac's node1 agent: $NODE_WT ($(git -C "$NODE_WT" rev-parse --short HEAD) on $(git -C "$NODE_WT" branch --show-current))"
fi
[ -f "$NODE_WT/Cargo.toml" ] || bs_die "no Cargo.toml in $NODE_WT"
ver=$(grep -m1 '^version' "$NODE_WT/Cargo.toml" | sed 's/.*"\(.*\)".*/\1/'); sha=$(git -C "$NODE_WT" rev-parse --short HEAD)
say "building igneumd $ver ($sha) on the box from $NODE_WT"
@ -77,7 +90,9 @@ case "$MODE" in
ovfile=$(mktemp); printf '%s\n' "$OV_JSON" > "$ovfile"
else
[ -f "$MAC_OV" ] || bs_die "no override file at $MAC_OV and no --override-json"
say "override from the Mac's file: $(cut -c1-200 "$MAC_OV")"; ovfile="$MAC_OV"
nf=$(python3 -c 'import json,sys; print(len(json.load(open(sys.argv[1]))))' "$MAC_OV")
say "override from the Mac's file ($nf fields): $(cut -c1-200 "$MAC_OV")"; ovfile="$MAC_OV"
[ "$nf" -ge 16 ] || say "WARNING: the Mac's override has $nf fields; publish 2 writes the sixteen-field object when it restarts the hands. Move only after the shipper's 'publish 2 live: digest <x>' line, or pass --override-json"
fi
run bs_rsync -p "$ovfile" "$BS_HOST:$H/override.json"
[ "$ovfile" = "$MAC_OV" ] || rm -f "$ovfile"
@ -99,7 +114,11 @@ case "$MODE" in
run mac_stop_agent "$label"
run sync_dir "$mac_dir" "$H/$MODE"
run rssh "systemctl start $unit && sleep 3 && systemctl is-active $unit"
[ "$GO" = 1 ] && first_exec_line "$unit"
if [ "$GO" = 1 ]; then
first_exec_line "$unit"
rssh "[ \$(strings $H/bin/igneumd | grep -c \"\$(readlink $H/bin/igneumd | sed -E 's/.*-([0-9a-f]{7,})\$/\\1/')\") -gt 0 ] && echo 'commit string present in the box binary' || echo 'WARNING: no commit string in the box binary'"
digest_readback "$unit" "$( [ "$MODE" = node1 ] && echo node1 || echo observer )" || true
fi
say "$MODE moved; the Mac's agent stays unloaded (step 5 removes the plist from the login)" ;;
observer)