pumuki-ast-hooks 5.6.16 → 6.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. package/README.md +9 -9
  2. package/package.json +1 -1
  3. package/scripts/hooks-system/.audit-reports/auto-recovery.log +4 -0
  4. package/scripts/hooks-system/.audit-reports/install-wizard.log +20 -0
  5. package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +104 -0
  6. package/scripts/hooks-system/application/services/PolicyBundleService.js +54 -0
  7. package/scripts/hooks-system/application/services/RulesDigestService.js +94 -0
  8. package/scripts/hooks-system/application/services/__tests__/PolicyBundleService.spec.js +60 -0
  9. package/scripts/hooks-system/application/services/__tests__/RulesDigestService.spec.js +83 -0
  10. package/scripts/hooks-system/config/project.config.json +1 -1
  11. package/scripts/hooks-system/infrastructure/ast/common/__tests__/network-resilience-analyzer.spec.js +12 -0
  12. package/scripts/hooks-system/infrastructure/ast/common/network-resilience-analyzer.js +2 -1
  13. package/scripts/hooks-system/infrastructure/ast/ios/detectors/__tests__/ios-unused-imports.spec.js +14 -0
  14. package/scripts/hooks-system/infrastructure/ast/ios/detectors/ios-ast-intelligent-strategies.js +6 -1
  15. package/scripts/hooks-system/infrastructure/ast/text/__tests__/text-scanner.spec.js +49 -0
  16. package/scripts/hooks-system/infrastructure/ast/text/text-scanner.js +6 -2
  17. package/scripts/hooks-system/infrastructure/mcp/__tests__/preflight-check-blocks-tests.spec.js +2 -2
  18. package/scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js +135 -2
  19. package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js +19 -0
  20. package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +39 -10
  21. package/scripts/hooks-system/infrastructure/watchdog/__tests__/.audit-reports/token-monitor.log +15 -0
package/README.md CHANGED
@@ -27,19 +27,19 @@ npx ast-hooks
27
27
 
28
28
  ### Pre‑write enforcement (block before writing)
29
29
 
30
- ![Pre-write hook output](./assets/Hook_02.png)
30
+ ![Pre-write hook output](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/Hook_02.png)
31
31
 
32
32
  ### AI Gate (blocked example)
33
33
 
34
- ![AI Gate blocked example](./assets/ai_gate.png)
34
+ ![AI Gate blocked example](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ai_gate.png)
35
35
 
36
36
  ### ai-start (bootstrap + evidence refresh)
37
37
 
38
- ![ai-start output](./assets/ai-start.png)
38
+ ![ai-start output](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ai-start.png)
39
39
 
40
40
  ### Pre-flight check (in-memory validation)
41
41
 
42
- ![pre-flight-check output](./assets/pre-flight-check.png)
42
+ ![pre-flight-check output](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/pre-flight-check.png)
43
43
 
44
44
  ### Interactive menu (orchestrator overview)
45
45
 
@@ -58,15 +58,15 @@ Documentation:
58
58
 
59
59
  ## Visual Overview
60
60
 
61
- ![AST Intelligence System Overview](./assets/ast_intelligence_01.svg)
61
+ ![AST Intelligence System Overview](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_01.svg)
62
62
 
63
- ![AST Intelligence Workflow](./assets/ast_intelligence_02.svg)
63
+ ![AST Intelligence Workflow](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_02.svg)
64
64
 
65
- ![AST Intelligence Audit - Part 1](./assets/ast_intelligence_03.svg)
65
+ ![AST Intelligence Audit - Part 1](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_03.svg)
66
66
 
67
- ![AST Intelligence Audit - Part 2](./assets/ast_intelligence_04.svg)
67
+ ![AST Intelligence Audit - Part 2](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_04.svg)
68
68
 
69
- ![AST Intelligence Audit - Part 3](./assets/ast_intelligence_05.svg)
69
+ ![AST Intelligence Audit - Part 3](https://raw.githubusercontent.com/SwiftEnProfundidad/ast-intelligence-hooks/main/assets/ast_intelligence_05.svg)
70
70
 
71
71
  ---
72
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki-ast-hooks",
3
- "version": "5.6.16",
3
+ "version": "6.0.2",
4
4
  "description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -12,3 +12,7 @@
12
12
  {"timestamp":"2026-01-11T20:43:24.310Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
13
13
  {"timestamp":"2026-01-11T21:27:16.722Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
14
14
  {"timestamp":"2026-01-11T21:44:57.015Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
15
+ {"timestamp":"2026-01-12T08:29:42.539Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
16
+ {"timestamp":"2026-01-12T08:44:03.829Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
17
+ {"timestamp":"2026-01-12T18:40:40.786Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
18
+ {"timestamp":"2026-01-12T19:29:17.037Z","level":"info","component":"AutoRecovery","event":"NotificationCenterService shutdown","data":{"totalEnqueued":0,"totalSent":0,"totalDeduplicated":0,"totalCooldownSkipped":0,"totalFailed":0,"totalRetries":0,"queueSize":0,"deduplication":{"size":0},"cooldowns":{"activeCooldowns":0}},"context":{}}
@@ -54,3 +54,23 @@
54
54
  {"timestamp":"2026-01-11T21:44:57.281Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
55
55
  {"timestamp":"2026-01-11T21:44:57.282Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
56
56
  {"timestamp":"2026-01-11T21:44:57.282Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
57
+ {"timestamp":"2026-01-12T08:28:17.002Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_START","data":{"repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"},"context":{}}
58
+ {"timestamp":"2026-01-12T08:28:17.010Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
59
+ {"timestamp":"2026-01-12T08:28:17.010Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
60
+ {"timestamp":"2026-01-12T08:28:17.010Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
61
+ {"timestamp":"2026-01-12T08:29:42.171Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_START","data":{"repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"},"context":{}}
62
+ {"timestamp":"2026-01-12T08:29:42.179Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
63
+ {"timestamp":"2026-01-12T08:29:42.180Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
64
+ {"timestamp":"2026-01-12T08:29:42.180Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
65
+ {"timestamp":"2026-01-12T08:44:03.887Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_START","data":{"repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"},"context":{}}
66
+ {"timestamp":"2026-01-12T08:44:03.895Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
67
+ {"timestamp":"2026-01-12T08:44:03.895Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
68
+ {"timestamp":"2026-01-12T08:44:03.895Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
69
+ {"timestamp":"2026-01-12T18:40:40.839Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_START","data":{"repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"},"context":{}}
70
+ {"timestamp":"2026-01-12T18:40:40.845Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
71
+ {"timestamp":"2026-01-12T18:40:40.846Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
72
+ {"timestamp":"2026-01-12T18:40:40.846Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
73
+ {"timestamp":"2026-01-12T19:29:17.091Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_START","data":{"repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"},"context":{}}
74
+ {"timestamp":"2026-01-12T19:29:17.097Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_CONFIG_EXISTS","data":{"configPath":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.hook-system/config.json"},"context":{}}
75
+ {"timestamp":"2026-01-12T19:29:17.098Z","level":"error","component":"InstallWizard","event":"INSTALL_WIZARD_SYMLINK_FAILED","data":{"error":"EEXIST: file already exists, symlink '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/scripts/hooks-system/bin/guard-supervisor.js' -> '/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system/.git/hooks/guard-supervisor'"},"context":{}}
76
+ {"timestamp":"2026-01-12T19:29:17.098Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
@@ -578,3 +578,107 @@
578
578
  {"timestamp":1768167897015,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
579
579
  {"timestamp":1768167897015,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
580
580
  {"timestamp":1768167897015,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
581
+ {"timestamp":1768206582536,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
582
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
583
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
584
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
585
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
586
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
587
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
588
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
589
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
590
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
591
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
592
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
593
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
594
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
595
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
596
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
597
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
598
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
599
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
600
+ {"timestamp":1768206582537,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
601
+ {"timestamp":1768206582538,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
602
+ {"timestamp":1768206582538,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
603
+ {"timestamp":1768206582538,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
604
+ {"timestamp":1768206582538,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
605
+ {"timestamp":1768207443827,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
606
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
607
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
608
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
609
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
610
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
611
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
612
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
613
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
614
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
615
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
616
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
617
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
618
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
619
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
620
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
621
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
622
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
623
+ {"timestamp":1768207443828,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
624
+ {"timestamp":1768207443829,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
625
+ {"timestamp":1768207443829,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
626
+ {"timestamp":1768207443829,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
627
+ {"timestamp":1768207443829,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
628
+ {"timestamp":1768207443829,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
629
+ {"timestamp":1768243240785,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
630
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
631
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
632
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
633
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
634
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
635
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
636
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
637
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
638
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
639
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
640
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
641
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
642
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
643
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
644
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
645
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
646
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
647
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
648
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
649
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
650
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
651
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
652
+ {"timestamp":1768243240786,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
653
+ {"timestamp":1768246157036,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
654
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
655
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
656
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
657
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
658
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
659
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
660
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
661
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
662
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
663
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
664
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
665
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
666
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
667
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
668
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
669
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
670
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
671
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
672
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
673
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
674
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
675
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
676
+ {"timestamp":1768246157037,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
677
+ {"timestamp":1768247437287,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
678
+ {"timestamp":1768247437287,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
679
+ {"timestamp":1768247437287,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
680
+ {"timestamp":1768247437287,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
681
+ {"timestamp":1768247690240,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
682
+ {"timestamp":1768247690240,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
683
+ {"timestamp":1768247690240,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
684
+ {"timestamp":1768247690241,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
@@ -0,0 +1,54 @@
1
+ const crypto = require('crypto');
2
+
3
+ class PolicyBundleService {
4
+ createBundle({ platforms, mandatory, enforcedAt, rulesSources = [] }) {
5
+ const bundleId = this._generateBundleId(platforms, rulesSources);
6
+ const ttlMinutes = 10;
7
+ const createdAt = new Date();
8
+ const expiresAt = new Date(createdAt.getTime() + ttlMinutes * 60000);
9
+
10
+ return {
11
+ policy_bundle_id: bundleId,
12
+ platforms,
13
+ mandatory,
14
+ enforcedAt,
15
+ rules_sources: rulesSources,
16
+ createdAt: createdAt.toISOString(),
17
+ expiresAt: expiresAt.toISOString(),
18
+ ttl_minutes: ttlMinutes
19
+ };
20
+ }
21
+
22
+ validateMandatory(bundle) {
23
+ if (!bundle || typeof bundle.mandatory !== 'boolean') {
24
+ return false;
25
+ }
26
+
27
+ return bundle.mandatory === true;
28
+ }
29
+
30
+ isExpired(bundle) {
31
+ if (!bundle || !bundle.expiresAt) {
32
+ return true;
33
+ }
34
+
35
+ const expiresAt = new Date(bundle.expiresAt);
36
+ return Date.now() >= expiresAt.getTime();
37
+ }
38
+
39
+ isValid(bundle) {
40
+ return bundle &&
41
+ bundle.policy_bundle_id &&
42
+ this.validateMandatory(bundle) &&
43
+ !this.isExpired(bundle);
44
+ }
45
+
46
+ _generateBundleId(platforms, rulesSources) {
47
+ const platformsStr = platforms.sort().join(',');
48
+ const sourcesStr = rulesSources.map(s => `${s.file}:${s.sha256}`).join('|');
49
+ const input = `${platformsStr}:${sourcesStr}:${Date.now()}`;
50
+ return `bundle-${crypto.createHash('sha256').update(input, 'utf8').digest('hex').substring(0, 16)}`;
51
+ }
52
+ }
53
+
54
+ module.exports = PolicyBundleService;
@@ -0,0 +1,94 @@
1
+ const crypto = require('crypto');
2
+
3
+ class RulesDigestService {
4
+ buildEntry({ file, content, path }) {
5
+ if (!content || typeof content !== 'string' || content.trim().length === 0) {
6
+ return {
7
+ file,
8
+ path: path || null,
9
+ verified: false,
10
+ summary: 'not found',
11
+ sha256: null,
12
+ linesRead: 0
13
+ };
14
+ }
15
+
16
+ const sha256 = crypto.createHash('sha256').update(content, 'utf8').digest('hex');
17
+ const linesRead = content.split('\n').length;
18
+ const summary = this._extractSummary(content);
19
+
20
+ return {
21
+ file,
22
+ path: path || null,
23
+ verified: true,
24
+ summary,
25
+ sha256,
26
+ linesRead,
27
+ content
28
+ };
29
+ }
30
+
31
+ _extractSummary(content) {
32
+ const lines = content.split('\n').filter(line => {
33
+ const trimmed = line.trim();
34
+ return trimmed.length > 0 && trimmed !== '---';
35
+ });
36
+
37
+ if (lines.length === 0) {
38
+ return 'not found';
39
+ }
40
+
41
+ const firstMeaningfulLine = lines.find(line => {
42
+ const trimmed = line.trim();
43
+ return !trimmed.startsWith('#') && !trimmed.startsWith('title:');
44
+ });
45
+
46
+ if (!firstMeaningfulLine) {
47
+ return lines[0].trim();
48
+ }
49
+
50
+ return firstMeaningfulLine.trim().substring(0, 100);
51
+ }
52
+
53
+ generateCompactDigest(rulesSources, rulesContent) {
54
+ if (!rulesContent || typeof rulesContent !== 'string') {
55
+ return {
56
+ never_do: [],
57
+ must_do: [],
58
+ sources: rulesSources || [],
59
+ digest_sha256: null,
60
+ generated_at: new Date().toISOString()
61
+ };
62
+ }
63
+
64
+ const lines = rulesContent.split('\n');
65
+ const neverRules = [];
66
+ const mustRules = [];
67
+
68
+ for (const line of lines) {
69
+ const trimmed = line.trim();
70
+ if (trimmed.startsWith('❌') || /nunca|never|prohibido|forbidden/i.test(trimmed.toLowerCase())) {
71
+ if (trimmed.startsWith('❌') || trimmed.toLowerCase().includes('nunca') || trimmed.toLowerCase().includes('never')) {
72
+ neverRules.push(trimmed.substring(0, 150));
73
+ }
74
+ } else if (trimmed.startsWith('✅')) {
75
+ mustRules.push(trimmed.substring(0, 150));
76
+ }
77
+ }
78
+
79
+ const digest = {
80
+ never_do: neverRules.slice(0, 20),
81
+ must_do: mustRules.slice(0, 20),
82
+ sources: rulesSources || [],
83
+ generated_at: new Date().toISOString()
84
+ };
85
+
86
+ const digestContent = JSON.stringify(digest);
87
+ const sha256 = crypto.createHash('sha256').update(digestContent, 'utf8').digest('hex');
88
+ digest.digest_sha256 = sha256;
89
+
90
+ return digest;
91
+ }
92
+ }
93
+
94
+ module.exports = RulesDigestService;
@@ -0,0 +1,60 @@
1
+ const PolicyBundleService = require('../PolicyBundleService');
2
+
3
+ describe('PolicyBundleService', () => {
4
+ function makeSUT() {
5
+ return new PolicyBundleService();
6
+ }
7
+
8
+ it('creates a bundle with mandatory flag and persists to evidence structure', () => {
9
+ const sut = makeSUT();
10
+
11
+ const allPlatforms = ['backend', 'frontend', 'ios', 'android'];
12
+ const bundle = sut.createBundle({
13
+ platforms: allPlatforms,
14
+ mandatory: true,
15
+ enforcedAt: 'pre-commit'
16
+ });
17
+
18
+ expect(bundle).toMatchObject({
19
+ mandatory: true,
20
+ enforcedAt: 'pre-commit',
21
+ createdAt: expect.any(String),
22
+ policy_bundle_id: expect.any(String),
23
+ expiresAt: expect.any(String),
24
+ ttl_minutes: 10
25
+ });
26
+
27
+ expect(bundle.platforms).toEqual(expect.arrayContaining(allPlatforms));
28
+ expect(bundle.platforms.length).toBe(4);
29
+ expect(new Date(bundle.createdAt).getTime()).toBeGreaterThan(0);
30
+ expect(new Date(bundle.expiresAt).getTime()).toBeGreaterThan(Date.now());
31
+ });
32
+
33
+ it('validates that bundle is mandatory before destructive actions', () => {
34
+ const sut = makeSUT();
35
+
36
+ const bundle = sut.createBundle({
37
+ platforms: ['ios'],
38
+ mandatory: true,
39
+ enforcedAt: 'pre-commit'
40
+ });
41
+
42
+ const isValid = sut.validateMandatory(bundle);
43
+
44
+ expect(isValid).toBe(true);
45
+ });
46
+
47
+ it('rejects bundle if mandatory is false', () => {
48
+ const sut = makeSUT();
49
+
50
+ const bundle = sut.createBundle({
51
+ platforms: ['ios'],
52
+ mandatory: false,
53
+ enforcedAt: 'pre-commit'
54
+ });
55
+
56
+ const isValid = sut.validateMandatory(bundle);
57
+
58
+ expect(isValid).toBe(false);
59
+ });
60
+ });
@@ -0,0 +1,83 @@
1
+ const crypto = require('crypto');
2
+
3
+ const RulesDigestService = require('../RulesDigestService');
4
+
5
+ describe('RulesDigestService', () => {
6
+ function makeSUT() {
7
+ return new RulesDigestService();
8
+ }
9
+
10
+ it('builds a digest entry with sha256 and linesRead and a non-empty summary', () => {
11
+ const content = ['---', 'title: Rules', '---', '', '# Gold Rules', 'Rule A'].join('\n');
12
+ const expectedSha256 = crypto.createHash('sha256').update(content, 'utf8').digest('hex');
13
+
14
+ const sut = makeSUT();
15
+
16
+ const entry = sut.buildEntry({
17
+ file: 'rulesgold.mdc',
18
+ content,
19
+ path: '/tmp/rulesgold.mdc'
20
+ });
21
+
22
+ expect(entry).toMatchObject({
23
+ file: 'rulesgold.mdc',
24
+ path: '/tmp/rulesgold.mdc',
25
+ verified: true,
26
+ sha256: expectedSha256,
27
+ linesRead: content.split('\n').length
28
+ });
29
+
30
+ expect(typeof entry.summary).toBe('string');
31
+ expect(entry.summary.trim().length).toBeGreaterThan(0);
32
+ expect(entry.summary.trim()).not.toBe('---');
33
+ });
34
+
35
+ it('returns not found summary and marks entry as not verified when content is empty', () => {
36
+ const sut = makeSUT();
37
+
38
+ const entry = sut.buildEntry({
39
+ file: 'rulesgold.mdc',
40
+ content: '',
41
+ path: null
42
+ });
43
+
44
+ expect(entry.verified).toBe(false);
45
+ expect(entry.summary).toBe('not found');
46
+ });
47
+
48
+ it('generates compact digest with never_do and must_do lists from rules sources', () => {
49
+ const rulesContent = [
50
+ '# Gold Rules',
51
+ '❌ NUNCA usar Singleton - usar Inyección de Dependencias',
52
+ '❌ NUNCA dejar catch vacíos - siempre loggear o propagar',
53
+ '✅ OBLIGATORIO seguir flujo BDD → TDD',
54
+ '✅ OBLIGATORIO verificar SOLID (SRP, OCP, LSP, ISP, DIP)',
55
+ 'Some other text that is not a rule'
56
+ ].join('\n');
57
+
58
+ const rulesSources = [
59
+ {
60
+ file: 'rulesgold.mdc',
61
+ sha256: crypto.createHash('sha256').update(rulesContent, 'utf8').digest('hex'),
62
+ path: '/tmp/rulesgold.mdc'
63
+ }
64
+ ];
65
+
66
+ const sut = makeSUT();
67
+ const digest = sut.generateCompactDigest(rulesSources, rulesContent);
68
+
69
+ expect(digest).toBeDefined();
70
+ expect(digest.never_do).toBeDefined();
71
+ expect(Array.isArray(digest.never_do)).toBe(true);
72
+ expect(digest.never_do.length).toBeGreaterThan(0);
73
+ expect(digest.must_do).toBeDefined();
74
+ expect(Array.isArray(digest.must_do)).toBe(true);
75
+ expect(digest.must_do.length).toBeGreaterThan(0);
76
+ expect(digest.digest_sha256).toBeDefined();
77
+ expect(typeof digest.digest_sha256).toBe('string');
78
+ expect(digest.digest_sha256.length).toBe(64);
79
+
80
+ const digestSize = JSON.stringify(digest).length;
81
+ expect(digestSize).toBeLessThanOrEqual(4096);
82
+ });
83
+ });
@@ -5,7 +5,7 @@
5
5
  "platforms": [
6
6
  "backend"
7
7
  ],
8
- "created": "2026-01-06T11:37:47.558Z"
8
+ "created": "2026-01-12T08:54:59.529Z"
9
9
  },
10
10
  "architecture": {
11
11
  "pattern": "FEATURE_FIRST_CLEAN_DDD",
@@ -176,5 +176,17 @@ describe('network-resilience-analyzer', () => {
176
176
 
177
177
  expect(findings.filter(f => f.ruleId === 'common.network.missing_connection_pooling')).toHaveLength(0);
178
178
  });
179
+
180
+ it('should ignore non-js sources', () => {
181
+ const project = createMockProject([{
182
+ path: '/app/services/KeychainHelper.swift',
183
+ content: 'struct KeychainHelper { }'
184
+ }]);
185
+ const findings = [];
186
+
187
+ analyzeNetworkResilience(project, findings);
188
+
189
+ expect(findings.filter(f => f.ruleId === 'common.network.missing_connection_pooling')).toHaveLength(0);
190
+ });
179
191
  });
180
192
  });
@@ -149,7 +149,8 @@ function checkConnectionPooling(sf, content, findings) {
149
149
  const filePath = sf.getFilePath();
150
150
  const isAnalyzer = /infrastructure\/ast\/|analyzers\/|detectors\/|scanner|analyzer|detector/i.test(filePath);
151
151
  const isTestFile = /\.(spec|test)\.(js|ts)$/i.test(filePath);
152
- if (isAnalyzer || isTestFile) {
152
+ const isSupportedSource = /\.(jsx?|tsx?)$/i.test(filePath);
153
+ if (isAnalyzer || isTestFile || !isSupportedSource) {
153
154
  return;
154
155
  }
155
156
 
@@ -31,4 +31,18 @@ describe('ios.imports.unused', () => {
31
31
 
32
32
  expect(analyzer.findings.find(f => f.ruleId === 'ios.imports.unused')).toBeUndefined();
33
33
  });
34
+
35
+ it('does not report unused Foundation when Foundation types are used in file content', () => {
36
+ const filePath = '/tmp/Bar.swift';
37
+ const analyzer = makeAnalyzer({
38
+ fileContent: 'import Foundation\n\nstruct Bar { let createdAt: Date }',
39
+ allNodes: []
40
+ });
41
+
42
+ analyzer.imports = [{ name: 'Foundation', line: 1 }];
43
+
44
+ analyzeImportsAST(analyzer, filePath);
45
+
46
+ expect(analyzer.findings.find(f => f.ruleId === 'ios.imports.unused')).toBeUndefined();
47
+ });
34
48
  });
@@ -87,6 +87,7 @@ function analyzeImportsAST(analyzer, filePath) {
87
87
  if (filePath.includes('Tests')) {
88
88
  return;
89
89
  }
90
+ const content = analyzer.fileContent || '';
90
91
  const importNames = analyzer.imports.map((i) => i.name);
91
92
 
92
93
  const hasUIKit = importNames.includes('UIKit');
@@ -117,14 +118,18 @@ function analyzeImportsAST(analyzer, filePath) {
117
118
 
118
119
  const unusedImportAllowlist = new Set(['Foundation', 'SwiftUI', 'UIKit', 'Combine']);
119
120
 
121
+ const foundationTypeUsage = /\b(Data|Date|URL|UUID|Decimal|NSNumber|NSDecimalNumber|NSSet|NSDictionary|NSArray|IndexPath|Notification|FileManager|Bundle|Locale|TimeZone|Calendar|DateComponents|URLRequest|URLSession)\b/;
120
122
  for (const imp of analyzer.imports) {
121
123
  if (!unusedImportAllowlist.has(imp.name)) continue;
122
124
 
123
- const isUsed = analyzer.allNodes.some((n) => {
125
+ let isUsed = analyzer.allNodes.some((n) => {
124
126
  const typename = n['key.typename'] || '';
125
127
  const name = n['key.name'] || '';
126
128
  return typename.includes(imp.name) || name.includes(imp.name);
127
129
  });
130
+ if (!isUsed && imp.name === 'Foundation') {
131
+ isUsed = foundationTypeUsage.test(content);
132
+ }
128
133
 
129
134
  if (!isUsed) {
130
135
  analyzer.pushFinding('ios.imports.unused', 'low', filePath, imp.line, `Unused import: ${imp.name}`);
@@ -1,4 +1,7 @@
1
1
  const { runTextScanner } = require('../text-scanner');
2
+ const fs = require('fs');
3
+ const os = require('os');
4
+ const path = require('path');
2
5
 
3
6
  describe('Text Scanner Module', () => {
4
7
  describe('runTextScanner', () => {
@@ -17,4 +20,50 @@ describe('Text Scanner Module', () => {
17
20
  expect(mod.runTextScanner).toBeDefined();
18
21
  });
19
22
  });
23
+
24
+ describe('ios rules', () => {
25
+ it('does not flag missing DI when Package.swift has empty dependencies', () => {
26
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ast-text-'));
27
+ const packagePath = path.join(root, 'Package.swift');
28
+ const content = [
29
+ '// swift-tools-version: 5.9',
30
+ 'import PackageDescription',
31
+ '',
32
+ 'let package = Package(',
33
+ ' name: "Demo",',
34
+ ' dependencies: [],',
35
+ ' targets: [',
36
+ ' .target(name: "Demo")',
37
+ ' ]',
38
+ ')'
39
+ ].join('\n');
40
+
41
+ fs.writeFileSync(packagePath, content, 'utf8');
42
+
43
+ const findings = [];
44
+ runTextScanner(root, findings);
45
+
46
+ const diFinding = findings.find(f => f.ruleId === 'ios.spm.dependency_injection');
47
+ expect(diFinding).toBeUndefined();
48
+ });
49
+
50
+ it('does not flag Any usage when Security context is present', () => {
51
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ast-text-'));
52
+ const filePath = path.join(root, 'KeychainHelper.swift');
53
+ const content = [
54
+ 'import Security',
55
+ 'struct KeychainHelper {',
56
+ ' func baseQuery() -> [String: Any] { [:] }',
57
+ '}'
58
+ ].join('\n');
59
+
60
+ fs.writeFileSync(filePath, content, 'utf8');
61
+
62
+ const findings = [];
63
+ runTextScanner(root, findings);
64
+
65
+ const typeSafety = findings.find(f => f.ruleId === 'ios.optionals.type_safety');
66
+ expect(typeSafety).toBeUndefined();
67
+ });
68
+ });
20
69
  });
@@ -561,7 +561,8 @@ function runTextScanner(root, findings) {
561
561
  if (targetCount < 2) {
562
562
  pushFileFinding('ios.spm.modular_architecture', 'low', file, 1, 1, 'Package.swift without multiple targets (weak modular architecture)', findings);
563
563
  }
564
- if (!/Swinject|Needle|Resolver|Factory\b/.test(content)) {
564
+ const hasEmptyDependencies = /dependencies\s*:\s*\[\s*\]/.test(content);
565
+ if (!hasEmptyDependencies && !/Swinject|Needle|Resolver|Factory\b/.test(content)) {
565
566
  pushFileFinding('ios.spm.dependency_injection', 'low', file, 1, 1, 'No DI library hint found in Package.swift', findings);
566
567
  }
567
568
  }
@@ -601,7 +602,10 @@ function runTextScanner(root, findings) {
601
602
  pushFileFinding('ios.optionals.optional_binding', 'medium', file, 1, 1, 'Optional check != nil (prefer optional binding)', findings);
602
603
  }
603
604
  if (/:\s*Any\b|\bas\s*Any\b/.test(content)) {
604
- pushFileFinding('ios.optionals.type_safety', 'medium', file, 1, 1, 'Usage of Any reduces type safety', findings);
605
+ const hasSecurityContext = /import\s+Security\b/.test(content) || /\bkSec[A-Za-z0-9_]+\b/.test(content);
606
+ if (!hasSecurityContext) {
607
+ pushFileFinding('ios.optionals.type_safety', 'medium', file, 1, 1, 'Usage of Any reduces type safety', findings);
608
+ }
605
609
  }
606
610
  if (/\.sink\s*\(/.test(content) && !/receiveCompletion\s*:\s*/.test(content)) {
607
611
  pushFileFinding('ios.combine.error_handling', 'medium', file, 1, 1, 'Combine sink without receiveCompletion handler', findings);
@@ -1,8 +1,8 @@
1
1
  const { preFlightCheck } = require('../ast-intelligence-automation');
2
2
 
3
3
  describe('pre_flight_check - tests are first-class', () => {
4
- it('blocks test edits when AST analysis finds CRITICAL violations', () => {
5
- const result = preFlightCheck({
4
+ it('blocks test edits when AST analysis finds CRITICAL violations', async () => {
5
+ const result = await preFlightCheck({
6
6
  action_type: 'edit',
7
7
  target_file: '/SomeModule/Tests/FooTests.swift',
8
8
  proposed_code: 'import XCTest\nfinal class FooTests: XCTestCase { func testX() { do { } catch { } } }'
@@ -1208,6 +1208,50 @@ async function aiGateCheck() {
1208
1208
  const warnings = [];
1209
1209
  const autoFixes = [];
1210
1210
 
1211
+ let policyBundle = null;
1212
+ try {
1213
+ if (fs.existsSync(EVIDENCE_FILE)) {
1214
+ const evidence = JSON.parse(fs.readFileSync(EVIDENCE_FILE, 'utf8'));
1215
+ policyBundle = evidence.policy_bundle || null;
1216
+ }
1217
+ } catch (error) {
1218
+ if (process.env.DEBUG) {
1219
+ process.stderr.write(`[MCP] Failed to read policy_bundle: ${error.message}\n`);
1220
+ }
1221
+ }
1222
+
1223
+ const PolicyBundleService = require('../../application/services/PolicyBundleService');
1224
+ const policyBundleService = new PolicyBundleService();
1225
+
1226
+ if (!policyBundle || !policyBundleService.isValid(policyBundle)) {
1227
+ if (!allowEvidenceAutofix) {
1228
+ violations.push('❌ POLICY_BUNDLE_INVALID: No valid policy bundle. Run evidence:full-update to refresh.');
1229
+ } else {
1230
+ const elapsed = Date.now() - startedAt;
1231
+ const remaining = Math.max(150, gateTimeoutMs - elapsed);
1232
+ const evidenceMonitor = getCompositionRoot().getEvidenceMonitor();
1233
+ const refreshResult = await runWithTimeout(() => evidenceMonitor.refresh(), remaining);
1234
+ if (refreshResult.ok) {
1235
+ autoFixes.push('✅ Policy bundle was invalid/expired - AUTO-FIXED by refreshing evidence');
1236
+ try {
1237
+ if (fs.existsSync(EVIDENCE_FILE)) {
1238
+ const updatedEvidence = JSON.parse(fs.readFileSync(EVIDENCE_FILE, 'utf8'));
1239
+ policyBundle = updatedEvidence.policy_bundle || null;
1240
+ }
1241
+ } catch (error) {
1242
+ if (process.env.DEBUG) {
1243
+ process.stderr.write(`[MCP] Failed to re-read policy_bundle after refresh: ${error.message}\n`);
1244
+ }
1245
+ }
1246
+ } else if (refreshResult.timedOut) {
1247
+ violations.push('❌ POLICY_BUNDLE_INVALID: Auto-fix timed out. Run evidence:full-update to refresh.');
1248
+ } else {
1249
+ const msg = refreshResult.error && refreshResult.error.message ? refreshResult.error.message : String(refreshResult.error);
1250
+ violations.push(`❌ POLICY_BUNDLE_INVALID: Auto-fix failed: ${msg}`);
1251
+ }
1252
+ }
1253
+ }
1254
+
1211
1255
  const evidenceMonitor = getCompositionRoot().getEvidenceMonitor();
1212
1256
  if (evidenceMonitor.isStale()) {
1213
1257
  if (!allowEvidenceAutofix) {
@@ -1714,7 +1758,7 @@ function suggestHumanIntent() {
1714
1758
  * 2. Proposed code doesn't violate critical rules (using AST Intelligence!)
1715
1759
  * 3. Code patterns are compliant with platform rules
1716
1760
  */
1717
- function preFlightCheck(params) {
1761
+ async function preFlightCheck(params) {
1718
1762
  const { action_type, target_file, proposed_code, bypass_tdd } = params;
1719
1763
  const tokenEconomyRule = 'TOKEN_ECONOMY: Prioritize token/cost efficiency. Batch related checks, avoid redundant scans, reuse cached context where possible, ask the user for missing info instead of exploring blindly, and keep responses concise.';
1720
1764
 
@@ -1727,6 +1771,94 @@ function preFlightCheck(params) {
1727
1771
  };
1728
1772
  }
1729
1773
 
1774
+ let policyBundle = null;
1775
+ try {
1776
+ if (fs.existsSync(EVIDENCE_FILE)) {
1777
+ const evidence = JSON.parse(fs.readFileSync(EVIDENCE_FILE, 'utf8'));
1778
+ policyBundle = evidence.policy_bundle || null;
1779
+ }
1780
+ } catch (error) {
1781
+ if (process.env.DEBUG) {
1782
+ process.stderr.write(`[MCP] Failed to read policy_bundle from evidence: ${error.message}\n`);
1783
+ }
1784
+ }
1785
+
1786
+ const PolicyBundleService = require('../../application/services/PolicyBundleService');
1787
+ const policyBundleService = new PolicyBundleService();
1788
+
1789
+ let rulesDigest = null;
1790
+ try {
1791
+ if (fs.existsSync(EVIDENCE_FILE)) {
1792
+ const evidence = JSON.parse(fs.readFileSync(EVIDENCE_FILE, 'utf8'));
1793
+ rulesDigest = evidence.rules_digest || null;
1794
+ }
1795
+ } catch (error) {
1796
+ if (process.env.DEBUG) {
1797
+ process.stderr.write(`[MCP] Failed to read rules_digest from evidence: ${error.message}\n`);
1798
+ }
1799
+ }
1800
+
1801
+ const isRulesDigestValid = (digest) => {
1802
+ if (!digest || !digest.expires_at) return false;
1803
+ const expiresAt = new Date(digest.expires_at);
1804
+ return Date.now() < expiresAt.getTime();
1805
+ };
1806
+
1807
+ if (!policyBundle || !policyBundleService.isValid(policyBundle) || !isRulesDigestValid(rulesDigest)) {
1808
+ const evidenceMonitor = getCompositionRoot().getEvidenceMonitor();
1809
+ try {
1810
+ const reason = !policyBundle || !policyBundleService.isValid(policyBundle)
1811
+ ? 'Policy bundle invalid/expired'
1812
+ : 'Rules digest missing/expired';
1813
+ process.stderr.write(`[MCP] ${reason} - triggering auto-refresh...\n`);
1814
+ await evidenceMonitor.refresh();
1815
+
1816
+ if (fs.existsSync(EVIDENCE_FILE)) {
1817
+ const updatedEvidence = JSON.parse(fs.readFileSync(EVIDENCE_FILE, 'utf8'));
1818
+ policyBundle = updatedEvidence.policy_bundle || null;
1819
+ rulesDigest = updatedEvidence.rules_digest || null;
1820
+
1821
+ if (policyBundle && policyBundleService.isValid(policyBundle) && isRulesDigestValid(rulesDigest)) {
1822
+ process.stderr.write('[MCP] Policy bundle and rules digest refreshed successfully - proceeding\n');
1823
+ } else if (!policyBundle || !policyBundleService.isValid(policyBundle)) {
1824
+ return {
1825
+ success: false,
1826
+ allowed: false,
1827
+ blocked: true,
1828
+ framework_rules: [tokenEconomyRule],
1829
+ reason: '🚫 POLICY_BUNDLE_STILL_INVALID: Auto-refresh completed but policy bundle still invalid',
1830
+ action_required: 'CHECK_EVIDENCE',
1831
+ suggestion: 'Evidence was refreshed but policy bundle is still invalid. Check .AI_EVIDENCE.json',
1832
+ policy_bundle_status: 'INVALID_AFTER_REFRESH'
1833
+ };
1834
+ } else {
1835
+ return {
1836
+ success: false,
1837
+ allowed: false,
1838
+ blocked: true,
1839
+ framework_rules: [tokenEconomyRule],
1840
+ reason: '🚫 RULES_DIGEST_STILL_INVALID: Auto-refresh completed but rules digest still invalid/expired',
1841
+ action_required: 'CHECK_EVIDENCE',
1842
+ suggestion: 'Evidence was refreshed but rules digest is still invalid. Check .AI_EVIDENCE.json',
1843
+ rules_digest_status: 'INVALID_AFTER_REFRESH'
1844
+ };
1845
+ }
1846
+ }
1847
+ } catch (error) {
1848
+ return {
1849
+ success: false,
1850
+ allowed: false,
1851
+ blocked: true,
1852
+ framework_rules: [tokenEconomyRule],
1853
+ reason: '🚫 AUTO_REFRESH_FAILED: Failed to auto-refresh evidence',
1854
+ action_required: 'MANUAL_REFRESH',
1855
+ suggestion: `Auto-refresh failed: ${error.message}. Run evidence:full-update manually`,
1856
+ policy_bundle_status: 'AUTO_REFRESH_FAILED',
1857
+ error: error.message
1858
+ };
1859
+ }
1860
+ }
1861
+
1730
1862
  const isTestFile = /\.(spec|test)\.(js|ts|swift|kt)$/.test(target_file);
1731
1863
 
1732
1864
  if (isTestFile) {
@@ -1810,7 +1942,8 @@ function preFlightCheck(params) {
1810
1942
  ast_analysis: astAnalysis,
1811
1943
  tdd_status: validation.tddStatus,
1812
1944
  phase: 'GREEN',
1813
- instruction: 'Implement the minimum code to make the test pass'
1945
+ instruction: 'Implement the minimum code to make the test pass',
1946
+ policy_bundle_id: policyBundle.policy_bundle_id
1814
1947
  };
1815
1948
  }
1816
1949
 
@@ -59,6 +59,25 @@ describe('intelligent-audit', () => {
59
59
  expect(mod.isViolationInStagedFiles('some/dir/.audit_tmp/AppCoordinator.123.staged.swift', stagedSet)).toBe(false);
60
60
  expect(mod.isViolationInStagedFiles('apps/ios/Application/AppCoordinator', stagedSet)).toBe(false);
61
61
  });
62
+
63
+ it('should refresh root timestamp when updating .AI_EVIDENCE.json', async () => {
64
+ const evidencePath = path.join(process.cwd(), '.AI_EVIDENCE.json');
65
+ const previous = {
66
+ timestamp: '2026-01-12T08:47:48.064+01:00',
67
+ severity_metrics: { last_updated: '2026-01-12T08:47:48.064+01:00', total_violations: 0, by_severity: { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 } },
68
+ ai_gate: { status: 'ALLOWED', scope: 'staging', last_check: '2026-01-12T08:47:48.064+01:00', violations: [], instruction: 'x', mandatory: true }
69
+ };
70
+ fs.writeFileSync(evidencePath, JSON.stringify(previous, null, 2));
71
+
72
+ const mod = require('../intelligent-audit');
73
+ expect(typeof mod.updateAIEvidence).toBe('function');
74
+
75
+ await mod.updateAIEvidence([], { passed: true, blockedBy: null }, { estimated: 1, percentUsed: 0, remaining: 1 });
76
+
77
+ const updated = JSON.parse(fs.readFileSync(evidencePath, 'utf8'));
78
+ expect(updated.timestamp).toBeDefined();
79
+ expect(updated.timestamp).not.toBe(previous.timestamp);
80
+ });
62
81
  });
63
82
 
64
83
  describe('AI_EVIDENCE.json structure validation', () => {
@@ -10,6 +10,8 @@ const { toErrorMessage } = require('../utils/error-utils');
10
10
  const fs = require('fs');
11
11
  const path = require('path');
12
12
  const DynamicRulesLoader = require('../../application/services/DynamicRulesLoader');
13
+ const RulesDigestService = require('../../application/services/RulesDigestService');
14
+ const PolicyBundleService = require('../../application/services/PolicyBundleService');
13
15
 
14
16
  function deriveCategoryFromRuleId(ruleId) {
15
17
  if (!ruleId || typeof ruleId !== 'string') return 'unknown';
@@ -307,6 +309,7 @@ async function writeAutoContextFiles(platformsEvidence) {
307
309
 
308
310
  async function buildRulesReadEvidence(platformsEvidence) {
309
311
  const loader = new DynamicRulesLoader();
312
+ const digestService = new RulesDigestService();
310
313
  const entries = [];
311
314
 
312
315
  const detectedPlatforms = ['backend', 'frontend', 'ios', 'android']
@@ -315,26 +318,24 @@ async function buildRulesReadEvidence(platformsEvidence) {
315
318
  const uniqueFiles = ['rulesgold.mdc', ...detectedPlatforms.map(p => loader.rulesMap[p]).filter(Boolean)];
316
319
 
317
320
  for (const file of uniqueFiles) {
318
- let verified = false;
319
- let summary = 'not found';
321
+ let content = null;
320
322
  let resolvedPath = null;
321
323
 
322
324
  try {
323
- const content = await loader.loadRule(file);
324
- verified = Boolean(content);
325
- summary = summarizeRulesContent(content);
325
+ content = await loader.loadRule(file);
326
326
  const cached = loader.cache && loader.cache.rules ? loader.cache.rules.get(file) : null;
327
327
  resolvedPath = cached && cached.fullPath ? cached.fullPath : null;
328
328
  } catch (error) {
329
- summary = `error: ${toErrorMessage(error)}`;
329
+ content = null;
330
330
  }
331
331
 
332
- entries.push({
332
+ const entry = digestService.buildEntry({
333
333
  file,
334
- verified,
335
- summary,
334
+ content,
336
335
  path: resolvedPath
337
336
  });
337
+
338
+ entries.push(entry);
338
339
  }
339
340
 
340
341
  return {
@@ -622,6 +623,8 @@ async function updateAIEvidence(violations, gateResult, tokenUsage) {
622
623
  try {
623
624
  const evidence = JSON.parse(fs.readFileSync(evidencePath, 'utf8'));
624
625
 
626
+ evidence.timestamp = formatLocalTimestamp();
627
+
625
628
  evidence.severity_metrics = {
626
629
  last_updated: formatLocalTimestamp(),
627
630
  total_violations: violations.length,
@@ -739,6 +742,19 @@ async function updateAIEvidence(violations, gateResult, tokenUsage) {
739
742
  evidence.rules_read = rulesRead.entries;
740
743
  evidence.rules_read_flags = rulesRead.legacyFlags;
741
744
 
745
+ const policyBundleService = new PolicyBundleService();
746
+ const detectedPlatforms = Object.keys(platformsEvidence).filter(p => platformsEvidence[p] && platformsEvidence[p].detected);
747
+ const rulesSources = rulesRead.entries
748
+ .filter(e => e.verified && e.sha256)
749
+ .map(e => ({ file: e.file, sha256: e.sha256, path: e.path, content: e.content || '' }));
750
+
751
+ evidence.policy_bundle = policyBundleService.createBundle({
752
+ platforms: detectedPlatforms,
753
+ mandatory: true,
754
+ enforcedAt: 'pre-commit',
755
+ rulesSources
756
+ });
757
+
742
758
  evidence.current_context = {
743
759
  working_on: env.get('AUTO_EVIDENCE_SUMMARY', 'AST Intelligence Analysis'),
744
760
  last_files_edited: [],
@@ -822,6 +838,19 @@ async function updateAIEvidence(violations, gateResult, tokenUsage) {
822
838
  evidence.semantic_snapshot = generateSemanticSnapshot(evidence, violations, gateResult);
823
839
 
824
840
  evidence.auto_intent = generateAutoIntent(evidence, violations, gateResult, stagedFiles);
841
+
842
+ const rulesDigestService = new RulesDigestService();
843
+ const allRulesContent = rulesSources.map(src => src.content || '').join('\n');
844
+ const compactDigest = rulesDigestService.generateCompactDigest(rulesSources, allRulesContent);
845
+ const ttlMinutes = 10;
846
+ const expiresAt = new Date(Date.now() + ttlMinutes * 60 * 1000).toISOString();
847
+
848
+ evidence.rules_digest = {
849
+ ...compactDigest,
850
+ ttl_minutes: ttlMinutes,
851
+ expires_at: expiresAt
852
+ };
853
+
825
854
  fs.writeFileSync(evidencePath, JSON.stringify(evidence, null, 2));
826
855
  console.log('[Intelligent Audit] ✅ .AI_EVIDENCE.json updated with complete format (ai_gate, severity_metrics, token_usage, git_flow, watchers, human_intent, semantic_snapshot)');
827
856
 
@@ -847,4 +876,4 @@ if (require.main === module) {
847
876
  });
848
877
  }
849
878
 
850
- module.exports = { runIntelligentAudit, isViolationInStagedFiles, toRepoRelativePath };
879
+ module.exports = { runIntelligentAudit, isViolationInStagedFiles, toRepoRelativePath, updateAIEvidence, formatLocalTimestamp };
@@ -34,3 +34,18 @@
34
34
  {"timestamp":"2026-01-11T21:45:00.904Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
35
35
  {"timestamp":"2026-01-11T21:45:00.906Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
36
36
  {"timestamp":"2026-01-11T21:45:00.907Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
37
+ {"timestamp":"2026-01-12T08:28:15.484Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
38
+ {"timestamp":"2026-01-12T08:28:15.486Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
39
+ {"timestamp":"2026-01-12T08:28:15.486Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
40
+ {"timestamp":"2026-01-12T08:29:40.908Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
41
+ {"timestamp":"2026-01-12T08:29:40.910Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
42
+ {"timestamp":"2026-01-12T08:29:40.910Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
43
+ {"timestamp":"2026-01-12T08:44:05.598Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
44
+ {"timestamp":"2026-01-12T08:44:05.599Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
45
+ {"timestamp":"2026-01-12T08:44:05.600Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
46
+ {"timestamp":"2026-01-12T18:40:42.667Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
47
+ {"timestamp":"2026-01-12T18:40:42.668Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
48
+ {"timestamp":"2026-01-12T18:40:42.669Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}
49
+ {"timestamp":"2026-01-12T19:29:18.617Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"ok","percentUsed":10,"tokensUsed":100000,"maxTokens":1000000,"source":"realtime","stale":false},"context":{"message":"Result level=ok percent=10% used=100000/1000000 source=realtime"}}
50
+ {"timestamp":"2026-01-12T19:29:18.618Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"warning","percentUsed":91,"tokensUsed":910000,"maxTokens":1000000,"source":"fallback","stale":false},"context":{"message":"Result level=warning percent=91% used=910000/1000000 source=fallback"}}
51
+ {"timestamp":"2026-01-12T19:29:18.618Z","level":"info","component":"TokenMonitor","event":"TOKEN_MONITOR_RESULT","data":{"level":"critical","percentUsed":98,"tokensUsed":980000,"maxTokens":1000000,"source":"realtime","stale":true},"context":{"message":"Result level=critical percent=98% used=980000/1000000 source=realtime (stale)"}}