Fleet: the deploy gate (syntax + master's defaults-line check + the comment-with-code class) in lib.box.put; the 75 percent table gate (standing.table_gate, the wind-down holds under it)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-06 23:04:29 +00:00
parent 25dac23a12
commit 3ced860f96
4 changed files with 35 additions and 1 deletions

16
tools/fleet/deploy-gate.sh Executable file
View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
# The fleet's deploy gate (coordinator, 6 October 2026 23:0xZ, after three comment-on-a-line faults): every script pushed to a box
# passes master's defaults-line check and a syntax check first; lib/box.py's put() calls this for any .sh/.py source under
# tools/fleet and refuses the push on red. Usage: deploy-gate.sh <file...> (exit 0 = clean)
set -u; rc=0; HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; MASTER_CI="${IGNEUM_MASTER_CI:-/Users/joshm/Projects/igneum/tools/ci}"
for f in "$@"; do
case "$f" in
*.sh) bash -n "$f" 2>&1 | head -2 | sed "s|^|$f: |" ; [ "${PIPESTATUS[0]}" = 0 ] || rc=1 ;;
*.py) python3 -m py_compile "$f" 2>&1 | head -2 | sed "s|^|$f: |"; [ "${PIPESTATUS[0]}" = 0 ] || rc=1 ;;
esac
# a comment inserted mid-line swallows the rest of the line: no code may follow a comment that follows code
if [ -f "$MASTER_CI/defaults-line-check.sh" ]; then bash "$MASTER_CI/defaults-line-check.sh" "$f" 2>&1 | grep -v "no assignment hides" | sed "s|^|$f: |" | head -3; [ "${PIPESTATUS[0]}" = 0 ] || rc=1; fi
if grep -nE '^[^#"'"'"']*[^ #]\s+#\s[^"]*\S+.*(\$\{|>>|&$|;\s*[a-zA-Z_]+=)' "$f" 2>/dev/null | grep -vE '^\s*[0-9]+:\s*#' | grep -qE '#.*(>> |&$|; [A-Z_]+=)'; then echo "$f: a comment with code after it (the 17:18Z/22:10Z/22:52Z class)"; rc=1; fi
done
[ $rc = 0 ] && echo "deploy-gate: clean ($# files)" || echo "deploy-gate: RED, the push is refused"
exit $rc

View file

@ -53,6 +53,11 @@ class Box:
if rc != 0: raise SshError(f"{self.label}: rc {rc}: {err.strip()[:200]}")
return out
def put(self, files, dest, tries=3, timeout=900):
# the deploy gate: a fleet script (.sh/.py under tools/fleet) goes to a box only when deploy-gate.sh reads clean
srcs = [f for f in files if (f.endswith(".sh") or f.endswith(".py")) and "/tools/fleet/" in os.path.abspath(f)]
if srcs:
g = subprocess.run(["bash", os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "deploy-gate.sh")] + srcs, capture_output=True, text=True, timeout=120)
if g.returncode != 0: raise SshError(f"{self.label}: deploy gate RED, push refused: {(g.stdout + g.stderr).strip()[:300]}")
for i in range(tries):
r = subprocess.run(["scp"] + SSH_OPTS + ["-P", str(self.port)] + list(files) + [f"{self.user}@{self.host}:{dest}"], capture_output=True, text=True, timeout=timeout)
if r.returncode == 0: return True

View file

@ -135,6 +135,17 @@ def weight_check(labels, limit=0.10, hours=1.0):
if ok:
with open(WEIGHT_LOG, "a") as f: f.write(json.dumps({"ts": time.time(), "labels": labels, "share": want}) + "\n")
return verdict
def table_signed_pct():
"""The signed share of the frozen voter table at the hub's last lock (the "% of total" of the LOCKED line), or None."""
hub = next((v for v in Registry.load().values() if v.get("hub") and v.get("state") != "destroyed"), None)
if not hub: return None
out = Box(hub["ssh_host"], hub["ssh_port"], "hub-1", None, None, hub.get("provider")).run("grep -E 'Finality: checkpoint [0-9]+ LOCKED:' /root/fleet/node.log | tail -1 | grep -oE '[0-9.]+% of total' | head -1", 30)[1].strip()
try: return float(out.split("%")[0])
except Exception: return None
def table_gate(min_pct=75.0):
"""The coordinator's rule (6 October 2026, 23:0xZ): nothing of the fleet's moves on a standing box, pool slices included, until the
table reads over min_pct signed. Returns (ok, pct)."""
pct = table_signed_pct(); return (pct is not None and pct > min_pct), pct
def loop(every=600):
dead = {}
while True:
@ -161,6 +172,8 @@ if __name__ == "__main__":
elif a[0] == "update": print(update(a[1]))
elif a[0] == "rerent": print(rerent(a[1]))
elif a[0] == "loop": loop(int(a[1]) if len(a) > 1 else 600)
elif a[0] == "table_gate":
ok, pct = table_gate(float(a[1]) if len(a) > 1 else 75.0); print(json.dumps({"ok": ok, "signed_pct_of_table": pct, "min": float(a[1]) if len(a) > 1 else 75.0})); sys.exit(0 if ok else 1)
elif a[0] == "weight_check":
v = weight_check(a[1:]); print(json.dumps(v)); sys.exit(0 if v["ok"] else 1)
elif a[0] == "weights":

View file

@ -8,7 +8,7 @@ line ten minutes later. Candidates: the wave pods, then the 8x rig (one voter),
for the proving agent's word. Log: ~/Desktop/fleet/winddown.log; schedule and finality reads in ~/Desktop/fleet/winddown.jsonl."""
import sys, os, time, json, datetime
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))); from lib import Box, Registry, standing
LIMIT = float(os.environ.get("LIMIT", "0.095")); HOLD_PCT = float(os.environ.get("HOLD_PCT", "66.7")); EVERY = int(os.environ.get("EVERY", "3600"))
LIMIT = float(os.environ.get("LIMIT", "0.095")); HOLD_PCT = float(os.environ.get("HOLD_PCT", "75.0")) # the coordinator's rule of 23:0xZ: nothing moves on a standing box, pool slices included, until the table reads over 75 percent signed; EVERY = int(os.environ.get("EVERY", "3600"))
ROOT = os.path.expanduser("~/Desktop/fleet"); J = os.path.join(ROOT, "winddown.jsonl")
def st(): return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
def log(**row): row["t"] = st(); open(J, "a").write(json.dumps(row) + "\n"); print(row["t"], {k: v for k, v in row.items() if k != "t"}, flush=True)