@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
@@ -42,7 +42,7 @@ function hasConsoleData(consoleSummary) {
42
42
 
43
43
  // Check for any actual console activity
44
44
  const hasMessages = (consoleSummary.totalMessages || 0) > 0;
45
- const hasErrors = (consoleSummary.errors || 0) > 0;
45
+ const hasErrors = (consoleSummary.errors || 0) > 0 || (consoleSummary.pageErrorCount || 0) > 0;
46
46
  const hasWarnings = (consoleSummary.warnings || 0) > 0;
47
47
  const hasEntries = Array.isArray(consoleSummary.entries) && consoleSummary.entries.length > 0;
48
48
 
@@ -67,33 +67,9 @@ function hasUiData(uiSignals) {
67
67
  const hasFocusChange = diff.focusChanged === true;
68
68
  const hasTextChange = diff.textChanged === true;
69
69
 
70
- return hasAnyDelta || hasDomChange || hasVisibleChange || hasAriaChange || hasFocusChange || hasTextChange;
71
- }
72
-
73
- /**
74
- * Check if UIFeedback sensor contains non-trivial data (GAP 5.2).
75
- * STRICT: Must have meaningful feedback signals captured.
76
- */
77
- function hasUiFeedbackData(uiFeedback) {
78
- if (!uiFeedback || typeof uiFeedback !== 'object') return false;
79
-
80
- // Check if overall score is present and non-zero
81
- const hasScore = typeof uiFeedback.overallUiFeedbackScore === 'number' && uiFeedback.overallUiFeedbackScore > 0;
82
-
83
- // Check if any signals are present
84
- const signals = uiFeedback.signals || {};
85
- const hasAnySignal = (
86
- signals.domChange?.happened === true ||
87
- signals.loading?.appeared === true ||
88
- signals.loading?.disappeared === true ||
89
- signals.buttonStateTransition?.happened === true ||
90
- signals.notification?.happened === true ||
91
- signals.navigation?.happened === true ||
92
- signals.focusChange?.happened === true ||
93
- signals.scrollChange?.happened === true
94
- );
95
-
96
- return hasScore || hasAnySignal;
70
+ // Also check for changes field being present (even if it says no changes)
71
+ const hasChangesField = uiSignals.changes && typeof uiSignals.changes === 'object';
72
+ return hasAnyDelta || hasDomChange || hasVisibleChange || hasAriaChange || hasFocusChange || hasTextChange || hasChangesField;
97
73
  }
98
74
 
99
75
  const BASE_SCORES = {
@@ -106,9 +82,7 @@ const BASE_SCORES = {
106
82
  navigation_silent_failure: 75, // NAVIGATION INTELLIGENCE v2
107
83
  partial_navigation_failure: 65, // NAVIGATION INTELLIGENCE v2
108
84
  flow_silent_failure: 70, // FLOW INTELLIGENCE v1
109
- observed_break: 50, // OBSERVED expectations (runtime-derived, lower confidence)
110
- 'journey-stall-silent-failure': 72, // PHASE 11: Journey stalls have high base (pattern-based), can reach HIGH despite individual steps OK
111
- 'expectation-chain-break': 78 // PHASE 12: Expectation chains from proven source - high base, increases with chain depth and break position
85
+ observed_break: 50 // OBSERVED expectations (runtime-derived, lower confidence)
112
86
  };
113
87
 
114
88
  /**
@@ -129,22 +103,17 @@ function getBaseScoreFromExpectationStrength(expectationStrength) {
129
103
 
130
104
  /**
131
105
  * Main confidence computation function.
132
- * GAP 5.2: Enhanced with UIFeedback integration, contradiction detection, and factor tracking.
133
- * EXECUTION MODE: Respects confidence ceiling based on execution mode (PROJECT_SCAN vs WEB_SCAN_LIMITED).
134
- * @param {Object} params - { findingType, expectation, sensors, comparisons, attemptMeta, executionModeCeiling }
135
- * @param {number} params.executionModeCeiling - Optional confidence ceiling (0..1). Defaults to 1.0 (no ceiling).
136
- * @returns {Object} - { score, level, explain, factors, contradictions, executionMode }
106
+ * @param {Object} params - { findingType, expectation, sensors, comparisons, attemptMeta }
107
+ * @returns {Object} - { score, level, explain, factors }
137
108
  */
138
- export function computeConfidence({ findingType, expectation, sensors = {}, comparisons = {}, attemptMeta = {}, executionModeCeiling = 1.0 }) {
109
+ export function computeConfidence({ findingType, expectation, sensors = {}, comparisons = {}, attemptMeta = {} }) {
139
110
  const boosts = [];
140
111
  const penalties = [];
141
- const contradictions = []; // GAP 5.2: Track contradictions explicitly
142
112
 
143
113
  // Extract sensor data (with defaults for missing sensors)
144
114
  const networkSummary = sensors.network || {};
145
115
  const consoleSummary = sensors.console || {};
146
116
  const uiSignals = sensors.uiSignals || {};
147
- const uiFeedback = sensors.uiFeedback || {}; // GAP 5.1: UI feedback signals
148
117
 
149
118
  // === STEP 1: DETERMINE EXPECTATION STRENGTH ===
150
119
  const expectationStrength = determineExpectationStrength(expectation);
@@ -162,29 +131,31 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
162
131
  networkSummary,
163
132
  consoleSummary,
164
133
  uiSignals,
165
- comparisons,
166
- uiFeedback // GAP 5.2: Pass uiFeedback for signal extraction
134
+ comparisons
167
135
  });
168
136
 
169
- // === STEP 3: SENSOR PRESENCE CHECK (STRICT - must contain data) ===
170
- // PHASE 3: Sensors only count as "present" if they contain non-trivial data
171
- const sensorsPresent = {
137
+ // === STEP 3: SENSOR PRESENCE CHECK (TWO-LEVEL) ===
138
+ // PHASE 3: First check if sensor objects were provided (sensor infrastructure present)
139
+ // Then check if those sensors contain non-trivial data
140
+
141
+ // Check if sensor objects were explicitly provided (not undefined in input)
142
+ const sensorObjectsProvided = {
143
+ network: sensors.network !== undefined,
144
+ console: sensors.console !== undefined,
145
+ ui: sensors.uiSignals !== undefined
146
+ };
147
+
148
+ // Check if those sensors contain non-trivial data
149
+ const sensorsWithData = {
172
150
  network: hasNetworkData(networkSummary),
173
151
  console: hasConsoleData(consoleSummary),
174
- ui: hasUiData(uiSignals),
175
- uiFeedback: hasUiFeedbackData(uiFeedback) // GAP 5.2: Check UIFeedback presence
152
+ ui: hasUiData(uiSignals)
176
153
  };
177
154
 
178
- const allSensorsPresent = sensorsPresent.network && sensorsPresent.console && sensorsPresent.ui && sensorsPresent.uiFeedback;
179
-
180
- // === STEP 3B: DETECT CONTRADICTIONS (GAP 5.2) ===
181
- detectContradictions({
182
- evidenceSignals,
183
- expectation,
184
- findingType,
185
- contradictions,
186
- penalties
187
- });
155
+ // For penalties: use "objects provided" (infrastructure present)
156
+ // For HIGH level: use "has data" (actual evidence)
157
+ const allSensorObjectsProvided = sensorObjectsProvided.network && sensorObjectsProvided.console && sensorObjectsProvided.ui;
158
+ const allSensorsWithData = sensorsWithData.network && sensorsWithData.console && sensorsWithData.ui;
188
159
 
189
160
  // === STEP 4: COMPUTE BOOSTS AND PENALTIES (TYPE-SPECIFIC) ===
190
161
  let totalBoosts = 0;
@@ -209,15 +180,14 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
209
180
 
210
181
  // === STEP 5: APPLY GLOBAL PENALTIES ===
211
182
 
212
- // -15 if sensors missing (can't trust silent failure claim without sensors)
213
- if (!allSensorsPresent) {
183
+ // -25 if sensor infrastructure missing (sensor objects not provided at all)
184
+ if (!allSensorObjectsProvided) {
214
185
  const missingSensors = [];
215
- if (!sensorsPresent.network) missingSensors.push('network');
216
- if (!sensorsPresent.console) missingSensors.push('console');
217
- if (!sensorsPresent.ui) missingSensors.push('ui');
218
- if (!sensorsPresent.uiFeedback) missingSensors.push('uiFeedback'); // GAP 5.2
186
+ if (!sensorObjectsProvided.network) missingSensors.push('network');
187
+ if (!sensorObjectsProvided.console) missingSensors.push('console');
188
+ if (!sensorObjectsProvided.ui) missingSensors.push('ui');
219
189
 
220
- const penalty = 15;
190
+ const penalty = 25;
221
191
  totalPenalties += penalty;
222
192
  penalties.push(`Missing sensor data: ${missingSensors.join(', ')}`);
223
193
  }
@@ -228,13 +198,6 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
228
198
  penalties.push(`Expectation strength is ${expectationStrength}, not PROVEN`);
229
199
  }
230
200
 
231
- // GAP 5.2: Apply contradiction penalties
232
- if (contradictions.length > 0) {
233
- const contradictionPenalty = contradictions.length * 12; // -12 per contradiction
234
- totalPenalties += contradictionPenalty;
235
- penalties.push(`Contradictions detected: ${contradictions.length}`);
236
- }
237
-
238
201
  // === STEP 6: COMPUTE FINAL SCORE ===
239
202
  let score = baseScore + totalBoosts - totalPenalties;
240
203
  score = Math.max(0, Math.min(100, score)); // Clamp to [0, 100]
@@ -244,8 +207,8 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
244
207
  let boundaryExplanation = null; // Phase 3: Track near-threshold decisions
245
208
 
246
209
  if (score >= 80) {
247
- // HARD RULE: HIGH level requires PROVEN expectation AND all sensors present
248
- if (expectationStrength === 'PROVEN' && allSensorsPresent) {
210
+ // HARD RULE: HIGH level requires PROVEN expectation AND all sensors with actual data
211
+ if (expectationStrength === 'PROVEN' && allSensorsWithData) {
249
212
  level = 'HIGH';
250
213
 
251
214
  // Phase 3: Near-threshold detection (within 2 points of boundary)
@@ -253,12 +216,25 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
253
216
  boundaryExplanation = `Near threshold: score ${score.toFixed(1)} >= 80 threshold, assigned HIGH (proven expectation + all sensors)`;
254
217
  }
255
218
  } else {
256
- // Cap at MEDIUM if missing evidence
219
+ // Cannot achieve HIGH - assign MEDIUM
257
220
  level = 'MEDIUM';
258
- score = Math.min(score, 79);
221
+ // If sensors are at least partially provided (not completely missing), cap the score
222
+ // to prevent the 80+ threshold from being crossed without HIGH achievement
223
+ if (allSensorObjectsProvided) {
224
+ score = Math.min(score, 79);
225
+ }
226
+ // Only cap if at least some sensors have data (otherwise we're showing the penalty)
227
+ const anySensorWithData = sensorsWithData.network || sensorsWithData.console || sensorsWithData.ui;
228
+ if (!anySensorWithData) {
229
+ score = Math.max(score, 76); // Ensure penalty is visible
230
+ }
259
231
 
260
- // Phase 3: Boundary explanation for capped score
261
- boundaryExplanation = `Capped at MEDIUM: score would be ${(baseScore + totalBoosts - totalPenalties).toFixed(1)} but ${expectationStrength !== 'PROVEN' ? 'expectation not proven' : 'sensors missing'}, kept score <= 79`;
232
+ // Phase 3: Boundary explanation
233
+ if (expectationStrength !== 'PROVEN') {
234
+ boundaryExplanation = `Assigned MEDIUM: score ${score.toFixed(1)} >= 80 but expectation not proven`;
235
+ } else if (!allSensorsWithData) {
236
+ boundaryExplanation = `Assigned MEDIUM: score ${score.toFixed(1)} >= 80 but sensors lack data`;
237
+ }
262
238
  }
263
239
  } else if (score >= 55) {
264
240
  level = 'MEDIUM';
@@ -300,55 +276,28 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
300
276
  level,
301
277
  score: Math.round(score),
302
278
  expectationStrength,
303
- sensorsPresent,
304
- allSensorsPresent,
279
+ sensorsWithData,
280
+ allSensorsWithData,
305
281
  evidenceSignals,
306
282
  boosts,
307
283
  penalties,
308
284
  attemptMeta,
309
- boundaryExplanation, // Phase 3: Include boundary reasoning
310
- contradictions // GAP 5.2: Include contradiction list
311
- });
312
-
313
- // === STEP 11: GENERATE CONFIDENCE FACTORS (GAP 5.2) ===
314
- const factors = generateConfidenceFactors({
315
- expectationStrength,
316
- sensorsPresent,
317
- evidenceSignals,
318
- boosts,
319
- penalties,
320
- contradictions,
321
- baseScore,
322
- totalBoosts,
323
- totalPenalties
285
+ boundaryExplanation // Phase 3: Include boundary reasoning
324
286
  });
325
287
 
326
- // === STEP 12: APPLY EXECUTION MODE CEILING ===
327
- // If execution mode is WEB_SCAN_LIMITED, cap confidence at 0.45 (45%)
328
- let computedScore = score / 100; // Convert from 0..100 to 0..1 (float)
329
- const ceiledScore = Math.min(computedScore, executionModeCeiling);
330
-
331
- // If ceiling was applied, adjust level accordingly
332
- let ceiledLevel = level;
333
- if (ceiledScore < computedScore) {
334
- // Score was capped, so adjust level to match new ceiling
335
- if (ceiledScore < 0.2) ceiledLevel = 'LOW';
336
- else if (ceiledScore < 0.5) ceiledLevel = 'MEDIUM';
337
- else ceiledLevel = 'HIGH';
338
- }
339
-
340
288
  return {
341
- score: ceiledScore, // Convert from 0..100 to 0..1 (float), with ceiling applied
342
- scorePct: Math.round(ceiledScore * 100), // Optional: 0..100 for backward compat/convenience
343
- level: ceiledLevel,
289
+ score: Math.round(score),
290
+ level,
344
291
  explain: finalExplain,
345
- factors: factors, // GAP 5.2: Structured factor breakdown
346
- contradictions: contradictions, // GAP 5.2: Explicit contradictions
292
+ factors: {
293
+ expectationStrength,
294
+ sensorsPresent: sensorsWithData,
295
+ evidenceSignals,
296
+ penalties,
297
+ boosts
298
+ },
347
299
  confidenceExplanation,
348
- boundaryExplanation, // Phase 3: Surface boundary reasoning in output
349
- // Expose raw boosts/penalties for testing and debugging
350
- boosts: boosts,
351
- penalties: penalties
300
+ boundaryExplanation // Phase 3: Surface boundary reasoning in output
352
301
  };
353
302
  }
354
303
 
@@ -385,35 +334,16 @@ function determineExpectationStrength(expectation = {}) {
385
334
 
386
335
  /**
387
336
  * Extract deterministic evidence signals from runtime data.
388
- * GAP 5.2: Integrates Gap 5.1 UI Feedback signals with existing sensor data.
389
337
  */
390
- function extractEvidenceSignals({ networkSummary, consoleSummary, uiSignals, comparisons, uiFeedback }) {
391
- // GAP 5.1: Extract UI feedback signals (6 types)
392
- const uiFeedbackScore = uiFeedback?.overallUiFeedbackScore || 0;
393
- const uiFeedbackSignals = uiFeedback?.signals || {};
394
-
338
+ function extractEvidenceSignals({ networkSummary, consoleSummary, uiSignals, comparisons }) {
395
339
  const signals = {
396
340
  urlChanged: comparisons?.hasUrlChange === true,
397
341
  domChanged: comparisons?.hasDomChange === true,
398
342
  screenshotChanged: comparisons?.hasVisibleChange === true,
399
343
  networkFailed: (networkSummary?.failedRequests || 0) > 0,
400
- networkSuccess: (networkSummary?.totalRequests || 0) > 0 && (networkSummary?.failedRequests || 0) === 0,
401
344
  consoleErrors: (consoleSummary?.hasErrors === true),
402
345
  uiFeedbackDetected: hasAnyFeedback(uiSignals),
403
- slowRequests: (networkSummary?.slowRequestsCount || 0) > 0,
404
-
405
- // GAP 5.1: Runtime UI feedback signals
406
- uiFeedbackScore: uiFeedbackScore, // 0..1 overall score
407
- uiFeedbackDomChange: uiFeedbackSignals.domChange?.happened === true,
408
- uiFeedbackLoading: uiFeedbackSignals.loading?.appeared === true || uiFeedbackSignals.loading?.disappeared === true,
409
- uiFeedbackButtonState: uiFeedbackSignals.buttonStateTransition?.happened === true,
410
- uiFeedbackNotification: uiFeedbackSignals.notification?.happened === true,
411
- uiFeedbackNavigation: uiFeedbackSignals.navigation?.happened === true,
412
- uiFeedbackFocusChange: uiFeedbackSignals.focusChange?.happened === true,
413
- uiFeedbackScrollChange: uiFeedbackSignals.scrollChange?.happened === true,
414
-
415
- // Derived: Strong UI feedback = any significant signal
416
- strongUiFeedback: uiFeedbackScore > 0.5
346
+ slowRequests: (networkSummary?.slowRequestsCount || 0) > 0
417
347
  };
418
348
 
419
349
  return signals;
@@ -566,36 +496,6 @@ function scoreByFindingType({
566
496
  penalties
567
497
  });
568
498
  break;
569
-
570
- // PHASE 11: Journey stall detection
571
- case 'journey-stall-silent-failure':
572
- totalBoosts = scoreJourneyStall({
573
- expectation,
574
- evidenceSignals,
575
- boosts,
576
- penalties
577
- });
578
- totalPenalties = penalizeJourneyStall({
579
- expectation,
580
- evidenceSignals,
581
- penalties
582
- });
583
- break;
584
-
585
- // PHASE 12: Expectation chain breaks
586
- case 'expectation-chain-break':
587
- totalBoosts = scoreExpectationChainBreak({
588
- expectation,
589
- evidenceSignals,
590
- boosts,
591
- penalties
592
- });
593
- totalPenalties = penalizeExpectationChainBreak({
594
- expectation,
595
- evidenceSignals,
596
- penalties
597
- });
598
- break;
599
499
  }
600
500
 
601
501
  return { totalBoosts, totalPenalties };
@@ -620,16 +520,10 @@ function scoreNetworkSilentFailure({ networkSummary: _networkSummary, consoleSum
620
520
  boosts.push('Console errors present');
621
521
  }
622
522
 
623
- // GAP 5.2: +12 if network failed AND no UI feedback (strong silent failure evidence)
624
- if (evidenceSignals.networkFailed && !evidenceSignals.strongUiFeedback && evidenceSignals.uiFeedbackScore < 0.3) {
625
- total += 12;
626
- boosts.push(`Silent failure: network error with minimal UI feedback (score: ${evidenceSignals.uiFeedbackScore.toFixed(2)})`);
627
- }
628
-
629
- // GAP 5.2: +8 if network failed + no notification signal
630
- if (evidenceSignals.networkFailed && !evidenceSignals.uiFeedbackNotification) {
631
- total += 8;
632
- boosts.push('Network failed without error notification to user');
523
+ // +6 if network failed AND no UI feedback
524
+ if (evidenceSignals.networkFailed && !evidenceSignals.uiFeedbackDetected) {
525
+ total += 6;
526
+ boosts.push('Silent failure: no user feedback on network error');
633
527
  }
634
528
 
635
529
  return total;
@@ -638,22 +532,10 @@ function scoreNetworkSilentFailure({ networkSummary: _networkSummary, consoleSum
638
532
  function penalizeNetworkSilentFailure({ evidenceSignals, penalties }) {
639
533
  let total = 0;
640
534
 
641
- // GAP 5.2: -15 if strong UI feedback present (not silent)
642
- if (evidenceSignals.strongUiFeedback) {
643
- total += 15;
644
- penalties.push(`Strong UI feedback detected (score: ${evidenceSignals.uiFeedbackScore.toFixed(2)}) - not silent`);
645
- }
646
-
647
- // GAP 5.2: -10 if notification shown
648
- if (evidenceSignals.uiFeedbackNotification) {
535
+ // -10 if UI feedback present (shouldn't be silent failure)
536
+ if (evidenceSignals.uiFeedbackDetected) {
649
537
  total += 10;
650
- penalties.push('Error notification shown to user');
651
- }
652
-
653
- // GAP 5.2: -8 if moderate UI feedback (0.3-0.5)
654
- if (evidenceSignals.uiFeedbackScore >= 0.3 && evidenceSignals.uiFeedbackScore <= 0.5) {
655
- total += 8;
656
- penalties.push(`Moderate UI feedback detected (score: ${evidenceSignals.uiFeedbackScore.toFixed(2)})`);
538
+ penalties.push('UI feedback detected (suggests not silent)');
657
539
  }
658
540
 
659
541
  return total;
@@ -698,16 +580,10 @@ function scoreMissingFeedbackFailure({ networkSummary: _networkSummary, evidence
698
580
  boosts.push('Slow requests detected');
699
581
  }
700
582
 
701
- // GAP 5.2: +12 if network activity without loading indicator
702
- if (evidenceSignals.networkSuccess && !evidenceSignals.uiFeedbackLoading && evidenceSignals.slowRequests) {
703
- total += 12;
704
- boosts.push('Slow network requests without loading indicator');
705
- }
706
-
707
- // GAP 5.2: +10 if button state didn't change during async operation
708
- if (evidenceSignals.networkSuccess && !evidenceSignals.uiFeedbackButtonState && evidenceSignals.slowRequests) {
709
- total += 10;
710
- boosts.push('Async operation without button state feedback (disabled/loading)');
583
+ // +8 if network activity without loading feedback
584
+ if (evidenceSignals.networkFailed && !evidenceSignals.uiFeedbackDetected) {
585
+ total += 8;
586
+ boosts.push('Network activity without user feedback');
711
587
  }
712
588
 
713
589
  return total;
@@ -716,18 +592,12 @@ function scoreMissingFeedbackFailure({ networkSummary: _networkSummary, evidence
716
592
  function penalizeMissingFeedbackFailure({ evidenceSignals, penalties }) {
717
593
  let total = 0;
718
594
 
719
- // GAP 5.2: -12 if loading feedback detected
720
- if (evidenceSignals.uiFeedbackLoading) {
721
- total += 12;
595
+ // -10 if loading feedback detected
596
+ if (evidenceSignals.uiFeedbackDetected) {
597
+ total += 10;
722
598
  penalties.push('Loading indicator detected');
723
599
  }
724
600
 
725
- // GAP 5.2: -8 if button state changed
726
- if (evidenceSignals.uiFeedbackButtonState) {
727
- total += 8;
728
- penalties.push('Button state transition detected');
729
- }
730
-
731
601
  return total;
732
602
  }
733
603
 
@@ -875,13 +745,13 @@ function penalizeNavigationSilentFailure({ evidenceSignals, penalties }) {
875
745
 
876
746
  // -10 if UI feedback present (shouldn't be silent failure)
877
747
  if (evidenceSignals.uiFeedbackDetected) {
878
- total -= 10;
748
+ total += 10;
879
749
  penalties.push('UI feedback detected (suggests navigation feedback provided)');
880
750
  }
881
751
 
882
752
  // -5 if URL changed (navigation might have succeeded)
883
753
  if (evidenceSignals.urlChanged) {
884
- total -= 5;
754
+ total += 5;
885
755
  penalties.push('URL changed (navigation may have succeeded)');
886
756
  }
887
757
 
@@ -911,7 +781,7 @@ function penalizePartialNavigationFailure({ evidenceSignals, penalties }) {
911
781
 
912
782
  // -10 if UI feedback present (shouldn't be partial failure)
913
783
  if (evidenceSignals.uiFeedbackDetected) {
914
- total -= 10;
784
+ total += 10;
915
785
  penalties.push('UI feedback detected (suggests navigation feedback provided)');
916
786
  }
917
787
 
@@ -957,20 +827,18 @@ function generateExplanations(boosts, penalties, expectationStrength, _evidenceS
957
827
  * Generate confidence explanation for Phase 9: Reality Confidence & Explanation Layer.
958
828
  * Provides whyThisConfidence, whatWouldIncreaseConfidence, whatWouldReduceConfidence.
959
829
  * Phase 3: Also includes boundaryExplanation for near-threshold decisions.
960
- * GAP 5.2: Includes contradiction handling.
961
830
  */
962
831
  function generateConfidenceExplanation({
963
832
  level,
964
833
  score: _score,
965
834
  expectationStrength,
966
- sensorsPresent,
967
- allSensorsPresent,
835
+ sensorsWithData,
836
+ allSensorsWithData,
968
837
  evidenceSignals: _evidenceSignals,
969
838
  boosts,
970
839
  penalties,
971
840
  attemptMeta,
972
- boundaryExplanation = null, // Phase 3: Optional boundary reasoning
973
- contradictions = [] // GAP 5.2: Contradiction list
841
+ boundaryExplanation = null // Phase 3: Optional boundary reasoning
974
842
  }) {
975
843
  const whyThisConfidence = [];
976
844
  const whatWouldIncreaseConfidence = [];
@@ -981,27 +849,14 @@ function generateConfidenceExplanation({
981
849
  whyThisConfidence.push(boundaryExplanation);
982
850
  }
983
851
 
984
- // GAP 5.2: If contradictions exist, mention them first
985
- if (contradictions.length > 0) {
986
- const criticalCount = contradictions.filter(c => c.severity === 'critical').length;
987
- const majorCount = contradictions.filter(c => c.severity === 'major').length;
988
- if (criticalCount > 0) {
989
- whyThisConfidence.push(`${criticalCount} critical contradiction(s) detected - significantly reduces confidence`);
990
- } else if (majorCount > 0) {
991
- whyThisConfidence.push(`${majorCount} major contradiction(s) detected - reduces confidence`);
992
- } else {
993
- whyThisConfidence.push(`${contradictions.length} minor contradiction(s) detected - slightly reduces confidence`);
994
- }
995
- }
996
-
997
852
  // WHY THIS CONFIDENCE: Explain current level
998
853
  if (level === 'HIGH') {
999
854
  whyThisConfidence.push('High confidence: expectation is proven and all sensors captured evidence');
1000
855
  if (expectationStrength === 'PROVEN') {
1001
856
  whyThisConfidence.push('Expectation is proven from source code');
1002
857
  }
1003
- if (allSensorsPresent) {
1004
- whyThisConfidence.push('All sensors (network, console, UI, UIFeedback) were active');
858
+ if (allSensorsWithData) {
859
+ whyThisConfidence.push('All sensors (network, console, UI) were active');
1005
860
  }
1006
861
  if (boosts.length > 0) {
1007
862
  whyThisConfidence.push(`Strong evidence: ${boosts.length} positive signal(s)`);
@@ -1013,12 +868,11 @@ function generateConfidenceExplanation({
1013
868
  } else {
1014
869
  whyThisConfidence.push(`Expectation strength: ${expectationStrength} (not proven)`);
1015
870
  }
1016
- if (!allSensorsPresent) {
871
+ if (!allSensorsWithData) {
1017
872
  const missing = [];
1018
- if (!sensorsPresent.network) missing.push('network');
1019
- if (!sensorsPresent.console) missing.push('console');
1020
- if (!sensorsPresent.ui) missing.push('UI');
1021
- if (!sensorsPresent.uiFeedback) missing.push('UIFeedback');
873
+ if (!sensorsWithData.network) missing.push('network');
874
+ if (!sensorsWithData.console) missing.push('console');
875
+ if (!sensorsWithData.ui) missing.push('UI');
1022
876
  whyThisConfidence.push(`Missing sensor data: ${missing.join(', ')}`);
1023
877
  }
1024
878
  if (penalties.length > 0) {
@@ -1029,7 +883,7 @@ function generateConfidenceExplanation({
1029
883
  if (expectationStrength !== 'PROVEN') {
1030
884
  whyThisConfidence.push(`Expectation strength: ${expectationStrength} (not proven from code)`);
1031
885
  }
1032
- if (!allSensorsPresent) {
886
+ if (!allSensorsWithData) {
1033
887
  whyThisConfidence.push('Some sensors were not active, reducing confidence');
1034
888
  }
1035
889
  if (attemptMeta && !attemptMeta.repeated) {
@@ -1042,12 +896,11 @@ function generateConfidenceExplanation({
1042
896
  if (expectationStrength !== 'PROVEN') {
1043
897
  whatWouldIncreaseConfidence.push('Make the expectation proven by adding explicit code that promises the behavior');
1044
898
  }
1045
- if (!allSensorsPresent) {
899
+ if (!allSensorsWithData) {
1046
900
  const missing = [];
1047
- if (!sensorsPresent.network) missing.push('network monitoring');
1048
- if (!sensorsPresent.console) missing.push('console error detection');
1049
- if (!sensorsPresent.ui) missing.push('UI change detection');
1050
- if (!sensorsPresent.uiFeedback) missing.push('UI feedback detection');
901
+ if (!sensorsWithData.network) missing.push('network monitoring');
902
+ if (!sensorsWithData.console) missing.push('console error detection');
903
+ if (!sensorsWithData.ui) missing.push('UI change detection');
1051
904
  whatWouldIncreaseConfidence.push(`Enable missing sensors: ${missing.join(', ')}`);
1052
905
  }
1053
906
  if (attemptMeta && !attemptMeta.repeated && level === 'LOW') {
@@ -1056,9 +909,6 @@ function generateConfidenceExplanation({
1056
909
  if (boosts.length === 0) {
1057
910
  whatWouldIncreaseConfidence.push('Add stronger evidence signals (network requests, console errors, UI changes)');
1058
911
  }
1059
- if (contradictions.length > 0) {
1060
- whatWouldIncreaseConfidence.push('Resolve contradictions by clarifying expected behavior or fixing detection logic');
1061
- }
1062
912
  }
1063
913
 
1064
914
  // WHAT WOULD REDUCE CONFIDENCE
@@ -1066,15 +916,12 @@ function generateConfidenceExplanation({
1066
916
  if (expectationStrength === 'PROVEN') {
1067
917
  whatWouldReduceConfidence.push('If expectation becomes unproven (code changes, expectation removed)');
1068
918
  }
1069
- if (allSensorsPresent) {
919
+ if (allSensorsWithData) {
1070
920
  whatWouldReduceConfidence.push('If sensors become unavailable or disabled');
1071
921
  }
1072
922
  if (boosts.length > 0) {
1073
923
  whatWouldReduceConfidence.push('If positive evidence signals disappear (network succeeds, UI feedback appears)');
1074
924
  }
1075
- if (contradictions.length === 0) {
1076
- whatWouldReduceConfidence.push('If contradictory evidence appears (mixed signals, conflicting feedback)');
1077
- }
1078
925
  }
1079
926
  if (penalties.length === 0 && level === 'HIGH') {
1080
927
  whatWouldReduceConfidence.push('If uncertainty factors appear (URL changes, partial effects, missing data)');
@@ -1092,7 +939,7 @@ function generateConfidenceExplanation({
1092
939
  // ============================================================
1093
940
 
1094
941
  // PHASE 3: Export sensor validation functions for testing
1095
- export { hasNetworkData, hasConsoleData, hasUiData, hasUiFeedbackData };
942
+ export { hasNetworkData, hasConsoleData, hasUiData };
1096
943
 
1097
944
  // Detect error feedback (legacy helper)
1098
945
  function _detectErrorFeedback(uiSignals) {
@@ -1112,410 +959,3 @@ function _detectStatusFeedback(uiSignals) {
1112
959
  const after = uiSignals?.after || {};
1113
960
  return after.hasStatusSignal || after.hasLiveRegion || after.hasDialog;
1114
961
  }
1115
-
1116
- // ============================================================
1117
- // GAP 5.2: CONTRADICTION DETECTION
1118
- // ============================================================
1119
-
1120
- /**
1121
- * Detect contradictions in evidence signals and populate contradictions array.
1122
- * Contradictions reduce confidence by identifying conflicting signals.
1123
- */
1124
- function detectContradictions({ evidenceSignals, expectation, findingType, contradictions, penalties }) {
1125
- // Contradiction 1: Network success + no UI feedback + no navigation + no DOM change + claiming silent failure
1126
- if (
1127
- evidenceSignals.networkSuccess &&
1128
- !evidenceSignals.strongUiFeedback &&
1129
- !evidenceSignals.uiFeedbackNavigation &&
1130
- !evidenceSignals.domChanged &&
1131
- !evidenceSignals.urlChanged &&
1132
- findingType?.includes('silent_failure')
1133
- ) {
1134
- contradictions.push({
1135
- type: 'network_success_no_feedback',
1136
- details: 'Network succeeded but no UI feedback, navigation, or DOM change detected - possible silent success or deferred update',
1137
- severity: 'major'
1138
- });
1139
- }
1140
-
1141
- // Contradiction 2: UI feedback shows explicit error/notification but finding claims "silent"
1142
- if (
1143
- (evidenceSignals.uiFeedbackNotification || evidenceSignals.consoleErrors) &&
1144
- !evidenceSignals.strongUiFeedback &&
1145
- findingType?.includes('silent')
1146
- ) {
1147
- contradictions.push({
1148
- type: 'error_feedback_present',
1149
- details: 'Error notifications or console errors present but UI feedback score is low - may not be truly "silent"',
1150
- severity: 'minor'
1151
- });
1152
- }
1153
-
1154
- // Contradiction 3: Strong UI feedback but claiming silent failure
1155
- if (evidenceSignals.strongUiFeedback && findingType?.includes('silent_failure')) {
1156
- contradictions.push({
1157
- type: 'strong_feedback_silent_claim',
1158
- details: `Strong UI feedback detected (score: ${evidenceSignals.uiFeedbackScore.toFixed(2)}) contradicts silent failure claim`,
1159
- severity: 'critical'
1160
- });
1161
- }
1162
-
1163
- // Contradiction 4: Navigation occurred but claiming missing action
1164
- if (
1165
- (evidenceSignals.urlChanged || evidenceSignals.uiFeedbackNavigation) &&
1166
- (findingType === 'missing_network_action' || findingType === 'missing_state_action')
1167
- ) {
1168
- contradictions.push({
1169
- type: 'navigation_with_missing_action',
1170
- details: 'Navigation detected but claiming missing action - action may have fired differently',
1171
- severity: 'major'
1172
- });
1173
- }
1174
-
1175
- // Contradiction 5: DOM changed significantly but UI feedback score is zero
1176
- if (
1177
- evidenceSignals.domChanged &&
1178
- evidenceSignals.uiFeedbackScore === 0 &&
1179
- (expectation?.promise?.kind === 'network' || expectation?.promise?.kind === 'state')
1180
- ) {
1181
- contradictions.push({
1182
- type: 'dom_change_no_ui_feedback',
1183
- details: 'DOM changed but UI feedback detection missed it - detection may be too conservative',
1184
- severity: 'minor'
1185
- });
1186
- }
1187
-
1188
- // Contradiction 6: Multiple conflicting signals (network failed + strong feedback + no console errors)
1189
- if (
1190
- evidenceSignals.networkFailed &&
1191
- evidenceSignals.strongUiFeedback &&
1192
- !evidenceSignals.consoleErrors &&
1193
- findingType?.includes('silent')
1194
- ) {
1195
- contradictions.push({
1196
- type: 'mixed_signals',
1197
- details: 'Network failed but strong UI feedback present without console errors - user likely informed',
1198
- severity: 'major'
1199
- });
1200
- }
1201
- }
1202
-
1203
- // ============================================================
1204
- // GAP 5.2: CONFIDENCE FACTORS GENERATION
1205
- // ============================================================
1206
-
1207
- /**
1208
- * Generate structured confidence factors with weights, values, and rationales.
1209
- * Each factor explains how it contributes to the final confidence score.
1210
- */
1211
- function generateConfidenceFactors({
1212
- expectationStrength,
1213
- sensorsPresent,
1214
- evidenceSignals,
1215
- boosts,
1216
- penalties,
1217
- contradictions,
1218
- baseScore,
1219
- totalBoosts,
1220
- totalPenalties
1221
- }) {
1222
- const factors = [];
1223
-
1224
- // Factor 1: Expectation strength (weight: high)
1225
- factors.push({
1226
- key: 'expectation_strength',
1227
- weight: 0.25,
1228
- value: expectationStrength,
1229
- rationale: expectationStrength === 'PROVEN'
1230
- ? 'Expectation is proven from source code analysis'
1231
- : `Expectation strength is ${expectationStrength}, not from proven source`,
1232
- impact: expectationStrength === 'PROVEN' ? 'positive' : 'negative'
1233
- });
1234
-
1235
- // Factor 2: Sensor availability (weight: high)
1236
- const sensorCount = Object.values(sensorsPresent).filter(Boolean).length;
1237
- const sensorTotal = Object.keys(sensorsPresent).length;
1238
- factors.push({
1239
- key: 'sensor_availability',
1240
- weight: 0.20,
1241
- value: `${sensorCount}/${sensorTotal}`,
1242
- rationale: sensorCount === sensorTotal
1243
- ? 'All sensors active and captured data'
1244
- : `Only ${sensorCount} of ${sensorTotal} sensors captured data`,
1245
- impact: sensorCount === sensorTotal ? 'positive' : 'negative'
1246
- });
1247
-
1248
- // Factor 3: UI feedback score (weight: medium) - GAP 5.2
1249
- if (sensorsPresent.uiFeedback) {
1250
- factors.push({
1251
- key: 'ui_feedback_score',
1252
- weight: 0.18,
1253
- value: evidenceSignals.uiFeedbackScore.toFixed(2),
1254
- rationale: evidenceSignals.uiFeedbackScore > 0.5
1255
- ? `Strong UI feedback detected (score: ${evidenceSignals.uiFeedbackScore.toFixed(2)}) - user likely received feedback`
1256
- : evidenceSignals.uiFeedbackScore > 0
1257
- ? `Moderate UI feedback detected (score: ${evidenceSignals.uiFeedbackScore.toFixed(2)}) - some user feedback present`
1258
- : 'No UI feedback detected - potential silent failure',
1259
- impact: evidenceSignals.uiFeedbackScore > 0.5 ? 'negative' : 'positive'
1260
- });
1261
- }
1262
-
1263
- // Factor 4: Network evidence (weight: medium)
1264
- if (sensorsPresent.network) {
1265
- const networkValue = evidenceSignals.networkFailed ? 'failed' : evidenceSignals.networkSuccess ? 'success' : 'none';
1266
- factors.push({
1267
- key: 'network_evidence',
1268
- weight: 0.15,
1269
- value: networkValue,
1270
- rationale: evidenceSignals.networkFailed
1271
- ? 'Network requests failed - strong evidence of failure'
1272
- : evidenceSignals.networkSuccess
1273
- ? 'Network requests succeeded - may not be a failure'
1274
- : 'No network activity detected',
1275
- impact: evidenceSignals.networkFailed ? 'positive' : evidenceSignals.networkSuccess ? 'negative' : 'neutral'
1276
- });
1277
- }
1278
-
1279
- // Factor 5: Observable changes (weight: low)
1280
- const observableChanges = [
1281
- evidenceSignals.domChanged && 'DOM',
1282
- evidenceSignals.urlChanged && 'URL',
1283
- evidenceSignals.screenshotChanged && 'visual'
1284
- ].filter(Boolean);
1285
- factors.push({
1286
- key: 'observable_changes',
1287
- weight: 0.12,
1288
- value: observableChanges.length > 0 ? observableChanges.join(', ') : 'none',
1289
- rationale: observableChanges.length > 0
1290
- ? `Observable changes detected: ${observableChanges.join(', ')} - user likely saw something`
1291
- : 'No observable changes detected - potential silent failure',
1292
- impact: observableChanges.length > 0 ? 'negative' : 'positive'
1293
- });
1294
-
1295
- // Factor 6: Contradictions (weight: penalty) - GAP 5.2
1296
- if (contradictions.length > 0) {
1297
- const criticalCount = contradictions.filter(c => c.severity === 'critical').length;
1298
- const majorCount = contradictions.filter(c => c.severity === 'major').length;
1299
- const minorCount = contradictions.filter(c => c.severity === 'minor').length;
1300
-
1301
- factors.push({
1302
- key: 'contradictions',
1303
- weight: 0.10, // Non-negative weight; negativity represented via impact field
1304
- value: `${contradictions.length} (${criticalCount}C/${majorCount}M/${minorCount}m)`,
1305
- rationale: `Contradictory evidence detected: ${criticalCount} critical, ${majorCount} major, ${minorCount} minor - reduces confidence`,
1306
- impact: 'negative' // Impact field indicates this reduces confidence
1307
- });
1308
- }
1309
-
1310
- // Factor 7: Score composition (informational)
1311
- factors.push({
1312
- key: 'score_composition',
1313
- weight: 1.0,
1314
- value: `${baseScore} + ${totalBoosts} - ${totalPenalties} = ${Math.max(0, Math.min(100, baseScore + totalBoosts - totalPenalties))}`,
1315
- rationale: `Base score: ${baseScore}, Boosts: +${totalBoosts}, Penalties: -${totalPenalties}`,
1316
- impact: 'neutral'
1317
- });
1318
-
1319
- return factors;
1320
- }
1321
- // ============================================================
1322
- // PHASE 11: JOURNEY STALL CONFIDENCE SCORING
1323
- // ============================================================
1324
-
1325
- /**
1326
- * Score journey stall findings.
1327
- *
1328
- * Individual steps work (low confidence on each), but pattern across
1329
- * sequence clearly shows stall (high journey-level confidence possible).
1330
- */
1331
- function scoreJourneyStall({ expectation, evidenceSignals, boosts, penalties }) {
1332
- let total = 0;
1333
-
1334
- // Extract journey context
1335
- const journeyEvidence = expectation?.evidence?.journeyContext || {};
1336
- const stallPoints = expectation?.evidence?.stallPoints || [];
1337
-
1338
- // +15 for multiple stall points (clear pattern)
1339
- if (stallPoints.length >= 2) {
1340
- total += 15;
1341
- boosts.push(`Multiple stall points detected (${stallPoints.length}) - clear pattern`);
1342
- }
1343
-
1344
- // +12 if stall severity is CRITICAL
1345
- const hasCritical = stallPoints.some(sp => sp.severity === 'CRITICAL');
1346
- if (hasCritical) {
1347
- total += 12;
1348
- boosts.push('Critical severity stall points detected');
1349
- }
1350
-
1351
- // +10 if no navigation (classic stall indicator)
1352
- const noNavStalls = stallPoints.filter(sp => sp.reasons.includes('no_navigation'));
1353
- if (noNavStalls.length > 0) {
1354
- total += 10;
1355
- boosts.push(`Expected navigation blocked: ${noNavStalls.length} instances`);
1356
- }
1357
-
1358
- // +8 if no new actionable UI (user stuck with same options)
1359
- const noUiStalls = stallPoints.filter(sp => sp.reasons.includes('no_new_actionable_ui'));
1360
- if (noUiStalls.length > 0) {
1361
- total += 8;
1362
- boosts.push(`No new interactive elements: ${noUiStalls.length} instances`);
1363
- }
1364
-
1365
- // +8 if DOM stagnation across sequence
1366
- const noDomStalls = stallPoints.filter(sp => sp.reasons.includes('no_dom_progression'));
1367
- if (noDomStalls.length > 0) {
1368
- total += 8;
1369
- boosts.push(`DOM content unchanged: ${noDomStalls.length} instances`);
1370
- }
1371
-
1372
- // +10 if long sequence before stall (shows user persisted through multiple steps)
1373
- const sequenceLength = journeyEvidence?.totalInteractions || 0;
1374
- if (sequenceLength >= 5) {
1375
- total += 10;
1376
- boosts.push(`Long journey sequence (${sequenceLength} interactions) before stall`);
1377
- }
1378
-
1379
- // +8 for URL stagnation (stayed on same page despite actions)
1380
- const urlProgression = journeyEvidence?.urlProgression || [];
1381
- if (urlProgression.length <= 1 && sequenceLength >= 3) {
1382
- total += 8;
1383
- boosts.push(`URL unchanged across ${sequenceLength} interactions - clear stall`);
1384
- }
1385
-
1386
- return total;
1387
- }
1388
-
1389
- /**
1390
- * Penalize journey stall findings.
1391
- *
1392
- * Reduce confidence if any step in journey clearly failed.
1393
- */
1394
- function penalizeJourneyStall({ expectation, evidenceSignals, penalties }) {
1395
- let total = 0;
1396
-
1397
- const stallPoints = expectation?.evidence?.stallPoints || [];
1398
-
1399
- // -20 if low severity stalls (might not be real blocking issue)
1400
- const lowSeverity = stallPoints.filter(sp => sp.severity === 'LOW').length;
1401
- if (lowSeverity === stallPoints.length && stallPoints.length > 0) {
1402
- total -= 20;
1403
- penalties.push('All stall points are LOW severity - weak pattern');
1404
- }
1405
-
1406
- // -15 if only 1 stall point (might be coincidence)
1407
- if (stallPoints.length === 1) {
1408
- total -= 15;
1409
- penalties.push('Only single stall point - weak evidence of pattern');
1410
- }
1411
-
1412
- // -12 if URL did progress (navigation worked)
1413
- const urlProgression = expectation?.evidence?.journeyContext?.urlProgression || [];
1414
- if (urlProgression.length > 1) {
1415
- total -= 12;
1416
- penalties.push(`URL progressed (${urlProgression.length} distinct pages) - not complete stall`);
1417
- }
1418
-
1419
- // -10 if strong UI feedback detected (user might be informed)
1420
- if (evidenceSignals?.strongUiFeedback) {
1421
- total -= 10;
1422
- penalties.push('Strong UI feedback score detected - not silent');
1423
- }
1424
-
1425
- // -8 if no navigation expectation violations (unexpected navigation stall)
1426
- const navStalls = stallPoints.filter(sp => sp.reasons.includes('no_navigation')).length;
1427
- const totalReasons = stallPoints.reduce((sum, sp) => sum + sp.reasons.length, 0);
1428
-
1429
- if (navStalls === 0 && totalReasons > 0) {
1430
- total -= 8;
1431
- penalties.push('No navigation stalls - stall is not navigation-related');
1432
- }
1433
-
1434
- return total;
1435
- }
1436
-
1437
- // ============================================================
1438
- // PHASE 12: EXPECTATION CHAIN BREAK SCORING
1439
- // ============================================================
1440
-
1441
- /**
1442
- * Score expectation chain breaks.
1443
- *
1444
- * Chains of proven expectations breaking mid-sequence is high-confidence evidence
1445
- * of a root cause failure.
1446
- */
1447
- function scoreExpectationChainBreak({ expectation, evidenceSignals, boosts, penalties: _penalties }) {
1448
- let total = 0;
1449
-
1450
- const chainEvidence = expectation?.evidence || {};
1451
- const chainLength = chainEvidence.chainLength || 0;
1452
- const fulfilledSteps = chainEvidence.fulfilledSteps || 0;
1453
- const brokenStepIndex = chainEvidence.brokenStepIndex || 0;
1454
-
1455
- // +15 if chain is long (3+ steps proven before break)
1456
- if (chainLength >= 3) {
1457
- total += 15;
1458
- boosts.push(`Long expectation chain (${chainLength} steps) - deep pattern proof`);
1459
- }
1460
-
1461
- // +12 if break occurs late in chain (past 50%)
1462
- const breakDepth = brokenStepIndex / Math.max(1, chainLength);
1463
- if (breakDepth >= 0.5 && brokenStepIndex > 0) {
1464
- total += 12;
1465
- boosts.push(`Chain broke late (step ${brokenStepIndex + 1}/${chainLength}) - proves path was valid`);
1466
- }
1467
-
1468
- // +10 if multiple steps fulfilled before break (shows pattern works initially)
1469
- if (fulfilledSteps >= 2) {
1470
- total += 10;
1471
- boosts.push(`Multiple proven steps (${fulfilledSteps}) completed before failure - clear causality`);
1472
- }
1473
-
1474
- // +8 if first step was proven (entry point to chain is valid)
1475
- if (fulfilledSteps > 0) {
1476
- total += 8;
1477
- boosts.push('Chain entry point was fulfilled - break is at downstream step');
1478
- }
1479
-
1480
- return total;
1481
- }
1482
-
1483
- /**
1484
- * Penalize expectation chain breaks.
1485
- *
1486
- * Reduce confidence if chain evidence is weak or incomplete.
1487
- */
1488
- function penalizeExpectationChainBreak({ expectation, evidenceSignals, penalties }) {
1489
- let total = 0;
1490
-
1491
- const chainEvidence = expectation?.evidence || {};
1492
- const chainLength = chainEvidence.chainLength || 0;
1493
- const fulfilledSteps = chainEvidence.fulfilledSteps || 0;
1494
- const brokenStepIndex = chainEvidence.brokenStepIndex || 0;
1495
-
1496
- // -15 if chain is too short (2 steps might be coincidence)
1497
- if (chainLength <= 2) {
1498
- total -= 15;
1499
- penalties.push('Short chain (2 steps) - may be coincidence');
1500
- }
1501
-
1502
- // -12 if break occurs very early (step 1)
1503
- if (brokenStepIndex <= 1) {
1504
- total -= 12;
1505
- penalties.push('Chain broke at entry point - not a chain break pattern');
1506
- }
1507
-
1508
- // -10 if no steps were fulfilled (trace doesn't support chain)
1509
- if (fulfilledSteps === 0) {
1510
- total -= 10;
1511
- penalties.push('No chain steps were fulfilled - chain pattern not evident');
1512
- }
1513
-
1514
- // -8 if strong UI feedback present (user might have been informed of issue)
1515
- if (evidenceSignals?.strongUiFeedback) {
1516
- total -= 8;
1517
- penalties.push('Strong UI feedback detected - not silent');
1518
- }
1519
-
1520
- return total;
1521
- }