trackops 2.0.5 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/quality.js ADDED
@@ -0,0 +1,1759 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { spawnSync } = require("child_process");
6
+
7
+ const config = require("./config");
8
+ const controlLib = require("./control");
9
+ const env = require("./env");
10
+ const { t, setLocale } = require("./i18n");
11
+ const { PHASE_DOD } = require("./opera-phase-dod");
12
+
13
+ const STALE_TASK_MS = 24 * 60 * 60 * 1000;
14
+ const BLOCKING_SEVERITIES = new Set(["critical", "high"]);
15
+ const QUALITY_STATUS_VALUES = new Set([
16
+ "pass",
17
+ "fail",
18
+ "warn",
19
+ "skip",
20
+ "passed",
21
+ "failed",
22
+ "skipped",
23
+ "not_configured",
24
+ "ready",
25
+ "blocked",
26
+ "attention",
27
+ "unknown",
28
+ ]);
29
+
30
+ function nowIso() {
31
+ return new Date().toISOString();
32
+ }
33
+
34
+ function fileExists(filePath) {
35
+ try {
36
+ return fs.existsSync(filePath);
37
+ } catch (_error) {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ function readText(filePath, fallback = "") {
43
+ if (!fileExists(filePath)) return fallback;
44
+ return fs.readFileSync(filePath, "utf8");
45
+ }
46
+
47
+ function readJson(filePath, fallback = null) {
48
+ if (!fileExists(filePath)) return fallback;
49
+ try {
50
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
51
+ } catch (_error) {
52
+ return fallback;
53
+ }
54
+ }
55
+
56
+ function writeJson(filePath, payload) {
57
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
58
+ fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
59
+ }
60
+
61
+ function normalizePath(value) {
62
+ return String(value || "").replace(/\\/g, "/");
63
+ }
64
+
65
+ function hasText(value) {
66
+ return Boolean(String(value || "").trim());
67
+ }
68
+
69
+ function hasOwn(object, key) {
70
+ return Boolean(object) && Object.prototype.hasOwnProperty.call(object, key);
71
+ }
72
+
73
+ function normalizeString(value) {
74
+ return hasText(value) ? String(value).trim() : null;
75
+ }
76
+
77
+ function normalizeStringList(values) {
78
+ return Array.isArray(values)
79
+ ? values.map((value) => String(value || "").trim()).filter(Boolean)
80
+ : [];
81
+ }
82
+
83
+ function ensureQualityStorage(context) {
84
+ fs.mkdirSync(context.paths.qualityDir, { recursive: true });
85
+ fs.mkdirSync(context.paths.qualityRunsDir, { recursive: true });
86
+ }
87
+
88
+ function formatQualityStatus(value) {
89
+ const normalized = String(value || "unknown").trim().toLowerCase();
90
+ return QUALITY_STATUS_VALUES.has(normalized) ? t(`quality.status.${normalized}`) : normalized || t("quality.status.unknown");
91
+ }
92
+
93
+ function formatScopeLabel(scope) {
94
+ const normalized = String(scope || "").trim().toLowerCase();
95
+ return t(`quality.scope.${normalized}`) || normalized || t("quality.value.unknown");
96
+ }
97
+
98
+ function normalizeIntakeSection(intake) {
99
+ if (!intake || typeof intake !== "object") return {};
100
+ return {
101
+ ...intake,
102
+ externalServices: normalizeStringList(intake.externalServices),
103
+ behaviorRules: normalizeStringList(intake.behaviorRules),
104
+ architecturalInvariants: normalizeStringList(intake.architecturalInvariants),
105
+ pipeline: normalizeStringList(intake.pipeline),
106
+ templates: normalizeStringList(intake.templates),
107
+ versionControl: intake.versionControl && typeof intake.versionControl === "object"
108
+ ? {
109
+ remote: hasOwn(intake.versionControl, "remote") ? intake.versionControl.remote === true : false,
110
+ provider: normalizeString(intake.versionControl.provider),
111
+ developmentBranch: normalizeString(intake.versionControl.developmentBranch),
112
+ releaseBranch: normalizeString(intake.versionControl.releaseBranch),
113
+ }
114
+ : null,
115
+ deployment: intake.deployment && typeof intake.deployment === "object"
116
+ ? {
117
+ mode: normalizeString(intake.deployment.mode),
118
+ target: normalizeString(intake.deployment.target),
119
+ smokeCommand: normalizeString(intake.deployment.smokeCommand),
120
+ }
121
+ : null,
122
+ };
123
+ }
124
+
125
+ function loadContract(context) {
126
+ const raw = readText(context.paths.contractFile, "");
127
+ if (!raw.trim()) return { exists: false, valid: false, data: null };
128
+ try {
129
+ return { exists: true, valid: true, data: JSON.parse(raw) };
130
+ } catch (error) {
131
+ return { exists: true, valid: false, data: null, error: error.message };
132
+ }
133
+ }
134
+
135
+ function loadPolicy(context) {
136
+ const raw = readText(context.paths.autonomyPolicyFile, "");
137
+ if (!raw.trim()) return { exists: false, valid: false, data: null };
138
+ try {
139
+ return { exists: true, valid: true, data: JSON.parse(raw) };
140
+ } catch (error) {
141
+ return { exists: true, valid: false, data: null, error: error.message };
142
+ }
143
+ }
144
+
145
+ function parsePackageScripts(context) {
146
+ const pkg = readJson(context.packageFile, null);
147
+ const scripts = pkg && typeof pkg.scripts === "object" ? pkg.scripts : {};
148
+ return {
149
+ packageJson: pkg,
150
+ packageDir: context.layout === "split" ? context.appRoot : context.workspaceRoot,
151
+ scripts,
152
+ };
153
+ }
154
+
155
+ function buildCommandSignature(commands) {
156
+ return (commands || [])
157
+ .map((item) => `${normalizePath(item.cwd || "")}::${String(item.command || "").trim()}`)
158
+ .join("|");
159
+ }
160
+
161
+ function latestVerificationSummary(context) {
162
+ const raw = readJson(context.paths.qualityLatestFile, {});
163
+ const normalizedRuns = {};
164
+ const sourceRuns = raw?.verification?.runs && typeof raw.verification.runs === "object"
165
+ ? raw.verification.runs
166
+ : {};
167
+
168
+ for (const [scope, run] of Object.entries(sourceRuns)) {
169
+ if (!run || typeof run !== "object") continue;
170
+ const commands = Array.isArray(run.commands)
171
+ ? run.commands
172
+ .map((entry) => ({
173
+ command: normalizeString(entry?.command),
174
+ cwd: normalizeString(entry?.cwd),
175
+ }))
176
+ .filter((entry) => entry.command)
177
+ : normalizeString(run.command)
178
+ ? [{ command: normalizeString(run.command), cwd: normalizeString(run.cwd) }]
179
+ : [];
180
+ normalizedRuns[scope] = {
181
+ status: String(run.status || "unknown").trim() || "unknown",
182
+ commands,
183
+ commandSignature: normalizeString(run.commandSignature) || buildCommandSignature(commands),
184
+ startedAt: normalizeString(run.startedAt),
185
+ finishedAt: normalizeString(run.finishedAt),
186
+ exitCode: Number.isFinite(Number(run.exitCode)) ? Number(run.exitCode) : null,
187
+ note: normalizeString(run.note) || "",
188
+ commit: normalizeString(run.commit),
189
+ scope,
190
+ };
191
+ }
192
+
193
+ return {
194
+ generatedAt: normalizeString(raw.generatedAt),
195
+ lastVerificationAt: normalizeString(raw.lastVerificationAt),
196
+ verification: { runs: normalizedRuns },
197
+ };
198
+ }
199
+
200
+ function getGitCommit(context) {
201
+ const result = spawnSync("git", ["rev-parse", "HEAD"], {
202
+ cwd: context.workspaceRoot,
203
+ encoding: "utf8",
204
+ });
205
+ return result.status === 0 ? result.stdout.trim() : null;
206
+ }
207
+
208
+ function gitText(args, cwd) {
209
+ const result = spawnSync("git", args, { cwd, encoding: "utf8" });
210
+ return result.status === 0 ? result.stdout.trim() : "";
211
+ }
212
+
213
+ function getGitRemoteUrl(context) {
214
+ return gitText(["remote", "get-url", "origin"], context.workspaceRoot) || null;
215
+ }
216
+
217
+ function getGitBranches(context) {
218
+ const output = gitText(["branch", "--format=%(refname:short)"], context.workspaceRoot);
219
+ return output.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
220
+ }
221
+
222
+ function inferProvider(remoteUrl) {
223
+ const value = String(remoteUrl || "").trim().toLowerCase();
224
+ if (!value) return null;
225
+ if (value.includes("github.com")) return "github";
226
+ if (value.includes("gitlab.com")) return "gitlab";
227
+ if (value.includes("bitbucket.org")) return "bitbucket";
228
+ return null;
229
+ }
230
+
231
+ function inferDevelopmentBranch(repo, branches) {
232
+ if (branches.includes("develop")) return "develop";
233
+ if (repo.branch && repo.branch !== "master" && repo.branch !== "main") return repo.branch;
234
+ if (branches.includes("main")) return "main";
235
+ if (branches.includes("master")) return "master";
236
+ return repo.branch || null;
237
+ }
238
+
239
+ function inferReleaseBranch(branches) {
240
+ if (branches.includes("master")) return "master";
241
+ if (branches.includes("main")) return "main";
242
+ return null;
243
+ }
244
+
245
+ function inferVersionControlMetadata(context, repo) {
246
+ if (!repo.available) return null;
247
+ const remoteUrl = getGitRemoteUrl(context);
248
+ const branches = getGitBranches(context);
249
+ const hasRemoteSignal = Boolean(remoteUrl || repo.hasUpstream);
250
+ if (!hasRemoteSignal && !branches.length) return null;
251
+ return {
252
+ remote: hasRemoteSignal,
253
+ provider: inferProvider(remoteUrl),
254
+ developmentBranch: inferDevelopmentBranch(repo, branches),
255
+ releaseBranch: inferReleaseBranch(branches),
256
+ };
257
+ }
258
+
259
+ function inferDeploymentMetadata(controlState, pkgInfo) {
260
+ const metaQuality = config.normalizeQualityMeta(controlState.meta?.quality);
261
+ const scripts = pkgInfo.scripts || {};
262
+ const smokeCommand = normalizeString(metaQuality.verification.smokeCommands[0])
263
+ || normalizeString(scripts.smoke ? "npm run smoke" : null)
264
+ || normalizeString(scripts["release:check"] ? "npm run release:check" : null);
265
+ if (!smokeCommand) return null;
266
+ return {
267
+ mode: "manual",
268
+ target: null,
269
+ smokeCommand,
270
+ };
271
+ }
272
+
273
+ function resolveVersionControlSection(explicitSection, fallbackSection, inferredSection) {
274
+ const source = explicitSection ? "intake" : fallbackSection ? "bootstrap" : inferredSection ? "inferred" : null;
275
+ if (!source) return { value: null, source: null, inferred: false };
276
+ const value = {
277
+ remote: hasOwn(explicitSection, "remote")
278
+ ? explicitSection.remote === true
279
+ : hasOwn(fallbackSection, "remote")
280
+ ? fallbackSection.remote === true
281
+ : inferredSection?.remote === true,
282
+ provider: normalizeString(explicitSection?.provider)
283
+ || normalizeString(fallbackSection?.provider)
284
+ || normalizeString(inferredSection?.provider),
285
+ developmentBranch: normalizeString(explicitSection?.developmentBranch)
286
+ || normalizeString(fallbackSection?.developmentBranch)
287
+ || normalizeString(inferredSection?.developmentBranch),
288
+ releaseBranch: normalizeString(explicitSection?.releaseBranch)
289
+ || normalizeString(fallbackSection?.releaseBranch)
290
+ || normalizeString(inferredSection?.releaseBranch),
291
+ };
292
+ return { value, source, inferred: source === "inferred" };
293
+ }
294
+
295
+ function resolveDeploymentSection(explicitSection, fallbackSection, inferredSection) {
296
+ const source = explicitSection ? "intake" : fallbackSection ? "bootstrap" : inferredSection ? "inferred" : null;
297
+ if (!source) return { value: null, source: null, inferred: false };
298
+ const value = {
299
+ mode: normalizeString(explicitSection?.mode)
300
+ || normalizeString(fallbackSection?.mode)
301
+ || normalizeString(inferredSection?.mode),
302
+ target: normalizeString(explicitSection?.target)
303
+ || normalizeString(fallbackSection?.target)
304
+ || normalizeString(inferredSection?.target),
305
+ smokeCommand: normalizeString(explicitSection?.smokeCommand)
306
+ || normalizeString(fallbackSection?.smokeCommand)
307
+ || normalizeString(inferredSection?.smokeCommand),
308
+ };
309
+ if (!value.mode && !value.target && !value.smokeCommand) {
310
+ return { value: null, source: null, inferred: false };
311
+ }
312
+ return { value, source, inferred: source === "inferred" };
313
+ }
314
+
315
+ function buildBootstrapMetadataBackfill(contextOrRoot, controlState, options = {}) {
316
+ const context = config.ensureContext(contextOrRoot);
317
+ const control = controlState || config.loadControl(context);
318
+ const repo = options.repo || controlLib.getRepoSnapshot(context);
319
+ const pkgInfo = options.pkgInfo || parsePackageScripts(context);
320
+ const bootstrapState = control.meta?.opera?.bootstrap || {};
321
+ const discovery = normalizeIntakeSection(bootstrapState.discovery || {});
322
+ const rawIntake = normalizeIntakeSection(readJson(path.join(context.paths.bootstrapDir, "intake.json"), null));
323
+ const explicitVersionControl = rawIntake.versionControl || null;
324
+ const explicitDeployment = rawIntake.deployment || null;
325
+ const fallbackVersionControl = discovery.versionControl || null;
326
+ const fallbackDeployment = discovery.deployment || null;
327
+ const inferredVersionControl = inferVersionControlMetadata(context, repo);
328
+ const inferredDeployment = inferDeploymentMetadata(control, pkgInfo);
329
+ const versionControl = resolveVersionControlSection(explicitVersionControl, fallbackVersionControl, inferredVersionControl);
330
+ const deployment = resolveDeploymentSection(explicitDeployment, fallbackDeployment, inferredDeployment);
331
+ const intake = normalizeIntakeSection({
332
+ ...discovery,
333
+ ...(rawIntake || {}),
334
+ versionControl: versionControl.value,
335
+ deployment: deployment.value,
336
+ });
337
+
338
+ return {
339
+ rawIntake: rawIntake || {},
340
+ discovery,
341
+ intake,
342
+ inferred: {
343
+ versionControl: inferredVersionControl,
344
+ deployment: inferredDeployment,
345
+ },
346
+ sources: {
347
+ versionControl: versionControl.source,
348
+ deployment: deployment.source,
349
+ },
350
+ changed: versionControl.inferred || deployment.inferred,
351
+ };
352
+ }
353
+
354
+ function backfillBootstrapMetadata(contextOrRoot, controlState, options = {}) {
355
+ const context = config.ensureContext(contextOrRoot);
356
+ const control = controlState || config.loadControl(context);
357
+ const hasBootstrapState = Boolean(control.meta?.opera?.bootstrap);
358
+ if (!hasBootstrapState) {
359
+ return { changed: false, intake: null, sources: { versionControl: null, deployment: null } };
360
+ }
361
+
362
+ const backfill = buildBootstrapMetadataBackfill(context, control, options);
363
+ const nextDiscovery = normalizeIntakeSection({
364
+ ...(control.meta.opera.bootstrap.discovery || {}),
365
+ versionControl: backfill.intake.versionControl,
366
+ deployment: backfill.intake.deployment,
367
+ });
368
+
369
+ const discoveryChanged = JSON.stringify(nextDiscovery.versionControl || null) !== JSON.stringify(control.meta.opera.bootstrap.discovery?.versionControl || null)
370
+ || JSON.stringify(nextDiscovery.deployment || null) !== JSON.stringify(control.meta.opera.bootstrap.discovery?.deployment || null);
371
+
372
+ if (discoveryChanged) {
373
+ control.meta.opera.bootstrap = {
374
+ ...control.meta.opera.bootstrap,
375
+ discovery: {
376
+ ...(control.meta.opera.bootstrap.discovery || {}),
377
+ versionControl: nextDiscovery.versionControl,
378
+ deployment: nextDiscovery.deployment,
379
+ },
380
+ };
381
+ }
382
+
383
+ const intakePath = path.join(context.paths.bootstrapDir, "intake.json");
384
+ const shouldWriteIntake = options.persist === true && (fileExists(intakePath) || Object.keys(backfill.rawIntake).length > 0 || Object.keys(backfill.discovery).length > 0);
385
+ let intakeChanged = false;
386
+ if (shouldWriteIntake) {
387
+ const nextIntake = normalizeIntakeSection({
388
+ ...backfill.rawIntake,
389
+ versionControl: backfill.intake.versionControl,
390
+ deployment: backfill.intake.deployment,
391
+ });
392
+ const currentSerialized = JSON.stringify(normalizeIntakeSection(readJson(intakePath, {})));
393
+ const nextSerialized = JSON.stringify(nextIntake);
394
+ if (currentSerialized !== nextSerialized) {
395
+ writeJson(intakePath, nextIntake);
396
+ intakeChanged = true;
397
+ }
398
+ }
399
+
400
+ return {
401
+ changed: discoveryChanged || intakeChanged,
402
+ intake: backfill.intake,
403
+ sources: backfill.sources,
404
+ };
405
+ }
406
+
407
+ function loadBootstrapArtifacts(context, controlState, options = {}) {
408
+ const backfill = buildBootstrapMetadataBackfill(context, controlState, options);
409
+ const specPath = path.join(context.paths.bootstrapDir, "spec-dossier.md");
410
+ const openQuestionsPath = path.join(context.paths.bootstrapDir, "open-questions.md");
411
+ const qualityReportPath = path.join(context.paths.bootstrapDir, "quality-report.json");
412
+ const intakePath = path.join(context.paths.bootstrapDir, "intake.json");
413
+ return {
414
+ intake: backfill.intake,
415
+ rawIntake: backfill.rawIntake,
416
+ discovery: backfill.discovery,
417
+ inferred: backfill.inferred,
418
+ sources: backfill.sources,
419
+ intakeExists: fileExists(intakePath),
420
+ specExists: fileExists(specPath),
421
+ specText: readText(specPath, "").trim(),
422
+ qualityReport: readJson(qualityReportPath, null),
423
+ openQuestions: readText(openQuestionsPath, "").trim(),
424
+ genesisExists: fileExists(context.paths.genesisFile),
425
+ paths: {
426
+ intake: normalizePath(intakePath),
427
+ specDossier: normalizePath(specPath),
428
+ openQuestions: normalizePath(openQuestionsPath),
429
+ qualityReport: normalizePath(qualityReportPath),
430
+ },
431
+ };
432
+ }
433
+
434
+ function detectProfiles(context, contract, intake, envState, pkgInfo, verificationConfig) {
435
+ const profiles = new Set(["baseline"]);
436
+ if (pkgInfo.packageJson) profiles.add("node-runtime");
437
+ if ((contract?.data?.system?.externalServices || []).length || (envState.requiredKeys || []).length) {
438
+ profiles.add("external-services");
439
+ }
440
+ if (context.layout === "split" || pkgInfo.packageJson?.version) {
441
+ profiles.add("publishable-release");
442
+ }
443
+ if (
444
+ intake?.deployment?.mode ||
445
+ intake?.deployment?.target ||
446
+ intake?.deployment?.smokeCommand ||
447
+ verificationConfig.smokeCommands.length
448
+ ) {
449
+ profiles.add("deployable-app");
450
+ }
451
+ return [...profiles];
452
+ }
453
+
454
+ function inferVerificationConfig(context, controlState, intake, pkgInfo) {
455
+ const metaQuality = config.normalizeQualityMeta(controlState.meta?.quality);
456
+ const fromMeta = metaQuality.verification || {};
457
+ const scripts = pkgInfo.scripts || {};
458
+ const packageDir = pkgInfo.packageDir || context.workspaceRoot;
459
+ const deploymentSmokeCommand = normalizeString(intake?.deployment?.smokeCommand);
460
+ const deploymentSmokeCwd = deploymentSmokeCommand && pkgInfo.packageJson
461
+ && /^npm(?:\.cmd)?\s+run\s+/i.test(deploymentSmokeCommand)
462
+ ? packageDir
463
+ : context.workspaceRoot;
464
+
465
+ const testCommands = fromMeta.testCommands.length
466
+ ? fromMeta.testCommands.map((command) => ({ command, cwd: packageDir }))
467
+ : scripts.test
468
+ ? [{ command: "npm test", cwd: packageDir }]
469
+ : [];
470
+ const buildCommands = fromMeta.buildCommands.length
471
+ ? fromMeta.buildCommands.map((command) => ({ command, cwd: packageDir }))
472
+ : scripts.build
473
+ ? [{ command: "npm run build", cwd: packageDir }]
474
+ : [];
475
+ const smokeCommands = fromMeta.smokeCommands.length
476
+ ? fromMeta.smokeCommands.map((command) => ({ command, cwd: packageDir }))
477
+ : deploymentSmokeCommand
478
+ ? [{ command: deploymentSmokeCommand, cwd: deploymentSmokeCwd }]
479
+ : scripts.smoke
480
+ ? [{ command: "npm run smoke", cwd: packageDir }]
481
+ : scripts["release:check"]
482
+ ? [{ command: "npm run release:check", cwd: packageDir }]
483
+ : [];
484
+
485
+ return {
486
+ reviewRequired: fromMeta.reviewRequired === true,
487
+ testCommands,
488
+ buildCommands,
489
+ smokeCommands,
490
+ };
491
+ }
492
+
493
+ function getBridgeSyncStatus(context) {
494
+ if (context.layout !== "split" || context.env.bridgeMode === "none") {
495
+ return { applicable: false, ok: true, missing: false, mismatchedKeys: [] };
496
+ }
497
+ if (!fileExists(context.env.appBridgeFile)) {
498
+ return { applicable: true, ok: false, missing: true, mismatchedKeys: [] };
499
+ }
500
+ const rootEntries = env.parseEnv(readText(context.env.rootFile, ""));
501
+ const bridgeEntries = env.parseEnv(readText(context.env.appBridgeFile, ""));
502
+ const keys = new Set([...Object.keys(rootEntries), ...Object.keys(bridgeEntries)]);
503
+ const mismatchedKeys = [...keys].filter((key) => String(rootEntries[key] || "") !== String(bridgeEntries[key] || ""));
504
+ return {
505
+ applicable: true,
506
+ ok: mismatchedKeys.length === 0,
507
+ missing: false,
508
+ mismatchedKeys,
509
+ };
510
+ }
511
+
512
+ function getTaskActivityMs(task, controlState) {
513
+ const timestamps = [];
514
+ if (task.execution?.updatedAt) timestamps.push(Date.parse(task.execution.updatedAt));
515
+ for (const entry of Array.isArray(task.history) ? task.history : []) {
516
+ if (entry?.at) timestamps.push(Date.parse(entry.at));
517
+ }
518
+ for (const entry of Array.isArray(controlState.timeEntries) ? controlState.timeEntries : []) {
519
+ if (entry?.taskId !== task.id) continue;
520
+ if (entry.startedAt) timestamps.push(Date.parse(entry.startedAt));
521
+ if (entry.stoppedAt) timestamps.push(Date.parse(entry.stoppedAt));
522
+ }
523
+ const valid = timestamps.filter((value) => Number.isFinite(value));
524
+ return valid.length ? Math.max(...valid) : null;
525
+ }
526
+
527
+ function normalizeFindingSeverity(finding) {
528
+ const value = String(finding?.severity || finding?.priority || "").trim().toLowerCase();
529
+ if (["critical", "high", "medium", "low"].includes(value)) return value;
530
+ if (value === "p0") return "critical";
531
+ if (value === "p1") return "high";
532
+ if (value === "p2") return "medium";
533
+ if (value === "p3") return "low";
534
+ return "medium";
535
+ }
536
+
537
+ function makeProbe(overrides) {
538
+ return {
539
+ id: overrides.id,
540
+ domain: overrides.domain,
541
+ severity: overrides.severity || "medium",
542
+ gates: Array.isArray(overrides.gates) ? overrides.gates : [],
543
+ phaseRelevance: Array.isArray(overrides.phaseRelevance) ? overrides.phaseRelevance : [],
544
+ status: overrides.status || "pass",
545
+ title: overrides.title || overrides.id,
546
+ message: overrides.message || "",
547
+ recommendation: overrides.recommendation || "",
548
+ evidence: overrides.evidence || {},
549
+ };
550
+ }
551
+
552
+ function summarizeProbes(probes) {
553
+ const counts = { pass: 0, fail: 0, skip: 0 };
554
+ const byDomain = {};
555
+ const failures = [];
556
+ for (const probe of probes) {
557
+ counts[probe.status] = (counts[probe.status] || 0) + 1;
558
+ byDomain[probe.domain] = byDomain[probe.domain] || { pass: 0, fail: 0, skip: 0 };
559
+ byDomain[probe.domain][probe.status] += 1;
560
+ if (probe.status === "fail") failures.push(probe);
561
+ }
562
+ const overallStatus = failures.some((probe) => BLOCKING_SEVERITIES.has(probe.severity))
563
+ ? "fail"
564
+ : failures.length
565
+ ? "warn"
566
+ : "pass";
567
+ return {
568
+ overallStatus,
569
+ counts,
570
+ byDomain,
571
+ failingIds: failures.map((probe) => probe.id),
572
+ failingHighSeverity: failures.filter((probe) => BLOCKING_SEVERITIES.has(probe.severity)).map((probe) => probe.id),
573
+ };
574
+ }
575
+
576
+ function probeTitle(id) {
577
+ return t(`quality.probe.${id}.title`);
578
+ }
579
+
580
+ function probeRecommendation(id) {
581
+ return t(`quality.probe.${id}.recommendation`);
582
+ }
583
+
584
+ function buildProbes(state) {
585
+ const probes = [];
586
+ const intake = state.bootstrapArtifacts.intake || {};
587
+ const bridgeSync = getBridgeSyncStatus(state.context);
588
+ const criticalFindings = (state.derived.openFindings || []).filter((finding) => normalizeFindingSeverity(finding) === "critical");
589
+ const highFindings = (state.derived.openFindings || []).filter((finding) => normalizeFindingSeverity(finding) === "high");
590
+ const blockedWithoutNote = (state.derived.blockers || []).filter((task) => !hasText(task.blocker));
591
+ const requiredWithoutAcceptance = (state.derived.actionableTasks || []).filter(
592
+ (task) => task.required !== false && (!Array.isArray(task.acceptance) || task.acceptance.length === 0),
593
+ );
594
+ const staleInProgress = (state.derived.activeTasks || []).filter((task) => {
595
+ const lastActivity = getTaskActivityMs(task, state.control);
596
+ return Number.isFinite(lastActivity) && (Date.now() - lastActivity) > STALE_TASK_MS;
597
+ });
598
+ const contractIntentMissing = state.contract.valid
599
+ ? ["problemStatement", "targetUser", "singularDesiredOutcome", "deliveryTarget"]
600
+ .filter((key) => !hasText(state.contract.data?.intent?.[key]))
601
+ : [];
602
+ const verificationConfigured = state.verificationConfig.testCommands.length
603
+ || state.verificationConfig.buildCommands.length
604
+ || state.verificationConfig.smokeCommands.length
605
+ || state.verificationConfig.reviewRequired;
606
+
607
+ probes.push(makeProbe({
608
+ id: "bootstrap.complete",
609
+ domain: "bootstrap",
610
+ severity: "high",
611
+ gates: ["phase", "release", "promotion"],
612
+ phaseRelevance: ["O", "P", "A"],
613
+ status: !state.hasOpera ? "skip" : String(state.control.meta?.opera?.bootstrap?.status || "").trim() === "completed" ? "pass" : "fail",
614
+ title: probeTitle("bootstrap.complete"),
615
+ message: !state.hasOpera
616
+ ? t("quality.message.notApplicable", { subject: probeTitle("bootstrap.complete") })
617
+ : String(state.control.meta?.opera?.bootstrap?.status || "").trim() === "completed"
618
+ ? t("quality.message.ok", { subject: probeTitle("bootstrap.complete") })
619
+ : t("quality.message.failDetail", {
620
+ subject: probeTitle("bootstrap.complete"),
621
+ detail: String(state.control.meta?.opera?.bootstrap?.status || t("quality.value.unknown")),
622
+ }),
623
+ recommendation: probeRecommendation("bootstrap.complete"),
624
+ evidence: { status: state.control.meta?.opera?.bootstrap?.status || null },
625
+ }));
626
+
627
+ probes.push(makeProbe({
628
+ id: "contract.exists",
629
+ domain: "contract",
630
+ severity: "high",
631
+ gates: ["phase", "release", "promotion"],
632
+ phaseRelevance: ["O", "R", "A"],
633
+ status: !state.hasOpera ? "skip" : state.contract.exists ? "pass" : "fail",
634
+ title: probeTitle("contract.exists"),
635
+ message: !state.hasOpera
636
+ ? t("quality.message.notApplicable", { subject: probeTitle("contract.exists") })
637
+ : state.contract.exists
638
+ ? t("quality.message.ok", { subject: probeTitle("contract.exists") })
639
+ : t("quality.message.fileMissing", { subject: probeTitle("contract.exists"), path: normalizePath(state.context.paths.contractFile) }),
640
+ recommendation: probeRecommendation("contract.exists"),
641
+ evidence: { path: normalizePath(state.context.paths.contractFile) },
642
+ }));
643
+
644
+ probes.push(makeProbe({
645
+ id: "contract.valid-json",
646
+ domain: "contract",
647
+ severity: "critical",
648
+ gates: ["phase", "release", "promotion"],
649
+ phaseRelevance: ["O", "R", "A"],
650
+ status: !state.contract.exists ? "skip" : state.contract.valid ? "pass" : "fail",
651
+ title: probeTitle("contract.valid-json"),
652
+ message: !state.contract.exists
653
+ ? t("quality.message.notApplicable", { subject: probeTitle("contract.valid-json") })
654
+ : state.contract.valid
655
+ ? t("quality.message.ok", { subject: probeTitle("contract.valid-json") })
656
+ : t("quality.message.failDetail", { subject: probeTitle("contract.valid-json"), detail: state.contract.error || t("quality.value.unknown") }),
657
+ recommendation: probeRecommendation("contract.valid-json"),
658
+ evidence: { error: state.contract.error || null },
659
+ }));
660
+
661
+ probes.push(makeProbe({
662
+ id: "contract.intent-complete",
663
+ domain: "contract",
664
+ severity: "high",
665
+ gates: ["phase", "release"],
666
+ phaseRelevance: ["O", "R"],
667
+ status: !state.contract.valid ? "skip" : contractIntentMissing.length ? "fail" : "pass",
668
+ title: probeTitle("contract.intent-complete"),
669
+ message: !state.contract.valid
670
+ ? t("quality.message.notApplicable", { subject: probeTitle("contract.intent-complete") })
671
+ : contractIntentMissing.length
672
+ ? t("quality.message.missingItems", { subject: probeTitle("contract.intent-complete"), items: contractIntentMissing.join(", ") })
673
+ : t("quality.message.ok", { subject: probeTitle("contract.intent-complete") }),
674
+ recommendation: probeRecommendation("contract.intent-complete"),
675
+ evidence: { missingFields: contractIntentMissing },
676
+ }));
677
+
678
+ probes.push(makeProbe({
679
+ id: "policy.exists",
680
+ domain: "policy",
681
+ severity: "high",
682
+ gates: ["phase", "release", "promotion"],
683
+ phaseRelevance: ["P", "A"],
684
+ status: !state.hasOpera ? "skip" : state.policy.exists ? "pass" : "fail",
685
+ title: probeTitle("policy.exists"),
686
+ message: !state.hasOpera
687
+ ? t("quality.message.notApplicable", { subject: probeTitle("policy.exists") })
688
+ : state.policy.exists
689
+ ? t("quality.message.ok", { subject: probeTitle("policy.exists") })
690
+ : t("quality.message.fileMissing", { subject: probeTitle("policy.exists"), path: normalizePath(state.context.paths.autonomyPolicyFile) }),
691
+ recommendation: probeRecommendation("policy.exists"),
692
+ evidence: { path: normalizePath(state.context.paths.autonomyPolicyFile) },
693
+ }));
694
+
695
+ probes.push(makeProbe({
696
+ id: "policy.valid-json",
697
+ domain: "policy",
698
+ severity: "critical",
699
+ gates: ["phase", "release", "promotion"],
700
+ phaseRelevance: ["P", "A"],
701
+ status: !state.policy.exists ? "skip" : state.policy.valid ? "pass" : "fail",
702
+ title: probeTitle("policy.valid-json"),
703
+ message: !state.policy.exists
704
+ ? t("quality.message.notApplicable", { subject: probeTitle("policy.valid-json") })
705
+ : state.policy.valid
706
+ ? t("quality.message.ok", { subject: probeTitle("policy.valid-json") })
707
+ : t("quality.message.failDetail", { subject: probeTitle("policy.valid-json"), detail: state.policy.error || t("quality.value.unknown") }),
708
+ recommendation: probeRecommendation("policy.valid-json"),
709
+ evidence: { error: state.policy.error || null },
710
+ }));
711
+
712
+ probes.push(makeProbe({
713
+ id: "docs.drift",
714
+ domain: "docs",
715
+ severity: "medium",
716
+ gates: ["phase", "release"],
717
+ phaseRelevance: ["R", "A"],
718
+ status: state.drift.length ? "fail" : "pass",
719
+ title: probeTitle("docs.drift"),
720
+ message: state.drift.length
721
+ ? t("quality.message.outOfSync", { subject: probeTitle("docs.drift"), detail: state.drift.join(", ") })
722
+ : t("quality.message.ok", { subject: probeTitle("docs.drift") }),
723
+ recommendation: probeRecommendation("docs.drift"),
724
+ evidence: { drift: state.drift },
725
+ }));
726
+
727
+ probes.push(makeProbe({
728
+ id: "docs.genesis-exists",
729
+ domain: "docs",
730
+ severity: "low",
731
+ gates: ["phase"],
732
+ phaseRelevance: ["O", "R"],
733
+ status: !state.hasOpera ? "skip" : state.bootstrapArtifacts.genesisExists ? "pass" : "fail",
734
+ title: probeTitle("docs.genesis-exists"),
735
+ message: !state.hasOpera
736
+ ? t("quality.message.notApplicable", { subject: probeTitle("docs.genesis-exists") })
737
+ : state.bootstrapArtifacts.genesisExists
738
+ ? t("quality.message.ok", { subject: probeTitle("docs.genesis-exists") })
739
+ : t("quality.message.fileMissing", { subject: probeTitle("docs.genesis-exists"), path: normalizePath(state.context.paths.genesisFile) }),
740
+ recommendation: probeRecommendation("docs.genesis-exists"),
741
+ evidence: { path: normalizePath(state.context.paths.genesisFile) },
742
+ }));
743
+
744
+ probes.push(makeProbe({
745
+ id: "env.missing-keys",
746
+ domain: "env",
747
+ severity: "high",
748
+ gates: ["phase", "release", "promotion"],
749
+ phaseRelevance: ["P", "A"],
750
+ status: state.envState.missingKeys.length ? "fail" : "pass",
751
+ title: probeTitle("env.missing-keys"),
752
+ message: state.envState.missingKeys.length
753
+ ? t("quality.message.missingItems", { subject: probeTitle("env.missing-keys"), items: state.envState.missingKeys.join(", ") })
754
+ : t("quality.message.ok", { subject: probeTitle("env.missing-keys") }),
755
+ recommendation: probeRecommendation("env.missing-keys"),
756
+ evidence: { missingKeys: state.envState.missingKeys },
757
+ }));
758
+
759
+ probes.push(makeProbe({
760
+ id: "env.bridge-sync",
761
+ domain: "env",
762
+ severity: "medium",
763
+ gates: ["phase", "release"],
764
+ phaseRelevance: ["P", "A"],
765
+ status: !bridgeSync.applicable ? "skip" : bridgeSync.ok ? "pass" : "fail",
766
+ title: probeTitle("env.bridge-sync"),
767
+ message: !bridgeSync.applicable
768
+ ? t("quality.message.notApplicable", { subject: probeTitle("env.bridge-sync") })
769
+ : bridgeSync.ok
770
+ ? t("quality.message.ok", { subject: probeTitle("env.bridge-sync") })
771
+ : bridgeSync.missing
772
+ ? t("quality.message.bridgeMissing", { subject: probeTitle("env.bridge-sync") })
773
+ : t("quality.message.bridgeMismatch", { subject: probeTitle("env.bridge-sync"), items: bridgeSync.mismatchedKeys.join(", ") }),
774
+ recommendation: probeRecommendation("env.bridge-sync"),
775
+ evidence: bridgeSync,
776
+ }));
777
+
778
+ probes.push(makeProbe({
779
+ id: "repo.clean",
780
+ domain: "repo",
781
+ severity: "high",
782
+ gates: ["release", "promotion"],
783
+ phaseRelevance: ["A"],
784
+ status: !state.repo.available ? "skip" : state.repo.clean ? "pass" : "fail",
785
+ title: probeTitle("repo.clean"),
786
+ message: !state.repo.available
787
+ ? t("quality.message.notApplicable", { subject: probeTitle("repo.clean") })
788
+ : state.repo.clean
789
+ ? t("quality.message.ok", { subject: probeTitle("repo.clean") })
790
+ : t("quality.message.dirtyTree", { subject: probeTitle("repo.clean") }),
791
+ recommendation: probeRecommendation("repo.clean"),
792
+ evidence: { clean: state.repo.clean, staged: state.repo.staged, unstaged: state.repo.unstaged, untracked: state.repo.untracked },
793
+ }));
794
+
795
+ probes.push(makeProbe({
796
+ id: "repo.behind-remote",
797
+ domain: "repo",
798
+ severity: "high",
799
+ gates: ["release", "promotion"],
800
+ phaseRelevance: ["A"],
801
+ status: !state.repo.available || !state.repo.hasUpstream ? "skip" : state.repo.behind > 0 ? "fail" : "pass",
802
+ title: probeTitle("repo.behind-remote"),
803
+ message: !state.repo.available || !state.repo.hasUpstream
804
+ ? t("quality.message.notApplicable", { subject: probeTitle("repo.behind-remote") })
805
+ : state.repo.behind > 0
806
+ ? t("quality.message.behindRemote", { subject: probeTitle("repo.behind-remote"), count: String(state.repo.behind) })
807
+ : t("quality.message.ok", { subject: probeTitle("repo.behind-remote") }),
808
+ recommendation: probeRecommendation("repo.behind-remote"),
809
+ evidence: { ahead: state.repo.ahead, behind: state.repo.behind, hasUpstream: state.repo.hasUpstream },
810
+ }));
811
+
812
+ probes.push(makeProbe({
813
+ id: "tasks.circular-deps",
814
+ domain: "tasks",
815
+ severity: "critical",
816
+ gates: ["phase", "release"],
817
+ phaseRelevance: ["E", "R", "A"],
818
+ status: state.derived.circularDeps.length ? "fail" : "pass",
819
+ title: probeTitle("tasks.circular-deps"),
820
+ message: state.derived.circularDeps.length
821
+ ? t("quality.message.missingItems", { subject: probeTitle("tasks.circular-deps"), items: state.derived.circularDeps.join(", ") })
822
+ : t("quality.message.ok", { subject: probeTitle("tasks.circular-deps") }),
823
+ recommendation: probeRecommendation("tasks.circular-deps"),
824
+ evidence: { taskIds: state.derived.circularDeps },
825
+ }));
826
+
827
+ probes.push(makeProbe({
828
+ id: "tasks.phantom-deps",
829
+ domain: "tasks",
830
+ severity: "high",
831
+ gates: ["phase", "release"],
832
+ phaseRelevance: ["E", "R"],
833
+ status: state.derived.phantomDeps.length ? "fail" : "pass",
834
+ title: probeTitle("tasks.phantom-deps"),
835
+ message: state.derived.phantomDeps.length
836
+ ? t("quality.message.countFail", { subject: probeTitle("tasks.phantom-deps"), count: String(state.derived.phantomDeps.length) })
837
+ : t("quality.message.ok", { subject: probeTitle("tasks.phantom-deps") }),
838
+ recommendation: probeRecommendation("tasks.phantom-deps"),
839
+ evidence: { phantomDeps: state.derived.phantomDeps },
840
+ }));
841
+
842
+ probes.push(makeProbe({
843
+ id: "tasks.phantom-parents",
844
+ domain: "tasks",
845
+ severity: "high",
846
+ gates: ["phase", "release"],
847
+ phaseRelevance: ["E", "R"],
848
+ status: state.derived.phantomParents.length ? "fail" : "pass",
849
+ title: probeTitle("tasks.phantom-parents"),
850
+ message: state.derived.phantomParents.length
851
+ ? t("quality.message.countFail", { subject: probeTitle("tasks.phantom-parents"), count: String(state.derived.phantomParents.length) })
852
+ : t("quality.message.ok", { subject: probeTitle("tasks.phantom-parents") }),
853
+ recommendation: probeRecommendation("tasks.phantom-parents"),
854
+ evidence: { phantomParents: state.derived.phantomParents },
855
+ }));
856
+
857
+ probes.push(makeProbe({
858
+ id: "tasks.hierarchy-cycles",
859
+ domain: "tasks",
860
+ severity: "critical",
861
+ gates: ["phase", "release"],
862
+ phaseRelevance: ["E", "R"],
863
+ status: state.derived.hierarchyCycles.length ? "fail" : "pass",
864
+ title: probeTitle("tasks.hierarchy-cycles"),
865
+ message: state.derived.hierarchyCycles.length
866
+ ? t("quality.message.missingItems", { subject: probeTitle("tasks.hierarchy-cycles"), items: state.derived.hierarchyCycles.join(", ") })
867
+ : t("quality.message.ok", { subject: probeTitle("tasks.hierarchy-cycles") }),
868
+ recommendation: probeRecommendation("tasks.hierarchy-cycles"),
869
+ evidence: { taskIds: state.derived.hierarchyCycles },
870
+ }));
871
+
872
+ probes.push(makeProbe({
873
+ id: "tasks.stale-in-progress",
874
+ domain: "tasks",
875
+ severity: "medium",
876
+ gates: ["phase"],
877
+ phaseRelevance: ["E", "R"],
878
+ status: staleInProgress.length ? "fail" : "pass",
879
+ title: probeTitle("tasks.stale-in-progress"),
880
+ message: staleInProgress.length
881
+ ? t("quality.message.staleTasks", { subject: probeTitle("tasks.stale-in-progress"), count: String(staleInProgress.length) })
882
+ : t("quality.message.ok", { subject: probeTitle("tasks.stale-in-progress") }),
883
+ recommendation: probeRecommendation("tasks.stale-in-progress"),
884
+ evidence: { taskIds: staleInProgress.map((task) => task.id) },
885
+ }));
886
+
887
+ probes.push(makeProbe({
888
+ id: "tasks.blocked-without-note",
889
+ domain: "tasks",
890
+ severity: "medium",
891
+ gates: ["phase"],
892
+ phaseRelevance: ["P", "E", "R"],
893
+ status: blockedWithoutNote.length ? "fail" : "pass",
894
+ title: probeTitle("tasks.blocked-without-note"),
895
+ message: blockedWithoutNote.length
896
+ ? t("quality.message.countFail", { subject: probeTitle("tasks.blocked-without-note"), count: String(blockedWithoutNote.length) })
897
+ : t("quality.message.ok", { subject: probeTitle("tasks.blocked-without-note") }),
898
+ recommendation: probeRecommendation("tasks.blocked-without-note"),
899
+ evidence: { taskIds: blockedWithoutNote.map((task) => task.id) },
900
+ }));
901
+
902
+ probes.push(makeProbe({
903
+ id: "tasks.required-without-acceptance",
904
+ domain: "tasks",
905
+ severity: "medium",
906
+ gates: ["phase"],
907
+ phaseRelevance: ["E", "R"],
908
+ status: requiredWithoutAcceptance.length ? "fail" : "pass",
909
+ title: probeTitle("tasks.required-without-acceptance"),
910
+ message: requiredWithoutAcceptance.length
911
+ ? t("quality.message.countFail", { subject: probeTitle("tasks.required-without-acceptance"), count: String(requiredWithoutAcceptance.length) })
912
+ : t("quality.message.ok", { subject: probeTitle("tasks.required-without-acceptance") }),
913
+ recommendation: probeRecommendation("tasks.required-without-acceptance"),
914
+ evidence: { taskIds: requiredWithoutAcceptance.map((task) => task.id) },
915
+ }));
916
+
917
+ probes.push(makeProbe({
918
+ id: "findings.open-critical",
919
+ domain: "findings",
920
+ severity: "critical",
921
+ gates: ["release", "promotion"],
922
+ phaseRelevance: ["R", "A"],
923
+ status: criticalFindings.length ? "fail" : "pass",
924
+ title: probeTitle("findings.open-critical"),
925
+ message: criticalFindings.length
926
+ ? t("quality.message.countFail", { subject: probeTitle("findings.open-critical"), count: String(criticalFindings.length) })
927
+ : t("quality.message.ok", { subject: probeTitle("findings.open-critical") }),
928
+ recommendation: probeRecommendation("findings.open-critical"),
929
+ evidence: { findingIds: criticalFindings.map((finding) => finding.id) },
930
+ }));
931
+
932
+ probes.push(makeProbe({
933
+ id: "findings.open-high",
934
+ domain: "findings",
935
+ severity: "high",
936
+ gates: ["phase"],
937
+ phaseRelevance: ["R", "A"],
938
+ status: highFindings.length ? "fail" : "pass",
939
+ title: probeTitle("findings.open-high"),
940
+ message: highFindings.length
941
+ ? t("quality.message.countFail", { subject: probeTitle("findings.open-high"), count: String(highFindings.length) })
942
+ : t("quality.message.ok", { subject: probeTitle("findings.open-high") }),
943
+ recommendation: probeRecommendation("findings.open-high"),
944
+ evidence: { findingIds: highFindings.map((finding) => finding.id) },
945
+ }));
946
+
947
+ probes.push(makeProbe({
948
+ id: "bootstrap.version-control",
949
+ domain: "bootstrap",
950
+ severity: "medium",
951
+ gates: ["phase", "promotion"],
952
+ phaseRelevance: ["O", "A"],
953
+ status: !state.hasOpera ? "skip" : intake.versionControl ? "pass" : "fail",
954
+ title: probeTitle("bootstrap.version-control"),
955
+ message: !state.hasOpera
956
+ ? t("quality.message.notApplicable", { subject: probeTitle("bootstrap.version-control") })
957
+ : intake.versionControl
958
+ ? t("quality.message.recordedFrom", {
959
+ subject: probeTitle("bootstrap.version-control"),
960
+ source: t(`quality.source.${state.bootstrapArtifacts.sources.versionControl || "unknown"}`),
961
+ })
962
+ : t("quality.message.failDetail", { subject: probeTitle("bootstrap.version-control"), detail: t("quality.value.missing") }),
963
+ recommendation: probeRecommendation("bootstrap.version-control"),
964
+ evidence: { versionControl: intake.versionControl || null, source: state.bootstrapArtifacts.sources.versionControl || null },
965
+ }));
966
+
967
+ probes.push(makeProbe({
968
+ id: "bootstrap.deployment",
969
+ domain: "bootstrap",
970
+ severity: "medium",
971
+ gates: ["phase", "promotion"],
972
+ phaseRelevance: ["O", "A"],
973
+ status: !state.hasOpera ? "skip" : intake.deployment ? "pass" : "fail",
974
+ title: probeTitle("bootstrap.deployment"),
975
+ message: !state.hasOpera
976
+ ? t("quality.message.notApplicable", { subject: probeTitle("bootstrap.deployment") })
977
+ : intake.deployment
978
+ ? t("quality.message.recordedFrom", {
979
+ subject: probeTitle("bootstrap.deployment"),
980
+ source: t(`quality.source.${state.bootstrapArtifacts.sources.deployment || "unknown"}`),
981
+ })
982
+ : t("quality.message.failDetail", { subject: probeTitle("bootstrap.deployment"), detail: t("quality.value.missing") }),
983
+ recommendation: probeRecommendation("bootstrap.deployment"),
984
+ evidence: { deployment: intake.deployment || null, source: state.bootstrapArtifacts.sources.deployment || null },
985
+ }));
986
+
987
+ probes.push(makeProbe({
988
+ id: "verification.configured",
989
+ domain: "verification",
990
+ severity: "low",
991
+ gates: ["phase"],
992
+ phaseRelevance: ["P", "R", "A"],
993
+ status: verificationConfigured ? "pass" : "skip",
994
+ title: probeTitle("verification.configured"),
995
+ message: verificationConfigured
996
+ ? t("quality.message.ok", { subject: probeTitle("verification.configured") })
997
+ : t("quality.message.notApplicable", { subject: probeTitle("verification.configured") }),
998
+ recommendation: probeRecommendation("verification.configured"),
999
+ evidence: {
1000
+ testCommands: state.verificationConfig.testCommands.map((item) => item.command),
1001
+ buildCommands: state.verificationConfig.buildCommands.map((item) => item.command),
1002
+ smokeCommands: state.verificationConfig.smokeCommands.map((item) => item.command),
1003
+ reviewRequired: state.verificationConfig.reviewRequired,
1004
+ },
1005
+ }));
1006
+
1007
+ return probes;
1008
+ }
1009
+
1010
+ function buildQualityState(contextOrRoot, controlState) {
1011
+ const context = config.ensureContext(contextOrRoot);
1012
+ const control = controlState || config.loadControl(context);
1013
+ setLocale(config.getLocale(control));
1014
+ const derived = controlLib.derive(control);
1015
+ const repo = controlLib.getRepoSnapshot(context);
1016
+ const drift = controlLib.getDocDrift(context, control);
1017
+ const envState = env.auditEnvironment(context, control);
1018
+ const contract = loadContract(context);
1019
+ const policy = loadPolicy(context);
1020
+ const pkgInfo = parsePackageScripts(context);
1021
+ const latest = latestVerificationSummary(context);
1022
+ const bootstrapArtifacts = loadBootstrapArtifacts(context, control, { repo, pkgInfo });
1023
+ const verificationConfig = inferVerificationConfig(context, control, bootstrapArtifacts.intake, pkgInfo);
1024
+ const activeProfiles = detectProfiles(context, contract, bootstrapArtifacts.intake, envState, pkgInfo, verificationConfig);
1025
+ return {
1026
+ __qualityState: true,
1027
+ context,
1028
+ control,
1029
+ derived,
1030
+ repo,
1031
+ drift,
1032
+ envState,
1033
+ contract,
1034
+ policy,
1035
+ pkgInfo,
1036
+ latestVerification: latest,
1037
+ bootstrapArtifacts,
1038
+ verificationConfig,
1039
+ activeProfiles,
1040
+ hasOpera: config.isOperaInstalled(control),
1041
+ };
1042
+ }
1043
+
1044
+ function ensureQualityState(contextOrRoot, controlState) {
1045
+ if (contextOrRoot && typeof contextOrRoot === "object" && contextOrRoot.__qualityState === true) {
1046
+ return contextOrRoot;
1047
+ }
1048
+ return buildQualityState(contextOrRoot, controlState);
1049
+ }
1050
+
1051
+ function buildQualityReportFromState(state) {
1052
+ const probes = buildProbes(state);
1053
+ const summary = summarizeProbes(probes);
1054
+ return {
1055
+ generatedAt: nowIso(),
1056
+ projectName: state.control.meta?.projectName || path.basename(state.context.workspaceRoot),
1057
+ currentPhase: state.derived.activePhase?.id || state.control.meta?.focusPhase || "O",
1058
+ activeProfiles: state.activeProfiles,
1059
+ summary,
1060
+ probes,
1061
+ verification: {
1062
+ config: {
1063
+ reviewRequired: state.verificationConfig.reviewRequired,
1064
+ testCommands: state.verificationConfig.testCommands.map((item) => item.command),
1065
+ buildCommands: state.verificationConfig.buildCommands.map((item) => item.command),
1066
+ smokeCommands: state.verificationConfig.smokeCommands.map((item) => item.command),
1067
+ },
1068
+ latest: state.latestVerification.verification?.runs || {},
1069
+ lastVerificationAt: state.latestVerification.lastVerificationAt || state.control.meta?.quality?.lastVerificationAt || null,
1070
+ },
1071
+ };
1072
+ }
1073
+
1074
+ function buildQualityReport(contextOrRoot, controlState) {
1075
+ return buildQualityReportFromState(ensureQualityState(contextOrRoot, controlState));
1076
+ }
1077
+
1078
+ function recomputeSummaryWithFilters(report, filters = {}) {
1079
+ const domain = filters.domain ? String(filters.domain).trim() : null;
1080
+ const phase = filters.phase && filters.phase !== "current"
1081
+ ? String(filters.phase).trim()
1082
+ : filters.phase === "current"
1083
+ ? report.currentPhase
1084
+ : null;
1085
+ const probes = report.probes.filter((probe) => {
1086
+ if (domain && probe.domain !== domain) return false;
1087
+ if (phase && probe.phaseRelevance.length && !probe.phaseRelevance.includes(phase)) return false;
1088
+ return true;
1089
+ });
1090
+ return {
1091
+ ...report,
1092
+ probes,
1093
+ summary: summarizeProbes(probes),
1094
+ };
1095
+ }
1096
+
1097
+ function normalizeWaiver(raw) {
1098
+ return {
1099
+ id: String(raw?.id || "").trim(),
1100
+ probeId: String(raw?.probeId || "").trim(),
1101
+ scope: String(raw?.scope || "").trim(),
1102
+ reason: String(raw?.reason || "").trim(),
1103
+ approvedBy: String(raw?.approvedBy || "").trim(),
1104
+ expiresAt: raw?.expiresAt ? String(raw.expiresAt).trim() : null,
1105
+ createdAt: raw?.createdAt ? String(raw.createdAt).trim() : null,
1106
+ };
1107
+ }
1108
+
1109
+ function listWaivers(contextOrRoot) {
1110
+ const context = config.ensureContext(contextOrRoot);
1111
+ const waivers = readJson(context.paths.qualityWaiversFile, []);
1112
+ return Array.isArray(waivers) ? waivers.map((item) => normalizeWaiver(item)).filter((item) => item.id) : [];
1113
+ }
1114
+
1115
+ function addWaiver(contextOrRoot, payload) {
1116
+ const context = config.ensureContext(contextOrRoot);
1117
+ const probeId = String(payload?.probeId || "").trim();
1118
+ const scope = String(payload?.scope || "").trim();
1119
+ const reason = String(payload?.reason || "").trim();
1120
+ const approvedBy = String(payload?.approvedBy || "").trim();
1121
+ const expiresAt = payload?.expiresAt ? String(payload.expiresAt).trim() : null;
1122
+
1123
+ if (!probeId) throw new Error(t("quality.error.waiverProbeRequired"));
1124
+ if (!["release", "promotion"].includes(scope)) throw new Error(t("quality.error.waiverScope"));
1125
+ if (!reason) throw new Error(t("quality.error.waiverReason"));
1126
+ if (!approvedBy) throw new Error(t("quality.error.waiverApprovedBy"));
1127
+ if (scope === "promotion" && !expiresAt) throw new Error(t("quality.error.waiverExpiry"));
1128
+
1129
+ ensureQualityStorage(context);
1130
+ const waivers = listWaivers(context);
1131
+ const waiver = {
1132
+ id: `waiver-${Date.now().toString(36)}`,
1133
+ probeId,
1134
+ scope,
1135
+ reason,
1136
+ approvedBy,
1137
+ expiresAt,
1138
+ createdAt: nowIso(),
1139
+ };
1140
+ waivers.push(waiver);
1141
+ writeJson(context.paths.qualityWaiversFile, waivers);
1142
+ return waiver;
1143
+ }
1144
+
1145
+ function activeWaiverFor(waivers, probeId, scope) {
1146
+ const now = Date.now();
1147
+ return waivers.find((waiver) => {
1148
+ if (waiver.probeId !== probeId || waiver.scope !== scope) return false;
1149
+ if (!waiver.expiresAt) return true;
1150
+ const expiry = Date.parse(waiver.expiresAt);
1151
+ return !Number.isFinite(expiry) || expiry > now;
1152
+ }) || null;
1153
+ }
1154
+
1155
+ function verificationRequirements(state) {
1156
+ return {
1157
+ test: state.verificationConfig.testCommands.length > 0,
1158
+ build: state.verificationConfig.buildCommands.length > 0,
1159
+ smoke: state.verificationConfig.smokeCommands.length > 0,
1160
+ review: state.verificationConfig.reviewRequired === true,
1161
+ };
1162
+ }
1163
+
1164
+ function verificationBlockers(state, scope) {
1165
+ const latestRuns = state.latestVerification.verification?.runs || {};
1166
+ const requirements = verificationRequirements(state);
1167
+ const blockers = [];
1168
+ const evaluateScope = (name, commands, required) => {
1169
+ if (!required) return;
1170
+ const latestRun = latestRuns[name];
1171
+ const expectedCommands = name === "review"
1172
+ ? [{ command: "manual-review", cwd: state.context.workspaceRoot }]
1173
+ : commands;
1174
+ const expectedSignature = buildCommandSignature(expectedCommands);
1175
+ const signatureMatches = latestRun?.commandSignature === expectedSignature;
1176
+ if (latestRun && latestRun.status === "passed" && signatureMatches) return;
1177
+ blockers.push({
1178
+ id: `verification.${name}`,
1179
+ source: "verification",
1180
+ severity: name === "review" ? "medium" : "high",
1181
+ message: !latestRun
1182
+ ? t("quality.verification.blocker.missing", { scope: formatScopeLabel(name) })
1183
+ : latestRun.status !== "passed"
1184
+ ? t("quality.verification.blocker.failed", { scope: formatScopeLabel(name) })
1185
+ : t("quality.verification.blocker.stale", { scope: formatScopeLabel(name) }),
1186
+ recommendation: t("quality.verification.blocker.recommendation", { scope: name }),
1187
+ evidence: latestRun || {
1188
+ commands: expectedCommands.map((item) => item.command),
1189
+ commandSignature: expectedSignature,
1190
+ },
1191
+ });
1192
+ };
1193
+
1194
+ if (scope === "release" || scope === "promotion") {
1195
+ evaluateScope("test", state.verificationConfig.testCommands, requirements.test);
1196
+ evaluateScope("build", state.verificationConfig.buildCommands, requirements.build);
1197
+ evaluateScope("smoke", state.verificationConfig.smokeCommands, requirements.smoke);
1198
+ evaluateScope("review", [], requirements.review);
1199
+ }
1200
+ return blockers;
1201
+ }
1202
+
1203
+ function evaluatePhaseReadinessFromState(state, options = {}) {
1204
+ const phaseId = options.phase && options.phase !== "current"
1205
+ ? String(options.phase).trim()
1206
+ : state.derived.activePhase?.id || state.control.meta?.focusPhase || "O";
1207
+ const definition = PHASE_DOD[phaseId] || PHASE_DOD.O;
1208
+ const checks = definition.checks.map((check) => {
1209
+ const result = check.evaluate({
1210
+ ...state,
1211
+ fs,
1212
+ fileExists,
1213
+ normalizeFindingSeverity,
1214
+ }) || { status: "skip" };
1215
+ const status = ["pass", "fail", "warn", "skip"].includes(result.status) ? result.status : "skip";
1216
+ const title = t(check.titleKey);
1217
+ const detail = hasText(result.detail) ? String(result.detail).trim() : "";
1218
+ const messageKey = status === "pass"
1219
+ ? "quality.phase.message.pass"
1220
+ : status === "fail"
1221
+ ? (detail ? "quality.phase.message.failDetail" : "quality.phase.message.fail")
1222
+ : status === "warn"
1223
+ ? (detail ? "quality.phase.message.warnDetail" : "quality.phase.message.warn")
1224
+ : "quality.phase.message.skip";
1225
+ return {
1226
+ id: check.id,
1227
+ severity: check.severity || "medium",
1228
+ status,
1229
+ title,
1230
+ message: detail ? t(messageKey, { title, detail }) : t(messageKey, { title }),
1231
+ recommendation: status === "pass" || status === "skip" ? "" : t(check.recommendationKey),
1232
+ evidence: result.evidence || {},
1233
+ };
1234
+ });
1235
+
1236
+ const blockers = checks.filter((check) => check.status === "fail");
1237
+ const warnings = checks.filter((check) => check.status === "warn");
1238
+ return {
1239
+ kind: "phase",
1240
+ phase: phaseId,
1241
+ generatedAt: nowIso(),
1242
+ ready: blockers.length === 0 && warnings.length === 0,
1243
+ status: blockers.length || warnings.length ? "attention" : "ready",
1244
+ blockers,
1245
+ warnings,
1246
+ checks,
1247
+ definition: {
1248
+ id: definition.id,
1249
+ title: t(definition.titleKey),
1250
+ },
1251
+ };
1252
+ }
1253
+
1254
+ function applyWaiversToItems(waivers, scope, items) {
1255
+ return items.map((item) => {
1256
+ const waiver = activeWaiverFor(waivers, item.id, scope);
1257
+ return { ...item, waived: Boolean(waiver), waiver };
1258
+ });
1259
+ }
1260
+
1261
+ function resolveReadinessFromState(kind, state, options = {}) {
1262
+ if (kind === "phase") {
1263
+ return evaluatePhaseReadinessFromState(state, options);
1264
+ }
1265
+
1266
+ const report = buildQualityReportFromState(state);
1267
+ const waivers = listWaivers(state.context);
1268
+ const scope = kind === "promotion" ? "promotion" : "release";
1269
+ const probeCandidates = report.probes
1270
+ .filter((probe) => probe.status === "fail" && probe.gates.includes(scope))
1271
+ .map((probe) => ({
1272
+ id: probe.id,
1273
+ source: "probe",
1274
+ severity: probe.severity,
1275
+ message: probe.message,
1276
+ recommendation: probe.recommendation,
1277
+ evidence: probe.evidence,
1278
+ }));
1279
+
1280
+ const extraBlockers = verificationBlockers(state, scope);
1281
+ if (kind === "promotion" && String(options.target || "").trim() === "production") {
1282
+ if (!state.bootstrapArtifacts.intake?.versionControl) {
1283
+ extraBlockers.push({
1284
+ id: "promotion.version-control-metadata",
1285
+ source: "policy",
1286
+ severity: "high",
1287
+ message: t("quality.promotion.versionControlRequired"),
1288
+ recommendation: t("quality.promotion.versionControlRecommendation"),
1289
+ evidence: { versionControl: null },
1290
+ });
1291
+ }
1292
+ const deployment = state.bootstrapArtifacts.intake?.deployment || null;
1293
+ if (!deployment?.target || !deployment?.mode || !deployment?.smokeCommand) {
1294
+ extraBlockers.push({
1295
+ id: "promotion.deployment-metadata",
1296
+ source: "policy",
1297
+ severity: "high",
1298
+ message: t("quality.promotion.deploymentRequired"),
1299
+ recommendation: t("quality.promotion.deploymentRecommendation"),
1300
+ evidence: { deployment },
1301
+ });
1302
+ }
1303
+ if (!state.policy.valid || state.policy.data?.approvalRules?.production_deploy_requires_approval !== true) {
1304
+ extraBlockers.push({
1305
+ id: "promotion.policy-approval",
1306
+ source: "policy",
1307
+ severity: "critical",
1308
+ message: t("quality.promotion.policyApprovalRequired"),
1309
+ recommendation: t("quality.promotion.policyApprovalRecommendation"),
1310
+ evidence: { approvalRules: state.policy.data?.approvalRules || null, valid: state.policy.valid },
1311
+ });
1312
+ }
1313
+ }
1314
+
1315
+ const candidates = applyWaiversToItems(waivers, scope, [...probeCandidates, ...extraBlockers]);
1316
+ const unwaived = candidates.filter((item) => !item.waived);
1317
+ const blockers = unwaived.filter((item) => BLOCKING_SEVERITIES.has(item.severity));
1318
+ const warnings = unwaived.filter((item) => !BLOCKING_SEVERITIES.has(item.severity));
1319
+
1320
+ return {
1321
+ kind,
1322
+ target: kind === "promotion" ? String(options.target || "").trim() || "staging" : null,
1323
+ phase: null,
1324
+ generatedAt: nowIso(),
1325
+ ready: blockers.length === 0,
1326
+ status: blockers.length ? "blocked" : warnings.length ? "attention" : "ready",
1327
+ blockers,
1328
+ warnings,
1329
+ waived: candidates.filter((item) => item.waived),
1330
+ verification: state.latestVerification.verification?.runs || {},
1331
+ };
1332
+ }
1333
+
1334
+ function buildPhaseReadiness(contextOrRoot, controlState, options = {}) {
1335
+ return resolveReadinessFromState("phase", ensureQualityState(contextOrRoot, controlState), options);
1336
+ }
1337
+
1338
+ function buildReleaseReadiness(contextOrRoot, controlState, options = {}) {
1339
+ return resolveReadinessFromState("release", ensureQualityState(contextOrRoot, controlState), options);
1340
+ }
1341
+
1342
+ function buildPromotionReadiness(contextOrRoot, controlState, options = {}) {
1343
+ return resolveReadinessFromState("promotion", ensureQualityState(contextOrRoot, controlState), options);
1344
+ }
1345
+
1346
+ function updateQualityMeta(context, controlState, patch) {
1347
+ controlState.meta = controlState.meta || {};
1348
+ controlState.meta.quality = config.normalizeQualityMeta({
1349
+ ...(controlState.meta.quality || {}),
1350
+ ...(patch || {}),
1351
+ });
1352
+ }
1353
+
1354
+ function persistVerificationLatest(context, runSummary) {
1355
+ ensureQualityStorage(context);
1356
+ const latest = latestVerificationSummary(context);
1357
+ latest.generatedAt = nowIso();
1358
+ if (runSummary.results.some((result) => result.executed)) {
1359
+ latest.lastVerificationAt = runSummary.finishedAt;
1360
+ }
1361
+ latest.verification = latest.verification || { runs: {} };
1362
+ latest.verification.runs = latest.verification.runs || {};
1363
+
1364
+ for (const result of runSummary.results) {
1365
+ if (!result.executed) continue;
1366
+ latest.verification.runs[result.scope] = {
1367
+ status: result.status,
1368
+ commands: result.commands.map((item) => ({
1369
+ command: item.command,
1370
+ cwd: normalizePath(item.cwd),
1371
+ })),
1372
+ commandSignature: buildCommandSignature(result.commands),
1373
+ startedAt: result.startedAt,
1374
+ finishedAt: result.finishedAt,
1375
+ exitCode: result.exitCode,
1376
+ note: result.note || "",
1377
+ commit: result.commit || null,
1378
+ };
1379
+ }
1380
+
1381
+ writeJson(context.paths.qualityLatestFile, latest);
1382
+ }
1383
+
1384
+ function truncateText(text, limit = 1200) {
1385
+ const value = String(text || "");
1386
+ if (value.length <= limit) return value;
1387
+ return `${value.slice(0, limit)}\n...[truncated]`;
1388
+ }
1389
+
1390
+ function runVerificationCommand(spec) {
1391
+ const startedAt = nowIso();
1392
+ const result = spawnSync(spec.command, {
1393
+ cwd: spec.cwd,
1394
+ shell: true,
1395
+ encoding: "utf8",
1396
+ });
1397
+ const finishedAt = nowIso();
1398
+ return {
1399
+ command: spec.command,
1400
+ cwd: spec.cwd,
1401
+ startedAt,
1402
+ finishedAt,
1403
+ exitCode: Number.isInteger(result.status) ? result.status : 1,
1404
+ stdout: truncateText(result.stdout || ""),
1405
+ stderr: truncateText(result.stderr || ""),
1406
+ status: result.status === 0 ? "passed" : "failed",
1407
+ };
1408
+ }
1409
+
1410
+ function scopeCommandSpecs(state, scope) {
1411
+ const map = {
1412
+ test: state.verificationConfig.testCommands,
1413
+ build: state.verificationConfig.buildCommands,
1414
+ smoke: state.verificationConfig.smokeCommands,
1415
+ };
1416
+ return (map[scope] || []).map((item) => ({ command: item.command, cwd: item.cwd }));
1417
+ }
1418
+
1419
+ function aggregateVerificationScope(state, scope, options = {}) {
1420
+ const note = String(options.note || "").trim();
1421
+ const commit = options.commit || null;
1422
+ const specific = options.specific === true;
1423
+ const at = nowIso();
1424
+
1425
+ if (scope === "review") {
1426
+ if (!state.verificationConfig.reviewRequired) {
1427
+ return {
1428
+ scope,
1429
+ status: "skipped",
1430
+ reason: "not_required",
1431
+ required: false,
1432
+ executed: false,
1433
+ commands: [],
1434
+ startedAt: at,
1435
+ finishedAt: at,
1436
+ exitCode: 0,
1437
+ note,
1438
+ commit,
1439
+ };
1440
+ }
1441
+ if (!note) {
1442
+ return {
1443
+ scope,
1444
+ status: "failed",
1445
+ reason: "missing_note",
1446
+ required: true,
1447
+ executed: true,
1448
+ commands: [{ command: "manual-review", cwd: state.context.workspaceRoot, status: "failed", exitCode: 1, startedAt: at, finishedAt: at, stdout: "", stderr: t("quality.verify.reviewNoteRequired") }],
1449
+ startedAt: at,
1450
+ finishedAt: at,
1451
+ exitCode: 1,
1452
+ note,
1453
+ commit,
1454
+ };
1455
+ }
1456
+ return {
1457
+ scope,
1458
+ status: "passed",
1459
+ reason: null,
1460
+ required: true,
1461
+ executed: true,
1462
+ commands: [{ command: "manual-review", cwd: state.context.workspaceRoot, status: "passed", exitCode: 0, startedAt: at, finishedAt: at, stdout: "", stderr: "" }],
1463
+ startedAt: at,
1464
+ finishedAt: at,
1465
+ exitCode: 0,
1466
+ note,
1467
+ commit,
1468
+ };
1469
+ }
1470
+
1471
+ const specs = scopeCommandSpecs(state, scope);
1472
+ if (!specs.length) {
1473
+ return {
1474
+ scope,
1475
+ status: specific ? "not_configured" : "skipped",
1476
+ reason: specific ? "missing_config" : "not_required",
1477
+ required: specific,
1478
+ executed: false,
1479
+ commands: [],
1480
+ startedAt: at,
1481
+ finishedAt: at,
1482
+ exitCode: specific ? 1 : 0,
1483
+ note,
1484
+ commit,
1485
+ };
1486
+ }
1487
+
1488
+ const commands = specs.map((spec) => runVerificationCommand(spec));
1489
+ return {
1490
+ scope,
1491
+ status: commands.every((item) => item.status === "passed") ? "passed" : "failed",
1492
+ reason: null,
1493
+ required: true,
1494
+ executed: true,
1495
+ commands,
1496
+ startedAt: commands[0]?.startedAt || at,
1497
+ finishedAt: commands[commands.length - 1]?.finishedAt || at,
1498
+ exitCode: commands.every((item) => item.exitCode === 0) ? 0 : 1,
1499
+ note,
1500
+ commit,
1501
+ };
1502
+ }
1503
+
1504
+ function requestedScopes(scope) {
1505
+ const normalized = String(scope || "all").trim().toLowerCase();
1506
+ if (normalized === "all") return ["test", "build", "smoke", "review"];
1507
+ return [normalized];
1508
+ }
1509
+
1510
+ function summarizeVerificationResults(results) {
1511
+ if (results.some((result) => result.status === "failed" || result.status === "not_configured")) {
1512
+ return { status: "failed", ok: false };
1513
+ }
1514
+ if (results.every((result) => result.status === "skipped")) {
1515
+ return { status: "skipped", ok: true };
1516
+ }
1517
+ if (results.every((result) => result.status === "passed" || result.status === "skipped")) {
1518
+ return { status: "passed", ok: true };
1519
+ }
1520
+ return { status: "failed", ok: false };
1521
+ }
1522
+
1523
+ function verify(contextOrRoot, options = {}) {
1524
+ const state = ensureQualityState(contextOrRoot);
1525
+ const context = state.context;
1526
+ const controlState = state.control;
1527
+ const scope = String(options.scope || "all").trim().toLowerCase();
1528
+ const note = String(options.note || "").trim();
1529
+ const commit = getGitCommit(context);
1530
+ const results = requestedScopes(scope).map((requestedScope) => aggregateVerificationScope(state, requestedScope, {
1531
+ note,
1532
+ commit,
1533
+ specific: scope !== "all",
1534
+ }));
1535
+ const finishedAt = nowIso();
1536
+ const summary = summarizeVerificationResults(results);
1537
+ const runId = finishedAt.replace(/[:.]/g, "-");
1538
+ const runSummary = {
1539
+ id: `quality-${runId}`,
1540
+ generatedAt: nowIso(),
1541
+ startedAt: results[0]?.startedAt || finishedAt,
1542
+ finishedAt,
1543
+ scope,
1544
+ note,
1545
+ commit,
1546
+ status: summary.status,
1547
+ results,
1548
+ };
1549
+
1550
+ ensureQualityStorage(context);
1551
+ writeJson(path.join(context.paths.qualityRunsDir, `${runId}.json`), runSummary);
1552
+ persistVerificationLatest(context, runSummary);
1553
+
1554
+ const latestByScope = Object.fromEntries(results.map((item) => [item.scope, item]));
1555
+ controlState.checks = controlState.checks || {};
1556
+ if (latestByScope.test?.executed) {
1557
+ controlState.checks.lastTest = {
1558
+ status: latestByScope.test.status === "passed" ? "pass" : "fail",
1559
+ date: latestByScope.test.finishedAt,
1560
+ note: latestByScope.test.note || latestByScope.test.commands.map((item) => item.command).join(" && "),
1561
+ };
1562
+ }
1563
+ if (latestByScope.build?.executed) {
1564
+ controlState.checks.lastBuild = {
1565
+ status: latestByScope.build.status === "passed" ? "pass" : "fail",
1566
+ date: latestByScope.build.finishedAt,
1567
+ note: latestByScope.build.note || latestByScope.build.commands.map((item) => item.command).join(" && "),
1568
+ };
1569
+ }
1570
+ if (latestByScope.review?.executed) {
1571
+ controlState.checks.lastReview = {
1572
+ status: latestByScope.review.status === "passed" ? "pass" : "fail",
1573
+ date: latestByScope.review.finishedAt,
1574
+ note: latestByScope.review.note || "manual-review",
1575
+ };
1576
+ }
1577
+ const activeProfiles = buildQualityReportFromState(buildQualityState(context, controlState)).activeProfiles;
1578
+ updateQualityMeta(context, controlState, {
1579
+ activeProfiles,
1580
+ lastVerificationAt: results.some((result) => result.executed) ? finishedAt : controlState.meta?.quality?.lastVerificationAt || null,
1581
+ });
1582
+ config.saveControl(context, controlState);
1583
+ controlLib.syncDocs(context, controlState);
1584
+ controlLib.refreshRepoRuntime(context, { quiet: true });
1585
+ return runSummary;
1586
+ }
1587
+
1588
+ function buildQualitySnapshot(contextOrRoot, controlState) {
1589
+ const state = ensureQualityState(contextOrRoot, controlState);
1590
+ return {
1591
+ report: buildQualityReportFromState(state),
1592
+ phaseReadiness: buildPhaseReadiness(state, null, { phase: "current" }),
1593
+ releaseReadiness: buildReleaseReadiness(state),
1594
+ promotionReadiness: buildPromotionReadiness(state, null, { target: "production" }),
1595
+ waivers: listWaivers(state.context),
1596
+ };
1597
+ }
1598
+
1599
+ function parseFlagValue(args, flagName) {
1600
+ const index = args.indexOf(flagName);
1601
+ if (index === -1) return null;
1602
+ return args[index + 1] || null;
1603
+ }
1604
+
1605
+ function printReadiness(readiness) {
1606
+ const label = readiness.kind === "phase"
1607
+ ? t("quality.readiness.phase", { phase: readiness.phase })
1608
+ : readiness.kind === "promotion"
1609
+ ? t("quality.readiness.promotion", { target: readiness.target })
1610
+ : t("quality.readiness.release");
1611
+ console.log(`${label}: ${formatQualityStatus(readiness.status)}`);
1612
+ if (readiness.blockers.length) {
1613
+ console.log("");
1614
+ console.log(t("quality.section.blockers"));
1615
+ readiness.blockers.forEach((item) => console.log(`- [${item.severity}] ${item.id}: ${item.message}`));
1616
+ }
1617
+ if (readiness.warnings.length) {
1618
+ console.log("");
1619
+ console.log(t("quality.section.warnings"));
1620
+ readiness.warnings.forEach((item) => console.log(`- [${item.severity}] ${item.id}: ${item.message}`));
1621
+ }
1622
+ if (readiness.waived?.length) {
1623
+ console.log("");
1624
+ console.log(t("quality.section.waived"));
1625
+ readiness.waived.forEach((item) => console.log(`- ${item.id} (${item.waiver.id})`));
1626
+ }
1627
+ }
1628
+
1629
+ function printStatusReport(report) {
1630
+ console.log(`${t("quality.section.status")}: ${formatQualityStatus(report.summary.overallStatus)}`);
1631
+ console.log(`${t("quality.section.currentPhase")}: ${report.currentPhase}`);
1632
+ console.log(`${t("quality.section.profiles")}: ${report.activeProfiles.join(", ") || t("locale.none")}`);
1633
+ console.log(`${t("quality.section.probes")}: pass=${report.summary.counts.pass} fail=${report.summary.counts.fail} skip=${report.summary.counts.skip}`);
1634
+ const failures = report.probes.filter((probe) => probe.status === "fail");
1635
+ if (failures.length) {
1636
+ console.log("");
1637
+ console.log(t("quality.section.findings"));
1638
+ failures.forEach((probe) => console.log(`- [${probe.severity}] ${probe.id}: ${probe.message}`));
1639
+ }
1640
+ }
1641
+
1642
+ function printVerification(runSummary) {
1643
+ console.log(`${t("quality.section.verification")}: ${formatQualityStatus(runSummary.status)}`);
1644
+ runSummary.results.forEach((result) => {
1645
+ console.log(`- ${formatScopeLabel(result.scope)}: ${formatQualityStatus(result.status)}`);
1646
+ });
1647
+ }
1648
+
1649
+ function printWaivers(waivers) {
1650
+ if (!waivers.length) {
1651
+ console.log(t("quality.waivers.none"));
1652
+ return;
1653
+ }
1654
+ waivers.forEach((waiver) => {
1655
+ console.log(`- ${waiver.id}: ${waiver.probeId} (${waiver.scope}) ${t("quality.waivers.approvedBy", { approvedBy: waiver.approvedBy })}`);
1656
+ });
1657
+ }
1658
+
1659
+ function printUsage() {
1660
+ console.log(t("quality.usage.title"));
1661
+ console.log(` ${t("quality.usage.status")}`);
1662
+ console.log(` ${t("quality.usage.verify")}`);
1663
+ console.log(` ${t("quality.usage.phaseReadiness")}`);
1664
+ console.log(` ${t("quality.usage.releaseReadiness")}`);
1665
+ console.log(` ${t("quality.usage.promoteReadiness")}`);
1666
+ console.log(` ${t("quality.usage.waiverList")}`);
1667
+ console.log(` ${t("quality.usage.waiverAdd")}`);
1668
+ }
1669
+
1670
+ function cmdQuality(root, args = []) {
1671
+ const context = config.ensureContext(root);
1672
+ const control = config.loadControl(context);
1673
+ setLocale(config.getLocale(control));
1674
+ const sub = String(args[0] || "status").trim();
1675
+ const json = args.includes("--json");
1676
+
1677
+ if (sub === "status") {
1678
+ const report = recomputeSummaryWithFilters(buildQualityReport(context, control), {
1679
+ domain: parseFlagValue(args, "--domain"),
1680
+ phase: parseFlagValue(args, "--phase"),
1681
+ });
1682
+ if (json) console.log(JSON.stringify(report, null, 2));
1683
+ else printStatusReport(report);
1684
+ return;
1685
+ }
1686
+
1687
+ if (sub === "verify") {
1688
+ const runSummary = verify(context, {
1689
+ scope: parseFlagValue(args, "--scope") || "all",
1690
+ note: parseFlagValue(args, "--note") || "",
1691
+ });
1692
+ if (json) console.log(JSON.stringify(runSummary, null, 2));
1693
+ else printVerification(runSummary);
1694
+ if (runSummary.status === "failed") process.exitCode = 1;
1695
+ return;
1696
+ }
1697
+
1698
+ if (sub === "phase-readiness") {
1699
+ const readiness = buildPhaseReadiness(context, control, { phase: parseFlagValue(args, "--phase") || "current" });
1700
+ if (json) console.log(JSON.stringify(readiness, null, 2));
1701
+ else printReadiness(readiness);
1702
+ return;
1703
+ }
1704
+
1705
+ if (sub === "release-readiness") {
1706
+ const readiness = buildReleaseReadiness(context, control);
1707
+ if (json) console.log(JSON.stringify(readiness, null, 2));
1708
+ else printReadiness(readiness);
1709
+ if (!readiness.ready) process.exitCode = 1;
1710
+ return;
1711
+ }
1712
+
1713
+ if (sub === "promote-readiness") {
1714
+ const readiness = buildPromotionReadiness(context, control, { target: parseFlagValue(args, "--target") || "staging" });
1715
+ if (json) console.log(JSON.stringify(readiness, null, 2));
1716
+ else printReadiness(readiness);
1717
+ if (!readiness.ready) process.exitCode = 1;
1718
+ return;
1719
+ }
1720
+
1721
+ if (sub === "waiver") {
1722
+ const action = String(args[1] || "list").trim();
1723
+ if (action === "list") {
1724
+ const waivers = listWaivers(context);
1725
+ if (json) console.log(JSON.stringify(waivers, null, 2));
1726
+ else printWaivers(waivers);
1727
+ return;
1728
+ }
1729
+ if (action === "add") {
1730
+ const waiver = addWaiver(context, {
1731
+ probeId: args[2],
1732
+ scope: parseFlagValue(args, "--scope"),
1733
+ reason: parseFlagValue(args, "--reason"),
1734
+ approvedBy: parseFlagValue(args, "--approved-by"),
1735
+ expiresAt: parseFlagValue(args, "--expires-at"),
1736
+ });
1737
+ if (json) console.log(JSON.stringify(waiver, null, 2));
1738
+ else console.log(t("quality.waivers.created", { id: waiver.id }));
1739
+ return;
1740
+ }
1741
+ }
1742
+
1743
+ printUsage();
1744
+ }
1745
+
1746
+ module.exports = {
1747
+ buildQualityReport,
1748
+ buildQualitySnapshot,
1749
+ buildPhaseReadiness,
1750
+ buildReleaseReadiness,
1751
+ buildPromotionReadiness,
1752
+ verify,
1753
+ listWaivers,
1754
+ addWaiver,
1755
+ backfillBootstrapMetadata,
1756
+ normalizeFindingSeverity,
1757
+ formatQualityStatus,
1758
+ cmdQuality,
1759
+ };