@veraxhq/verax 0.2.0 → 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 (217) hide show
  1. package/README.md +14 -18
  2. package/bin/verax.js +7 -0
  3. package/package.json +15 -5
  4. package/src/cli/commands/baseline.js +104 -0
  5. package/src/cli/commands/default.js +323 -111
  6. package/src/cli/commands/doctor.js +36 -4
  7. package/src/cli/commands/ga.js +243 -0
  8. package/src/cli/commands/gates.js +95 -0
  9. package/src/cli/commands/inspect.js +131 -2
  10. package/src/cli/commands/release-check.js +213 -0
  11. package/src/cli/commands/run.js +498 -103
  12. package/src/cli/commands/security-check.js +211 -0
  13. package/src/cli/commands/truth.js +114 -0
  14. package/src/cli/entry.js +305 -68
  15. package/src/cli/util/angular-component-extractor.js +179 -0
  16. package/src/cli/util/angular-navigation-detector.js +141 -0
  17. package/src/cli/util/angular-network-detector.js +161 -0
  18. package/src/cli/util/angular-state-detector.js +162 -0
  19. package/src/cli/util/ast-interactive-detector.js +546 -0
  20. package/src/cli/util/ast-network-detector.js +603 -0
  21. package/src/cli/util/ast-usestate-detector.js +602 -0
  22. package/src/cli/util/bootstrap-guard.js +86 -0
  23. package/src/cli/util/detection-engine.js +4 -3
  24. package/src/cli/util/determinism-runner.js +123 -0
  25. package/src/cli/util/determinism-writer.js +129 -0
  26. package/src/cli/util/env-url.js +4 -0
  27. package/src/cli/util/events.js +76 -0
  28. package/src/cli/util/expectation-extractor.js +380 -74
  29. package/src/cli/util/findings-writer.js +126 -15
  30. package/src/cli/util/learn-writer.js +3 -1
  31. package/src/cli/util/observation-engine.js +69 -23
  32. package/src/cli/util/observe-writer.js +3 -1
  33. package/src/cli/util/paths.js +6 -14
  34. package/src/cli/util/project-discovery.js +23 -0
  35. package/src/cli/util/project-writer.js +3 -1
  36. package/src/cli/util/redact.js +2 -2
  37. package/src/cli/util/run-resolver.js +64 -0
  38. package/src/cli/util/runtime-budget.js +147 -0
  39. package/src/cli/util/source-requirement.js +55 -0
  40. package/src/cli/util/summary-writer.js +13 -1
  41. package/src/cli/util/svelte-navigation-detector.js +163 -0
  42. package/src/cli/util/svelte-network-detector.js +80 -0
  43. package/src/cli/util/svelte-sfc-extractor.js +147 -0
  44. package/src/cli/util/svelte-state-detector.js +243 -0
  45. package/src/cli/util/vue-navigation-detector.js +177 -0
  46. package/src/cli/util/vue-sfc-extractor.js +162 -0
  47. package/src/cli/util/vue-state-detector.js +215 -0
  48. package/src/types/global.d.ts +28 -0
  49. package/src/types/ts-ast.d.ts +24 -0
  50. package/src/verax/cli/doctor.js +2 -2
  51. package/src/verax/cli/finding-explainer.js +56 -3
  52. package/src/verax/cli/init.js +1 -1
  53. package/src/verax/cli/url-safety.js +12 -2
  54. package/src/verax/cli/wizard.js +13 -2
  55. package/src/verax/core/artifacts/registry.js +154 -0
  56. package/src/verax/core/artifacts/verifier.js +980 -0
  57. package/src/verax/core/baseline/baseline.enforcer.js +137 -0
  58. package/src/verax/core/baseline/baseline.snapshot.js +231 -0
  59. package/src/verax/core/budget-engine.js +1 -1
  60. package/src/verax/core/capabilities/gates.js +499 -0
  61. package/src/verax/core/capabilities/registry.js +475 -0
  62. package/src/verax/core/confidence/confidence-compute.js +137 -0
  63. package/src/verax/core/confidence/confidence-invariants.js +234 -0
  64. package/src/verax/core/confidence/confidence-report-writer.js +112 -0
  65. package/src/verax/core/confidence/confidence-weights.js +44 -0
  66. package/src/verax/core/confidence/confidence.defaults.js +65 -0
  67. package/src/verax/core/confidence/confidence.loader.js +79 -0
  68. package/src/verax/core/confidence/confidence.schema.js +94 -0
  69. package/src/verax/core/confidence-engine-refactor.js +484 -0
  70. package/src/verax/core/confidence-engine.js +486 -0
  71. package/src/verax/core/confidence-engine.js.backup +471 -0
  72. package/src/verax/core/contracts/index.js +29 -0
  73. package/src/verax/core/contracts/types.js +185 -0
  74. package/src/verax/core/contracts/validators.js +381 -0
  75. package/src/verax/core/decision-snapshot.js +31 -4
  76. package/src/verax/core/decisions/decision.trace.js +276 -0
  77. package/src/verax/core/determinism/contract-writer.js +89 -0
  78. package/src/verax/core/determinism/contract.js +139 -0
  79. package/src/verax/core/determinism/diff.js +364 -0
  80. package/src/verax/core/determinism/engine.js +221 -0
  81. package/src/verax/core/determinism/finding-identity.js +148 -0
  82. package/src/verax/core/determinism/normalize.js +438 -0
  83. package/src/verax/core/determinism/report-writer.js +92 -0
  84. package/src/verax/core/determinism/run-fingerprint.js +118 -0
  85. package/src/verax/core/determinism-model.js +35 -6
  86. package/src/verax/core/dynamic-route-intelligence.js +528 -0
  87. package/src/verax/core/evidence/evidence-capture-service.js +307 -0
  88. package/src/verax/core/evidence/evidence-intent-ledger.js +165 -0
  89. package/src/verax/core/evidence-builder.js +487 -0
  90. package/src/verax/core/execution-mode-context.js +77 -0
  91. package/src/verax/core/execution-mode-detector.js +190 -0
  92. package/src/verax/core/failures/exit-codes.js +86 -0
  93. package/src/verax/core/failures/failure-summary.js +76 -0
  94. package/src/verax/core/failures/failure.factory.js +225 -0
  95. package/src/verax/core/failures/failure.ledger.js +132 -0
  96. package/src/verax/core/failures/failure.types.js +196 -0
  97. package/src/verax/core/failures/index.js +10 -0
  98. package/src/verax/core/ga/ga-report-writer.js +43 -0
  99. package/src/verax/core/ga/ga.artifact.js +49 -0
  100. package/src/verax/core/ga/ga.contract.js +434 -0
  101. package/src/verax/core/ga/ga.enforcer.js +86 -0
  102. package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
  103. package/src/verax/core/guardrails/policy.defaults.js +210 -0
  104. package/src/verax/core/guardrails/policy.loader.js +83 -0
  105. package/src/verax/core/guardrails/policy.schema.js +110 -0
  106. package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
  107. package/src/verax/core/guardrails-engine.js +505 -0
  108. package/src/verax/core/incremental-store.js +15 -7
  109. package/src/verax/core/observe/run-timeline.js +316 -0
  110. package/src/verax/core/perf/perf.contract.js +186 -0
  111. package/src/verax/core/perf/perf.display.js +65 -0
  112. package/src/verax/core/perf/perf.enforcer.js +91 -0
  113. package/src/verax/core/perf/perf.monitor.js +209 -0
  114. package/src/verax/core/perf/perf.report.js +198 -0
  115. package/src/verax/core/pipeline-tracker.js +238 -0
  116. package/src/verax/core/product-definition.js +127 -0
  117. package/src/verax/core/release/provenance.builder.js +271 -0
  118. package/src/verax/core/release/release-report-writer.js +40 -0
  119. package/src/verax/core/release/release.enforcer.js +159 -0
  120. package/src/verax/core/release/reproducibility.check.js +221 -0
  121. package/src/verax/core/release/sbom.builder.js +283 -0
  122. package/src/verax/core/replay-validator.js +4 -4
  123. package/src/verax/core/replay.js +1 -1
  124. package/src/verax/core/report/cross-index.js +192 -0
  125. package/src/verax/core/report/human-summary.js +222 -0
  126. package/src/verax/core/route-intelligence.js +419 -0
  127. package/src/verax/core/security/secrets.scan.js +326 -0
  128. package/src/verax/core/security/security-report.js +50 -0
  129. package/src/verax/core/security/security.enforcer.js +124 -0
  130. package/src/verax/core/security/supplychain.defaults.json +38 -0
  131. package/src/verax/core/security/supplychain.policy.js +326 -0
  132. package/src/verax/core/security/vuln.scan.js +265 -0
  133. package/src/verax/core/silence-impact.js +1 -1
  134. package/src/verax/core/silence-model.js +9 -7
  135. package/src/verax/core/truth/truth.certificate.js +250 -0
  136. package/src/verax/core/ui-feedback-intelligence.js +515 -0
  137. package/src/verax/detect/comparison.js +8 -3
  138. package/src/verax/detect/confidence-engine.js +645 -57
  139. package/src/verax/detect/confidence-helper.js +33 -0
  140. package/src/verax/detect/detection-engine.js +19 -2
  141. package/src/verax/detect/dynamic-route-findings.js +335 -0
  142. package/src/verax/detect/evidence-index.js +15 -65
  143. package/src/verax/detect/expectation-chain-detector.js +417 -0
  144. package/src/verax/detect/expectation-model.js +56 -3
  145. package/src/verax/detect/explanation-helpers.js +1 -1
  146. package/src/verax/detect/finding-detector.js +2 -2
  147. package/src/verax/detect/findings-writer.js +149 -20
  148. package/src/verax/detect/flow-detector.js +4 -4
  149. package/src/verax/detect/index.js +265 -15
  150. package/src/verax/detect/interactive-findings.js +3 -4
  151. package/src/verax/detect/journey-stall-detector.js +558 -0
  152. package/src/verax/detect/route-findings.js +218 -0
  153. package/src/verax/detect/signal-mapper.js +2 -2
  154. package/src/verax/detect/skip-classifier.js +4 -4
  155. package/src/verax/detect/ui-feedback-findings.js +207 -0
  156. package/src/verax/detect/verdict-engine.js +61 -9
  157. package/src/verax/detect/view-switch-correlator.js +242 -0
  158. package/src/verax/flow/flow-engine.js +3 -2
  159. package/src/verax/flow/flow-spec.js +1 -2
  160. package/src/verax/index.js +413 -33
  161. package/src/verax/intel/effect-detector.js +1 -1
  162. package/src/verax/intel/index.js +2 -2
  163. package/src/verax/intel/route-extractor.js +3 -3
  164. package/src/verax/intel/vue-navigation-extractor.js +81 -18
  165. package/src/verax/intel/vue-router-extractor.js +4 -2
  166. package/src/verax/learn/action-contract-extractor.js +684 -66
  167. package/src/verax/learn/ast-contract-extractor.js +53 -1
  168. package/src/verax/learn/index.js +36 -2
  169. package/src/verax/learn/manifest-writer.js +28 -14
  170. package/src/verax/learn/route-extractor.js +1 -1
  171. package/src/verax/learn/route-validator.js +12 -8
  172. package/src/verax/learn/state-extractor.js +1 -1
  173. package/src/verax/learn/static-extractor-navigation.js +1 -1
  174. package/src/verax/learn/static-extractor-validation.js +2 -2
  175. package/src/verax/learn/static-extractor.js +8 -7
  176. package/src/verax/learn/ts-contract-resolver.js +14 -12
  177. package/src/verax/observe/browser.js +22 -3
  178. package/src/verax/observe/console-sensor.js +2 -2
  179. package/src/verax/observe/expectation-executor.js +2 -1
  180. package/src/verax/observe/focus-sensor.js +1 -1
  181. package/src/verax/observe/human-driver.js +29 -10
  182. package/src/verax/observe/index.js +92 -844
  183. package/src/verax/observe/interaction-discovery.js +27 -15
  184. package/src/verax/observe/interaction-runner.js +31 -14
  185. package/src/verax/observe/loading-sensor.js +6 -0
  186. package/src/verax/observe/navigation-sensor.js +1 -1
  187. package/src/verax/observe/observe-context.js +205 -0
  188. package/src/verax/observe/observe-helpers.js +191 -0
  189. package/src/verax/observe/observe-runner.js +226 -0
  190. package/src/verax/observe/observers/budget-observer.js +185 -0
  191. package/src/verax/observe/observers/console-observer.js +102 -0
  192. package/src/verax/observe/observers/coverage-observer.js +107 -0
  193. package/src/verax/observe/observers/interaction-observer.js +471 -0
  194. package/src/verax/observe/observers/navigation-observer.js +132 -0
  195. package/src/verax/observe/observers/network-observer.js +87 -0
  196. package/src/verax/observe/observers/safety-observer.js +82 -0
  197. package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
  198. package/src/verax/observe/settle.js +1 -0
  199. package/src/verax/observe/state-sensor.js +8 -4
  200. package/src/verax/observe/state-ui-sensor.js +7 -1
  201. package/src/verax/observe/traces-writer.js +27 -16
  202. package/src/verax/observe/ui-feedback-detector.js +742 -0
  203. package/src/verax/observe/ui-signal-sensor.js +155 -2
  204. package/src/verax/scan-summary-writer.js +46 -9
  205. package/src/verax/shared/artifact-manager.js +9 -6
  206. package/src/verax/shared/budget-profiles.js +2 -2
  207. package/src/verax/shared/caching.js +1 -1
  208. package/src/verax/shared/config-loader.js +1 -2
  209. package/src/verax/shared/css-spinner-rules.js +204 -0
  210. package/src/verax/shared/dynamic-route-utils.js +12 -6
  211. package/src/verax/shared/retry-policy.js +1 -6
  212. package/src/verax/shared/root-artifacts.js +1 -1
  213. package/src/verax/shared/view-switch-rules.js +208 -0
  214. package/src/verax/shared/zip-artifacts.js +1 -0
  215. package/src/verax/validate/context-validator.js +1 -1
  216. package/src/verax/observe/index.js.backup +0 -1
  217. package/src/verax/validate/context-validator.js.bak +0 -0
@@ -0,0 +1,243 @@
1
+ /**
2
+ * PHASE 21.6 — GA Readiness CLI Command
3
+ *
4
+ * Pure inspection command. Evaluates GA readiness using existing artifacts only.
5
+ * No URL, no browser, no project execution.
6
+ */
7
+
8
+ import { evaluateGAReadiness } from '../../verax/core/ga/ga.contract.js';
9
+ import { writeGAStatus } from '../../verax/core/ga/ga.artifact.js';
10
+ import { writeGAReport } from '../../verax/core/ga/ga-report-writer.js';
11
+ import { GA_BLOCKER_CODE } from '../../verax/core/ga/ga.contract.js';
12
+ import { resolve } from 'path';
13
+ import { readFileSync, existsSync } from 'fs';
14
+ import { findLatestRunId, validateRunId } from '../util/run-resolver.js';
15
+ import { UsageError } from '../util/errors.js';
16
+
17
+ /**
18
+ * Load failure ledger summary
19
+ *
20
+ * @param {string} projectDir - Project directory
21
+ * @param {string} runId - Run ID
22
+ * @returns {Object|null} Failure ledger summary or null
23
+ */
24
+ function loadFailureLedger(projectDir, runId) {
25
+ const ledgerPath = resolve(projectDir, '.verax', 'runs', runId, 'failure.ledger.json');
26
+ if (!existsSync(ledgerPath)) {
27
+ return null;
28
+ }
29
+
30
+ try {
31
+ const content = readFileSync(ledgerPath, 'utf-8');
32
+ const ledger = JSON.parse(content);
33
+ return ledger.summary || null;
34
+ } catch (error) {
35
+ return null;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Load determinism verdict
41
+ *
42
+ * @param {string} projectDir - Project directory
43
+ * @param {string} runId - Run ID
44
+ * @returns {Promise<string|null>} Determinism verdict or null
45
+ */
46
+ async function loadDeterminismVerdict(projectDir, runId) {
47
+ const decisionsPath = resolve(projectDir, '.verax', 'runs', runId, 'decisions.json');
48
+ if (!existsSync(decisionsPath)) {
49
+ return null;
50
+ }
51
+
52
+ try {
53
+ const decisions = JSON.parse(readFileSync(decisionsPath, 'utf-8'));
54
+ const { DecisionRecorder } = await import('../../verax/core/determinism-model.js');
55
+ const recorder = DecisionRecorder.fromExport(decisions);
56
+ const { computeDeterminismVerdict } = await import('../../verax/core/determinism/contract.js');
57
+ const verdict = computeDeterminismVerdict(recorder);
58
+ return verdict.verdict;
59
+ } catch (error) {
60
+ return null;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Check for Evidence Law violations
66
+ *
67
+ * @param {string} projectDir - Project directory
68
+ * @param {string} runId - Run ID
69
+ * @returns {boolean} Whether Evidence Law was violated
70
+ */
71
+ function checkEvidenceLawViolations(projectDir, runId) {
72
+ const findingsPath = resolve(projectDir, '.verax', 'runs', runId, 'findings.json');
73
+ if (!existsSync(findingsPath)) {
74
+ return false;
75
+ }
76
+
77
+ try {
78
+ const content = readFileSync(findingsPath, 'utf-8');
79
+ const findings = JSON.parse(content);
80
+
81
+ if (!Array.isArray(findings.findings)) {
82
+ return false;
83
+ }
84
+
85
+ // Check for CONFIRMED findings with incomplete evidence
86
+ for (const finding of findings.findings) {
87
+ if ((finding.severity === 'CONFIRMED' || finding.status === 'CONFIRMED') &&
88
+ finding.evidencePackage && !finding.evidencePackage.isComplete) {
89
+ return true;
90
+ }
91
+ }
92
+
93
+ return false;
94
+ } catch (error) {
95
+ return false;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * PHASE 21.6.1: `verax ga` command
101
+ *
102
+ * Pure inspection command. No URL, no browser, no execution.
103
+ *
104
+ * @param {Object} options - Options
105
+ * @param {string} [options.runId] - Run ID (defaults to latest)
106
+ * @param {boolean} [options.json] - Output as JSON
107
+ */
108
+ export async function gaCommand(options = {}) {
109
+ const { runId: providedRunId = null, json = false } = options;
110
+
111
+ const projectDir = resolve(process.cwd());
112
+
113
+ // Resolve run ID: use provided or find latest
114
+ let runId = providedRunId;
115
+
116
+ if (!runId) {
117
+ // Find latest run
118
+ runId = findLatestRunId(projectDir);
119
+
120
+ if (!runId) {
121
+ // No runs found - GA is BLOCKED
122
+ const gaResult = {
123
+ pass: false,
124
+ blockers: [{
125
+ code: GA_BLOCKER_CODE.NO_RUNS_FOUND || 'GA_NO_RUNS_FOUND',
126
+ message: 'No runs found in .verax/runs/. Run a scan first.',
127
+ context: {}
128
+ }],
129
+ warnings: [],
130
+ summary: {
131
+ pass: false,
132
+ blockersCount: 1,
133
+ warningsCount: 0,
134
+ checkedAt: new Date().toISOString()
135
+ },
136
+ inputs: {
137
+ gates: null,
138
+ determinism: null,
139
+ evidenceLaw: null,
140
+ failureLedger: null
141
+ }
142
+ };
143
+
144
+ if (json) {
145
+ console.log(JSON.stringify({
146
+ gaReady: false,
147
+ blockers: gaResult.blockers,
148
+ warnings: [],
149
+ summary: gaResult.summary
150
+ }, null, 2));
151
+ } else {
152
+ console.log('\n' + '='.repeat(80));
153
+ console.log('GA READINESS EVALUATION');
154
+ console.log('='.repeat(80));
155
+ console.log('\nGA STATUS: ❌ BLOCKED');
156
+ console.log('\nBlockers:');
157
+ console.log('- No runs found in .verax/runs/. Run a scan first.');
158
+ console.log('='.repeat(80) + '\n');
159
+ }
160
+
161
+ process.exit(4);
162
+ return;
163
+ }
164
+ } else {
165
+ // Validate provided run ID
166
+ if (!validateRunId(projectDir, runId)) {
167
+ const error = new UsageError(`Run ID not found: ${runId}`);
168
+ // UsageError already has exit code 64
169
+ throw error;
170
+ }
171
+ }
172
+
173
+ // Load context from artifacts (pure filesystem reads)
174
+ const failureLedger = loadFailureLedger(projectDir, runId);
175
+ const determinismVerdict = await loadDeterminismVerdict(projectDir, runId);
176
+ const evidenceLawViolated = checkEvidenceLawViolations(projectDir, runId);
177
+
178
+ // Evaluate GA readiness
179
+ const gaResult = await evaluateGAReadiness({
180
+ projectDir,
181
+ runId,
182
+ determinismVerdict,
183
+ evidenceLawViolated,
184
+ failureLedger
185
+ });
186
+
187
+ // Write status artifact
188
+ const artifactPath = writeGAStatus(projectDir, runId, gaResult);
189
+
190
+ // Write GA report
191
+ const reportPath = writeGAReport(projectDir, runId, gaResult);
192
+
193
+ // Output
194
+ if (json) {
195
+ console.log(JSON.stringify({
196
+ gaReady: gaResult.pass,
197
+ blockers: gaResult.blockers,
198
+ warnings: gaResult.warnings,
199
+ summary: gaResult.summary,
200
+ artifactPath,
201
+ reportPath
202
+ }, null, 2));
203
+ } else {
204
+ console.log('\n' + '='.repeat(80));
205
+ console.log('GA READINESS EVALUATION');
206
+ console.log('='.repeat(80));
207
+ console.log(`\nGA STATUS: ${gaResult.pass ? '✅ READY' : '❌ BLOCKED'}`);
208
+
209
+ if (gaResult.blockers.length > 0) {
210
+ console.log('\nBlockers:');
211
+ for (const blocker of gaResult.blockers) {
212
+ console.log(`- ${blocker.message}`);
213
+ }
214
+ }
215
+
216
+ if (gaResult.warnings.length > 0) {
217
+ console.log('\nWarnings:');
218
+ for (const warning of gaResult.warnings) {
219
+ console.log(`- ${warning.message}`);
220
+ }
221
+ }
222
+
223
+ console.log(`\nSee: ${artifactPath}`);
224
+ console.log(`Report: ${reportPath}`);
225
+ console.log('='.repeat(80) + '\n');
226
+ }
227
+
228
+ // Exit codes: 0 = GA-READY, 2 = GA-BLOCKED, 70 = Internal corruption
229
+ if (!gaResult.pass) {
230
+ // Check if it's an internal corruption issue
231
+ const hasInternalBlocker = gaResult.blockers.some(b =>
232
+ b.code === 'GA_INTERNAL_FAILURES' ||
233
+ b.code === 'GA_CONTRACT_FAILURES'
234
+ );
235
+
236
+ if (hasInternalBlocker) {
237
+ process.exit(70);
238
+ } else {
239
+ process.exit(2);
240
+ }
241
+ }
242
+ }
243
+
@@ -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
+