152 lines
5.3 KiB
Python
152 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Independent verification of the sol-profile directory classification audit.
|
|
|
|
Re-derives the top-level classification of the live sol profile from disk using
|
|
an independent rule table, then checks three things:
|
|
|
|
(a) the committed 2026-11-02 sol-profile-triage JSON matches the live
|
|
re-derivation exactly (known-good baseline);
|
|
(b) the committed 2026-11-01 JSON (lowercase) records
|
|
.skills_prompt_snapshot.json as state_db, which DIFFERS from the live
|
|
re-derivation (config_data) - i.e. the audit finding is reproducible;
|
|
(c) the 2026-11-01 JSON (uppercase Sol-Profile-Triage...) matches the live
|
|
re-derivation exactly (known-good reference).
|
|
|
|
Exit 0 = audit confirmed (verdict correct); nonzero = verdict wrong.
|
|
|
|
Run from the troupe repo root, e.g.:
|
|
python3 wizard-work/sol/verify_classify_audit.py
|
|
"""
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
REPO = os.path.dirname(os.path.dirname(HERE)) # troupe repo root
|
|
|
|
PROFILE = "/root/.hermes/profiles/sol"
|
|
AUDIT_JSON = os.path.join(REPO, "wizard-work", "sol",
|
|
"sol-profile-triage-2026-11-01.json")
|
|
REF_JSON = os.path.join(REPO, "wizard-work", "sol",
|
|
"Sol-Profile-Triage-2026-11-01.json")
|
|
BASELINE_JSON = os.path.join(REPO, "wizard-work", "sol",
|
|
"sol-profile-triage-2026-11-02.json")
|
|
|
|
KEY_FILE = ".skills_prompt_snapshot.json"
|
|
EXPECTED_LIVE = "config_data" # independent rule: any .json not in exact-name set is config_data
|
|
EXPECTED_RECORDED = "state_db" # the audit finding: the lowercase 11-01 record
|
|
|
|
|
|
def classify(name):
|
|
IDENTITY = {".env", ".env.bak-20261014T165715Z"}
|
|
CONFIG = {".env.lock", "config.yaml", "config.yaml.bak-20261014T165715Z"}
|
|
STATE_DB = {
|
|
"auth.json", "auth.lock",
|
|
"state.db", "state.db-shm", "state.db-wal",
|
|
"state.db.auto-maintenance.lock", "state.db.fts_rebuild.lock",
|
|
"state.db.quarantine.lock",
|
|
"models_dev_cache.etag", "models_dev_cache.json",
|
|
}
|
|
CACHE_DIRS = {"audio_cache", "cache", "image_cache"}
|
|
PLUGIN_DIRS = {"plugin-update-checks", "plugins"}
|
|
LOG_DIRS = {"logs"}
|
|
BACKUP_DIRS = {"backups"}
|
|
VAULT_DIRS = {"vault"}
|
|
SESSIONS_DIRS = {"sessions"}
|
|
SKILLS_DIRS = {"skills"}
|
|
SPECIAL_FILES = {"SOUL.md", "gateway.lock", "projects.db",
|
|
"context_length_cache.yaml", "context_length_cache.yaml.lock"}
|
|
|
|
if name in IDENTITY:
|
|
return "identity"
|
|
if name in CONFIG:
|
|
return "config"
|
|
if name in STATE_DB:
|
|
return "state_db"
|
|
if name == "SOUL.md":
|
|
return "soul"
|
|
if name in ("gateway.lock", "projects.db"):
|
|
return "other"
|
|
if name in SPECIAL_FILES:
|
|
return "config_data" if name == "context_length_cache.yaml" else "other"
|
|
if os.path.isdir(os.path.join(PROFILE, name)):
|
|
if name in CACHE_DIRS:
|
|
return "cache"
|
|
if name in PLUGIN_DIRS:
|
|
return "plugins"
|
|
if name in LOG_DIRS:
|
|
return "logs"
|
|
if name in BACKUP_DIRS:
|
|
return "backups"
|
|
if name in VAULT_DIRS:
|
|
return "vault"
|
|
if name in SESSIONS_DIRS:
|
|
return "sessions"
|
|
if name in SKILLS_DIRS:
|
|
return "skills"
|
|
return "extension_dir"
|
|
if name.endswith((".yaml", ".json")):
|
|
return "config_data"
|
|
if name.endswith(".py"):
|
|
return "script"
|
|
return "other"
|
|
|
|
|
|
def load_entries(path):
|
|
with open(path, encoding="utf-8") as fh:
|
|
art = json.load(fh)
|
|
if isinstance(art, dict) and isinstance(art.get("entries"), list):
|
|
return {e.get("name"): e.get("category") for e in art["entries"]}
|
|
return {}
|
|
|
|
|
|
def main():
|
|
errs = []
|
|
|
|
disk = sorted(os.listdir(PROFILE))
|
|
live_counts = {}
|
|
for n in disk:
|
|
c = classify(n)
|
|
live_counts[c] = live_counts.get(c, 0) + 1
|
|
|
|
if len(disk) != 69:
|
|
errs.append("expected 69 live entries, got %d" % len(disk))
|
|
|
|
# (a) baseline: 2026-11-02 committed artifact matches live.
|
|
baseline = load_entries(BASELINE_JSON)
|
|
if baseline != {n: classify(n) for n in disk}:
|
|
errs.append("2026-11-02 baseline does not match live")
|
|
|
|
# (b) audit finding: lowercase 11-01 records state_db for the key file
|
|
# while the live re-derivation says config_data.
|
|
audit = load_entries(AUDIT_JSON)
|
|
if audit.get(KEY_FILE) != EXPECTED_RECORDED:
|
|
errs.append("audit JSON record for %s is %r, expected %r"
|
|
% (KEY_FILE, audit.get(KEY_FILE), EXPECTED_RECORDED))
|
|
if classify(KEY_FILE) != EXPECTED_LIVE:
|
|
errs.append("live re-derivation for %s is %r, expected %r"
|
|
% (KEY_FILE, classify(KEY_FILE), EXPECTED_LIVE))
|
|
|
|
# (c) reference: 2026-11-01 CAPS artifact matches live.
|
|
ref = load_entries(REF_JSON)
|
|
if ref != {n: classify(n) for n in disk}:
|
|
errs.append("CAPS 11-01 reference does not match live")
|
|
|
|
if errs:
|
|
print("VERIFY FAIL")
|
|
for e in errs:
|
|
print(" -", e)
|
|
return 1
|
|
|
|
print("VERIFY OK")
|
|
print(" live entries: %d" % len(disk))
|
|
print(" .skills_prompt_snapshot.json live=%s (recorded=%s)" % (
|
|
classify(KEY_FILE), audit.get(KEY_FILE)))
|
|
print(" 2026-11-02 baseline matches live: True")
|
|
print(" CAPS 11-01 reference matches live: True")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|