@veraxhq/verax 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -18
- package/bin/verax.js +7 -0
- package/package.json +3 -3
- package/src/cli/commands/baseline.js +104 -0
- package/src/cli/commands/default.js +79 -25
- package/src/cli/commands/ga.js +243 -0
- package/src/cli/commands/gates.js +95 -0
- package/src/cli/commands/inspect.js +131 -2
- package/src/cli/commands/release-check.js +213 -0
- package/src/cli/commands/run.js +246 -35
- package/src/cli/commands/security-check.js +211 -0
- package/src/cli/commands/truth.js +114 -0
- package/src/cli/entry.js +304 -67
- package/src/cli/util/angular-component-extractor.js +179 -0
- package/src/cli/util/angular-navigation-detector.js +141 -0
- package/src/cli/util/angular-network-detector.js +161 -0
- package/src/cli/util/angular-state-detector.js +162 -0
- package/src/cli/util/ast-interactive-detector.js +546 -0
- package/src/cli/util/ast-network-detector.js +603 -0
- package/src/cli/util/ast-usestate-detector.js +602 -0
- package/src/cli/util/bootstrap-guard.js +86 -0
- package/src/cli/util/determinism-runner.js +123 -0
- package/src/cli/util/determinism-writer.js +129 -0
- package/src/cli/util/env-url.js +4 -0
- package/src/cli/util/expectation-extractor.js +369 -73
- package/src/cli/util/findings-writer.js +126 -16
- package/src/cli/util/learn-writer.js +3 -1
- package/src/cli/util/observe-writer.js +3 -1
- package/src/cli/util/paths.js +3 -12
- package/src/cli/util/project-discovery.js +3 -0
- package/src/cli/util/project-writer.js +3 -1
- package/src/cli/util/run-resolver.js +64 -0
- package/src/cli/util/source-requirement.js +55 -0
- package/src/cli/util/summary-writer.js +1 -0
- package/src/cli/util/svelte-navigation-detector.js +163 -0
- package/src/cli/util/svelte-network-detector.js +80 -0
- package/src/cli/util/svelte-sfc-extractor.js +147 -0
- package/src/cli/util/svelte-state-detector.js +243 -0
- package/src/cli/util/vue-navigation-detector.js +177 -0
- package/src/cli/util/vue-sfc-extractor.js +162 -0
- package/src/cli/util/vue-state-detector.js +215 -0
- package/src/verax/cli/finding-explainer.js +56 -3
- package/src/verax/core/artifacts/registry.js +154 -0
- package/src/verax/core/artifacts/verifier.js +980 -0
- package/src/verax/core/baseline/baseline.enforcer.js +137 -0
- package/src/verax/core/baseline/baseline.snapshot.js +231 -0
- package/src/verax/core/capabilities/gates.js +499 -0
- package/src/verax/core/capabilities/registry.js +475 -0
- package/src/verax/core/confidence/confidence-compute.js +137 -0
- package/src/verax/core/confidence/confidence-invariants.js +234 -0
- package/src/verax/core/confidence/confidence-report-writer.js +112 -0
- package/src/verax/core/confidence/confidence-weights.js +44 -0
- package/src/verax/core/confidence/confidence.defaults.js +65 -0
- package/src/verax/core/confidence/confidence.loader.js +79 -0
- package/src/verax/core/confidence/confidence.schema.js +94 -0
- package/src/verax/core/confidence-engine-refactor.js +484 -0
- package/src/verax/core/confidence-engine.js +486 -0
- package/src/verax/core/confidence-engine.js.backup +471 -0
- package/src/verax/core/contracts/index.js +29 -0
- package/src/verax/core/contracts/types.js +185 -0
- package/src/verax/core/contracts/validators.js +381 -0
- package/src/verax/core/decision-snapshot.js +30 -3
- package/src/verax/core/decisions/decision.trace.js +276 -0
- package/src/verax/core/determinism/contract-writer.js +89 -0
- package/src/verax/core/determinism/contract.js +139 -0
- package/src/verax/core/determinism/diff.js +364 -0
- package/src/verax/core/determinism/engine.js +221 -0
- package/src/verax/core/determinism/finding-identity.js +148 -0
- package/src/verax/core/determinism/normalize.js +438 -0
- package/src/verax/core/determinism/report-writer.js +92 -0
- package/src/verax/core/determinism/run-fingerprint.js +118 -0
- package/src/verax/core/dynamic-route-intelligence.js +528 -0
- package/src/verax/core/evidence/evidence-capture-service.js +307 -0
- package/src/verax/core/evidence/evidence-intent-ledger.js +165 -0
- package/src/verax/core/evidence-builder.js +487 -0
- package/src/verax/core/execution-mode-context.js +77 -0
- package/src/verax/core/execution-mode-detector.js +190 -0
- package/src/verax/core/failures/exit-codes.js +86 -0
- package/src/verax/core/failures/failure-summary.js +76 -0
- package/src/verax/core/failures/failure.factory.js +225 -0
- package/src/verax/core/failures/failure.ledger.js +132 -0
- package/src/verax/core/failures/failure.types.js +196 -0
- package/src/verax/core/failures/index.js +10 -0
- package/src/verax/core/ga/ga-report-writer.js +43 -0
- package/src/verax/core/ga/ga.artifact.js +49 -0
- package/src/verax/core/ga/ga.contract.js +434 -0
- package/src/verax/core/ga/ga.enforcer.js +86 -0
- package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
- package/src/verax/core/guardrails/policy.defaults.js +210 -0
- package/src/verax/core/guardrails/policy.loader.js +83 -0
- package/src/verax/core/guardrails/policy.schema.js +110 -0
- package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
- package/src/verax/core/guardrails-engine.js +505 -0
- package/src/verax/core/observe/run-timeline.js +316 -0
- package/src/verax/core/perf/perf.contract.js +186 -0
- package/src/verax/core/perf/perf.display.js +65 -0
- package/src/verax/core/perf/perf.enforcer.js +91 -0
- package/src/verax/core/perf/perf.monitor.js +209 -0
- package/src/verax/core/perf/perf.report.js +198 -0
- package/src/verax/core/pipeline-tracker.js +238 -0
- package/src/verax/core/product-definition.js +127 -0
- package/src/verax/core/release/provenance.builder.js +271 -0
- package/src/verax/core/release/release-report-writer.js +40 -0
- package/src/verax/core/release/release.enforcer.js +159 -0
- package/src/verax/core/release/reproducibility.check.js +221 -0
- package/src/verax/core/release/sbom.builder.js +283 -0
- package/src/verax/core/report/cross-index.js +192 -0
- package/src/verax/core/report/human-summary.js +222 -0
- package/src/verax/core/route-intelligence.js +419 -0
- package/src/verax/core/security/secrets.scan.js +326 -0
- package/src/verax/core/security/security-report.js +50 -0
- package/src/verax/core/security/security.enforcer.js +124 -0
- package/src/verax/core/security/supplychain.defaults.json +38 -0
- package/src/verax/core/security/supplychain.policy.js +326 -0
- package/src/verax/core/security/vuln.scan.js +265 -0
- package/src/verax/core/truth/truth.certificate.js +250 -0
- package/src/verax/core/ui-feedback-intelligence.js +515 -0
- package/src/verax/detect/confidence-engine.js +628 -40
- package/src/verax/detect/confidence-helper.js +33 -0
- package/src/verax/detect/detection-engine.js +18 -1
- package/src/verax/detect/dynamic-route-findings.js +335 -0
- package/src/verax/detect/expectation-chain-detector.js +417 -0
- package/src/verax/detect/expectation-model.js +3 -1
- package/src/verax/detect/findings-writer.js +141 -5
- package/src/verax/detect/index.js +229 -5
- package/src/verax/detect/journey-stall-detector.js +558 -0
- package/src/verax/detect/route-findings.js +218 -0
- package/src/verax/detect/ui-feedback-findings.js +207 -0
- package/src/verax/detect/verdict-engine.js +57 -3
- package/src/verax/detect/view-switch-correlator.js +242 -0
- package/src/verax/index.js +413 -45
- package/src/verax/learn/action-contract-extractor.js +682 -64
- package/src/verax/learn/route-validator.js +4 -1
- package/src/verax/observe/index.js +88 -843
- package/src/verax/observe/interaction-runner.js +25 -8
- package/src/verax/observe/observe-context.js +205 -0
- package/src/verax/observe/observe-helpers.js +191 -0
- package/src/verax/observe/observe-runner.js +226 -0
- package/src/verax/observe/observers/budget-observer.js +185 -0
- package/src/verax/observe/observers/console-observer.js +102 -0
- package/src/verax/observe/observers/coverage-observer.js +107 -0
- package/src/verax/observe/observers/interaction-observer.js +471 -0
- package/src/verax/observe/observers/navigation-observer.js +132 -0
- package/src/verax/observe/observers/network-observer.js +87 -0
- package/src/verax/observe/observers/safety-observer.js +82 -0
- package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
- package/src/verax/observe/ui-feedback-detector.js +742 -0
- package/src/verax/observe/ui-signal-sensor.js +148 -2
- package/src/verax/scan-summary-writer.js +42 -8
- package/src/verax/shared/artifact-manager.js +8 -5
- package/src/verax/shared/css-spinner-rules.js +204 -0
- package/src/verax/shared/view-switch-rules.js +208 -0
|
@@ -70,6 +70,32 @@ function hasUiData(uiSignals) {
|
|
|
70
70
|
return hasAnyDelta || hasDomChange || hasVisibleChange || hasAriaChange || hasFocusChange || hasTextChange;
|
|
71
71
|
}
|
|
72
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;
|
|
97
|
+
}
|
|
98
|
+
|
|
73
99
|
const BASE_SCORES = {
|
|
74
100
|
network_silent_failure: 70,
|
|
75
101
|
validation_silent_failure: 60, // VALIDATION INTELLIGENCE v1
|
|
@@ -80,7 +106,9 @@ const BASE_SCORES = {
|
|
|
80
106
|
navigation_silent_failure: 75, // NAVIGATION INTELLIGENCE v2
|
|
81
107
|
partial_navigation_failure: 65, // NAVIGATION INTELLIGENCE v2
|
|
82
108
|
flow_silent_failure: 70, // FLOW INTELLIGENCE v1
|
|
83
|
-
observed_break: 50 // OBSERVED expectations (runtime-derived, lower confidence)
|
|
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
|
|
84
112
|
};
|
|
85
113
|
|
|
86
114
|
/**
|
|
@@ -101,17 +129,22 @@ function getBaseScoreFromExpectationStrength(expectationStrength) {
|
|
|
101
129
|
|
|
102
130
|
/**
|
|
103
131
|
* Main confidence computation function.
|
|
104
|
-
*
|
|
105
|
-
*
|
|
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
137
|
*/
|
|
107
|
-
export function computeConfidence({ findingType, expectation, sensors = {}, comparisons = {}, attemptMeta = {} }) {
|
|
138
|
+
export function computeConfidence({ findingType, expectation, sensors = {}, comparisons = {}, attemptMeta = {}, executionModeCeiling = 1.0 }) {
|
|
108
139
|
const boosts = [];
|
|
109
140
|
const penalties = [];
|
|
141
|
+
const contradictions = []; // GAP 5.2: Track contradictions explicitly
|
|
110
142
|
|
|
111
143
|
// Extract sensor data (with defaults for missing sensors)
|
|
112
144
|
const networkSummary = sensors.network || {};
|
|
113
145
|
const consoleSummary = sensors.console || {};
|
|
114
146
|
const uiSignals = sensors.uiSignals || {};
|
|
147
|
+
const uiFeedback = sensors.uiFeedback || {}; // GAP 5.1: UI feedback signals
|
|
115
148
|
|
|
116
149
|
// === STEP 1: DETERMINE EXPECTATION STRENGTH ===
|
|
117
150
|
const expectationStrength = determineExpectationStrength(expectation);
|
|
@@ -129,7 +162,8 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
|
|
|
129
162
|
networkSummary,
|
|
130
163
|
consoleSummary,
|
|
131
164
|
uiSignals,
|
|
132
|
-
comparisons
|
|
165
|
+
comparisons,
|
|
166
|
+
uiFeedback // GAP 5.2: Pass uiFeedback for signal extraction
|
|
133
167
|
});
|
|
134
168
|
|
|
135
169
|
// === STEP 3: SENSOR PRESENCE CHECK (STRICT - must contain data) ===
|
|
@@ -137,10 +171,20 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
|
|
|
137
171
|
const sensorsPresent = {
|
|
138
172
|
network: hasNetworkData(networkSummary),
|
|
139
173
|
console: hasConsoleData(consoleSummary),
|
|
140
|
-
ui: hasUiData(uiSignals)
|
|
174
|
+
ui: hasUiData(uiSignals),
|
|
175
|
+
uiFeedback: hasUiFeedbackData(uiFeedback) // GAP 5.2: Check UIFeedback presence
|
|
141
176
|
};
|
|
142
177
|
|
|
143
|
-
const allSensorsPresent = sensorsPresent.network && sensorsPresent.console && sensorsPresent.ui;
|
|
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
|
+
});
|
|
144
188
|
|
|
145
189
|
// === STEP 4: COMPUTE BOOSTS AND PENALTIES (TYPE-SPECIFIC) ===
|
|
146
190
|
let totalBoosts = 0;
|
|
@@ -171,6 +215,7 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
|
|
|
171
215
|
if (!sensorsPresent.network) missingSensors.push('network');
|
|
172
216
|
if (!sensorsPresent.console) missingSensors.push('console');
|
|
173
217
|
if (!sensorsPresent.ui) missingSensors.push('ui');
|
|
218
|
+
if (!sensorsPresent.uiFeedback) missingSensors.push('uiFeedback'); // GAP 5.2
|
|
174
219
|
|
|
175
220
|
const penalty = 15;
|
|
176
221
|
totalPenalties += penalty;
|
|
@@ -183,6 +228,13 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
|
|
|
183
228
|
penalties.push(`Expectation strength is ${expectationStrength}, not PROVEN`);
|
|
184
229
|
}
|
|
185
230
|
|
|
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
|
+
|
|
186
238
|
// === STEP 6: COMPUTE FINAL SCORE ===
|
|
187
239
|
let score = baseScore + totalBoosts - totalPenalties;
|
|
188
240
|
score = Math.max(0, Math.min(100, score)); // Clamp to [0, 100]
|
|
@@ -254,22 +306,49 @@ export function computeConfidence({ findingType, expectation, sensors = {}, comp
|
|
|
254
306
|
boosts,
|
|
255
307
|
penalties,
|
|
256
308
|
attemptMeta,
|
|
257
|
-
boundaryExplanation // Phase 3: Include boundary reasoning
|
|
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
|
|
258
324
|
});
|
|
259
325
|
|
|
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
|
+
|
|
260
340
|
return {
|
|
261
|
-
score:
|
|
262
|
-
|
|
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,
|
|
263
344
|
explain: finalExplain,
|
|
264
|
-
factors:
|
|
265
|
-
|
|
266
|
-
sensorsPresent,
|
|
267
|
-
evidenceSignals,
|
|
268
|
-
penalties,
|
|
269
|
-
boosts
|
|
270
|
-
},
|
|
345
|
+
factors: factors, // GAP 5.2: Structured factor breakdown
|
|
346
|
+
contradictions: contradictions, // GAP 5.2: Explicit contradictions
|
|
271
347
|
confidenceExplanation,
|
|
272
|
-
boundaryExplanation // Phase 3: Surface boundary reasoning in output
|
|
348
|
+
boundaryExplanation, // Phase 3: Surface boundary reasoning in output
|
|
349
|
+
// Expose raw boosts/penalties for testing and debugging
|
|
350
|
+
boosts: boosts,
|
|
351
|
+
penalties: penalties
|
|
273
352
|
};
|
|
274
353
|
}
|
|
275
354
|
|
|
@@ -306,16 +385,35 @@ function determineExpectationStrength(expectation = {}) {
|
|
|
306
385
|
|
|
307
386
|
/**
|
|
308
387
|
* Extract deterministic evidence signals from runtime data.
|
|
388
|
+
* GAP 5.2: Integrates Gap 5.1 UI Feedback signals with existing sensor data.
|
|
309
389
|
*/
|
|
310
|
-
function extractEvidenceSignals({ networkSummary, consoleSummary, uiSignals, comparisons }) {
|
|
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
|
+
|
|
311
395
|
const signals = {
|
|
312
396
|
urlChanged: comparisons?.hasUrlChange === true,
|
|
313
397
|
domChanged: comparisons?.hasDomChange === true,
|
|
314
398
|
screenshotChanged: comparisons?.hasVisibleChange === true,
|
|
315
399
|
networkFailed: (networkSummary?.failedRequests || 0) > 0,
|
|
400
|
+
networkSuccess: (networkSummary?.totalRequests || 0) > 0 && (networkSummary?.failedRequests || 0) === 0,
|
|
316
401
|
consoleErrors: (consoleSummary?.hasErrors === true),
|
|
317
402
|
uiFeedbackDetected: hasAnyFeedback(uiSignals),
|
|
318
|
-
slowRequests: (networkSummary?.slowRequestsCount || 0) > 0
|
|
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
|
|
319
417
|
};
|
|
320
418
|
|
|
321
419
|
return signals;
|
|
@@ -468,6 +566,36 @@ function scoreByFindingType({
|
|
|
468
566
|
penalties
|
|
469
567
|
});
|
|
470
568
|
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;
|
|
471
599
|
}
|
|
472
600
|
|
|
473
601
|
return { totalBoosts, totalPenalties };
|
|
@@ -492,10 +620,16 @@ function scoreNetworkSilentFailure({ networkSummary: _networkSummary, consoleSum
|
|
|
492
620
|
boosts.push('Console errors present');
|
|
493
621
|
}
|
|
494
622
|
|
|
495
|
-
// +
|
|
496
|
-
if (evidenceSignals.networkFailed && !evidenceSignals.
|
|
497
|
-
total +=
|
|
498
|
-
boosts.push(
|
|
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');
|
|
499
633
|
}
|
|
500
634
|
|
|
501
635
|
return total;
|
|
@@ -504,10 +638,22 @@ function scoreNetworkSilentFailure({ networkSummary: _networkSummary, consoleSum
|
|
|
504
638
|
function penalizeNetworkSilentFailure({ evidenceSignals, penalties }) {
|
|
505
639
|
let total = 0;
|
|
506
640
|
|
|
507
|
-
// -
|
|
508
|
-
if (evidenceSignals.
|
|
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) {
|
|
509
649
|
total += 10;
|
|
510
|
-
penalties.push('
|
|
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)})`);
|
|
511
657
|
}
|
|
512
658
|
|
|
513
659
|
return total;
|
|
@@ -552,10 +698,16 @@ function scoreMissingFeedbackFailure({ networkSummary: _networkSummary, evidence
|
|
|
552
698
|
boosts.push('Slow requests detected');
|
|
553
699
|
}
|
|
554
700
|
|
|
555
|
-
// +
|
|
556
|
-
if (evidenceSignals.
|
|
557
|
-
total +=
|
|
558
|
-
boosts.push('
|
|
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)');
|
|
559
711
|
}
|
|
560
712
|
|
|
561
713
|
return total;
|
|
@@ -564,12 +716,18 @@ function scoreMissingFeedbackFailure({ networkSummary: _networkSummary, evidence
|
|
|
564
716
|
function penalizeMissingFeedbackFailure({ evidenceSignals, penalties }) {
|
|
565
717
|
let total = 0;
|
|
566
718
|
|
|
567
|
-
// -
|
|
568
|
-
if (evidenceSignals.
|
|
569
|
-
total +=
|
|
719
|
+
// GAP 5.2: -12 if loading feedback detected
|
|
720
|
+
if (evidenceSignals.uiFeedbackLoading) {
|
|
721
|
+
total += 12;
|
|
570
722
|
penalties.push('Loading indicator detected');
|
|
571
723
|
}
|
|
572
724
|
|
|
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
|
+
|
|
573
731
|
return total;
|
|
574
732
|
}
|
|
575
733
|
|
|
@@ -717,13 +875,13 @@ function penalizeNavigationSilentFailure({ evidenceSignals, penalties }) {
|
|
|
717
875
|
|
|
718
876
|
// -10 if UI feedback present (shouldn't be silent failure)
|
|
719
877
|
if (evidenceSignals.uiFeedbackDetected) {
|
|
720
|
-
total
|
|
878
|
+
total -= 10;
|
|
721
879
|
penalties.push('UI feedback detected (suggests navigation feedback provided)');
|
|
722
880
|
}
|
|
723
881
|
|
|
724
882
|
// -5 if URL changed (navigation might have succeeded)
|
|
725
883
|
if (evidenceSignals.urlChanged) {
|
|
726
|
-
total
|
|
884
|
+
total -= 5;
|
|
727
885
|
penalties.push('URL changed (navigation may have succeeded)');
|
|
728
886
|
}
|
|
729
887
|
|
|
@@ -753,7 +911,7 @@ function penalizePartialNavigationFailure({ evidenceSignals, penalties }) {
|
|
|
753
911
|
|
|
754
912
|
// -10 if UI feedback present (shouldn't be partial failure)
|
|
755
913
|
if (evidenceSignals.uiFeedbackDetected) {
|
|
756
|
-
total
|
|
914
|
+
total -= 10;
|
|
757
915
|
penalties.push('UI feedback detected (suggests navigation feedback provided)');
|
|
758
916
|
}
|
|
759
917
|
|
|
@@ -799,6 +957,7 @@ function generateExplanations(boosts, penalties, expectationStrength, _evidenceS
|
|
|
799
957
|
* Generate confidence explanation for Phase 9: Reality Confidence & Explanation Layer.
|
|
800
958
|
* Provides whyThisConfidence, whatWouldIncreaseConfidence, whatWouldReduceConfidence.
|
|
801
959
|
* Phase 3: Also includes boundaryExplanation for near-threshold decisions.
|
|
960
|
+
* GAP 5.2: Includes contradiction handling.
|
|
802
961
|
*/
|
|
803
962
|
function generateConfidenceExplanation({
|
|
804
963
|
level,
|
|
@@ -810,7 +969,8 @@ function generateConfidenceExplanation({
|
|
|
810
969
|
boosts,
|
|
811
970
|
penalties,
|
|
812
971
|
attemptMeta,
|
|
813
|
-
boundaryExplanation = null // Phase 3: Optional boundary reasoning
|
|
972
|
+
boundaryExplanation = null, // Phase 3: Optional boundary reasoning
|
|
973
|
+
contradictions = [] // GAP 5.2: Contradiction list
|
|
814
974
|
}) {
|
|
815
975
|
const whyThisConfidence = [];
|
|
816
976
|
const whatWouldIncreaseConfidence = [];
|
|
@@ -821,6 +981,19 @@ function generateConfidenceExplanation({
|
|
|
821
981
|
whyThisConfidence.push(boundaryExplanation);
|
|
822
982
|
}
|
|
823
983
|
|
|
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
|
+
|
|
824
997
|
// WHY THIS CONFIDENCE: Explain current level
|
|
825
998
|
if (level === 'HIGH') {
|
|
826
999
|
whyThisConfidence.push('High confidence: expectation is proven and all sensors captured evidence');
|
|
@@ -828,7 +1001,7 @@ function generateConfidenceExplanation({
|
|
|
828
1001
|
whyThisConfidence.push('Expectation is proven from source code');
|
|
829
1002
|
}
|
|
830
1003
|
if (allSensorsPresent) {
|
|
831
|
-
whyThisConfidence.push('All sensors (network, console, UI) were active');
|
|
1004
|
+
whyThisConfidence.push('All sensors (network, console, UI, UIFeedback) were active');
|
|
832
1005
|
}
|
|
833
1006
|
if (boosts.length > 0) {
|
|
834
1007
|
whyThisConfidence.push(`Strong evidence: ${boosts.length} positive signal(s)`);
|
|
@@ -845,6 +1018,7 @@ function generateConfidenceExplanation({
|
|
|
845
1018
|
if (!sensorsPresent.network) missing.push('network');
|
|
846
1019
|
if (!sensorsPresent.console) missing.push('console');
|
|
847
1020
|
if (!sensorsPresent.ui) missing.push('UI');
|
|
1021
|
+
if (!sensorsPresent.uiFeedback) missing.push('UIFeedback');
|
|
848
1022
|
whyThisConfidence.push(`Missing sensor data: ${missing.join(', ')}`);
|
|
849
1023
|
}
|
|
850
1024
|
if (penalties.length > 0) {
|
|
@@ -873,6 +1047,7 @@ function generateConfidenceExplanation({
|
|
|
873
1047
|
if (!sensorsPresent.network) missing.push('network monitoring');
|
|
874
1048
|
if (!sensorsPresent.console) missing.push('console error detection');
|
|
875
1049
|
if (!sensorsPresent.ui) missing.push('UI change detection');
|
|
1050
|
+
if (!sensorsPresent.uiFeedback) missing.push('UI feedback detection');
|
|
876
1051
|
whatWouldIncreaseConfidence.push(`Enable missing sensors: ${missing.join(', ')}`);
|
|
877
1052
|
}
|
|
878
1053
|
if (attemptMeta && !attemptMeta.repeated && level === 'LOW') {
|
|
@@ -881,6 +1056,9 @@ function generateConfidenceExplanation({
|
|
|
881
1056
|
if (boosts.length === 0) {
|
|
882
1057
|
whatWouldIncreaseConfidence.push('Add stronger evidence signals (network requests, console errors, UI changes)');
|
|
883
1058
|
}
|
|
1059
|
+
if (contradictions.length > 0) {
|
|
1060
|
+
whatWouldIncreaseConfidence.push('Resolve contradictions by clarifying expected behavior or fixing detection logic');
|
|
1061
|
+
}
|
|
884
1062
|
}
|
|
885
1063
|
|
|
886
1064
|
// WHAT WOULD REDUCE CONFIDENCE
|
|
@@ -894,6 +1072,9 @@ function generateConfidenceExplanation({
|
|
|
894
1072
|
if (boosts.length > 0) {
|
|
895
1073
|
whatWouldReduceConfidence.push('If positive evidence signals disappear (network succeeds, UI feedback appears)');
|
|
896
1074
|
}
|
|
1075
|
+
if (contradictions.length === 0) {
|
|
1076
|
+
whatWouldReduceConfidence.push('If contradictory evidence appears (mixed signals, conflicting feedback)');
|
|
1077
|
+
}
|
|
897
1078
|
}
|
|
898
1079
|
if (penalties.length === 0 && level === 'HIGH') {
|
|
899
1080
|
whatWouldReduceConfidence.push('If uncertainty factors appear (URL changes, partial effects, missing data)');
|
|
@@ -911,7 +1092,7 @@ function generateConfidenceExplanation({
|
|
|
911
1092
|
// ============================================================
|
|
912
1093
|
|
|
913
1094
|
// PHASE 3: Export sensor validation functions for testing
|
|
914
|
-
export { hasNetworkData, hasConsoleData, hasUiData };
|
|
1095
|
+
export { hasNetworkData, hasConsoleData, hasUiData, hasUiFeedbackData };
|
|
915
1096
|
|
|
916
1097
|
// Detect error feedback (legacy helper)
|
|
917
1098
|
function _detectErrorFeedback(uiSignals) {
|
|
@@ -931,3 +1112,410 @@ function _detectStatusFeedback(uiSignals) {
|
|
|
931
1112
|
const after = uiSignals?.after || {};
|
|
932
1113
|
return after.hasStatusSignal || after.hasLiveRegion || after.hasDialog;
|
|
933
1114
|
}
|
|
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
|
+
}
|