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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pumuki-ast-hooks",
3
- "version": "5.6.15",
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
  }
@@ -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 };
@@ -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)"}}