scene-capability-engine 3.6.45 → 3.6.46
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/CHANGELOG.md +11 -0
- package/docs/releases/README.md +1 -0
- package/docs/releases/v3.6.46.md +23 -0
- package/docs/zh/releases/README.md +1 -0
- package/docs/zh/releases/v3.6.46.md +23 -0
- package/package.json +4 -2
- package/scripts/auto-strategy-router.js +231 -0
- package/scripts/capability-mapping-report.js +339 -0
- package/scripts/check-branding-consistency.js +140 -0
- package/scripts/check-sce-tracking.js +54 -0
- package/scripts/check-skip-allowlist.js +94 -0
- package/scripts/errorbook-registry-health-gate.js +172 -0
- package/scripts/errorbook-release-gate.js +132 -0
- package/scripts/failure-attribution-repair.js +317 -0
- package/scripts/git-managed-gate.js +464 -0
- package/scripts/interactive-approval-event-projection.js +400 -0
- package/scripts/interactive-approval-workflow.js +829 -0
- package/scripts/interactive-authorization-tier-evaluate.js +413 -0
- package/scripts/interactive-change-plan-gate.js +225 -0
- package/scripts/interactive-context-bridge.js +617 -0
- package/scripts/interactive-customization-loop.js +1690 -0
- package/scripts/interactive-dialogue-governance.js +842 -0
- package/scripts/interactive-feedback-log.js +253 -0
- package/scripts/interactive-flow-smoke.js +238 -0
- package/scripts/interactive-flow.js +1059 -0
- package/scripts/interactive-governance-report.js +1112 -0
- package/scripts/interactive-intent-build.js +707 -0
- package/scripts/interactive-loop-smoke.js +215 -0
- package/scripts/interactive-moqui-adapter.js +304 -0
- package/scripts/interactive-plan-build.js +426 -0
- package/scripts/interactive-runtime-policy-evaluate.js +495 -0
- package/scripts/interactive-work-order-build.js +552 -0
- package/scripts/matrix-regression-gate.js +167 -0
- package/scripts/moqui-core-regression-suite.js +397 -0
- package/scripts/moqui-lexicon-audit.js +651 -0
- package/scripts/moqui-matrix-remediation-phased-runner.js +865 -0
- package/scripts/moqui-matrix-remediation-queue.js +852 -0
- package/scripts/moqui-metadata-extract.js +1340 -0
- package/scripts/moqui-rebuild-gate.js +167 -0
- package/scripts/moqui-release-summary.js +729 -0
- package/scripts/moqui-standard-rebuild.js +1370 -0
- package/scripts/moqui-template-baseline-report.js +682 -0
- package/scripts/npm-package-runtime-asset-check.js +221 -0
- package/scripts/problem-closure-gate.js +441 -0
- package/scripts/release-asset-integrity-check.js +216 -0
- package/scripts/release-asset-nonempty-normalize.js +166 -0
- package/scripts/release-drift-evaluate.js +223 -0
- package/scripts/release-drift-signals.js +255 -0
- package/scripts/release-governance-snapshot-export.js +132 -0
- package/scripts/release-ops-weekly-summary.js +934 -0
- package/scripts/release-risk-remediation-bundle.js +315 -0
- package/scripts/release-weekly-ops-gate.js +423 -0
- package/scripts/state-migration-reconciliation-gate.js +110 -0
- package/scripts/state-storage-tiering-audit.js +337 -0
- package/scripts/steering-content-audit.js +393 -0
- package/scripts/symbol-evidence-locate.js +366 -0
|
@@ -0,0 +1,1112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
|
|
7
|
+
const DEFAULT_INTENT_AUDIT = '.sce/reports/interactive-copilot-audit.jsonl';
|
|
8
|
+
const DEFAULT_APPROVAL_AUDIT = '.sce/reports/interactive-approval-events.jsonl';
|
|
9
|
+
const DEFAULT_EXECUTION_LEDGER = '.sce/reports/interactive-execution-ledger.jsonl';
|
|
10
|
+
const DEFAULT_FEEDBACK_FILE = '.sce/reports/interactive-user-feedback.jsonl';
|
|
11
|
+
const DEFAULT_MATRIX_SIGNALS = '.sce/reports/interactive-matrix-signals.jsonl';
|
|
12
|
+
const DEFAULT_DIALOGUE_AUTHORIZATION_SIGNALS = '.sce/reports/interactive-dialogue-authorization-signals.jsonl';
|
|
13
|
+
const DEFAULT_RUNTIME_SIGNALS = '.sce/reports/interactive-runtime-signals.jsonl';
|
|
14
|
+
const DEFAULT_AUTHORIZATION_TIER_SIGNALS = '.sce/reports/interactive-authorization-tier-signals.jsonl';
|
|
15
|
+
const DEFAULT_THRESHOLDS = 'docs/interactive-customization/governance-threshold-baseline.json';
|
|
16
|
+
const DEFAULT_OUT = '.sce/reports/interactive-governance-report.json';
|
|
17
|
+
const DEFAULT_MARKDOWN_OUT = '.sce/reports/interactive-governance-report.md';
|
|
18
|
+
|
|
19
|
+
function parseArgs(argv) {
|
|
20
|
+
const options = {
|
|
21
|
+
intentAudit: DEFAULT_INTENT_AUDIT,
|
|
22
|
+
approvalAudit: DEFAULT_APPROVAL_AUDIT,
|
|
23
|
+
executionLedger: DEFAULT_EXECUTION_LEDGER,
|
|
24
|
+
feedbackFile: DEFAULT_FEEDBACK_FILE,
|
|
25
|
+
matrixSignals: DEFAULT_MATRIX_SIGNALS,
|
|
26
|
+
dialogueAuthorizationSignals: DEFAULT_DIALOGUE_AUTHORIZATION_SIGNALS,
|
|
27
|
+
runtimeSignals: DEFAULT_RUNTIME_SIGNALS,
|
|
28
|
+
authorizationTierSignals: DEFAULT_AUTHORIZATION_TIER_SIGNALS,
|
|
29
|
+
thresholds: DEFAULT_THRESHOLDS,
|
|
30
|
+
period: 'weekly',
|
|
31
|
+
from: null,
|
|
32
|
+
to: null,
|
|
33
|
+
out: DEFAULT_OUT,
|
|
34
|
+
markdownOut: DEFAULT_MARKDOWN_OUT,
|
|
35
|
+
failOnAlert: false,
|
|
36
|
+
json: false
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
40
|
+
const token = argv[i];
|
|
41
|
+
const next = argv[i + 1];
|
|
42
|
+
if (token === '--intent-audit' && next) {
|
|
43
|
+
options.intentAudit = next;
|
|
44
|
+
i += 1;
|
|
45
|
+
} else if (token === '--approval-audit' && next) {
|
|
46
|
+
options.approvalAudit = next;
|
|
47
|
+
i += 1;
|
|
48
|
+
} else if (token === '--execution-ledger' && next) {
|
|
49
|
+
options.executionLedger = next;
|
|
50
|
+
i += 1;
|
|
51
|
+
} else if (token === '--feedback-file' && next) {
|
|
52
|
+
options.feedbackFile = next;
|
|
53
|
+
i += 1;
|
|
54
|
+
} else if (token === '--matrix-signals' && next) {
|
|
55
|
+
options.matrixSignals = next;
|
|
56
|
+
i += 1;
|
|
57
|
+
} else if (token === '--dialogue-authorization-signals' && next) {
|
|
58
|
+
options.dialogueAuthorizationSignals = next;
|
|
59
|
+
i += 1;
|
|
60
|
+
} else if (token === '--runtime-signals' && next) {
|
|
61
|
+
options.runtimeSignals = next;
|
|
62
|
+
i += 1;
|
|
63
|
+
} else if (token === '--authorization-tier-signals' && next) {
|
|
64
|
+
options.authorizationTierSignals = next;
|
|
65
|
+
i += 1;
|
|
66
|
+
} else if (token === '--thresholds' && next) {
|
|
67
|
+
options.thresholds = next;
|
|
68
|
+
i += 1;
|
|
69
|
+
} else if (token === '--period' && next) {
|
|
70
|
+
options.period = next;
|
|
71
|
+
i += 1;
|
|
72
|
+
} else if (token === '--from' && next) {
|
|
73
|
+
options.from = next;
|
|
74
|
+
i += 1;
|
|
75
|
+
} else if (token === '--to' && next) {
|
|
76
|
+
options.to = next;
|
|
77
|
+
i += 1;
|
|
78
|
+
} else if (token === '--out' && next) {
|
|
79
|
+
options.out = next;
|
|
80
|
+
i += 1;
|
|
81
|
+
} else if (token === '--markdown-out' && next) {
|
|
82
|
+
options.markdownOut = next;
|
|
83
|
+
i += 1;
|
|
84
|
+
} else if (token === '--fail-on-alert') {
|
|
85
|
+
options.failOnAlert = true;
|
|
86
|
+
} else if (token === '--json') {
|
|
87
|
+
options.json = true;
|
|
88
|
+
} else if (token === '--help' || token === '-h') {
|
|
89
|
+
printHelpAndExit(0);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const allowedPeriods = new Set(['weekly', 'monthly', 'all', 'custom']);
|
|
94
|
+
const normalizedPeriod = `${options.period || ''}`.trim().toLowerCase();
|
|
95
|
+
if (!allowedPeriods.has(normalizedPeriod)) {
|
|
96
|
+
throw new Error('--period must be one of: weekly, monthly, all, custom');
|
|
97
|
+
}
|
|
98
|
+
options.period = normalizedPeriod;
|
|
99
|
+
|
|
100
|
+
if (options.period === 'custom') {
|
|
101
|
+
if (!options.from || !options.to) {
|
|
102
|
+
throw new Error('--from and --to are required when --period custom is used');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return options;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function printHelpAndExit(code) {
|
|
110
|
+
const lines = [
|
|
111
|
+
'Usage: node scripts/interactive-governance-report.js [options]',
|
|
112
|
+
'',
|
|
113
|
+
'Options:',
|
|
114
|
+
` --intent-audit <path> Intent audit JSONL (default: ${DEFAULT_INTENT_AUDIT})`,
|
|
115
|
+
` --approval-audit <path> Approval audit JSONL (default: ${DEFAULT_APPROVAL_AUDIT})`,
|
|
116
|
+
` --execution-ledger <path> Execution ledger JSONL (default: ${DEFAULT_EXECUTION_LEDGER})`,
|
|
117
|
+
` --feedback-file <path> User feedback JSONL (default: ${DEFAULT_FEEDBACK_FILE})`,
|
|
118
|
+
` --matrix-signals <path> Matrix signal JSONL (default: ${DEFAULT_MATRIX_SIGNALS})`,
|
|
119
|
+
` --dialogue-authorization-signals <path> Dialogue authorization signal JSONL (default: ${DEFAULT_DIALOGUE_AUTHORIZATION_SIGNALS})`,
|
|
120
|
+
` --runtime-signals <path> Runtime policy signal JSONL (default: ${DEFAULT_RUNTIME_SIGNALS})`,
|
|
121
|
+
` --authorization-tier-signals <path> Authorization tier signal JSONL (default: ${DEFAULT_AUTHORIZATION_TIER_SIGNALS})`,
|
|
122
|
+
` --thresholds <path> Governance threshold JSON (default: ${DEFAULT_THRESHOLDS})`,
|
|
123
|
+
' --period <type> weekly|monthly|all|custom (default: weekly)',
|
|
124
|
+
' --from <ISO datetime> Start time (required for custom period)',
|
|
125
|
+
' --to <ISO datetime> End time (required for custom period)',
|
|
126
|
+
` --out <path> JSON report output (default: ${DEFAULT_OUT})`,
|
|
127
|
+
` --markdown-out <path> Markdown report output (default: ${DEFAULT_MARKDOWN_OUT})`,
|
|
128
|
+
' --fail-on-alert Exit code 2 when any medium/high breach exists',
|
|
129
|
+
' --json Print JSON payload',
|
|
130
|
+
' -h, --help Show this help'
|
|
131
|
+
];
|
|
132
|
+
console.log(lines.join('\n'));
|
|
133
|
+
process.exit(code);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function resolvePath(cwd, value) {
|
|
137
|
+
return path.isAbsolute(value) ? value : path.resolve(cwd, value);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function parseDate(value, label) {
|
|
141
|
+
const date = new Date(value);
|
|
142
|
+
if (Number.isNaN(date.getTime())) {
|
|
143
|
+
throw new Error(`invalid date for ${label}: ${value}`);
|
|
144
|
+
}
|
|
145
|
+
return date;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function buildPeriodWindow(options, now = new Date()) {
|
|
149
|
+
if (options.period === 'all') {
|
|
150
|
+
return { from: null, to: null, period: 'all' };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (options.period === 'custom') {
|
|
154
|
+
const from = parseDate(options.from, '--from');
|
|
155
|
+
const to = parseDate(options.to, '--to');
|
|
156
|
+
if (from.getTime() > to.getTime()) {
|
|
157
|
+
throw new Error('--from must be <= --to');
|
|
158
|
+
}
|
|
159
|
+
return { from, to, period: 'custom' };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const to = now;
|
|
163
|
+
const from = new Date(now.getTime());
|
|
164
|
+
if (options.period === 'weekly') {
|
|
165
|
+
from.setUTCDate(from.getUTCDate() - 7);
|
|
166
|
+
} else {
|
|
167
|
+
from.setUTCDate(from.getUTCDate() - 30);
|
|
168
|
+
}
|
|
169
|
+
return { from, to, period: options.period };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function readJsonLinesFile(filePath) {
|
|
173
|
+
if (!(await fs.pathExists(filePath))) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
177
|
+
return `${content || ''}`
|
|
178
|
+
.split(/\r?\n/)
|
|
179
|
+
.map(line => line.trim())
|
|
180
|
+
.filter(Boolean)
|
|
181
|
+
.map((line) => {
|
|
182
|
+
try {
|
|
183
|
+
return JSON.parse(line);
|
|
184
|
+
} catch (_error) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
.filter(Boolean);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function pickTimestamp(entry, fields = []) {
|
|
192
|
+
for (const field of fields) {
|
|
193
|
+
if (!entry || entry[field] == null) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const date = new Date(entry[field]);
|
|
197
|
+
if (!Number.isNaN(date.getTime())) {
|
|
198
|
+
return date;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function filterByWindow(entries, fields, window) {
|
|
205
|
+
const fromMs = window.from ? window.from.getTime() : null;
|
|
206
|
+
const toMs = window.to ? window.to.getTime() : null;
|
|
207
|
+
return entries.filter((entry) => {
|
|
208
|
+
const timestamp = pickTimestamp(entry, fields);
|
|
209
|
+
if (!timestamp) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
const value = timestamp.getTime();
|
|
213
|
+
if (fromMs != null && value < fromMs) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
if (toMs != null && value > toMs) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
return true;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function toRatePercent(numerator, denominator) {
|
|
224
|
+
const d = Number(denominator);
|
|
225
|
+
if (!Number.isFinite(d) || d <= 0) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
return Number(((Number(numerator) / d) * 100).toFixed(2));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function toAverage(values) {
|
|
232
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const numbers = values
|
|
236
|
+
.map(item => Number(item))
|
|
237
|
+
.filter(value => Number.isFinite(value));
|
|
238
|
+
if (numbers.length === 0) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
return Number((numbers.reduce((acc, value) => acc + value, 0) / numbers.length).toFixed(2));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function normalizeBusinessMode(value) {
|
|
245
|
+
const normalized = `${value || ''}`.trim().toLowerCase();
|
|
246
|
+
if (normalized === 'user-mode' || normalized === 'ops-mode' || normalized === 'dev-mode') {
|
|
247
|
+
return normalized;
|
|
248
|
+
}
|
|
249
|
+
return 'unknown';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function createBusinessModeBucket() {
|
|
253
|
+
return {
|
|
254
|
+
total: 0,
|
|
255
|
+
allow_total: 0,
|
|
256
|
+
deny_total: 0,
|
|
257
|
+
review_required_total: 0,
|
|
258
|
+
block_total: 0,
|
|
259
|
+
block_rate_percent: null
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function buildBusinessModeBreakdown(entries = []) {
|
|
264
|
+
const byMode = {
|
|
265
|
+
user_mode: createBusinessModeBucket(),
|
|
266
|
+
ops_mode: createBusinessModeBucket(),
|
|
267
|
+
dev_mode: createBusinessModeBucket(),
|
|
268
|
+
unknown: createBusinessModeBucket()
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const modeKeyMap = {
|
|
272
|
+
'user-mode': 'user_mode',
|
|
273
|
+
'ops-mode': 'ops_mode',
|
|
274
|
+
'dev-mode': 'dev_mode',
|
|
275
|
+
unknown: 'unknown'
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
for (const entry of entries) {
|
|
279
|
+
const mode = normalizeBusinessMode(entry && entry.business_mode);
|
|
280
|
+
const modeKey = modeKeyMap[mode] || 'unknown';
|
|
281
|
+
const decision = `${entry && entry.decision ? entry.decision : ''}`.trim().toLowerCase();
|
|
282
|
+
const bucket = byMode[modeKey];
|
|
283
|
+
bucket.total += 1;
|
|
284
|
+
if (decision === 'allow') {
|
|
285
|
+
bucket.allow_total += 1;
|
|
286
|
+
} else if (decision === 'deny') {
|
|
287
|
+
bucket.deny_total += 1;
|
|
288
|
+
} else if (decision === 'review-required') {
|
|
289
|
+
bucket.review_required_total += 1;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
for (const bucket of Object.values(byMode)) {
|
|
294
|
+
bucket.block_total = bucket.deny_total + bucket.review_required_total;
|
|
295
|
+
bucket.block_rate_percent = toRatePercent(bucket.block_total, bucket.total);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const total = Object.values(byMode).reduce((acc, bucket) => acc + bucket.total, 0);
|
|
299
|
+
const unknownTotal = byMode.unknown.total;
|
|
300
|
+
return {
|
|
301
|
+
total,
|
|
302
|
+
known_total: total - unknownTotal,
|
|
303
|
+
unknown_total: unknownTotal,
|
|
304
|
+
user_mode_total: byMode.user_mode.total,
|
|
305
|
+
ops_mode_total: byMode.ops_mode.total,
|
|
306
|
+
dev_mode_total: byMode.dev_mode.total,
|
|
307
|
+
by_mode: byMode
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function loadThresholds(raw = {}) {
|
|
312
|
+
const thresholds = raw && typeof raw === 'object' ? raw : {};
|
|
313
|
+
return {
|
|
314
|
+
min_intent_samples: Number.isFinite(Number(thresholds.min_intent_samples))
|
|
315
|
+
? Number(thresholds.min_intent_samples)
|
|
316
|
+
: 5,
|
|
317
|
+
adoption_rate_min_percent: Number.isFinite(Number(thresholds.adoption_rate_min_percent))
|
|
318
|
+
? Number(thresholds.adoption_rate_min_percent)
|
|
319
|
+
: 30,
|
|
320
|
+
execution_success_rate_min_percent: Number.isFinite(Number(thresholds.execution_success_rate_min_percent))
|
|
321
|
+
? Number(thresholds.execution_success_rate_min_percent)
|
|
322
|
+
: 90,
|
|
323
|
+
rollback_rate_max_percent: Number.isFinite(Number(thresholds.rollback_rate_max_percent))
|
|
324
|
+
? Number(thresholds.rollback_rate_max_percent)
|
|
325
|
+
: 20,
|
|
326
|
+
security_intercept_rate_max_percent: Number.isFinite(Number(thresholds.security_intercept_rate_max_percent))
|
|
327
|
+
? Number(thresholds.security_intercept_rate_max_percent)
|
|
328
|
+
: 60,
|
|
329
|
+
satisfaction_min_score: Number.isFinite(Number(thresholds.satisfaction_min_score))
|
|
330
|
+
? Number(thresholds.satisfaction_min_score)
|
|
331
|
+
: 4,
|
|
332
|
+
min_feedback_samples: Number.isFinite(Number(thresholds.min_feedback_samples))
|
|
333
|
+
? Number(thresholds.min_feedback_samples)
|
|
334
|
+
: 3,
|
|
335
|
+
min_matrix_samples: Number.isFinite(Number(thresholds.min_matrix_samples))
|
|
336
|
+
? Number(thresholds.min_matrix_samples)
|
|
337
|
+
: 3,
|
|
338
|
+
min_dialogue_authorization_samples: Number.isFinite(Number(thresholds.min_dialogue_authorization_samples))
|
|
339
|
+
? Number(thresholds.min_dialogue_authorization_samples)
|
|
340
|
+
: 3,
|
|
341
|
+
dialogue_authorization_block_rate_max_percent: Number.isFinite(Number(thresholds.dialogue_authorization_block_rate_max_percent))
|
|
342
|
+
? Number(thresholds.dialogue_authorization_block_rate_max_percent)
|
|
343
|
+
: 40,
|
|
344
|
+
min_runtime_samples: Number.isFinite(Number(thresholds.min_runtime_samples))
|
|
345
|
+
? Number(thresholds.min_runtime_samples)
|
|
346
|
+
: 3,
|
|
347
|
+
runtime_block_rate_max_percent: Number.isFinite(Number(thresholds.runtime_block_rate_max_percent))
|
|
348
|
+
? Number(thresholds.runtime_block_rate_max_percent)
|
|
349
|
+
: 40,
|
|
350
|
+
runtime_ui_mode_violation_max_total: Number.isFinite(Number(thresholds.runtime_ui_mode_violation_max_total))
|
|
351
|
+
? Number(thresholds.runtime_ui_mode_violation_max_total)
|
|
352
|
+
: 0,
|
|
353
|
+
min_authorization_tier_samples: Number.isFinite(Number(thresholds.min_authorization_tier_samples))
|
|
354
|
+
? Number(thresholds.min_authorization_tier_samples)
|
|
355
|
+
: 3,
|
|
356
|
+
authorization_tier_block_rate_max_percent: Number.isFinite(Number(thresholds.authorization_tier_block_rate_max_percent))
|
|
357
|
+
? Number(thresholds.authorization_tier_block_rate_max_percent)
|
|
358
|
+
: 40,
|
|
359
|
+
matrix_portfolio_pass_rate_min_percent: Number.isFinite(Number(thresholds.matrix_portfolio_pass_rate_min_percent))
|
|
360
|
+
? Number(thresholds.matrix_portfolio_pass_rate_min_percent)
|
|
361
|
+
: 80,
|
|
362
|
+
matrix_regression_positive_rate_max_percent: Number.isFinite(Number(thresholds.matrix_regression_positive_rate_max_percent))
|
|
363
|
+
? Number(thresholds.matrix_regression_positive_rate_max_percent)
|
|
364
|
+
: 20,
|
|
365
|
+
matrix_stage_error_rate_max_percent: Number.isFinite(Number(thresholds.matrix_stage_error_rate_max_percent))
|
|
366
|
+
? Number(thresholds.matrix_stage_error_rate_max_percent)
|
|
367
|
+
: 20
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function buildAlert({
|
|
372
|
+
id,
|
|
373
|
+
severity,
|
|
374
|
+
metric,
|
|
375
|
+
actual,
|
|
376
|
+
threshold,
|
|
377
|
+
direction,
|
|
378
|
+
message,
|
|
379
|
+
recommendation
|
|
380
|
+
}) {
|
|
381
|
+
return {
|
|
382
|
+
id,
|
|
383
|
+
severity,
|
|
384
|
+
status: 'breach',
|
|
385
|
+
metric,
|
|
386
|
+
actual,
|
|
387
|
+
threshold,
|
|
388
|
+
direction,
|
|
389
|
+
message,
|
|
390
|
+
recommendation
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function evaluateAlerts(metrics, thresholds) {
|
|
395
|
+
const alerts = [];
|
|
396
|
+
|
|
397
|
+
if (metrics.intent_total < thresholds.min_intent_samples) {
|
|
398
|
+
alerts.push({
|
|
399
|
+
id: 'adoption-sample-insufficient',
|
|
400
|
+
severity: 'low',
|
|
401
|
+
status: 'warning',
|
|
402
|
+
metric: 'intent_total',
|
|
403
|
+
actual: metrics.intent_total,
|
|
404
|
+
threshold: thresholds.min_intent_samples,
|
|
405
|
+
direction: 'min',
|
|
406
|
+
message: 'Intent sample size is below minimum; adoption trend is not statistically stable.',
|
|
407
|
+
recommendation: 'Collect more interactive intent/apply runs before enforcing adoption policy.'
|
|
408
|
+
});
|
|
409
|
+
} else if (
|
|
410
|
+
metrics.adoption_rate_percent != null &&
|
|
411
|
+
metrics.adoption_rate_percent < thresholds.adoption_rate_min_percent
|
|
412
|
+
) {
|
|
413
|
+
alerts.push(buildAlert({
|
|
414
|
+
id: 'adoption-rate-low',
|
|
415
|
+
severity: 'medium',
|
|
416
|
+
metric: 'adoption_rate_percent',
|
|
417
|
+
actual: metrics.adoption_rate_percent,
|
|
418
|
+
threshold: thresholds.adoption_rate_min_percent,
|
|
419
|
+
direction: 'min',
|
|
420
|
+
message: 'Adoption rate is below minimum threshold.',
|
|
421
|
+
recommendation: 'Review plan quality and reduce review friction in common low-risk scenarios.'
|
|
422
|
+
}));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (
|
|
426
|
+
metrics.execution_success_rate_percent != null &&
|
|
427
|
+
metrics.execution_success_rate_percent < thresholds.execution_success_rate_min_percent
|
|
428
|
+
) {
|
|
429
|
+
alerts.push(buildAlert({
|
|
430
|
+
id: 'execution-success-low',
|
|
431
|
+
severity: 'high',
|
|
432
|
+
metric: 'execution_success_rate_percent',
|
|
433
|
+
actual: metrics.execution_success_rate_percent,
|
|
434
|
+
threshold: thresholds.execution_success_rate_min_percent,
|
|
435
|
+
direction: 'min',
|
|
436
|
+
message: 'Execution success rate is below minimum threshold.',
|
|
437
|
+
recommendation: 'Analyze failed apply records and tighten validation before execution.'
|
|
438
|
+
}));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (
|
|
442
|
+
metrics.rollback_rate_percent != null &&
|
|
443
|
+
metrics.rollback_rate_percent > thresholds.rollback_rate_max_percent
|
|
444
|
+
) {
|
|
445
|
+
alerts.push(buildAlert({
|
|
446
|
+
id: 'rollback-rate-high',
|
|
447
|
+
severity: 'high',
|
|
448
|
+
metric: 'rollback_rate_percent',
|
|
449
|
+
actual: metrics.rollback_rate_percent,
|
|
450
|
+
threshold: thresholds.rollback_rate_max_percent,
|
|
451
|
+
direction: 'max',
|
|
452
|
+
message: 'Rollback rate is above maximum threshold.',
|
|
453
|
+
recommendation: 'Freeze risky rollout scope and enforce stronger verification checks before apply.'
|
|
454
|
+
}));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (
|
|
458
|
+
metrics.security_intercept_rate_percent != null &&
|
|
459
|
+
metrics.security_intercept_rate_percent > thresholds.security_intercept_rate_max_percent
|
|
460
|
+
) {
|
|
461
|
+
alerts.push(buildAlert({
|
|
462
|
+
id: 'security-intercept-high',
|
|
463
|
+
severity: 'medium',
|
|
464
|
+
metric: 'security_intercept_rate_percent',
|
|
465
|
+
actual: metrics.security_intercept_rate_percent,
|
|
466
|
+
threshold: thresholds.security_intercept_rate_max_percent,
|
|
467
|
+
direction: 'max',
|
|
468
|
+
message: 'Security intercept rate is above maximum threshold.',
|
|
469
|
+
recommendation: 'Improve intent guidance and plan generation to avoid blocked action categories.'
|
|
470
|
+
}));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (
|
|
474
|
+
metrics.satisfaction_response_count >= thresholds.min_feedback_samples &&
|
|
475
|
+
metrics.satisfaction_avg_score != null &&
|
|
476
|
+
metrics.satisfaction_avg_score < thresholds.satisfaction_min_score
|
|
477
|
+
) {
|
|
478
|
+
alerts.push(buildAlert({
|
|
479
|
+
id: 'satisfaction-low',
|
|
480
|
+
severity: 'medium',
|
|
481
|
+
metric: 'satisfaction_avg_score',
|
|
482
|
+
actual: metrics.satisfaction_avg_score,
|
|
483
|
+
threshold: thresholds.satisfaction_min_score,
|
|
484
|
+
direction: 'min',
|
|
485
|
+
message: 'User satisfaction is below minimum threshold.',
|
|
486
|
+
recommendation: 'Review failed/blocked cases with business users and tune scene guidance.'
|
|
487
|
+
}));
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (metrics.satisfaction_response_count < thresholds.min_feedback_samples) {
|
|
491
|
+
alerts.push({
|
|
492
|
+
id: 'feedback-sample-insufficient',
|
|
493
|
+
severity: 'low',
|
|
494
|
+
status: 'warning',
|
|
495
|
+
metric: 'satisfaction_response_count',
|
|
496
|
+
actual: metrics.satisfaction_response_count,
|
|
497
|
+
threshold: thresholds.min_feedback_samples,
|
|
498
|
+
direction: 'min',
|
|
499
|
+
message: 'Feedback sample size is below minimum; satisfaction trend is not statistically stable.',
|
|
500
|
+
recommendation: 'Collect more user feedback samples before making policy changes.'
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (metrics.dialogue_authorization_total < thresholds.min_dialogue_authorization_samples) {
|
|
505
|
+
alerts.push({
|
|
506
|
+
id: 'dialogue-authorization-sample-insufficient',
|
|
507
|
+
severity: 'low',
|
|
508
|
+
status: 'warning',
|
|
509
|
+
metric: 'dialogue_authorization_total',
|
|
510
|
+
actual: metrics.dialogue_authorization_total,
|
|
511
|
+
threshold: thresholds.min_dialogue_authorization_samples,
|
|
512
|
+
direction: 'min',
|
|
513
|
+
message: 'Dialogue authorization sample size is below minimum; block trend is not statistically stable.',
|
|
514
|
+
recommendation: 'Collect more user-app/ops-console sessions before tightening dialogue authorization policy.'
|
|
515
|
+
});
|
|
516
|
+
} else if (
|
|
517
|
+
metrics.dialogue_authorization_block_rate_percent != null &&
|
|
518
|
+
metrics.dialogue_authorization_block_rate_percent > thresholds.dialogue_authorization_block_rate_max_percent
|
|
519
|
+
) {
|
|
520
|
+
alerts.push(buildAlert({
|
|
521
|
+
id: 'dialogue-authorization-block-rate-high',
|
|
522
|
+
severity: 'medium',
|
|
523
|
+
metric: 'dialogue_authorization_block_rate_percent',
|
|
524
|
+
actual: metrics.dialogue_authorization_block_rate_percent,
|
|
525
|
+
threshold: thresholds.dialogue_authorization_block_rate_max_percent,
|
|
526
|
+
direction: 'max',
|
|
527
|
+
message: 'Dialogue authorization block/review rate is above maximum threshold.',
|
|
528
|
+
recommendation: 'Tune ui-mode/profile defaults and dialogue policy prompts to reduce non-actionable requests.'
|
|
529
|
+
}));
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (metrics.runtime_total < thresholds.min_runtime_samples) {
|
|
533
|
+
alerts.push({
|
|
534
|
+
id: 'runtime-sample-insufficient',
|
|
535
|
+
severity: 'low',
|
|
536
|
+
status: 'warning',
|
|
537
|
+
metric: 'runtime_total',
|
|
538
|
+
actual: metrics.runtime_total,
|
|
539
|
+
threshold: thresholds.min_runtime_samples,
|
|
540
|
+
direction: 'min',
|
|
541
|
+
message: 'Runtime policy sample size is below minimum; runtime block trend is not statistically stable.',
|
|
542
|
+
recommendation: 'Collect more runtime policy evaluations before tightening runtime thresholds.'
|
|
543
|
+
});
|
|
544
|
+
} else if (
|
|
545
|
+
metrics.runtime_block_rate_percent != null &&
|
|
546
|
+
metrics.runtime_block_rate_percent > thresholds.runtime_block_rate_max_percent
|
|
547
|
+
) {
|
|
548
|
+
alerts.push(buildAlert({
|
|
549
|
+
id: 'runtime-block-rate-high',
|
|
550
|
+
severity: 'medium',
|
|
551
|
+
metric: 'runtime_block_rate_percent',
|
|
552
|
+
actual: metrics.runtime_block_rate_percent,
|
|
553
|
+
threshold: thresholds.runtime_block_rate_max_percent,
|
|
554
|
+
direction: 'max',
|
|
555
|
+
message: 'Runtime policy block/review rate is above maximum threshold.',
|
|
556
|
+
recommendation: 'Review runtime mode/environment defaults and reduce invalid apply paths before runtime gate.'
|
|
557
|
+
}));
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (
|
|
561
|
+
Number.isFinite(metrics.runtime_ui_mode_violation_total) &&
|
|
562
|
+
metrics.runtime_ui_mode_violation_total > thresholds.runtime_ui_mode_violation_max_total
|
|
563
|
+
) {
|
|
564
|
+
alerts.push(buildAlert({
|
|
565
|
+
id: 'runtime-ui-mode-violation-high',
|
|
566
|
+
severity: 'medium',
|
|
567
|
+
metric: 'runtime_ui_mode_violation_total',
|
|
568
|
+
actual: metrics.runtime_ui_mode_violation_total,
|
|
569
|
+
threshold: thresholds.runtime_ui_mode_violation_max_total,
|
|
570
|
+
direction: 'max',
|
|
571
|
+
message: 'Runtime ui_mode policy violations are above maximum threshold.',
|
|
572
|
+
recommendation: 'Route user-app apply intents to ops-console and align runtime ui_mode policy with surface roles.'
|
|
573
|
+
}));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (Number.isFinite(metrics.business_mode_unknown_signal_total) && metrics.business_mode_unknown_signal_total > 0) {
|
|
577
|
+
alerts.push({
|
|
578
|
+
id: 'business-mode-signal-missing',
|
|
579
|
+
severity: 'low',
|
|
580
|
+
status: 'warning',
|
|
581
|
+
metric: 'business_mode_unknown_signal_total',
|
|
582
|
+
actual: metrics.business_mode_unknown_signal_total,
|
|
583
|
+
threshold: 0,
|
|
584
|
+
direction: 'max',
|
|
585
|
+
message: 'Some governance signals are missing business_mode tagging.',
|
|
586
|
+
recommendation: 'Upgrade interactive-loop/flow signal emitters so every signal includes business_mode.'
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (metrics.authorization_tier_total < thresholds.min_authorization_tier_samples) {
|
|
591
|
+
alerts.push({
|
|
592
|
+
id: 'authorization-tier-sample-insufficient',
|
|
593
|
+
severity: 'low',
|
|
594
|
+
status: 'warning',
|
|
595
|
+
metric: 'authorization_tier_total',
|
|
596
|
+
actual: metrics.authorization_tier_total,
|
|
597
|
+
threshold: thresholds.min_authorization_tier_samples,
|
|
598
|
+
direction: 'min',
|
|
599
|
+
message: 'Authorization tier sample size is below minimum; block trend is not statistically stable.',
|
|
600
|
+
recommendation: 'Collect more authorization-tier evaluations before tightening tier policy.'
|
|
601
|
+
});
|
|
602
|
+
} else if (
|
|
603
|
+
metrics.authorization_tier_block_rate_percent != null &&
|
|
604
|
+
metrics.authorization_tier_block_rate_percent > thresholds.authorization_tier_block_rate_max_percent
|
|
605
|
+
) {
|
|
606
|
+
alerts.push(buildAlert({
|
|
607
|
+
id: 'authorization-tier-block-rate-high',
|
|
608
|
+
severity: 'medium',
|
|
609
|
+
metric: 'authorization_tier_block_rate_percent',
|
|
610
|
+
actual: metrics.authorization_tier_block_rate_percent,
|
|
611
|
+
threshold: thresholds.authorization_tier_block_rate_max_percent,
|
|
612
|
+
direction: 'max',
|
|
613
|
+
message: 'Authorization tier block/review rate is above maximum threshold.',
|
|
614
|
+
recommendation: 'Refine dialogue profile defaults and step-up policy to reduce non-actionable apply requests.'
|
|
615
|
+
}));
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (metrics.matrix_signal_total < thresholds.min_matrix_samples) {
|
|
619
|
+
alerts.push({
|
|
620
|
+
id: 'matrix-sample-insufficient',
|
|
621
|
+
severity: 'low',
|
|
622
|
+
status: 'warning',
|
|
623
|
+
metric: 'matrix_signal_total',
|
|
624
|
+
actual: metrics.matrix_signal_total,
|
|
625
|
+
threshold: thresholds.min_matrix_samples,
|
|
626
|
+
direction: 'min',
|
|
627
|
+
message: 'Matrix signal sample size is below minimum; matrix trend is not statistically stable.',
|
|
628
|
+
recommendation: 'Collect more interactive-flow runs with matrix snapshots before tightening matrix gates.'
|
|
629
|
+
});
|
|
630
|
+
} else {
|
|
631
|
+
if (
|
|
632
|
+
metrics.matrix_portfolio_pass_rate_percent != null &&
|
|
633
|
+
metrics.matrix_portfolio_pass_rate_percent < thresholds.matrix_portfolio_pass_rate_min_percent
|
|
634
|
+
) {
|
|
635
|
+
alerts.push(buildAlert({
|
|
636
|
+
id: 'matrix-portfolio-pass-rate-low',
|
|
637
|
+
severity: 'medium',
|
|
638
|
+
metric: 'matrix_portfolio_pass_rate_percent',
|
|
639
|
+
actual: metrics.matrix_portfolio_pass_rate_percent,
|
|
640
|
+
threshold: thresholds.matrix_portfolio_pass_rate_min_percent,
|
|
641
|
+
direction: 'min',
|
|
642
|
+
message: 'Matrix portfolio pass rate is below minimum threshold.',
|
|
643
|
+
recommendation: 'Prioritize ontology closure and semantic quality improvements in failing templates.'
|
|
644
|
+
}));
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (
|
|
648
|
+
metrics.matrix_regression_positive_rate_percent != null &&
|
|
649
|
+
metrics.matrix_regression_positive_rate_percent > thresholds.matrix_regression_positive_rate_max_percent
|
|
650
|
+
) {
|
|
651
|
+
alerts.push(buildAlert({
|
|
652
|
+
id: 'matrix-regression-rate-high',
|
|
653
|
+
severity: 'medium',
|
|
654
|
+
metric: 'matrix_regression_positive_rate_percent',
|
|
655
|
+
actual: metrics.matrix_regression_positive_rate_percent,
|
|
656
|
+
threshold: thresholds.matrix_regression_positive_rate_max_percent,
|
|
657
|
+
direction: 'max',
|
|
658
|
+
message: 'Matrix regression-positive rate is above maximum threshold.',
|
|
659
|
+
recommendation: 'Run matrix remediation queue and block release until regression deltas are closed.'
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (
|
|
664
|
+
metrics.matrix_stage_error_rate_percent != null &&
|
|
665
|
+
metrics.matrix_stage_error_rate_percent > thresholds.matrix_stage_error_rate_max_percent
|
|
666
|
+
) {
|
|
667
|
+
alerts.push(buildAlert({
|
|
668
|
+
id: 'matrix-stage-error-rate-high',
|
|
669
|
+
severity: 'medium',
|
|
670
|
+
metric: 'matrix_stage_error_rate_percent',
|
|
671
|
+
actual: metrics.matrix_stage_error_rate_percent,
|
|
672
|
+
threshold: thresholds.matrix_stage_error_rate_max_percent,
|
|
673
|
+
direction: 'max',
|
|
674
|
+
message: 'Matrix stage non-zero/error rate is above maximum threshold.',
|
|
675
|
+
recommendation: 'Stabilize matrix baseline runtime (inputs/template-dir/compare refs) before scale-out.'
|
|
676
|
+
}));
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return alerts;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function buildRecommendations(alerts) {
|
|
684
|
+
const recommendations = [];
|
|
685
|
+
const seen = new Set();
|
|
686
|
+
for (const alert of alerts) {
|
|
687
|
+
const recommendation = `${alert.recommendation || ''}`.trim();
|
|
688
|
+
if (!recommendation || seen.has(recommendation)) {
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
seen.add(recommendation);
|
|
692
|
+
recommendations.push(recommendation);
|
|
693
|
+
}
|
|
694
|
+
if (recommendations.length === 0) {
|
|
695
|
+
recommendations.push('Current governance metrics are within configured thresholds.');
|
|
696
|
+
}
|
|
697
|
+
return recommendations;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function parseFeedbackScore(entry) {
|
|
701
|
+
const candidates = [
|
|
702
|
+
entry && entry.satisfaction_score,
|
|
703
|
+
entry && entry.score,
|
|
704
|
+
entry && entry.rating
|
|
705
|
+
];
|
|
706
|
+
for (const value of candidates) {
|
|
707
|
+
const num = Number(value);
|
|
708
|
+
if (Number.isFinite(num)) {
|
|
709
|
+
return num;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return null;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function buildMarkdown(report) {
|
|
716
|
+
const lines = [];
|
|
717
|
+
lines.push('# Interactive Governance Report');
|
|
718
|
+
lines.push('');
|
|
719
|
+
lines.push(`- Generated at: ${report.generated_at}`);
|
|
720
|
+
lines.push(`- Period: ${report.period.type}`);
|
|
721
|
+
lines.push(`- Window: ${report.period.from || 'n/a'} -> ${report.period.to || 'n/a'}`);
|
|
722
|
+
lines.push('');
|
|
723
|
+
lines.push('## Metrics');
|
|
724
|
+
lines.push('');
|
|
725
|
+
lines.push('| Metric | Value |');
|
|
726
|
+
lines.push('| --- | ---: |');
|
|
727
|
+
lines.push(`| Adoption rate | ${report.metrics.adoption_rate_percent == null ? 'n/a' : `${report.metrics.adoption_rate_percent}%`} |`);
|
|
728
|
+
lines.push(`| Execution success rate | ${report.metrics.execution_success_rate_percent == null ? 'n/a' : `${report.metrics.execution_success_rate_percent}%`} |`);
|
|
729
|
+
lines.push(`| Rollback rate | ${report.metrics.rollback_rate_percent == null ? 'n/a' : `${report.metrics.rollback_rate_percent}%`} |`);
|
|
730
|
+
lines.push(`| Security intercept rate | ${report.metrics.security_intercept_rate_percent == null ? 'n/a' : `${report.metrics.security_intercept_rate_percent}%`} |`);
|
|
731
|
+
lines.push(`| Dialogue authorization deny total | ${report.metrics.dialogue_authorization_deny_total} |`);
|
|
732
|
+
lines.push(`| Dialogue authorization review-required total | ${report.metrics.dialogue_authorization_review_required_total} |`);
|
|
733
|
+
lines.push(`| Dialogue authorization block rate | ${report.metrics.dialogue_authorization_block_rate_percent == null ? 'n/a' : `${report.metrics.dialogue_authorization_block_rate_percent}%`} |`);
|
|
734
|
+
lines.push(`| User-app apply attempt total | ${report.metrics.dialogue_authorization_user_app_apply_attempt_total} |`);
|
|
735
|
+
lines.push(`| Runtime deny total | ${report.metrics.runtime_deny_total} |`);
|
|
736
|
+
lines.push(`| Runtime review-required total | ${report.metrics.runtime_review_required_total} |`);
|
|
737
|
+
lines.push(`| Runtime block rate | ${report.metrics.runtime_block_rate_percent == null ? 'n/a' : `${report.metrics.runtime_block_rate_percent}%`} |`);
|
|
738
|
+
lines.push(`| Runtime ui-mode violation total | ${report.metrics.runtime_ui_mode_violation_total} |`);
|
|
739
|
+
lines.push(`| Runtime unknown business-mode total | ${report.metrics.runtime_unknown_business_mode_total} |`);
|
|
740
|
+
lines.push(`| Authorization tier deny total | ${report.metrics.authorization_tier_deny_total} |`);
|
|
741
|
+
lines.push(`| Authorization tier review-required total | ${report.metrics.authorization_tier_review_required_total} |`);
|
|
742
|
+
lines.push(`| Authorization tier block rate | ${report.metrics.authorization_tier_block_rate_percent == null ? 'n/a' : `${report.metrics.authorization_tier_block_rate_percent}%`} |`);
|
|
743
|
+
lines.push(`| Authorization tier unknown business-mode total | ${report.metrics.authorization_tier_unknown_business_mode_total} |`);
|
|
744
|
+
lines.push(`| Satisfaction (avg) | ${report.metrics.satisfaction_avg_score == null ? 'n/a' : report.metrics.satisfaction_avg_score} |`);
|
|
745
|
+
lines.push(`| Satisfaction samples | ${report.metrics.satisfaction_response_count} |`);
|
|
746
|
+
lines.push(`| Matrix portfolio pass rate | ${report.metrics.matrix_portfolio_pass_rate_percent == null ? 'n/a' : `${report.metrics.matrix_portfolio_pass_rate_percent}%`} |`);
|
|
747
|
+
lines.push(`| Matrix regression-positive rate | ${report.metrics.matrix_regression_positive_rate_percent == null ? 'n/a' : `${report.metrics.matrix_regression_positive_rate_percent}%`} |`);
|
|
748
|
+
lines.push(`| Matrix stage error rate | ${report.metrics.matrix_stage_error_rate_percent == null ? 'n/a' : `${report.metrics.matrix_stage_error_rate_percent}%`} |`);
|
|
749
|
+
lines.push(`| Matrix avg semantic score | ${report.metrics.matrix_avg_score == null ? 'n/a' : report.metrics.matrix_avg_score} |`);
|
|
750
|
+
lines.push(`| Matrix signal samples | ${report.metrics.matrix_signal_total} |`);
|
|
751
|
+
lines.push(`| Dialogue unknown business-mode total | ${report.metrics.dialogue_authorization_unknown_business_mode_total} |`);
|
|
752
|
+
lines.push(`| All unknown business-mode signals | ${report.metrics.business_mode_unknown_signal_total} |`);
|
|
753
|
+
lines.push('');
|
|
754
|
+
lines.push('## Alerts');
|
|
755
|
+
lines.push('');
|
|
756
|
+
if (!Array.isArray(report.alerts) || report.alerts.length === 0) {
|
|
757
|
+
lines.push('- none');
|
|
758
|
+
} else {
|
|
759
|
+
for (const alert of report.alerts) {
|
|
760
|
+
lines.push(`- [${alert.severity}/${alert.status}] ${alert.id}: ${alert.message} (actual=${alert.actual}, threshold=${alert.threshold})`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
lines.push('');
|
|
764
|
+
lines.push('## Recommendations');
|
|
765
|
+
lines.push('');
|
|
766
|
+
for (const item of report.recommendations || []) {
|
|
767
|
+
lines.push(`- ${item}`);
|
|
768
|
+
}
|
|
769
|
+
return `${lines.join('\n')}\n`;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
async function main() {
|
|
773
|
+
const options = parseArgs(process.argv.slice(2));
|
|
774
|
+
const cwd = process.cwd();
|
|
775
|
+
|
|
776
|
+
const intentAuditPath = resolvePath(cwd, options.intentAudit);
|
|
777
|
+
const approvalAuditPath = resolvePath(cwd, options.approvalAudit);
|
|
778
|
+
const executionLedgerPath = resolvePath(cwd, options.executionLedger);
|
|
779
|
+
const feedbackFilePath = resolvePath(cwd, options.feedbackFile);
|
|
780
|
+
const matrixSignalsPath = resolvePath(cwd, options.matrixSignals);
|
|
781
|
+
const dialogueAuthorizationSignalsPath = resolvePath(cwd, options.dialogueAuthorizationSignals);
|
|
782
|
+
const runtimeSignalsPath = resolvePath(cwd, options.runtimeSignals);
|
|
783
|
+
const authorizationTierSignalsPath = resolvePath(cwd, options.authorizationTierSignals);
|
|
784
|
+
const thresholdsPath = resolvePath(cwd, options.thresholds);
|
|
785
|
+
const outPath = resolvePath(cwd, options.out);
|
|
786
|
+
const markdownOutPath = resolvePath(cwd, options.markdownOut);
|
|
787
|
+
|
|
788
|
+
const window = buildPeriodWindow(options, new Date());
|
|
789
|
+
|
|
790
|
+
const [
|
|
791
|
+
intentEventsRaw,
|
|
792
|
+
approvalEventsRaw,
|
|
793
|
+
executionRecordsRaw,
|
|
794
|
+
feedbackRaw,
|
|
795
|
+
matrixSignalsRaw,
|
|
796
|
+
dialogueAuthorizationSignalsRaw,
|
|
797
|
+
runtimeSignalsRaw,
|
|
798
|
+
authorizationTierSignalsRaw
|
|
799
|
+
] = await Promise.all([
|
|
800
|
+
readJsonLinesFile(intentAuditPath),
|
|
801
|
+
readJsonLinesFile(approvalAuditPath),
|
|
802
|
+
readJsonLinesFile(executionLedgerPath),
|
|
803
|
+
readJsonLinesFile(feedbackFilePath),
|
|
804
|
+
readJsonLinesFile(matrixSignalsPath),
|
|
805
|
+
readJsonLinesFile(dialogueAuthorizationSignalsPath),
|
|
806
|
+
readJsonLinesFile(runtimeSignalsPath),
|
|
807
|
+
readJsonLinesFile(authorizationTierSignalsPath)
|
|
808
|
+
]);
|
|
809
|
+
|
|
810
|
+
const thresholds = loadThresholds(
|
|
811
|
+
await fs.readJson(thresholdsPath).catch(() => ({}))
|
|
812
|
+
);
|
|
813
|
+
|
|
814
|
+
const intentEvents = filterByWindow(
|
|
815
|
+
intentEventsRaw.filter(item => item && (item.intent_id || `${item.event_type || ''}`.startsWith('interactive.intent.'))),
|
|
816
|
+
['timestamp', 'created_at', 'generated_at'],
|
|
817
|
+
window
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
const approvalEvents = filterByWindow(
|
|
821
|
+
approvalEventsRaw.filter(item => item && item.action),
|
|
822
|
+
['timestamp', 'updated_at', 'created_at'],
|
|
823
|
+
window
|
|
824
|
+
);
|
|
825
|
+
|
|
826
|
+
const executionRecords = filterByWindow(
|
|
827
|
+
executionRecordsRaw.filter(item => item && item.execution_id && item.result),
|
|
828
|
+
['executed_at', 'timestamp'],
|
|
829
|
+
window
|
|
830
|
+
);
|
|
831
|
+
|
|
832
|
+
const feedbackEvents = filterByWindow(
|
|
833
|
+
feedbackRaw,
|
|
834
|
+
['timestamp', 'created_at', 'submitted_at'],
|
|
835
|
+
window
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
const matrixSignals = filterByWindow(
|
|
839
|
+
matrixSignalsRaw.filter(item => item && item.matrix && typeof item.matrix === 'object'),
|
|
840
|
+
['generated_at', 'timestamp', 'created_at'],
|
|
841
|
+
window
|
|
842
|
+
);
|
|
843
|
+
const dialogueAuthorizationSignals = filterByWindow(
|
|
844
|
+
dialogueAuthorizationSignalsRaw.filter(item => item && item.decision),
|
|
845
|
+
['timestamp', 'evaluated_at', 'generated_at', 'created_at'],
|
|
846
|
+
window
|
|
847
|
+
);
|
|
848
|
+
const runtimeSignals = filterByWindow(
|
|
849
|
+
runtimeSignalsRaw.filter(item => item && item.decision),
|
|
850
|
+
['timestamp', 'evaluated_at', 'generated_at', 'created_at'],
|
|
851
|
+
window
|
|
852
|
+
);
|
|
853
|
+
const authorizationTierSignals = filterByWindow(
|
|
854
|
+
authorizationTierSignalsRaw.filter(item => item && item.decision),
|
|
855
|
+
['timestamp', 'evaluated_at', 'generated_at', 'created_at'],
|
|
856
|
+
window
|
|
857
|
+
);
|
|
858
|
+
|
|
859
|
+
const applyRecords = executionRecords.filter(item =>
|
|
860
|
+
['success', 'failed', 'skipped'].includes(`${item.result || ''}`.trim().toLowerCase())
|
|
861
|
+
);
|
|
862
|
+
const rollbackRecords = executionRecords.filter(item =>
|
|
863
|
+
`${item.result || ''}`.trim().toLowerCase() === 'rolled-back'
|
|
864
|
+
);
|
|
865
|
+
|
|
866
|
+
const successApplyCount = applyRecords.filter(item =>
|
|
867
|
+
`${item.result || ''}`.trim().toLowerCase() === 'success'
|
|
868
|
+
).length;
|
|
869
|
+
const failedApplyCount = applyRecords.filter(item =>
|
|
870
|
+
`${item.result || ''}`.trim().toLowerCase() === 'failed'
|
|
871
|
+
).length;
|
|
872
|
+
const skippedApplyCount = applyRecords.filter(item =>
|
|
873
|
+
`${item.result || ''}`.trim().toLowerCase() === 'skipped'
|
|
874
|
+
).length;
|
|
875
|
+
const executedApplyCount = successApplyCount + failedApplyCount;
|
|
876
|
+
const securityInterceptCount = applyRecords.filter((item) => {
|
|
877
|
+
const decision = `${item.policy_decision || ''}`.trim().toLowerCase();
|
|
878
|
+
const result = `${item.result || ''}`.trim().toLowerCase();
|
|
879
|
+
return decision !== 'allow' || result === 'skipped';
|
|
880
|
+
}).length;
|
|
881
|
+
|
|
882
|
+
const feedbackScores = feedbackEvents
|
|
883
|
+
.map(parseFeedbackScore)
|
|
884
|
+
.filter(value => Number.isFinite(value) && value >= 0);
|
|
885
|
+
const matrixScoreValues = matrixSignals
|
|
886
|
+
.map((item) => Number(item && item.matrix ? item.matrix.avg_score : null))
|
|
887
|
+
.filter(value => Number.isFinite(value));
|
|
888
|
+
const matrixValidRateValues = matrixSignals
|
|
889
|
+
.map((item) => Number(item && item.matrix ? item.matrix.valid_rate_percent : null))
|
|
890
|
+
.filter(value => Number.isFinite(value));
|
|
891
|
+
const matrixPortfolioPassedCount = matrixSignals.filter((item) =>
|
|
892
|
+
item && item.matrix && item.matrix.portfolio_passed === true
|
|
893
|
+
).length;
|
|
894
|
+
const matrixRegressionPositiveCount = matrixSignals.filter((item) =>
|
|
895
|
+
Number(item && item.matrix ? item.matrix.regression_count : 0) > 0
|
|
896
|
+
).length;
|
|
897
|
+
const matrixStageErrorCount = matrixSignals.filter((item) => {
|
|
898
|
+
const status = `${item && item.matrix ? item.matrix.stage_status || '' : ''}`.trim().toLowerCase();
|
|
899
|
+
return status === 'error' || status === 'non-zero-exit';
|
|
900
|
+
}).length;
|
|
901
|
+
const dialogueAuthorizationDenyCount = dialogueAuthorizationSignals.filter((item) =>
|
|
902
|
+
`${item && item.decision ? item.decision : ''}`.trim().toLowerCase() === 'deny'
|
|
903
|
+
).length;
|
|
904
|
+
const dialogueAuthorizationReviewRequiredCount = dialogueAuthorizationSignals.filter((item) =>
|
|
905
|
+
`${item && item.decision ? item.decision : ''}`.trim().toLowerCase() === 'review-required'
|
|
906
|
+
).length;
|
|
907
|
+
const dialogueAuthorizationAllowCount = dialogueAuthorizationSignals.filter((item) =>
|
|
908
|
+
`${item && item.decision ? item.decision : ''}`.trim().toLowerCase() === 'allow'
|
|
909
|
+
).length;
|
|
910
|
+
const dialogueAuthorizationBlockCount = dialogueAuthorizationDenyCount + dialogueAuthorizationReviewRequiredCount;
|
|
911
|
+
const dialogueAuthorizationUserAppApplyAttemptCount = dialogueAuthorizationSignals.filter((item) =>
|
|
912
|
+
`${item && item.ui_mode ? item.ui_mode : ''}`.trim().toLowerCase() === 'user-app' &&
|
|
913
|
+
`${item && item.execution_mode ? item.execution_mode : ''}`.trim().toLowerCase() === 'apply'
|
|
914
|
+
).length;
|
|
915
|
+
const runtimeDenyCount = runtimeSignals.filter((item) =>
|
|
916
|
+
`${item && item.decision ? item.decision : ''}`.trim().toLowerCase() === 'deny'
|
|
917
|
+
).length;
|
|
918
|
+
const runtimeReviewRequiredCount = runtimeSignals.filter((item) =>
|
|
919
|
+
`${item && item.decision ? item.decision : ''}`.trim().toLowerCase() === 'review-required'
|
|
920
|
+
).length;
|
|
921
|
+
const runtimeAllowCount = runtimeSignals.filter((item) =>
|
|
922
|
+
`${item && item.decision ? item.decision : ''}`.trim().toLowerCase() === 'allow'
|
|
923
|
+
).length;
|
|
924
|
+
const runtimeBlockCount = runtimeDenyCount + runtimeReviewRequiredCount;
|
|
925
|
+
const runtimeUiModeViolationCount = runtimeSignals.filter((item) => {
|
|
926
|
+
if (item && item.ui_mode_violation === true) {
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
929
|
+
const codes = Array.isArray(item && item.violation_codes)
|
|
930
|
+
? item.violation_codes
|
|
931
|
+
: [];
|
|
932
|
+
return codes.some(code => `${code || ''}`.trim().toLowerCase().startsWith('ui-mode-'));
|
|
933
|
+
}).length;
|
|
934
|
+
const authorizationTierDenyCount = authorizationTierSignals.filter((item) =>
|
|
935
|
+
`${item && item.decision ? item.decision : ''}`.trim().toLowerCase() === 'deny'
|
|
936
|
+
).length;
|
|
937
|
+
const authorizationTierReviewRequiredCount = authorizationTierSignals.filter((item) =>
|
|
938
|
+
`${item && item.decision ? item.decision : ''}`.trim().toLowerCase() === 'review-required'
|
|
939
|
+
).length;
|
|
940
|
+
const authorizationTierAllowCount = authorizationTierSignals.filter((item) =>
|
|
941
|
+
`${item && item.decision ? item.decision : ''}`.trim().toLowerCase() === 'allow'
|
|
942
|
+
).length;
|
|
943
|
+
const authorizationTierBlockCount = authorizationTierDenyCount + authorizationTierReviewRequiredCount;
|
|
944
|
+
const dialogueAuthorizationBusinessModeBreakdown = buildBusinessModeBreakdown(dialogueAuthorizationSignals);
|
|
945
|
+
const runtimeBusinessModeBreakdown = buildBusinessModeBreakdown(runtimeSignals);
|
|
946
|
+
const authorizationTierBusinessModeBreakdown = buildBusinessModeBreakdown(authorizationTierSignals);
|
|
947
|
+
const businessModeUnknownSignalTotal =
|
|
948
|
+
dialogueAuthorizationBusinessModeBreakdown.unknown_total +
|
|
949
|
+
runtimeBusinessModeBreakdown.unknown_total +
|
|
950
|
+
authorizationTierBusinessModeBreakdown.unknown_total;
|
|
951
|
+
|
|
952
|
+
const approvalSubmittedCount = approvalEvents.filter(item =>
|
|
953
|
+
`${item.action || ''}`.trim().toLowerCase() === 'submit' && item.blocked !== true
|
|
954
|
+
).length;
|
|
955
|
+
const approvalApprovedCount = approvalEvents.filter(item =>
|
|
956
|
+
`${item.action || ''}`.trim().toLowerCase() === 'approve' && item.blocked !== true
|
|
957
|
+
).length;
|
|
958
|
+
const approvalRejectedCount = approvalEvents.filter(item =>
|
|
959
|
+
`${item.action || ''}`.trim().toLowerCase() === 'reject' && item.blocked !== true
|
|
960
|
+
).length;
|
|
961
|
+
|
|
962
|
+
const metrics = {
|
|
963
|
+
intent_total: intentEvents.length,
|
|
964
|
+
apply_total: applyRecords.length,
|
|
965
|
+
apply_success_total: successApplyCount,
|
|
966
|
+
apply_failed_total: failedApplyCount,
|
|
967
|
+
apply_skipped_total: skippedApplyCount,
|
|
968
|
+
rollback_total: rollbackRecords.length,
|
|
969
|
+
security_intercept_total: securityInterceptCount,
|
|
970
|
+
approval_submitted_total: approvalSubmittedCount,
|
|
971
|
+
approval_approved_total: approvalApprovedCount,
|
|
972
|
+
approval_rejected_total: approvalRejectedCount,
|
|
973
|
+
adoption_rate_percent: toRatePercent(successApplyCount, intentEvents.length),
|
|
974
|
+
execution_success_rate_percent: toRatePercent(successApplyCount, executedApplyCount),
|
|
975
|
+
rollback_rate_percent: toRatePercent(rollbackRecords.length, successApplyCount),
|
|
976
|
+
security_intercept_rate_percent: toRatePercent(securityInterceptCount, applyRecords.length),
|
|
977
|
+
satisfaction_avg_score: toAverage(feedbackScores),
|
|
978
|
+
satisfaction_response_count: feedbackScores.length,
|
|
979
|
+
matrix_signal_total: matrixSignals.length,
|
|
980
|
+
dialogue_authorization_total: dialogueAuthorizationSignals.length,
|
|
981
|
+
dialogue_authorization_allow_total: dialogueAuthorizationAllowCount,
|
|
982
|
+
dialogue_authorization_deny_total: dialogueAuthorizationDenyCount,
|
|
983
|
+
dialogue_authorization_review_required_total: dialogueAuthorizationReviewRequiredCount,
|
|
984
|
+
dialogue_authorization_block_total: dialogueAuthorizationBlockCount,
|
|
985
|
+
dialogue_authorization_user_app_apply_attempt_total: dialogueAuthorizationUserAppApplyAttemptCount,
|
|
986
|
+
dialogue_authorization_unknown_business_mode_total: dialogueAuthorizationBusinessModeBreakdown.unknown_total,
|
|
987
|
+
dialogue_authorization_business_mode_breakdown: dialogueAuthorizationBusinessModeBreakdown,
|
|
988
|
+
runtime_total: runtimeSignals.length,
|
|
989
|
+
runtime_allow_total: runtimeAllowCount,
|
|
990
|
+
runtime_deny_total: runtimeDenyCount,
|
|
991
|
+
runtime_review_required_total: runtimeReviewRequiredCount,
|
|
992
|
+
runtime_block_total: runtimeBlockCount,
|
|
993
|
+
runtime_ui_mode_violation_total: runtimeUiModeViolationCount,
|
|
994
|
+
runtime_unknown_business_mode_total: runtimeBusinessModeBreakdown.unknown_total,
|
|
995
|
+
runtime_business_mode_breakdown: runtimeBusinessModeBreakdown,
|
|
996
|
+
authorization_tier_total: authorizationTierSignals.length,
|
|
997
|
+
authorization_tier_allow_total: authorizationTierAllowCount,
|
|
998
|
+
authorization_tier_deny_total: authorizationTierDenyCount,
|
|
999
|
+
authorization_tier_review_required_total: authorizationTierReviewRequiredCount,
|
|
1000
|
+
authorization_tier_block_total: authorizationTierBlockCount,
|
|
1001
|
+
authorization_tier_unknown_business_mode_total: authorizationTierBusinessModeBreakdown.unknown_total,
|
|
1002
|
+
authorization_tier_business_mode_breakdown: authorizationTierBusinessModeBreakdown,
|
|
1003
|
+
business_mode_unknown_signal_total: businessModeUnknownSignalTotal,
|
|
1004
|
+
matrix_portfolio_pass_total: matrixPortfolioPassedCount,
|
|
1005
|
+
matrix_regression_positive_total: matrixRegressionPositiveCount,
|
|
1006
|
+
matrix_stage_error_total: matrixStageErrorCount,
|
|
1007
|
+
matrix_portfolio_pass_rate_percent: toRatePercent(matrixPortfolioPassedCount, matrixSignals.length),
|
|
1008
|
+
matrix_regression_positive_rate_percent: toRatePercent(matrixRegressionPositiveCount, matrixSignals.length),
|
|
1009
|
+
matrix_stage_error_rate_percent: toRatePercent(matrixStageErrorCount, matrixSignals.length),
|
|
1010
|
+
dialogue_authorization_block_rate_percent: toRatePercent(dialogueAuthorizationBlockCount, dialogueAuthorizationSignals.length),
|
|
1011
|
+
runtime_block_rate_percent: toRatePercent(runtimeBlockCount, runtimeSignals.length),
|
|
1012
|
+
runtime_ui_mode_violation_rate_percent: toRatePercent(runtimeUiModeViolationCount, runtimeSignals.length),
|
|
1013
|
+
authorization_tier_block_rate_percent: toRatePercent(authorizationTierBlockCount, authorizationTierSignals.length),
|
|
1014
|
+
matrix_avg_score: toAverage(matrixScoreValues),
|
|
1015
|
+
matrix_avg_valid_rate_percent: toAverage(matrixValidRateValues)
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
const alerts = evaluateAlerts(metrics, thresholds);
|
|
1019
|
+
const recommendations = buildRecommendations(alerts);
|
|
1020
|
+
const breachCount = alerts.filter(item => item.status === 'breach').length;
|
|
1021
|
+
const warningCount = alerts.filter(item => item.status === 'warning').length;
|
|
1022
|
+
|
|
1023
|
+
const report = {
|
|
1024
|
+
mode: 'interactive-governance-report',
|
|
1025
|
+
generated_at: new Date().toISOString(),
|
|
1026
|
+
period: {
|
|
1027
|
+
type: window.period,
|
|
1028
|
+
from: window.from ? window.from.toISOString() : null,
|
|
1029
|
+
to: window.to ? window.to.toISOString() : null
|
|
1030
|
+
},
|
|
1031
|
+
inputs: {
|
|
1032
|
+
intent_audit: path.relative(cwd, intentAuditPath) || '.',
|
|
1033
|
+
approval_audit: path.relative(cwd, approvalAuditPath) || '.',
|
|
1034
|
+
execution_ledger: path.relative(cwd, executionLedgerPath) || '.',
|
|
1035
|
+
feedback_file: path.relative(cwd, feedbackFilePath) || '.',
|
|
1036
|
+
matrix_signals: path.relative(cwd, matrixSignalsPath) || '.',
|
|
1037
|
+
dialogue_authorization_signals: path.relative(cwd, dialogueAuthorizationSignalsPath) || '.',
|
|
1038
|
+
runtime_signals: path.relative(cwd, runtimeSignalsPath) || '.',
|
|
1039
|
+
authorization_tier_signals: path.relative(cwd, authorizationTierSignalsPath) || '.',
|
|
1040
|
+
thresholds: path.relative(cwd, thresholdsPath) || '.'
|
|
1041
|
+
},
|
|
1042
|
+
thresholds,
|
|
1043
|
+
metrics,
|
|
1044
|
+
summary: {
|
|
1045
|
+
breaches: breachCount,
|
|
1046
|
+
warnings: warningCount,
|
|
1047
|
+
status: breachCount > 0 ? 'alert' : 'ok'
|
|
1048
|
+
},
|
|
1049
|
+
alerts,
|
|
1050
|
+
recommendations,
|
|
1051
|
+
output: {
|
|
1052
|
+
json: path.relative(cwd, outPath) || '.',
|
|
1053
|
+
markdown: path.relative(cwd, markdownOutPath) || '.'
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
await fs.ensureDir(path.dirname(outPath));
|
|
1058
|
+
await fs.writeJson(outPath, report, { spaces: 2 });
|
|
1059
|
+
await fs.ensureDir(path.dirname(markdownOutPath));
|
|
1060
|
+
await fs.writeFile(markdownOutPath, buildMarkdown(report), 'utf8');
|
|
1061
|
+
|
|
1062
|
+
if (options.json) {
|
|
1063
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
1064
|
+
} else {
|
|
1065
|
+
process.stdout.write(`Interactive governance report generated: ${report.summary.status}\n`);
|
|
1066
|
+
process.stdout.write(`- JSON: ${report.output.json}\n`);
|
|
1067
|
+
process.stdout.write(`- Markdown: ${report.output.markdown}\n`);
|
|
1068
|
+
process.stdout.write(`- Breaches: ${report.summary.breaches}\n`);
|
|
1069
|
+
process.stdout.write(`- Warnings: ${report.summary.warnings}\n`);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
if (
|
|
1073
|
+
options.failOnAlert &&
|
|
1074
|
+
alerts.some(item => item.status === 'breach' && ['medium', 'high'].includes(item.severity))
|
|
1075
|
+
) {
|
|
1076
|
+
process.exitCode = 2;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
if (require.main === module) {
|
|
1081
|
+
main().catch((error) => {
|
|
1082
|
+
console.error(`Interactive governance report failed: ${error.message}`);
|
|
1083
|
+
process.exit(1);
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
module.exports = {
|
|
1088
|
+
DEFAULT_INTENT_AUDIT,
|
|
1089
|
+
DEFAULT_APPROVAL_AUDIT,
|
|
1090
|
+
DEFAULT_EXECUTION_LEDGER,
|
|
1091
|
+
DEFAULT_FEEDBACK_FILE,
|
|
1092
|
+
DEFAULT_MATRIX_SIGNALS,
|
|
1093
|
+
DEFAULT_DIALOGUE_AUTHORIZATION_SIGNALS,
|
|
1094
|
+
DEFAULT_RUNTIME_SIGNALS,
|
|
1095
|
+
DEFAULT_AUTHORIZATION_TIER_SIGNALS,
|
|
1096
|
+
DEFAULT_THRESHOLDS,
|
|
1097
|
+
DEFAULT_OUT,
|
|
1098
|
+
DEFAULT_MARKDOWN_OUT,
|
|
1099
|
+
parseArgs,
|
|
1100
|
+
buildPeriodWindow,
|
|
1101
|
+
readJsonLinesFile,
|
|
1102
|
+
filterByWindow,
|
|
1103
|
+
toRatePercent,
|
|
1104
|
+
toAverage,
|
|
1105
|
+
normalizeBusinessMode,
|
|
1106
|
+
buildBusinessModeBreakdown,
|
|
1107
|
+
loadThresholds,
|
|
1108
|
+
evaluateAlerts,
|
|
1109
|
+
parseFeedbackScore,
|
|
1110
|
+
buildMarkdown,
|
|
1111
|
+
main
|
|
1112
|
+
};
|