@veraxhq/verax 0.3.0 → 0.4.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 (191) hide show
  1. package/README.md +28 -20
  2. package/bin/verax.js +11 -18
  3. package/package.json +28 -7
  4. package/src/cli/commands/baseline.js +1 -2
  5. package/src/cli/commands/default.js +72 -81
  6. package/src/cli/commands/doctor.js +29 -0
  7. package/src/cli/commands/ga.js +3 -0
  8. package/src/cli/commands/gates.js +1 -1
  9. package/src/cli/commands/inspect.js +6 -133
  10. package/src/cli/commands/release-check.js +2 -0
  11. package/src/cli/commands/run.js +74 -246
  12. package/src/cli/commands/security-check.js +2 -1
  13. package/src/cli/commands/truth.js +0 -1
  14. package/src/cli/entry.js +82 -309
  15. package/src/cli/util/angular-component-extractor.js +2 -2
  16. package/src/cli/util/angular-navigation-detector.js +2 -2
  17. package/src/cli/util/ast-interactive-detector.js +4 -6
  18. package/src/cli/util/ast-network-detector.js +3 -3
  19. package/src/cli/util/ast-promise-extractor.js +581 -0
  20. package/src/cli/util/ast-usestate-detector.js +3 -3
  21. package/src/cli/util/atomic-write.js +12 -1
  22. package/src/cli/util/console-reporter.js +72 -0
  23. package/src/cli/util/detection-engine.js +105 -41
  24. package/src/cli/util/determinism-runner.js +2 -1
  25. package/src/cli/util/determinism-writer.js +1 -1
  26. package/src/cli/util/digest-engine.js +359 -0
  27. package/src/cli/util/dom-diff.js +226 -0
  28. package/src/cli/util/env-url.js +0 -4
  29. package/src/cli/util/evidence-engine.js +287 -0
  30. package/src/cli/util/expectation-extractor.js +217 -367
  31. package/src/cli/util/findings-writer.js +19 -126
  32. package/src/cli/util/framework-detector.js +572 -0
  33. package/src/cli/util/idgen.js +1 -1
  34. package/src/cli/util/interaction-planner.js +529 -0
  35. package/src/cli/util/learn-writer.js +2 -2
  36. package/src/cli/util/ledger-writer.js +110 -0
  37. package/src/cli/util/monorepo-resolver.js +162 -0
  38. package/src/cli/util/observation-engine.js +127 -278
  39. package/src/cli/util/observe-writer.js +2 -2
  40. package/src/cli/util/paths.js +12 -3
  41. package/src/cli/util/project-discovery.js +284 -3
  42. package/src/cli/util/project-writer.js +2 -2
  43. package/src/cli/util/run-id.js +23 -27
  44. package/src/cli/util/run-result.js +778 -0
  45. package/src/cli/util/selector-resolver.js +235 -0
  46. package/src/cli/util/summary-writer.js +2 -1
  47. package/src/cli/util/svelte-navigation-detector.js +3 -3
  48. package/src/cli/util/svelte-sfc-extractor.js +0 -1
  49. package/src/cli/util/svelte-state-detector.js +1 -2
  50. package/src/cli/util/trust-activation-integration.js +496 -0
  51. package/src/cli/util/trust-activation-wrapper.js +85 -0
  52. package/src/cli/util/trust-integration-hooks.js +164 -0
  53. package/src/cli/util/types.js +153 -0
  54. package/src/cli/util/url-validation.js +40 -0
  55. package/src/cli/util/vue-navigation-detector.js +4 -3
  56. package/src/cli/util/vue-sfc-extractor.js +1 -2
  57. package/src/cli/util/vue-state-detector.js +1 -1
  58. package/src/types/fs-augment.d.ts +23 -0
  59. package/src/types/global.d.ts +137 -0
  60. package/src/types/internal-types.d.ts +35 -0
  61. package/src/verax/cli/finding-explainer.js +3 -56
  62. package/src/verax/cli/init.js +4 -18
  63. package/src/verax/core/action-classifier.js +4 -3
  64. package/src/verax/core/artifacts/registry.js +0 -15
  65. package/src/verax/core/artifacts/verifier.js +18 -8
  66. package/src/verax/core/baseline/baseline.snapshot.js +2 -0
  67. package/src/verax/core/capabilities/gates.js +7 -1
  68. package/src/verax/core/confidence/confidence-compute.js +14 -7
  69. package/src/verax/core/confidence/confidence.loader.js +1 -0
  70. package/src/verax/core/confidence-engine-refactor.js +8 -3
  71. package/src/verax/core/confidence-engine.js +162 -23
  72. package/src/verax/core/contracts/types.js +1 -0
  73. package/src/verax/core/contracts/validators.js +79 -4
  74. package/src/verax/core/decision-snapshot.js +3 -30
  75. package/src/verax/core/decisions/decision.trace.js +2 -0
  76. package/src/verax/core/determinism/contract-writer.js +2 -2
  77. package/src/verax/core/determinism/contract.js +1 -1
  78. package/src/verax/core/determinism/diff.js +42 -1
  79. package/src/verax/core/determinism/engine.js +7 -6
  80. package/src/verax/core/determinism/finding-identity.js +3 -2
  81. package/src/verax/core/determinism/normalize.js +32 -4
  82. package/src/verax/core/determinism/report-writer.js +1 -0
  83. package/src/verax/core/determinism/run-fingerprint.js +7 -2
  84. package/src/verax/core/dynamic-route-intelligence.js +8 -7
  85. package/src/verax/core/evidence/evidence-capture-service.js +1 -0
  86. package/src/verax/core/evidence/evidence-intent-ledger.js +2 -1
  87. package/src/verax/core/evidence-builder.js +2 -2
  88. package/src/verax/core/execution-mode-context.js +1 -1
  89. package/src/verax/core/execution-mode-detector.js +5 -3
  90. package/src/verax/core/failures/exit-codes.js +39 -37
  91. package/src/verax/core/failures/failure-summary.js +1 -1
  92. package/src/verax/core/failures/failure.factory.js +3 -3
  93. package/src/verax/core/failures/failure.ledger.js +3 -2
  94. package/src/verax/core/ga/ga.artifact.js +1 -1
  95. package/src/verax/core/ga/ga.contract.js +3 -2
  96. package/src/verax/core/ga/ga.enforcer.js +1 -0
  97. package/src/verax/core/guardrails/policy.loader.js +1 -0
  98. package/src/verax/core/guardrails/truth-reconciliation.js +1 -1
  99. package/src/verax/core/guardrails-engine.js +2 -2
  100. package/src/verax/core/incremental-store.js +1 -0
  101. package/src/verax/core/integrity/budget.js +138 -0
  102. package/src/verax/core/integrity/determinism.js +342 -0
  103. package/src/verax/core/integrity/integrity.js +208 -0
  104. package/src/verax/core/integrity/poisoning.js +108 -0
  105. package/src/verax/core/integrity/transaction.js +140 -0
  106. package/src/verax/core/observe/run-timeline.js +2 -0
  107. package/src/verax/core/perf/perf.report.js +2 -0
  108. package/src/verax/core/pipeline-tracker.js +5 -0
  109. package/src/verax/core/release/provenance.builder.js +73 -214
  110. package/src/verax/core/release/release.enforcer.js +14 -9
  111. package/src/verax/core/release/reproducibility.check.js +1 -0
  112. package/src/verax/core/release/sbom.builder.js +32 -23
  113. package/src/verax/core/replay-validator.js +2 -0
  114. package/src/verax/core/replay.js +4 -0
  115. package/src/verax/core/report/cross-index.js +6 -3
  116. package/src/verax/core/report/human-summary.js +141 -1
  117. package/src/verax/core/route-intelligence.js +4 -3
  118. package/src/verax/core/run-id.js +6 -3
  119. package/src/verax/core/run-manifest.js +4 -3
  120. package/src/verax/core/security/secrets.scan.js +10 -7
  121. package/src/verax/core/security/security.enforcer.js +4 -0
  122. package/src/verax/core/security/supplychain.policy.js +9 -1
  123. package/src/verax/core/security/vuln.scan.js +2 -2
  124. package/src/verax/core/truth/truth.certificate.js +3 -1
  125. package/src/verax/core/ui-feedback-intelligence.js +12 -46
  126. package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
  127. package/src/verax/detect/confidence-engine.js +100 -660
  128. package/src/verax/detect/confidence-helper.js +1 -0
  129. package/src/verax/detect/detection-engine.js +1 -18
  130. package/src/verax/detect/dynamic-route-findings.js +17 -14
  131. package/src/verax/detect/expectation-chain-detector.js +1 -1
  132. package/src/verax/detect/expectation-model.js +3 -5
  133. package/src/verax/detect/failure-cause-inference.js +293 -0
  134. package/src/verax/detect/findings-writer.js +126 -166
  135. package/src/verax/detect/flow-detector.js +2 -2
  136. package/src/verax/detect/form-silent-failure.js +98 -0
  137. package/src/verax/detect/index.js +51 -234
  138. package/src/verax/detect/invariants-enforcer.js +147 -0
  139. package/src/verax/detect/journey-stall-detector.js +4 -4
  140. package/src/verax/detect/navigation-silent-failure.js +82 -0
  141. package/src/verax/detect/problem-aggregator.js +361 -0
  142. package/src/verax/detect/route-findings.js +7 -6
  143. package/src/verax/detect/summary-writer.js +477 -0
  144. package/src/verax/detect/test-failure-cause-inference.js +314 -0
  145. package/src/verax/detect/ui-feedback-findings.js +18 -18
  146. package/src/verax/detect/verdict-engine.js +3 -57
  147. package/src/verax/detect/view-switch-correlator.js +2 -2
  148. package/src/verax/flow/flow-engine.js +2 -1
  149. package/src/verax/flow/flow-spec.js +0 -6
  150. package/src/verax/index.js +48 -412
  151. package/src/verax/intel/ts-program.js +1 -0
  152. package/src/verax/intel/vue-navigation-extractor.js +3 -0
  153. package/src/verax/learn/action-contract-extractor.js +67 -682
  154. package/src/verax/learn/ast-contract-extractor.js +1 -1
  155. package/src/verax/learn/flow-extractor.js +1 -0
  156. package/src/verax/learn/project-detector.js +5 -0
  157. package/src/verax/learn/react-router-extractor.js +2 -0
  158. package/src/verax/learn/route-validator.js +1 -4
  159. package/src/verax/learn/source-instrumenter.js +1 -0
  160. package/src/verax/learn/state-extractor.js +2 -1
  161. package/src/verax/learn/static-extractor.js +1 -0
  162. package/src/verax/observe/coverage-gaps.js +132 -0
  163. package/src/verax/observe/expectation-handler.js +126 -0
  164. package/src/verax/observe/incremental-skip.js +46 -0
  165. package/src/verax/observe/index.js +735 -84
  166. package/src/verax/observe/interaction-executor.js +192 -0
  167. package/src/verax/observe/interaction-runner.js +782 -530
  168. package/src/verax/observe/network-firewall.js +86 -0
  169. package/src/verax/observe/observation-builder.js +169 -0
  170. package/src/verax/observe/observe-context.js +1 -1
  171. package/src/verax/observe/observe-helpers.js +2 -1
  172. package/src/verax/observe/observe-runner.js +28 -24
  173. package/src/verax/observe/observers/budget-observer.js +3 -3
  174. package/src/verax/observe/observers/console-observer.js +4 -4
  175. package/src/verax/observe/observers/coverage-observer.js +4 -4
  176. package/src/verax/observe/observers/interaction-observer.js +3 -3
  177. package/src/verax/observe/observers/navigation-observer.js +4 -4
  178. package/src/verax/observe/observers/network-observer.js +4 -4
  179. package/src/verax/observe/observers/safety-observer.js +1 -1
  180. package/src/verax/observe/observers/ui-feedback-observer.js +4 -4
  181. package/src/verax/observe/page-traversal.js +138 -0
  182. package/src/verax/observe/snapshot-ops.js +94 -0
  183. package/src/verax/observe/ui-signal-sensor.js +2 -148
  184. package/src/verax/scan-summary-writer.js +10 -42
  185. package/src/verax/shared/artifact-manager.js +30 -13
  186. package/src/verax/shared/caching.js +1 -0
  187. package/src/verax/shared/expectation-tracker.js +1 -0
  188. package/src/verax/shared/zip-artifacts.js +6 -0
  189. package/src/verax/core/confidence-engine.js.backup +0 -471
  190. package/src/verax/shared/config-loader.js +0 -169
  191. /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
@@ -0,0 +1,94 @@
1
+ /**
2
+ * SNAPSHOT OPERATIONS MODULE
3
+ *
4
+ * Encapsulates snapshot loading, comparison, building, and saving logic.
5
+ * Extracted from observe/index.js (STAGE D2.1).
6
+ *
7
+ * Preserves 100% of original behavior:
8
+ * - Same incremental mode conditions
9
+ * - Same snapshot JSON shape
10
+ * - Same load/save timing
11
+ * - Same trace filtering
12
+ */
13
+
14
+ import {
15
+ loadPreviousSnapshot,
16
+ buildSnapshot,
17
+ compareSnapshots,
18
+ saveSnapshot
19
+ } from '../core/incremental-store.js';
20
+
21
+ /**
22
+ * Initialize snapshot operations at the beginning of observation
23
+ *
24
+ * Loads previous snapshot, compares with baseline, determines incremental mode.
25
+ * Extracted from lines 108-111 of observe/index.js
26
+ *
27
+ * @param {string} projectDir - Project directory
28
+ * @param {Object} manifest - Loaded manifest object
29
+ * @returns {Promise<{oldSnapshot: Object|null, snapshotDiff: Object|null, incrementalMode: boolean}>}
30
+ */
31
+ export async function initializeSnapshot(projectDir, manifest) {
32
+ let oldSnapshot = null;
33
+ let snapshotDiff = null;
34
+ let incrementalMode = false;
35
+
36
+ // SCALE INTELLIGENCE: Load previous snapshot for incremental mode
37
+ oldSnapshot = loadPreviousSnapshot(projectDir);
38
+ if (oldSnapshot) {
39
+ const currentSnapshot = buildSnapshot(manifest, []);
40
+ snapshotDiff = compareSnapshots(oldSnapshot, currentSnapshot);
41
+ incrementalMode = !snapshotDiff.hasChanges; // Use incremental if nothing changed
42
+ }
43
+
44
+ return {
45
+ oldSnapshot,
46
+ snapshotDiff,
47
+ incrementalMode
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Finalize snapshot operations at the end of observation
53
+ *
54
+ * Builds current snapshot from observed interactions, saves for next run,
55
+ * and creates incremental metadata for observation object.
56
+ * Extracted from lines 789-811 of observe/index.js
57
+ *
58
+ * @param {Object} manifest - Loaded manifest object (or null if not provided)
59
+ * @param {Array} traces - All collected traces
60
+ * @param {Array} skippedInteractions - Array of skipped interactions
61
+ * @param {boolean} incrementalMode - Whether incremental mode was enabled
62
+ * @param {Object} snapshotDiff - Snapshot diff from initialization (or null)
63
+ * @param {string} projectDir - Project directory
64
+ * @param {string} runId - Run identifier
65
+ * @param {string} url - Initial URL (fallback for trace.before.url)
66
+ * @returns {Promise<{enabled: boolean, snapshotDiff: Object, skippedInteractionsCount: number}|null>}
67
+ */
68
+ export async function finalizeSnapshot(manifest, traces, skippedInteractions, incrementalMode, snapshotDiff, projectDir, runId, url) {
69
+ let incrementalMetadata = null;
70
+
71
+ // SCALE INTELLIGENCE: Save snapshot for next incremental run
72
+ if (manifest) {
73
+ // Build snapshot from current run (extract interactions from traces)
74
+ const observedInteractions = traces
75
+ .filter(t => t.interaction && !t.incremental)
76
+ .map(t => ({
77
+ type: t.interaction?.type,
78
+ selector: t.interaction?.selector,
79
+ url: t.before?.url || url
80
+ }));
81
+
82
+ const currentSnapshot = buildSnapshot(manifest, observedInteractions);
83
+ saveSnapshot(projectDir, currentSnapshot, runId);
84
+
85
+ // Add incremental mode metadata to observation
86
+ incrementalMetadata = {
87
+ enabled: incrementalMode,
88
+ snapshotDiff: snapshotDiff,
89
+ skippedInteractionsCount: skippedInteractions.filter(s => s.reason === 'incremental_unchanged').length
90
+ };
91
+ }
92
+
93
+ return incrementalMetadata;
94
+ }
@@ -2,30 +2,14 @@
2
2
  * WAVE 3: UI Signal Sensor
3
3
  * Detects user-visible feedback signals: loading states, dialogs, error messages
4
4
  * Conservative: only count signals with accessibility semantics or explicit attributes
5
- *
6
- * CSS SPINNER DETECTION: Detects CSS-only loading indicators without semantic attributes
7
5
  */
8
6
 
9
- import {
10
- isBorderSpinnerPattern,
11
- isRotationAnimation,
12
- isPulseAnimation,
13
- isSpinnerSize,
14
- isDecorativeElement,
15
- CSS_SPINNER_REASON_CODES
16
- } from '../shared/css-spinner-rules.js';
17
-
18
7
  export class UISignalSensor {
19
8
  /**
20
9
  * Snapshot current UI signals on the page.
21
- * Returns: { hasLoadingIndicator, hasDialog, buttonStateChanged, errorSignals, explanation, cssSpinners }
22
- *
23
- * @param {Object} page - Playwright page object
24
- * @param {number} interactionTime - Optional: timestamp of interaction (for timing window)
25
- * @param {Object} beforeSnapshot - Optional: snapshot before interaction (for correlation)
26
- * @returns {Promise<Object>} UI signals snapshot
10
+ * Returns: { hasLoadingIndicator, hasDialog, buttonStateChanged, errorSignals, explanation }
27
11
  */
28
- async snapshot(page, interactionTime = null, beforeSnapshot = null) {
12
+ async snapshot(page) {
29
13
  const signals = await page.evaluate(() => {
30
14
  const result = {
31
15
  hasLoadingIndicator: false,
@@ -101,136 +85,6 @@ export class UISignalSensor {
101
85
  );
102
86
  }
103
87
 
104
- // CSS SPINNER DETECTION: Detect CSS-only loading indicators
105
- result.cssSpinners = [];
106
- result.cssSpinnerDetected = false;
107
-
108
- const allElements = Array.from(document.querySelectorAll('*'));
109
- const currentTime = Date.now();
110
- const MAX_SPINNER_SIZE = 100;
111
- const MIN_SPINNER_SIZE = 8;
112
- const SPINNER_TIMING_WINDOW_MS = 2000;
113
-
114
- // Helper functions (inlined for browser context)
115
- const isBorderSpinnerPattern = (style) => {
116
- const borderWidth = parseFloat(style.borderWidth) || 0;
117
- const borderTopWidth = parseFloat(style.borderTopWidth) || 0;
118
- if (borderWidth < 2 && borderTopWidth < 2) return false;
119
-
120
- const borderColor = style.borderColor || '';
121
- const borderTopColor = style.borderTopColor || '';
122
- const hasDifferentTopColor = borderTopColor && borderColor && borderTopColor !== borderColor && borderTopColor !== 'rgba(0, 0, 0, 0)';
123
-
124
- const borderRadius = style.borderRadius || '';
125
- const isCircular = borderRadius.includes('50%') || borderRadius.includes('999') || parseFloat(borderRadius) > 20;
126
-
127
- return hasDifferentTopColor && isCircular;
128
- };
129
-
130
- const isRotationAnimation = (style) => {
131
- const animationName = (style.animationName || '').toLowerCase();
132
- const animation = (style.animation || '').toLowerCase();
133
- const transform = style.transform || '';
134
- const animationDuration = style.animationDuration || '';
135
-
136
- const spinnerKeywords = ['spin', 'rotate', 'loading', 'loader'];
137
- const hasSpinnerKeyword = spinnerKeywords.some(k => animationName.includes(k) || animation.includes(k));
138
- const hasRotation = transform.includes('rotate');
139
- const isAnimated = animationDuration && animationDuration !== '0s' && !animationDuration.includes('none');
140
-
141
- return (hasRotation || hasSpinnerKeyword) && isAnimated;
142
- };
143
-
144
- const isPulseAnimation = (style) => {
145
- const animationName = (style.animationName || '').toLowerCase();
146
- const animation = (style.animation || '').toLowerCase();
147
- const animationDuration = style.animationDuration || '';
148
- const opacity = parseFloat(style.opacity) || 1;
149
-
150
- const pulseKeywords = ['pulse', 'fade', 'loading'];
151
- const hasPulseKeyword = pulseKeywords.some(k => animationName.includes(k) || animation.includes(k));
152
- const isAnimated = animationDuration && animationDuration !== '0s' && !animationDuration.includes('none');
153
- const hasOpacityVariation = opacity < 1;
154
-
155
- return hasPulseKeyword && isAnimated && hasOpacityVariation;
156
- };
157
-
158
- const isSpinnerSize = (width, height) => {
159
- const maxDim = Math.max(width, height);
160
- const minDim = Math.min(width, height);
161
- if (maxDim > MAX_SPINNER_SIZE || minDim < MIN_SPINNER_SIZE) return false;
162
- const aspectRatio = maxDim / (minDim || 1);
163
- return aspectRatio <= 2.0;
164
- };
165
-
166
- for (const el of allElements) {
167
- const style = window.getComputedStyle(el);
168
- // @ts-expect-error
169
- if (el.offsetParent === null || style.visibility === 'hidden' || style.display === 'none' || style.opacity === '0') {
170
- continue;
171
- }
172
-
173
- // Skip if element has semantic loading attributes
174
- if (el.hasAttribute('aria-busy') || el.hasAttribute('data-loading') || el.getAttribute('role') === 'progressbar') {
175
- continue;
176
- }
177
-
178
- const width = el.offsetWidth || 0;
179
- const height = el.offsetHeight || 0;
180
- const elementId = el.id || `${el.tagName}-${el.className}`;
181
-
182
- // Check size bounds
183
- if (!isSpinnerSize(width, height)) {
184
- continue;
185
- }
186
-
187
- // Check for spinner patterns
188
- let spinnerType = null;
189
- let reasonCode = null;
190
-
191
- if (isBorderSpinnerPattern(style)) {
192
- spinnerType = 'border-spinner';
193
- reasonCode = 'UI_CSS_SPINNER_DETECTED_BORDER';
194
- } else if (isRotationAnimation(style)) {
195
- spinnerType = 'rotation-spinner';
196
- reasonCode = 'UI_CSS_SPINNER_DETECTED_ROTATION';
197
- } else if (isPulseAnimation(style)) {
198
- spinnerType = 'pulse-spinner';
199
- reasonCode = 'UI_CSS_SPINNER_DETECTED_PULSE';
200
- }
201
-
202
- if (spinnerType) {
203
- result.cssSpinners.push({
204
- type: spinnerType,
205
- reasonCode,
206
- elementId,
207
- width,
208
- height
209
- });
210
- result.cssSpinnerDetected = true;
211
- }
212
- }
213
-
214
- // Require 2+ independent signals for CONFIRMED CSS spinner feedback
215
- if (result.cssSpinnerDetected) {
216
- const hasDisabledButton = result.disabledElements.length > 0;
217
- const hasPointerEventsDisabled = allElements.some(el => {
218
- const style = window.getComputedStyle(el);
219
- return style.pointerEvents === 'none';
220
- });
221
-
222
- const corroboratingSignals = [hasDisabledButton, hasPointerEventsDisabled].filter(Boolean).length;
223
-
224
- if (corroboratingSignals >= 1) {
225
- result.hasLoadingIndicator = true;
226
- result.explanation.push(`CSS spinner detected with ${corroboratingSignals} corroborating signal(s)`);
227
- result.cssSpinnerReasonCode = 'UI_CSS_SPINNER_ACCEPTED_WITH_CORROBORATION';
228
- } else {
229
- result.explanation.push('CSS spinner detected but no corroborating signals (SUSPECTED)');
230
- result.cssSpinnerReasonCode = 'UI_CSS_SPINNER_REJECTED_NO_CORROBORATION';
231
- }
232
- }
233
-
234
88
  // VALIDATION INTELLIGENCE v1: Detect visible validation feedback
235
89
  // Check for aria-invalid="true" with visible error text nearby
236
90
  const invalidElements = Array.from(document.querySelectorAll('[aria-invalid="true"]'));
@@ -3,11 +3,8 @@ import { writeFileSync, mkdirSync, readFileSync, existsSync } from 'fs';
3
3
  import { computeExpectationsSummary } from './shared/artifact-manager.js';
4
4
  import { createImpactSummary } from './core/silence-impact.js';
5
5
  import { computeDecisionSnapshot } from './core/decision-snapshot.js';
6
- import { VERAX_PRODUCT_DEFINITION } from './core/product-definition.js';
7
- import { ARTIFACT_REGISTRY, getArtifactVersions } from './core/artifacts/registry.js';
8
- import { generateHumanSummary } from './core/report/human-summary.js';
9
6
 
10
- export async function writeScanSummary(projectDir, url, projectType, learnTruth, observeTruth, detectTruth, manifestPath, tracesPath, findingsPath, runDirOpt, findingsArray = null) {
7
+ export function writeScanSummary(projectDir, url, projectType, learnTruth, observeTruth, detectTruth, manifestPath, tracesPath, findingsPath, runDirOpt, findingsArray = null) {
11
8
  if (!runDirOpt) {
12
9
  throw new Error('runDirOpt is required');
13
10
  }
@@ -18,6 +15,7 @@ export async function writeScanSummary(projectDir, url, projectType, learnTruth,
18
15
  let expectationsSummary = { total: 0, navigation: 0, networkActions: 0, stateActions: 0 };
19
16
  if (manifestPath && existsSync(manifestPath)) {
20
17
  try {
18
+ // @ts-expect-error - readFileSync with encoding returns string
21
19
  const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
22
20
  expectationsSummary = computeExpectationsSummary(manifest);
23
21
  } catch (error) {
@@ -32,30 +30,21 @@ export async function writeScanSummary(projectDir, url, projectType, learnTruth,
32
30
  }
33
31
 
34
32
  // PHASE 6: Compute determinism summary from decisions.json
35
- // PHASE 21.2: Use HARD verdict from determinism contract
36
33
  let determinismSummary = null;
37
34
  if (runDirOpt && observeTruth?.runId) {
38
35
  const decisionsPath = resolve(runDirOpt, 'decisions.json');
39
36
  if (existsSync(decisionsPath)) {
40
37
  try {
38
+ // @ts-expect-error - readFileSync with encoding returns string
41
39
  const decisions = JSON.parse(readFileSync(decisionsPath, 'utf-8'));
42
- const { DecisionRecorder } = await import('./core/determinism-model.js');
40
+ const { DecisionRecorder } = require('./core/determinism-model.js');
43
41
  const recorder = DecisionRecorder.fromExport(decisions);
44
42
  const summary = recorder.getSummary();
45
43
 
46
- // PHASE 21.2: Compute HARD verdict from adaptive events
47
- const { computeDeterminismVerdict } = await import('./core/determinism/contract.js');
48
- const verdict = computeDeterminismVerdict(recorder);
49
-
50
44
  determinismSummary = {
51
- verdict: verdict.verdict, // PHASE 21.2: HARD verdict (DETERMINISTIC or NON_DETERMINISTIC)
52
- message: verdict.message,
53
- reasons: verdict.reasons,
54
- adaptiveEventsCount: verdict.adaptiveEvents.length,
55
- // Legacy fields for backward compatibility
56
- isDeterministic: verdict.verdict === 'DETERMINISTIC',
57
- totalDecisions: summary.total,
58
- decisionsByCategory: summary.byCategory,
45
+ isDeterministic: summary.isDeterministic,
46
+ totalDecisions: summary.totalDecisions,
47
+ decisionsByCategory: summary.decisionsByCategory,
59
48
  decisionsPath: decisionsPath
60
49
  };
61
50
  } catch (error) {
@@ -71,37 +60,19 @@ export async function writeScanSummary(projectDir, url, projectType, learnTruth,
71
60
  decisionSnapshot = computeDecisionSnapshot(findingsArray, detectTruth, observeTruth, silences);
72
61
  }
73
62
 
74
- // PHASE 21.10: Generate human summary
75
- let humanSummary = null;
76
- if (observeTruth?.runId) {
77
- try {
78
- humanSummary = await generateHumanSummary(projectDir, observeTruth.runId);
79
- } catch {
80
- // Ignore errors generating human summary
81
- }
82
- }
83
-
84
63
  const summary = {
85
64
  version: 1,
86
- contractVersion: 1, // PHASE 0: Track schema changes
87
65
  scannedAt: new Date().toISOString(),
88
66
  url: url,
89
67
  projectType: projectType,
90
68
  expectationsSummary: expectationsSummary,
91
- // PHASE 0: Include Evidence Law statement
92
- evidenceLaw: {
93
- statement: VERAX_PRODUCT_DEFINITION.evidenceLaw.statement,
94
- description: VERAX_PRODUCT_DEFINITION.evidenceLaw.definition,
95
- enforcement: VERAX_PRODUCT_DEFINITION.evidenceLaw.enforcement
96
- },
97
69
  // PHASE 7: Decision snapshot first (most important for human decision-making)
98
70
  decisionSnapshot: decisionSnapshot,
99
71
  // PHASE 7: Misinterpretation guards (explicit warnings)
100
72
  interpretationGuards: {
101
73
  zeroFindings: 'Zero findings does NOT mean no problems. Check unverified count and confidence level.',
102
74
  deterministicRun: 'Deterministic run does NOT mean correct site. Only means scan was reproducible.',
103
- highSilenceImpact: 'High silence impact does NOT mean failures exist. Only means unknowns affect confidence.',
104
- evidenceLaw: 'Not all findings are CONFIRMED. Some may be SUSPECTED (insufficient evidence). Only CONFIRMED findings are actionable.'
75
+ highSilenceImpact: 'High silence impact does NOT mean failures exist. Only means unknowns affect confidence.'
105
76
  },
106
77
  truth: {
107
78
  learn: learnTruth,
@@ -119,17 +90,14 @@ export async function writeScanSummary(projectDir, url, projectType, learnTruth,
119
90
  } : null,
120
91
  // PHASE 6: Add determinism summary
121
92
  determinism: determinismSummary,
122
- // PHASE 21.10: Add human summary
123
- humanSummary: humanSummary,
124
93
  paths: {
125
94
  manifest: manifestPath,
126
95
  traces: tracesPath,
127
96
  findings: findingsPath
128
- },
129
- artifactVersions: getArtifactVersions()
97
+ }
130
98
  };
131
99
 
132
- const summaryPath = resolve(scanDir, ARTIFACT_REGISTRY.scanSummary.filename);
100
+ const summaryPath = resolve(scanDir, 'scan-summary.json');
133
101
  writeFileSync(summaryPath, JSON.stringify(summary, null, 2) + '\n');
134
102
 
135
103
  return {
@@ -14,15 +14,34 @@
14
14
 
15
15
  import { existsSync, mkdirSync, writeFileSync, appendFileSync } from 'fs';
16
16
  import { resolve } from 'path';
17
- import { randomBytes } from 'crypto';
18
- import { buildRunArtifactPaths } from '../core/artifacts/registry.js';
17
+ import { generateRunId as generateDeterministicRunId } from '../core/run-id.js';
18
+
19
+ const ZERO_BUDGET = Object.freeze({
20
+ maxScanDurationMs: 0,
21
+ maxInteractionsPerPage: 0,
22
+ maxUniqueUrls: 0,
23
+ interactionTimeoutMs: 0,
24
+ navigationTimeoutMs: 0,
25
+ });
19
26
 
20
27
  /**
21
28
  * Generate a unique run ID.
22
29
  * @returns {string} - 8-character hex ID
23
30
  */
24
- export function generateRunId() {
25
- return randomBytes(4).toString('hex');
31
+ export function generateRunId(seed = 'about:blank') {
32
+ let baseOrigin = 'about:blank';
33
+ try {
34
+ baseOrigin = new URL(seed).origin;
35
+ } catch {
36
+ baseOrigin = seed;
37
+ }
38
+ return generateDeterministicRunId({
39
+ url: seed,
40
+ safetyFlags: {},
41
+ baseOrigin,
42
+ scanBudget: ZERO_BUDGET,
43
+ manifestPath: null,
44
+ });
26
45
  }
27
46
 
28
47
  /**
@@ -31,22 +50,20 @@ export function generateRunId() {
31
50
  * @param {string} runId - Run identifier
32
51
  * @returns {Object} - Paths to each artifact location
33
52
  */
34
- export function initArtifactPaths(projectRoot, runId = null) {
35
- const id = runId || generateRunId();
53
+ export function initArtifactPaths(projectRoot, runId = null, seed = projectRoot) {
54
+ const id = runId || generateRunId(seed);
36
55
  const runDir = resolve(projectRoot, '.verax', 'runs', id);
37
- const registryPaths = buildRunArtifactPaths(runDir);
38
56
 
39
57
  const paths = {
40
58
  runId: id,
41
59
  runDir,
42
- summary: registryPaths.summaryJson,
43
- findings: registryPaths.findingsJson,
60
+ summary: resolve(runDir, 'summary.json'),
61
+ findings: resolve(runDir, 'findings.json'),
44
62
  expectations: resolve(runDir, 'expectations.json'),
45
- traces: registryPaths.tracesJsonl,
46
- evidence: registryPaths.evidenceDir,
63
+ traces: resolve(runDir, 'traces.jsonl'),
64
+ evidence: resolve(runDir, 'evidence'),
47
65
  flows: resolve(runDir, 'flows'),
48
- artifacts: resolve(projectRoot, '.verax', 'artifacts'), // Legacy compat
49
- registry: registryPaths.artifactVersions
66
+ artifacts: resolve(projectRoot, '.verax', 'artifacts') // Legacy compat
50
67
  };
51
68
 
52
69
  // Create directories
@@ -50,6 +50,7 @@ export function computeCacheKey(projectRoot, tsconfigPath, sourceFiles = []) {
50
50
  }
51
51
  }
52
52
 
53
+ // @ts-expect-error - digest returns string
53
54
  return hash.digest('hex').substring(0, 16); // Use first 16 chars for brevity
54
55
  }
55
56
 
@@ -26,6 +26,7 @@ export function generateExpectationId(expectation, type) {
26
26
 
27
27
  // Generate stable 8-character hex hash
28
28
  const hash = createHash('sha256').update(hashInput).digest('hex');
29
+ // @ts-expect-error - digest returns string
29
30
  return hash.substring(0, 8);
30
31
  }
31
32
 
@@ -45,6 +45,7 @@ export async function createArtifactsZip(runDir, outputPath = null) {
45
45
 
46
46
  // Handle errors
47
47
  archive.on('error', (err) => {
48
+ output.destroy();
48
49
  reject(err);
49
50
  });
50
51
 
@@ -52,6 +53,11 @@ export async function createArtifactsZip(runDir, outputPath = null) {
52
53
  resolvePromise(outputPath);
53
54
  });
54
55
 
56
+ output.on('error', (err) => {
57
+ archive.destroy();
58
+ reject(err);
59
+ });
60
+
55
61
  // Pipe archive data to file
56
62
  archive.pipe(output);
57
63