Files
MoFin/scripts/classify_diverged.py
T
hmo 4f83ee8a01 feat(hygiene): anti-redundancy enforcement — spec rules + weekly audit
Root cause analysis of the 2026-07-20 redundancy incident:
1. No single-source-of-truth rule -> same file legitimately lived in 4+
   locations, diverging silently
2. Relative path resolution (Path(__file__).parent/'data') -> each
   hardlinked copy of mofin_db.py pointed to a DIFFERENT database
3. 'Backup habit' left .bak/legacy files in production dirs, which
   monitoring then scanned and reported as false alarms
4. Half-done migrations: DB tables created but old JSON writers/readers
   stayed (price_events), old files stayed
5. Dead modules never got buried: xiaoguo 'dead' but bot ran 8 days
   as root eating 2.5GB
6. Monitoring checked 'does it exist' not 'is it alive' -> stale file
   mtime reported as 'pipeline stalled 14 days' (false alarm)
7. No 'system hygiene' as a check category at all

Prevention implemented:
- dev-spec.md v2.0: 五条红线 -> 十条红线
  #6 single source of truth (hardlink only, no independent copies)
  #7 absolute data paths only (no __file__-relative data resolution)
  #8 no backups/legacy in production data dirs (archive immediately)
  #9 dead module burial checklist (6 mandatory steps)
  #10 monitor liveness (DB table freshness) not existence
- File Location Constitution: canonical location per content type
- NEW system_hygiene_audit.py: weekly Monday 07:30 cron checking
  diverged copies / broken hardlinks / zombie processes / orphan data
  files / dead cron scripts / DB freshness -> hygiene_report.json + XMPP
- specs/hygiene.json: module spec per red line #1
- Verified: audit found 5 real issues on first run, all fixed, re-run clean
2026-07-20 19:04:05 +08:00

47 lines
2.4 KiB
Python

import subprocess, os, re
from datetime import datetime
DIVERGED = """advice_reconciliation.py branch_scanner.py bulk_strategy_regenerate.py
collect_evaluation_data.py cron_to_xmpp.py market_insight.py market_screener.py market_watch.py
memory_guardian.py mo_provider.py mofin_collect.py mofin_health.py mofin_news.py multi_timeframe.py
premarket_full_review.py promote_candidates.py prune_branches.py server.py stock_profile.py
stock_sector_enrich.py strategy_evaluator.py strategy_feedback.py strategy_lifecycle.py strategy_tree.py
system_audit.py system_health_check.py technical_analysis.py trend_detector.py
xiaoguo_news_processor.py xiaoguo_scanner.py xmpp_agent_core.py""".split()
SEARCH_DIRS = ['/home/hmo/MoFin/deploy/profile-scripts', '/home/hmo/.hermes/profiles/position-analyst/scripts', '/home/hmo/.hermes/scripts']
now = datetime.now()
print(f"{'file':35s} {'imported_by':40s} {'cron?':5s} {'root_mtime':12s} {'deploy_mtime':12s} verdict")
print('-' * 130)
for f in DIVERGED:
stem = f[:-3]
# 谁 import 它
r = subprocess.run(
f"grep -rln -E '(^|\\s)(from|import)\\s+{stem}(\\s|$|\\.)' /home/hmo/MoFin/deploy/profile-scripts /home/hmo/.hermes/profiles/position-analyst/scripts /home/hmo/MoFin --include='*.py' 2>/dev/null | grep -v venv | grep -v '{f}' | head -3",
shell=True, capture_output=True, text=True, timeout=15)
importers = [os.path.basename(x) for x in r.stdout.splitlines() if x.strip() and f not in x]
imported = ','.join(importers[:3]) if importers else '-'
# 是否在 cron 里被直接执行
in_cron = subprocess.run(
f"grep -l '\"script\": \"{f}\"' /home/hmo/.hermes/profiles/position-analyst/cron/jobs.json /home/hmo/.hermes/cron/jobs.json 2>/dev/null",
shell=True, capture_output=True, text=True, timeout=5)
is_cron = 'Y' if in_cron.stdout.strip() else '-'
# mtimes
root_p = f'/home/hmo/MoFin/{f}'
deploy_p = f'/home/hmo/MoFin/deploy/profile-scripts/{f}'
rm = datetime.fromtimestamp(os.path.getmtime(root_p)).strftime('%m-%d') if os.path.exists(root_p) else '-'
dm = datetime.fromtimestamp(os.path.getmtime(deploy_p)).strftime('%m-%d') if os.path.exists(deploy_p) else '-'
# verdict
if importers:
verdict = 'LIBRARY->root为准'
elif is_cron == 'Y':
verdict = 'CRON->deploy为准'
else:
verdict = '待查'
print(f"{f:35s} {imported:40s} {is_cron:5s} {rm:12s} {dm:12s} {verdict}")