@veraxhq/verax 0.2.0 → 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 +15 -5
- package/src/cli/commands/baseline.js +104 -0
- package/src/cli/commands/default.js +323 -111
- package/src/cli/commands/doctor.js +36 -4
- 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 +498 -103
- package/src/cli/commands/security-check.js +211 -0
- package/src/cli/commands/truth.js +114 -0
- package/src/cli/entry.js +305 -68
- 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/detection-engine.js +4 -3
- 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/events.js +76 -0
- package/src/cli/util/expectation-extractor.js +380 -74
- package/src/cli/util/findings-writer.js +126 -15
- package/src/cli/util/learn-writer.js +3 -1
- package/src/cli/util/observation-engine.js +69 -23
- package/src/cli/util/observe-writer.js +3 -1
- package/src/cli/util/paths.js +6 -14
- package/src/cli/util/project-discovery.js +23 -0
- package/src/cli/util/project-writer.js +3 -1
- package/src/cli/util/redact.js +2 -2
- package/src/cli/util/run-resolver.js +64 -0
- package/src/cli/util/runtime-budget.js +147 -0
- package/src/cli/util/source-requirement.js +55 -0
- package/src/cli/util/summary-writer.js +13 -1
- 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/types/global.d.ts +28 -0
- package/src/types/ts-ast.d.ts +24 -0
- package/src/verax/cli/doctor.js +2 -2
- package/src/verax/cli/finding-explainer.js +56 -3
- package/src/verax/cli/init.js +1 -1
- package/src/verax/cli/url-safety.js +12 -2
- package/src/verax/cli/wizard.js +13 -2
- 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/budget-engine.js +1 -1
- 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 +31 -4
- 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/determinism-model.js +35 -6
- 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/incremental-store.js +15 -7
- 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/replay-validator.js +4 -4
- package/src/verax/core/replay.js +1 -1
- 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/silence-impact.js +1 -1
- package/src/verax/core/silence-model.js +9 -7
- package/src/verax/core/truth/truth.certificate.js +250 -0
- package/src/verax/core/ui-feedback-intelligence.js +515 -0
- package/src/verax/detect/comparison.js +8 -3
- package/src/verax/detect/confidence-engine.js +645 -57
- package/src/verax/detect/confidence-helper.js +33 -0
- package/src/verax/detect/detection-engine.js +19 -2
- package/src/verax/detect/dynamic-route-findings.js +335 -0
- package/src/verax/detect/evidence-index.js +15 -65
- package/src/verax/detect/expectation-chain-detector.js +417 -0
- package/src/verax/detect/expectation-model.js +56 -3
- package/src/verax/detect/explanation-helpers.js +1 -1
- package/src/verax/detect/finding-detector.js +2 -2
- package/src/verax/detect/findings-writer.js +149 -20
- package/src/verax/detect/flow-detector.js +4 -4
- package/src/verax/detect/index.js +265 -15
- package/src/verax/detect/interactive-findings.js +3 -4
- package/src/verax/detect/journey-stall-detector.js +558 -0
- package/src/verax/detect/route-findings.js +218 -0
- package/src/verax/detect/signal-mapper.js +2 -2
- package/src/verax/detect/skip-classifier.js +4 -4
- package/src/verax/detect/ui-feedback-findings.js +207 -0
- package/src/verax/detect/verdict-engine.js +61 -9
- package/src/verax/detect/view-switch-correlator.js +242 -0
- package/src/verax/flow/flow-engine.js +3 -2
- package/src/verax/flow/flow-spec.js +1 -2
- package/src/verax/index.js +413 -33
- package/src/verax/intel/effect-detector.js +1 -1
- package/src/verax/intel/index.js +2 -2
- package/src/verax/intel/route-extractor.js +3 -3
- package/src/verax/intel/vue-navigation-extractor.js +81 -18
- package/src/verax/intel/vue-router-extractor.js +4 -2
- package/src/verax/learn/action-contract-extractor.js +684 -66
- package/src/verax/learn/ast-contract-extractor.js +53 -1
- package/src/verax/learn/index.js +36 -2
- package/src/verax/learn/manifest-writer.js +28 -14
- package/src/verax/learn/route-extractor.js +1 -1
- package/src/verax/learn/route-validator.js +12 -8
- package/src/verax/learn/state-extractor.js +1 -1
- package/src/verax/learn/static-extractor-navigation.js +1 -1
- package/src/verax/learn/static-extractor-validation.js +2 -2
- package/src/verax/learn/static-extractor.js +8 -7
- package/src/verax/learn/ts-contract-resolver.js +14 -12
- package/src/verax/observe/browser.js +22 -3
- package/src/verax/observe/console-sensor.js +2 -2
- package/src/verax/observe/expectation-executor.js +2 -1
- package/src/verax/observe/focus-sensor.js +1 -1
- package/src/verax/observe/human-driver.js +29 -10
- package/src/verax/observe/index.js +92 -844
- package/src/verax/observe/interaction-discovery.js +27 -15
- package/src/verax/observe/interaction-runner.js +31 -14
- package/src/verax/observe/loading-sensor.js +6 -0
- package/src/verax/observe/navigation-sensor.js +1 -1
- 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/settle.js +1 -0
- package/src/verax/observe/state-sensor.js +8 -4
- package/src/verax/observe/state-ui-sensor.js +7 -1
- package/src/verax/observe/traces-writer.js +27 -16
- package/src/verax/observe/ui-feedback-detector.js +742 -0
- package/src/verax/observe/ui-signal-sensor.js +155 -2
- package/src/verax/scan-summary-writer.js +46 -9
- package/src/verax/shared/artifact-manager.js +9 -6
- package/src/verax/shared/budget-profiles.js +2 -2
- package/src/verax/shared/caching.js +1 -1
- package/src/verax/shared/config-loader.js +1 -2
- package/src/verax/shared/css-spinner-rules.js +204 -0
- package/src/verax/shared/dynamic-route-utils.js +12 -6
- package/src/verax/shared/retry-policy.js +1 -6
- package/src/verax/shared/root-artifacts.js +1 -1
- package/src/verax/shared/view-switch-rules.js +208 -0
- package/src/verax/shared/zip-artifacts.js +1 -0
- package/src/verax/validate/context-validator.js +1 -1
- package/src/verax/observe/index.js.backup +0 -1
- package/src/verax/validate/context-validator.js.bak +0 -0
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { resolve
|
|
1
|
+
import { resolve } from 'path';
|
|
2
2
|
import { mkdirSync, writeFileSync } from 'fs';
|
|
3
|
-
import { getArtifactPath } from '../core/run-id.js';
|
|
4
3
|
import { CANONICAL_OUTCOMES } from '../core/canonical-outcomes.js';
|
|
4
|
+
import { enforceContractsOnFindings, FINDING_STATUS } from '../core/contracts/index.js';
|
|
5
|
+
import { ARTIFACT_REGISTRY, getArtifactVersions } from '../core/artifacts/registry.js';
|
|
6
|
+
import { buildAndEnforceEvidencePackage, EvidenceBuildError, validateEvidencePackageStrict } from '../core/evidence-builder.js';
|
|
7
|
+
import { writeEvidenceIntentLedger } from '../core/evidence/evidence-intent-ledger.js';
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
|
-
* Write findings to canonical artifact root.
|
|
8
|
-
*
|
|
10
|
+
* Write findings to canonical artifact root.
|
|
11
|
+
* Writes to .verax/runs/<runId>/findings.json.
|
|
12
|
+
*
|
|
13
|
+
* PHASE 0 ENFORCEMENT: Applies contracts enforcement
|
|
14
|
+
* - Downgrades findings without evidence from CONFIRMED to SUSPECTED
|
|
15
|
+
* - Drops findings that violate critical contracts
|
|
9
16
|
*
|
|
10
17
|
* PHASE 2: Includes outcome classification summary.
|
|
11
18
|
* PHASE 3: Includes promise type summary.
|
|
@@ -14,22 +21,119 @@ import { CANONICAL_OUTCOMES } from '../core/canonical-outcomes.js';
|
|
|
14
21
|
* @param {string} url
|
|
15
22
|
* @param {Array} findings
|
|
16
23
|
* @param {Array} coverageGaps
|
|
17
|
-
* @param {string
|
|
24
|
+
* @param {string} runDirOpt - Required absolute run directory path
|
|
25
|
+
* @returns {Object} findings report with enforcement metadata
|
|
18
26
|
*/
|
|
19
|
-
export function writeFindings(projectDir, url, findings, coverageGaps = [], runDirOpt
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Canonical location
|
|
23
|
-
mkdirSync(runDirOpt, { recursive: true });
|
|
24
|
-
findingsPath = resolve(runDirOpt, 'findings.json');
|
|
25
|
-
} else {
|
|
26
|
-
// Legacy location for backward compatibility (tests without runId)
|
|
27
|
-
const detectDir = resolve(projectDir, '.veraxverax', 'detect');
|
|
28
|
-
mkdirSync(detectDir, { recursive: true });
|
|
29
|
-
findingsPath = resolve(detectDir, 'findings.json');
|
|
27
|
+
export function writeFindings(projectDir, url, findings, coverageGaps = [], runDirOpt) {
|
|
28
|
+
if (!runDirOpt) {
|
|
29
|
+
throw new Error('runDirOpt is required');
|
|
30
30
|
}
|
|
31
|
+
mkdirSync(runDirOpt, { recursive: true });
|
|
32
|
+
const findingsPath = resolve(runDirOpt, ARTIFACT_REGISTRY.findings.filename);
|
|
31
33
|
|
|
32
|
-
//
|
|
34
|
+
// ARCHITECTURAL HARDENING: Evidence capture failures downgrade findings, never drop them
|
|
35
|
+
// A finding must NEVER disappear. If evidence capture fails, downgrade explicitly.
|
|
36
|
+
const findingsWithEvidence = [];
|
|
37
|
+
const evidenceBuildFailures = [];
|
|
38
|
+
|
|
39
|
+
for (const finding of (findings || [])) {
|
|
40
|
+
// If evidencePackage already exists, validate it strictly for CONFIRMED findings
|
|
41
|
+
if (finding.evidencePackage) {
|
|
42
|
+
const severity = finding.severity || finding.status || 'SUSPECTED';
|
|
43
|
+
if (severity === 'CONFIRMED') {
|
|
44
|
+
try {
|
|
45
|
+
validateEvidencePackageStrict(finding.evidencePackage, severity);
|
|
46
|
+
findingsWithEvidence.push(finding);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
// CONFIRMED finding with incomplete evidencePackage → DOWNGRADE to SUSPECTED
|
|
49
|
+
const downgradedFinding = {
|
|
50
|
+
...finding,
|
|
51
|
+
severity: 'SUSPECTED',
|
|
52
|
+
status: 'SUSPECTED',
|
|
53
|
+
evidenceCompleteness: {
|
|
54
|
+
downgraded: true,
|
|
55
|
+
reason: `Evidence package incomplete: ${error.message}`,
|
|
56
|
+
originalSeverity: 'CONFIRMED'
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
findingsWithEvidence.push(downgradedFinding);
|
|
60
|
+
evidenceBuildFailures.push({
|
|
61
|
+
finding: { type: finding.type || 'unknown', id: finding.findingId || finding.id },
|
|
62
|
+
reason: `EVIDENCE_VALIDATION_FAILED: ${error.message}`,
|
|
63
|
+
errorCode: error.code || 'EVIDENCE_VALIDATION_FAILED',
|
|
64
|
+
action: 'DOWNGRADED'
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
findingsWithEvidence.push(finding);
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Build evidence package from finding data
|
|
74
|
+
// If building fails → DOWNGRADE finding (never drop)
|
|
75
|
+
try {
|
|
76
|
+
const findingWithEvidence = buildAndEnforceEvidencePackage(finding, {
|
|
77
|
+
expectation: finding.expectation || null,
|
|
78
|
+
trace: {
|
|
79
|
+
interaction: finding.interaction || {},
|
|
80
|
+
before: finding.evidence?.before ? { screenshot: finding.evidence.before, url: finding.evidence.beforeUrl } : null,
|
|
81
|
+
after: finding.evidence?.after ? { screenshot: finding.evidence.after, url: finding.evidence.afterUrl } : null,
|
|
82
|
+
sensors: finding.evidence?.sensors || {},
|
|
83
|
+
dom: finding.evidence?.dom || {},
|
|
84
|
+
},
|
|
85
|
+
evidence: finding.evidence || {},
|
|
86
|
+
confidence: finding.confidenceLevel ? {
|
|
87
|
+
level: finding.confidenceLevel,
|
|
88
|
+
score: finding.confidence,
|
|
89
|
+
reasons: finding.confidenceReasons || [],
|
|
90
|
+
} : null,
|
|
91
|
+
});
|
|
92
|
+
findingsWithEvidence.push(findingWithEvidence);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
// Evidence building failure → DOWNGRADE finding (never drop)
|
|
95
|
+
const originalSeverity = finding.severity || finding.status || 'SUSPECTED';
|
|
96
|
+
const downgradedFinding = {
|
|
97
|
+
...finding,
|
|
98
|
+
severity: 'SUSPECTED',
|
|
99
|
+
status: 'SUSPECTED',
|
|
100
|
+
evidenceCompleteness: {
|
|
101
|
+
downgraded: true,
|
|
102
|
+
reason: `Evidence capture failed: ${error.message}`,
|
|
103
|
+
originalSeverity: originalSeverity,
|
|
104
|
+
errorCode: error.code || 'EVIDENCE_BUILD_FAILED',
|
|
105
|
+
missingFields: error.missingFields || []
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
findingsWithEvidence.push(downgradedFinding);
|
|
109
|
+
|
|
110
|
+
if (error instanceof EvidenceBuildError) {
|
|
111
|
+
evidenceBuildFailures.push({
|
|
112
|
+
finding: { type: finding.type || 'unknown', id: finding.findingId || finding.id },
|
|
113
|
+
reason: `EVIDENCE_BUILD_FAILED: ${error.message}`,
|
|
114
|
+
errorCode: error.code || 'EVIDENCE_BUILD_FAILED',
|
|
115
|
+
missingFields: error.missingFields || [],
|
|
116
|
+
action: 'DOWNGRADED'
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
evidenceBuildFailures.push({
|
|
120
|
+
finding: { type: finding.type || 'unknown', id: finding.findingId || finding.id },
|
|
121
|
+
reason: `EVIDENCE_BUILD_FAILED: Unexpected error: ${error.message}`,
|
|
122
|
+
errorCode: 'EVIDENCE_BUILD_FAILED',
|
|
123
|
+
action: 'DOWNGRADED'
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// PHASE 0: Enforce contracts on all findings (Evidence Law)
|
|
130
|
+
const { valid: enforcedFindings, dropped, downgrades } = enforceContractsOnFindings(findingsWithEvidence);
|
|
131
|
+
|
|
132
|
+
// ARCHITECTURAL HARDENING: Track evidence build failures separately (they result in downgrades, not drops)
|
|
133
|
+
// Only contract violations result in drops
|
|
134
|
+
const allDropped = dropped;
|
|
135
|
+
|
|
136
|
+
// PHASE 2: Compute outcome summary (using enforced findings)
|
|
33
137
|
const outcomeSummary = {};
|
|
34
138
|
Object.values(CANONICAL_OUTCOMES).forEach(outcome => {
|
|
35
139
|
outcomeSummary[outcome] = 0;
|
|
@@ -38,7 +142,7 @@ export function writeFindings(projectDir, url, findings, coverageGaps = [], runD
|
|
|
38
142
|
// PHASE 3: Compute promise summary
|
|
39
143
|
const promiseSummary = {};
|
|
40
144
|
|
|
41
|
-
for (const finding of
|
|
145
|
+
for (const finding of enforcedFindings) {
|
|
42
146
|
const outcome = finding.outcome || CANONICAL_OUTCOMES.SILENT_FAILURE;
|
|
43
147
|
outcomeSummary[outcome] = (outcomeSummary[outcome] || 0) + 1;
|
|
44
148
|
|
|
@@ -48,20 +152,45 @@ export function writeFindings(projectDir, url, findings, coverageGaps = [], runD
|
|
|
48
152
|
|
|
49
153
|
const findingsReport = {
|
|
50
154
|
version: 1,
|
|
155
|
+
contractVersion: 1, // Track schema changes from contracts enforcement
|
|
156
|
+
artifactVersions: getArtifactVersions(),
|
|
51
157
|
detectedAt: new Date().toISOString(),
|
|
52
158
|
url: url,
|
|
53
159
|
outcomeSummary: outcomeSummary, // PHASE 2
|
|
54
160
|
promiseSummary: promiseSummary, // PHASE 3
|
|
55
|
-
findings:
|
|
161
|
+
findings: enforcedFindings,
|
|
56
162
|
coverageGaps: coverageGaps,
|
|
163
|
+
// PHASE 0: Enforcement metadata
|
|
164
|
+
// PHASE 21.1: Include evidence build failures in dropped count
|
|
165
|
+
enforcement: {
|
|
166
|
+
droppedCount: allDropped.length,
|
|
167
|
+
downgradedCount: downgrades.length,
|
|
168
|
+
downgrades: downgrades.map(d => ({
|
|
169
|
+
reason: d.reason,
|
|
170
|
+
originalStatus: d.original.status,
|
|
171
|
+
downgradeToStatus: d.downgraded.status
|
|
172
|
+
})),
|
|
173
|
+
evidenceBuildFailures: evidenceBuildFailures.map(f => ({
|
|
174
|
+
reason: f.reason,
|
|
175
|
+
errorCode: f.errorCode,
|
|
176
|
+
findingType: f.finding.type || 'unknown',
|
|
177
|
+
findingId: f.finding.id || null,
|
|
178
|
+
missingFields: f.missingFields || [],
|
|
179
|
+
action: f.action || 'DOWNGRADED'
|
|
180
|
+
}))
|
|
181
|
+
},
|
|
57
182
|
notes: []
|
|
58
183
|
};
|
|
59
184
|
|
|
60
185
|
writeFileSync(findingsPath, JSON.stringify(findingsReport, null, 2) + '\n');
|
|
61
186
|
|
|
187
|
+
// PHASE 22: Write evidence intent ledger
|
|
188
|
+
const evidenceIntentPath = writeEvidenceIntentLedger(runDirOpt, enforcedFindings, captureFailuresMap);
|
|
189
|
+
|
|
62
190
|
return {
|
|
63
191
|
...findingsReport,
|
|
64
|
-
findingsPath: findingsPath
|
|
192
|
+
findingsPath: findingsPath,
|
|
193
|
+
evidenceIntentPath: evidenceIntentPath
|
|
65
194
|
};
|
|
66
195
|
}
|
|
67
196
|
|
|
@@ -133,9 +133,9 @@ export function detectFlowSilentFailures(traces, manifest, findings, coverageGap
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// Look for silent failures followed by lack of recovery
|
|
136
|
-
|
|
136
|
+
const _hasSilentFailure = false;
|
|
137
137
|
let failedStepIndex = -1;
|
|
138
|
-
|
|
138
|
+
const _failedExpectation = null;
|
|
139
139
|
|
|
140
140
|
for (let i = 0; i < flowTraces.length; i++) {
|
|
141
141
|
const trace = flowTraces[i];
|
|
@@ -248,9 +248,9 @@ export function detectFlowSilentFailures(traces, manifest, findings, coverageGap
|
|
|
248
248
|
|
|
249
249
|
if (isSilentFailure && matchedExpectation) {
|
|
250
250
|
// Silent failure detected at this step
|
|
251
|
-
|
|
251
|
+
const _hasSilentFailure = true;
|
|
252
252
|
failedStepIndex = i;
|
|
253
|
-
|
|
253
|
+
const _failedExpectation = matchedExpectation;
|
|
254
254
|
|
|
255
255
|
// Check if subsequent steps show UI recovery
|
|
256
256
|
let hasSubsequentRecovery = false;
|
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'fs';
|
|
2
|
-
import { dirname } from 'path';
|
|
2
|
+
import { dirname, basename, join } from 'path';
|
|
3
3
|
import { expectsNavigation } from './expectation-model.js';
|
|
4
4
|
import { hasMeaningfulUrlChange, hasVisibleChange, hasDomChange } from './comparison.js';
|
|
5
5
|
import { writeFindings } from './findings-writer.js';
|
|
6
6
|
import { getUrlPath } from './evidence-validator.js';
|
|
7
7
|
import { classifySkipReason, collectSkipReasons } from './skip-classifier.js';
|
|
8
8
|
import { detectInteractiveFindings } from './interactive-findings.js';
|
|
9
|
+
import { detectRouteFindings } from './route-findings.js';
|
|
10
|
+
import { detectUIFeedbackFindings } from './ui-feedback-findings.js';
|
|
11
|
+
import { detectDynamicRouteFindings } from './dynamic-route-findings.js';
|
|
12
|
+
import { addUnifiedConfidence } from './confidence-helper.js';
|
|
13
|
+
import { applyGuardrails } from '../core/guardrails-engine.js';
|
|
14
|
+
import { finalizeFindingTruth } from '../core/guardrails/truth-reconciliation.js';
|
|
15
|
+
import { writeGuardrailsReport } from '../core/guardrails/guardrails-report-writer.js';
|
|
16
|
+
import { computeFinalConfidence } from '../core/confidence/confidence-compute.js';
|
|
17
|
+
import { enforceConfidenceInvariants } from '../core/confidence/confidence-invariants.js';
|
|
18
|
+
import { writeConfidenceReport } from '../core/confidence/confidence-report-writer.js';
|
|
9
19
|
|
|
10
|
-
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} manifestPath
|
|
22
|
+
* @param {string} tracesPath
|
|
23
|
+
* @param {Object} [validation]
|
|
24
|
+
* @returns {Promise<any>}
|
|
25
|
+
*/
|
|
26
|
+
export async function detect(manifestPath, tracesPath, validation = null, _expectationCoverageGaps = null, _silenceTracker = null) {
|
|
11
27
|
if (!existsSync(manifestPath)) {
|
|
12
28
|
throw new Error(`Manifest not found: ${manifestPath}`);
|
|
13
29
|
}
|
|
@@ -25,8 +41,21 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
25
41
|
const projectDir = manifest.projectDir;
|
|
26
42
|
const findings = [];
|
|
27
43
|
|
|
44
|
+
// Extract runId from tracesPath: .verax/runs/<runId>/observation-traces.json
|
|
45
|
+
let runId = null;
|
|
46
|
+
try {
|
|
47
|
+
const runDir = dirname(tracesPath);
|
|
48
|
+
const runDirBasename = basename(runDir);
|
|
49
|
+
// Check if runDir is in .verax/runs/<runId> structure
|
|
50
|
+
const parentDir = dirname(runDir);
|
|
51
|
+
if (basename(parentDir) === 'runs' && basename(dirname(parentDir)) === '.verax') {
|
|
52
|
+
runId = runDirBasename;
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
// Ignore path parsing errors
|
|
56
|
+
}
|
|
57
|
+
|
|
28
58
|
let interactionsAnalyzed = 0;
|
|
29
|
-
let interactionsSkippedNoExpectation = 0;
|
|
30
59
|
const skips = [];
|
|
31
60
|
|
|
32
61
|
for (const trace of observation.traces) {
|
|
@@ -39,7 +68,6 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
39
68
|
const expectsNav = expectsNavigation(manifest, interaction, beforeUrl);
|
|
40
69
|
|
|
41
70
|
if (!expectsNav) {
|
|
42
|
-
interactionsSkippedNoExpectation++;
|
|
43
71
|
const skipReason = classifySkipReason(manifest, interaction, beforeUrl, validation);
|
|
44
72
|
if (skipReason) {
|
|
45
73
|
skips.push({
|
|
@@ -73,8 +101,8 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
73
101
|
const interactionSelector = interaction.selector || '';
|
|
74
102
|
|
|
75
103
|
if (selectorHint && interactionSelector) {
|
|
76
|
-
const normalizedSelectorHint = selectorHint.replace(/[
|
|
77
|
-
const normalizedInteractionSelector = interactionSelector.replace(/[
|
|
104
|
+
const normalizedSelectorHint = selectorHint.replace(/[[\]()]/g, '');
|
|
105
|
+
const normalizedInteractionSelector = interactionSelector.replace(/[[\]()]/g, '');
|
|
78
106
|
|
|
79
107
|
if (selectorHint === interactionSelector ||
|
|
80
108
|
selectorHint.includes(interactionSelector) ||
|
|
@@ -117,7 +145,6 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
117
145
|
label: interaction.label
|
|
118
146
|
}
|
|
119
147
|
});
|
|
120
|
-
interactionsSkippedNoExpectation++;
|
|
121
148
|
continue;
|
|
122
149
|
}
|
|
123
150
|
}
|
|
@@ -161,7 +188,6 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
161
188
|
label: interaction.label
|
|
162
189
|
}
|
|
163
190
|
});
|
|
164
|
-
interactionsSkippedNoExpectation++;
|
|
165
191
|
continue;
|
|
166
192
|
} else if (matchingRoutes.length === 1) {
|
|
167
193
|
expectedTargetPath = matchingRoutes[0];
|
|
@@ -180,14 +206,22 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
180
206
|
label: interaction.label
|
|
181
207
|
}
|
|
182
208
|
});
|
|
183
|
-
interactionsSkippedNoExpectation++;
|
|
184
209
|
continue;
|
|
185
210
|
}
|
|
186
211
|
|
|
187
212
|
interactionsAnalyzed++;
|
|
188
213
|
|
|
189
214
|
const hasUrlChange = hasMeaningfulUrlChange(beforeUrl, afterUrl);
|
|
190
|
-
|
|
215
|
+
// hasVisibleChange requires runId, skip comparison if runId unavailable
|
|
216
|
+
let hasVisibleChangeResult = false;
|
|
217
|
+
if (runId) {
|
|
218
|
+
try {
|
|
219
|
+
hasVisibleChangeResult = hasVisibleChange(beforeScreenshot, afterScreenshot, projectDir, runId);
|
|
220
|
+
} catch (e) {
|
|
221
|
+
// If screenshot comparison fails, treat as no visible change
|
|
222
|
+
hasVisibleChangeResult = false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
191
225
|
const hasDomChangeResult = hasDomChange(trace);
|
|
192
226
|
|
|
193
227
|
if (expectedTargetPath) {
|
|
@@ -197,7 +231,7 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
197
231
|
|
|
198
232
|
if (expectationType === 'form_submission') {
|
|
199
233
|
if (normalizedAfter !== normalizedTarget && !hasUrlChange && !hasDomChangeResult) {
|
|
200
|
-
|
|
234
|
+
const finding = {
|
|
201
235
|
type: 'silent_failure',
|
|
202
236
|
interaction: {
|
|
203
237
|
type: interaction.type,
|
|
@@ -211,7 +245,27 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
211
245
|
beforeUrl: beforeUrl,
|
|
212
246
|
afterUrl: afterUrl
|
|
213
247
|
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// PHASE 15: Add unified confidence
|
|
251
|
+
const findingWithConfidence = addUnifiedConfidence(finding, {
|
|
252
|
+
expectation: { targetPath: expectedTargetPath, type: expectationType },
|
|
253
|
+
sensors: trace.sensors || {},
|
|
254
|
+
comparisons: {
|
|
255
|
+
urlChanged: hasUrlChange,
|
|
256
|
+
domChanged: hasDomChangeResult,
|
|
257
|
+
},
|
|
258
|
+
evidence: {
|
|
259
|
+
beforeAfter: {
|
|
260
|
+
beforeScreenshot,
|
|
261
|
+
afterScreenshot,
|
|
262
|
+
beforeUrl,
|
|
263
|
+
afterUrl,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
214
266
|
});
|
|
267
|
+
|
|
268
|
+
findings.push(findingWithConfidence);
|
|
215
269
|
}
|
|
216
270
|
} else if (expectationType === 'navigation') {
|
|
217
271
|
const urlMatchesTarget = normalizedAfter === normalizedTarget;
|
|
@@ -237,7 +291,7 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
237
291
|
}
|
|
238
292
|
} else {
|
|
239
293
|
if (!hasUrlChange && !hasVisibleChangeResult && !hasDomChangeResult) {
|
|
240
|
-
|
|
294
|
+
const finding = {
|
|
241
295
|
type: 'silent_failure',
|
|
242
296
|
interaction: {
|
|
243
297
|
type: interaction.type,
|
|
@@ -251,7 +305,28 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
251
305
|
beforeUrl: beforeUrl,
|
|
252
306
|
afterUrl: afterUrl
|
|
253
307
|
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// PHASE 15: Add unified confidence
|
|
311
|
+
const findingWithConfidence = addUnifiedConfidence(finding, {
|
|
312
|
+
expectation: null,
|
|
313
|
+
sensors: trace.sensors || {},
|
|
314
|
+
comparisons: {
|
|
315
|
+
urlChanged: hasUrlChange,
|
|
316
|
+
domChanged: hasDomChangeResult,
|
|
317
|
+
visibleChanged: hasVisibleChangeResult,
|
|
318
|
+
},
|
|
319
|
+
evidence: {
|
|
320
|
+
beforeAfter: {
|
|
321
|
+
beforeScreenshot,
|
|
322
|
+
afterScreenshot,
|
|
323
|
+
beforeUrl,
|
|
324
|
+
afterUrl,
|
|
325
|
+
},
|
|
326
|
+
},
|
|
254
327
|
});
|
|
328
|
+
|
|
329
|
+
findings.push(findingWithConfidence);
|
|
255
330
|
}
|
|
256
331
|
}
|
|
257
332
|
}
|
|
@@ -259,14 +334,187 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
259
334
|
// Interactive and accessibility intelligence
|
|
260
335
|
detectInteractiveFindings(observation.traces, manifest, findings);
|
|
261
336
|
|
|
262
|
-
//
|
|
337
|
+
// PHASE 12: Route intelligence findings
|
|
338
|
+
const routeFindings = detectRouteFindings(observation.traces, manifest, findings);
|
|
339
|
+
findings.push(...routeFindings);
|
|
340
|
+
|
|
341
|
+
// PHASE 13: UI feedback findings
|
|
342
|
+
const uiFeedbackFindings = detectUIFeedbackFindings(observation.traces, manifest, findings);
|
|
343
|
+
findings.push(...uiFeedbackFindings);
|
|
344
|
+
|
|
345
|
+
// PHASE 14: Dynamic route findings
|
|
346
|
+
const dynamicRouteResult = detectDynamicRouteFindings(observation.traces, manifest, findings);
|
|
347
|
+
findings.push(...dynamicRouteResult.findings);
|
|
348
|
+
// Note: skips are handled separately and should be included in skip summary
|
|
349
|
+
|
|
350
|
+
// PHASE 23: Apply guardrails + truth reconciliation (AFTER evidence builder, BEFORE writing artifacts)
|
|
351
|
+
const guardrailsSummary = {
|
|
352
|
+
totalFindingsProcessed: 0,
|
|
353
|
+
preventedConfirmedCount: 0,
|
|
354
|
+
downgradedCount: 0,
|
|
355
|
+
informationalCount: 0,
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const truthDecisions = {}; // Map of findingIdentity -> truthDecision
|
|
359
|
+
|
|
360
|
+
const findingsWithGuardrails = findings.map(finding => {
|
|
361
|
+
guardrailsSummary.totalFindingsProcessed++;
|
|
362
|
+
|
|
363
|
+
// Capture initial confidence before guardrails
|
|
364
|
+
const initialConfidence = finding.confidence || 0;
|
|
365
|
+
const initialConfidenceLevel = finding.confidenceLevel ||
|
|
366
|
+
(initialConfidence >= 0.8 ? 'HIGH' : initialConfidence >= 0.5 ? 'MEDIUM' : initialConfidence >= 0.2 ? 'LOW' : 'UNPROVEN');
|
|
367
|
+
|
|
368
|
+
// Build context for guardrails
|
|
369
|
+
const context = {
|
|
370
|
+
evidencePackage: finding.evidencePackage,
|
|
371
|
+
signals: finding.evidencePackage?.signals || finding.evidence || {},
|
|
372
|
+
confidenceReasons: finding.confidenceReasons || [],
|
|
373
|
+
promiseType: finding.expectation?.type || finding.promise?.type || null,
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// Apply guardrails
|
|
377
|
+
const guardrailsResult = applyGuardrails(finding, context);
|
|
378
|
+
|
|
379
|
+
// Finalize truth (reconcile confidence with guardrails outcome)
|
|
380
|
+
const { finalFinding, truthDecision } = finalizeFindingTruth(
|
|
381
|
+
guardrailsResult.finding,
|
|
382
|
+
guardrailsResult,
|
|
383
|
+
{
|
|
384
|
+
initialConfidence,
|
|
385
|
+
initialConfidenceLevel
|
|
386
|
+
}
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
// Store truth decision for report
|
|
390
|
+
const findingIdentity = finalFinding.findingId || finalFinding.id || `finding-${findings.indexOf(finding)}`;
|
|
391
|
+
truthDecisions[findingIdentity] = truthDecision;
|
|
392
|
+
|
|
393
|
+
// Track guardrails impact
|
|
394
|
+
const originalSeverity = finding.severity || 'SUSPECTED';
|
|
395
|
+
const finalSeverity = truthDecision.finalStatus;
|
|
396
|
+
|
|
397
|
+
if (finalSeverity !== originalSeverity && originalSeverity === 'CONFIRMED') {
|
|
398
|
+
guardrailsSummary.preventedConfirmedCount++;
|
|
399
|
+
}
|
|
400
|
+
if (finalSeverity === 'SUSPECTED' && originalSeverity === 'CONFIRMED') {
|
|
401
|
+
guardrailsSummary.downgradedCount++;
|
|
402
|
+
}
|
|
403
|
+
if (finalSeverity === 'INFORMATIONAL') {
|
|
404
|
+
guardrailsSummary.informationalCount++;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return finalFinding;
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// Replace findings with guardrails-applied + truth-reconciled findings
|
|
411
|
+
findings.length = 0;
|
|
412
|
+
findings.push(...findingsWithGuardrails);
|
|
413
|
+
|
|
414
|
+
// PHASE 24: Apply confidence invariants and compute final confidence
|
|
415
|
+
const confidenceData = {}; // Map of findingIdentity -> confidence computation result
|
|
416
|
+
|
|
417
|
+
// Load evidence intent if available
|
|
418
|
+
let evidenceIntentLedger = null;
|
|
263
419
|
let runDir = null;
|
|
264
420
|
try {
|
|
265
421
|
runDir = dirname(tracesPath);
|
|
266
|
-
|
|
422
|
+
const evidenceIntentPath = join(runDir, 'evidence.intent.json');
|
|
423
|
+
if (existsSync(evidenceIntentPath)) {
|
|
424
|
+
try {
|
|
425
|
+
const intentContent = readFileSync(evidenceIntentPath, 'utf-8');
|
|
426
|
+
evidenceIntentLedger = JSON.parse(intentContent);
|
|
427
|
+
} catch {
|
|
428
|
+
// Ignore parse errors
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
} catch {
|
|
432
|
+
// Ignore path parsing errors
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const findingsWithConfidence = findings.map(finding => {
|
|
436
|
+
const findingIdentity = finding.findingId || finding.id || `finding-${findings.indexOf(finding)}`;
|
|
437
|
+
|
|
438
|
+
// Get evidence intent entry for this finding
|
|
439
|
+
const evidenceIntentEntry = evidenceIntentLedger?.entries?.find(e => e.findingIdentity === findingIdentity) || null;
|
|
440
|
+
|
|
441
|
+
// Get guardrails outcome
|
|
442
|
+
const guardrailsOutcome = finding.guardrails || truthDecisions[findingIdentity] || null;
|
|
443
|
+
|
|
444
|
+
// Compute final confidence
|
|
445
|
+
const confidenceResult = computeFinalConfidence({
|
|
446
|
+
findingType: finding.type || 'unknown',
|
|
447
|
+
rawSignals: finding.evidencePackage?.signals || finding.evidence || {},
|
|
448
|
+
evidenceIntent: evidenceIntentEntry,
|
|
449
|
+
guardrailsOutcome,
|
|
450
|
+
truthStatus: finding.severity || finding.status || 'SUSPECTED',
|
|
451
|
+
expectation: finding.expectation || null,
|
|
452
|
+
sensors: finding.evidencePackage?.signals || finding.evidence || {},
|
|
453
|
+
comparisons: {},
|
|
454
|
+
evidence: finding.evidence || {},
|
|
455
|
+
options: {
|
|
456
|
+
verificationStatus: null // Will be set by verifier
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
// Store confidence data for report
|
|
461
|
+
confidenceData[findingIdentity] = confidenceResult;
|
|
462
|
+
|
|
463
|
+
// Enforce invariants
|
|
464
|
+
const invariantResult = enforceConfidenceInvariants(finding, {
|
|
465
|
+
expectationProof: confidenceResult.expectationProof,
|
|
466
|
+
verificationStatus: confidenceResult.verificationStatus,
|
|
467
|
+
guardrailsOutcome
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// Update finding with final confidence
|
|
471
|
+
const finalFinding = {
|
|
472
|
+
...invariantResult.finding,
|
|
473
|
+
confidence: confidenceResult.confidenceAfter,
|
|
474
|
+
confidenceLevel: confidenceResult.confidenceLevel,
|
|
475
|
+
confidenceReasons: confidenceResult.explanation
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
return finalFinding;
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
// Replace findings with confidence-enforced findings
|
|
482
|
+
findings.length = 0;
|
|
483
|
+
findings.push(...findingsWithConfidence);
|
|
484
|
+
|
|
485
|
+
// Infer canonical run directory from tracesPath when available
|
|
486
|
+
if (!runDir) {
|
|
487
|
+
try {
|
|
488
|
+
runDir = dirname(tracesPath);
|
|
489
|
+
} catch {
|
|
490
|
+
// Ignore path parsing errors
|
|
491
|
+
}
|
|
492
|
+
}
|
|
267
493
|
|
|
268
494
|
const findingsResult = writeFindings(projectDir, observation.url, findings, [], runDir);
|
|
269
495
|
|
|
496
|
+
// PHASE 23: Write guardrails report
|
|
497
|
+
let guardrailsReportPath = null;
|
|
498
|
+
if (runDir) {
|
|
499
|
+
try {
|
|
500
|
+
guardrailsReportPath = writeGuardrailsReport(runDir, findings, truthDecisions);
|
|
501
|
+
} catch (error) {
|
|
502
|
+
// Log but don't fail the run
|
|
503
|
+
console.error('Failed to write guardrails report:', error.message);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// PHASE 24: Write confidence report
|
|
508
|
+
let confidenceReportPath = null;
|
|
509
|
+
if (runDir) {
|
|
510
|
+
try {
|
|
511
|
+
confidenceReportPath = writeConfidenceReport(runDir, findings, confidenceData);
|
|
512
|
+
} catch (error) {
|
|
513
|
+
// Log but don't fail the run
|
|
514
|
+
console.error('Failed to write confidence report:', error.message);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
270
518
|
const skipSummary = collectSkipReasons(skips);
|
|
271
519
|
|
|
272
520
|
const detectTruth = {
|
|
@@ -278,6 +526,8 @@ export async function detect(manifestPath, tracesPath, validation = null) {
|
|
|
278
526
|
|
|
279
527
|
return {
|
|
280
528
|
...findingsResult,
|
|
281
|
-
detectTruth: detectTruth
|
|
529
|
+
detectTruth: detectTruth,
|
|
530
|
+
guardrailsSummary: guardrailsSummary, // PHASE 17: Include guardrails summary
|
|
531
|
+
guardrailsReportPath: guardrailsReportPath // PHASE 23: Include guardrails report path
|
|
282
532
|
};
|
|
283
533
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Handles keyboard, hover, file_upload, login, logout, auth_guard interactions
|
|
3
3
|
// Plus accessibility detections: focus, ARIA, keyboard trap, feedback gap, freeze
|
|
4
4
|
|
|
5
|
-
import { hasMeaningfulUrlChange,
|
|
5
|
+
import { hasMeaningfulUrlChange, hasDomChange } from './comparison.js';
|
|
6
6
|
import { computeConfidence } from './confidence-engine.js';
|
|
7
7
|
import { enrichFindingWithExplanations } from './finding-detector.js';
|
|
8
8
|
|
|
@@ -14,10 +14,9 @@ import { enrichFindingWithExplanations } from './finding-detector.js';
|
|
|
14
14
|
* @param {Array} traces - Interaction traces to analyze
|
|
15
15
|
* @param {Object} manifest - Project manifest (not used currently)
|
|
16
16
|
* @param {Array} findings - Findings array to append to
|
|
17
|
-
* @param {Object} helpers - Helper functions (not used currently)
|
|
18
17
|
* @returns {Array} Array of detected interactive findings
|
|
19
18
|
*/
|
|
20
|
-
export function detectInteractiveFindings(traces, manifest, findings,
|
|
19
|
+
export function detectInteractiveFindings(traces, manifest, findings, _helpers = {}) {
|
|
21
20
|
const interactiveFindings = [];
|
|
22
21
|
|
|
23
22
|
for (const trace of traces) {
|
|
@@ -165,7 +164,7 @@ export function detectInteractiveFindings(traces, manifest, findings, helpers =
|
|
|
165
164
|
const domChanged = hasDomChange(trace);
|
|
166
165
|
const urlChanged = hasMeaningfulUrlChange(beforeUrl, afterUrl);
|
|
167
166
|
const network = sensors.network || {};
|
|
168
|
-
const
|
|
167
|
+
const _hasNetwork = (network.totalRequests || 0) > 0;
|
|
169
168
|
const loading = sensors.loading || {};
|
|
170
169
|
const stateData = sensors.state || {};
|
|
171
170
|
|