@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,625 @@
1
+ /**
2
+ * PHASE 21.4 — Confidence Engine (Policy-Driven with Truth Locks)
3
+ *
4
+ * Central confidence engine that computes confidence scores using policies.
5
+ * Truth locks are enforced and cannot be configured away.
6
+ */
7
+
8
+ import { computeConfidence as computeConfidenceLegacy } from '../detect/confidence-engine.js';
9
+ import { loadConfidencePolicy, getPolicyReport } from './confidence/confidence.loader.js';
10
+
11
+ // Re-export constants for backward compatibility
12
+ export const CONFIDENCE_LEVEL = {
13
+ HIGH: 'HIGH',
14
+ MEDIUM: 'MEDIUM',
15
+ LOW: 'LOW',
16
+ UNPROVEN: 'UNPROVEN',
17
+ };
18
+
19
+ export const CONFIDENCE_REASON = {
20
+ // Promise Strength (A)
21
+ PROMISE_AST_BASED: 'PROMISE_AST_BASED',
22
+ PROMISE_PROVEN: 'PROMISE_PROVEN',
23
+ PROMISE_OBSERVED: 'PROMISE_OBSERVED',
24
+ PROMISE_WEAK: 'PROMISE_WEAK',
25
+ PROMISE_UNKNOWN: 'PROMISE_UNKNOWN',
26
+
27
+ // Observation Strength (B)
28
+ OBS_URL_CHANGED: 'OBS_URL_CHANGED',
29
+ OBS_DOM_CHANGED: 'OBS_DOM_CHANGED',
30
+ OBS_UI_FEEDBACK_CONFIRMED: 'OBS_UI_FEEDBACK_CONFIRMED',
31
+ OBS_CONSOLE_ERRORS: 'OBS_CONSOLE_ERRORS',
32
+ OBS_NETWORK_FAILURE: 'OBS_NETWORK_FAILURE',
33
+ OBS_NETWORK_SUCCESS: 'OBS_NETWORK_SUCCESS',
34
+ OBS_NO_SIGNALS: 'OBS_NO_SIGNALS',
35
+
36
+ // Correlation Quality (C)
37
+ CORR_TIMING_ALIGNED: 'CORR_TIMING_ALIGNED',
38
+ CORR_ROUTE_MATCHED: 'CORR_ROUTE_MATCHED',
39
+ CORR_REQUEST_MATCHED: 'CORR_REQUEST_MATCHED',
40
+ CORR_TRACE_LINKED: 'CORR_TRACE_LINKED',
41
+ CORR_WEAK_CORRELATION: 'CORR_WEAK_CORRELATION',
42
+
43
+ // Guardrails & Contradictions (D)
44
+ GUARD_ANALYTICS_FILTERED: 'GUARD_ANALYTICS_FILTERED',
45
+ GUARD_SHALLOW_ROUTING: 'GUARD_SHALLOW_ROUTING',
46
+ GUARD_NETWORK_SUCCESS_NO_UI: 'GUARD_NETWORK_SUCCESS_NO_UI',
47
+ GUARD_UI_FEEDBACK_PRESENT: 'GUARD_UI_FEEDBACK_PRESENT',
48
+ GUARD_CONTRADICTION_DETECTED: 'GUARD_CONTRADICTION_DETECTED',
49
+
50
+ // Evidence Completeness (E)
51
+ EVIDENCE_SCREENSHOTS: 'EVIDENCE_SCREENSHOTS',
52
+ EVIDENCE_TRACES: 'EVIDENCE_TRACES',
53
+ EVIDENCE_SIGNALS: 'EVIDENCE_SIGNALS',
54
+ EVIDENCE_SNIPPETS: 'EVIDENCE_SNIPPETS',
55
+ EVIDENCE_INCOMPLETE: 'EVIDENCE_INCOMPLETE',
56
+
57
+ // Sensor Presence
58
+ SENSOR_NETWORK_PRESENT: 'SENSOR_NETWORK_PRESENT',
59
+ SENSOR_CONSOLE_PRESENT: 'SENSOR_CONSOLE_PRESENT',
60
+ SENSOR_UI_PRESENT: 'SENSOR_UI_PRESENT',
61
+ SENSOR_UI_FEEDBACK_PRESENT: 'SENSOR_UI_FEEDBACK_PRESENT',
62
+ SENSOR_MISSING: 'SENSOR_MISSING',
63
+ };
64
+
65
+ // === PHASE 26: CONFIDENCE ENGINE SIMPLIFICATION ===
66
+ // 7 Core Buckets for scoring (Damage Control)
67
+ export const CONFIDENCE_BUCKET = {
68
+ CRITICAL_EVIDENCE: 'critical_evidence',
69
+ MULTI_SOURCE: 'multi_source_corroboration',
70
+ ASSET_CRITICAL: 'asset_criticality',
71
+ ABUSE_KNOWN: 'known_abuse_indicators',
72
+ EXPLOITABLE: 'exploitability_indicator',
73
+ PRIV_ESCALATION: 'privilege_escalation_path',
74
+ IMPACT_RADIUS: 'impact_radius',
75
+ };
76
+
77
+ const CORE_BUCKETS = new Set(Object.values(CONFIDENCE_BUCKET));
78
+
79
+ /**
80
+ * Classify confidence reason codes into buckets.
81
+ * Only CORE_BUCKETS contribute to score01.
82
+ * All other reasons become ADVISORY (tracked, not scored).
83
+ */
84
+ const CORE_BUCKET_CLASSIFICATION = {
85
+ // === CRITICAL_EVIDENCE (Reason codes that prove the finding) ===
86
+ [CONFIDENCE_REASON.PROMISE_PROVEN]: CONFIDENCE_BUCKET.CRITICAL_EVIDENCE,
87
+ [CONFIDENCE_REASON.OBS_UI_FEEDBACK_CONFIRMED]: CONFIDENCE_BUCKET.CRITICAL_EVIDENCE,
88
+ [CONFIDENCE_REASON.OBS_NETWORK_FAILURE]: CONFIDENCE_BUCKET.CRITICAL_EVIDENCE,
89
+
90
+ // === MULTI_SOURCE (Multiple independent signals align) ===
91
+ [CONFIDENCE_REASON.CORR_TIMING_ALIGNED]: CONFIDENCE_BUCKET.MULTI_SOURCE,
92
+ [CONFIDENCE_REASON.CORR_ROUTE_MATCHED]: CONFIDENCE_BUCKET.MULTI_SOURCE,
93
+ [CONFIDENCE_REASON.CORR_REQUEST_MATCHED]: CONFIDENCE_BUCKET.MULTI_SOURCE,
94
+
95
+ // === ASSET_CRITICAL (Critical asset/flow) ===
96
+ [CONFIDENCE_REASON.GUARD_SHALLOW_ROUTING]: CONFIDENCE_BUCKET.ASSET_CRITICAL,
97
+ [CONFIDENCE_REASON.EVIDENCE_TRACES]: CONFIDENCE_BUCKET.ASSET_CRITICAL,
98
+
99
+ // === ABUSE_KNOWN (Known abuse patterns) ===
100
+ [CONFIDENCE_REASON.GUARD_ANALYTICS_FILTERED]: CONFIDENCE_BUCKET.ABUSE_KNOWN,
101
+ [CONFIDENCE_REASON.OBS_NETWORK_SUCCESS]: CONFIDENCE_BUCKET.ABUSE_KNOWN,
102
+
103
+ // === EXPLOITABLE (Finding is actionable) ===
104
+ [CONFIDENCE_REASON.OBS_DOM_CHANGED]: CONFIDENCE_BUCKET.EXPLOITABLE,
105
+ [CONFIDENCE_REASON.OBS_CONSOLE_ERRORS]: CONFIDENCE_BUCKET.EXPLOITABLE,
106
+
107
+ // === PRIV_ESCALATION (Access/privilege changes) ===
108
+ [CONFIDENCE_REASON.OBS_URL_CHANGED]: CONFIDENCE_BUCKET.PRIV_ESCALATION,
109
+ [CONFIDENCE_REASON.CORR_TRACE_LINKED]: CONFIDENCE_BUCKET.PRIV_ESCALATION,
110
+
111
+ // === IMPACT_RADIUS (Scope/impact evidence) ===
112
+ [CONFIDENCE_REASON.EVIDENCE_SCREENSHOTS]: CONFIDENCE_BUCKET.IMPACT_RADIUS,
113
+ [CONFIDENCE_REASON.EVIDENCE_SNIPPETS]: CONFIDENCE_BUCKET.IMPACT_RADIUS,
114
+
115
+ // === ADVISORY (Non-core signals, tracked but not scored) ===
116
+ [CONFIDENCE_REASON.PROMISE_OBSERVED]: null,
117
+ [CONFIDENCE_REASON.PROMISE_WEAK]: null,
118
+ [CONFIDENCE_REASON.PROMISE_UNKNOWN]: null,
119
+ [CONFIDENCE_REASON.PROMISE_AST_BASED]: null,
120
+ [CONFIDENCE_REASON.OBS_NO_SIGNALS]: null,
121
+ [CONFIDENCE_REASON.CORR_WEAK_CORRELATION]: null,
122
+ [CONFIDENCE_REASON.GUARD_UI_FEEDBACK_PRESENT]: null,
123
+ [CONFIDENCE_REASON.GUARD_CONTRADICTION_DETECTED]: null,
124
+ [CONFIDENCE_REASON.GUARD_NETWORK_SUCCESS_NO_UI]: null,
125
+ [CONFIDENCE_REASON.EVIDENCE_SIGNALS]: null,
126
+ [CONFIDENCE_REASON.EVIDENCE_INCOMPLETE]: null,
127
+ [CONFIDENCE_REASON.SENSOR_NETWORK_PRESENT]: null,
128
+ [CONFIDENCE_REASON.SENSOR_CONSOLE_PRESENT]: null,
129
+ [CONFIDENCE_REASON.SENSOR_UI_PRESENT]: null,
130
+ [CONFIDENCE_REASON.SENSOR_UI_FEEDBACK_PRESENT]: null,
131
+ [CONFIDENCE_REASON.SENSOR_MISSING]: null,
132
+ };
133
+
134
+ /**
135
+ * Get bucket for a confidence reason. Returns null if advisory.
136
+ */
137
+ function getReasonBucket(reasonCode) {
138
+ return CORE_BUCKET_CLASSIFICATION[reasonCode] || null;
139
+ }
140
+
141
+ /**
142
+ * Filter reasons to only core bucket reasons.
143
+ */
144
+ function filterCoreReasons(reasons) {
145
+ return reasons.filter(reason => {
146
+ const bucket = getReasonBucket(reason);
147
+ return bucket !== null && CORE_BUCKETS.has(bucket);
148
+ });
149
+ }
150
+
151
+ /**
152
+ * Extract advisory reasons (non-core).
153
+ */
154
+ function extractAdvisoryReasons(reasons) {
155
+ return reasons.filter(reason => {
156
+ const bucket = getReasonBucket(reason);
157
+ return bucket === null;
158
+ });
159
+ }
160
+
161
+ // Global policy cache
162
+ let cachedPolicy = null;
163
+
164
+ /**
165
+ * Get confidence policy (cached)
166
+ */
167
+ function getConfidencePolicy(policyPath = null, projectDir = null) {
168
+ if (!cachedPolicy) {
169
+ cachedPolicy = loadConfidencePolicy(policyPath, projectDir);
170
+ }
171
+ return cachedPolicy;
172
+ }
173
+
174
+ /**
175
+ * PHASE 21.4: Compute unified confidence using policy
176
+ * PHASE 26: Bucket-gated scoring (Confidence Simplification Contract v1)
177
+ *
178
+ * Note: For backward compatibility, ALL reasons are returned in `reasons` array.
179
+ * Use `advisoryReasons` to identify which ones didn't contribute to score01.
180
+ *
181
+ * @param {Object} params - Confidence computation parameters
182
+ * @param {string} params.findingType - Type of finding
183
+ * @param {Object} params.expectation - Promise/expectation
184
+ * @param {Object} params.sensors - Sensor data
185
+ * @param {Object} params.comparisons - Comparison data
186
+ * @param {Object} params.evidence - Evidence data (optional)
187
+ * @param {Object} params.options - Options { policyPath, projectDir, determinismVerdict, evidencePackage }
188
+ * @returns {Object} { score, level, reasons[], advisoryReasons[], policyReport }
189
+ */
190
+ export function computeUnifiedConfidence({ findingType, expectation, sensors = {}, comparisons = {}, evidence = {}, options = {} }) {
191
+ // Backward compatibility: options is optional
192
+ const policy = getConfidencePolicy(options?.policyPath, options?.projectDir);
193
+ const reasons = [];
194
+
195
+ // === PILLAR A: Promise Strength ===
196
+ const promiseStrength = assessPromiseStrength(expectation, reasons, policy);
197
+
198
+ // === PILLAR B: Observation Strength ===
199
+ const observationStrength = assessObservationStrength(sensors, comparisons, reasons, policy);
200
+
201
+ // === PILLAR C: Correlation Quality ===
202
+ const correlationQuality = assessCorrelationQuality(expectation, sensors, comparisons, evidence, reasons, policy);
203
+
204
+ // === PILLAR D: Guardrails & Contradictions ===
205
+ const guardrails = assessGuardrails(sensors, comparisons, findingType, reasons, policy);
206
+
207
+ // === PILLAR E: Evidence Completeness ===
208
+ const evidenceCompleteness = assessEvidenceCompleteness(evidence, sensors, reasons, policy);
209
+
210
+ // === PHASE 26: Identify core vs advisory reasons ===
211
+ const coreReasons = filterCoreReasons(reasons);
212
+ const advisoryReasons = extractAdvisoryReasons(reasons);
213
+
214
+ // === COMPUTE BASE SCORE using policy weights ===
215
+ const baseScore = (
216
+ promiseStrength * policy.weights.promiseStrength +
217
+ observationStrength * policy.weights.observationStrength +
218
+ correlationQuality * policy.weights.correlationQuality +
219
+ guardrails * policy.weights.guardrails +
220
+ evidenceCompleteness * policy.weights.evidenceCompleteness
221
+ );
222
+
223
+ // === APPLY MISSING SENSORS PENALTY ===
224
+ // Legacy rule: -15 penalty when any core sensor object is missing (network, console, uiSignals)
225
+ const requiredSensors = ['network', 'console', 'uiSignals'];
226
+ const missingSensors = requiredSensors.filter(key => sensors[key] === undefined);
227
+ // Increase penalty so missing core sensors are clearly visible in score100
228
+ const missingSensorsPenalty = missingSensors.length > 0 ? 0.25 : 0; // -25 in 0..1 scale
229
+ if (missingSensorsPenalty > 0) {
230
+ reasons.push(CONFIDENCE_REASON.SENSOR_MISSING);
231
+ }
232
+
233
+ // === APPLY CONTRADICTIONS using policy ===
234
+ // Apply contradiction penalty only when guardrails are materially degraded
235
+ const contradictionPenalty = guardrails < 0.5 ? policy.truthLocks.contradictionPenalty : 0;
236
+ let finalScore = Math.max(0, Math.min(1, baseScore - contradictionPenalty - missingSensorsPenalty));
237
+
238
+ // === TRUTH LOCKS: Apply determinism cap ===
239
+ if (options?.determinismVerdict === 'NON_DETERMINISTIC') {
240
+ const maxConfidence = policy.truthLocks.nonDeterministicMaxConfidence;
241
+ if (finalScore > maxConfidence) {
242
+ coreReasons.push('TRUTH_LOCK_NON_DETERMINISTIC_CAP');
243
+ finalScore = Math.min(finalScore, maxConfidence);
244
+ }
245
+ }
246
+
247
+ // === TRUTH LOCKS: Evidence Law cap ===
248
+ const evidencePackage = options?.evidencePackage || evidence?.evidencePackage || {};
249
+ if (evidencePackage?.severity === 'CONFIRMED' || evidencePackage?.status === 'CONFIRMED') {
250
+ if (policy.truthLocks.evidenceCompleteRequired && !evidencePackage.isComplete) {
251
+ coreReasons.push('TRUTH_LOCK_EVIDENCE_INCOMPLETE');
252
+ // Force downgrade from CONFIRMED
253
+ finalScore = Math.min(finalScore, 0.6); // Cap at MEDIUM
254
+ }
255
+ }
256
+
257
+ // === DETERMINE LEVEL using Contract v1 thresholds ===
258
+ const level = determineConfidenceLevel(finalScore, promiseStrength, evidenceCompleteness, policy);
259
+
260
+ const policyReport = getPolicyReport(policy);
261
+
262
+ // Contract v1: score01 is canonical, derive score100 and topReasons (core only)
263
+ const topReasons = coreReasons.slice(0, 4);
264
+
265
+ return {
266
+ score: finalScore, // alias for score01 for backward compatibility
267
+ score01: finalScore,
268
+ score100: Math.round(finalScore * 100),
269
+ level,
270
+ reasons: reasons.slice(0, 10), // ALL reasons for backward compat (core + advisory)
271
+ advisoryReasons: advisoryReasons.slice(0, 10), // Advisory reasons (for analyst/context)
272
+ topReasons,
273
+ policyReport: {
274
+ version: policyReport.version,
275
+ source: policyReport.source,
276
+ thresholds: policyReport.thresholds
277
+ }
278
+ };
279
+ }
280
+
281
+ /**
282
+ * Assess promise strength (Pillar A) using policy
283
+ */
284
+ function assessPromiseStrength(expectation, reasons, policy) {
285
+ if (!expectation) {
286
+ reasons.push(CONFIDENCE_REASON.PROMISE_UNKNOWN);
287
+ return 0.0;
288
+ }
289
+
290
+ const isASTBased = expectation.source?.astSource || expectation.metadata?.astSource;
291
+ if (isASTBased) {
292
+ reasons.push(CONFIDENCE_REASON.PROMISE_AST_BASED);
293
+ }
294
+
295
+ const proof = expectation.proof || 'UNKNOWN';
296
+ if (proof === 'PROVEN_EXPECTATION' || proof === 'PROVEN') {
297
+ reasons.push(CONFIDENCE_REASON.PROMISE_PROVEN);
298
+ return policy.baseScores.promiseProven;
299
+ }
300
+
301
+ if (proof === 'OBSERVED' || expectation.confidence >= 0.8) {
302
+ reasons.push(CONFIDENCE_REASON.PROMISE_OBSERVED);
303
+ return policy.baseScores.promiseObserved;
304
+ }
305
+
306
+ if (expectation.confidence >= 0.5) {
307
+ reasons.push(CONFIDENCE_REASON.PROMISE_WEAK);
308
+ return policy.baseScores.promiseWeak;
309
+ }
310
+
311
+ reasons.push(CONFIDENCE_REASON.PROMISE_UNKNOWN);
312
+ return policy.baseScores.promiseUnknown;
313
+ }
314
+
315
+ /**
316
+ * Assess observation strength (Pillar B) using policy
317
+ */
318
+ function assessObservationStrength(sensors, comparisons, reasons, policy) {
319
+ let strength = 0.0;
320
+ let signals = 0;
321
+
322
+ const urlChanged = sensors.navigation?.urlChanged === true || comparisons.urlChanged === true;
323
+ if (urlChanged) {
324
+ reasons.push(CONFIDENCE_REASON.OBS_URL_CHANGED);
325
+ strength += policy.baseScores.urlChanged;
326
+ signals++;
327
+ }
328
+
329
+ const domChanged = sensors.dom?.changed === true || comparisons.domChanged === true;
330
+ if (domChanged) {
331
+ reasons.push(CONFIDENCE_REASON.OBS_DOM_CHANGED);
332
+ strength += policy.baseScores.domChanged;
333
+ signals++;
334
+ }
335
+
336
+ const uiFeedback = sensors.uiFeedback || {};
337
+ const uiFeedbackScore = uiFeedback.overallUiFeedbackScore || 0;
338
+ if (uiFeedbackScore > 0.5) {
339
+ reasons.push(CONFIDENCE_REASON.OBS_UI_FEEDBACK_CONFIRMED);
340
+ strength += policy.baseScores.uiFeedbackConfirmed;
341
+ signals++;
342
+ }
343
+
344
+ const consoleErrors = sensors.console?.errors > 0 || sensors.console?.errorCount > 0;
345
+ if (consoleErrors) {
346
+ reasons.push(CONFIDENCE_REASON.OBS_CONSOLE_ERRORS);
347
+ strength += policy.baseScores.consoleErrors;
348
+ signals++;
349
+ }
350
+
351
+ const networkFailure = sensors.network?.failedRequests > 0 || sensors.network?.topFailedUrls?.length > 0;
352
+ if (networkFailure) {
353
+ reasons.push(CONFIDENCE_REASON.OBS_NETWORK_FAILURE);
354
+ strength += policy.baseScores.networkFailure;
355
+ signals++;
356
+ }
357
+
358
+ const networkSuccess = sensors.network?.successfulRequests > 0 && !networkFailure;
359
+ if (networkSuccess) {
360
+ reasons.push(CONFIDENCE_REASON.OBS_NETWORK_SUCCESS);
361
+ strength += policy.baseScores.networkSuccess;
362
+ signals++;
363
+ }
364
+
365
+ if (signals === 0) {
366
+ reasons.push(CONFIDENCE_REASON.OBS_NO_SIGNALS);
367
+ return 0.0;
368
+ }
369
+
370
+ return Math.min(1.0, strength);
371
+ }
372
+
373
+ /**
374
+ * Assess correlation quality (Pillar C) using policy
375
+ */
376
+ function assessCorrelationQuality(expectation, sensors, comparisons, evidence, reasons, policy) {
377
+ let quality = 0.5;
378
+
379
+ if (evidence.timing || sensors.timing) {
380
+ reasons.push(CONFIDENCE_REASON.CORR_TIMING_ALIGNED);
381
+ quality += policy.baseScores.timingAligned;
382
+ }
383
+
384
+ if (evidence.correlation?.routeMatched === true || evidence.routeDefinition?.path) {
385
+ reasons.push(CONFIDENCE_REASON.CORR_ROUTE_MATCHED);
386
+ quality += policy.baseScores.routeMatched;
387
+ }
388
+
389
+ if (evidence.networkRequest?.matched === true || evidence.correlation?.requestMatched === true) {
390
+ reasons.push(CONFIDENCE_REASON.CORR_REQUEST_MATCHED);
391
+ quality += policy.baseScores.requestMatched;
392
+ }
393
+
394
+ if (evidence.traceId || evidence.source?.file) {
395
+ reasons.push(CONFIDENCE_REASON.CORR_TRACE_LINKED);
396
+ quality += policy.baseScores.traceLinked;
397
+ }
398
+
399
+ if (quality < 0.6) {
400
+ reasons.push(CONFIDENCE_REASON.CORR_WEAK_CORRELATION);
401
+ }
402
+
403
+ return Math.min(1.0, quality);
404
+ }
405
+
406
+ /**
407
+ * Assess guardrails & contradictions (Pillar D) using policy
408
+ */
409
+ function assessGuardrails(sensors, comparisons, findingType, reasons, _policy) {
410
+ let guardrailScore = 1.0;
411
+
412
+ const networkSensor = sensors.network || {};
413
+ const hasAnalytics = networkSensor.observedRequestUrls?.some(url =>
414
+ url && typeof url === 'string' && url.includes('/api/analytics')
415
+ );
416
+ if (hasAnalytics && !sensors.navigation?.urlChanged && !sensors.uiSignals?.diff?.changed) {
417
+ reasons.push(CONFIDENCE_REASON.GUARD_ANALYTICS_FILTERED);
418
+ guardrailScore -= 0.2;
419
+ }
420
+
421
+ if (sensors.navigation?.shallowRouting === true && !sensors.navigation?.urlChanged) {
422
+ reasons.push(CONFIDENCE_REASON.GUARD_SHALLOW_ROUTING);
423
+ guardrailScore -= 0.3;
424
+ }
425
+
426
+ const networkSuccess = networkSensor.successfulRequests > 0;
427
+ const noUIChange = !sensors.uiSignals?.diff?.changed && !sensors.uiFeedback?.overallUiFeedbackScore;
428
+ if (networkSuccess && noUIChange && findingType?.includes('silent_failure')) {
429
+ reasons.push(CONFIDENCE_REASON.GUARD_NETWORK_SUCCESS_NO_UI);
430
+ guardrailScore -= 0.2;
431
+ }
432
+
433
+ const uiFeedbackScore = sensors.uiFeedback?.overallUiFeedbackScore || 0;
434
+ if (uiFeedbackScore > 0.5 && findingType?.includes('silent_failure')) {
435
+ reasons.push(CONFIDENCE_REASON.GUARD_UI_FEEDBACK_PRESENT);
436
+ guardrailScore -= 0.4;
437
+ }
438
+
439
+ if (guardrailScore < 0.6) {
440
+ reasons.push(CONFIDENCE_REASON.GUARD_CONTRADICTION_DETECTED);
441
+ }
442
+
443
+ return Math.max(0.0, guardrailScore);
444
+ }
445
+
446
+ /**
447
+ * Assess evidence completeness (Pillar E) using policy
448
+ */
449
+ function assessEvidenceCompleteness(evidence, sensors, reasons, policy) {
450
+ let completeness = 0.0;
451
+
452
+ if (evidence.beforeAfter?.beforeScreenshot && evidence.beforeAfter?.afterScreenshot) {
453
+ reasons.push(CONFIDENCE_REASON.EVIDENCE_SCREENSHOTS);
454
+ completeness += policy.baseScores.screenshots;
455
+ }
456
+
457
+ if (evidence.traceId || sensors.traceId) {
458
+ reasons.push(CONFIDENCE_REASON.EVIDENCE_TRACES);
459
+ completeness += policy.baseScores.traces;
460
+ }
461
+
462
+ if (evidence.signals && Object.keys(evidence.signals).length > 0) {
463
+ reasons.push(CONFIDENCE_REASON.EVIDENCE_SIGNALS);
464
+ completeness += policy.baseScores.signals;
465
+ }
466
+
467
+ if (evidence.source?.astSource || evidence.navigationTrigger?.astSource) {
468
+ reasons.push(CONFIDENCE_REASON.EVIDENCE_SNIPPETS);
469
+ completeness += policy.baseScores.snippets;
470
+ }
471
+
472
+ if (completeness < 0.5) {
473
+ reasons.push(CONFIDENCE_REASON.EVIDENCE_INCOMPLETE);
474
+ }
475
+
476
+ return completeness;
477
+ }
478
+
479
+ /**
480
+ * Determine confidence level using Contract v1 thresholds
481
+ * HIGH: score01 >= 0.85
482
+ * MEDIUM: 0.60 <= score01 < 0.85
483
+ * UNPROVEN: score01 < 0.60
484
+ */
485
+ function determineConfidenceLevel(score, _promiseStrength, _evidenceCompleteness, _policy) {
486
+ if (score >= 0.85) {
487
+ return CONFIDENCE_LEVEL.HIGH;
488
+ }
489
+
490
+ if (score >= 0.60) {
491
+ return CONFIDENCE_LEVEL.MEDIUM;
492
+ }
493
+
494
+ return CONFIDENCE_LEVEL.UNPROVEN;
495
+ }
496
+
497
+ /**
498
+ * PHASE 21.4: Compute confidence for finding (wrapper with policy support)
499
+ *
500
+ * @param {Object} params - Confidence computation parameters
501
+ * @param {string} params.findingType - Type of finding
502
+ * @param {Object} params.expectation - Promise/expectation
503
+ * @param {Object} params.sensors - Sensor data
504
+ * @param {Object} params.comparisons - Comparison data
505
+ * @param {Object} params.evidence - Evidence data
506
+ * @param {Object} params.options - Options { policyPath, projectDir, determinismVerdict, evidencePackage }
507
+ * @returns {Object} { score, level, reasons[] }
508
+ */
509
+ export function computeConfidenceForFinding(params) {
510
+ // Backward compatibility: options may be in params.options or params directly
511
+ const options = params.options || {};
512
+
513
+ // Use legacy system for base computation
514
+ const legacyParams = {
515
+ findingType: params.findingType,
516
+ expectation: params.expectation,
517
+ sensors: params.sensors,
518
+ comparisons: params.comparisons,
519
+ attemptMeta: params['attemptMeta'] || {},
520
+ executionModeCeiling: options.executionModeCeiling || 1.0
521
+ };
522
+ const legacyResult = computeConfidenceLegacy(legacyParams);
523
+
524
+ // Legacy returns score in 0-100; normalize to 0-1 for unified engine
525
+ let normalizedScore = (legacyResult.score || 0) / 100;
526
+ normalizedScore = Math.max(0, Math.min(1, normalizedScore));
527
+
528
+ // Extract reasons from legacy explain/factors
529
+ const reasons = extractReasonsFromLegacy(legacyResult, params);
530
+
531
+ // Load policy for truth locks
532
+ const policy = getConfidencePolicy(options?.policyPath, options?.projectDir);
533
+
534
+ // === TRUTH LOCKS: Apply determinism cap ===
535
+ if (options?.determinismVerdict === 'NON_DETERMINISTIC') {
536
+ const maxConfidence = policy.truthLocks.nonDeterministicMaxConfidence;
537
+ if (normalizedScore > maxConfidence) {
538
+ reasons.push('TRUTH_LOCK_NON_DETERMINISTIC_CAP');
539
+ normalizedScore = Math.min(normalizedScore, maxConfidence);
540
+ }
541
+ }
542
+
543
+ // === TRUTH LOCKS: Evidence Law cap ===
544
+ const evidencePackage = options?.evidencePackage || (params.evidence && params.evidence.evidencePackage) || {};
545
+ if (evidencePackage?.severity === 'CONFIRMED' || evidencePackage?.status === 'CONFIRMED') {
546
+ if (policy.truthLocks.evidenceCompleteRequired && !evidencePackage.isComplete) {
547
+ reasons.push('TRUTH_LOCK_EVIDENCE_INCOMPLETE');
548
+ normalizedScore = Math.min(normalizedScore, 0.6);
549
+ }
550
+ }
551
+
552
+ // Determine level using policy thresholds
553
+ const level = determineConfidenceLevel(
554
+ normalizedScore,
555
+ assessPromiseStrength(params.expectation, [], policy),
556
+ assessEvidenceCompleteness(params.evidence || {}, params.sensors || {}, [], policy),
557
+ policy
558
+ );
559
+
560
+ // Contract v1: Return score01, score100, level, and topReasons
561
+ const topReasons = reasons.slice(0, 4);
562
+
563
+ return {
564
+ score01: normalizedScore,
565
+ score100: Math.round(normalizedScore * 100),
566
+ score: normalizedScore, // Backward compat - should be 0..1
567
+ level,
568
+ reasons,
569
+ topReasons,
570
+ };
571
+ }
572
+
573
+ /**
574
+ * Extract stable reason codes from legacy confidence result
575
+ */
576
+ function extractReasonsFromLegacy(legacyResult, params) {
577
+ const reasons = [];
578
+
579
+ if (legacyResult.explain && Array.isArray(legacyResult.explain)) {
580
+ for (const explanation of legacyResult.explain) {
581
+ if (typeof explanation === 'string') {
582
+ if (explanation.includes('AST') || explanation.includes('proven')) {
583
+ reasons.push(CONFIDENCE_REASON.PROMISE_PROVEN);
584
+ }
585
+ if (explanation.includes('network') && explanation.includes('failed')) {
586
+ reasons.push(CONFIDENCE_REASON.OBS_NETWORK_FAILURE);
587
+ }
588
+ if (explanation.includes('UI feedback')) {
589
+ reasons.push(CONFIDENCE_REASON.OBS_UI_FEEDBACK_CONFIRMED);
590
+ }
591
+ if (explanation.includes('contradiction')) {
592
+ reasons.push(CONFIDENCE_REASON.GUARD_CONTRADICTION_DETECTED);
593
+ }
594
+ }
595
+ }
596
+ }
597
+
598
+ if (legacyResult.factors && Array.isArray(legacyResult.factors)) {
599
+ for (const factor of legacyResult.factors) {
600
+ if (factor.key === 'promise_strength' && factor.value === 'PROVEN') {
601
+ reasons.push(CONFIDENCE_REASON.PROMISE_PROVEN);
602
+ }
603
+ if (factor.key === 'ui_feedback_score' && parseFloat(factor.value) > 0.5) {
604
+ reasons.push(CONFIDENCE_REASON.OBS_UI_FEEDBACK_CONFIRMED);
605
+ }
606
+ }
607
+ }
608
+
609
+ const sensors = params.sensors || {};
610
+ if (sensors.network && Object.keys(sensors.network).length > 0) {
611
+ reasons.push(CONFIDENCE_REASON.SENSOR_NETWORK_PRESENT);
612
+ }
613
+ if (sensors.console && Object.keys(sensors.console).length > 0) {
614
+ reasons.push(CONFIDENCE_REASON.SENSOR_CONSOLE_PRESENT);
615
+ }
616
+ if (sensors.uiSignals && Object.keys(sensors.uiSignals).length > 0) {
617
+ reasons.push(CONFIDENCE_REASON.SENSOR_UI_PRESENT);
618
+ }
619
+ if (sensors.uiFeedback && Object.keys(sensors.uiFeedback).length > 0) {
620
+ reasons.push(CONFIDENCE_REASON.SENSOR_UI_FEEDBACK_PRESENT);
621
+ }
622
+
623
+ return [...new Set(reasons)];
624
+ }
625
+
@@ -0,0 +1,29 @@
1
+ /**
2
+ * VERAX Core Contracts - Main export
3
+ *
4
+ * Single source of truth for:
5
+ * - Canonical type definitions (Finding, Evidence, Confidence, Observation, Signals)
6
+ * - All enums (CONFIDENCE_LEVEL, FINDING_STATUS, IMPACT, USER_RISK, OWNERSHIP, etc.)
7
+ * - Runtime validators that enforce contracts
8
+ * - Evidence Law enforcement (findings without evidence cannot be CONFIRMED)
9
+ */
10
+
11
+ export {
12
+ CONFIDENCE_LEVEL,
13
+ FINDING_STATUS,
14
+ FINDING_TYPE,
15
+ IMPACT,
16
+ USER_RISK,
17
+ OWNERSHIP,
18
+ EVIDENCE_TYPE,
19
+ ALL_ENUMS
20
+ } from './types.js';
21
+
22
+ export {
23
+ validateFinding,
24
+ validateEvidence,
25
+ validateConfidence,
26
+ validateSignals,
27
+ isEvidenceSubstantive,
28
+ enforceContractsOnFindings
29
+ } from './validators.js';