134 lines
5.4 KiB
Python
134 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Independent re-verification for the sol-profile file/dir class artifact.
|
|
|
|
Checks the artifact against live disk using an independently written
|
|
recompute + integrity tests. Exit 0 = correct.
|
|
|
|
Non-tautology: the recompute table is written separately from the generator,
|
|
and the checks include (a) every live entry appears exactly once in the
|
|
artifact, and (b) every class label belongs to a known vocabulary.
|
|
"""
|
|
import json
|
|
import os
|
|
import stat
|
|
import sys
|
|
|
|
PROFILE = "/root/.hermes/profiles/sol"
|
|
ARTIFACT = "/root/troupe-standup/wizard-work/sol/sol-profile-file-class-2026-10-27.json"
|
|
|
|
# Vocabulary of class labels the artifact may use.
|
|
ALLOWED = {
|
|
"BACKUP::DIR", "BIN::DIR", "CACHE::DIR", "CACHE_ETAG::FILE", "CHECK_DIR::DIR",
|
|
"CONFIG_JSON::FILE", "CONFIG_YAML::FILE", "DIR::UNCLASSIFIED", "DOC::FILE",
|
|
"FILE::UNCLASSIFIED", "FORGE::DIR", "HOME_DIR::DIR", "HOOK::DIR", "LOCK::FILE",
|
|
"LOG_DIR::DIR", "LSP::DIR", "MEMORY::DIR", "OUTPUT_DIR::DIR", "PAIR_PLAT::DIR",
|
|
"PLUGIN_DIR::DIR", "RUNTIME_DIR::DIR", "SCRIPT_PY::FILE", "SKILL_CONTAINER::DIR",
|
|
"SKIN::DIR", "STATE_DB::FILE", "VAULT::DIR",
|
|
}
|
|
|
|
|
|
def rule_class(name):
|
|
"""Independent rule: name-based, no os.stat dependency inside the table."""
|
|
if os.path.isdir(os.path.join(PROFILE, name)):
|
|
if name == "skills":
|
|
return "SKILL_CONTAINER::DIR"
|
|
if name in ("lsp", "bin", "forge", "skills", "vault", "memories",
|
|
"logs", "skins", "pairing", "platforms", "hooks",
|
|
"source-checks", "cache", "backups", "keys", "image_cache",
|
|
"audio_cache", "plugins", "plugin-update-checks",
|
|
"wizard-outputs", "cron", "sessions", "sandboxes",
|
|
"runtime", "workspace", "home"):
|
|
return {"lsp": "LSP::DIR", "bin": "BIN::DIR", "forge": "FORGE::DIR",
|
|
"skills": "SKILL_CONTAINER::DIR", "vault": "VAULT::DIR",
|
|
"memories": "MEMORY::DIR", "logs": "LOG_DIR::DIR",
|
|
"skins": "SKIN::DIR", "pairing": "PAIR_PLAT::DIR",
|
|
"platforms": "PAIR_PLAT::DIR", "hooks": "HOOK::DIR",
|
|
"source-checks": "CHECK_DIR::DIR", "cache": "CACHE::DIR",
|
|
"backups": "BACKUP::DIR", "keys": "CACHE::DIR",
|
|
"image_cache": "CACHE::DIR", "audio_cache": "CACHE::DIR",
|
|
"plugins": "PLUGIN_DIR::DIR", "plugin-update-checks":
|
|
"PLUGIN_DIR::DIR", "wizard-outputs": "OUTPUT_DIR::DIR",
|
|
"cron": "RUNTIME_DIR::DIR", "sessions": "RUNTIME_DIR::DIR",
|
|
"sandboxes": "RUNTIME_DIR::DIR", "runtime": "RUNTIME_DIR::DIR",
|
|
"workspace": "RUNTIME_DIR::DIR", "home": "HOME_DIR::DIR"}.get(name, "DIR::UNCLASSIFIED")
|
|
return "DIR::UNCLASSIFIED"
|
|
# files
|
|
if ".db" in name:
|
|
return "STATE_DB::FILE"
|
|
if name.endswith(".py"):
|
|
return "SCRIPT_PY::FILE"
|
|
if name.endswith((".sh",)):
|
|
return "SCRIPT_SH::FILE"
|
|
if name.endswith((".yaml", ".yml")):
|
|
return "CONFIG_YAML::FILE"
|
|
if name.endswith((".json",)):
|
|
return "CONFIG_JSON::FILE"
|
|
if name.endswith(".lock"):
|
|
return "LOCK::FILE"
|
|
if name.endswith(".etag"):
|
|
return "CACHE_ETAG::FILE"
|
|
if name.endswith((".mp3", ".wav", ".png", ".jpg", ".jpeg")):
|
|
return "ARTIFACT::FILE"
|
|
if name.endswith(".md"):
|
|
return "DOC::FILE"
|
|
if name.endswith(".txt"):
|
|
return "LOG_TXT::FILE"
|
|
return "FILE::UNCLASSIFIED"
|
|
|
|
|
|
def main():
|
|
a = json.load(open(ARTIFACT, encoding="utf-8"))
|
|
errs = []
|
|
|
|
disk_names = set(os.listdir(PROFILE))
|
|
art_names = [e["name"] for e in a["entries"]]
|
|
if len(art_names) != len(set(art_names)):
|
|
errs.append("duplicate entry names in artifact")
|
|
if set(art_names) != disk_names:
|
|
missing = disk_names - set(art_names)
|
|
extra = set(art_names) - disk_names
|
|
errs.append("name set mismatch; missing=%s extra=%s" % (missing, extra))
|
|
|
|
recomputed = {}
|
|
files = dirs = 0
|
|
for name in sorted(disk_names):
|
|
p = os.path.join(PROFILE, name)
|
|
if os.path.isdir(p):
|
|
dirs += 1
|
|
recomputed[name] = rule_class(name)
|
|
else:
|
|
files += 1
|
|
recomputed[name] = rule_class(name)
|
|
|
|
if a.get("files") != files:
|
|
errs.append("files count artifact=%s recomputed=%s" % (a.get("files"), files))
|
|
if a.get("dirs") != dirs:
|
|
errs.append("dirs count artifact=%s recomputed=%s" % (a.get("dirs"), dirs))
|
|
if a.get("total_entries") != len(disk_names):
|
|
errs.append("total_entries artifact=%s recomputed=%d"
|
|
% (a.get("total_entries"), len(disk_names)))
|
|
if a.get("entries") is None or len(a["entries"]) != len(disk_names):
|
|
errs.append("entries length != disk count")
|
|
|
|
for e in a.get("entries", []):
|
|
want = recomputed.get(e["name"])
|
|
if want != e.get("class"):
|
|
errs.append("class mismatch %s: artifact=%r recompute=%r"
|
|
% (e["name"], e.get("class"), want))
|
|
if e.get("class") not in ALLOWED:
|
|
errs.append("unknown class label %r" % (e.get("class"),))
|
|
|
|
if errs:
|
|
print("CHECK FAIL")
|
|
for e in errs:
|
|
print(" -", e)
|
|
return 1
|
|
|
|
print("CHECK OK: %d entries, %d files, %d dirs; artifact == independently "
|
|
"recomputed disk state" % (len(disk_names), files, dirs))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|