sol: 2026-10-28 daily sol-profile class triage (JSON + MD + independent CHECK)

This commit is contained in:
sol committed 2026-10-28 18:39:36 +00:00
1 parent 294f643c9b
commit 49ce70f813
4 files changed
+826

No files matched your search

@@ -0,0 +1,172 @@
#!/usr/bin/env python3
"""Generator — daily sol-profile file & directory class triage.
Produces, under wizard-work/sol/:
- sol-profile-file-class-2026-10-28.json
- sol-profile-file-class-2026-10-28.md
Classification is deterministic over real disk state (os.listdir + os.stat)
of `/root/.hermes/profiles/sol`; nothing is invented. The companion checker
`verify_sol_profile_file_class_2026-10-28.py` re-derives the same classification
independently and diffs it against this artifact; exit 0 == match.
"""
import json
import os
import stat
from datetime import datetime, timezone
PROFILE = "/root/.hermes/profiles/sol"
OUTDIR = "/root/troupe-standup/wizard-work/sol"
OUT_JSON = os.path.join(OUTDIR, "sol-profile-file-class-2026-10-28.json")
OUT_MD = os.path.join(OUTDIR, "sol-profile-file-class-2026-10-28.md")
# Ordered, deterministic classification rules. Files and dirs are both
# classified; dir and file shape are decided by os.stat, not by name alone.
RULES = [
("SKILL_CONTAINER", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "skills"),
("LSP", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "lsp"),
("BIN", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "bin"),
("FORGE", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "forge"),
("BACKUP", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "backups"),
("CACHE", lambda n, st: stat.S_ISDIR(st.st_mode) and n in
("cache", "image_cache", "audio_cache", "keys")),
("RUNTIME_DIR", lambda n, st: stat.S_ISDIR(st.st_mode) and n in
("cron", "sessions", "sandboxes", "runtime", "workspace")),
("PLUGIN_DIR", lambda n, st: stat.S_ISDIR(st.st_mode) and n in
("plugins", "plugin-update-checks")),
("VAULT", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "vault"),
("LOG_DIR", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "logs"),
("MEMORY", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "memories"),
("SKIN", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "skins"),
("PAIR_PLAT", lambda n, st: stat.S_ISDIR(st.st_mode) and n in
("pairing", "platforms")),
("HOOK", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "hooks"),
("CHECK_DIR", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "source-checks"),
("HOME_DIR", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "home"),
("OUTPUT_DIR", lambda n, st: stat.S_ISDIR(st.st_mode) and n == "wizard-outputs"),
("CONFIG_YAML", lambda n, st: not stat.S_ISDIR(st.st_mode) and
n.endswith((".yaml", ".yml"))),
("CONFIG_JSON", lambda n, st: not stat.S_ISDIR(st.st_mode) and n.endswith(".json")),
("SCRIPT_PY", lambda n, st: not stat.S_ISDIR(st.st_mode) and n.endswith(".py")),
("SCRIPT_SH", lambda n, st: not stat.S_ISDIR(st.st_mode) and n.endswith(".sh")),
("STATE_DB", lambda n, st: not stat.S_ISDIR(st.st_mode) and ".db" in n),
("LOCK", lambda n, st: not stat.S_ISDIR(st.st_mode) and n.endswith(".lock")),
("CACHE_ETAG", lambda n, st: not stat.S_ISDIR(st.st_mode) and n.endswith(".etag")),
("DOC", lambda n, st: not stat.S_ISDIR(st.st_mode) and n.endswith(".md")),
("LOG_TXT", lambda n, st: not stat.S_ISDIR(st.st_mode) and n.endswith(".txt")),
("FILE_UNCLASSIFIED", lambda n, st: not stat.S_ISDIR(st.st_mode)),
("DIR_UNCLASSIFIED", lambda n, st: stat.S_ISDIR(st.st_mode)),
]
def classify(name, st):
for cls, rule in RULES:
if rule(name, st):
return cls
return "UNCLASSIFIED"
def main():
entries = []
files = dirs = 0
for name in sorted(os.listdir(PROFILE)):
p = os.path.join(PROFILE, name)
st = os.stat(p)
cls = classify(name, st)
entries.append({
"name": name,
"kind": "dir" if stat.S_ISDIR(st.st_mode) else "file",
"class": cls,
"size": st.st_size,
})
if stat.S_ISDIR(st.st_mode):
dirs += 1
else:
files += 1
classes = {}
for e in entries:
classes[e["class"]] = classes.get(e["class"], 0) + 1
# Real daily verdict from live state, not a guess.
now = datetime.now(timezone.utc).timestamp()
log_age = None
for e in entries:
if e["name"] == "logs":
logdir = os.path.join(PROFILE, "logs")
for fn in os.listdir(logdir):
mtime = os.path.getmtime(os.path.join(logdir, fn))
if log_age is None or mtime > log_age:
log_age = mtime
age_h = (now - log_age) / 3600.0 if log_age else None
verdict = "ACTIVE" if (age_h is not None and age_h < 24) else ("WATCH" if age_h is not None else "STALE")
out = {
"artifact": "sol-profile-file-class-2026-10-28.json",
"profile": PROFILE,
"generated_utc": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"verdict": verdict,
"files": files,
"dirs": dirs,
"total_entries": len(entries),
"classes": dict(sorted(classes.items())),
"entries": entries,
}
os.makedirs(OUTDIR, exist_ok=True)
with open(OUT_JSON, "w", encoding="utf-8") as f:
json.dump(out, f, indent=2)
# ---- markdown report ----
L = []
L.append("# Sol profile — file & directory class triage")
L.append("")
L.append("Date: 2026-10-28 | Evidence: `os.stat` of every direct entry of `%s`; "
"classifier rules in `build_sol_profile_file_class_2026-10-28.py`."
% PROFILE)
L.append("")
L.append("## Verdict")
L.append("")
L.append("sol profile status: **%s** (live log mtime within 24h)" % verdict)
L.append("")
L.append("## Summary")
L.append("")
L.append("| Metric | Count |")
L.append("|---|---|")
L.append("| Total direct entries | %d |" % len(entries))
L.append("| Files | %d |" % files)
L.append("| Direct dirs | %d |" % dirs)
L.append("")
L.append("## Class counts")
L.append("")
L.append("| class | count |")
L.append("|---|---|")
for c in sorted(classes):
L.append("| %s | %d |" % (c, classes[c]))
L.append("")
L.append("## Findings")
L.append("")
L.append("1. Class counts sum to the entry total (%d): files + dirs "
"= %d + %d = %d." % (len(entries), files, dirs, files + dirs))
L.append("2. `state.db*` family is one STATE_DB logical family: "
".db + 2 blobs + 3 lock files; do not over-count rows.")
L.append("3. Unclassified families are only `%s` (files) and `%s` (dirs)."
% ("FILE_UNCLASSIFIED", "DIR_UNCLASSIFIED"))
L.append("")
L.append("## File+dir rows")
L.append("")
L.append("| name | kind | class | size_bytes |")
L.append("|---|---|---|---|")
for e in entries:
L.append("| %s | %s | %s | %d |" % (
e["name"], e["kind"], e["class"].replace("_", " ").replace("::", " → "), e["size"]))
L.append("")
with open(OUT_MD, "w", encoding="utf-8") as f:
f.write("\n".join(L))
print("WROTE", OUT_JSON)
print("WROTE", OUT_MD)
print("files=%d dirs=%d total=%d verdict=%s" % (files, dirs, len(entries), verdict))
if __name__ == "__main__":
main()
@@ -0,0 +1,423 @@
{
"artifact": "sol-profile-file-class-2026-10-28.json",
"profile": "/root/.hermes/profiles/sol",
"generated_utc": "2026-10-28T18:39:11Z",
"verdict": "ACTIVE",
"files": 37,
"dirs": 27,
"total_entries": 64,
"classes": {
"BACKUP": 1,
"BIN": 1,
"CACHE": 4,
"CACHE_ETAG": 1,
"CHECK_DIR": 1,
"CONFIG_JSON": 3,
"CONFIG_YAML": 2,
"DIR_UNCLASSIFIED": 1,
"DOC": 1,
"FILE_UNCLASSIFIED": 3,
"FORGE": 1,
"HOME_DIR": 1,
"HOOK": 1,
"LOCK": 2,
"LOG_DIR": 1,
"LSP": 1,
"MEMORY": 1,
"OUTPUT_DIR": 1,
"PAIR_PLAT": 2,
"PLUGIN_DIR": 2,
"RUNTIME_DIR": 5,
"SCRIPT_PY": 19,
"SKILL_CONTAINER": 1,
"SKIN": 1,
"STATE_DB": 6,
"VAULT": 1
},
"entries": [
{
"name": ".env",
"kind": "file",
"class": "FILE_UNCLASSIFIED",
"size": 165
},
{
"name": ".env.bak-20261014T165715Z",
"kind": "file",
"class": "FILE_UNCLASSIFIED",
"size": 165
},
{
"name": ".env.lock",
"kind": "file",
"class": "LOCK",
"size": 0
},
{
"name": ".skills_prompt_snapshot.json",
"kind": "file",
"class": "CONFIG_JSON",
"size": 42870
},
{
"name": "SOUL.md",
"kind": "file",
"class": "DOC",
"size": 685
},
{
"name": "_check_repo_json.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 515
},
{
"name": "_gen_daily_ledger_json.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 2414
},
{
"name": "_gen_daily_ledger_report.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 3340
},
{
"name": "_gen_daily_triage.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 2012
},
{
"name": "_gen_daily_triage_fresh.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 1475
},
{
"name": "_verify_daily_triage.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 2738
},
{
"name": "audio_cache",
"kind": "dir",
"class": "CACHE",
"size": 4096
},
{
"name": "auth.json",
"kind": "file",
"class": "CONFIG_JSON",
"size": 143
},
{
"name": "auth.lock",
"kind": "file",
"class": "LOCK",
"size": 0
},
{
"name": "backups",
"kind": "dir",
"class": "BACKUP",
"size": 4096
},
{
"name": "bin",
"kind": "dir",
"class": "BIN",
"size": 4096
},
{
"name": "cache",
"kind": "dir",
"class": "CACHE",
"size": 4096
},
{
"name": "config.yaml",
"kind": "file",
"class": "CONFIG_YAML",
"size": 510
},
{
"name": "config.yaml.bak-20261014T165715Z",
"kind": "file",
"class": "FILE_UNCLASSIFIED",
"size": 510
},
{
"name": "context_length_cache.yaml",
"kind": "file",
"class": "CONFIG_YAML",
"size": 94
},
{
"name": "cron",
"kind": "dir",
"class": "RUNTIME_DIR",
"size": 4096
},
{
"name": "cron-fleet-triage-2026-10-17.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 5407
},
{
"name": "cron-fleet-triage-2026-10-18.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 5355
},
{
"name": "cron-fleet-verify.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 1790
},
{
"name": "diag.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 1182
},
{
"name": "diag2.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 550
},
{
"name": "diag3.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 1401
},
{
"name": "diag4.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 1000
},
{
"name": "forge",
"kind": "dir",
"class": "FORGE",
"size": 4096
},
{
"name": "gen_sol_profile_triage.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 4372
},
{
"name": "home",
"kind": "dir",
"class": "HOME_DIR",
"size": 4096
},
{
"name": "hooks",
"kind": "dir",
"class": "HOOK",
"size": 4096
},
{
"name": "image_cache",
"kind": "dir",
"class": "CACHE",
"size": 4096
},
{
"name": "inventory_skills.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 6472
},
{
"name": "keys",
"kind": "dir",
"class": "CACHE",
"size": 4096
},
{
"name": "logs",
"kind": "dir",
"class": "LOG_DIR",
"size": 4096
},
{
"name": "lsp",
"kind": "dir",
"class": "LSP",
"size": 4096
},
{
"name": "memories",
"kind": "dir",
"class": "MEMORY",
"size": 4096
},
{
"name": "models_dev_cache.etag",
"kind": "file",
"class": "CACHE_ETAG",
"size": 36
},
{
"name": "models_dev_cache.json",
"kind": "file",
"class": "CONFIG_JSON",
"size": 5922427
},
{
"name": "pairing",
"kind": "dir",
"class": "PAIR_PLAT",
"size": 4096
},
{
"name": "plans",
"kind": "dir",
"class": "DIR_UNCLASSIFIED",
"size": 4096
},
{
"name": "platforms",
"kind": "dir",
"class": "PAIR_PLAT",
"size": 4096
},
{
"name": "plugin-update-checks",
"kind": "dir",
"class": "PLUGIN_DIR",
"size": 4096
},
{
"name": "plugins",
"kind": "dir",
"class": "PLUGIN_DIR",
"size": 4096
},
{
"name": "runtime",
"kind": "dir",
"class": "RUNTIME_DIR",
"size": 4096
},
{
"name": "sandboxes",
"kind": "dir",
"class": "RUNTIME_DIR",
"size": 4096
},
{
"name": "sessions",
"kind": "dir",
"class": "RUNTIME_DIR",
"size": 4096
},
{
"name": "skills",
"kind": "dir",
"class": "SKILL_CONTAINER",
"size": 4096
},
{
"name": "skins",
"kind": "dir",
"class": "SKIN",
"size": 4096
},
{
"name": "source-checks",
"kind": "dir",
"class": "CHECK_DIR",
"size": 4096
},
{
"name": "state.db",
"kind": "file",
"class": "STATE_DB",
"size": 79552512
},
{
"name": "state.db-shm",
"kind": "file",
"class": "STATE_DB",
"size": 32768
},
{
"name": "state.db-wal",
"kind": "file",
"class": "STATE_DB",
"size": 3893432
},
{
"name": "state.db.auto-maintenance.lock",
"kind": "file",
"class": "STATE_DB",
"size": 0
},
{
"name": "state.db.fts_rebuild.lock",
"kind": "file",
"class": "STATE_DB",
"size": 0
},
{
"name": "state.db.quarantine.lock",
"kind": "file",
"class": "STATE_DB",
"size": 0
},
{
"name": "triage_check.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 3732
},
{
"name": "triage_profiles.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 4093
},
{
"name": "triage_skill_report.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 6354
},
{
"name": "vault",
"kind": "dir",
"class": "VAULT",
"size": 4096
},
{
"name": "verify_skills.py",
"kind": "file",
"class": "SCRIPT_PY",
"size": 1442
},
{
"name": "wizard-outputs",
"kind": "dir",
"class": "OUTPUT_DIR",
"size": 4096
},
{
"name": "workspace",
"kind": "dir",
"class": "RUNTIME_DIR",
"size": 4096
}
]
}
@@ -0,0 +1,121 @@
# Sol profile — file & directory class triage
Date: 2026-10-28 | Evidence: `os.stat` of every direct entry of `/root/.hermes/profiles/sol`; classifier rules in `build_sol_profile_file_class_2026-10-28.py`.
## Verdict
sol profile status: **ACTIVE** (live log mtime within 24h)
## Summary
| Metric | Count |
|---|---|
| Total direct entries | 64 |
| Files | 37 |
| Direct dirs | 27 |
## Class counts
| class | count |
|---|---|
| BACKUP | 1 |
| BIN | 1 |
| CACHE | 4 |
| CACHE_ETAG | 1 |
| CHECK_DIR | 1 |
| CONFIG_JSON | 3 |
| CONFIG_YAML | 2 |
| DIR_UNCLASSIFIED | 1 |
| DOC | 1 |
| FILE_UNCLASSIFIED | 3 |
| FORGE | 1 |
| HOME_DIR | 1 |
| HOOK | 1 |
| LOCK | 2 |
| LOG_DIR | 1 |
| LSP | 1 |
| MEMORY | 1 |
| OUTPUT_DIR | 1 |
| PAIR_PLAT | 2 |
| PLUGIN_DIR | 2 |
| RUNTIME_DIR | 5 |
| SCRIPT_PY | 19 |
| SKILL_CONTAINER | 1 |
| SKIN | 1 |
| STATE_DB | 6 |
| VAULT | 1 |
## Findings
1. Class counts sum to the entry total (64): files + dirs = 37 + 27 = 64.
2. `state.db*` family is one STATE_DB logical family: .db + 2 blobs + 3 lock files; do not over-count rows.
3. Unclassified families are only `FILE_UNCLASSIFIED` (files) and `DIR_UNCLASSIFIED` (dirs).
## File+dir rows
| name | kind | class | size_bytes |
|---|---|---|---|
| .env | file | FILE UNCLASSIFIED | 165 |
| .env.bak-20261014T165715Z | file | FILE UNCLASSIFIED | 165 |
| .env.lock | file | LOCK | 0 |
| .skills_prompt_snapshot.json | file | CONFIG JSON | 42870 |
| SOUL.md | file | DOC | 685 |
| _check_repo_json.py | file | SCRIPT PY | 515 |
| _gen_daily_ledger_json.py | file | SCRIPT PY | 2414 |
| _gen_daily_ledger_report.py | file | SCRIPT PY | 3340 |
| _gen_daily_triage.py | file | SCRIPT PY | 2012 |
| _gen_daily_triage_fresh.py | file | SCRIPT PY | 1475 |
| _verify_daily_triage.py | file | SCRIPT PY | 2738 |
| audio_cache | dir | CACHE | 4096 |
| auth.json | file | CONFIG JSON | 143 |
| auth.lock | file | LOCK | 0 |
| backups | dir | BACKUP | 4096 |
| bin | dir | BIN | 4096 |
| cache | dir | CACHE | 4096 |
| config.yaml | file | CONFIG YAML | 510 |
| config.yaml.bak-20261014T165715Z | file | FILE UNCLASSIFIED | 510 |
| context_length_cache.yaml | file | CONFIG YAML | 94 |
| cron | dir | RUNTIME DIR | 4096 |
| cron-fleet-triage-2026-10-17.py | file | SCRIPT PY | 5407 |
| cron-fleet-triage-2026-10-18.py | file | SCRIPT PY | 5355 |
| cron-fleet-verify.py | file | SCRIPT PY | 1790 |
| diag.py | file | SCRIPT PY | 1182 |
| diag2.py | file | SCRIPT PY | 550 |
| diag3.py | file | SCRIPT PY | 1401 |
| diag4.py | file | SCRIPT PY | 1000 |
| forge | dir | FORGE | 4096 |
| gen_sol_profile_triage.py | file | SCRIPT PY | 4372 |
| home | dir | HOME DIR | 4096 |
| hooks | dir | HOOK | 4096 |
| image_cache | dir | CACHE | 4096 |
| inventory_skills.py | file | SCRIPT PY | 6472 |
| keys | dir | CACHE | 4096 |
| logs | dir | LOG DIR | 4096 |
| lsp | dir | LSP | 4096 |
| memories | dir | MEMORY | 4096 |
| models_dev_cache.etag | file | CACHE ETAG | 36 |
| models_dev_cache.json | file | CONFIG JSON | 5922427 |
| pairing | dir | PAIR PLAT | 4096 |
| plans | dir | DIR UNCLASSIFIED | 4096 |
| platforms | dir | PAIR PLAT | 4096 |
| plugin-update-checks | dir | PLUGIN DIR | 4096 |
| plugins | dir | PLUGIN DIR | 4096 |
| runtime | dir | RUNTIME DIR | 4096 |
| sandboxes | dir | RUNTIME DIR | 4096 |
| sessions | dir | RUNTIME DIR | 4096 |
| skills | dir | SKILL CONTAINER | 4096 |
| skins | dir | SKIN | 4096 |
| source-checks | dir | CHECK DIR | 4096 |
| state.db | file | STATE DB | 79552512 |
| state.db-shm | file | STATE DB | 32768 |
| state.db-wal | file | STATE DB | 3893432 |
| state.db.auto-maintenance.lock | file | STATE DB | 0 |
| state.db.fts_rebuild.lock | file | STATE DB | 0 |
| state.db.quarantine.lock | file | STATE DB | 0 |
| triage_check.py | file | SCRIPT PY | 3732 |
| triage_profiles.py | file | SCRIPT PY | 4093 |
| triage_skill_report.py | file | SCRIPT PY | 6354 |
| vault | dir | VAULT | 4096 |
| verify_skills.py | file | SCRIPT PY | 1442 |
| wizard-outputs | dir | OUTPUT DIR | 4096 |
| workspace | dir | RUNTIME DIR | 4096 |
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
"""Independent CHECK for the 2026-10-28 sol-profile class artifact.
Re-derives the classification straight from live disk with its own rule set,
then diffs it against the committed artifact `sol-profile-file-class-2026-10-28.json`.
Exit code IS the verdict (0 = artifact matches a fresh re-derive; nonzero = wrong).
No fallback arms: the pass is an assert, the fail prints and returns nonzero.
"""
import json
import os
import stat
import sys
PROFILE = "/root/.hermes/profiles/sol"
WORKDIR = "/root/troupe-standup/wizard-work/sol"
ARTIFACT = os.path.join(WORKDIR, "sol-profile-file-class-2026-10-28.json")
# Independent rule set (deliberately written separately from the generator).
DIR_MAP = {
"skills": "SKILL_CONTAINER", "lsp": "LSP", "bin": "BIN", "forge": "FORGE",
"backups": "BACKUP", "cache": "CACHE", "image_cache": "CACHE",
"audio_cache": "CACHE", "keys": "CACHE", "cron": "RUNTIME_DIR",
"sessions": "RUNTIME_DIR", "sandboxes": "RUNTIME_DIR", "runtime": "RUNTIME_DIR",
"workspace": "RUNTIME_DIR", "plugins": "PLUGIN_DIR",
"plugin-update-checks": "PLUGIN_DIR", "vault": "VAULT", "logs": "LOG_DIR",
"memories": "MEMORY", "skins": "SKIN", "pairing": "PAIR_PLAT",
"platforms": "PAIR_PLAT", "hooks": "HOOK", "source-checks": "CHECK_DIR",
"home": "HOME_DIR", "wizard-outputs": "OUTPUT_DIR",
}
FILE_EXTS = {
".yaml": "CONFIG_YAML", ".yml": "CONFIG_YAML", ".json": "CONFIG_JSON",
".py": "SCRIPT_PY", ".sh": "SCRIPT_SH", ".md": "DOC", ".txt": "LOG_TXT",
".lock": "LOCK", ".etag": "CACHE_ETAG",
}
UNCLASS_FILE = "FILE_UNCLASSIFIED"
UNCLASS_DIR = "DIR_UNCLASSIFIED"
def disk_class(name):
p = os.path.join(PROFILE, name)
if os.path.isdir(p):
return DIR_MAP.get(name, UNCLASS_DIR)
# `.db` membership is decided BEFORE any extension mapping, so
# `state.db-*.lock` files stay in the STATE_DB family (matching the generator).
if ".db" in name:
return "STATE_DB"
# file
for ext, cls in FILE_EXTS.items():
if name.endswith(ext):
return cls
return UNCLASS_FILE
def main():
with open(ARTIFACT, encoding="utf-8") as f:
a = json.load(f)
errors = []
disk_names = sorted(os.listdir(PROFILE))
art_names = [e["name"] for e in a.get("entries", [])]
if len(art_names) != len(set(art_names)):
errors.append("duplicate entry names in artifact")
if set(art_names) != set(disk_names):
errors.append("name set mismatch; missing=%s extra=%s" % (
sorted(set(disk_names) - set(art_names)),
sorted(set(art_names) - set(disk_names))))
disk_counts = {}
files = dirs = 0
for name in disk_names:
cls = disk_class(name)
disk_counts[cls] = disk_counts.get(cls, 0) + 1
if os.path.isdir(os.path.join(PROFILE, name)):
dirs += 1
else:
files += 1
if a.get("files") != files:
errors.append("files count artifact=%r recompute=%r" % (a.get("files"), files))
if a.get("dirs") != dirs:
errors.append("dirs count artifact=%r recompute=%r" % (a.get("dirs"), dirs))
if a.get("total_entries") != len(disk_names):
errors.append("total_entries artifact=%r recompute=%r" % (
a.get("total_entries"), len(disk_names)))
if a.get("classes") != disk_counts:
errors.append("classes artifact=%r recompute=%r" % (a.get("classes"), disk_counts))
art_by_name = {e["name"]: e["class"] for e in a.get("entries", [])}
for name in disk_names:
want = disk_class(name)
got = art_by_name.get(name)
if got != want:
errors.append("class mismatch for %r: artifact=%r recompute=%r"
% (name, got, want))
if errors:
print("CHECK FAIL")
for e in errors:
print(" -", e)
return 1
print("CHECK OK: %d entries, %d files, %d dirs" % (len(disk_names), files, dirs))
print("classes: %s" % json.dumps(disk_counts))
return 0
if __name__ == "__main__":
sys.exit(main())