@veraxhq/verax 0.2.1 → 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 (213) hide show
  1. package/README.md +10 -6
  2. package/bin/verax.js +11 -11
  3. package/package.json +29 -8
  4. package/src/cli/commands/baseline.js +103 -0
  5. package/src/cli/commands/default.js +51 -6
  6. package/src/cli/commands/doctor.js +29 -0
  7. package/src/cli/commands/ga.js +246 -0
  8. package/src/cli/commands/gates.js +95 -0
  9. package/src/cli/commands/inspect.js +4 -2
  10. package/src/cli/commands/release-check.js +215 -0
  11. package/src/cli/commands/run.js +45 -6
  12. package/src/cli/commands/security-check.js +212 -0
  13. package/src/cli/commands/truth.js +113 -0
  14. package/src/cli/entry.js +30 -20
  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 +544 -0
  20. package/src/cli/util/ast-network-detector.js +603 -0
  21. package/src/cli/util/ast-promise-extractor.js +581 -0
  22. package/src/cli/util/ast-usestate-detector.js +602 -0
  23. package/src/cli/util/atomic-write.js +12 -1
  24. package/src/cli/util/bootstrap-guard.js +86 -0
  25. package/src/cli/util/console-reporter.js +72 -0
  26. package/src/cli/util/detection-engine.js +105 -41
  27. package/src/cli/util/determinism-runner.js +124 -0
  28. package/src/cli/util/determinism-writer.js +129 -0
  29. package/src/cli/util/digest-engine.js +359 -0
  30. package/src/cli/util/dom-diff.js +226 -0
  31. package/src/cli/util/evidence-engine.js +287 -0
  32. package/src/cli/util/expectation-extractor.js +151 -5
  33. package/src/cli/util/findings-writer.js +3 -0
  34. package/src/cli/util/framework-detector.js +572 -0
  35. package/src/cli/util/idgen.js +1 -1
  36. package/src/cli/util/interaction-planner.js +529 -0
  37. package/src/cli/util/learn-writer.js +2 -0
  38. package/src/cli/util/ledger-writer.js +110 -0
  39. package/src/cli/util/monorepo-resolver.js +162 -0
  40. package/src/cli/util/observation-engine.js +127 -278
  41. package/src/cli/util/observe-writer.js +2 -0
  42. package/src/cli/util/project-discovery.js +284 -0
  43. package/src/cli/util/project-writer.js +2 -0
  44. package/src/cli/util/run-id.js +23 -27
  45. package/src/cli/util/run-resolver.js +64 -0
  46. package/src/cli/util/run-result.js +778 -0
  47. package/src/cli/util/selector-resolver.js +235 -0
  48. package/src/cli/util/source-requirement.js +55 -0
  49. package/src/cli/util/summary-writer.js +2 -0
  50. package/src/cli/util/svelte-navigation-detector.js +163 -0
  51. package/src/cli/util/svelte-network-detector.js +80 -0
  52. package/src/cli/util/svelte-sfc-extractor.js +146 -0
  53. package/src/cli/util/svelte-state-detector.js +242 -0
  54. package/src/cli/util/trust-activation-integration.js +496 -0
  55. package/src/cli/util/trust-activation-wrapper.js +85 -0
  56. package/src/cli/util/trust-integration-hooks.js +164 -0
  57. package/src/cli/util/types.js +153 -0
  58. package/src/cli/util/url-validation.js +40 -0
  59. package/src/cli/util/vue-navigation-detector.js +178 -0
  60. package/src/cli/util/vue-sfc-extractor.js +161 -0
  61. package/src/cli/util/vue-state-detector.js +215 -0
  62. package/src/types/fs-augment.d.ts +23 -0
  63. package/src/types/global.d.ts +137 -0
  64. package/src/types/internal-types.d.ts +35 -0
  65. package/src/verax/cli/init.js +4 -18
  66. package/src/verax/core/action-classifier.js +4 -3
  67. package/src/verax/core/artifacts/registry.js +139 -0
  68. package/src/verax/core/artifacts/verifier.js +990 -0
  69. package/src/verax/core/baseline/baseline.enforcer.js +137 -0
  70. package/src/verax/core/baseline/baseline.snapshot.js +233 -0
  71. package/src/verax/core/capabilities/gates.js +505 -0
  72. package/src/verax/core/capabilities/registry.js +475 -0
  73. package/src/verax/core/confidence/confidence-compute.js +144 -0
  74. package/src/verax/core/confidence/confidence-invariants.js +234 -0
  75. package/src/verax/core/confidence/confidence-report-writer.js +112 -0
  76. package/src/verax/core/confidence/confidence-weights.js +44 -0
  77. package/src/verax/core/confidence/confidence.defaults.js +65 -0
  78. package/src/verax/core/confidence/confidence.loader.js +80 -0
  79. package/src/verax/core/confidence/confidence.schema.js +94 -0
  80. package/src/verax/core/confidence-engine-refactor.js +489 -0
  81. package/src/verax/core/confidence-engine.js +625 -0
  82. package/src/verax/core/contracts/index.js +29 -0
  83. package/src/verax/core/contracts/types.js +186 -0
  84. package/src/verax/core/contracts/validators.js +456 -0
  85. package/src/verax/core/decisions/decision.trace.js +278 -0
  86. package/src/verax/core/determinism/contract-writer.js +89 -0
  87. package/src/verax/core/determinism/contract.js +139 -0
  88. package/src/verax/core/determinism/diff.js +405 -0
  89. package/src/verax/core/determinism/engine.js +222 -0
  90. package/src/verax/core/determinism/finding-identity.js +149 -0
  91. package/src/verax/core/determinism/normalize.js +466 -0
  92. package/src/verax/core/determinism/report-writer.js +93 -0
  93. package/src/verax/core/determinism/run-fingerprint.js +123 -0
  94. package/src/verax/core/dynamic-route-intelligence.js +529 -0
  95. package/src/verax/core/evidence/evidence-capture-service.js +308 -0
  96. package/src/verax/core/evidence/evidence-intent-ledger.js +166 -0
  97. package/src/verax/core/evidence-builder.js +487 -0
  98. package/src/verax/core/execution-mode-context.js +77 -0
  99. package/src/verax/core/execution-mode-detector.js +192 -0
  100. package/src/verax/core/failures/exit-codes.js +88 -0
  101. package/src/verax/core/failures/failure-summary.js +76 -0
  102. package/src/verax/core/failures/failure.factory.js +225 -0
  103. package/src/verax/core/failures/failure.ledger.js +133 -0
  104. package/src/verax/core/failures/failure.types.js +196 -0
  105. package/src/verax/core/failures/index.js +10 -0
  106. package/src/verax/core/ga/ga-report-writer.js +43 -0
  107. package/src/verax/core/ga/ga.artifact.js +49 -0
  108. package/src/verax/core/ga/ga.contract.js +435 -0
  109. package/src/verax/core/ga/ga.enforcer.js +87 -0
  110. package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
  111. package/src/verax/core/guardrails/policy.defaults.js +210 -0
  112. package/src/verax/core/guardrails/policy.loader.js +84 -0
  113. package/src/verax/core/guardrails/policy.schema.js +110 -0
  114. package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
  115. package/src/verax/core/guardrails-engine.js +505 -0
  116. package/src/verax/core/incremental-store.js +1 -0
  117. package/src/verax/core/integrity/budget.js +138 -0
  118. package/src/verax/core/integrity/determinism.js +342 -0
  119. package/src/verax/core/integrity/integrity.js +208 -0
  120. package/src/verax/core/integrity/poisoning.js +108 -0
  121. package/src/verax/core/integrity/transaction.js +140 -0
  122. package/src/verax/core/observe/run-timeline.js +318 -0
  123. package/src/verax/core/perf/perf.contract.js +186 -0
  124. package/src/verax/core/perf/perf.display.js +65 -0
  125. package/src/verax/core/perf/perf.enforcer.js +91 -0
  126. package/src/verax/core/perf/perf.monitor.js +209 -0
  127. package/src/verax/core/perf/perf.report.js +200 -0
  128. package/src/verax/core/pipeline-tracker.js +243 -0
  129. package/src/verax/core/product-definition.js +127 -0
  130. package/src/verax/core/release/provenance.builder.js +130 -0
  131. package/src/verax/core/release/release-report-writer.js +40 -0
  132. package/src/verax/core/release/release.enforcer.js +164 -0
  133. package/src/verax/core/release/reproducibility.check.js +222 -0
  134. package/src/verax/core/release/sbom.builder.js +292 -0
  135. package/src/verax/core/replay-validator.js +2 -0
  136. package/src/verax/core/replay.js +4 -0
  137. package/src/verax/core/report/cross-index.js +195 -0
  138. package/src/verax/core/report/human-summary.js +362 -0
  139. package/src/verax/core/route-intelligence.js +420 -0
  140. package/src/verax/core/run-id.js +6 -3
  141. package/src/verax/core/run-manifest.js +4 -3
  142. package/src/verax/core/security/secrets.scan.js +329 -0
  143. package/src/verax/core/security/security-report.js +50 -0
  144. package/src/verax/core/security/security.enforcer.js +128 -0
  145. package/src/verax/core/security/supplychain.defaults.json +38 -0
  146. package/src/verax/core/security/supplychain.policy.js +334 -0
  147. package/src/verax/core/security/vuln.scan.js +265 -0
  148. package/src/verax/core/truth/truth.certificate.js +252 -0
  149. package/src/verax/core/ui-feedback-intelligence.js +481 -0
  150. package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
  151. package/src/verax/detect/confidence-engine.js +62 -34
  152. package/src/verax/detect/confidence-helper.js +34 -0
  153. package/src/verax/detect/dynamic-route-findings.js +338 -0
  154. package/src/verax/detect/expectation-chain-detector.js +417 -0
  155. package/src/verax/detect/expectation-model.js +2 -2
  156. package/src/verax/detect/failure-cause-inference.js +293 -0
  157. package/src/verax/detect/findings-writer.js +131 -35
  158. package/src/verax/detect/flow-detector.js +2 -2
  159. package/src/verax/detect/form-silent-failure.js +98 -0
  160. package/src/verax/detect/index.js +46 -5
  161. package/src/verax/detect/invariants-enforcer.js +147 -0
  162. package/src/verax/detect/journey-stall-detector.js +558 -0
  163. package/src/verax/detect/navigation-silent-failure.js +82 -0
  164. package/src/verax/detect/problem-aggregator.js +361 -0
  165. package/src/verax/detect/route-findings.js +219 -0
  166. package/src/verax/detect/summary-writer.js +477 -0
  167. package/src/verax/detect/test-failure-cause-inference.js +314 -0
  168. package/src/verax/detect/ui-feedback-findings.js +207 -0
  169. package/src/verax/detect/view-switch-correlator.js +242 -0
  170. package/src/verax/flow/flow-engine.js +2 -1
  171. package/src/verax/flow/flow-spec.js +0 -6
  172. package/src/verax/index.js +4 -0
  173. package/src/verax/intel/ts-program.js +1 -0
  174. package/src/verax/intel/vue-navigation-extractor.js +3 -0
  175. package/src/verax/learn/action-contract-extractor.js +3 -0
  176. package/src/verax/learn/ast-contract-extractor.js +1 -1
  177. package/src/verax/learn/flow-extractor.js +1 -0
  178. package/src/verax/learn/project-detector.js +5 -0
  179. package/src/verax/learn/react-router-extractor.js +2 -0
  180. package/src/verax/learn/source-instrumenter.js +1 -0
  181. package/src/verax/learn/state-extractor.js +2 -1
  182. package/src/verax/learn/static-extractor.js +1 -0
  183. package/src/verax/observe/coverage-gaps.js +132 -0
  184. package/src/verax/observe/expectation-handler.js +126 -0
  185. package/src/verax/observe/incremental-skip.js +46 -0
  186. package/src/verax/observe/index.js +51 -155
  187. package/src/verax/observe/interaction-executor.js +192 -0
  188. package/src/verax/observe/interaction-runner.js +782 -513
  189. package/src/verax/observe/network-firewall.js +86 -0
  190. package/src/verax/observe/observation-builder.js +169 -0
  191. package/src/verax/observe/observe-context.js +205 -0
  192. package/src/verax/observe/observe-helpers.js +192 -0
  193. package/src/verax/observe/observe-runner.js +230 -0
  194. package/src/verax/observe/observers/budget-observer.js +185 -0
  195. package/src/verax/observe/observers/console-observer.js +102 -0
  196. package/src/verax/observe/observers/coverage-observer.js +107 -0
  197. package/src/verax/observe/observers/interaction-observer.js +471 -0
  198. package/src/verax/observe/observers/navigation-observer.js +132 -0
  199. package/src/verax/observe/observers/network-observer.js +87 -0
  200. package/src/verax/observe/observers/safety-observer.js +82 -0
  201. package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
  202. package/src/verax/observe/page-traversal.js +138 -0
  203. package/src/verax/observe/snapshot-ops.js +94 -0
  204. package/src/verax/observe/ui-feedback-detector.js +742 -0
  205. package/src/verax/scan-summary-writer.js +2 -0
  206. package/src/verax/shared/artifact-manager.js +25 -5
  207. package/src/verax/shared/caching.js +1 -0
  208. package/src/verax/shared/css-spinner-rules.js +204 -0
  209. package/src/verax/shared/expectation-tracker.js +1 -0
  210. package/src/verax/shared/view-switch-rules.js +208 -0
  211. package/src/verax/shared/zip-artifacts.js +6 -0
  212. package/src/verax/shared/config-loader.js +0 -169
  213. /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
@@ -0,0 +1,420 @@
1
+ /**
2
+ * PHASE 12 — Route Intelligence & Deep Route Detection
3
+ *
4
+ * Unified route model and intelligence layer that:
5
+ * - Defines route taxonomy (static, dynamic, ambiguous)
6
+ * - Correlates navigation promises with routes
7
+ * - Handles dynamic routes honestly
8
+ * - Provides evidence for route-related findings
9
+ */
10
+
11
+ import { normalizeDynamicRoute as _normalizeDynamicRoute, isDynamicPath, normalizeNavigationTarget } from '../shared/dynamic-route-utils.js';
12
+
13
+ /**
14
+ * Route Taxonomy
15
+ */
16
+ export const ROUTE_TYPE = {
17
+ STATIC: 'static',
18
+ DYNAMIC: 'dynamic',
19
+ AMBIGUOUS: 'ambiguous',
20
+ PROGRAMMATIC: 'programmatic'
21
+ };
22
+
23
+ export const ROUTE_STABILITY = {
24
+ STATIC: 'static', // Fully static route
25
+ DYNAMIC: 'dynamic', // Dynamic but can be normalized
26
+ AMBIGUOUS: 'ambiguous' // Cannot be deterministically validated
27
+ };
28
+
29
+ export const ROUTE_SOURCE = {
30
+ FILE_SYSTEM: 'file_system', // Next.js pages/app router
31
+ JSX: 'jsx', // React Router <Route>
32
+ CONFIG: 'config', // Vue Router config
33
+ PROGRAMMATIC: 'programmatic' // navigate(), router.push()
34
+ };
35
+
36
+ /**
37
+ * PHASE 12: Unified Route Model
38
+ *
39
+ * @typedef {Object} RouteModel
40
+ * @property {string} path - Normalized route path
41
+ * @property {string} type - ROUTE_TYPE (static, dynamic, ambiguous, programmatic)
42
+ * @property {string} stability - ROUTE_STABILITY (static, dynamic, ambiguous)
43
+ * @property {string} source - ROUTE_SOURCE (file_system, jsx, config, programmatic)
44
+ * @property {string} framework - Framework identifier
45
+ * @property {string} sourceRef - Source reference (file:line:col)
46
+ * @property {string} file - Source file path
47
+ * @property {number} line - Source line number
48
+ * @property {string} [originalPattern] - Original dynamic pattern (if dynamic)
49
+ * @property {string[]} [parameters] - Dynamic parameters (if dynamic)
50
+ * @property {boolean} [isDynamic] - Whether route is dynamic
51
+ * @property {boolean} [exampleExecution] - Whether example path was generated
52
+ */
53
+
54
+ /**
55
+ * Build unified route model from extracted routes
56
+ *
57
+ * @param {Array} extractedRoutes - Routes from route extractors
58
+ * @returns {Array<RouteModel>} Unified route models
59
+ */
60
+ export function buildRouteModels(extractedRoutes) {
61
+ const routeModels = [];
62
+
63
+ for (const route of extractedRoutes) {
64
+ const path = route.path || '';
65
+ const isDynamicRoute = route.isDynamic || isDynamicPath(path);
66
+
67
+ // Determine route type
68
+ let type = ROUTE_TYPE.STATIC;
69
+ let stability = ROUTE_STABILITY.STATIC;
70
+
71
+ if (isDynamicRoute) {
72
+ type = ROUTE_TYPE.DYNAMIC;
73
+
74
+ // Check if dynamic route can be normalized
75
+ if (route.originalPattern || route.exampleExecution) {
76
+ stability = ROUTE_STABILITY.DYNAMIC; // Can be normalized
77
+ } else {
78
+ stability = ROUTE_STABILITY.AMBIGUOUS; // Cannot be deterministically validated
79
+ }
80
+ }
81
+
82
+ // Determine source
83
+ let source = ROUTE_SOURCE.PROGRAMMATIC;
84
+ if (route.framework === 'next-pages' || route.framework === 'next-app') {
85
+ source = ROUTE_SOURCE.FILE_SYSTEM;
86
+ } else if (route.framework === 'react-router') {
87
+ source = ROUTE_SOURCE.JSX;
88
+ } else if (route.framework === 'vue-router') {
89
+ source = ROUTE_SOURCE.CONFIG;
90
+ }
91
+
92
+ const routeModel = {
93
+ path,
94
+ type,
95
+ stability,
96
+ source,
97
+ framework: route.framework || 'unknown',
98
+ sourceRef: route.sourceRef || `${route.file || 'unknown'}:${route.line || 1}`,
99
+ file: route.file || null,
100
+ line: route.line || null,
101
+ };
102
+
103
+ // Add dynamic route metadata
104
+ if (isDynamicRoute) {
105
+ routeModel.originalPattern = route.originalPattern || path;
106
+ routeModel.isDynamic = true;
107
+ routeModel.exampleExecution = route.exampleExecution || false;
108
+ routeModel.parameters = route.parameters || extractParameters(path);
109
+ }
110
+
111
+ routeModels.push(routeModel);
112
+ }
113
+
114
+ return routeModels;
115
+ }
116
+
117
+ /**
118
+ * Extract parameter names from dynamic route path
119
+ */
120
+ function extractParameters(path) {
121
+ const parameters = [];
122
+
123
+ // React/Vue Router :param
124
+ const reactMatches = path.matchAll(/:(\w+)/g);
125
+ for (const match of reactMatches) {
126
+ parameters.push(match[1]);
127
+ }
128
+
129
+ // Next.js [param]
130
+ const nextMatches = path.matchAll(/\[(\w+)\]/g);
131
+ for (const match of nextMatches) {
132
+ parameters.push(match[1]);
133
+ }
134
+
135
+ return parameters;
136
+ }
137
+
138
+ /**
139
+ * PHASE 12: Correlate navigation promise with route
140
+ *
141
+ * @param {string} navigationTarget - Navigation target from promise
142
+ * @param {Array<RouteModel>} routeModels - Available route models
143
+ * @returns {Object|null} Correlation result or null
144
+ */
145
+ export function correlateNavigationWithRoute(navigationTarget, routeModels) {
146
+ if (!navigationTarget || typeof navigationTarget !== 'string') {
147
+ return null;
148
+ }
149
+
150
+ // Normalize navigation target (handle dynamic targets)
151
+ const normalized = normalizeNavigationTarget(navigationTarget);
152
+ const targetToMatch = normalized.exampleTarget || navigationTarget;
153
+
154
+ // Try exact match first
155
+ let matchedRoute = routeModels.find(r => r.path === targetToMatch);
156
+
157
+ if (matchedRoute) {
158
+ return {
159
+ route: matchedRoute,
160
+ matchType: 'exact',
161
+ confidence: 1.0,
162
+ normalizedTarget: targetToMatch,
163
+ originalTarget: navigationTarget,
164
+ isDynamic: normalized.isDynamic,
165
+ };
166
+ }
167
+
168
+ // Try prefix match for dynamic routes
169
+ for (const route of routeModels) {
170
+ if (route.isDynamic && route.originalPattern) {
171
+ // Check if target matches dynamic pattern
172
+ const patternMatch = matchDynamicPattern(targetToMatch, route.originalPattern);
173
+ if (patternMatch) {
174
+ return {
175
+ route: route,
176
+ matchType: 'pattern',
177
+ confidence: 0.9,
178
+ normalizedTarget: targetToMatch,
179
+ originalTarget: navigationTarget,
180
+ isDynamic: true,
181
+ patternMatch: patternMatch,
182
+ };
183
+ }
184
+ }
185
+ }
186
+
187
+ // No match found
188
+ return null;
189
+ }
190
+
191
+ /**
192
+ * Match a target path against a dynamic route pattern
193
+ */
194
+ function matchDynamicPattern(target, pattern) {
195
+ // Convert pattern to regex
196
+ // React Router: /users/:id -> /users/(\w+)
197
+ // Next.js: /users/[id] -> /users/(\w+)
198
+
199
+ let regexPattern = pattern;
200
+
201
+ // Replace :param with (\w+)
202
+ regexPattern = regexPattern.replace(/:(\w+)/g, '(\\w+)');
203
+
204
+ // Replace [param] with (\w+)
205
+ regexPattern = regexPattern.replace(/\[(\w+)\]/g, '(\\w+)');
206
+
207
+ // Escape other special characters
208
+ regexPattern = regexPattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
209
+
210
+ // Restore the capture groups
211
+ regexPattern = regexPattern.replace(/\\\(\\\\w\+\\\)/g, '(\\w+)');
212
+
213
+ const regex = new RegExp(`^${regexPattern}$`);
214
+ const match = target.match(regex);
215
+
216
+ return match ? { matched: true, groups: match.slice(1) } : null;
217
+ }
218
+
219
+ /**
220
+ * PHASE 12: Evaluate route navigation outcome
221
+ *
222
+ * @param {Object} correlation - Route correlation result
223
+ * @param {Object} trace - Interaction trace
224
+ * @param {string} beforeUrl - URL before interaction
225
+ * @param {string} afterUrl - URL after interaction
226
+ * @returns {Object} Evaluation result
227
+ */
228
+ export function evaluateRouteNavigation(correlation, trace, beforeUrl, afterUrl) {
229
+ if (!correlation) {
230
+ return {
231
+ outcome: 'NO_ROUTE_MATCH',
232
+ confidence: 0.0,
233
+ reason: 'Navigation target does not match any known route',
234
+ };
235
+ }
236
+
237
+ const { route, normalizedTarget } = correlation;
238
+ const sensors = trace.sensors || {};
239
+ const navSensor = sensors.navigation || {};
240
+
241
+ // Check URL change
242
+ const urlChanged = navSensor.urlChanged === true ||
243
+ (beforeUrl && afterUrl && beforeUrl !== afterUrl);
244
+
245
+ // Check if URL matches route
246
+ const afterPath = extractPathFromUrl(afterUrl);
247
+ const routeMatched = afterPath === route.path ||
248
+ afterPath === normalizedTarget ||
249
+ (route.isDynamic && matchDynamicPattern(afterPath, route.originalPattern));
250
+
251
+ // Check router state change (for SPAs)
252
+ const routerStateChanged = navSensor.routerStateChanged === true ||
253
+ navSensor.routeChanged === true ||
254
+ (navSensor.routerEvents && navSensor.routerEvents.length > 0);
255
+
256
+ // Check UI change
257
+ const uiChanged = sensors.uiSignals?.diff?.changed === true;
258
+ const domChanged = trace.after?.dom !== trace.before?.dom;
259
+
260
+ // PHASE 12: Evidence Law - require before/after + supporting signal
261
+ const hasEvidence = (beforeUrl && afterUrl) &&
262
+ (urlChanged || routerStateChanged || uiChanged || domChanged);
263
+
264
+ if (routeMatched && hasEvidence) {
265
+ return {
266
+ outcome: 'VERIFIED',
267
+ confidence: route.stability === ROUTE_STABILITY.STATIC ? 1.0 : 0.9,
268
+ reason: null,
269
+ evidence: {
270
+ urlChanged,
271
+ routerStateChanged,
272
+ uiChanged,
273
+ domChanged,
274
+ routeMatched: true,
275
+ },
276
+ };
277
+ }
278
+
279
+ if (!routeMatched && urlChanged) {
280
+ return {
281
+ outcome: 'ROUTE_MISMATCH',
282
+ confidence: 0.8,
283
+ reason: 'Navigation occurred but target route does not match',
284
+ evidence: {
285
+ urlChanged: true,
286
+ expectedRoute: route.path,
287
+ actualPath: afterPath,
288
+ },
289
+ };
290
+ }
291
+
292
+ if (!hasEvidence) {
293
+ return {
294
+ outcome: 'SILENT_FAILURE',
295
+ confidence: correlation.confidence,
296
+ reason: 'Navigation promise not fulfilled - no URL change, router state change, or UI change',
297
+ evidence: {
298
+ urlChanged: false,
299
+ routerStateChanged: false,
300
+ uiChanged: false,
301
+ domChanged: false,
302
+ },
303
+ };
304
+ }
305
+
306
+ // Ambiguous case
307
+ if (route.stability === ROUTE_STABILITY.AMBIGUOUS) {
308
+ return {
309
+ outcome: 'SUSPECTED',
310
+ confidence: 0.6,
311
+ reason: 'Dynamic route cannot be deterministically validated',
312
+ evidence: {
313
+ routeStability: 'ambiguous',
314
+ urlChanged,
315
+ routerStateChanged,
316
+ uiChanged,
317
+ },
318
+ };
319
+ }
320
+
321
+ return {
322
+ outcome: 'UNKNOWN',
323
+ confidence: 0.5,
324
+ reason: 'Route navigation outcome unclear',
325
+ };
326
+ }
327
+
328
+ /**
329
+ * Extract path from URL
330
+ */
331
+ function extractPathFromUrl(url) {
332
+ if (!url || typeof url !== 'string') return '';
333
+
334
+ try {
335
+ const urlObj = new URL(url);
336
+ return urlObj.pathname;
337
+ } catch {
338
+ // Relative URL
339
+ const pathMatch = url.match(/^([^?#]+)/);
340
+ return pathMatch ? pathMatch[1] : url;
341
+ }
342
+ }
343
+
344
+ /**
345
+ * PHASE 12: Build evidence for route-related finding
346
+ *
347
+ * @param {Object} correlation - Route correlation
348
+ * @param {Object} navigationPromise - Navigation promise from expectation
349
+ * @param {Object} evaluation - Route evaluation result
350
+ * @param {Object} trace - Interaction trace
351
+ * @returns {Object} Evidence object
352
+ */
353
+ export function buildRouteEvidence(correlation, navigationPromise, evaluation, trace) {
354
+ const evidence = {
355
+ routeDefinition: {
356
+ path: correlation?.route?.path || null,
357
+ type: correlation?.route?.type || null,
358
+ stability: correlation?.route?.stability || null,
359
+ source: correlation?.route?.source || null,
360
+ sourceRef: correlation?.route?.sourceRef || null,
361
+ },
362
+ navigationTrigger: {
363
+ target: navigationPromise?.target || null,
364
+ method: navigationPromise?.method || null,
365
+ astSource: navigationPromise?.astSource || null,
366
+ context: navigationPromise?.context || null,
367
+ },
368
+ beforeAfter: {
369
+ beforeUrl: trace.before?.url || null,
370
+ afterUrl: trace.after?.url || null,
371
+ beforeScreenshot: trace.before?.screenshot || null,
372
+ afterScreenshot: trace.after?.screenshot || null,
373
+ },
374
+ signals: {
375
+ urlChanged: evaluation.evidence?.urlChanged || false,
376
+ routerStateChanged: evaluation.evidence?.routerStateChanged || false,
377
+ uiChanged: evaluation.evidence?.uiChanged || false,
378
+ domChanged: evaluation.evidence?.domChanged || false,
379
+ routeMatched: evaluation.evidence?.routeMatched || false,
380
+ },
381
+ evaluation: {
382
+ outcome: evaluation.outcome,
383
+ confidence: evaluation.confidence,
384
+ reason: evaluation.reason,
385
+ },
386
+ };
387
+
388
+ return evidence;
389
+ }
390
+
391
+ /**
392
+ * PHASE 12: Check if route change is false positive
393
+ *
394
+ * @param {Object} trace - Interaction trace
395
+ * @param {Object} _correlation - Route correlation
396
+ * @ts-expect-error - JSDoc param documented but unused
397
+ * @returns {boolean} True if should be ignored (false positive)
398
+ */
399
+ export function isRouteChangeFalsePositive(trace, _correlation) {
400
+ const sensors = trace.sensors || {};
401
+ const navSensor = sensors.navigation || {};
402
+
403
+ // Internal state-only route changes (shallow routing)
404
+ if (navSensor.shallowRouting === true && !navSensor.urlChanged) {
405
+ return true;
406
+ }
407
+
408
+ // Analytics-only navigation
409
+ const networkSensor = sensors.network || {};
410
+ const hasAnalyticsRequest = networkSensor.observedRequestUrls?.some(url =>
411
+ url && typeof url === 'string' && url.includes('/api/analytics')
412
+ );
413
+
414
+ if (hasAnalyticsRequest && !navSensor.urlChanged && !sensors.uiSignals?.diff?.changed) {
415
+ return true;
416
+ }
417
+
418
+ return false;
419
+ }
420
+
@@ -20,6 +20,7 @@ const __dirname = dirname(__filename);
20
20
  export function getVeraxVersion() {
21
21
  try {
22
22
  const packagePath = join(__dirname, '..', '..', '..', 'package.json');
23
+ // @ts-expect-error - readFileSync with encoding returns string
23
24
  const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
24
25
  return pkg.version || '0.1.0';
25
26
  } catch (error) {
@@ -35,7 +36,7 @@ export function getVeraxVersion() {
35
36
  *
36
37
  * @param {Object} params - Run parameters
37
38
  * @param {string} params.url - Target URL
38
- * @param {Object} params.safetyFlags - Safety flags (allowWrites, allowRiskyActions, allowCrossOrigin)
39
+ * @param {Object} params.safetyFlags - Safety flags (allowRiskyActions, allowCrossOrigin)
39
40
  * @param {string} params.baseOrigin - Base origin
40
41
  * @param {Object} params.scanBudget - Scan budget configuration
41
42
  * @param {string} params.manifestPath - Optional manifest path
@@ -44,11 +45,11 @@ export function getVeraxVersion() {
44
45
  export function generateRunId(params) {
45
46
  const { url, safetyFlags = {}, baseOrigin, scanBudget, manifestPath = null } = params;
46
47
 
47
- // Sort flags deterministically
48
+ // Sort flags deterministically (allowWrites permanently false - constitutional)
48
49
  const sortedFlags = {
49
50
  allowCrossOrigin: safetyFlags.allowCrossOrigin || false,
50
51
  allowRiskyActions: safetyFlags.allowRiskyActions || false,
51
- allowWrites: safetyFlags.allowWrites || false
52
+ allowWrites: false // CONSTITUTIONAL: Always false (read-only mode)
52
53
  };
53
54
 
54
55
  // Create deterministic representation
@@ -81,6 +82,7 @@ export function generateRunId(params) {
81
82
  const hash = createHash('sha256').update(configString).digest('hex');
82
83
 
83
84
  // Return first 16 chars for readability (still collision-resistant)
85
+ // @ts-expect-error - digest returns string
84
86
  return hash.substring(0, 16);
85
87
  }
86
88
 
@@ -146,6 +148,7 @@ export function getExpectedArtifacts(projectDir, runId) {
146
148
  export function computeFileHash(filePath) {
147
149
  try {
148
150
  const content = readFileSync(filePath);
151
+ // @ts-expect-error - digest returns string
149
152
  return createHash('sha256').update(content).digest('hex');
150
153
  } catch (error) {
151
154
  return null;
@@ -34,13 +34,13 @@ export function createRunManifest(projectDir, runId, params) {
34
34
  url,
35
35
  baseOrigin,
36
36
  flags: {
37
- allowWrites: safetyFlags.allowWrites || false,
37
+ allowWrites: false, // CONSTITUTIONAL: Always false (read-only mode)
38
38
  allowRiskyActions: safetyFlags.allowRiskyActions || false,
39
39
  allowCrossOrigin: safetyFlags.allowCrossOrigin || false
40
40
  },
41
41
  safeMode: {
42
- enabled: !safetyFlags.allowWrites || !safetyFlags.allowRiskyActions || !safetyFlags.allowCrossOrigin,
43
- writesBlocked: !safetyFlags.allowWrites,
42
+ enabled: true, // Always enabled (constitutional enforcement)
43
+ writesBlocked: true, // CONSTITUTIONAL: Always blocked
44
44
  riskyActionsBlocked: !safetyFlags.allowRiskyActions,
45
45
  crossOriginBlocked: !safetyFlags.allowCrossOrigin
46
46
  },
@@ -89,6 +89,7 @@ export function updateRunManifestHashes(projectDir, runId, hashes) {
89
89
  const runManifestPath = `${runDir}/run-manifest.json`;
90
90
 
91
91
  try {
92
+ // @ts-expect-error - readFileSync with encoding returns string
92
93
  const runManifest = JSON.parse(readFileSync(runManifestPath, 'utf-8'));
93
94
  runManifest.artifactHashes = hashes;
94
95
  runManifest.endTime = new Date().toISOString();