Files

146 lines
4.9 KiB
Python

"""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))