Files
troupe-standup/wizard-work/sol/verify_fleet_classify.py
T

97 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""CHECK — sol fleet-classification verdict (lite).
Independently recomputes the seven *stable* fleet classes straight from the
live filesystem and diffs them against the generated artifact. The artifact
is the sol lane's classification of the Hermes runtime instance.
Exit code 0 == CORRECT, 1 == WRONG.
NOTE on git: the troupe repo's untracked/modified set is actively churned by a
background file-writer, so git-derived fields are intentionally NOT part of
this check. This check covers only the stable, reproducibly-censusable
classes: profiles, skills, plugins, memories, cron-fires.
"""
import glob
import json
import os
import sys
HERMES = "/root/.hermes"
ARTIFACT = os.path.join(HERMES, "wizard-outputs", "sol", "fleet-classify-2026-11-01.json")
STABLE_CLASSES = [
"active-profile", "deleted-profile", "active-skill",
"archived-skill", "plugin", "memory", "cron-fire",
]
def recompute_fs():
out = {c: [] for c in STABLE_CLASSES}
profdir = os.path.join(HERMES, "profiles")
for n in os.listdir(profdir):
if not n.startswith(".deleted"):
out["active-profile"].append(n)
for n in os.listdir(os.path.join(profdir, ".deleted")):
out["deleted-profile"].append(n)
skills_dir = os.path.join(HERMES, "skills")
for n in os.listdir(skills_dir):
if n.startswith("."):
continue
out["active-skill"].append(n)
for n in os.listdir(os.path.join(skills_dir, ".archive")):
out["archived-skill"].append(n)
out["plugin"].extend(sorted(os.listdir(os.path.join(HERMES, "plugins"))))
out["memory"].extend(sorted(os.listdir(os.path.join(HERMES, "memories"))))
out["cron-fire"].extend(
sorted(os.path.basename(p) for p in glob.glob(os.path.join(HERMES, "cron", ".fire*")))
)
return out
def main():
try:
with open(ARTIFACT) as f:
art = json.load(f)
except Exception as exc:
print("VERDICT: WRONG")
print(" artifact not valid JSON:", exc)
sys.exit(1)
if not isinstance(art, dict) or "classes" not in art or "summary" not in art:
print("VERDICT: WRONG")
print(" artifact missing 'classes'/'summary'")
sys.exit(1)
fs = recompute_fs()
errors = []
# list equality for every stable class
for c in STABLE_CLASSES:
art_list = sorted(it["name"] for it in art["classes"].get(c, []) if isinstance(it, dict))
if art_list != sorted(fs[c]):
errors.append("class %r: artifact=%r fs=%r" % (c, art["classes"].get(c), fs[c]))
# summary count cross-check
for c in STABLE_CLASSES:
if art["summary"].get(c) != len(fs[c]):
errors.append("summary %r: %r != expected %d" % (c, art["summary"].get(c), len(fs[c])))
if errors:
print("VERDICT: WRONG")
for e in errors:
print(" -", e)
sys.exit(1)
print("VERDICT: CORRECT")
print(" active-profile=%d deleted-profile=%d active-skill=%d archived-skill=%d plugin=%d memory=%d cron-fire=%d"
% tuple(art["summary"][c] for c in STABLE_CLASSES))
sys.exit(0)
if __name__ == "__main__":
main()