@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,434 @@
1
+ /**
2
+ * PHASE 21.6 — GA Readiness Contract
3
+ *
4
+ * Hard gate that determines if VERAX is Enterprise-GA ready.
5
+ * No human judgment, only code decides.
6
+ */
7
+
8
+ import { evaluateAllCapabilityGates, buildGateContext } from '../capabilities/gates.js';
9
+ import { CAPABILITY_MATURITY } from '../capabilities/gates.js';
10
+ import { verifyRun } from '../artifacts/verifier.js';
11
+ import { checkSecurityStatus } from '../security/security.enforcer.js';
12
+ import { readFileSync, existsSync } from 'fs';
13
+ import { resolve } from 'path';
14
+ import { isBaselineFrozen, enforceBaseline } from '../baseline/baseline.enforcer.js';
15
+
16
+ /**
17
+ * GA Blocker Codes
18
+ */
19
+ export const GA_BLOCKER_CODE = {
20
+ NO_RUNS_FOUND: 'GA_NO_RUNS_FOUND',
21
+ CAPABILITY_GATES_FAILED: 'GA_CAPABILITY_GATES_FAILED',
22
+ FRAMEWORK_WAVES_UNSTABLE: 'GA_FRAMEWORK_WAVES_UNSTABLE',
23
+ EVIDENCE_LAW_VIOLATION: 'GA_EVIDENCE_LAW_VIOLATION',
24
+ DETERMINISM_NOT_ACCEPTED: 'GA_DETERMINISM_NOT_ACCEPTED',
25
+ BLOCKING_FAILURES: 'GA_BLOCKING_FAILURES',
26
+ DEGRADED_FAILURES: 'GA_DEGRADED_FAILURES',
27
+ INTERNAL_FAILURES: 'GA_INTERNAL_FAILURES',
28
+ CONTRACT_FAILURES: 'GA_CONTRACT_FAILURES',
29
+ POLICY_INVALID: 'GA_POLICY_INVALID',
30
+ ARTIFACT_VERIFIER_FAILED: 'GA_ARTIFACT_VERIFIER_FAILED',
31
+ SECURITY_NOT_CHECKED: 'GA_SECURITY_NOT_CHECKED',
32
+ SECURITY_BLOCKED: 'GA_SECURITY_BLOCKED',
33
+ PERFORMANCE_BLOCKED: 'GA_PERFORMANCE_BLOCKED',
34
+ BASELINE_DRIFT: 'GA_BASELINE_DRIFT'
35
+ };
36
+
37
+ /**
38
+ * GA Warning Codes
39
+ */
40
+ export const GA_WARNING_CODE = {
41
+ WARNINGS_IN_LEDGER: 'GA_WARNINGS_IN_LEDGER',
42
+ NON_DETERMINISTIC_ACCEPTED: 'GA_NON_DETERMINISTIC_ACCEPTED',
43
+ SECURITY_NOT_CHECKED: 'GA_SECURITY_NOT_CHECKED'
44
+ };
45
+
46
+ /**
47
+ * @typedef {Object} GA_BLOCKER
48
+ * @property {string} code - Blocker code
49
+ * @property {string} message - Human-readable message
50
+ * @property {Object} context - Additional context
51
+ */
52
+
53
+ /**
54
+ * @typedef {Object} GA_WARNING
55
+ * @property {string} code - Warning code
56
+ * @property {string} message - Human-readable message
57
+ * @property {Object} context - Additional context
58
+ */
59
+
60
+ /**
61
+ * @typedef {Object} GAReadinessResult
62
+ * @property {boolean} pass - Whether GA-ready
63
+ * @property {GA_BLOCKER[]} blockers - List of blockers
64
+ * @property {GA_WARNING[]} warnings - List of warnings
65
+ * @property {Object} summary - Summary of evaluation
66
+ * @property {Object} inputs - Inputs used for evaluation
67
+ */
68
+
69
+ /**
70
+ * Check if determinism acceptance policy exists and is valid
71
+ *
72
+ * @param {string} projectDir - Project directory
73
+ * @returns {Object|null} Acceptance policy or null
74
+ */
75
+ function loadDeterminismAcceptancePolicy(projectDir) {
76
+ const policyPath = resolve(projectDir, 'determinism.acceptance.json');
77
+ if (!existsSync(policyPath)) {
78
+ return null;
79
+ }
80
+
81
+ try {
82
+ const content = readFileSync(policyPath, 'utf-8');
83
+ const policy = JSON.parse(content);
84
+
85
+ // Validate policy structure
86
+ if (!policy.allowedAdaptiveEvents || !Array.isArray(policy.allowedAdaptiveEvents)) {
87
+ return null;
88
+ }
89
+
90
+ if (!policy.justification || typeof policy.justification !== 'string') {
91
+ return null;
92
+ }
93
+
94
+ return policy;
95
+ } catch (error) {
96
+ return null;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Check if determinism verdict is accepted
102
+ *
103
+ * @param {string} determinismVerdict - Determinism verdict (DETERMINISTIC | NON_DETERMINISTIC)
104
+ * @param {Object|null} acceptancePolicy - Determinism acceptance policy
105
+ * @returns {boolean} Whether verdict is accepted
106
+ */
107
+ function isDeterminismAccepted(determinismVerdict, acceptancePolicy) {
108
+ if (determinismVerdict === 'DETERMINISTIC') {
109
+ return true;
110
+ }
111
+
112
+ if (determinismVerdict === 'NON_DETERMINISTIC' && acceptancePolicy) {
113
+ return true;
114
+ }
115
+
116
+ return false;
117
+ }
118
+
119
+ /**
120
+ * Check if all framework waves are STABLE
121
+ *
122
+ * @param {Object} gateResult - Capability gates result
123
+ * @returns {Object} { allStable: boolean, unstable: string[] }
124
+ */
125
+ function checkFrameworkWavesStable(gateResult) {
126
+ const unstable = [];
127
+
128
+ // Check all capabilities marked as STABLE actually pass gates
129
+ for (const [capabilityId, result] of Object.entries(gateResult.perCapability || {})) {
130
+ // This is a simplified check - in reality, we'd need to check the registry
131
+ // to see which capabilities are marked as STABLE
132
+ // For now, we check if any capability fails gates
133
+ if (!result.pass) {
134
+ unstable.push(capabilityId);
135
+ }
136
+ }
137
+
138
+ return {
139
+ allStable: unstable.length === 0,
140
+ unstable
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Evaluate GA Readiness
146
+ *
147
+ * @param {Object} context - Evaluation context
148
+ * @param {string} context.projectDir - Project directory
149
+ * @param {string} [context.runId] - Run ID (for artifact verification)
150
+ * @param {string} [context.determinismVerdict] - Determinism verdict
151
+ * @param {boolean} [context.evidenceLawViolated] - Whether Evidence Law was violated
152
+ * @param {Object} [context.failureLedger] - Failure ledger summary
153
+ * @returns {Promise<GAReadinessResult>} GA readiness result
154
+ */
155
+ export async function evaluateGAReadiness(context) {
156
+ const {
157
+ projectDir,
158
+ runId = null,
159
+ determinismVerdict = null,
160
+ evidenceLawViolated = false,
161
+ failureLedger = null
162
+ } = context;
163
+
164
+ const blockers = [];
165
+ const warnings = [];
166
+ const inputs = {
167
+ gates: null,
168
+ determinism: determinismVerdict || 'UNKNOWN',
169
+ evidenceLaw: evidenceLawViolated ? 'VIOLATED' : 'ENFORCED',
170
+ failureLedger: failureLedger || { total: 0, bySeverity: {} },
171
+ artifactVerifier: null
172
+ };
173
+
174
+ // 1. Phase 19: Capability Gates - 100% PASS required
175
+ let gateResult = null;
176
+ try {
177
+ const gateContext = await buildGateContext({ projectRoot: projectDir });
178
+ gateResult = evaluateAllCapabilityGates(gateContext);
179
+ inputs.gates = gateResult.pass ? 'PASS' : 'FAIL';
180
+
181
+ if (!gateResult.pass) {
182
+ blockers.push({
183
+ code: GA_BLOCKER_CODE.CAPABILITY_GATES_FAILED,
184
+ message: `Capability gates failed: ${gateResult.summary.fail} of ${gateResult.summary.total} capabilities failed`,
185
+ context: {
186
+ total: gateResult.summary.total,
187
+ pass: gateResult.summary.pass,
188
+ fail: gateResult.summary.fail,
189
+ failures: gateResult.summary.allFailures
190
+ }
191
+ });
192
+ }
193
+ } catch (error) {
194
+ blockers.push({
195
+ code: GA_BLOCKER_CODE.CAPABILITY_GATES_FAILED,
196
+ message: `Failed to evaluate capability gates: ${error.message}`,
197
+ context: { error: error.message }
198
+ });
199
+ }
200
+
201
+ // 2. Phase 20: Framework Waves - ALL STABLE required
202
+ if (gateResult) {
203
+ const wavesCheck = checkFrameworkWavesStable(gateResult);
204
+ inputs.frameworkWaves = wavesCheck.allStable ? 'ALL_STABLE' : 'UNSTABLE';
205
+
206
+ if (!wavesCheck.allStable) {
207
+ blockers.push({
208
+ code: GA_BLOCKER_CODE.FRAMEWORK_WAVES_UNSTABLE,
209
+ message: `Framework waves not stable: ${wavesCheck.unstable.length} capabilities unstable`,
210
+ context: {
211
+ unstable: wavesCheck.unstable
212
+ }
213
+ });
214
+ }
215
+ }
216
+
217
+ // 3. Evidence Law - No violations allowed
218
+ if (evidenceLawViolated) {
219
+ blockers.push({
220
+ code: GA_BLOCKER_CODE.EVIDENCE_LAW_VIOLATION,
221
+ message: 'Evidence Law violated: CONFIRMED findings with incomplete evidence',
222
+ context: {}
223
+ });
224
+ }
225
+
226
+ // 4. Determinism - Either DETERMINISTIC or explicitly accepted
227
+ const acceptancePolicy = loadDeterminismAcceptancePolicy(projectDir);
228
+ if (determinismVerdict) {
229
+ if (!isDeterminismAccepted(determinismVerdict, acceptancePolicy)) {
230
+ blockers.push({
231
+ code: GA_BLOCKER_CODE.DETERMINISM_NOT_ACCEPTED,
232
+ message: `NON_DETERMINISTIC execution without acceptance policy`,
233
+ context: {
234
+ verdict: determinismVerdict,
235
+ hasPolicy: !!acceptancePolicy
236
+ }
237
+ });
238
+ } else if (determinismVerdict === 'NON_DETERMINISTIC' && acceptancePolicy) {
239
+ warnings.push({
240
+ code: GA_WARNING_CODE.NON_DETERMINISTIC_ACCEPTED,
241
+ message: `NON_DETERMINISTIC execution accepted via policy: ${acceptancePolicy.justification}`,
242
+ context: {
243
+ allowedEvents: acceptancePolicy.allowedAdaptiveEvents
244
+ }
245
+ });
246
+ }
247
+ }
248
+
249
+ // 5. Failure Ledger - BLOCKING = 0, DEGRADED = 0 required
250
+ if (failureLedger) {
251
+ const blockingCount = failureLedger.bySeverity?.BLOCKING || 0;
252
+ const degradedCount = failureLedger.bySeverity?.DEGRADED || 0;
253
+ const warningCount = failureLedger.bySeverity?.WARNING || 0;
254
+
255
+ if (blockingCount > 0) {
256
+ blockers.push({
257
+ code: GA_BLOCKER_CODE.BLOCKING_FAILURES,
258
+ message: `${blockingCount} BLOCKING failure(s) in failure ledger`,
259
+ context: {
260
+ count: blockingCount
261
+ }
262
+ });
263
+ }
264
+
265
+ if (degradedCount > 0) {
266
+ blockers.push({
267
+ code: GA_BLOCKER_CODE.DEGRADED_FAILURES,
268
+ message: `${degradedCount} DEGRADED failure(s) in failure ledger`,
269
+ context: {
270
+ count: degradedCount
271
+ }
272
+ });
273
+ }
274
+
275
+ // Check for INTERNAL or CONTRACT failures
276
+ const internalCount = failureLedger.byCategory?.INTERNAL || 0;
277
+ const contractCount = failureLedger.byCategory?.CONTRACT || 0;
278
+
279
+ if (internalCount > 0) {
280
+ blockers.push({
281
+ code: GA_BLOCKER_CODE.INTERNAL_FAILURES,
282
+ message: `${internalCount} INTERNAL failure(s) in failure ledger`,
283
+ context: {
284
+ count: internalCount
285
+ }
286
+ });
287
+ }
288
+
289
+ if (contractCount > 0) {
290
+ blockers.push({
291
+ code: GA_BLOCKER_CODE.CONTRACT_FAILURES,
292
+ message: `${contractCount} CONTRACT failure(s) in failure ledger`,
293
+ context: {
294
+ count: contractCount
295
+ }
296
+ });
297
+ }
298
+
299
+ if (warningCount > 0) {
300
+ warnings.push({
301
+ code: GA_WARNING_CODE.WARNINGS_IN_LEDGER,
302
+ message: `${warningCount} WARNING failure(s) in failure ledger`,
303
+ context: {
304
+ count: warningCount
305
+ }
306
+ });
307
+ }
308
+ }
309
+
310
+ // 6. Policies - All must be valid and loaded
311
+ try {
312
+ const { loadGuardrailsPolicy } = await import('../guardrails/policy.loader.js');
313
+ const { loadConfidencePolicy } = await import('../confidence/confidence.loader.js');
314
+
315
+ try {
316
+ loadGuardrailsPolicy();
317
+ } catch (error) {
318
+ blockers.push({
319
+ code: GA_BLOCKER_CODE.POLICY_INVALID,
320
+ message: `Guardrails policy invalid: ${error.message}`,
321
+ context: { error: error.message }
322
+ });
323
+ }
324
+
325
+ try {
326
+ loadConfidencePolicy();
327
+ } catch (error) {
328
+ blockers.push({
329
+ code: GA_BLOCKER_CODE.POLICY_INVALID,
330
+ message: `Confidence policy invalid: ${error.message}`,
331
+ context: { error: error.message }
332
+ });
333
+ }
334
+ } catch (error) {
335
+ blockers.push({
336
+ code: GA_BLOCKER_CODE.POLICY_INVALID,
337
+ message: `Failed to load policies: ${error.message}`,
338
+ context: { error: error.message }
339
+ });
340
+ }
341
+
342
+ // 7. Artifact Verifier - Must pass with no blocking errors
343
+ if (runId) {
344
+ try {
345
+ const runDir = resolve(projectDir, '.verax', 'runs', runId);
346
+ const verification = verifyRun(runDir);
347
+ inputs.artifactVerifier = verification.ok ? 'PASS' : 'FAIL';
348
+
349
+ if (!verification.ok) {
350
+ blockers.push({
351
+ code: GA_BLOCKER_CODE.ARTIFACT_VERIFIER_FAILED,
352
+ message: `Artifact verifier failed: ${verification.errors.length} error(s)`,
353
+ context: {
354
+ errors: verification.errors,
355
+ warnings: verification.warnings
356
+ }
357
+ });
358
+ }
359
+ } catch (error) {
360
+ blockers.push({
361
+ code: GA_BLOCKER_CODE.ARTIFACT_VERIFIER_FAILED,
362
+ message: `Failed to verify artifacts: ${error.message}`,
363
+ context: { error: error.message }
364
+ });
365
+ }
366
+ }
367
+
368
+ // 8. Security Baseline (PHASE 21.8) - SECURITY-OK required
369
+ try {
370
+ const securityCheck = checkSecurityStatus(projectDir);
371
+ inputs.security = securityCheck.ok ? 'OK' : 'BLOCKED';
372
+
373
+ if (!securityCheck.exists) {
374
+ blockers.push({
375
+ code: GA_BLOCKER_CODE.SECURITY_NOT_CHECKED,
376
+ message: 'Security reports not found. Run "verax security:check" first.',
377
+ context: {}
378
+ });
379
+ } else if (!securityCheck.ok) {
380
+ blockers.push({
381
+ code: GA_BLOCKER_CODE.SECURITY_BLOCKED,
382
+ message: `SECURITY-BLOCKED: ${securityCheck.blockers.join('; ')}`,
383
+ context: { blockers: securityCheck.blockers }
384
+ });
385
+ }
386
+ } catch (error) {
387
+ blockers.push({
388
+ code: GA_BLOCKER_CODE.SECURITY_NOT_CHECKED,
389
+ message: `Security check failed: ${error.message}`,
390
+ context: { error: error.message }
391
+ });
392
+ }
393
+
394
+ // 9. Performance Budget (PHASE 21.9) - BLOCKING perf violations block GA
395
+ if (runId) {
396
+ try {
397
+ const { checkPerformanceStatus } = await import('../perf/perf.enforcer.js');
398
+ const perfCheck = checkPerformanceStatus(projectDir, runId);
399
+ inputs.performance = perfCheck.ok ? 'OK' : 'BLOCKED';
400
+
401
+ if (!perfCheck.exists) {
402
+ // Performance report missing is not a blocker (may be from old runs)
403
+ inputs.performance = 'MISSING';
404
+ } else if (!perfCheck.ok) {
405
+ blockers.push({
406
+ code: GA_BLOCKER_CODE.PERFORMANCE_BLOCKED,
407
+ message: `PERFORMANCE-BLOCKED: ${perfCheck.blockers.join('; ')}`,
408
+ context: { blockers: perfCheck.blockers, verdict: perfCheck.verdict }
409
+ });
410
+ }
411
+ } catch (error) {
412
+ // Performance check failure is not a blocker (may be from old runs)
413
+ inputs.performance = 'ERROR';
414
+ }
415
+ }
416
+
417
+ const pass = blockers.length === 0;
418
+
419
+ const summary = {
420
+ pass,
421
+ blockersCount: blockers.length,
422
+ warningsCount: warnings.length,
423
+ checkedAt: new Date().toISOString()
424
+ };
425
+
426
+ return {
427
+ pass,
428
+ blockers,
429
+ warnings,
430
+ summary,
431
+ inputs
432
+ };
433
+ }
434
+
@@ -0,0 +1,86 @@
1
+ /**
2
+ * PHASE 21.6 — GA Enforcer
3
+ *
4
+ * Hard enforcement that blocks shipping without GA-READY status.
5
+ * This is not advisory. This is hard enforcement.
6
+ */
7
+
8
+ import { readFileSync, existsSync } from 'fs';
9
+ import { resolve } from 'path';
10
+ import { createInternalFailure } from '../failures/failure.factory.js';
11
+ import { FAILURE_CODE } from '../failures/failure.types.js';
12
+
13
+ /**
14
+ * Check if GA status exists and is READY
15
+ *
16
+ * @param {string} projectDir - Project directory
17
+ * @param {string} runId - Run ID
18
+ * @returns {Object} { exists: boolean, ready: boolean, status: Object|null }
19
+ */
20
+ export function checkGAStatus(projectDir, runId) {
21
+ const statusPath = resolve(projectDir, '.verax', 'runs', runId, 'ga.status.json');
22
+
23
+ if (!existsSync(statusPath)) {
24
+ return {
25
+ exists: false,
26
+ ready: false,
27
+ status: null
28
+ };
29
+ }
30
+
31
+ try {
32
+ const content = readFileSync(statusPath, 'utf-8');
33
+ const status = JSON.parse(content);
34
+
35
+ return {
36
+ exists: true,
37
+ ready: status.gaReady === true,
38
+ status
39
+ };
40
+ } catch (error) {
41
+ return {
42
+ exists: false,
43
+ ready: false,
44
+ status: null,
45
+ error: error.message
46
+ };
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Enforce GA readiness before release operations
52
+ *
53
+ * @param {string} projectDir - Project directory
54
+ * @param {string} runId - Run ID
55
+ * @param {string} operation - Operation name (publish, release, tag)
56
+ * @throws {Error} If GA not ready
57
+ */
58
+ export function enforceGAReadiness(projectDir, runId, operation) {
59
+ const check = checkGAStatus(projectDir, runId);
60
+
61
+ if (!check.exists) {
62
+ const failure = createInternalFailure(
63
+ FAILURE_CODE.INTERNAL_UNEXPECTED_ERROR,
64
+ `Cannot ${operation}: GA status not found. Run 'verax ga' first.`,
65
+ 'ga.enforcer',
66
+ { operation, runId },
67
+ null
68
+ );
69
+ throw failure;
70
+ }
71
+
72
+ if (!check.ready) {
73
+ const blockers = check.status?.blockers || [];
74
+ const blockerMessages = blockers.map(b => b.message).join('; ');
75
+
76
+ const failure = createInternalFailure(
77
+ FAILURE_CODE.INTERNAL_UNEXPECTED_ERROR,
78
+ `Cannot ${operation}: GA-BLOCKED. Blockers: ${blockerMessages}`,
79
+ 'ga.enforcer',
80
+ { operation, runId, blockers },
81
+ null
82
+ );
83
+ throw failure;
84
+ }
85
+ }
86
+
@@ -0,0 +1,109 @@
1
+ /**
2
+ * PHASE 23 — Guardrails Report Writer
3
+ *
4
+ * Writes guardrails.report.json artifact per run.
5
+ */
6
+
7
+ import { writeFileSync } from 'fs';
8
+ import { resolve } from 'path';
9
+ import { ARTIFACT_REGISTRY } from '../artifacts/registry.js';
10
+
11
+ /**
12
+ * Write guardrails report to disk.
13
+ *
14
+ * @param {string} runDir - Absolute run directory path
15
+ * @param {Array} findings - Array of findings with guardrails data
16
+ * @param {Object} truthDecisions - Map of findingIdentity -> truthDecision
17
+ * @returns {string} Path to written report
18
+ */
19
+ export function writeGuardrailsReport(runDir, findings, truthDecisions = {}) {
20
+ const reportPath = resolve(runDir, ARTIFACT_REGISTRY.guardrailsReport.filename);
21
+
22
+ // Build per-finding entries (deterministic ordering by findingIdentity)
23
+ const perFinding = {};
24
+ const summary = {
25
+ totalFindings: findings.length,
26
+ byFinalDecision: {
27
+ CONFIRMED: 0,
28
+ SUSPECTED: 0,
29
+ INFORMATIONAL: 0,
30
+ IGNORED: 0
31
+ },
32
+ topRules: {},
33
+ contradictionCount: 0,
34
+ reconciliationCount: 0
35
+ };
36
+
37
+ for (const finding of findings) {
38
+ const findingIdentity = finding.findingId || finding.id || `finding-${findings.indexOf(finding)}`;
39
+ const guardrails = finding.guardrails || {};
40
+ const truthDecision = truthDecisions[findingIdentity] || null;
41
+
42
+ const appliedRules = guardrails.appliedRules || [];
43
+ const contradictions = guardrails.contradictions || [];
44
+ const finalDecision = truthDecision?.finalStatus || guardrails.finalDecision || finding.severity || 'SUSPECTED';
45
+
46
+ // Track top rules
47
+ for (const rule of appliedRules) {
48
+ const ruleCode = rule.code || rule.ruleId || 'unknown';
49
+ summary.topRules[ruleCode] = (summary.topRules[ruleCode] || 0) + 1;
50
+ }
51
+
52
+ // Track contradictions
53
+ if (contradictions.length > 0) {
54
+ summary.contradictionCount += contradictions.length;
55
+ }
56
+
57
+ // Track reconciliation
58
+ if (truthDecision && truthDecision.reconciliationReasons.length > 0) {
59
+ summary.reconciliationCount++;
60
+ }
61
+
62
+ // Track by final decision
63
+ if (summary.byFinalDecision[finalDecision] !== undefined) {
64
+ summary.byFinalDecision[finalDecision]++;
65
+ }
66
+
67
+ // Build per-finding entry
68
+ perFinding[findingIdentity] = {
69
+ appliedRules: appliedRules.map(r => ({
70
+ code: r.code || r.ruleId,
71
+ severity: r.severity,
72
+ category: r.category
73
+ })),
74
+ contradictions: contradictions.map(c => ({
75
+ code: c.code,
76
+ message: c.message
77
+ })),
78
+ finalDecision,
79
+ confidenceDelta: truthDecision?.confidenceDelta || guardrails.confidenceDelta || 0,
80
+ confidenceBefore: truthDecision?.confidenceBefore || finding.confidence || 0,
81
+ confidenceAfter: truthDecision?.confidenceAfter || finding.confidence || 0,
82
+ reconciliationReasons: truthDecision?.reconciliationReasons || [],
83
+ contradictionsResolved: truthDecision?.contradictionsResolved || []
84
+ };
85
+ }
86
+
87
+ // Sort top rules by count (descending)
88
+ const topRulesSorted = Object.entries(summary.topRules)
89
+ .sort((a, b) => b[1] - a[1])
90
+ .slice(0, 10)
91
+ .map(([code, count]) => ({ code, count }));
92
+
93
+ // Build report
94
+ const report = {
95
+ version: 1,
96
+ generatedAt: new Date().toISOString(),
97
+ summary: {
98
+ ...summary,
99
+ topRules: topRulesSorted
100
+ },
101
+ perFinding
102
+ };
103
+
104
+ // Write to disk
105
+ writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf8');
106
+
107
+ return reportPath;
108
+ }
109
+