@veraxhq/verax 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -20
- package/bin/verax.js +11 -18
- package/package.json +28 -7
- package/src/cli/commands/baseline.js +1 -2
- package/src/cli/commands/default.js +72 -81
- package/src/cli/commands/doctor.js +29 -0
- package/src/cli/commands/ga.js +3 -0
- package/src/cli/commands/gates.js +1 -1
- package/src/cli/commands/inspect.js +6 -133
- package/src/cli/commands/release-check.js +2 -0
- package/src/cli/commands/run.js +74 -246
- package/src/cli/commands/security-check.js +2 -1
- package/src/cli/commands/truth.js +0 -1
- package/src/cli/entry.js +82 -309
- package/src/cli/util/angular-component-extractor.js +2 -2
- package/src/cli/util/angular-navigation-detector.js +2 -2
- package/src/cli/util/ast-interactive-detector.js +4 -6
- package/src/cli/util/ast-network-detector.js +3 -3
- package/src/cli/util/ast-promise-extractor.js +581 -0
- package/src/cli/util/ast-usestate-detector.js +3 -3
- package/src/cli/util/atomic-write.js +12 -1
- package/src/cli/util/console-reporter.js +72 -0
- package/src/cli/util/detection-engine.js +105 -41
- package/src/cli/util/determinism-runner.js +2 -1
- package/src/cli/util/determinism-writer.js +1 -1
- package/src/cli/util/digest-engine.js +359 -0
- package/src/cli/util/dom-diff.js +226 -0
- package/src/cli/util/env-url.js +0 -4
- package/src/cli/util/evidence-engine.js +287 -0
- package/src/cli/util/expectation-extractor.js +217 -367
- package/src/cli/util/findings-writer.js +19 -126
- package/src/cli/util/framework-detector.js +572 -0
- package/src/cli/util/idgen.js +1 -1
- package/src/cli/util/interaction-planner.js +529 -0
- package/src/cli/util/learn-writer.js +2 -2
- package/src/cli/util/ledger-writer.js +110 -0
- package/src/cli/util/monorepo-resolver.js +162 -0
- package/src/cli/util/observation-engine.js +127 -278
- package/src/cli/util/observe-writer.js +2 -2
- package/src/cli/util/paths.js +12 -3
- package/src/cli/util/project-discovery.js +284 -3
- package/src/cli/util/project-writer.js +2 -2
- package/src/cli/util/run-id.js +23 -27
- package/src/cli/util/run-result.js +778 -0
- package/src/cli/util/selector-resolver.js +235 -0
- package/src/cli/util/summary-writer.js +2 -1
- package/src/cli/util/svelte-navigation-detector.js +3 -3
- package/src/cli/util/svelte-sfc-extractor.js +0 -1
- package/src/cli/util/svelte-state-detector.js +1 -2
- package/src/cli/util/trust-activation-integration.js +496 -0
- package/src/cli/util/trust-activation-wrapper.js +85 -0
- package/src/cli/util/trust-integration-hooks.js +164 -0
- package/src/cli/util/types.js +153 -0
- package/src/cli/util/url-validation.js +40 -0
- package/src/cli/util/vue-navigation-detector.js +4 -3
- package/src/cli/util/vue-sfc-extractor.js +1 -2
- package/src/cli/util/vue-state-detector.js +1 -1
- package/src/types/fs-augment.d.ts +23 -0
- package/src/types/global.d.ts +137 -0
- package/src/types/internal-types.d.ts +35 -0
- package/src/verax/cli/finding-explainer.js +3 -56
- package/src/verax/cli/init.js +4 -18
- package/src/verax/core/action-classifier.js +4 -3
- package/src/verax/core/artifacts/registry.js +0 -15
- package/src/verax/core/artifacts/verifier.js +18 -8
- package/src/verax/core/baseline/baseline.snapshot.js +2 -0
- package/src/verax/core/capabilities/gates.js +7 -1
- package/src/verax/core/confidence/confidence-compute.js +14 -7
- package/src/verax/core/confidence/confidence.loader.js +1 -0
- package/src/verax/core/confidence-engine-refactor.js +8 -3
- package/src/verax/core/confidence-engine.js +162 -23
- package/src/verax/core/contracts/types.js +1 -0
- package/src/verax/core/contracts/validators.js +79 -4
- package/src/verax/core/decision-snapshot.js +3 -30
- package/src/verax/core/decisions/decision.trace.js +2 -0
- package/src/verax/core/determinism/contract-writer.js +2 -2
- package/src/verax/core/determinism/contract.js +1 -1
- package/src/verax/core/determinism/diff.js +42 -1
- package/src/verax/core/determinism/engine.js +7 -6
- package/src/verax/core/determinism/finding-identity.js +3 -2
- package/src/verax/core/determinism/normalize.js +32 -4
- package/src/verax/core/determinism/report-writer.js +1 -0
- package/src/verax/core/determinism/run-fingerprint.js +7 -2
- package/src/verax/core/dynamic-route-intelligence.js +8 -7
- package/src/verax/core/evidence/evidence-capture-service.js +1 -0
- package/src/verax/core/evidence/evidence-intent-ledger.js +2 -1
- package/src/verax/core/evidence-builder.js +2 -2
- package/src/verax/core/execution-mode-context.js +1 -1
- package/src/verax/core/execution-mode-detector.js +5 -3
- package/src/verax/core/failures/exit-codes.js +39 -37
- package/src/verax/core/failures/failure-summary.js +1 -1
- package/src/verax/core/failures/failure.factory.js +3 -3
- package/src/verax/core/failures/failure.ledger.js +3 -2
- package/src/verax/core/ga/ga.artifact.js +1 -1
- package/src/verax/core/ga/ga.contract.js +3 -2
- package/src/verax/core/ga/ga.enforcer.js +1 -0
- package/src/verax/core/guardrails/policy.loader.js +1 -0
- package/src/verax/core/guardrails/truth-reconciliation.js +1 -1
- package/src/verax/core/guardrails-engine.js +2 -2
- package/src/verax/core/incremental-store.js +1 -0
- package/src/verax/core/integrity/budget.js +138 -0
- package/src/verax/core/integrity/determinism.js +342 -0
- package/src/verax/core/integrity/integrity.js +208 -0
- package/src/verax/core/integrity/poisoning.js +108 -0
- package/src/verax/core/integrity/transaction.js +140 -0
- package/src/verax/core/observe/run-timeline.js +2 -0
- package/src/verax/core/perf/perf.report.js +2 -0
- package/src/verax/core/pipeline-tracker.js +5 -0
- package/src/verax/core/release/provenance.builder.js +73 -214
- package/src/verax/core/release/release.enforcer.js +14 -9
- package/src/verax/core/release/reproducibility.check.js +1 -0
- package/src/verax/core/release/sbom.builder.js +32 -23
- package/src/verax/core/replay-validator.js +2 -0
- package/src/verax/core/replay.js +4 -0
- package/src/verax/core/report/cross-index.js +6 -3
- package/src/verax/core/report/human-summary.js +141 -1
- package/src/verax/core/route-intelligence.js +4 -3
- package/src/verax/core/run-id.js +6 -3
- package/src/verax/core/run-manifest.js +4 -3
- package/src/verax/core/security/secrets.scan.js +10 -7
- package/src/verax/core/security/security.enforcer.js +4 -0
- package/src/verax/core/security/supplychain.policy.js +9 -1
- package/src/verax/core/security/vuln.scan.js +2 -2
- package/src/verax/core/truth/truth.certificate.js +3 -1
- package/src/verax/core/ui-feedback-intelligence.js +12 -46
- package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
- package/src/verax/detect/confidence-engine.js +100 -660
- package/src/verax/detect/confidence-helper.js +1 -0
- package/src/verax/detect/detection-engine.js +1 -18
- package/src/verax/detect/dynamic-route-findings.js +17 -14
- package/src/verax/detect/expectation-chain-detector.js +1 -1
- package/src/verax/detect/expectation-model.js +3 -5
- package/src/verax/detect/failure-cause-inference.js +293 -0
- package/src/verax/detect/findings-writer.js +126 -166
- package/src/verax/detect/flow-detector.js +2 -2
- package/src/verax/detect/form-silent-failure.js +98 -0
- package/src/verax/detect/index.js +51 -234
- package/src/verax/detect/invariants-enforcer.js +147 -0
- package/src/verax/detect/journey-stall-detector.js +4 -4
- package/src/verax/detect/navigation-silent-failure.js +82 -0
- package/src/verax/detect/problem-aggregator.js +361 -0
- package/src/verax/detect/route-findings.js +7 -6
- package/src/verax/detect/summary-writer.js +477 -0
- package/src/verax/detect/test-failure-cause-inference.js +314 -0
- package/src/verax/detect/ui-feedback-findings.js +18 -18
- package/src/verax/detect/verdict-engine.js +3 -57
- package/src/verax/detect/view-switch-correlator.js +2 -2
- package/src/verax/flow/flow-engine.js +2 -1
- package/src/verax/flow/flow-spec.js +0 -6
- package/src/verax/index.js +48 -412
- package/src/verax/intel/ts-program.js +1 -0
- package/src/verax/intel/vue-navigation-extractor.js +3 -0
- package/src/verax/learn/action-contract-extractor.js +67 -682
- package/src/verax/learn/ast-contract-extractor.js +1 -1
- package/src/verax/learn/flow-extractor.js +1 -0
- package/src/verax/learn/project-detector.js +5 -0
- package/src/verax/learn/react-router-extractor.js +2 -0
- package/src/verax/learn/route-validator.js +1 -4
- package/src/verax/learn/source-instrumenter.js +1 -0
- package/src/verax/learn/state-extractor.js +2 -1
- package/src/verax/learn/static-extractor.js +1 -0
- package/src/verax/observe/coverage-gaps.js +132 -0
- package/src/verax/observe/expectation-handler.js +126 -0
- package/src/verax/observe/incremental-skip.js +46 -0
- package/src/verax/observe/index.js +735 -84
- package/src/verax/observe/interaction-executor.js +192 -0
- package/src/verax/observe/interaction-runner.js +782 -530
- package/src/verax/observe/network-firewall.js +86 -0
- package/src/verax/observe/observation-builder.js +169 -0
- package/src/verax/observe/observe-context.js +1 -1
- package/src/verax/observe/observe-helpers.js +2 -1
- package/src/verax/observe/observe-runner.js +28 -24
- package/src/verax/observe/observers/budget-observer.js +3 -3
- package/src/verax/observe/observers/console-observer.js +4 -4
- package/src/verax/observe/observers/coverage-observer.js +4 -4
- package/src/verax/observe/observers/interaction-observer.js +3 -3
- package/src/verax/observe/observers/navigation-observer.js +4 -4
- package/src/verax/observe/observers/network-observer.js +4 -4
- package/src/verax/observe/observers/safety-observer.js +1 -1
- package/src/verax/observe/observers/ui-feedback-observer.js +4 -4
- package/src/verax/observe/page-traversal.js +138 -0
- package/src/verax/observe/snapshot-ops.js +94 -0
- package/src/verax/observe/ui-signal-sensor.js +2 -148
- package/src/verax/scan-summary-writer.js +10 -42
- package/src/verax/shared/artifact-manager.js +30 -13
- package/src/verax/shared/caching.js +1 -0
- package/src/verax/shared/expectation-tracker.js +1 -0
- package/src/verax/shared/zip-artifacts.js +6 -0
- package/src/verax/core/confidence-engine.js.backup +0 -471
- package/src/verax/shared/config-loader.js +0 -169
- /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
|
@@ -62,6 +62,102 @@ export const CONFIDENCE_REASON = {
|
|
|
62
62
|
SENSOR_MISSING: 'SENSOR_MISSING',
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
+
// === PHASE 26: CONFIDENCE ENGINE SIMPLIFICATION ===
|
|
66
|
+
// 7 Core Buckets for scoring (Damage Control)
|
|
67
|
+
export const CONFIDENCE_BUCKET = {
|
|
68
|
+
CRITICAL_EVIDENCE: 'critical_evidence',
|
|
69
|
+
MULTI_SOURCE: 'multi_source_corroboration',
|
|
70
|
+
ASSET_CRITICAL: 'asset_criticality',
|
|
71
|
+
ABUSE_KNOWN: 'known_abuse_indicators',
|
|
72
|
+
EXPLOITABLE: 'exploitability_indicator',
|
|
73
|
+
PRIV_ESCALATION: 'privilege_escalation_path',
|
|
74
|
+
IMPACT_RADIUS: 'impact_radius',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const CORE_BUCKETS = new Set(Object.values(CONFIDENCE_BUCKET));
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Classify confidence reason codes into buckets.
|
|
81
|
+
* Only CORE_BUCKETS contribute to score01.
|
|
82
|
+
* All other reasons become ADVISORY (tracked, not scored).
|
|
83
|
+
*/
|
|
84
|
+
const CORE_BUCKET_CLASSIFICATION = {
|
|
85
|
+
// === CRITICAL_EVIDENCE (Reason codes that prove the finding) ===
|
|
86
|
+
[CONFIDENCE_REASON.PROMISE_PROVEN]: CONFIDENCE_BUCKET.CRITICAL_EVIDENCE,
|
|
87
|
+
[CONFIDENCE_REASON.OBS_UI_FEEDBACK_CONFIRMED]: CONFIDENCE_BUCKET.CRITICAL_EVIDENCE,
|
|
88
|
+
[CONFIDENCE_REASON.OBS_NETWORK_FAILURE]: CONFIDENCE_BUCKET.CRITICAL_EVIDENCE,
|
|
89
|
+
|
|
90
|
+
// === MULTI_SOURCE (Multiple independent signals align) ===
|
|
91
|
+
[CONFIDENCE_REASON.CORR_TIMING_ALIGNED]: CONFIDENCE_BUCKET.MULTI_SOURCE,
|
|
92
|
+
[CONFIDENCE_REASON.CORR_ROUTE_MATCHED]: CONFIDENCE_BUCKET.MULTI_SOURCE,
|
|
93
|
+
[CONFIDENCE_REASON.CORR_REQUEST_MATCHED]: CONFIDENCE_BUCKET.MULTI_SOURCE,
|
|
94
|
+
|
|
95
|
+
// === ASSET_CRITICAL (Critical asset/flow) ===
|
|
96
|
+
[CONFIDENCE_REASON.GUARD_SHALLOW_ROUTING]: CONFIDENCE_BUCKET.ASSET_CRITICAL,
|
|
97
|
+
[CONFIDENCE_REASON.EVIDENCE_TRACES]: CONFIDENCE_BUCKET.ASSET_CRITICAL,
|
|
98
|
+
|
|
99
|
+
// === ABUSE_KNOWN (Known abuse patterns) ===
|
|
100
|
+
[CONFIDENCE_REASON.GUARD_ANALYTICS_FILTERED]: CONFIDENCE_BUCKET.ABUSE_KNOWN,
|
|
101
|
+
[CONFIDENCE_REASON.OBS_NETWORK_SUCCESS]: CONFIDENCE_BUCKET.ABUSE_KNOWN,
|
|
102
|
+
|
|
103
|
+
// === EXPLOITABLE (Finding is actionable) ===
|
|
104
|
+
[CONFIDENCE_REASON.OBS_DOM_CHANGED]: CONFIDENCE_BUCKET.EXPLOITABLE,
|
|
105
|
+
[CONFIDENCE_REASON.OBS_CONSOLE_ERRORS]: CONFIDENCE_BUCKET.EXPLOITABLE,
|
|
106
|
+
|
|
107
|
+
// === PRIV_ESCALATION (Access/privilege changes) ===
|
|
108
|
+
[CONFIDENCE_REASON.OBS_URL_CHANGED]: CONFIDENCE_BUCKET.PRIV_ESCALATION,
|
|
109
|
+
[CONFIDENCE_REASON.CORR_TRACE_LINKED]: CONFIDENCE_BUCKET.PRIV_ESCALATION,
|
|
110
|
+
|
|
111
|
+
// === IMPACT_RADIUS (Scope/impact evidence) ===
|
|
112
|
+
[CONFIDENCE_REASON.EVIDENCE_SCREENSHOTS]: CONFIDENCE_BUCKET.IMPACT_RADIUS,
|
|
113
|
+
[CONFIDENCE_REASON.EVIDENCE_SNIPPETS]: CONFIDENCE_BUCKET.IMPACT_RADIUS,
|
|
114
|
+
|
|
115
|
+
// === ADVISORY (Non-core signals, tracked but not scored) ===
|
|
116
|
+
[CONFIDENCE_REASON.PROMISE_OBSERVED]: null,
|
|
117
|
+
[CONFIDENCE_REASON.PROMISE_WEAK]: null,
|
|
118
|
+
[CONFIDENCE_REASON.PROMISE_UNKNOWN]: null,
|
|
119
|
+
[CONFIDENCE_REASON.PROMISE_AST_BASED]: null,
|
|
120
|
+
[CONFIDENCE_REASON.OBS_NO_SIGNALS]: null,
|
|
121
|
+
[CONFIDENCE_REASON.CORR_WEAK_CORRELATION]: null,
|
|
122
|
+
[CONFIDENCE_REASON.GUARD_UI_FEEDBACK_PRESENT]: null,
|
|
123
|
+
[CONFIDENCE_REASON.GUARD_CONTRADICTION_DETECTED]: null,
|
|
124
|
+
[CONFIDENCE_REASON.GUARD_NETWORK_SUCCESS_NO_UI]: null,
|
|
125
|
+
[CONFIDENCE_REASON.EVIDENCE_SIGNALS]: null,
|
|
126
|
+
[CONFIDENCE_REASON.EVIDENCE_INCOMPLETE]: null,
|
|
127
|
+
[CONFIDENCE_REASON.SENSOR_NETWORK_PRESENT]: null,
|
|
128
|
+
[CONFIDENCE_REASON.SENSOR_CONSOLE_PRESENT]: null,
|
|
129
|
+
[CONFIDENCE_REASON.SENSOR_UI_PRESENT]: null,
|
|
130
|
+
[CONFIDENCE_REASON.SENSOR_UI_FEEDBACK_PRESENT]: null,
|
|
131
|
+
[CONFIDENCE_REASON.SENSOR_MISSING]: null,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get bucket for a confidence reason. Returns null if advisory.
|
|
136
|
+
*/
|
|
137
|
+
function getReasonBucket(reasonCode) {
|
|
138
|
+
return CORE_BUCKET_CLASSIFICATION[reasonCode] || null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Filter reasons to only core bucket reasons.
|
|
143
|
+
*/
|
|
144
|
+
function filterCoreReasons(reasons) {
|
|
145
|
+
return reasons.filter(reason => {
|
|
146
|
+
const bucket = getReasonBucket(reason);
|
|
147
|
+
return bucket !== null && CORE_BUCKETS.has(bucket);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Extract advisory reasons (non-core).
|
|
153
|
+
*/
|
|
154
|
+
function extractAdvisoryReasons(reasons) {
|
|
155
|
+
return reasons.filter(reason => {
|
|
156
|
+
const bucket = getReasonBucket(reason);
|
|
157
|
+
return bucket === null;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
65
161
|
// Global policy cache
|
|
66
162
|
let cachedPolicy = null;
|
|
67
163
|
|
|
@@ -77,6 +173,10 @@ function getConfidencePolicy(policyPath = null, projectDir = null) {
|
|
|
77
173
|
|
|
78
174
|
/**
|
|
79
175
|
* PHASE 21.4: Compute unified confidence using policy
|
|
176
|
+
* PHASE 26: Bucket-gated scoring (Confidence Simplification Contract v1)
|
|
177
|
+
*
|
|
178
|
+
* Note: For backward compatibility, ALL reasons are returned in `reasons` array.
|
|
179
|
+
* Use `advisoryReasons` to identify which ones didn't contribute to score01.
|
|
80
180
|
*
|
|
81
181
|
* @param {Object} params - Confidence computation parameters
|
|
82
182
|
* @param {string} params.findingType - Type of finding
|
|
@@ -85,7 +185,7 @@ function getConfidencePolicy(policyPath = null, projectDir = null) {
|
|
|
85
185
|
* @param {Object} params.comparisons - Comparison data
|
|
86
186
|
* @param {Object} params.evidence - Evidence data (optional)
|
|
87
187
|
* @param {Object} params.options - Options { policyPath, projectDir, determinismVerdict, evidencePackage }
|
|
88
|
-
* @returns {Object} { score, level, reasons[], policyReport }
|
|
188
|
+
* @returns {Object} { score, level, reasons[], advisoryReasons[], policyReport }
|
|
89
189
|
*/
|
|
90
190
|
export function computeUnifiedConfidence({ findingType, expectation, sensors = {}, comparisons = {}, evidence = {}, options = {} }) {
|
|
91
191
|
// Backward compatibility: options is optional
|
|
@@ -107,6 +207,10 @@ export function computeUnifiedConfidence({ findingType, expectation, sensors = {
|
|
|
107
207
|
// === PILLAR E: Evidence Completeness ===
|
|
108
208
|
const evidenceCompleteness = assessEvidenceCompleteness(evidence, sensors, reasons, policy);
|
|
109
209
|
|
|
210
|
+
// === PHASE 26: Identify core vs advisory reasons ===
|
|
211
|
+
const coreReasons = filterCoreReasons(reasons);
|
|
212
|
+
const advisoryReasons = extractAdvisoryReasons(reasons);
|
|
213
|
+
|
|
110
214
|
// === COMPUTE BASE SCORE using policy weights ===
|
|
111
215
|
const baseScore = (
|
|
112
216
|
promiseStrength * policy.weights.promiseStrength +
|
|
@@ -115,16 +219,27 @@ export function computeUnifiedConfidence({ findingType, expectation, sensors = {
|
|
|
115
219
|
guardrails * policy.weights.guardrails +
|
|
116
220
|
evidenceCompleteness * policy.weights.evidenceCompleteness
|
|
117
221
|
);
|
|
118
|
-
|
|
222
|
+
|
|
223
|
+
// === APPLY MISSING SENSORS PENALTY ===
|
|
224
|
+
// Legacy rule: -15 penalty when any core sensor object is missing (network, console, uiSignals)
|
|
225
|
+
const requiredSensors = ['network', 'console', 'uiSignals'];
|
|
226
|
+
const missingSensors = requiredSensors.filter(key => sensors[key] === undefined);
|
|
227
|
+
// Increase penalty so missing core sensors are clearly visible in score100
|
|
228
|
+
const missingSensorsPenalty = missingSensors.length > 0 ? 0.25 : 0; // -25 in 0..1 scale
|
|
229
|
+
if (missingSensorsPenalty > 0) {
|
|
230
|
+
reasons.push(CONFIDENCE_REASON.SENSOR_MISSING);
|
|
231
|
+
}
|
|
232
|
+
|
|
119
233
|
// === APPLY CONTRADICTIONS using policy ===
|
|
234
|
+
// Apply contradiction penalty only when guardrails are materially degraded
|
|
120
235
|
const contradictionPenalty = guardrails < 0.5 ? policy.truthLocks.contradictionPenalty : 0;
|
|
121
|
-
let finalScore = Math.max(0, Math.min(1, baseScore - contradictionPenalty));
|
|
236
|
+
let finalScore = Math.max(0, Math.min(1, baseScore - contradictionPenalty - missingSensorsPenalty));
|
|
122
237
|
|
|
123
238
|
// === TRUTH LOCKS: Apply determinism cap ===
|
|
124
239
|
if (options?.determinismVerdict === 'NON_DETERMINISTIC') {
|
|
125
240
|
const maxConfidence = policy.truthLocks.nonDeterministicMaxConfidence;
|
|
126
241
|
if (finalScore > maxConfidence) {
|
|
127
|
-
|
|
242
|
+
coreReasons.push('TRUTH_LOCK_NON_DETERMINISTIC_CAP');
|
|
128
243
|
finalScore = Math.min(finalScore, maxConfidence);
|
|
129
244
|
}
|
|
130
245
|
}
|
|
@@ -133,21 +248,28 @@ export function computeUnifiedConfidence({ findingType, expectation, sensors = {
|
|
|
133
248
|
const evidencePackage = options?.evidencePackage || evidence?.evidencePackage || {};
|
|
134
249
|
if (evidencePackage?.severity === 'CONFIRMED' || evidencePackage?.status === 'CONFIRMED') {
|
|
135
250
|
if (policy.truthLocks.evidenceCompleteRequired && !evidencePackage.isComplete) {
|
|
136
|
-
|
|
251
|
+
coreReasons.push('TRUTH_LOCK_EVIDENCE_INCOMPLETE');
|
|
137
252
|
// Force downgrade from CONFIRMED
|
|
138
253
|
finalScore = Math.min(finalScore, 0.6); // Cap at MEDIUM
|
|
139
254
|
}
|
|
140
255
|
}
|
|
141
256
|
|
|
142
|
-
// === DETERMINE LEVEL using
|
|
257
|
+
// === DETERMINE LEVEL using Contract v1 thresholds ===
|
|
143
258
|
const level = determineConfidenceLevel(finalScore, promiseStrength, evidenceCompleteness, policy);
|
|
144
259
|
|
|
145
260
|
const policyReport = getPolicyReport(policy);
|
|
146
261
|
|
|
262
|
+
// Contract v1: score01 is canonical, derive score100 and topReasons (core only)
|
|
263
|
+
const topReasons = coreReasons.slice(0, 4);
|
|
264
|
+
|
|
147
265
|
return {
|
|
148
|
-
score: finalScore,
|
|
266
|
+
score: finalScore, // alias for score01 for backward compatibility
|
|
267
|
+
score01: finalScore,
|
|
268
|
+
score100: Math.round(finalScore * 100),
|
|
149
269
|
level,
|
|
150
|
-
reasons: reasons.slice(0, 10),
|
|
270
|
+
reasons: reasons.slice(0, 10), // ALL reasons for backward compat (core + advisory)
|
|
271
|
+
advisoryReasons: advisoryReasons.slice(0, 10), // Advisory reasons (for analyst/context)
|
|
272
|
+
topReasons,
|
|
151
273
|
policyReport: {
|
|
152
274
|
version: policyReport.version,
|
|
153
275
|
source: policyReport.source,
|
|
@@ -284,7 +406,7 @@ function assessCorrelationQuality(expectation, sensors, comparisons, evidence, r
|
|
|
284
406
|
/**
|
|
285
407
|
* Assess guardrails & contradictions (Pillar D) using policy
|
|
286
408
|
*/
|
|
287
|
-
function assessGuardrails(sensors, comparisons, findingType, reasons,
|
|
409
|
+
function assessGuardrails(sensors, comparisons, findingType, reasons, _policy) {
|
|
288
410
|
let guardrailScore = 1.0;
|
|
289
411
|
|
|
290
412
|
const networkSensor = sensors.network || {};
|
|
@@ -355,23 +477,20 @@ function assessEvidenceCompleteness(evidence, sensors, reasons, policy) {
|
|
|
355
477
|
}
|
|
356
478
|
|
|
357
479
|
/**
|
|
358
|
-
* Determine confidence level using
|
|
480
|
+
* Determine confidence level using Contract v1 thresholds
|
|
481
|
+
* HIGH: score01 >= 0.85
|
|
482
|
+
* MEDIUM: 0.60 <= score01 < 0.85
|
|
483
|
+
* UNPROVEN: score01 < 0.60
|
|
359
484
|
*/
|
|
360
|
-
function determineConfidenceLevel(score,
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (score >= thresholds.high && promiseStrength >= 0.9 && evidenceCompleteness >= 0.7) {
|
|
485
|
+
function determineConfidenceLevel(score, _promiseStrength, _evidenceCompleteness, _policy) {
|
|
486
|
+
if (score >= 0.85) {
|
|
364
487
|
return CONFIDENCE_LEVEL.HIGH;
|
|
365
488
|
}
|
|
366
489
|
|
|
367
|
-
if (score >=
|
|
490
|
+
if (score >= 0.60) {
|
|
368
491
|
return CONFIDENCE_LEVEL.MEDIUM;
|
|
369
492
|
}
|
|
370
493
|
|
|
371
|
-
if (score >= thresholds.low) {
|
|
372
|
-
return CONFIDENCE_LEVEL.LOW;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
494
|
return CONFIDENCE_LEVEL.UNPROVEN;
|
|
376
495
|
}
|
|
377
496
|
|
|
@@ -379,6 +498,11 @@ function determineConfidenceLevel(score, promiseStrength, evidenceCompleteness,
|
|
|
379
498
|
* PHASE 21.4: Compute confidence for finding (wrapper with policy support)
|
|
380
499
|
*
|
|
381
500
|
* @param {Object} params - Confidence computation parameters
|
|
501
|
+
* @param {string} params.findingType - Type of finding
|
|
502
|
+
* @param {Object} params.expectation - Promise/expectation
|
|
503
|
+
* @param {Object} params.sensors - Sensor data
|
|
504
|
+
* @param {Object} params.comparisons - Comparison data
|
|
505
|
+
* @param {Object} params.evidence - Evidence data
|
|
382
506
|
* @param {Object} params.options - Options { policyPath, projectDir, determinismVerdict, evidencePackage }
|
|
383
507
|
* @returns {Object} { score, level, reasons[] }
|
|
384
508
|
*/
|
|
@@ -387,10 +511,19 @@ export function computeConfidenceForFinding(params) {
|
|
|
387
511
|
const options = params.options || {};
|
|
388
512
|
|
|
389
513
|
// Use legacy system for base computation
|
|
390
|
-
const
|
|
514
|
+
const legacyParams = {
|
|
515
|
+
findingType: params.findingType,
|
|
516
|
+
expectation: params.expectation,
|
|
517
|
+
sensors: params.sensors,
|
|
518
|
+
comparisons: params.comparisons,
|
|
519
|
+
attemptMeta: params['attemptMeta'] || {},
|
|
520
|
+
executionModeCeiling: options.executionModeCeiling || 1.0
|
|
521
|
+
};
|
|
522
|
+
const legacyResult = computeConfidenceLegacy(legacyParams);
|
|
391
523
|
|
|
392
|
-
//
|
|
524
|
+
// Legacy returns score in 0-100; normalize to 0-1 for unified engine
|
|
393
525
|
let normalizedScore = (legacyResult.score || 0) / 100;
|
|
526
|
+
normalizedScore = Math.max(0, Math.min(1, normalizedScore));
|
|
394
527
|
|
|
395
528
|
// Extract reasons from legacy explain/factors
|
|
396
529
|
const reasons = extractReasonsFromLegacy(legacyResult, params);
|
|
@@ -408,7 +541,7 @@ export function computeConfidenceForFinding(params) {
|
|
|
408
541
|
}
|
|
409
542
|
|
|
410
543
|
// === TRUTH LOCKS: Evidence Law cap ===
|
|
411
|
-
const evidencePackage = options?.evidencePackage || params.evidence
|
|
544
|
+
const evidencePackage = options?.evidencePackage || (params.evidence && params.evidence.evidencePackage) || {};
|
|
412
545
|
if (evidencePackage?.severity === 'CONFIRMED' || evidencePackage?.status === 'CONFIRMED') {
|
|
413
546
|
if (policy.truthLocks.evidenceCompleteRequired && !evidencePackage.isComplete) {
|
|
414
547
|
reasons.push('TRUTH_LOCK_EVIDENCE_INCOMPLETE');
|
|
@@ -424,10 +557,16 @@ export function computeConfidenceForFinding(params) {
|
|
|
424
557
|
policy
|
|
425
558
|
);
|
|
426
559
|
|
|
560
|
+
// Contract v1: Return score01, score100, level, and topReasons
|
|
561
|
+
const topReasons = reasons.slice(0, 4);
|
|
562
|
+
|
|
427
563
|
return {
|
|
428
|
-
|
|
564
|
+
score01: normalizedScore,
|
|
565
|
+
score100: Math.round(normalizedScore * 100),
|
|
566
|
+
score: normalizedScore, // Backward compat - should be 0..1
|
|
429
567
|
level,
|
|
430
568
|
reasons,
|
|
569
|
+
topReasons,
|
|
431
570
|
};
|
|
432
571
|
}
|
|
433
572
|
|
|
@@ -25,6 +25,7 @@ export const FINDING_STATUS = {
|
|
|
25
25
|
CONFIRMED: 'CONFIRMED', // Evidence law satisfied: sufficient evidence exists
|
|
26
26
|
SUSPECTED: 'SUSPECTED', // Needs evidence: signal observed but evidence incomplete
|
|
27
27
|
INFORMATIONAL: 'INFORMATIONAL', // Observation recorded, no claim of failure
|
|
28
|
+
UNPROVEN: 'UNPROVEN', // Evidence Law v1: insufficient evidence to support claim
|
|
28
29
|
DROPPED: 'DROPPED' // Violated contracts, removed from report
|
|
29
30
|
};
|
|
30
31
|
|
|
@@ -97,7 +97,23 @@ export function validateFinding(finding) {
|
|
|
97
97
|
// *** EVIDENCE LAW ENFORCEMENT ***
|
|
98
98
|
// PHASE 16: Check evidencePackage completeness for CONFIRMED findings
|
|
99
99
|
// PHASE 21.1: HARD LOCK - CONFIRMED without complete evidencePackage is IMPOSSIBLE
|
|
100
|
+
// PHASE 21: Evidence Law validation for CONFIRMED findings
|
|
100
101
|
if (finding.status === FINDING_STATUS.CONFIRMED || finding.severity === 'CONFIRMED') {
|
|
102
|
+
// EVIDENCE LAW v1: Check evidence structure first (context anchor + effect evidence)
|
|
103
|
+
const evidenceLawResult = enforceEvidenceLawV1(finding.evidence);
|
|
104
|
+
if (!evidenceLawResult.ok && evidenceLawResult.downgrade) {
|
|
105
|
+
console.log(
|
|
106
|
+
`EVIDENCE_LAW v1: downgraded CONFIRMED -> ${evidenceLawResult.downgrade} ` +
|
|
107
|
+
`(missing: ${evidenceLawResult.missing.join(', ')})`
|
|
108
|
+
);
|
|
109
|
+
return {
|
|
110
|
+
ok: true,
|
|
111
|
+
errors: [],
|
|
112
|
+
shouldDowngrade: true,
|
|
113
|
+
suggestedStatus: evidenceLawResult.downgrade
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
101
117
|
// PHASE 21.1: Strict invariant - CONFIRMED findings MUST have complete evidencePackage
|
|
102
118
|
if (finding.evidencePackage) {
|
|
103
119
|
const missingFields = finding.evidencePackage.missingEvidence || [];
|
|
@@ -222,10 +238,17 @@ export function validateConfidence(confidence) {
|
|
|
222
238
|
);
|
|
223
239
|
}
|
|
224
240
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
241
|
+
// Contract v1: Accept both score01 (0-1) and legacy score (0-100) for backward compat
|
|
242
|
+
if (confidence.score01 !== undefined) {
|
|
243
|
+
if (typeof confidence.score01 !== 'number' || confidence.score01 < 0 || confidence.score01 > 1) {
|
|
244
|
+
errors.push(`Invalid confidence.score01: ${confidence.score01}. Must be a number 0-1`);
|
|
245
|
+
}
|
|
246
|
+
} else if (confidence.score !== undefined) {
|
|
247
|
+
if (typeof confidence.score !== 'number' || confidence.score < 0 || confidence.score > 100) {
|
|
248
|
+
errors.push(`Invalid confidence.score: ${confidence.score}. Must be a number 0-100`);
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
errors.push('Missing required field: confidence.score01 or confidence.score');
|
|
229
252
|
}
|
|
230
253
|
|
|
231
254
|
return {
|
|
@@ -285,6 +308,57 @@ export function validateSignals(signals) {
|
|
|
285
308
|
};
|
|
286
309
|
}
|
|
287
310
|
|
|
311
|
+
/**
|
|
312
|
+
* EVIDENCE LAW v1: Check if CONFIRMED finding has complete evidence structure
|
|
313
|
+
*
|
|
314
|
+
* Rule A (Context Anchor): Must have beforeUrl OR beforeScreenshot OR before
|
|
315
|
+
* Rule B (Effect Evidence): Must have afterUrl OR after OR flags OR quantitative indicators
|
|
316
|
+
*
|
|
317
|
+
* @param {Object} evidence - Evidence object to validate
|
|
318
|
+
* @returns {Object} { ok: boolean, downgrade: 'UNPROVEN'|'SUSPECTED'|null, missing: string[] }
|
|
319
|
+
*/
|
|
320
|
+
export function enforceEvidenceLawV1(evidence) {
|
|
321
|
+
if (!evidence || typeof evidence !== 'object' || Object.keys(evidence).length === 0) {
|
|
322
|
+
return { ok: false, downgrade: 'UNPROVEN', missing: ['evidence object'] };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const missing = [];
|
|
326
|
+
|
|
327
|
+
// Rule A: Context anchor (before state)
|
|
328
|
+
const hasContextAnchor = evidence.beforeUrl || evidence.beforeScreenshot || evidence.before;
|
|
329
|
+
if (!hasContextAnchor) {
|
|
330
|
+
missing.push('context anchor (beforeUrl/beforeScreenshot/before)');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Rule B: Effect evidence (after state or change indicators)
|
|
334
|
+
const hasEffectEvidence =
|
|
335
|
+
evidence.afterUrl ||
|
|
336
|
+
evidence.afterScreenshot ||
|
|
337
|
+
evidence.after ||
|
|
338
|
+
evidence.urlChanged === true ||
|
|
339
|
+
evidence.domChanged === true ||
|
|
340
|
+
evidence.uiChanged === true ||
|
|
341
|
+
(typeof evidence.networkRequests === 'number' && evidence.networkRequests > 0) ||
|
|
342
|
+
(Array.isArray(evidence.networkRequests) && evidence.networkRequests.length > 0) ||
|
|
343
|
+
(typeof evidence.consoleErrors === 'number' && evidence.consoleErrors > 0) ||
|
|
344
|
+
(Array.isArray(evidence.consoleErrors) && evidence.consoleErrors.length > 0) ||
|
|
345
|
+
evidence.timingBreakdown;
|
|
346
|
+
|
|
347
|
+
if (!hasEffectEvidence) {
|
|
348
|
+
missing.push('effect evidence (after/flags/quantitative)');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Downgrade logic
|
|
352
|
+
if (!hasContextAnchor && !hasEffectEvidence) {
|
|
353
|
+
return { ok: false, downgrade: 'UNPROVEN', missing };
|
|
354
|
+
}
|
|
355
|
+
if (!hasContextAnchor || !hasEffectEvidence) {
|
|
356
|
+
return { ok: false, downgrade: 'SUSPECTED', missing };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return { ok: true, downgrade: null, missing: [] };
|
|
360
|
+
}
|
|
361
|
+
|
|
288
362
|
/**
|
|
289
363
|
* EVIDENCE LAW: Determine if evidence is sufficient for CONFIRMED status
|
|
290
364
|
*
|
|
@@ -377,5 +451,6 @@ export default {
|
|
|
377
451
|
validateConfidence,
|
|
378
452
|
validateSignals,
|
|
379
453
|
isEvidenceSubstantive,
|
|
454
|
+
enforceEvidenceLawV1,
|
|
380
455
|
enforceContractsOnFindings
|
|
381
456
|
};
|
|
@@ -176,11 +176,9 @@ function extractUnverified(detectTruth, observeTruth) {
|
|
|
176
176
|
* @param {Object} detectTruth - Detect phase truth
|
|
177
177
|
* @param {Object} observeTruth - Observe phase truth
|
|
178
178
|
* @param {Object} _silences - Silence data (unused parameter, kept for API compatibility)
|
|
179
|
-
* @
|
|
180
|
-
* @returns {Object} - Decision snapshot answering 6 mandatory questions + execution mode info
|
|
179
|
+
* @returns {Object} - Decision snapshot answering 6 mandatory questions
|
|
181
180
|
*/
|
|
182
|
-
export function computeDecisionSnapshot(findings, detectTruth, observeTruth, _silences
|
|
183
|
-
const { executionMode = null, executionModeCeiling = 1.0 } = options;
|
|
181
|
+
export function computeDecisionSnapshot(findings, detectTruth, observeTruth, _silences) {
|
|
184
182
|
// Question 1: Do we have confirmed SILENT FAILURES?
|
|
185
183
|
const confirmedFailures = findings.filter(f =>
|
|
186
184
|
f.outcome === 'broken' || f.type === 'silent_failure'
|
|
@@ -266,35 +264,10 @@ export function computeDecisionSnapshot(findings, detectTruth, observeTruth, _si
|
|
|
266
264
|
score: confidence.score,
|
|
267
265
|
coverageRatio: confidence.coverageRatio,
|
|
268
266
|
factors: confidence.factors
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// EXECUTION MODE
|
|
272
|
-
executionMode: executionMode,
|
|
273
|
-
executionModeCeiling: executionModeCeiling,
|
|
274
|
-
executionModeExplanation: generateExecutionModeExplanation(executionMode, executionModeCeiling)
|
|
267
|
+
}
|
|
275
268
|
};
|
|
276
269
|
}
|
|
277
270
|
|
|
278
|
-
/**
|
|
279
|
-
* Generate explanation for execution mode awareness
|
|
280
|
-
* @param {string} mode - Execution mode (PROJECT_SCAN or WEB_SCAN_LIMITED)
|
|
281
|
-
* @param {number} ceiling - Confidence ceiling (0..1)
|
|
282
|
-
* @returns {string} - Human-readable explanation
|
|
283
|
-
*/
|
|
284
|
-
function generateExecutionModeExplanation(mode, ceiling) {
|
|
285
|
-
if (!mode) {
|
|
286
|
-
return null;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (mode === 'WEB_SCAN_LIMITED') {
|
|
290
|
-
return `Analysis limited to runtime behavior observation (no source code). Confidence capped at ${Math.round(ceiling * 100)}%. Use source code scanning for fuller analysis.`;
|
|
291
|
-
} else if (mode === 'PROJECT_SCAN') {
|
|
292
|
-
return `Full project analysis with source code. Confidence unrestricted based on evidence quality.`;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return null;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
271
|
/**
|
|
299
272
|
* Format decision snapshot for human reading
|
|
300
273
|
* @param {Object} snapshot - Decision snapshot
|
|
@@ -27,6 +27,7 @@ export function buildDecisionTrace(projectDir, runId) {
|
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
30
31
|
const findings = JSON.parse(readFileSync(findingsPath, 'utf-8'));
|
|
31
32
|
const traces = [];
|
|
32
33
|
|
|
@@ -268,6 +269,7 @@ export function loadDecisionTrace(projectDir, runId) {
|
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
try {
|
|
272
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
271
273
|
return JSON.parse(readFileSync(tracePath, 'utf-8'));
|
|
272
274
|
} catch {
|
|
273
275
|
return null;
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
|
|
8
8
|
import { writeFileSync } from 'fs';
|
|
9
9
|
import { resolve } from 'path';
|
|
10
|
-
import { DecisionRecorder } from '../determinism-model.js';
|
|
10
|
+
import { DecisionRecorder as _DecisionRecorder } from '../determinism-model.js';
|
|
11
11
|
import { ARTIFACT_REGISTRY } from '../artifacts/registry.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Write determinism contract artifact
|
|
15
15
|
*
|
|
16
16
|
* @param {string} runDir - Absolute run directory path
|
|
17
|
-
* @param {
|
|
17
|
+
* @param {Object} decisionRecorder - Decision recorder instance
|
|
18
18
|
* @returns {string} Path to written contract
|
|
19
19
|
*/
|
|
20
20
|
export function writeDeterminismContract(runDir, decisionRecorder) {
|
|
@@ -71,7 +71,7 @@ export function isAdaptiveEventCategory(category) {
|
|
|
71
71
|
*
|
|
72
72
|
* HARD RULE: If any adaptive event occurred → NON_DETERMINISTIC
|
|
73
73
|
*
|
|
74
|
-
* @param {
|
|
74
|
+
* @param {Object} decisionRecorder - Decision recorder instance
|
|
75
75
|
* @returns {Object} { verdict, reasons, adaptiveEvents }
|
|
76
76
|
*/
|
|
77
77
|
export function computeDeterminismVerdict(decisionRecorder) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Generates structured diffs between normalized artifacts from different runs.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { computeFindingIdentity } from './finding-identity.js';
|
|
7
|
+
import { computeFindingIdentity as _computeFindingIdentity } from './finding-identity.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* PHASE 18: Diff reason codes
|
|
@@ -48,6 +48,18 @@ export const DIFF_SEVERITY = {
|
|
|
48
48
|
INFO: 'INFO',
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
function diffRunMeta(artifactA, artifactB) {
|
|
52
|
+
return diffGeneric(artifactA, artifactB, 'runMeta');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function diffDeterminismContract(artifactA, artifactB) {
|
|
56
|
+
return diffGeneric(artifactA, artifactB, 'determinismContract');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function diffReportArtifact(artifactA, artifactB, artifactName) {
|
|
60
|
+
return diffGeneric(artifactA, artifactB, artifactName);
|
|
61
|
+
}
|
|
62
|
+
|
|
51
63
|
/**
|
|
52
64
|
* PHASE 18: Diff artifacts
|
|
53
65
|
*
|
|
@@ -320,6 +332,35 @@ function diffFinding(findingA, findingB, identity) {
|
|
|
320
332
|
findingIdentity: identity,
|
|
321
333
|
field: 'evidencePackage',
|
|
322
334
|
});
|
|
335
|
+
} else if (evidencePackageA && evidencePackageB) {
|
|
336
|
+
if (evidencePackageA.isComplete !== evidencePackageB.isComplete) {
|
|
337
|
+
diffs.push({
|
|
338
|
+
category: DIFF_CATEGORY.EVIDENCE,
|
|
339
|
+
severity: DIFF_SEVERITY.BLOCKER,
|
|
340
|
+
reasonCode: DIFF_REASON.EVIDENCE_COMPLETENESS_CHANGED,
|
|
341
|
+
message: `Evidence completeness changed: ${evidencePackageA.isComplete} → ${evidencePackageB.isComplete}`,
|
|
342
|
+
artifact: 'findings',
|
|
343
|
+
findingIdentity: identity,
|
|
344
|
+
field: 'evidencePackage.isComplete',
|
|
345
|
+
oldValue: evidencePackageA.isComplete,
|
|
346
|
+
newValue: evidencePackageB.isComplete,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
const missingA = Array.isArray(evidencePackageA.missingEvidence) ? evidencePackageA.missingEvidence.sort() : [];
|
|
350
|
+
const missingB = Array.isArray(evidencePackageB.missingEvidence) ? evidencePackageB.missingEvidence.sort() : [];
|
|
351
|
+
if (missingA.join('|') !== missingB.join('|')) {
|
|
352
|
+
diffs.push({
|
|
353
|
+
category: DIFF_CATEGORY.EVIDENCE,
|
|
354
|
+
severity: DIFF_SEVERITY.BLOCKER,
|
|
355
|
+
reasonCode: DIFF_REASON.EVIDENCE_MISSING,
|
|
356
|
+
message: `Missing evidence changed`,
|
|
357
|
+
artifact: 'findings',
|
|
358
|
+
findingIdentity: identity,
|
|
359
|
+
field: 'evidencePackage.missingEvidence',
|
|
360
|
+
oldValue: missingA,
|
|
361
|
+
newValue: missingB,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
323
364
|
}
|
|
324
365
|
|
|
325
366
|
return diffs;
|
|
@@ -7,12 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { readFileSync, existsSync } from 'fs';
|
|
10
|
-
import { join, resolve } from 'path';
|
|
10
|
+
import { join as _join, resolve } from 'path';
|
|
11
11
|
import { normalizeArtifact } from './normalize.js';
|
|
12
12
|
import { diffArtifacts } from './diff.js';
|
|
13
13
|
import { computeFindingIdentity } from './finding-identity.js';
|
|
14
|
-
import {
|
|
15
|
-
import { computeDeterminismVerdict, DETERMINISM_VERDICT, DETERMINISM_REASON } from './contract.js';
|
|
14
|
+
import { computeDeterminismVerdict, DETERMINISM_VERDICT } from './contract.js';
|
|
16
15
|
import { DecisionRecorder } from '../determinism-model.js';
|
|
17
16
|
|
|
18
17
|
/**
|
|
@@ -28,9 +27,9 @@ export { DETERMINISM_VERDICT, DETERMINISM_REASON } from './contract.js';
|
|
|
28
27
|
* @param {number} options.runs - Number of runs (default: 2)
|
|
29
28
|
* @param {Object} options.config - Configuration for runs
|
|
30
29
|
* @param {boolean} options.normalize - Whether to normalize artifacts (default: true)
|
|
31
|
-
* @returns {Object} { verdict, summary, diffs, runsMeta }
|
|
30
|
+
* @returns {Promise<Object>} { verdict, summary, diffs, runsMeta }
|
|
32
31
|
*/
|
|
33
|
-
export async function runDeterminismCheck(runFn, options = {}) {
|
|
32
|
+
export async function runDeterminismCheck(runFn, options = { runs: 2, config: {}, normalize: true }) {
|
|
34
33
|
const { runs = 2, config = {}, normalize = true } = options;
|
|
35
34
|
|
|
36
35
|
const runsMeta = [];
|
|
@@ -53,6 +52,7 @@ export async function runDeterminismCheck(runFn, options = {}) {
|
|
|
53
52
|
for (const [key, path] of Object.entries(runResult.artifactPaths)) {
|
|
54
53
|
try {
|
|
55
54
|
const content = readFileSync(path, 'utf-8');
|
|
55
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
56
56
|
artifacts[key] = JSON.parse(content);
|
|
57
57
|
} catch (error) {
|
|
58
58
|
// Artifact not found or invalid
|
|
@@ -121,6 +121,7 @@ export async function runDeterminismCheck(runFn, options = {}) {
|
|
|
121
121
|
|
|
122
122
|
if (existsSync(decisionsPath)) {
|
|
123
123
|
try {
|
|
124
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
124
125
|
const decisionsData = JSON.parse(readFileSync(decisionsPath, 'utf-8'));
|
|
125
126
|
const decisionRecorder = DecisionRecorder.fromExport(decisionsData);
|
|
126
127
|
const adaptiveCheck = computeDeterminismVerdict(decisionRecorder);
|
|
@@ -187,7 +188,7 @@ function buildFindingIdentityMap(artifactA, artifactB) {
|
|
|
187
188
|
/**
|
|
188
189
|
* Build summary from diffs
|
|
189
190
|
*/
|
|
190
|
-
function buildSummary(diffs,
|
|
191
|
+
function buildSummary(diffs, _runsMeta) {
|
|
191
192
|
const blockerCount = diffs.filter(d => d.severity === 'BLOCKER').length;
|
|
192
193
|
const warnCount = diffs.filter(d => d.severity === 'WARN').length;
|
|
193
194
|
const infoCount = diffs.filter(d => d.severity === 'INFO').length;
|
|
@@ -120,8 +120,8 @@ function normalizePath(path) {
|
|
|
120
120
|
// Normalize separators
|
|
121
121
|
let normalized = path.replace(/\\/g, '/');
|
|
122
122
|
// Remove absolute path prefixes (keep relative structure)
|
|
123
|
-
normalized = normalized.replace(/^[A-Z]:\/[
|
|
124
|
-
normalized = normalized.replace(/^\/[
|
|
123
|
+
normalized = normalized.replace(/^[A-Z]:\/[^/]+/, '');
|
|
124
|
+
normalized = normalized.replace(/^\/[^/]+/, '');
|
|
125
125
|
return normalized;
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -143,6 +143,7 @@ function normalizeUrl(url) {
|
|
|
143
143
|
* Hash string for stable identity
|
|
144
144
|
*/
|
|
145
145
|
function hashString(str) {
|
|
146
|
+
// @ts-expect-error - digest returns string
|
|
146
147
|
return createHash('sha256').update(str).digest('hex').substring(0, 16);
|
|
147
148
|
}
|
|
148
149
|
|