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:
parent
25dac23a12
commit
3ced860f96
4 changed files with 35 additions and 1 deletions
16
tools/fleet/deploy-gate.sh
Executable file
16
tools/fleet/deploy-gate.sh
Executable 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue