scene-capability-engine 3.6.45 → 3.6.47
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 +22 -0
- package/README.md +1 -0
- package/README.zh.md +1 -0
- package/docs/agent-runtime/symbol-evidence.schema.json +1 -1
- package/docs/command-reference.md +8 -0
- package/docs/interactive-customization/dialogue-governance-policy-baseline.json +4 -1
- package/docs/interactive-customization/embedded-assistant-authorization-dialogue-rules.md +5 -0
- package/docs/releases/README.md +2 -0
- package/docs/releases/v3.6.46.md +23 -0
- package/docs/releases/v3.6.47.md +23 -0
- package/docs/sce-business-mode-map.md +2 -1
- package/docs/sce-capability-matrix-e2e-example.md +2 -1
- package/docs/security-governance-default-baseline.md +2 -0
- package/docs/starter-kit/README.md +3 -0
- package/docs/zh/releases/README.md +2 -0
- package/docs/zh/releases/v3.6.46.md +23 -0
- package/docs/zh/releases/v3.6.47.md +23 -0
- package/lib/workspace/takeover-baseline.js +293 -1
- package/package.json +6 -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/clarification-first-audit.js +322 -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 +873 -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 +370 -0
- package/template/.sce/README.md +1 -0
- package/template/.sce/steering/CORE_PRINCIPLES.md +25 -0
|
@@ -0,0 +1,934 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
|
|
7
|
+
const DEFAULT_EVIDENCE = '.sce/reports/release-evidence/handoff-runs.json';
|
|
8
|
+
const DEFAULT_GATE_HISTORY = '.sce/reports/release-evidence/release-gate-history.json';
|
|
9
|
+
const DEFAULT_INTERACTIVE_GOVERNANCE = '.sce/reports/interactive-governance-report.json';
|
|
10
|
+
const DEFAULT_MATRIX_SIGNALS = '.sce/reports/interactive-matrix-signals.jsonl';
|
|
11
|
+
const DEFAULT_OUT = '.sce/reports/release-evidence/weekly-ops-summary.json';
|
|
12
|
+
const DEFAULT_MARKDOWN_OUT = '.sce/reports/release-evidence/weekly-ops-summary.md';
|
|
13
|
+
|
|
14
|
+
function parseArgs(argv) {
|
|
15
|
+
const options = {
|
|
16
|
+
evidence: DEFAULT_EVIDENCE,
|
|
17
|
+
gateHistory: DEFAULT_GATE_HISTORY,
|
|
18
|
+
interactiveGovernance: DEFAULT_INTERACTIVE_GOVERNANCE,
|
|
19
|
+
matrixSignals: DEFAULT_MATRIX_SIGNALS,
|
|
20
|
+
out: DEFAULT_OUT,
|
|
21
|
+
markdownOut: DEFAULT_MARKDOWN_OUT,
|
|
22
|
+
from: null,
|
|
23
|
+
to: null,
|
|
24
|
+
windowDays: 7,
|
|
25
|
+
json: false
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
29
|
+
const token = argv[i];
|
|
30
|
+
const next = argv[i + 1];
|
|
31
|
+
if (token === '--evidence' && next) {
|
|
32
|
+
options.evidence = next;
|
|
33
|
+
i += 1;
|
|
34
|
+
} else if (token === '--gate-history' && next) {
|
|
35
|
+
options.gateHistory = next;
|
|
36
|
+
i += 1;
|
|
37
|
+
} else if (token === '--interactive-governance' && next) {
|
|
38
|
+
options.interactiveGovernance = next;
|
|
39
|
+
i += 1;
|
|
40
|
+
} else if (token === '--matrix-signals' && next) {
|
|
41
|
+
options.matrixSignals = next;
|
|
42
|
+
i += 1;
|
|
43
|
+
} else if (token === '--out' && next) {
|
|
44
|
+
options.out = next;
|
|
45
|
+
i += 1;
|
|
46
|
+
} else if (token === '--markdown-out' && next) {
|
|
47
|
+
options.markdownOut = next;
|
|
48
|
+
i += 1;
|
|
49
|
+
} else if (token === '--from' && next) {
|
|
50
|
+
options.from = next;
|
|
51
|
+
i += 1;
|
|
52
|
+
} else if (token === '--to' && next) {
|
|
53
|
+
options.to = next;
|
|
54
|
+
i += 1;
|
|
55
|
+
} else if (token === '--window-days' && next) {
|
|
56
|
+
options.windowDays = Number(next);
|
|
57
|
+
i += 1;
|
|
58
|
+
} else if (token === '--json') {
|
|
59
|
+
options.json = true;
|
|
60
|
+
} else if (token === '--help' || token === '-h') {
|
|
61
|
+
printHelpAndExit(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const hasFrom = Boolean(typeof options.from === 'string' && options.from.trim());
|
|
66
|
+
const hasTo = Boolean(typeof options.to === 'string' && options.to.trim());
|
|
67
|
+
if (hasFrom !== hasTo) {
|
|
68
|
+
throw new Error('--from and --to must be provided together.');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!Number.isFinite(options.windowDays) || options.windowDays < 1 || options.windowDays > 90) {
|
|
72
|
+
throw new Error('--window-days must be an integer between 1 and 90.');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return options;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function printHelpAndExit(code) {
|
|
79
|
+
const lines = [
|
|
80
|
+
'Usage: node scripts/release-ops-weekly-summary.js [options]',
|
|
81
|
+
'',
|
|
82
|
+
'Options:',
|
|
83
|
+
` --evidence <path> Handoff evidence JSON (default: ${DEFAULT_EVIDENCE})`,
|
|
84
|
+
` --gate-history <path> Release gate history JSON (default: ${DEFAULT_GATE_HISTORY})`,
|
|
85
|
+
` --interactive-governance <path> Interactive governance JSON (default: ${DEFAULT_INTERACTIVE_GOVERNANCE})`,
|
|
86
|
+
` --matrix-signals <path> Interactive matrix signals JSONL (default: ${DEFAULT_MATRIX_SIGNALS})`,
|
|
87
|
+
` --out <path> JSON output path (default: ${DEFAULT_OUT})`,
|
|
88
|
+
` --markdown-out <path> Markdown output path (default: ${DEFAULT_MARKDOWN_OUT})`,
|
|
89
|
+
' --from <ISO datetime> Window start (requires --to)',
|
|
90
|
+
' --to <ISO datetime> Window end (requires --from)',
|
|
91
|
+
' --window-days <n> Default rolling window in days (1-90, default: 7)',
|
|
92
|
+
' --json Print report JSON payload',
|
|
93
|
+
' -h, --help Show this help'
|
|
94
|
+
];
|
|
95
|
+
console.log(lines.join('\n'));
|
|
96
|
+
process.exit(code);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function resolvePath(cwd, value) {
|
|
100
|
+
return path.isAbsolute(value) ? value : path.resolve(cwd, value);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function parseIsoDate(value, label) {
|
|
104
|
+
const date = new Date(value);
|
|
105
|
+
if (Number.isNaN(date.getTime())) {
|
|
106
|
+
throw new Error(`invalid date for ${label}: ${value}`);
|
|
107
|
+
}
|
|
108
|
+
return date;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildWindow(options, now = new Date()) {
|
|
112
|
+
if (options.from && options.to) {
|
|
113
|
+
const from = parseIsoDate(options.from, '--from');
|
|
114
|
+
const to = parseIsoDate(options.to, '--to');
|
|
115
|
+
if (from.getTime() > to.getTime()) {
|
|
116
|
+
throw new Error('--from must be <= --to.');
|
|
117
|
+
}
|
|
118
|
+
return { from, to, mode: 'custom' };
|
|
119
|
+
}
|
|
120
|
+
const to = new Date(now.getTime());
|
|
121
|
+
const from = new Date(now.getTime());
|
|
122
|
+
from.setUTCDate(from.getUTCDate() - options.windowDays);
|
|
123
|
+
return { from, to, mode: 'rolling' };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function safeReadJson(cwd, candidatePath) {
|
|
127
|
+
const absolutePath = resolvePath(cwd, candidatePath);
|
|
128
|
+
const relativePath = path.relative(cwd, absolutePath) || '.';
|
|
129
|
+
const exists = await fs.pathExists(absolutePath);
|
|
130
|
+
if (!exists) {
|
|
131
|
+
return {
|
|
132
|
+
path: relativePath,
|
|
133
|
+
exists: false,
|
|
134
|
+
parse_error: null,
|
|
135
|
+
payload: null
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
const payload = await fs.readJson(absolutePath);
|
|
141
|
+
return {
|
|
142
|
+
path: relativePath,
|
|
143
|
+
exists: true,
|
|
144
|
+
parse_error: null,
|
|
145
|
+
payload
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
return {
|
|
149
|
+
path: relativePath,
|
|
150
|
+
exists: true,
|
|
151
|
+
parse_error: error.message,
|
|
152
|
+
payload: null
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function safeReadJsonLines(cwd, candidatePath) {
|
|
158
|
+
const absolutePath = resolvePath(cwd, candidatePath);
|
|
159
|
+
const relativePath = path.relative(cwd, absolutePath) || '.';
|
|
160
|
+
const exists = await fs.pathExists(absolutePath);
|
|
161
|
+
if (!exists) {
|
|
162
|
+
return {
|
|
163
|
+
path: relativePath,
|
|
164
|
+
exists: false,
|
|
165
|
+
parse_error: null,
|
|
166
|
+
records: []
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
const content = await fs.readFile(absolutePath, 'utf8');
|
|
172
|
+
const records = `${content || ''}`
|
|
173
|
+
.split(/\r?\n/)
|
|
174
|
+
.map(line => line.trim())
|
|
175
|
+
.filter(Boolean)
|
|
176
|
+
.map((line) => {
|
|
177
|
+
try {
|
|
178
|
+
return JSON.parse(line);
|
|
179
|
+
} catch (_error) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
.filter(Boolean);
|
|
184
|
+
return {
|
|
185
|
+
path: relativePath,
|
|
186
|
+
exists: true,
|
|
187
|
+
parse_error: null,
|
|
188
|
+
records
|
|
189
|
+
};
|
|
190
|
+
} catch (error) {
|
|
191
|
+
return {
|
|
192
|
+
path: relativePath,
|
|
193
|
+
exists: true,
|
|
194
|
+
parse_error: error.message,
|
|
195
|
+
records: []
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function parseTimestamp(entry, fields = []) {
|
|
201
|
+
for (const key of fields) {
|
|
202
|
+
const value = entry && entry[key];
|
|
203
|
+
if (!value) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const parsed = Date.parse(value);
|
|
207
|
+
if (Number.isFinite(parsed)) {
|
|
208
|
+
return parsed;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function filterByWindow(entries, window, fields, includeUnknown = false) {
|
|
215
|
+
const fromMs = window.from.getTime();
|
|
216
|
+
const toMs = window.to.getTime();
|
|
217
|
+
return entries.filter((entry) => {
|
|
218
|
+
const ts = parseTimestamp(entry, fields);
|
|
219
|
+
if (ts === null) {
|
|
220
|
+
return includeUnknown;
|
|
221
|
+
}
|
|
222
|
+
return ts >= fromMs && ts <= toMs;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function toPercent(numerator, denominator) {
|
|
227
|
+
const total = Number(denominator);
|
|
228
|
+
if (!Number.isFinite(total) || total <= 0) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
return Number(((Number(numerator) / total) * 100).toFixed(2));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function toAverage(values = []) {
|
|
235
|
+
const nums = values
|
|
236
|
+
.map(value => Number(value))
|
|
237
|
+
.filter(value => Number.isFinite(value));
|
|
238
|
+
if (nums.length === 0) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
return Number((nums.reduce((sum, value) => sum + value, 0) / nums.length).toFixed(2));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function buildHandoffSnapshot(payload, window) {
|
|
245
|
+
const sessions = Array.isArray(payload && payload.sessions) ? payload.sessions : [];
|
|
246
|
+
const scoped = filterByWindow(
|
|
247
|
+
sessions,
|
|
248
|
+
window,
|
|
249
|
+
['merged_at', 'generated_at', 'updated_at'],
|
|
250
|
+
false
|
|
251
|
+
);
|
|
252
|
+
const statusCounts = {
|
|
253
|
+
completed: 0,
|
|
254
|
+
failed: 0,
|
|
255
|
+
dry_run: 0,
|
|
256
|
+
running: 0,
|
|
257
|
+
other: 0
|
|
258
|
+
};
|
|
259
|
+
const risk = {
|
|
260
|
+
low: 0,
|
|
261
|
+
medium: 0,
|
|
262
|
+
high: 0,
|
|
263
|
+
unknown: 0
|
|
264
|
+
};
|
|
265
|
+
let gateKnownRuns = 0;
|
|
266
|
+
let gatePassedRuns = 0;
|
|
267
|
+
let sceneKnownRuns = 0;
|
|
268
|
+
let scenePassedRuns = 0;
|
|
269
|
+
let preflightKnownRuns = 0;
|
|
270
|
+
let preflightBlockedRuns = 0;
|
|
271
|
+
const specRateValues = [];
|
|
272
|
+
const expectedUnknownKnown = [];
|
|
273
|
+
const providedUnknownKnown = [];
|
|
274
|
+
|
|
275
|
+
for (const session of scoped) {
|
|
276
|
+
const status = `${session && session.status ? session.status : ''}`.trim().toLowerCase();
|
|
277
|
+
if (statusCounts[status] !== undefined) {
|
|
278
|
+
statusCounts[status] += 1;
|
|
279
|
+
} else {
|
|
280
|
+
statusCounts.other += 1;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const gate = session && session.gate && typeof session.gate === 'object' ? session.gate : {};
|
|
284
|
+
const gateActual = gate && gate.actual && typeof gate.actual === 'object' ? gate.actual : {};
|
|
285
|
+
if (gate.passed === true || gate.passed === false) {
|
|
286
|
+
gateKnownRuns += 1;
|
|
287
|
+
if (gate.passed === true) {
|
|
288
|
+
gatePassedRuns += 1;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const specRate = Number(gateActual.spec_success_rate_percent);
|
|
293
|
+
if (Number.isFinite(specRate)) {
|
|
294
|
+
specRateValues.push(specRate);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const riskLevel = `${gateActual.risk_level || 'unknown'}`.trim().toLowerCase();
|
|
298
|
+
if (risk[riskLevel] !== undefined) {
|
|
299
|
+
risk[riskLevel] += 1;
|
|
300
|
+
} else {
|
|
301
|
+
risk.unknown += 1;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const scenePackage = session && session.scene_package_batch && typeof session.scene_package_batch === 'object'
|
|
305
|
+
? session.scene_package_batch
|
|
306
|
+
: {};
|
|
307
|
+
const sceneSummary = scenePackage && scenePackage.summary && typeof scenePackage.summary === 'object'
|
|
308
|
+
? scenePackage.summary
|
|
309
|
+
: {};
|
|
310
|
+
const scenePassedCandidate = sceneSummary.batch_gate_passed;
|
|
311
|
+
if (scenePassedCandidate === true || scenePassedCandidate === false) {
|
|
312
|
+
sceneKnownRuns += 1;
|
|
313
|
+
if (scenePassedCandidate === true) {
|
|
314
|
+
scenePassedRuns += 1;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const preflight = session && session.release_gate_preflight && typeof session.release_gate_preflight === 'object'
|
|
319
|
+
? session.release_gate_preflight
|
|
320
|
+
: {};
|
|
321
|
+
if (preflight.blocked === true || preflight.blocked === false) {
|
|
322
|
+
preflightKnownRuns += 1;
|
|
323
|
+
if (preflight.blocked === true) {
|
|
324
|
+
preflightBlockedRuns += 1;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const expectedUnknown = Number(
|
|
329
|
+
gateActual.capability_expected_unknown_count !== undefined
|
|
330
|
+
? gateActual.capability_expected_unknown_count
|
|
331
|
+
: null
|
|
332
|
+
);
|
|
333
|
+
if (Number.isFinite(expectedUnknown)) {
|
|
334
|
+
expectedUnknownKnown.push(Math.max(0, expectedUnknown));
|
|
335
|
+
}
|
|
336
|
+
const providedUnknown = Number(
|
|
337
|
+
gateActual.capability_provided_unknown_count !== undefined
|
|
338
|
+
? gateActual.capability_provided_unknown_count
|
|
339
|
+
: null
|
|
340
|
+
);
|
|
341
|
+
if (Number.isFinite(providedUnknown)) {
|
|
342
|
+
providedUnknownKnown.push(Math.max(0, providedUnknown));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const expectedUnknownPositive = expectedUnknownKnown.filter(value => value > 0).length;
|
|
347
|
+
const providedUnknownPositive = providedUnknownKnown.filter(value => value > 0).length;
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
total_runs: scoped.length,
|
|
351
|
+
status_counts: statusCounts,
|
|
352
|
+
gate_passed_runs: gatePassedRuns,
|
|
353
|
+
gate_known_runs: gateKnownRuns,
|
|
354
|
+
gate_pass_rate_percent: toPercent(gatePassedRuns, gateKnownRuns),
|
|
355
|
+
avg_spec_success_rate_percent: toAverage(specRateValues),
|
|
356
|
+
risk_levels: risk,
|
|
357
|
+
scene_batch_passed_runs: scenePassedRuns,
|
|
358
|
+
scene_batch_known_runs: sceneKnownRuns,
|
|
359
|
+
scene_batch_pass_rate_percent: toPercent(scenePassedRuns, sceneKnownRuns),
|
|
360
|
+
release_preflight_blocked_runs: preflightBlockedRuns,
|
|
361
|
+
release_preflight_known_runs: preflightKnownRuns,
|
|
362
|
+
release_preflight_block_rate_percent: toPercent(preflightBlockedRuns, preflightKnownRuns),
|
|
363
|
+
capability_expected_unknown_positive_runs: expectedUnknownPositive,
|
|
364
|
+
capability_expected_unknown_known_runs: expectedUnknownKnown.length,
|
|
365
|
+
capability_expected_unknown_positive_rate_percent: toPercent(expectedUnknownPositive, expectedUnknownKnown.length),
|
|
366
|
+
capability_provided_unknown_positive_runs: providedUnknownPositive,
|
|
367
|
+
capability_provided_unknown_known_runs: providedUnknownKnown.length,
|
|
368
|
+
capability_provided_unknown_positive_rate_percent: toPercent(providedUnknownPositive, providedUnknownKnown.length)
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function buildReleaseGateHistorySnapshot(payload, window) {
|
|
373
|
+
const entries = Array.isArray(payload && payload.entries) ? payload.entries : [];
|
|
374
|
+
const scoped = filterByWindow(
|
|
375
|
+
entries,
|
|
376
|
+
window,
|
|
377
|
+
['evaluated_at'],
|
|
378
|
+
true
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
let gateKnown = 0;
|
|
382
|
+
let gatePassed = 0;
|
|
383
|
+
let preflightKnown = 0;
|
|
384
|
+
let preflightBlocked = 0;
|
|
385
|
+
let driftKnown = 0;
|
|
386
|
+
let driftPositive = 0;
|
|
387
|
+
const risk = {
|
|
388
|
+
low: 0,
|
|
389
|
+
medium: 0,
|
|
390
|
+
high: 0,
|
|
391
|
+
unknown: 0
|
|
392
|
+
};
|
|
393
|
+
const expectedUnknownKnown = [];
|
|
394
|
+
const providedUnknownKnown = [];
|
|
395
|
+
|
|
396
|
+
for (const entry of scoped) {
|
|
397
|
+
if (entry && (entry.gate_passed === true || entry.gate_passed === false)) {
|
|
398
|
+
gateKnown += 1;
|
|
399
|
+
if (entry.gate_passed === true) {
|
|
400
|
+
gatePassed += 1;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const riskLevel = `${entry && entry.risk_level ? entry.risk_level : 'unknown'}`.trim().toLowerCase();
|
|
405
|
+
if (risk[riskLevel] !== undefined) {
|
|
406
|
+
risk[riskLevel] += 1;
|
|
407
|
+
} else {
|
|
408
|
+
risk.unknown += 1;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (entry && (entry.release_gate_preflight_blocked === true || entry.release_gate_preflight_blocked === false)) {
|
|
412
|
+
preflightKnown += 1;
|
|
413
|
+
if (entry.release_gate_preflight_blocked === true) {
|
|
414
|
+
preflightBlocked += 1;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const driftAlertCount = Number(entry && entry.drift_alert_count);
|
|
419
|
+
if (Number.isFinite(driftAlertCount)) {
|
|
420
|
+
driftKnown += 1;
|
|
421
|
+
if (driftAlertCount > 0) {
|
|
422
|
+
driftPositive += 1;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const expectedUnknown = Number(entry && entry.capability_expected_unknown_count);
|
|
427
|
+
if (Number.isFinite(expectedUnknown)) {
|
|
428
|
+
expectedUnknownKnown.push(Math.max(0, expectedUnknown));
|
|
429
|
+
}
|
|
430
|
+
const providedUnknown = Number(entry && entry.capability_provided_unknown_count);
|
|
431
|
+
if (Number.isFinite(providedUnknown)) {
|
|
432
|
+
providedUnknownKnown.push(Math.max(0, providedUnknown));
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const expectedUnknownPositive = expectedUnknownKnown.filter(value => value > 0).length;
|
|
437
|
+
const providedUnknownPositive = providedUnknownKnown.filter(value => value > 0).length;
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
total_entries: scoped.length,
|
|
441
|
+
gate_passed_runs: gatePassed,
|
|
442
|
+
gate_known_runs: gateKnown,
|
|
443
|
+
gate_pass_rate_percent: toPercent(gatePassed, gateKnown),
|
|
444
|
+
risk_levels: risk,
|
|
445
|
+
release_preflight_blocked_runs: preflightBlocked,
|
|
446
|
+
release_preflight_known_runs: preflightKnown,
|
|
447
|
+
release_preflight_block_rate_percent: toPercent(preflightBlocked, preflightKnown),
|
|
448
|
+
drift_alert_positive_runs: driftPositive,
|
|
449
|
+
drift_alert_known_runs: driftKnown,
|
|
450
|
+
drift_alert_positive_rate_percent: toPercent(driftPositive, driftKnown),
|
|
451
|
+
capability_expected_unknown_positive_runs: expectedUnknownPositive,
|
|
452
|
+
capability_expected_unknown_known_runs: expectedUnknownKnown.length,
|
|
453
|
+
capability_expected_unknown_positive_rate_percent: toPercent(expectedUnknownPositive, expectedUnknownKnown.length),
|
|
454
|
+
capability_provided_unknown_positive_runs: providedUnknownPositive,
|
|
455
|
+
capability_provided_unknown_known_runs: providedUnknownKnown.length,
|
|
456
|
+
capability_provided_unknown_positive_rate_percent: toPercent(providedUnknownPositive, providedUnknownKnown.length)
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function buildInteractiveGovernanceSnapshot(payload, window) {
|
|
461
|
+
const generatedTs = parseTimestamp(payload, ['generated_at']);
|
|
462
|
+
const generatedAt = payload && payload.generated_at ? payload.generated_at : null;
|
|
463
|
+
const inWindow = generatedTs === null
|
|
464
|
+
? null
|
|
465
|
+
: (generatedTs >= window.from.getTime() && generatedTs <= window.to.getTime());
|
|
466
|
+
const summary = payload && payload.summary && typeof payload.summary === 'object'
|
|
467
|
+
? payload.summary
|
|
468
|
+
: {};
|
|
469
|
+
const metrics = payload && payload.metrics && typeof payload.metrics === 'object'
|
|
470
|
+
? payload.metrics
|
|
471
|
+
: {};
|
|
472
|
+
return {
|
|
473
|
+
generated_at: generatedAt,
|
|
474
|
+
in_window: inWindow,
|
|
475
|
+
status: typeof summary.status === 'string' ? summary.status : null,
|
|
476
|
+
breaches: Number.isFinite(Number(summary.breaches)) ? Number(summary.breaches) : null,
|
|
477
|
+
warnings: Number.isFinite(Number(summary.warnings)) ? Number(summary.warnings) : null,
|
|
478
|
+
authorization_tier_total: Number.isFinite(Number(metrics.authorization_tier_total))
|
|
479
|
+
? Number(metrics.authorization_tier_total)
|
|
480
|
+
: null,
|
|
481
|
+
authorization_tier_deny_total: Number.isFinite(Number(metrics.authorization_tier_deny_total))
|
|
482
|
+
? Number(metrics.authorization_tier_deny_total)
|
|
483
|
+
: null,
|
|
484
|
+
authorization_tier_review_required_total: Number.isFinite(Number(metrics.authorization_tier_review_required_total))
|
|
485
|
+
? Number(metrics.authorization_tier_review_required_total)
|
|
486
|
+
: null,
|
|
487
|
+
authorization_tier_block_rate_percent: Number.isFinite(Number(metrics.authorization_tier_block_rate_percent))
|
|
488
|
+
? Number(metrics.authorization_tier_block_rate_percent)
|
|
489
|
+
: null,
|
|
490
|
+
dialogue_authorization_total: Number.isFinite(Number(metrics.dialogue_authorization_total))
|
|
491
|
+
? Number(metrics.dialogue_authorization_total)
|
|
492
|
+
: null,
|
|
493
|
+
dialogue_authorization_block_total: Number.isFinite(Number(metrics.dialogue_authorization_block_total))
|
|
494
|
+
? Number(metrics.dialogue_authorization_block_total)
|
|
495
|
+
: null,
|
|
496
|
+
dialogue_authorization_block_rate_percent: Number.isFinite(Number(metrics.dialogue_authorization_block_rate_percent))
|
|
497
|
+
? Number(metrics.dialogue_authorization_block_rate_percent)
|
|
498
|
+
: null,
|
|
499
|
+
dialogue_authorization_user_app_apply_attempt_total: Number.isFinite(
|
|
500
|
+
Number(metrics.dialogue_authorization_user_app_apply_attempt_total)
|
|
501
|
+
)
|
|
502
|
+
? Number(metrics.dialogue_authorization_user_app_apply_attempt_total)
|
|
503
|
+
: null,
|
|
504
|
+
dialogue_authorization_unknown_business_mode_total: Number.isFinite(
|
|
505
|
+
Number(metrics.dialogue_authorization_unknown_business_mode_total)
|
|
506
|
+
)
|
|
507
|
+
? Number(metrics.dialogue_authorization_unknown_business_mode_total)
|
|
508
|
+
: null,
|
|
509
|
+
runtime_total: Number.isFinite(Number(metrics.runtime_total))
|
|
510
|
+
? Number(metrics.runtime_total)
|
|
511
|
+
: null,
|
|
512
|
+
runtime_deny_total: Number.isFinite(Number(metrics.runtime_deny_total))
|
|
513
|
+
? Number(metrics.runtime_deny_total)
|
|
514
|
+
: null,
|
|
515
|
+
runtime_review_required_total: Number.isFinite(Number(metrics.runtime_review_required_total))
|
|
516
|
+
? Number(metrics.runtime_review_required_total)
|
|
517
|
+
: null,
|
|
518
|
+
runtime_block_rate_percent: Number.isFinite(Number(metrics.runtime_block_rate_percent))
|
|
519
|
+
? Number(metrics.runtime_block_rate_percent)
|
|
520
|
+
: null,
|
|
521
|
+
runtime_ui_mode_violation_total: Number.isFinite(Number(metrics.runtime_ui_mode_violation_total))
|
|
522
|
+
? Number(metrics.runtime_ui_mode_violation_total)
|
|
523
|
+
: null,
|
|
524
|
+
runtime_ui_mode_violation_rate_percent: Number.isFinite(Number(metrics.runtime_ui_mode_violation_rate_percent))
|
|
525
|
+
? Number(metrics.runtime_ui_mode_violation_rate_percent)
|
|
526
|
+
: null,
|
|
527
|
+
runtime_unknown_business_mode_total: Number.isFinite(Number(metrics.runtime_unknown_business_mode_total))
|
|
528
|
+
? Number(metrics.runtime_unknown_business_mode_total)
|
|
529
|
+
: null,
|
|
530
|
+
authorization_tier_unknown_business_mode_total: Number.isFinite(
|
|
531
|
+
Number(metrics.authorization_tier_unknown_business_mode_total)
|
|
532
|
+
)
|
|
533
|
+
? Number(metrics.authorization_tier_unknown_business_mode_total)
|
|
534
|
+
: null,
|
|
535
|
+
business_mode_unknown_signal_total: Number.isFinite(Number(metrics.business_mode_unknown_signal_total))
|
|
536
|
+
? Number(metrics.business_mode_unknown_signal_total)
|
|
537
|
+
: null,
|
|
538
|
+
matrix_signal_total: Number.isFinite(Number(metrics.matrix_signal_total))
|
|
539
|
+
? Number(metrics.matrix_signal_total)
|
|
540
|
+
: null,
|
|
541
|
+
matrix_portfolio_pass_rate_percent: Number.isFinite(Number(metrics.matrix_portfolio_pass_rate_percent))
|
|
542
|
+
? Number(metrics.matrix_portfolio_pass_rate_percent)
|
|
543
|
+
: null,
|
|
544
|
+
matrix_regression_positive_rate_percent: Number.isFinite(Number(metrics.matrix_regression_positive_rate_percent))
|
|
545
|
+
? Number(metrics.matrix_regression_positive_rate_percent)
|
|
546
|
+
: null,
|
|
547
|
+
matrix_stage_error_rate_percent: Number.isFinite(Number(metrics.matrix_stage_error_rate_percent))
|
|
548
|
+
? Number(metrics.matrix_stage_error_rate_percent)
|
|
549
|
+
: null
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function buildMatrixSignalSnapshot(records, window) {
|
|
554
|
+
const scoped = filterByWindow(
|
|
555
|
+
records,
|
|
556
|
+
window,
|
|
557
|
+
['generated_at', 'timestamp', 'created_at'],
|
|
558
|
+
false
|
|
559
|
+
);
|
|
560
|
+
let portfolioPassed = 0;
|
|
561
|
+
let regressionPositive = 0;
|
|
562
|
+
let stageError = 0;
|
|
563
|
+
const scores = [];
|
|
564
|
+
|
|
565
|
+
for (const entry of scoped) {
|
|
566
|
+
const matrix = entry && entry.matrix && typeof entry.matrix === 'object'
|
|
567
|
+
? entry.matrix
|
|
568
|
+
: null;
|
|
569
|
+
if (!matrix) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
if (matrix.portfolio_passed === true) {
|
|
573
|
+
portfolioPassed += 1;
|
|
574
|
+
}
|
|
575
|
+
if (Number(matrix.regression_count) > 0) {
|
|
576
|
+
regressionPositive += 1;
|
|
577
|
+
}
|
|
578
|
+
const stageStatus = `${matrix.stage_status || ''}`.trim().toLowerCase();
|
|
579
|
+
if (stageStatus === 'error' || stageStatus === 'non-zero-exit') {
|
|
580
|
+
stageError += 1;
|
|
581
|
+
}
|
|
582
|
+
const score = Number(matrix.avg_score);
|
|
583
|
+
if (Number.isFinite(score)) {
|
|
584
|
+
scores.push(score);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return {
|
|
589
|
+
total_signals: scoped.length,
|
|
590
|
+
portfolio_passed_signals: portfolioPassed,
|
|
591
|
+
portfolio_pass_rate_percent: toPercent(portfolioPassed, scoped.length),
|
|
592
|
+
regression_positive_signals: regressionPositive,
|
|
593
|
+
regression_positive_rate_percent: toPercent(regressionPositive, scoped.length),
|
|
594
|
+
stage_error_signals: stageError,
|
|
595
|
+
stage_error_rate_percent: toPercent(stageError, scoped.length),
|
|
596
|
+
avg_score: toAverage(scores)
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function promoteRisk(current, target) {
|
|
601
|
+
const rank = {
|
|
602
|
+
low: 1,
|
|
603
|
+
medium: 2,
|
|
604
|
+
high: 3
|
|
605
|
+
};
|
|
606
|
+
return rank[target] > rank[current] ? target : current;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function buildHealth(snapshots, warnings) {
|
|
610
|
+
let risk = 'low';
|
|
611
|
+
const concerns = [];
|
|
612
|
+
const recommendations = [];
|
|
613
|
+
const seen = new Set();
|
|
614
|
+
const pushConcern = (value) => {
|
|
615
|
+
const text = `${value || ''}`.trim();
|
|
616
|
+
if (!text || seen.has(`c:${text}`)) {
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
seen.add(`c:${text}`);
|
|
620
|
+
concerns.push(text);
|
|
621
|
+
};
|
|
622
|
+
const pushRecommendation = (value) => {
|
|
623
|
+
const text = `${value || ''}`.trim();
|
|
624
|
+
if (!text || seen.has(`r:${text}`)) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
seen.add(`r:${text}`);
|
|
628
|
+
recommendations.push(text);
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
if (warnings.length > 0) {
|
|
632
|
+
risk = promoteRisk(risk, 'medium');
|
|
633
|
+
pushConcern(`evidence inputs missing/invalid: ${warnings.length}`);
|
|
634
|
+
pushRecommendation('Repair missing inputs and regenerate: `node scripts/release-ops-weekly-summary.js --json`.');
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const handoff = snapshots.handoff;
|
|
638
|
+
if (handoff.total_runs === 0) {
|
|
639
|
+
risk = promoteRisk(risk, 'medium');
|
|
640
|
+
pushConcern('handoff evidence has no runs in current window');
|
|
641
|
+
pushRecommendation('Generate handoff evidence: `npx sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --profile moqui --json`.');
|
|
642
|
+
}
|
|
643
|
+
if (Number.isFinite(handoff.gate_pass_rate_percent) && handoff.gate_pass_rate_percent < 95) {
|
|
644
|
+
risk = promoteRisk(risk, handoff.gate_pass_rate_percent < 80 ? 'high' : 'medium');
|
|
645
|
+
pushConcern(`handoff gate pass rate is ${handoff.gate_pass_rate_percent}%`);
|
|
646
|
+
pushRecommendation('Investigate gate regressions: `npx sce auto handoff regression --session-id latest --json`.');
|
|
647
|
+
}
|
|
648
|
+
if (
|
|
649
|
+
Number.isFinite(handoff.capability_expected_unknown_positive_rate_percent)
|
|
650
|
+
&& handoff.capability_expected_unknown_positive_rate_percent > 0
|
|
651
|
+
) {
|
|
652
|
+
risk = promoteRisk(risk, 'medium');
|
|
653
|
+
pushConcern('handoff capability expected-unknown count is positive');
|
|
654
|
+
pushRecommendation('Close capability lexicon gaps: `node scripts/moqui-lexicon-audit.js --manifest docs/handoffs/handoff-manifest.json --fail-on-gap --json`.');
|
|
655
|
+
}
|
|
656
|
+
if (
|
|
657
|
+
Number.isFinite(handoff.capability_provided_unknown_positive_rate_percent)
|
|
658
|
+
&& handoff.capability_provided_unknown_positive_rate_percent > 0
|
|
659
|
+
) {
|
|
660
|
+
risk = promoteRisk(risk, 'medium');
|
|
661
|
+
pushConcern('handoff capability provided-unknown count is positive');
|
|
662
|
+
pushRecommendation('Regenerate capability matrix and enforce semantic closure: `npx sce auto handoff capability-matrix --manifest docs/handoffs/handoff-manifest.json --profile moqui --fail-on-gap --json`.');
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const history = snapshots.release_gate_history;
|
|
666
|
+
if (history.total_entries === 0) {
|
|
667
|
+
risk = promoteRisk(risk, 'medium');
|
|
668
|
+
pushConcern('release gate history has no entries in current window');
|
|
669
|
+
pushRecommendation('Refresh release gate history index: `npx sce auto handoff gate-index --dir .sce/reports/release-evidence --out .sce/reports/release-evidence/release-gate-history.json --json`.');
|
|
670
|
+
}
|
|
671
|
+
if (Number.isFinite(history.gate_pass_rate_percent) && history.gate_pass_rate_percent < 90) {
|
|
672
|
+
risk = promoteRisk(risk, history.gate_pass_rate_percent < 75 ? 'high' : 'medium');
|
|
673
|
+
pushConcern(`release gate history pass rate is ${history.gate_pass_rate_percent}%`);
|
|
674
|
+
}
|
|
675
|
+
if (
|
|
676
|
+
Number.isFinite(history.release_preflight_block_rate_percent)
|
|
677
|
+
&& history.release_preflight_block_rate_percent >= 20
|
|
678
|
+
) {
|
|
679
|
+
risk = promoteRisk(risk, 'medium');
|
|
680
|
+
pushConcern(`release preflight blocked rate is ${history.release_preflight_block_rate_percent}%`);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const governance = snapshots.interactive_governance;
|
|
684
|
+
if (governance.status === 'alert') {
|
|
685
|
+
risk = promoteRisk(risk, governance.breaches >= 3 ? 'high' : 'medium');
|
|
686
|
+
pushConcern('interactive governance report is in alert status');
|
|
687
|
+
pushRecommendation('Resolve governance alerts: `node scripts/interactive-governance-report.js --period weekly --fail-on-alert --json`.');
|
|
688
|
+
}
|
|
689
|
+
if (
|
|
690
|
+
Number.isFinite(governance.authorization_tier_block_rate_percent)
|
|
691
|
+
&& governance.authorization_tier_block_rate_percent > 40
|
|
692
|
+
) {
|
|
693
|
+
risk = promoteRisk(risk, governance.authorization_tier_block_rate_percent >= 60 ? 'high' : 'medium');
|
|
694
|
+
pushConcern(`authorization-tier block rate is ${governance.authorization_tier_block_rate_percent}%`);
|
|
695
|
+
pushRecommendation('Tune dialogue profile + authorization-tier policy to reduce deny/review pressure for actionable requests.');
|
|
696
|
+
}
|
|
697
|
+
if (
|
|
698
|
+
Number.isFinite(governance.dialogue_authorization_block_rate_percent)
|
|
699
|
+
&& governance.dialogue_authorization_block_rate_percent > 40
|
|
700
|
+
) {
|
|
701
|
+
risk = promoteRisk(risk, governance.dialogue_authorization_block_rate_percent >= 60 ? 'high' : 'medium');
|
|
702
|
+
pushConcern(`dialogue-authorization block rate is ${governance.dialogue_authorization_block_rate_percent}%`);
|
|
703
|
+
pushRecommendation('Tune authorization dialogue policy and ui-mode routing to reduce blocked user intent execution.');
|
|
704
|
+
}
|
|
705
|
+
if (
|
|
706
|
+
Number.isFinite(governance.runtime_ui_mode_violation_total)
|
|
707
|
+
&& governance.runtime_ui_mode_violation_total > 0
|
|
708
|
+
) {
|
|
709
|
+
risk = promoteRisk(risk, governance.runtime_ui_mode_violation_total >= 3 ? 'high' : 'medium');
|
|
710
|
+
pushConcern(`runtime ui-mode violations observed: ${governance.runtime_ui_mode_violation_total}`);
|
|
711
|
+
pushRecommendation('Enforce dual-surface routing: user-app suggestion-only, ops-console for apply workflows.');
|
|
712
|
+
}
|
|
713
|
+
if (
|
|
714
|
+
Number.isFinite(governance.business_mode_unknown_signal_total)
|
|
715
|
+
&& governance.business_mode_unknown_signal_total > 0
|
|
716
|
+
) {
|
|
717
|
+
risk = promoteRisk(risk, 'medium');
|
|
718
|
+
pushConcern(`interactive governance signals missing business-mode tags: ${governance.business_mode_unknown_signal_total}`);
|
|
719
|
+
pushRecommendation('Upgrade to latest interactive-flow/loop so governance signals always include business_mode.');
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
const matrix = snapshots.matrix_signals;
|
|
723
|
+
if (matrix.total_signals === 0) {
|
|
724
|
+
risk = promoteRisk(risk, 'medium');
|
|
725
|
+
pushConcern('interactive matrix signals missing in current window');
|
|
726
|
+
pushRecommendation('Generate matrix signals via interactive flow smoke: `npm run test:interactive-flow-smoke`.');
|
|
727
|
+
} else if (
|
|
728
|
+
Number.isFinite(matrix.regression_positive_rate_percent)
|
|
729
|
+
&& matrix.regression_positive_rate_percent > 20
|
|
730
|
+
) {
|
|
731
|
+
risk = promoteRisk(risk, 'medium');
|
|
732
|
+
pushConcern(`matrix regression-positive rate is ${matrix.regression_positive_rate_percent}%`);
|
|
733
|
+
pushRecommendation('Run matrix remediation queue: `npm run report:matrix-remediation-queue`.');
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (recommendations.length === 0) {
|
|
737
|
+
recommendations.push('Current weekly ops metrics are stable. Keep default release and governance gates enabled.');
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
return {
|
|
741
|
+
risk,
|
|
742
|
+
concerns,
|
|
743
|
+
recommendations
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function buildMarkdown(report) {
|
|
748
|
+
const lines = [];
|
|
749
|
+
lines.push('# Release Weekly Ops Summary');
|
|
750
|
+
lines.push('');
|
|
751
|
+
lines.push(`- Generated at: ${report.generated_at}`);
|
|
752
|
+
lines.push(`- Window: ${report.period.from} -> ${report.period.to}`);
|
|
753
|
+
lines.push(`- Risk: ${report.health.risk}`);
|
|
754
|
+
lines.push('');
|
|
755
|
+
lines.push('## Handoff');
|
|
756
|
+
lines.push('');
|
|
757
|
+
lines.push(`- Total runs: ${report.snapshots.handoff.total_runs}`);
|
|
758
|
+
lines.push(`- Gate pass rate: ${report.snapshots.handoff.gate_pass_rate_percent == null ? 'n/a' : `${report.snapshots.handoff.gate_pass_rate_percent}%`}`);
|
|
759
|
+
lines.push(`- Avg spec success: ${report.snapshots.handoff.avg_spec_success_rate_percent == null ? 'n/a' : `${report.snapshots.handoff.avg_spec_success_rate_percent}%`}`);
|
|
760
|
+
lines.push(`- Scene batch pass rate: ${report.snapshots.handoff.scene_batch_pass_rate_percent == null ? 'n/a' : `${report.snapshots.handoff.scene_batch_pass_rate_percent}%`}`);
|
|
761
|
+
lines.push(`- Release preflight blocked rate: ${report.snapshots.handoff.release_preflight_block_rate_percent == null ? 'n/a' : `${report.snapshots.handoff.release_preflight_block_rate_percent}%`}`);
|
|
762
|
+
lines.push('');
|
|
763
|
+
lines.push('## Release Gate History');
|
|
764
|
+
lines.push('');
|
|
765
|
+
lines.push(`- Entries: ${report.snapshots.release_gate_history.total_entries}`);
|
|
766
|
+
lines.push(`- Gate pass rate: ${report.snapshots.release_gate_history.gate_pass_rate_percent == null ? 'n/a' : `${report.snapshots.release_gate_history.gate_pass_rate_percent}%`}`);
|
|
767
|
+
lines.push(`- Preflight blocked rate: ${report.snapshots.release_gate_history.release_preflight_block_rate_percent == null ? 'n/a' : `${report.snapshots.release_gate_history.release_preflight_block_rate_percent}%`}`);
|
|
768
|
+
lines.push(`- Drift alert positive rate: ${report.snapshots.release_gate_history.drift_alert_positive_rate_percent == null ? 'n/a' : `${report.snapshots.release_gate_history.drift_alert_positive_rate_percent}%`}`);
|
|
769
|
+
lines.push('');
|
|
770
|
+
lines.push('## Interactive Governance');
|
|
771
|
+
lines.push('');
|
|
772
|
+
lines.push(`- Status: ${report.snapshots.interactive_governance.status || 'n/a'}`);
|
|
773
|
+
lines.push(`- Breaches: ${report.snapshots.interactive_governance.breaches == null ? 'n/a' : report.snapshots.interactive_governance.breaches}`);
|
|
774
|
+
lines.push(`- Warnings: ${report.snapshots.interactive_governance.warnings == null ? 'n/a' : report.snapshots.interactive_governance.warnings}`);
|
|
775
|
+
lines.push(`- Authorization tier signals: ${report.snapshots.interactive_governance.authorization_tier_total == null ? 'n/a' : report.snapshots.interactive_governance.authorization_tier_total}`);
|
|
776
|
+
lines.push(`- Authorization tier deny total: ${report.snapshots.interactive_governance.authorization_tier_deny_total == null ? 'n/a' : report.snapshots.interactive_governance.authorization_tier_deny_total}`);
|
|
777
|
+
lines.push(`- Authorization tier review-required total: ${report.snapshots.interactive_governance.authorization_tier_review_required_total == null ? 'n/a' : report.snapshots.interactive_governance.authorization_tier_review_required_total}`);
|
|
778
|
+
lines.push(`- Authorization tier block rate: ${report.snapshots.interactive_governance.authorization_tier_block_rate_percent == null ? 'n/a' : `${report.snapshots.interactive_governance.authorization_tier_block_rate_percent}%`}`);
|
|
779
|
+
lines.push(`- Dialogue authorization signals: ${report.snapshots.interactive_governance.dialogue_authorization_total == null ? 'n/a' : report.snapshots.interactive_governance.dialogue_authorization_total}`);
|
|
780
|
+
lines.push(`- Dialogue authorization block total: ${report.snapshots.interactive_governance.dialogue_authorization_block_total == null ? 'n/a' : report.snapshots.interactive_governance.dialogue_authorization_block_total}`);
|
|
781
|
+
lines.push(`- Dialogue authorization block rate: ${report.snapshots.interactive_governance.dialogue_authorization_block_rate_percent == null ? 'n/a' : `${report.snapshots.interactive_governance.dialogue_authorization_block_rate_percent}%`}`);
|
|
782
|
+
lines.push(`- Dialogue user-app apply attempts: ${report.snapshots.interactive_governance.dialogue_authorization_user_app_apply_attempt_total == null ? 'n/a' : report.snapshots.interactive_governance.dialogue_authorization_user_app_apply_attempt_total}`);
|
|
783
|
+
lines.push(`- Dialogue unknown business-mode signals: ${report.snapshots.interactive_governance.dialogue_authorization_unknown_business_mode_total == null ? 'n/a' : report.snapshots.interactive_governance.dialogue_authorization_unknown_business_mode_total}`);
|
|
784
|
+
lines.push(`- Runtime signals: ${report.snapshots.interactive_governance.runtime_total == null ? 'n/a' : report.snapshots.interactive_governance.runtime_total}`);
|
|
785
|
+
lines.push(`- Runtime block rate: ${report.snapshots.interactive_governance.runtime_block_rate_percent == null ? 'n/a' : `${report.snapshots.interactive_governance.runtime_block_rate_percent}%`}`);
|
|
786
|
+
lines.push(`- Runtime ui-mode violations: ${report.snapshots.interactive_governance.runtime_ui_mode_violation_total == null ? 'n/a' : report.snapshots.interactive_governance.runtime_ui_mode_violation_total}`);
|
|
787
|
+
lines.push(`- Runtime unknown business-mode signals: ${report.snapshots.interactive_governance.runtime_unknown_business_mode_total == null ? 'n/a' : report.snapshots.interactive_governance.runtime_unknown_business_mode_total}`);
|
|
788
|
+
lines.push(`- Authorization-tier unknown business-mode signals: ${report.snapshots.interactive_governance.authorization_tier_unknown_business_mode_total == null ? 'n/a' : report.snapshots.interactive_governance.authorization_tier_unknown_business_mode_total}`);
|
|
789
|
+
lines.push(`- Total unknown business-mode signals: ${report.snapshots.interactive_governance.business_mode_unknown_signal_total == null ? 'n/a' : report.snapshots.interactive_governance.business_mode_unknown_signal_total}`);
|
|
790
|
+
lines.push('');
|
|
791
|
+
lines.push('## Matrix Signals');
|
|
792
|
+
lines.push('');
|
|
793
|
+
lines.push(`- Total signals: ${report.snapshots.matrix_signals.total_signals}`);
|
|
794
|
+
lines.push(`- Portfolio pass rate: ${report.snapshots.matrix_signals.portfolio_pass_rate_percent == null ? 'n/a' : `${report.snapshots.matrix_signals.portfolio_pass_rate_percent}%`}`);
|
|
795
|
+
lines.push(`- Regression-positive rate: ${report.snapshots.matrix_signals.regression_positive_rate_percent == null ? 'n/a' : `${report.snapshots.matrix_signals.regression_positive_rate_percent}%`}`);
|
|
796
|
+
lines.push(`- Stage error rate: ${report.snapshots.matrix_signals.stage_error_rate_percent == null ? 'n/a' : `${report.snapshots.matrix_signals.stage_error_rate_percent}%`}`);
|
|
797
|
+
if (report.warnings.length > 0) {
|
|
798
|
+
lines.push('');
|
|
799
|
+
lines.push('## Warnings');
|
|
800
|
+
lines.push('');
|
|
801
|
+
for (const warning of report.warnings) {
|
|
802
|
+
lines.push(`- ${warning}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
lines.push('');
|
|
806
|
+
lines.push('## Concerns');
|
|
807
|
+
lines.push('');
|
|
808
|
+
if (report.health.concerns.length === 0) {
|
|
809
|
+
lines.push('- none');
|
|
810
|
+
} else {
|
|
811
|
+
for (const concern of report.health.concerns) {
|
|
812
|
+
lines.push(`- ${concern}`);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
lines.push('');
|
|
816
|
+
lines.push('## Recommendations');
|
|
817
|
+
lines.push('');
|
|
818
|
+
for (const recommendation of report.health.recommendations) {
|
|
819
|
+
lines.push(`- ${recommendation}`);
|
|
820
|
+
}
|
|
821
|
+
return `${lines.join('\n')}\n`;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
async function buildReleaseOpsWeeklySummary(cwd, options, now = new Date()) {
|
|
825
|
+
const window = buildWindow(options, now);
|
|
826
|
+
const [evidenceInput, gateHistoryInput, governanceInput, matrixSignalsInput] = await Promise.all([
|
|
827
|
+
safeReadJson(cwd, options.evidence),
|
|
828
|
+
safeReadJson(cwd, options.gateHistory),
|
|
829
|
+
safeReadJson(cwd, options.interactiveGovernance),
|
|
830
|
+
safeReadJsonLines(cwd, options.matrixSignals)
|
|
831
|
+
]);
|
|
832
|
+
|
|
833
|
+
const warnings = [];
|
|
834
|
+
const collectWarning = (label, input) => {
|
|
835
|
+
if (!input.exists) {
|
|
836
|
+
warnings.push(`${label}: missing (${input.path})`);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
if (input.parse_error) {
|
|
840
|
+
warnings.push(`${label}: parse error (${input.path}) ${input.parse_error}`);
|
|
841
|
+
}
|
|
842
|
+
};
|
|
843
|
+
collectWarning('evidence', evidenceInput);
|
|
844
|
+
collectWarning('gate_history', gateHistoryInput);
|
|
845
|
+
collectWarning('interactive_governance', governanceInput);
|
|
846
|
+
collectWarning('matrix_signals', matrixSignalsInput);
|
|
847
|
+
|
|
848
|
+
const snapshots = {
|
|
849
|
+
handoff: buildHandoffSnapshot(evidenceInput.payload || {}, window),
|
|
850
|
+
release_gate_history: buildReleaseGateHistorySnapshot(gateHistoryInput.payload || {}, window),
|
|
851
|
+
interactive_governance: buildInteractiveGovernanceSnapshot(governanceInput.payload || {}, window),
|
|
852
|
+
matrix_signals: buildMatrixSignalSnapshot(matrixSignalsInput.records || [], window)
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
const health = buildHealth(snapshots, warnings);
|
|
856
|
+
const outPath = resolvePath(cwd, options.out);
|
|
857
|
+
const markdownOutPath = resolvePath(cwd, options.markdownOut);
|
|
858
|
+
|
|
859
|
+
const report = {
|
|
860
|
+
mode: 'release-weekly-ops-summary',
|
|
861
|
+
generated_at: now.toISOString(),
|
|
862
|
+
period: {
|
|
863
|
+
mode: window.mode,
|
|
864
|
+
window_days: options.windowDays,
|
|
865
|
+
from: window.from.toISOString(),
|
|
866
|
+
to: window.to.toISOString()
|
|
867
|
+
},
|
|
868
|
+
inputs: {
|
|
869
|
+
evidence: evidenceInput,
|
|
870
|
+
gate_history: gateHistoryInput,
|
|
871
|
+
interactive_governance: governanceInput,
|
|
872
|
+
matrix_signals: {
|
|
873
|
+
path: matrixSignalsInput.path,
|
|
874
|
+
exists: matrixSignalsInput.exists,
|
|
875
|
+
parse_error: matrixSignalsInput.parse_error
|
|
876
|
+
}
|
|
877
|
+
},
|
|
878
|
+
warnings,
|
|
879
|
+
snapshots,
|
|
880
|
+
health,
|
|
881
|
+
output: {
|
|
882
|
+
json: path.relative(cwd, outPath) || '.',
|
|
883
|
+
markdown: path.relative(cwd, markdownOutPath) || '.'
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
await fs.ensureDir(path.dirname(outPath));
|
|
888
|
+
await fs.writeJson(outPath, report, { spaces: 2 });
|
|
889
|
+
await fs.ensureDir(path.dirname(markdownOutPath));
|
|
890
|
+
await fs.writeFile(markdownOutPath, buildMarkdown(report), 'utf8');
|
|
891
|
+
|
|
892
|
+
return report;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
async function main() {
|
|
896
|
+
const options = parseArgs(process.argv.slice(2));
|
|
897
|
+
const cwd = process.cwd();
|
|
898
|
+
const report = await buildReleaseOpsWeeklySummary(cwd, options, new Date());
|
|
899
|
+
if (options.json) {
|
|
900
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
901
|
+
} else {
|
|
902
|
+
process.stdout.write(`Release weekly ops summary generated (${report.health.risk}).\n`);
|
|
903
|
+
process.stdout.write(`- JSON: ${report.output.json}\n`);
|
|
904
|
+
process.stdout.write(`- Markdown: ${report.output.markdown}\n`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if (require.main === module) {
|
|
909
|
+
main().catch((error) => {
|
|
910
|
+
console.error(`Release weekly ops summary failed: ${error.message}`);
|
|
911
|
+
process.exit(1);
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
module.exports = {
|
|
916
|
+
DEFAULT_EVIDENCE,
|
|
917
|
+
DEFAULT_GATE_HISTORY,
|
|
918
|
+
DEFAULT_INTERACTIVE_GOVERNANCE,
|
|
919
|
+
DEFAULT_MATRIX_SIGNALS,
|
|
920
|
+
DEFAULT_OUT,
|
|
921
|
+
DEFAULT_MARKDOWN_OUT,
|
|
922
|
+
parseArgs,
|
|
923
|
+
buildWindow,
|
|
924
|
+
safeReadJson,
|
|
925
|
+
safeReadJsonLines,
|
|
926
|
+
buildHandoffSnapshot,
|
|
927
|
+
buildReleaseGateHistorySnapshot,
|
|
928
|
+
buildInteractiveGovernanceSnapshot,
|
|
929
|
+
buildMatrixSignalSnapshot,
|
|
930
|
+
buildHealth,
|
|
931
|
+
buildMarkdown,
|
|
932
|
+
buildReleaseOpsWeeklySummary,
|
|
933
|
+
main
|
|
934
|
+
};
|