@veraxhq/verax 0.2.1 → 0.3.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 (152) hide show
  1. package/README.md +14 -18
  2. package/bin/verax.js +7 -0
  3. package/package.json +3 -3
  4. package/src/cli/commands/baseline.js +104 -0
  5. package/src/cli/commands/default.js +79 -25
  6. package/src/cli/commands/ga.js +243 -0
  7. package/src/cli/commands/gates.js +95 -0
  8. package/src/cli/commands/inspect.js +131 -2
  9. package/src/cli/commands/release-check.js +213 -0
  10. package/src/cli/commands/run.js +246 -35
  11. package/src/cli/commands/security-check.js +211 -0
  12. package/src/cli/commands/truth.js +114 -0
  13. package/src/cli/entry.js +304 -67
  14. package/src/cli/util/angular-component-extractor.js +179 -0
  15. package/src/cli/util/angular-navigation-detector.js +141 -0
  16. package/src/cli/util/angular-network-detector.js +161 -0
  17. package/src/cli/util/angular-state-detector.js +162 -0
  18. package/src/cli/util/ast-interactive-detector.js +546 -0
  19. package/src/cli/util/ast-network-detector.js +603 -0
  20. package/src/cli/util/ast-usestate-detector.js +602 -0
  21. package/src/cli/util/bootstrap-guard.js +86 -0
  22. package/src/cli/util/determinism-runner.js +123 -0
  23. package/src/cli/util/determinism-writer.js +129 -0
  24. package/src/cli/util/env-url.js +4 -0
  25. package/src/cli/util/expectation-extractor.js +369 -73
  26. package/src/cli/util/findings-writer.js +126 -16
  27. package/src/cli/util/learn-writer.js +3 -1
  28. package/src/cli/util/observe-writer.js +3 -1
  29. package/src/cli/util/paths.js +3 -12
  30. package/src/cli/util/project-discovery.js +3 -0
  31. package/src/cli/util/project-writer.js +3 -1
  32. package/src/cli/util/run-resolver.js +64 -0
  33. package/src/cli/util/source-requirement.js +55 -0
  34. package/src/cli/util/summary-writer.js +1 -0
  35. package/src/cli/util/svelte-navigation-detector.js +163 -0
  36. package/src/cli/util/svelte-network-detector.js +80 -0
  37. package/src/cli/util/svelte-sfc-extractor.js +147 -0
  38. package/src/cli/util/svelte-state-detector.js +243 -0
  39. package/src/cli/util/vue-navigation-detector.js +177 -0
  40. package/src/cli/util/vue-sfc-extractor.js +162 -0
  41. package/src/cli/util/vue-state-detector.js +215 -0
  42. package/src/verax/cli/finding-explainer.js +56 -3
  43. package/src/verax/core/artifacts/registry.js +154 -0
  44. package/src/verax/core/artifacts/verifier.js +980 -0
  45. package/src/verax/core/baseline/baseline.enforcer.js +137 -0
  46. package/src/verax/core/baseline/baseline.snapshot.js +231 -0
  47. package/src/verax/core/capabilities/gates.js +499 -0
  48. package/src/verax/core/capabilities/registry.js +475 -0
  49. package/src/verax/core/confidence/confidence-compute.js +137 -0
  50. package/src/verax/core/confidence/confidence-invariants.js +234 -0
  51. package/src/verax/core/confidence/confidence-report-writer.js +112 -0
  52. package/src/verax/core/confidence/confidence-weights.js +44 -0
  53. package/src/verax/core/confidence/confidence.defaults.js +65 -0
  54. package/src/verax/core/confidence/confidence.loader.js +79 -0
  55. package/src/verax/core/confidence/confidence.schema.js +94 -0
  56. package/src/verax/core/confidence-engine-refactor.js +484 -0
  57. package/src/verax/core/confidence-engine.js +486 -0
  58. package/src/verax/core/confidence-engine.js.backup +471 -0
  59. package/src/verax/core/contracts/index.js +29 -0
  60. package/src/verax/core/contracts/types.js +185 -0
  61. package/src/verax/core/contracts/validators.js +381 -0
  62. package/src/verax/core/decision-snapshot.js +30 -3
  63. package/src/verax/core/decisions/decision.trace.js +276 -0
  64. package/src/verax/core/determinism/contract-writer.js +89 -0
  65. package/src/verax/core/determinism/contract.js +139 -0
  66. package/src/verax/core/determinism/diff.js +364 -0
  67. package/src/verax/core/determinism/engine.js +221 -0
  68. package/src/verax/core/determinism/finding-identity.js +148 -0
  69. package/src/verax/core/determinism/normalize.js +438 -0
  70. package/src/verax/core/determinism/report-writer.js +92 -0
  71. package/src/verax/core/determinism/run-fingerprint.js +118 -0
  72. package/src/verax/core/dynamic-route-intelligence.js +528 -0
  73. package/src/verax/core/evidence/evidence-capture-service.js +307 -0
  74. package/src/verax/core/evidence/evidence-intent-ledger.js +165 -0
  75. package/src/verax/core/evidence-builder.js +487 -0
  76. package/src/verax/core/execution-mode-context.js +77 -0
  77. package/src/verax/core/execution-mode-detector.js +190 -0
  78. package/src/verax/core/failures/exit-codes.js +86 -0
  79. package/src/verax/core/failures/failure-summary.js +76 -0
  80. package/src/verax/core/failures/failure.factory.js +225 -0
  81. package/src/verax/core/failures/failure.ledger.js +132 -0
  82. package/src/verax/core/failures/failure.types.js +196 -0
  83. package/src/verax/core/failures/index.js +10 -0
  84. package/src/verax/core/ga/ga-report-writer.js +43 -0
  85. package/src/verax/core/ga/ga.artifact.js +49 -0
  86. package/src/verax/core/ga/ga.contract.js +434 -0
  87. package/src/verax/core/ga/ga.enforcer.js +86 -0
  88. package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
  89. package/src/verax/core/guardrails/policy.defaults.js +210 -0
  90. package/src/verax/core/guardrails/policy.loader.js +83 -0
  91. package/src/verax/core/guardrails/policy.schema.js +110 -0
  92. package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
  93. package/src/verax/core/guardrails-engine.js +505 -0
  94. package/src/verax/core/observe/run-timeline.js +316 -0
  95. package/src/verax/core/perf/perf.contract.js +186 -0
  96. package/src/verax/core/perf/perf.display.js +65 -0
  97. package/src/verax/core/perf/perf.enforcer.js +91 -0
  98. package/src/verax/core/perf/perf.monitor.js +209 -0
  99. package/src/verax/core/perf/perf.report.js +198 -0
  100. package/src/verax/core/pipeline-tracker.js +238 -0
  101. package/src/verax/core/product-definition.js +127 -0
  102. package/src/verax/core/release/provenance.builder.js +271 -0
  103. package/src/verax/core/release/release-report-writer.js +40 -0
  104. package/src/verax/core/release/release.enforcer.js +159 -0
  105. package/src/verax/core/release/reproducibility.check.js +221 -0
  106. package/src/verax/core/release/sbom.builder.js +283 -0
  107. package/src/verax/core/report/cross-index.js +192 -0
  108. package/src/verax/core/report/human-summary.js +222 -0
  109. package/src/verax/core/route-intelligence.js +419 -0
  110. package/src/verax/core/security/secrets.scan.js +326 -0
  111. package/src/verax/core/security/security-report.js +50 -0
  112. package/src/verax/core/security/security.enforcer.js +124 -0
  113. package/src/verax/core/security/supplychain.defaults.json +38 -0
  114. package/src/verax/core/security/supplychain.policy.js +326 -0
  115. package/src/verax/core/security/vuln.scan.js +265 -0
  116. package/src/verax/core/truth/truth.certificate.js +250 -0
  117. package/src/verax/core/ui-feedback-intelligence.js +515 -0
  118. package/src/verax/detect/confidence-engine.js +628 -40
  119. package/src/verax/detect/confidence-helper.js +33 -0
  120. package/src/verax/detect/detection-engine.js +18 -1
  121. package/src/verax/detect/dynamic-route-findings.js +335 -0
  122. package/src/verax/detect/expectation-chain-detector.js +417 -0
  123. package/src/verax/detect/expectation-model.js +3 -1
  124. package/src/verax/detect/findings-writer.js +141 -5
  125. package/src/verax/detect/index.js +229 -5
  126. package/src/verax/detect/journey-stall-detector.js +558 -0
  127. package/src/verax/detect/route-findings.js +218 -0
  128. package/src/verax/detect/ui-feedback-findings.js +207 -0
  129. package/src/verax/detect/verdict-engine.js +57 -3
  130. package/src/verax/detect/view-switch-correlator.js +242 -0
  131. package/src/verax/index.js +413 -45
  132. package/src/verax/learn/action-contract-extractor.js +682 -64
  133. package/src/verax/learn/route-validator.js +4 -1
  134. package/src/verax/observe/index.js +88 -843
  135. package/src/verax/observe/interaction-runner.js +25 -8
  136. package/src/verax/observe/observe-context.js +205 -0
  137. package/src/verax/observe/observe-helpers.js +191 -0
  138. package/src/verax/observe/observe-runner.js +226 -0
  139. package/src/verax/observe/observers/budget-observer.js +185 -0
  140. package/src/verax/observe/observers/console-observer.js +102 -0
  141. package/src/verax/observe/observers/coverage-observer.js +107 -0
  142. package/src/verax/observe/observers/interaction-observer.js +471 -0
  143. package/src/verax/observe/observers/navigation-observer.js +132 -0
  144. package/src/verax/observe/observers/network-observer.js +87 -0
  145. package/src/verax/observe/observers/safety-observer.js +82 -0
  146. package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
  147. package/src/verax/observe/ui-feedback-detector.js +742 -0
  148. package/src/verax/observe/ui-signal-sensor.js +148 -2
  149. package/src/verax/scan-summary-writer.js +42 -8
  150. package/src/verax/shared/artifact-manager.js +8 -5
  151. package/src/verax/shared/css-spinner-rules.js +204 -0
  152. package/src/verax/shared/view-switch-rules.js +208 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * PHASE 19 — Capability Gates CLI Command
3
+ *
4
+ * Evaluates all capability gates and reports PASS/FAIL.
5
+ */
6
+
7
+ import {
8
+ evaluateAllCapabilityGates,
9
+ buildGateContext,
10
+ } from '../../verax/core/capabilities/gates.js';
11
+ import { resolve } from 'path';
12
+ import { fileURLToPath } from 'url';
13
+ import { dirname } from 'path';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+
18
+ /**
19
+ * PHASE 19: `verax gates` command
20
+ *
21
+ * @param {Object} options - Options
22
+ * @param {boolean} options.json - Output as JSON
23
+ * @param {boolean} options.verbose - Verbose output
24
+ */
25
+ export async function gatesCommand(options = {}) {
26
+ const { json = false, verbose = false } = options;
27
+
28
+ // Pass explicit project root to ensure correct path resolution
29
+ const projectRoot = resolve(__dirname, '../../..');
30
+ const context = await buildGateContext({ projectRoot });
31
+ const result = evaluateAllCapabilityGates(context);
32
+
33
+ if (json) {
34
+ console.log(JSON.stringify({
35
+ pass: result.pass,
36
+ summary: result.summary,
37
+ perCapability: result.perCapability,
38
+ }, null, 2));
39
+ } else {
40
+ // Human-readable output
41
+ console.log('\n' + '='.repeat(80));
42
+ console.log('CAPABILITY GATES EVALUATION');
43
+ console.log('='.repeat(80));
44
+ console.log(`\nStatus: ${result.pass ? '✅ PASS' : '❌ FAIL'}`);
45
+ console.log(`Total Capabilities: ${result.summary.total}`);
46
+ console.log(`Passing: ${result.summary.pass}`);
47
+ console.log(`Failing: ${result.summary.fail}`);
48
+
49
+ if (!result.pass) {
50
+ console.log('\n' + '-'.repeat(80));
51
+ console.log('FAILING CAPABILITIES:');
52
+ console.log('-'.repeat(80));
53
+
54
+ for (const failure of result.summary.allFailures) {
55
+ console.log(`\n❌ ${failure.capabilityId}`);
56
+ for (const gateFailure of failure.failures) {
57
+ console.log(` └─ ${gateFailure.reasonCode}: ${gateFailure.message}`);
58
+ }
59
+ if (failure.warnings && failure.warnings.length > 0) {
60
+ for (const warning of failure.warnings) {
61
+ console.log(` ⚠️ ${warning.reasonCode}: ${warning.message}`);
62
+ }
63
+ }
64
+ }
65
+
66
+ if (verbose) {
67
+ console.log('\n' + '-'.repeat(80));
68
+ console.log('REQUIREMENTS BY MATURITY LEVEL:');
69
+ console.log('-'.repeat(80));
70
+ console.log('\nEXPERIMENTAL:');
71
+ console.log(' - Must exist in registry');
72
+ console.log(' - Must have at least 1 test matrix entry');
73
+ console.log('\nPARTIAL:');
74
+ console.log(' - All EXPERIMENTAL requirements');
75
+ console.log(' - Must have at least 1 realistic fixture mapping');
76
+ console.log(' - Must have documentation');
77
+ console.log('\nSTABLE:');
78
+ console.log(' - All PARTIAL requirements');
79
+ console.log(' - Must have determinism test coverage');
80
+ console.log(' - Must have artifact assertions in test matrix');
81
+ console.log(' - Must have guardrails coverage (if category requires it)');
82
+ }
83
+ } else {
84
+ console.log('\n✅ All capabilities meet their required gates!');
85
+ }
86
+
87
+ console.log('='.repeat(80) + '\n');
88
+ }
89
+
90
+ // Exit code: 0 if PASS, 2 if FAIL
91
+ if (!result.pass) {
92
+ process.exit(2);
93
+ }
94
+ }
95
+
@@ -1,13 +1,41 @@
1
- import { resolve } from 'path';
1
+ import { resolve, dirname, basename } from 'path';
2
2
  import { existsSync, readFileSync, readdirSync } from 'fs';
3
3
  import { DataError } from '../util/errors.js';
4
+ import { displayPerformanceInInspect } from '../../verax/core/perf/perf.display.js';
5
+ import { loadRunTimeline } from '../../verax/core/observe/run-timeline.js';
6
+ import { loadDecisionTrace } from '../../verax/core/decisions/decision.trace.js';
7
+ import { loadCrossIndex } from '../../verax/core/report/cross-index.js';
8
+ import { generateHumanSummary, formatHumanSummary } from '../../verax/core/report/human-summary.js';
9
+
10
+ /**
11
+ * Extract runId from runPath
12
+ */
13
+ function extractRunId(runPath) {
14
+ const fullPath = resolve(runPath);
15
+ const runDirBasename = basename(fullPath);
16
+ const parentDir = basename(dirname(fullPath));
17
+
18
+ // Check if path is .verax/runs/<runId>
19
+ if (parentDir === 'runs') {
20
+ return runDirBasename;
21
+ }
22
+
23
+ return runDirBasename;
24
+ }
4
25
 
5
26
  /**
6
27
  * `verax inspect` command
7
28
  * Read an existing run folder and display summary
8
29
  */
9
30
  export async function inspectCommand(runPath, options = {}) {
10
- const { json = false } = options;
31
+ const {
32
+ json = false,
33
+ timeline = false,
34
+ decisions = false,
35
+ failures = false,
36
+ performance = false,
37
+ evidence = false
38
+ } = options;
11
39
 
12
40
  const fullPath = resolve(runPath);
13
41
 
@@ -102,7 +130,108 @@ export async function inspectCommand(runPath, options = {}) {
102
130
  console.log(`Evidence: not found`);
103
131
  }
104
132
 
133
+ // PHASE 21.9: Display performance metrics
134
+ const runId = output.runId;
135
+ const projectDir = resolve(process.cwd());
136
+
137
+ // PHASE 21.10: Handle specific flags
138
+ if (timeline) {
139
+ const timelineData = loadRunTimeline(projectDir, runId);
140
+ if (timelineData) {
141
+ if (json) {
142
+ console.log(JSON.stringify(timelineData, null, 2));
143
+ } else {
144
+ console.log('\n=== Timeline ===\n');
145
+ for (const event of timelineData.events) {
146
+ console.log(`${event.timestamp || 'N/A'} [${event.phase}] ${event.event}`);
147
+ if (event.data) {
148
+ console.log(` ${JSON.stringify(event.data)}`);
149
+ }
150
+ }
151
+ console.log('');
152
+ }
153
+ return output;
154
+ }
155
+ }
156
+
157
+ if (decisions) {
158
+ const decisionTrace = loadDecisionTrace(projectDir, runId);
159
+ if (decisionTrace) {
160
+ if (json) {
161
+ console.log(JSON.stringify(decisionTrace, null, 2));
162
+ } else {
163
+ console.log('\n=== Decision Trace ===\n');
164
+ for (const trace of decisionTrace.findings) {
165
+ console.log(`Finding: ${trace.findingId}`);
166
+ console.log(` Status: ${trace.status.value}`);
167
+ console.log(` Confidence: ${trace.confidence.level || 'UNKNOWN'}`);
168
+ console.log(` Why detected: ${trace.detection.why.map(w => w.reason).join('; ')}`);
169
+ console.log(` Why status: ${trace.status.why.map(w => w.reason).join('; ')}`);
170
+ console.log('');
171
+ }
172
+ }
173
+ return output;
174
+ }
175
+ }
176
+
177
+ if (failures) {
178
+ const failureLedgerPath = resolve(fullPath, 'failure.ledger.json');
179
+ if (existsSync(failureLedgerPath)) {
180
+ const failureLedger = JSON.parse(readFileSync(failureLedgerPath, 'utf-8'));
181
+ if (json) {
182
+ console.log(JSON.stringify(failureLedger, null, 2));
183
+ } else {
184
+ console.log('\n=== Failures ===\n');
185
+ console.log(`Total: ${failureLedger.summary?.total || 0}`);
186
+ if (failureLedger.failures) {
187
+ for (const failure of failureLedger.failures) {
188
+ console.log(`[${failure.severity || 'UNKNOWN'}] ${failure.code}: ${failure.message}`);
189
+ }
190
+ }
191
+ console.log('');
192
+ }
193
+ return output;
194
+ }
195
+ }
196
+
197
+ if (performance) {
198
+ displayPerformanceInInspect(projectDir, runId);
105
199
  console.log('');
200
+ return output;
201
+ }
202
+
203
+ if (evidence) {
204
+ const crossIndex = loadCrossIndex(projectDir, runId);
205
+ if (crossIndex) {
206
+ if (json) {
207
+ console.log(JSON.stringify(crossIndex, null, 2));
208
+ } else {
209
+ console.log('\n=== Evidence Cross-Index ===\n');
210
+ for (const [findingId, entry] of Object.entries(crossIndex.findings)) {
211
+ console.log(`Finding: ${findingId}`);
212
+ console.log(` Evidence files: ${entry.evidence.files.length}`);
213
+ console.log(` Evidence complete: ${entry.evidence.isComplete}`);
214
+ console.log(` Confidence: ${entry.confidence.level || 'UNKNOWN'}`);
215
+ console.log(` Guardrails: ${entry.guardrails.applied.length} rule(s) applied`);
216
+ console.log('');
217
+ }
218
+ }
219
+ return output;
220
+ }
221
+ }
222
+
223
+ // Default: show summary + human summary
224
+ displayPerformanceInInspect(projectDir, runId);
225
+
226
+ // PHASE 21.10: Display human summary
227
+ const humanSummary = await generateHumanSummary(projectDir, runId);
228
+ if (humanSummary && !json) {
229
+ console.log(formatHumanSummary(humanSummary));
230
+ } else if (humanSummary && json) {
231
+ output.humanSummary = humanSummary;
232
+ }
233
+
234
+ console.log('');
106
235
  }
107
236
 
108
237
  return output;
@@ -0,0 +1,213 @@
1
+ /**
2
+ * PHASE 21.7 — Release Check CLI Command
3
+ *
4
+ * Checks release readiness: GA status, Provenance, SBOM, Reproducibility.
5
+ * Exit codes: 0 = RELEASE-READY, 5 = RELEASE-BLOCKED, 70 = Internal corruption
6
+ */
7
+
8
+ import { buildProvenance, writeProvenance } from '../../verax/core/release/provenance.builder.js';
9
+ import { buildSBOM, writeSBOM } from '../../verax/core/release/sbom.builder.js';
10
+ import { checkReproducibility, writeReproducibilityReport } from '../../verax/core/release/reproducibility.check.js';
11
+ import { checkGAStatus } from '../../verax/core/ga/ga.enforcer.js';
12
+ import { writeReleaseReport } from '../../verax/core/release/release-report-writer.js';
13
+ import { findLatestRunId } from '../util/run-resolver.js';
14
+ import { resolve } from 'path';
15
+ import { existsSync, readFileSync } from 'fs';
16
+
17
+ /**
18
+ * Check release readiness
19
+ *
20
+ * @param {Object} options - Options
21
+ * @param {boolean} [options.json] - Output as JSON
22
+ */
23
+ export async function releaseCheckCommand(options = {}) {
24
+ const { json = false } = options;
25
+ const projectDir = resolve(process.cwd());
26
+
27
+ const status = {
28
+ ga: { ok: false, status: 'UNKNOWN', blockers: [] },
29
+ provenance: { ok: false, exists: false, blockers: [] },
30
+ sbom: { ok: false, exists: false, blockers: [] },
31
+ reproducibility: { ok: false, verdict: 'UNKNOWN', blockers: [] }
32
+ };
33
+
34
+ // 1. Check GA status
35
+ try {
36
+ const runId = findLatestRunId(projectDir);
37
+ if (runId) {
38
+ const gaCheck = checkGAStatus(projectDir, runId);
39
+ status.ga.ok = gaCheck.ready;
40
+ status.ga.status = gaCheck.ready ? 'GA-READY' : 'GA-BLOCKED';
41
+ if (!gaCheck.ready && gaCheck.status?.blockers) {
42
+ status.ga.blockers = gaCheck.status.blockers.map(b => b.message);
43
+ }
44
+ } else {
45
+ status.ga.blockers.push('No runs found. Run a scan first.');
46
+ }
47
+ } catch (error) {
48
+ status.ga.blockers.push(`GA check failed: ${error.message}`);
49
+ }
50
+
51
+ // 2. Check Provenance
52
+ try {
53
+ const provenancePath = resolve(projectDir, 'release', 'release.provenance.json');
54
+ if (existsSync(provenancePath)) {
55
+ status.provenance.exists = true;
56
+ const provenance = JSON.parse(readFileSync(provenancePath, 'utf-8'));
57
+
58
+ // Validate provenance structure
59
+ if (!provenance.version || !provenance.git || !provenance.env) {
60
+ status.provenance.blockers.push('Invalid provenance structure');
61
+ } else if (provenance.git.dirty) {
62
+ status.provenance.blockers.push('Provenance indicates dirty git repository');
63
+ } else if (provenance.gaStatus !== 'GA-READY') {
64
+ status.provenance.blockers.push(`GA status is ${provenance.gaStatus}, not GA-READY`);
65
+ } else {
66
+ status.provenance.ok = true;
67
+ }
68
+ } else {
69
+ // Try to build it
70
+ try {
71
+ const provenance = await buildProvenance(projectDir);
72
+ writeProvenance(projectDir, provenance);
73
+ status.provenance.exists = true;
74
+ status.provenance.ok = true;
75
+ } catch (error) {
76
+ status.provenance.blockers.push(`Cannot build provenance: ${error.message}`);
77
+ }
78
+ }
79
+ } catch (error) {
80
+ status.provenance.blockers.push(`Provenance check failed: ${error.message}`);
81
+ }
82
+
83
+ // 3. Check SBOM
84
+ try {
85
+ const sbomPath = resolve(projectDir, 'release', 'sbom.json');
86
+ if (existsSync(sbomPath)) {
87
+ status.sbom.exists = true;
88
+ const sbom = JSON.parse(readFileSync(sbomPath, 'utf-8'));
89
+
90
+ // Validate SBOM structure
91
+ if (!sbom.bomFormat || !sbom.components || !Array.isArray(sbom.components)) {
92
+ status.sbom.blockers.push('Invalid SBOM structure');
93
+ } else if (sbom.components.length === 0) {
94
+ status.sbom.blockers.push('SBOM has no components');
95
+ } else {
96
+ status.sbom.ok = true;
97
+ }
98
+ } else {
99
+ // Try to build it
100
+ try {
101
+ const sbom = await buildSBOM(projectDir);
102
+ writeSBOM(projectDir, sbom);
103
+ status.sbom.exists = true;
104
+ status.sbom.ok = true;
105
+ } catch (error) {
106
+ status.sbom.blockers.push(`Cannot build SBOM: ${error.message}`);
107
+ }
108
+ }
109
+ } catch (error) {
110
+ status.sbom.blockers.push(`SBOM check failed: ${error.message}`);
111
+ }
112
+
113
+ // 4. Check Reproducibility
114
+ try {
115
+ const report = await checkReproducibility(projectDir);
116
+ writeReproducibilityReport(projectDir, report);
117
+
118
+ if (report.verdict === 'REPRODUCIBLE') {
119
+ status.reproducibility.ok = true;
120
+ status.reproducibility.verdict = 'REPRODUCIBLE';
121
+ } else {
122
+ status.reproducibility.verdict = 'NON_REPRODUCIBLE';
123
+ if (report.differences && report.differences.length > 0) {
124
+ status.reproducibility.blockers = report.differences.map(d => d.message);
125
+ } else {
126
+ status.reproducibility.blockers.push('Build is not reproducible');
127
+ }
128
+ }
129
+ } catch (error) {
130
+ status.reproducibility.blockers.push(`Reproducibility check failed: ${error.message}`);
131
+ }
132
+
133
+ // Determine overall status
134
+ const allOk = status.ga.ok && status.provenance.ok && status.sbom.ok && status.reproducibility.ok;
135
+ const hasInternalCorruption =
136
+ status.ga.blockers.some(b => b.includes('corruption') || b.includes('INTERNAL')) ||
137
+ status.provenance.blockers.some(b => b.includes('corruption')) ||
138
+ status.sbom.blockers.some(b => b.includes('corruption'));
139
+
140
+ // Write release report
141
+ const releaseStatus = {
142
+ releaseReady: allOk,
143
+ status,
144
+ summary: {
145
+ ga: status.ga.ok ? 'OK' : 'BLOCKED',
146
+ provenance: status.provenance.ok ? 'OK' : 'BLOCKED',
147
+ sbom: status.sbom.ok ? 'OK' : 'BLOCKED',
148
+ reproducibility: status.reproducibility.ok ? 'OK' : 'BLOCKED'
149
+ }
150
+ };
151
+ const releaseReportPath = writeReleaseReport(projectDir, releaseStatus);
152
+
153
+ // Output
154
+ if (json) {
155
+ console.log(JSON.stringify({
156
+ releaseReady: allOk,
157
+ status,
158
+ summary: {
159
+ ga: status.ga.ok ? 'OK' : 'BLOCKED',
160
+ provenance: status.provenance.ok ? 'OK' : 'BLOCKED',
161
+ sbom: status.sbom.ok ? 'OK' : 'BLOCKED',
162
+ reproducibility: status.reproducibility.ok ? 'OK' : 'BLOCKED'
163
+ },
164
+ reportPath: releaseReportPath
165
+ }, null, 2));
166
+ } else {
167
+ console.log('\n' + '='.repeat(80));
168
+ console.log('RELEASE READINESS CHECK');
169
+ console.log('='.repeat(80));
170
+
171
+ console.log(`\nGA Status: ${status.ga.ok ? '✅ READY' : '❌ BLOCKED'}`);
172
+ if (status.ga.blockers.length > 0) {
173
+ for (const blocker of status.ga.blockers) {
174
+ console.log(` - ${blocker}`);
175
+ }
176
+ }
177
+
178
+ console.log(`\nProvenance: ${status.provenance.ok ? '✅ OK' : '❌ BLOCKED'}`);
179
+ if (status.provenance.blockers.length > 0) {
180
+ for (const blocker of status.provenance.blockers) {
181
+ console.log(` - ${blocker}`);
182
+ }
183
+ }
184
+
185
+ console.log(`\nSBOM: ${status.sbom.ok ? '✅ OK' : '❌ BLOCKED'}`);
186
+ if (status.sbom.blockers.length > 0) {
187
+ for (const blocker of status.sbom.blockers) {
188
+ console.log(` - ${blocker}`);
189
+ }
190
+ }
191
+
192
+ console.log(`\nReproducibility: ${status.reproducibility.ok ? '✅ REPRODUCIBLE' : '❌ NON_REPRODUCIBLE'}`);
193
+ if (status.reproducibility.blockers.length > 0) {
194
+ for (const blocker of status.reproducibility.blockers) {
195
+ console.log(` - ${blocker}`);
196
+ }
197
+ }
198
+
199
+ console.log(`\nOverall: ${allOk ? '✅ RELEASE-READY' : '❌ RELEASE-BLOCKED'}`);
200
+ console.log(`\nSee report: ${releaseReportPath}`);
201
+ console.log('='.repeat(80) + '\n');
202
+ }
203
+
204
+ // Exit codes: 0 = RELEASE-READY, 2 = RELEASE-BLOCKED, 70 = Internal corruption
205
+ if (allOk) {
206
+ process.exit(0);
207
+ } else if (hasInternalCorruption) {
208
+ process.exit(70);
209
+ } else {
210
+ process.exit(2);
211
+ }
212
+ }
213
+