#!/usr/bin/env python3 """Trace Hooks — Find orphan hooks (registered but never triggered) and orphan triggers (triggered but never received).""" from __future__ import annotations import re, sys, json from pathlib import Path from collections import defaultdict ROOT = Path(__file__).resolve().parent.parent.parent APP = ROOT / "app" def find_hook_registrations() -> list[dict]: regs = [] for py in APP.rglob("*.py"): try: content = py.read_text() except: continue rel = str(py.relative_to(ROOT)) for m in re.finditer(r'register_(?:action|filter)s?\s*\(\s*["\']([^"\']+)['"]', content): regs.append({"file": rel, "hook_type": "action_or_filter", "hook_name": m.group(1)}) for m in re.finditer(r'register_actions_by_owner\s*\(\s*["\']([^"\']+)['"]', content): regs.append({"file": rel, "hook_type": "action_group", "hook_name": m.group(1)}) return regs def find_hook_triggers() -> list[dict]: triggers = [] for py in APP.rglob("*.py"): try: content = py.read_text() except: continue rel = str(py.relative_to(ROOT)) for m in re.finditer(r'do_action\s*\(\s*["\']([^"\']+)['"]', content): triggers.append({"file": rel, "hook_name": m.group(1), "type": "action"}) for m in re.finditer(r'apply_filters?\s*\(\s*["\']([^"\']+)['"]', content): triggers.append({"file": rel, "hook_name": m.group(1), "type": "filter"}) for m in re.finditer(r'trigger\s*\(\s*["\']([^"\']+)['"]', content): triggers.append({"file": rel, "hook_name": m.group(1), "type": "trigger"}) return triggers def main(): print("=" * 70) print("TRACE HOOKS — Orphan Hooks & Triggers") print("=" * 70) regs = find_hook_registrations() triggers = find_hook_triggers() reg_names = {r["hook_name"] for r in regs} trig_names = {t["hook_name"] for t in triggers} orphans_reg = [r for r in regs if r["hook_name"] not in trig_names] orphans_trig = [t for t in triggers if t["hook_name"] not in reg_names] print(f"\nHook registrations: {len(regs)}") print(f"Hook triggers: {len(triggers)}") print(f"\nISSUES: {len(orphans_reg) + len(orphans_trig)}") print(f" Registered but never triggered: {len(orphans_reg)}") print(f" Triggered but never received: {len(orphans_trig)}") if orphans_reg: print("\n--- Registered but never triggered ---") for o in orphans_reg[:20]: print(f" {o['hook_name']} (in {o['file']})") if orphans_trig: print("\n--- Triggered but never received ---") for o in orphans_trig[:20]: print(f" {o['hook_name']} (in {o['file']})") results = {"registrations": regs, "triggers": triggers, "orphan_registrations": orphans_reg, "orphan_triggers": orphans_trig} with open(ROOT / "scripts" / "test_suite" / "results_trace_hooks.json", "w") as f: json.dump(results, f, indent=2, default=str) return len(orphans_reg) + len(orphans_trig) if __name__ == "__main__": sys.exit(main())