trackops 2.0.6 → 2.2.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.
Files changed (60) hide show
  1. package/README.md +307 -701
  2. package/bin/trackops.js +24 -16
  3. package/lib/config.js +265 -58
  4. package/lib/control.js +830 -292
  5. package/lib/init.js +46 -16
  6. package/lib/opera-bootstrap.js +85 -45
  7. package/lib/opera-phase-dod.js +485 -0
  8. package/lib/opera.js +8 -5
  9. package/lib/plans.js +1329 -0
  10. package/lib/quality-assert.js +49 -0
  11. package/lib/quality.js +1759 -0
  12. package/lib/release.js +18 -11
  13. package/lib/server.js +504 -192
  14. package/lib/skills.js +94 -41
  15. package/locales/en.json +249 -15
  16. package/locales/es.json +249 -15
  17. package/package.json +3 -2
  18. package/scripts/quality-unit-tests.js +130 -0
  19. package/scripts/skills-marketplace-smoke.js +156 -124
  20. package/scripts/smoke-tests.js +378 -71
  21. package/scripts/sync-skill-version.js +29 -19
  22. package/scripts/validate-skill.js +188 -103
  23. package/skills/trackops/SKILL.md +25 -7
  24. package/skills/trackops/locales/en/SKILL.md +25 -7
  25. package/skills/trackops/locales/en/references/activation.md +3 -3
  26. package/skills/trackops/locales/en/references/workflow.md +5 -4
  27. package/skills/trackops/references/activation.md +3 -3
  28. package/skills/trackops/references/workflow.md +5 -4
  29. package/skills/trackops/skill.json +29 -29
  30. package/skills/trackops-quality-guard/SKILL.md +78 -0
  31. package/skills/trackops-quality-guard/agents/openai.yaml +7 -0
  32. package/skills/trackops-quality-guard/locales/en/SKILL.md +78 -0
  33. package/skills/trackops-quality-guard/locales/en/references/commands.md +36 -0
  34. package/skills/trackops-quality-guard/locales/en/references/decision-policy.md +16 -0
  35. package/skills/trackops-quality-guard/locales/en/references/output-format.md +24 -0
  36. package/skills/trackops-quality-guard/references/commands.md +36 -0
  37. package/skills/trackops-quality-guard/references/decision-policy.md +16 -0
  38. package/skills/trackops-quality-guard/references/output-format.md +24 -0
  39. package/skills/trackops-quality-guard/skill.json +28 -0
  40. package/templates/skills/opera-skill/SKILL.md +12 -0
  41. package/templates/skills/opera-skill/locales/en/SKILL.md +12 -0
  42. package/templates/skills/trackops-quality-guard/SKILL.md +72 -0
  43. package/templates/skills/trackops-quality-guard/locales/en/SKILL.md +72 -0
  44. package/templates/skills/trackops-quality-guard/locales/en/references/commands.md +30 -0
  45. package/templates/skills/trackops-quality-guard/locales/en/references/decision-policy.md +14 -0
  46. package/templates/skills/trackops-quality-guard/locales/en/references/output-format.md +21 -0
  47. package/templates/skills/trackops-quality-guard/references/commands.md +30 -0
  48. package/templates/skills/trackops-quality-guard/references/decision-policy.md +14 -0
  49. package/templates/skills/trackops-quality-guard/references/output-format.md +21 -0
  50. package/ui/js/api.js +93 -26
  51. package/ui/js/app.js +13 -7
  52. package/ui/js/filters.js +49 -29
  53. package/ui/js/time-tracker.js +41 -28
  54. package/ui/js/views/board.js +22 -14
  55. package/ui/js/views/dashboard.js +206 -49
  56. package/ui/js/views/execution.js +7 -3
  57. package/ui/js/views/plans.js +284 -0
  58. package/ui/js/views/scrum.js +25 -13
  59. package/ui/js/views/sidebar.js +9 -8
  60. package/ui/js/views/tasks.js +238 -134
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
+ };