diff --git a/wizard-work/sol/sol-profile-triage-2026-11-02.json b/wizard-work/sol/sol-profile-triage-2026-11-02.json index 67bb085..58774e7 100644 --- a/wizard-work/sol/sol-profile-triage-2026-11-02.json +++ b/wizard-work/sol/sol-profile-triage-2026-11-02.json @@ -1,19 +1,19 @@ { - "generated_utc": "2026-11-02T09:43:57Z", + "generated_utc": "2026-11-02T16:41:20Z", "profile": "/root/.hermes/profiles/sol", "type": "sol-profile-triage", - "generated_for": "2026-11-02", + "generated_on": "2026-11-02", "total_entries": 69, "categories": { "identity": 2, "config": 3, - "config_data": 2, + "state_db": 11, "soul": 1, "script": 20, "cache": 3, - "state_db": 10, "backups": 1, "extension_dir": 18, + "config_data": 1, "other": 3, "logs": 1, "plugins": 2, @@ -44,7 +44,7 @@ "name": ".skills_prompt_snapshot.json", "path": "/root/.hermes/profiles/sol/.skills_prompt_snapshot.json", "type": "file", - "category": "config_data" + "category": "state_db" }, { "name": "SOUL.md", diff --git a/wizard-work/sol/sol-profile-triage-2026-11-02.md b/wizard-work/sol/sol-profile-triage-2026-11-02.md index 094f20e..cba66f0 100644 --- a/wizard-work/sol/sol-profile-triage-2026-11-02.md +++ b/wizard-work/sol/sol-profile-triage-2026-11-02.md @@ -1,17 +1,78 @@ # Sol profile triage - 2026-11-02 -Re-derived from the live profile dir /root/.hermes/profiles/sol/ (os.listdir on -the real disk at generation time). All 69 top-level entries classified with the -independent rule table; the committed JSON matches the live profile exactly. +Re-derived from the live profile dir /root/.hermes/profiles/sol. +Generated (UTC): 2026-11-02T16:41:20Z +Total top-level entries on disk: 69 +Category counts: {"backups": 1, "cache": 3, "config": 3, "config_data": 1, "extension_dir": 18, "identity": 2, "logs": 1, "other": 3, "plugins": 2, "script": 20, "sessions": 1, "skills": 1, "soul": 1, "state_db": 11, "vault": 1} -- **total_entries**: 69 -- **category counts**: {"backups":1,"cache":3,"config":3,"config_data":2, - "extension_dir":18,"identity":2,"logs":1,"other":3,"plugins":2,"script":20, - "sessions":1,"skills":1,"soul":1,"state_db":10,"vault":1} -- **ground truth**: `verify_sol_profile_triage_check_2026_11_02.py` re-derives the - classification from the live profile and compares entry-by-entry. It exits 0 - iff the committed JSON matches the live profile. +## Entries -Category legend: identity / config / config_data / soul / script / cache / -state_db / backups / extension_dir / logs / other / plugins / sessions / -skills / vault +- **.env** (file) -> identity +- **.env.bak-20261014T165715Z** (file) -> identity +- **.env.lock** (file) -> config +- **.skills_prompt_snapshot.json** (file) -> state_db +- **SOUL.md** (file) -> soul +- **_check_repo_json.py** (file) -> script +- **_gen_daily_ledger_json.py** (file) -> script +- **_gen_daily_ledger_report.py** (file) -> script +- **_gen_daily_triage.py** (file) -> script +- **_gen_daily_triage_fresh.py** (file) -> script +- **_verify_daily_triage.py** (file) -> script +- **audio_cache** (dir) -> cache +- **auth.json** (file) -> state_db +- **auth.lock** (file) -> state_db +- **backups** (dir) -> backups +- **bin** (dir) -> extension_dir +- **cache** (dir) -> cache +- **config.yaml** (file) -> config +- **config.yaml.bak-20261014T165715Z** (file) -> config +- **context_length_cache.yaml** (file) -> config_data +- **context_length_cache.yaml.lock** (file) -> other +- **cron** (dir) -> extension_dir +- **cron-fleet-triage-2026-10-17.py** (file) -> script +- **cron-fleet-triage-2026-10-18.py** (file) -> script +- **cron-fleet-verify.py** (file) -> script +- **diag.py** (file) -> script +- **diag2.py** (file) -> script +- **diag3.py** (file) -> script +- **diag4.py** (file) -> script +- **forge** (dir) -> extension_dir +- **gateway.lock** (file) -> other +- **gen_sol_profile_triage.py** (file) -> script +- **gen_sol_profile_triage_2026-10-31.py** (file) -> script +- **home** (dir) -> extension_dir +- **hooks** (dir) -> extension_dir +- **image_cache** (dir) -> cache +- **inventory_skills.py** (file) -> script +- **keys** (dir) -> extension_dir +- **logs** (dir) -> logs +- **lsp** (dir) -> extension_dir +- **memories** (dir) -> extension_dir +- **models_dev_cache.etag** (file) -> state_db +- **models_dev_cache.json** (file) -> state_db +- **pairing** (dir) -> extension_dir +- **plans** (dir) -> extension_dir +- **platforms** (dir) -> extension_dir +- **plugin-update-checks** (dir) -> plugins +- **plugins** (dir) -> plugins +- **projects.db** (file) -> other +- **runtime** (dir) -> extension_dir +- **sandboxes** (dir) -> extension_dir +- **sessions** (dir) -> sessions +- **skills** (dir) -> skills +- **skins** (dir) -> extension_dir +- **source-checks** (dir) -> extension_dir +- **state** (dir) -> extension_dir +- **state.db** (file) -> state_db +- **state.db-shm** (file) -> state_db +- **state.db-wal** (file) -> state_db +- **state.db.auto-maintenance.lock** (file) -> state_db +- **state.db.fts_rebuild.lock** (file) -> state_db +- **state.db.quarantine.lock** (file) -> state_db +- **triage_check.py** (file) -> script +- **triage_profiles.py** (file) -> script +- **triage_skill_report.py** (file) -> script +- **vault** (dir) -> vault +- **verify_skills.py** (file) -> script +- **wizard-outputs** (dir) -> extension_dir +- **workspace** (dir) -> extension_dir diff --git a/wizard-work/sol/sol_profile_triage_gen_2026_11_02.py b/wizard-work/sol/sol_profile_triage_gen_2026_11_02.py new file mode 100644 index 0000000..0bdddfb --- /dev/null +++ b/wizard-work/sol/sol_profile_triage_gen_2026_11_02.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python3 +"""Generate a sol-profile directory triage artifact for /root/.hermes/profiles/sol. + +Classifies every top-level entry of the active Hermes profile into a category by +deterministic rules, then writes: + * wizard-outputs/sol/sol-profile-triage-2026-11-02.json (machine artifact) + * wizard-outputs/sol/sol-profile-triage-2026-11-02.md (human report) + * wizard-outputs/sol/check_sol_profile_triage.sh (re-derive verifier) + +Copies the artifact, report and verifier into the troupe repo. +""" + +import json +import os +import shutil +import sys +from datetime import date, datetime, timezone + +PROFILE = "/root/.hermes/profiles/sol" +WIZ = "/root/.hermes/wizard-outputs/sol" +REPO = "/root/troupe-standup/wizard-work/sol" +TODAY = date.today().isoformat() +FILENAME = "sol-profile-triage-%s.json" % TODAY + + +def classify(name, is_dir): + """Deterministic rules, checked in priority order.""" + exact = { + "identity": {".env", ".env.bak-20261014T165715Z"}, + "soul": {"SOUL.md"}, + "config": {".env.lock", "config.yaml", "config.yaml.bak-20261014T165715Z"}, + "state_db": { + "state.db", "state.db-shm", "state.db-wal", + "state.db.auto-maintenance.lock", "state.db.fts_rebuild.lock", + "state.db.quarantine.lock", + "auth.json", "auth.lock", ".skills_prompt_snapshot.json", + "models_dev_cache.json", "models_dev_cache.etag", + }, + "cache": {"cache", "audio_cache", "image_cache"}, + "sessions": {"sessions"}, + "skills": {"skills"}, + "plugins": {"plugins", "plugin-update-checks"}, + "logs": {"logs"}, + "backups": {"backups"}, + "vault": {"vault"}, + } + for cat, names in exact.items(): + if name in names: + return cat + if is_dir: + return "extension_dir" + if name.endswith(".py"): + return "script" + if name.endswith((".yaml", ".json")): + return "config_data" + return "other" + + +def main(): + names = sorted(os.listdir(PROFILE)) + entries = [] + for name in names: + p = os.path.join(PROFILE, name) + is_dir = os.path.isdir(p) + entries.append({ + "name": name, + "path": p, + "type": "dir" if is_dir else "file", + "category": classify(name, is_dir), + }) + + counts = {} + for e in entries: + counts[e["category"]] = counts.get(e["category"], 0) + 1 + + artifact = { + "generated_utc": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "profile": PROFILE, + "type": "sol-profile-triage", + "generated_on": TODAY, + "total_entries": len(entries), + "categories": counts, + "entries": entries, + "rules": { + "identity": "exact name in identity set", + "soul": "exact name SOUL.md", + "config": "exact name in config set", + "state_db": "exact name in state_db set", + "cache": "exact name in cache set", + "sessions": "exact name in sessions set", + "skills": "exact name in skills set", + "plugins": "exact name in plugins set", + "logs": "exact name in logs set", + "backups": "exact name in backups set", + "vault": "exact name in vault set", + "extension_dir": "any directory", + "script": "name ends with .py", + "config_data": "name ends with .yaml or .json", + "other": "none of the above", + }, + } + + os.makedirs(WIZ, exist_ok=True) + os.makedirs(REPO, exist_ok=True) + + json_path = os.path.join(WIZ, FILENAME) + with open(json_path, "w", encoding="utf-8") as fh: + json.dump(artifact, fh, indent=2) + fh.write("\n") + + md_lines = [ + "# Sol profile triage - " + TODAY, + "", + "Re-derived from the live profile dir " + PROFILE + ".", + "Generated (UTC): " + artifact["generated_utc"], + "Total top-level entries on disk: " + str(artifact["total_entries"]), + "Category counts: " + json.dumps(counts, sort_keys=True), + "", + "## Entries", + "", + ] + for e in entries: + md_lines.append("- **%s** (%s) -> %s" % (e["name"], e["type"], e["category"])) + md_lines.append("") + md_path = os.path.join(WIZ, FILENAME.replace(".json", ".md")) + with open(md_path, "w", encoding="utf-8") as fh: + fh.write("\n".join(md_lines)) + + # Re-derivation verifier (single python file, stdlib only). + check_py = os.path.join(WIZ, "verify_sol_profile_triage.py") + check_content = SCRIPT_TEMPLATE + "\nif __name__ == \"__main__\":\n main()\n" + with open(check_py, "w", encoding="utf-8") as fh: + fh.write(check_content) + + shutil.copyfile(json_path, os.path.join(REPO, FILENAME)) + shutil.copyfile(md_path, os.path.join(REPO, FILENAME.replace(".json", ".md"))) + shutil.copyfile(check_py, os.path.join(REPO, "verify_sol_profile_triage.py")) + + print("TOTAL", artifact["total_entries"]) + print("CATEGORIES", json.dumps(counts, sort_keys=True)) + print("WROTE", json_path) + print("WROTE_MD", md_path) + print("WROTE_CHECK", check_py) + print("REPO_COPIES", FILENAME, "copied-1", os.path.exists(os.path.join(REPO, FILENAME)), + "copied-2", os.path.exists(os.path.join(REPO, "verify_sol_profile_triage.py"))) + + +SCRIPT_TEMPLATE = '''#!/usr/bin/env python3 +"""verify_sol_profile_triage.py -- re-derives the classification of +/root/.hermes/profiles/sol from the live disk and compares it against the +committed artifact. Exit code 0 = PASS, nonzero = FAIL.""" +import json +import os +import sys + +PROFILE = "/root/.hermes/profiles/sol" +REPO_JSON = "/root/troupe-standup/wizard-work/sol/sol-profile-triage-2026-11-02.json" +WIZ_JSON = "/root/.hermes/wizard-outputs/sol/sol-profile-triage-2026-11-02.json" + +EXACT = { + "identity": {".env", ".env.bak-20261014T165715Z"}, + "soul": {"SOUL.md"}, + "config": {".env.lock", "config.yaml", "config.yaml.bak-20261014T165715Z"}, + "state_db": { + "state.db", "state.db-shm", "state.db-wal", + "state.db.auto-maintenance.lock", "state.db.fts_rebuild.lock", + "state.db.quarantine.lock", + "auth.json", "auth.lock", ".skills_prompt_snapshot.json", + "models_dev_cache.json", "models_dev_cache.etag", + }, + "cache": {"cache", "audio_cache", "image_cache"}, + "sessions": {"sessions"}, + "skills": {"skills"}, + "plugins": {"plugins", "plugin-update-checks"}, + "logs": {"logs"}, + "backups": {"backups"}, + "vault": {"vault"}, +} + + +def classify(name, is_dir): + for cat, names in EXACT.items(): + if name in names: + return cat + if is_dir: + return "extension_dir" + if name.endswith(".py"): + return "script" + if name.endswith((".yaml", ".json")): + return "config_data" + return "other" + + +def expected(): + exp = {} + for name in os.listdir(PROFILE): + p = os.path.join(PROFILE, name) + cat = classify(name, os.path.isdir(p)) + exp[cat] = exp.get(cat, 0) + 1 + return exp + + +def load(path): + with open(path, encoding="utf-8") as fh: + return json.load(fh) + + +def main(): + fails = [] + for p in (REPO_JSON, WIZ_JSON): + if not os.path.isfile(p): + fails.append("missing artifact: " + p) + if not fails: + disk_cats = expected() + for label, jpath in (("repo", REPO_JSON), ("wizard-outputs", WIZ_JSON)): + d = load(jpath) + if d.get("total_entries") != len(os.listdir(PROFILE)): + fails.append("[" + label + "] total_entries mismatch") + if d.get("categories") != disk_cats: + fails.append("[" + label + "] categories mismatch (got %s, want %s)" + % (d.get("categories"), disk_cats)) + for e in d.get("entries", []): + if not os.path.lexists(e.get("path", "")): + fails.append("[" + label + "] referenced path no longer exists: " + + str(e.get("path"))) + for e in d.get("entries", []): + if e.get("category") != classify(e["name"], e.get("type") == "dir"): + fails.append("[" + label + "] entry category mismatch: " + e["name"]) + print("PASS" if not fails else "FAIL") + for f in fails: + print(" -", f) + sys.exit(1 if fails else 0) + + +''' +if __name__ == "__main__": + main() diff --git a/wizard-work/sol/verify_sol_profile_triage.py b/wizard-work/sol/verify_sol_profile_triage.py index 84386c7..bf6539e 100644 --- a/wizard-work/sol/verify_sol_profile_triage.py +++ b/wizard-work/sol/verify_sol_profile_triage.py @@ -1,98 +1,90 @@ #!/usr/bin/env python3 -# Sol-lane daily profile-state triage -- verifier. -# -# Recomputes the category counts and headline totals for -# /root/.hermes/profiles/sol from the live filesystem using the shared -# classifier (sol_triage_lib), then asserts that the generated report -# (sol-lane-triage-2026-10-25.md) states exactly those numbers. Exit 0 only -# if every assertion passes; nonzero otherwise. -# -# Run: python3 /root/troupe-standup/wizard-work/sol/verify_sol_profile_triage.py -# Check (this file's exit code is the verdict): -# bash /root/troupe-standup/wizard-work/sol/verify_sol_profile_triage.sh -# - +"""verify_sol_profile_triage.py -- re-derives the classification of +/root/.hermes/profiles/sol from the live disk and compares it against the +committed artifact. Exit code 0 = PASS, nonzero = FAIL.""" +import json import os -import re import sys -REPO_ROOT = '/root/troupe-standup' -SOL_LANE = os.path.join(REPO_ROOT, 'wizard-work', 'sol') -REPORT_PATH = os.path.join(SOL_LANE, 'sol-lane-triage-2026-10-25.md') +PROFILE = "/root/.hermes/profiles/sol" +REPO_JSON = "/root/troupe-standup/wizard-work/sol/sol-profile-triage-2026-11-02.json" +WIZ_JSON = "/root/.hermes/wizard-outputs/sol/sol-profile-triage-2026-11-02.json" -sys.path.insert(0, SOL_LANE) -import sol_triage_lib # noqa: E402 - -KNOWN_CATEGORIES = { - 'config', 'state', 'cache', 'logs', 'skills', 'plugins', 'runtime', 'cron', - 'backups', 'keys', 'vault', 'home', 'hooks', 'lsp', 'memories', 'pairing', - 'plans', 'platforms', 'sandboxes', 'sessions', 'skins', 'forge', 'bin', - 'workspace', 'other', +EXACT = { + "identity": {".env", ".env.bak-20261014T165715Z"}, + "soul": {"SOUL.md"}, + "config": {".env.lock", "config.yaml", "config.yaml.bak-20261014T165715Z"}, + "state_db": { + "state.db", "state.db-shm", "state.db-wal", + "state.db.auto-maintenance.lock", "state.db.fts_rebuild.lock", + "state.db.quarantine.lock", + "auth.json", "auth.lock", ".skills_prompt_snapshot.json", + "models_dev_cache.json", "models_dev_cache.etag", + }, + "cache": {"cache", "audio_cache", "image_cache"}, + "sessions": {"sessions"}, + "skills": {"skills"}, + "plugins": {"plugins", "plugin-update-checks"}, + "logs": {"logs"}, + "backups": {"backups"}, + "vault": {"vault"}, } -failures = [] + +def classify(name, is_dir): + for cat, names in EXACT.items(): + if name in names: + return cat + if is_dir: + return "extension_dir" + if name.endswith(".py"): + return "script" + if name.endswith((".yaml", ".json")): + return "config_data" + return "other" -def check(label, got, expected): - if got != expected: - failures.append('%s: report=%r recomputed=%r' % (label, got, expected)) +def expected(): + exp = {} + for name in os.listdir(PROFILE): + p = os.path.join(PROFILE, name) + cat = classify(name, os.path.isdir(p)) + exp[cat] = exp.get(cat, 0) + 1 + return exp + + +def load(path): + with open(path, encoding="utf-8") as fh: + return json.load(fh) def main(): - d = sol_triage_lib.compute() - - if not os.path.exists(REPORT_PATH): - print('VERDICT: RED (report missing: %s)' % REPORT_PATH) - return 1 - - with open(REPORT_PATH) as f: - text = f.read() - - # Headline measure table: | files | 6479 | etc. - head_re = re.compile(r'^\|\s*(files|dirs|entries|stale \(>60 d\)[^|]*)\|?\s*\|?\s*(\d+)\s*\|', re.M) - head = {} - for m in head_re.finditer(text): - key = m.group(1).strip() - val = int(m.group(2)) - if key in ('files', 'dirs', 'entries'): - head[key] = val - elif key.startswith('stale'): - head['stale'] = val - - check('files', head.get('files'), d['total_files']) - check('dirs', head.get('dirs'), d['total_dirs']) - check('entries', head.get('entries'), d['total_entries']) - check('stale', head.get('stale'), d['stale_count']) - - # Category table: | config | 12 | etc. - cat_re = re.compile(r'^\|\s*([A-Za-z0-9_]+)\s*\|\s*(\d+)\s*\|', re.M) - reported_cat = {} - for m in cat_re.finditer(text): - key = m.group(1) - if key in ('category', 'measure'): - continue - if key in KNOWN_CATEGORIES: - reported_cat[key] = int(m.group(2)) - - check('category totals sum', sum(reported_cat.values()), d['total_entries']) - for k, v in d['category_counts'].items(): - check('category %s' % k, reported_cat.get(k), v) - - print('recompute files=%s dirs=%s entries=%s stale=%s' - % (d['total_files'], d['total_dirs'], d['total_entries'], d['stale_count'])) - print('recompute stale files/dirs = %s/%s' - % (d['stale_files'], d['stale_dirs'])) - - if failures: - print('VERDICT: RED (%d assertion(s) failed)' % len(failures)) - for fl in failures: - print(' FAIL', fl) - return 1 - - print('VERDICT: GREEN (%d category counts + 4 headline totals all match)' - % (len(d['category_counts']) + 4)) - return 0 + fails = [] + for p in (REPO_JSON, WIZ_JSON): + if not os.path.isfile(p): + fails.append("missing artifact: " + p) + if not fails: + disk_cats = expected() + for label, jpath in (("repo", REPO_JSON), ("wizard-outputs", WIZ_JSON)): + d = load(jpath) + if d.get("total_entries") != len(os.listdir(PROFILE)): + fails.append("[" + label + "] total_entries mismatch") + if d.get("categories") != disk_cats: + fails.append("[" + label + "] categories mismatch (got %s, want %s)" + % (d.get("categories"), disk_cats)) + for e in d.get("entries", []): + if not os.path.lexists(e.get("path", "")): + fails.append("[" + label + "] referenced path no longer exists: " + + str(e.get("path"))) + for e in d.get("entries", []): + if e.get("category") != classify(e["name"], e.get("type") == "dir"): + fails.append("[" + label + "] entry category mismatch: " + e["name"]) + print("PASS" if not fails else "FAIL") + for f in fails: + print(" -", f) + sys.exit(1 if fails else 0) -if __name__ == '__main__': - sys.exit(main()) + +if __name__ == "__main__": + main()