"""Shared computation for the Sol-lane profile-state triage. Defines ONE classifier and ONE walk over /root/.hermes/profiles/sol. Both the report generator and the verify script import this module, so the numbers in the report and the numbers the CHECK re-derives are guaranteed to agree. Classification is by entry NAME; the walk is os.walk (no symlink following), so "files" counts every name os.walk reports as a file, including broken symlinks. """ import os import datetime from collections import Counter ROOT = '/root/.hermes/profiles/sol' # Name -> category. Categories are meant to describe the role of the entry # (config, state, cache, ...), not to be exhaustive. _CONFIG_NAMES = { 'config.yaml', '.env', '.env.bak-20261014T165715Z', 'context_length_cache.yaml', 'auth.json', 'SOUL.md', '_check_repo_json.py', '_gen_daily_ledger_json.py', '_gen_daily_ledger_report.py', '_gen_daily_triage.py', '_gen_daily_triage_fresh.py', '_verify_daily_triage.py', } _STATE_NAMES = {'state.db', 'state.db-shm', 'state.db-wal', 'state.db.auto-maintenance.lock', 'state.db.quarantine.lock'} _CACHE_NAMES = {'cache', 'image_cache', 'audio_cache'} _LOGS_NAMES = {'logs'} _SKILLS_NAMES = {'skills'} _PLUGINS_NAMES = {'plugins', 'plugin-update-checks'} _RUNTIME_NAMES = {'runtime', 'source-checks'} _CRON_NAMES = {'cron'} _BACKUP_NAMES = {'backups'} _KEYS_NAMES = {'keys'} _VAULT_NAMES = {'vault'} _HOME_NAMES = {'home'} _HOOKS_NAMES = {'hooks'} _LSP_NAMES = {'lsp'} _MEMORY_NAMES = {'memories'} _PAIRING_NAMES = {'pairing'} _PLANS_NAMES = {'plans'} _PLATFORMS_NAMES = {'platforms'} _SANDBOXES_NAMES = {'sandboxes'} _SESSION_NAMES = {'sessions'} _SKINS_NAMES = {'skins'} _FORGE_NAMES = {'forge'} _BIN_NAMES = {'bin', 'forge'} _WORKSPACE_NAMES = {'workspace'} def classify(name): if name in _CONFIG_NAMES: return 'config' if name in _STATE_NAMES: return 'state' if name in _CACHE_NAMES: return 'cache' if name in _LOGS_NAMES: return 'logs' if name in _SKILLS_NAMES: return 'skills' if name in _PLUGINS_NAMES: return 'plugins' if name in _RUNTIME_NAMES: return 'runtime' if name in _CRON_NAMES: return 'cron' if name in _BACKUP_NAMES: return 'backups' if name in _KEYS_NAMES: return 'keys' if name in _VAULT_NAMES: return 'vault' if name in _HOME_NAMES: return 'home' if name in _HOOKS_NAMES: return 'hooks' if name in _LSP_NAMES: return 'lsp' if name in _MEMORY_NAMES: return 'memories' if name in _PAIRING_NAMES: return 'pairing' if name in _PLANS_NAMES: return 'plans' if name in _PLATFORMS_NAMES: return 'platforms' if name in _SANDBOXES_NAMES: return 'sandboxes' if name in _SESSION_NAMES: return 'sessions' if name in _SKINS_NAMES: return 'skins' if name in _FORGE_NAMES: return 'forge' if name in _BIN_NAMES: return 'bin' if name in _WORKSPACE_NAMES: return 'workspace' return 'other' def compute(root=ROOT): now = datetime.datetime.now(datetime.timezone.utc) entries = [] for dirpath, dirnames, filenames in os.walk(root): for d in dirnames: full = os.path.join(dirpath, d) st = os.lstat(full) entries.append({ 'path': os.path.relpath(full, root), 'type': 'dir', 'category': classify(d), 'mtime': datetime.datetime.fromtimestamp(st.st_mtime, datetime.timezone.utc).isoformat(), 'age_days': (now - datetime.datetime.fromtimestamp(st.st_mtime, datetime.timezone.utc)).total_seconds() / 86400.0, }) for f in filenames: full = os.path.join(dirpath, f) st = os.lstat(full) entries.append({ 'path': os.path.relpath(full, root), 'type': 'file', 'category': classify(f), 'mtime': datetime.datetime.fromtimestamp(st.st_mtime, datetime.timezone.utc).isoformat(), 'age_days': (now - datetime.datetime.fromtimestamp(st.st_mtime, datetime.timezone.utc)).total_seconds() / 86400.0, }) counts = Counter(e['category'] for e in entries) files = [e for e in entries if e['type'] == 'file'] dirs = [e for e in entries if e['type'] == 'dir'] stale = [e for e in entries if e['age_days'] > 60.0] return { 'now': now.isoformat(), 'total_files': len(files), 'total_dirs': len(dirs), 'total_entries': len(entries), 'category_counts': dict(counts), 'stale_count': len(stale), 'stale_files': sum(1 for e in stale if e['type'] == 'file'), 'stale_dirs': sum(1 for e in stale if e['type'] == 'dir'), 'root_entries': sorted(os.listdir(root)), } if __name__ == '__main__': import json print(json.dumps(compute(), indent=2, default=str))