pumuki-ast-hooks 5.6.15 → 6.0.0
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.
- package/package.json +1 -1
- package/scripts/hooks-system/.audit-reports/auto-recovery.log +3 -0
- package/scripts/hooks-system/.audit-reports/install-wizard.log +16 -0
- package/scripts/hooks-system/.audit_tmp/hook-metrics.jsonl +72 -0
- package/scripts/hooks-system/application/services/PolicyBundleService.js +54 -0
- package/scripts/hooks-system/application/services/RulesDigestService.js +94 -0
- package/scripts/hooks-system/application/services/__tests__/PolicyBundleService.spec.js +60 -0
- package/scripts/hooks-system/application/services/__tests__/RulesDigestService.spec.js +83 -0
- package/scripts/hooks-system/infrastructure/ast/common/ast-common.js +2 -2
- package/scripts/hooks-system/infrastructure/mcp/__tests__/preflight-check-blocks-tests.spec.js +2 -2
- package/scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js +135 -2
- package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js +19 -0
- package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +39 -10
- package/scripts/hooks-system/infrastructure/watchdog/__tests__/.audit-reports/token-monitor.log +12 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki-ast-hooks",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
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": {
|
|
@@ -11,3 +11,6 @@
|
|
|
11
11
|
{"timestamp":"2026-01-11T19:08:34.232Z","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":{}}
|
|
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
|
+
{"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":{}}
|
|
@@ -50,3 +50,19 @@
|
|
|
50
50
|
{"timestamp":"2026-01-11T21:27:16.569Z","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":{}}
|
|
51
51
|
{"timestamp":"2026-01-11T21:27:16.571Z","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":{}}
|
|
52
52
|
{"timestamp":"2026-01-11T21:27:16.572Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_COMPLETED","data":{},"context":{}}
|
|
53
|
+
{"timestamp":"2026-01-11T21:44:57.269Z","level":"info","component":"InstallWizard","event":"INSTALL_WIZARD_START","data":{"repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"},"context":{}}
|
|
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
|
+
{"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
|
+
{"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":{}}
|
|
@@ -554,3 +554,75 @@
|
|
|
554
554
|
{"timestamp":1768166836722,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
|
|
555
555
|
{"timestamp":1768166836722,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
|
|
556
556
|
{"timestamp":1768166836722,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
557
|
+
{"timestamp":1768167897013,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
558
|
+
{"timestamp":1768167897013,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
|
|
559
|
+
{"timestamp":1768167897013,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
|
|
560
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
561
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
562
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
|
|
563
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
|
|
564
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
565
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
566
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
|
|
567
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
|
|
568
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
569
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
570
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
|
|
571
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
|
|
572
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
573
|
+
{"timestamp":1768167897014,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
574
|
+
{"timestamp":1768167897015,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
|
|
575
|
+
{"timestamp":1768167897015,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
|
|
576
|
+
{"timestamp":1768167897015,"hook":"audit_logger","operation":"constructor","status":"success","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
577
|
+
{"timestamp":1768167897015,"hook":"audit_logger","operation":"constructor","status":"started","repoRoot":"/Users/juancarlosmerlosalbarracin/Developer/Projects/ast-intelligence-hooks/scripts/hooks-system"}
|
|
578
|
+
{"timestamp":1768167897015,"hook":"audit_logger","operation":"ensure_dir","status":"started"}
|
|
579
|
+
{"timestamp":1768167897015,"hook":"audit_logger","operation":"ensure_dir","status":"success"}
|
|
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"}
|
|
@@ -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
|
+
});
|
|
@@ -296,7 +296,7 @@ function runCommonIntelligence(project, findings) {
|
|
|
296
296
|
'high',
|
|
297
297
|
sf,
|
|
298
298
|
sf,
|
|
299
|
-
'Test file without makeSUT factory - use makeSUT pattern for consistent DI and teardown',
|
|
299
|
+
'Test file without makeSUT factory - use makeSUT pattern for consistent DI and teardown. Swift template:\n\nprivate func makeSUT() -> SUT {\n let sut = SUT(/* deps */)\n trackForMemoryLeaks(sut, testCase: self, file: #file, line: #line)\n return sut\n}\n\nfunc test_example() {\n let sut = makeSUT()\n // assertions\n}',
|
|
300
300
|
findings
|
|
301
301
|
);
|
|
302
302
|
}
|
|
@@ -309,7 +309,7 @@ function runCommonIntelligence(project, findings) {
|
|
|
309
309
|
'critical',
|
|
310
310
|
sf,
|
|
311
311
|
sf,
|
|
312
|
-
'Test file without trackForMemoryLeaks - add memory leak tracking to prevent leaks and cross-test interference',
|
|
312
|
+
'Test file without trackForMemoryLeaks - add memory leak tracking to prevent leaks and cross-test interference. Swift template (helper must exist):\n\nprivate func makeSUT() -> SUT {\n let sut = SUT(/* deps */)\n trackForMemoryLeaks(sut, testCase: self, file: #file, line: #line)\n return sut\n}\n\nfunc test_example() {\n let sut = makeSUT()\n // assertions\n}\n\nAlternative (XCTest native):\naddTeardownBlock { [weak sut] in XCTAssertNil(sut) }',
|
|
313
313
|
findings
|
|
314
314
|
);
|
|
315
315
|
}
|
package/scripts/hooks-system/infrastructure/mcp/__tests__/preflight-check-blocks-tests.spec.js
CHANGED
|
@@ -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
|
|
package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js
CHANGED
|
@@ -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
|
|
319
|
-
let summary = 'not found';
|
|
321
|
+
let content = null;
|
|
320
322
|
let resolvedPath = null;
|
|
321
323
|
|
|
322
324
|
try {
|
|
323
|
-
|
|
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
|
-
|
|
329
|
+
content = null;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
|
|
332
|
+
const entry = digestService.buildEntry({
|
|
333
333
|
file,
|
|
334
|
-
|
|
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 };
|
package/scripts/hooks-system/infrastructure/watchdog/__tests__/.audit-reports/token-monitor.log
CHANGED
|
@@ -31,3 +31,15 @@
|
|
|
31
31
|
{"timestamp":"2026-01-11T21:27:20.297Z","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"}}
|
|
32
32
|
{"timestamp":"2026-01-11T21:27:20.301Z","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"}}
|
|
33
33
|
{"timestamp":"2026-01-11T21:27:20.301Z","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)"}}
|
|
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
|
+
{"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
|
+
{"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)"}}
|