@veraxhq/verax 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -6
- package/bin/verax.js +11 -11
- package/package.json +29 -8
- package/src/cli/commands/baseline.js +103 -0
- package/src/cli/commands/default.js +51 -6
- package/src/cli/commands/doctor.js +29 -0
- package/src/cli/commands/ga.js +246 -0
- package/src/cli/commands/gates.js +95 -0
- package/src/cli/commands/inspect.js +4 -2
- package/src/cli/commands/release-check.js +215 -0
- package/src/cli/commands/run.js +45 -6
- package/src/cli/commands/security-check.js +212 -0
- package/src/cli/commands/truth.js +113 -0
- package/src/cli/entry.js +30 -20
- 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 +544 -0
- package/src/cli/util/ast-network-detector.js +603 -0
- package/src/cli/util/ast-promise-extractor.js +581 -0
- package/src/cli/util/ast-usestate-detector.js +602 -0
- package/src/cli/util/atomic-write.js +12 -1
- package/src/cli/util/bootstrap-guard.js +86 -0
- 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 +124 -0
- package/src/cli/util/determinism-writer.js +129 -0
- package/src/cli/util/digest-engine.js +359 -0
- package/src/cli/util/dom-diff.js +226 -0
- package/src/cli/util/evidence-engine.js +287 -0
- package/src/cli/util/expectation-extractor.js +151 -5
- package/src/cli/util/findings-writer.js +3 -0
- 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 -0
- 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 -0
- package/src/cli/util/project-discovery.js +284 -0
- package/src/cli/util/project-writer.js +2 -0
- package/src/cli/util/run-id.js +23 -27
- package/src/cli/util/run-resolver.js +64 -0
- package/src/cli/util/run-result.js +778 -0
- package/src/cli/util/selector-resolver.js +235 -0
- package/src/cli/util/source-requirement.js +55 -0
- package/src/cli/util/summary-writer.js +2 -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 +146 -0
- package/src/cli/util/svelte-state-detector.js +242 -0
- 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 +178 -0
- package/src/cli/util/vue-sfc-extractor.js +161 -0
- package/src/cli/util/vue-state-detector.js +215 -0
- 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/init.js +4 -18
- package/src/verax/core/action-classifier.js +4 -3
- package/src/verax/core/artifacts/registry.js +139 -0
- package/src/verax/core/artifacts/verifier.js +990 -0
- package/src/verax/core/baseline/baseline.enforcer.js +137 -0
- package/src/verax/core/baseline/baseline.snapshot.js +233 -0
- package/src/verax/core/capabilities/gates.js +505 -0
- package/src/verax/core/capabilities/registry.js +475 -0
- package/src/verax/core/confidence/confidence-compute.js +144 -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 +80 -0
- package/src/verax/core/confidence/confidence.schema.js +94 -0
- package/src/verax/core/confidence-engine-refactor.js +489 -0
- package/src/verax/core/confidence-engine.js +625 -0
- package/src/verax/core/contracts/index.js +29 -0
- package/src/verax/core/contracts/types.js +186 -0
- package/src/verax/core/contracts/validators.js +456 -0
- package/src/verax/core/decisions/decision.trace.js +278 -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 +405 -0
- package/src/verax/core/determinism/engine.js +222 -0
- package/src/verax/core/determinism/finding-identity.js +149 -0
- package/src/verax/core/determinism/normalize.js +466 -0
- package/src/verax/core/determinism/report-writer.js +93 -0
- package/src/verax/core/determinism/run-fingerprint.js +123 -0
- package/src/verax/core/dynamic-route-intelligence.js +529 -0
- package/src/verax/core/evidence/evidence-capture-service.js +308 -0
- package/src/verax/core/evidence/evidence-intent-ledger.js +166 -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 +192 -0
- package/src/verax/core/failures/exit-codes.js +88 -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 +133 -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 +435 -0
- package/src/verax/core/ga/ga.enforcer.js +87 -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 +84 -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 +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 +318 -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 +200 -0
- package/src/verax/core/pipeline-tracker.js +243 -0
- package/src/verax/core/product-definition.js +127 -0
- package/src/verax/core/release/provenance.builder.js +130 -0
- package/src/verax/core/release/release-report-writer.js +40 -0
- package/src/verax/core/release/release.enforcer.js +164 -0
- package/src/verax/core/release/reproducibility.check.js +222 -0
- package/src/verax/core/release/sbom.builder.js +292 -0
- 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 +195 -0
- package/src/verax/core/report/human-summary.js +362 -0
- package/src/verax/core/route-intelligence.js +420 -0
- 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 +329 -0
- package/src/verax/core/security/security-report.js +50 -0
- package/src/verax/core/security/security.enforcer.js +128 -0
- package/src/verax/core/security/supplychain.defaults.json +38 -0
- package/src/verax/core/security/supplychain.policy.js +334 -0
- package/src/verax/core/security/vuln.scan.js +265 -0
- package/src/verax/core/truth/truth.certificate.js +252 -0
- package/src/verax/core/ui-feedback-intelligence.js +481 -0
- package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
- package/src/verax/detect/confidence-engine.js +62 -34
- package/src/verax/detect/confidence-helper.js +34 -0
- package/src/verax/detect/dynamic-route-findings.js +338 -0
- package/src/verax/detect/expectation-chain-detector.js +417 -0
- package/src/verax/detect/expectation-model.js +2 -2
- package/src/verax/detect/failure-cause-inference.js +293 -0
- package/src/verax/detect/findings-writer.js +131 -35
- 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 +46 -5
- package/src/verax/detect/invariants-enforcer.js +147 -0
- package/src/verax/detect/journey-stall-detector.js +558 -0
- 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 +219 -0
- 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 +207 -0
- package/src/verax/detect/view-switch-correlator.js +242 -0
- package/src/verax/flow/flow-engine.js +2 -1
- package/src/verax/flow/flow-spec.js +0 -6
- package/src/verax/index.js +4 -0
- 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 +3 -0
- 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/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 +51 -155
- package/src/verax/observe/interaction-executor.js +192 -0
- package/src/verax/observe/interaction-runner.js +782 -513
- 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 +205 -0
- package/src/verax/observe/observe-helpers.js +192 -0
- package/src/verax/observe/observe-runner.js +230 -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/page-traversal.js +138 -0
- package/src/verax/observe/snapshot-ops.js +94 -0
- package/src/verax/observe/ui-feedback-detector.js +742 -0
- package/src/verax/scan-summary-writer.js +2 -0
- package/src/verax/shared/artifact-manager.js +25 -5
- package/src/verax/shared/caching.js +1 -0
- package/src/verax/shared/css-spinner-rules.js +204 -0
- package/src/verax/shared/expectation-tracker.js +1 -0
- package/src/verax/shared/view-switch-rules.js +208 -0
- package/src/verax/shared/zip-artifacts.js +6 -0
- package/src/verax/shared/config-loader.js +0 -169
- /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.10 — Decision Trace
|
|
3
|
+
*
|
|
4
|
+
* Traces why each finding was detected, which signals contributed,
|
|
5
|
+
* which guardrails applied, and why confidence/status decisions were made.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, existsSync, writeFileSync } from 'fs';
|
|
9
|
+
import { resolve } from 'path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Build decision trace for findings
|
|
13
|
+
*
|
|
14
|
+
* @param {string} projectDir - Project directory
|
|
15
|
+
* @param {string} runId - Run ID
|
|
16
|
+
* @returns {Object} Decision trace
|
|
17
|
+
*/
|
|
18
|
+
export function buildDecisionTrace(projectDir, runId) {
|
|
19
|
+
const runDir = resolve(projectDir, '.verax', 'runs', runId);
|
|
20
|
+
|
|
21
|
+
if (!existsSync(runDir)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const findingsPath = resolve(runDir, 'findings.json');
|
|
26
|
+
if (!existsSync(findingsPath)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
31
|
+
const findings = JSON.parse(readFileSync(findingsPath, 'utf-8'));
|
|
32
|
+
const traces = [];
|
|
33
|
+
|
|
34
|
+
if (!Array.isArray(findings.findings)) {
|
|
35
|
+
return {
|
|
36
|
+
runId,
|
|
37
|
+
findings: [],
|
|
38
|
+
summary: { total: 0 },
|
|
39
|
+
generatedAt: new Date().toISOString()
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const finding of findings.findings) {
|
|
44
|
+
const findingId = finding.findingId || finding.id || `finding-${traces.length}`;
|
|
45
|
+
|
|
46
|
+
// Why detected?
|
|
47
|
+
const detectionReasons = [];
|
|
48
|
+
if (finding.type) {
|
|
49
|
+
detectionReasons.push({
|
|
50
|
+
code: 'FINDING_TYPE',
|
|
51
|
+
reason: `Finding type: ${finding.type}`,
|
|
52
|
+
value: finding.type
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (finding.outcome) {
|
|
56
|
+
detectionReasons.push({
|
|
57
|
+
code: 'OUTCOME_CLASSIFICATION',
|
|
58
|
+
reason: `Outcome: ${finding.outcome}`,
|
|
59
|
+
value: finding.outcome
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (finding.promise?.type) {
|
|
63
|
+
detectionReasons.push({
|
|
64
|
+
code: 'PROMISE_TYPE',
|
|
65
|
+
reason: `Promise type: ${finding.promise.type}`,
|
|
66
|
+
value: finding.promise.type
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Which signals contributed?
|
|
71
|
+
const signals = [];
|
|
72
|
+
if (finding.evidence?.sensors) {
|
|
73
|
+
const sensors = finding.evidence.sensors;
|
|
74
|
+
|
|
75
|
+
if (sensors.network) {
|
|
76
|
+
signals.push({
|
|
77
|
+
type: 'NETWORK',
|
|
78
|
+
contributed: sensors.network.totalRequests > 0 || sensors.network.failedRequests > 0,
|
|
79
|
+
data: {
|
|
80
|
+
totalRequests: sensors.network.totalRequests || 0,
|
|
81
|
+
failedRequests: sensors.network.failedRequests || 0
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (sensors.console) {
|
|
87
|
+
signals.push({
|
|
88
|
+
type: 'CONSOLE',
|
|
89
|
+
contributed: (sensors.console.errors || 0) > 0 || (sensors.console.warnings || 0) > 0,
|
|
90
|
+
data: {
|
|
91
|
+
errors: sensors.console.errors || 0,
|
|
92
|
+
warnings: sensors.console.warnings || 0
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (sensors.uiSignals) {
|
|
98
|
+
signals.push({
|
|
99
|
+
type: 'UI_SIGNALS',
|
|
100
|
+
contributed: sensors.uiSignals.diff?.changed === true,
|
|
101
|
+
data: {
|
|
102
|
+
changed: sensors.uiSignals.diff?.changed || false
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Which guardrails applied?
|
|
109
|
+
const guardrailsApplied = [];
|
|
110
|
+
if (finding.guardrails?.appliedRules) {
|
|
111
|
+
for (const rule of finding.guardrails.appliedRules) {
|
|
112
|
+
guardrailsApplied.push({
|
|
113
|
+
ruleId: rule.id || rule,
|
|
114
|
+
category: rule.category || null,
|
|
115
|
+
action: rule.action || null,
|
|
116
|
+
matched: rule.matched || true
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Why confidence = X?
|
|
122
|
+
const confidenceReasons = [];
|
|
123
|
+
if (finding.confidenceLevel) {
|
|
124
|
+
confidenceReasons.push({
|
|
125
|
+
code: 'CONFIDENCE_LEVEL',
|
|
126
|
+
reason: `Confidence level: ${finding.confidenceLevel}`,
|
|
127
|
+
value: finding.confidenceLevel
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (finding.confidence !== undefined) {
|
|
131
|
+
confidenceReasons.push({
|
|
132
|
+
code: 'CONFIDENCE_SCORE',
|
|
133
|
+
reason: `Confidence score: ${finding.confidence}`,
|
|
134
|
+
value: finding.confidence
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (finding.confidenceReasons && Array.isArray(finding.confidenceReasons)) {
|
|
138
|
+
for (const reason of finding.confidenceReasons) {
|
|
139
|
+
confidenceReasons.push({
|
|
140
|
+
code: 'CONFIDENCE_FACTOR',
|
|
141
|
+
reason: reason,
|
|
142
|
+
value: reason
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Why status = CONFIRMED / SUSPECTED / DROPPED?
|
|
148
|
+
const statusReasons = [];
|
|
149
|
+
const status = finding.severity || finding.status || 'SUSPECTED';
|
|
150
|
+
|
|
151
|
+
statusReasons.push({
|
|
152
|
+
code: 'STATUS_ASSIGNED',
|
|
153
|
+
reason: `Status: ${status}`,
|
|
154
|
+
value: status
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
if (finding.evidencePackage) {
|
|
158
|
+
if (finding.evidencePackage.isComplete) {
|
|
159
|
+
statusReasons.push({
|
|
160
|
+
code: 'EVIDENCE_COMPLETE',
|
|
161
|
+
reason: 'Evidence package is complete',
|
|
162
|
+
value: true
|
|
163
|
+
});
|
|
164
|
+
} else {
|
|
165
|
+
statusReasons.push({
|
|
166
|
+
code: 'EVIDENCE_INCOMPLETE',
|
|
167
|
+
reason: 'Evidence package is incomplete',
|
|
168
|
+
value: false
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (finding.guardrails?.finalDecision) {
|
|
174
|
+
statusReasons.push({
|
|
175
|
+
code: 'GUARDRAILS_DECISION',
|
|
176
|
+
reason: `Guardrails decision: ${finding.guardrails.finalDecision}`,
|
|
177
|
+
value: finding.guardrails.finalDecision
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (status === 'CONFIRMED' && finding.evidencePackage && !finding.evidencePackage.isComplete) {
|
|
182
|
+
statusReasons.push({
|
|
183
|
+
code: 'EVIDENCE_LAW_VIOLATION',
|
|
184
|
+
reason: 'CONFIRMED finding with incomplete evidence violates Evidence Law',
|
|
185
|
+
value: 'VIOLATION'
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
traces.push({
|
|
190
|
+
findingId,
|
|
191
|
+
detection: {
|
|
192
|
+
why: detectionReasons,
|
|
193
|
+
signals: signals.filter(s => s.contributed),
|
|
194
|
+
expectationId: finding.expectationId || null,
|
|
195
|
+
interactionId: finding.interaction?.selector || null
|
|
196
|
+
},
|
|
197
|
+
guardrails: {
|
|
198
|
+
applied: guardrailsApplied,
|
|
199
|
+
finalDecision: finding.guardrails?.finalDecision || null,
|
|
200
|
+
contradictions: finding.guardrails?.contradictions || []
|
|
201
|
+
},
|
|
202
|
+
confidence: {
|
|
203
|
+
level: finding.confidenceLevel || null,
|
|
204
|
+
score: finding.confidence !== undefined ? finding.confidence : null,
|
|
205
|
+
why: confidenceReasons
|
|
206
|
+
},
|
|
207
|
+
status: {
|
|
208
|
+
value: status,
|
|
209
|
+
why: statusReasons
|
|
210
|
+
},
|
|
211
|
+
evidence: {
|
|
212
|
+
packageId: finding.evidencePackage?.id || null,
|
|
213
|
+
isComplete: finding.evidencePackage?.isComplete || false,
|
|
214
|
+
files: finding.evidencePackage?.files || []
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
runId,
|
|
221
|
+
findings: traces,
|
|
222
|
+
summary: {
|
|
223
|
+
total: traces.length,
|
|
224
|
+
byStatus: traces.reduce((acc, t) => {
|
|
225
|
+
const status = t.status.value;
|
|
226
|
+
acc[status] = (acc[status] || 0) + 1;
|
|
227
|
+
return acc;
|
|
228
|
+
}, {}),
|
|
229
|
+
byConfidence: traces.reduce((acc, t) => {
|
|
230
|
+
const level = t.confidence.level || 'UNKNOWN';
|
|
231
|
+
acc[level] = (acc[level] || 0) + 1;
|
|
232
|
+
return acc;
|
|
233
|
+
}, {}),
|
|
234
|
+
withGuardrails: traces.filter(t => t.guardrails.applied.length > 0).length,
|
|
235
|
+
withCompleteEvidence: traces.filter(t => t.evidence.isComplete).length
|
|
236
|
+
},
|
|
237
|
+
generatedAt: new Date().toISOString()
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Write decision trace to file
|
|
243
|
+
*
|
|
244
|
+
* @param {string} projectDir - Project directory
|
|
245
|
+
* @param {string} runId - Run ID
|
|
246
|
+
* @param {Object} trace - Decision trace
|
|
247
|
+
* @returns {string} Path to written file
|
|
248
|
+
*/
|
|
249
|
+
export function writeDecisionTrace(projectDir, runId, trace) {
|
|
250
|
+
const runDir = resolve(projectDir, '.verax', 'runs', runId);
|
|
251
|
+
const outputPath = resolve(runDir, 'decisions.trace.json');
|
|
252
|
+
writeFileSync(outputPath, JSON.stringify(trace, null, 2), 'utf-8');
|
|
253
|
+
return outputPath;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Load decision trace from file
|
|
258
|
+
*
|
|
259
|
+
* @param {string} projectDir - Project directory
|
|
260
|
+
* @param {string} runId - Run ID
|
|
261
|
+
* @returns {Object|null} Decision trace or null
|
|
262
|
+
*/
|
|
263
|
+
export function loadDecisionTrace(projectDir, runId) {
|
|
264
|
+
const runDir = resolve(projectDir, '.verax', 'runs', runId);
|
|
265
|
+
const tracePath = resolve(runDir, 'decisions.trace.json');
|
|
266
|
+
|
|
267
|
+
if (!existsSync(tracePath)) {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
// @ts-expect-error - readFileSync with encoding returns string
|
|
273
|
+
return JSON.parse(readFileSync(tracePath, 'utf-8'));
|
|
274
|
+
} catch {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 25 — Determinism Contract Writer
|
|
3
|
+
*
|
|
4
|
+
* Writes determinism.contract.json artifact capturing adaptive events,
|
|
5
|
+
* retries, timing adjustments, and other non-deterministic behaviors.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { writeFileSync } from 'fs';
|
|
9
|
+
import { resolve } from 'path';
|
|
10
|
+
import { DecisionRecorder as _DecisionRecorder } from '../determinism-model.js';
|
|
11
|
+
import { ARTIFACT_REGISTRY } from '../artifacts/registry.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Write determinism contract artifact
|
|
15
|
+
*
|
|
16
|
+
* @param {string} runDir - Absolute run directory path
|
|
17
|
+
* @param {Object} decisionRecorder - Decision recorder instance
|
|
18
|
+
* @returns {string} Path to written contract
|
|
19
|
+
*/
|
|
20
|
+
export function writeDeterminismContract(runDir, decisionRecorder) {
|
|
21
|
+
const contractPath = resolve(runDir, ARTIFACT_REGISTRY.determinismContract.filename);
|
|
22
|
+
|
|
23
|
+
const adaptiveEvents = [];
|
|
24
|
+
const retryEvents = [];
|
|
25
|
+
const timingAdjustments = [];
|
|
26
|
+
|
|
27
|
+
if (decisionRecorder) {
|
|
28
|
+
// Extract adaptive stabilization events
|
|
29
|
+
const adaptiveStabilization = decisionRecorder.getByCategory('ADAPTIVE_STABILIZATION');
|
|
30
|
+
for (const decision of adaptiveStabilization) {
|
|
31
|
+
adaptiveEvents.push({
|
|
32
|
+
decision_id: decision.decision_id,
|
|
33
|
+
category: decision.category,
|
|
34
|
+
timestamp: decision.timestamp,
|
|
35
|
+
reason: decision.reason,
|
|
36
|
+
context: decision.context || null,
|
|
37
|
+
chosen_value: decision.chosen_value,
|
|
38
|
+
inputs: decision.inputs || {}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Extract retry events
|
|
43
|
+
const retries = decisionRecorder.getByCategory('RETRY');
|
|
44
|
+
for (const decision of retries) {
|
|
45
|
+
retryEvents.push({
|
|
46
|
+
decision_id: decision.decision_id,
|
|
47
|
+
category: decision.category,
|
|
48
|
+
timestamp: decision.timestamp,
|
|
49
|
+
reason: decision.reason,
|
|
50
|
+
context: decision.context || null,
|
|
51
|
+
chosen_value: decision.chosen_value,
|
|
52
|
+
inputs: decision.inputs || {}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Extract timing adjustments (timeout decisions)
|
|
57
|
+
const timeouts = decisionRecorder.getByCategory('TIMEOUT');
|
|
58
|
+
for (const decision of timeouts) {
|
|
59
|
+
timingAdjustments.push({
|
|
60
|
+
decision_id: decision.decision_id,
|
|
61
|
+
category: decision.category,
|
|
62
|
+
timestamp: decision.timestamp,
|
|
63
|
+
reason: decision.reason,
|
|
64
|
+
context: decision.context || null,
|
|
65
|
+
chosen_value: decision.chosen_value,
|
|
66
|
+
inputs: decision.inputs || {}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const contract = {
|
|
72
|
+
version: 1,
|
|
73
|
+
generatedAt: new Date().toISOString(),
|
|
74
|
+
adaptiveEvents,
|
|
75
|
+
retryEvents,
|
|
76
|
+
timingAdjustments,
|
|
77
|
+
summary: {
|
|
78
|
+
adaptiveEventsCount: adaptiveEvents.length,
|
|
79
|
+
retryEventsCount: retryEvents.length,
|
|
80
|
+
timingAdjustmentsCount: timingAdjustments.length,
|
|
81
|
+
isDeterministic: adaptiveEvents.length === 0 && retryEvents.length === 0
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
writeFileSync(contractPath, JSON.stringify(contract, null, 2), 'utf8');
|
|
86
|
+
|
|
87
|
+
return contractPath;
|
|
88
|
+
}
|
|
89
|
+
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.2 — Determinism Truth Lock: Strict Contract
|
|
3
|
+
*
|
|
4
|
+
* DETERMINISM CONTRACT:
|
|
5
|
+
*
|
|
6
|
+
* DETERMINISTIC means:
|
|
7
|
+
* - Same inputs (source code, URL, config)
|
|
8
|
+
* - Same environment (browser, OS, Node version)
|
|
9
|
+
* - Same config (budget, timeouts, flags)
|
|
10
|
+
* → identical normalized artifacts (findings, traces, evidence)
|
|
11
|
+
*
|
|
12
|
+
* NON_DETERMINISTIC means:
|
|
13
|
+
* - Any adaptive behavior occurred (adaptive stabilization, retries, dynamic timeouts)
|
|
14
|
+
* - Any timing variance that affects results
|
|
15
|
+
* - Any environment-dependent behavior
|
|
16
|
+
* - Tracking adaptive decisions is NOT determinism
|
|
17
|
+
*
|
|
18
|
+
* HARD RULE: If adaptiveEvents.length > 0 → verdict MUST be NON_DETERMINISTIC
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* PHASE 21.2: Determinism verdict (binary)
|
|
23
|
+
* PHASE 25: Extended with expected/unexpected distinction
|
|
24
|
+
*/
|
|
25
|
+
export const DETERMINISM_VERDICT = {
|
|
26
|
+
DETERMINISTIC: 'DETERMINISTIC',
|
|
27
|
+
NON_DETERMINISTIC_EXPECTED: 'NON_DETERMINISTIC_EXPECTED',
|
|
28
|
+
NON_DETERMINISTIC_UNEXPECTED: 'NON_DETERMINISTIC_UNEXPECTED',
|
|
29
|
+
NON_DETERMINISTIC: 'NON_DETERMINISTIC' // Backward compatibility
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* PHASE 21.2: Determinism reason codes (stable)
|
|
34
|
+
* PHASE 25: Extended with new reason codes
|
|
35
|
+
*/
|
|
36
|
+
export const DETERMINISM_REASON = {
|
|
37
|
+
ADAPTIVE_STABILIZATION_USED: 'ADAPTIVE_STABILIZATION_USED',
|
|
38
|
+
RETRY_TRIGGERED: 'RETRY_TRIGGERED',
|
|
39
|
+
TIMING_VARIANCE: 'TIMING_VARIANCE',
|
|
40
|
+
TRUNCATION_OCCURRED: 'TRUNCATION_OCCURRED',
|
|
41
|
+
ENVIRONMENT_VARIANCE: 'ENVIRONMENT_VARIANCE',
|
|
42
|
+
NO_ADAPTIVE_EVENTS: 'NO_ADAPTIVE_EVENTS',
|
|
43
|
+
RUN_FINGERPRINT_MISMATCH: 'RUN_FINGERPRINT_MISMATCH',
|
|
44
|
+
ARTIFACT_DIFF_DETECTED: 'ARTIFACT_DIFF_DETECTED',
|
|
45
|
+
VERIFIER_ERRORS_DETECTED: 'VERIFIER_ERRORS_DETECTED',
|
|
46
|
+
EXPECTED_ADAPTIVE_BEHAVIOR: 'EXPECTED_ADAPTIVE_BEHAVIOR',
|
|
47
|
+
UNEXPECTED_DIFF_WITHOUT_ADAPTIVE: 'UNEXPECTED_DIFF_WITHOUT_ADAPTIVE'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* PHASE 21.2: Adaptive event categories that break determinism
|
|
52
|
+
*/
|
|
53
|
+
export const ADAPTIVE_EVENT_CATEGORIES = [
|
|
54
|
+
'ADAPTIVE_STABILIZATION',
|
|
55
|
+
'RETRY',
|
|
56
|
+
'TRUNCATION'
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* PHASE 21.2: Check if a decision category breaks determinism
|
|
61
|
+
*
|
|
62
|
+
* @param {string} category - Decision category
|
|
63
|
+
* @returns {boolean} True if this category breaks determinism
|
|
64
|
+
*/
|
|
65
|
+
export function isAdaptiveEventCategory(category) {
|
|
66
|
+
return ADAPTIVE_EVENT_CATEGORIES.includes(category);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* PHASE 21.2: Compute determinism verdict from DecisionRecorder
|
|
71
|
+
*
|
|
72
|
+
* HARD RULE: If any adaptive event occurred → NON_DETERMINISTIC
|
|
73
|
+
*
|
|
74
|
+
* @param {Object} decisionRecorder - Decision recorder instance
|
|
75
|
+
* @returns {Object} { verdict, reasons, adaptiveEvents }
|
|
76
|
+
*/
|
|
77
|
+
export function computeDeterminismVerdict(decisionRecorder) {
|
|
78
|
+
if (!decisionRecorder) {
|
|
79
|
+
return {
|
|
80
|
+
verdict: DETERMINISM_VERDICT.NON_DETERMINISTIC,
|
|
81
|
+
reasons: [DETERMINISM_REASON.ENVIRONMENT_VARIANCE],
|
|
82
|
+
adaptiveEvents: [],
|
|
83
|
+
message: 'DecisionRecorder not available - cannot verify determinism'
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const adaptiveEvents = [];
|
|
88
|
+
const reasons = [];
|
|
89
|
+
|
|
90
|
+
// Check for adaptive stabilization
|
|
91
|
+
const adaptiveStabilization = decisionRecorder.getByCategory('ADAPTIVE_STABILIZATION');
|
|
92
|
+
const adaptiveExtensions = adaptiveStabilization.filter(d =>
|
|
93
|
+
d.decision_id === 'ADAPTIVE_STABILIZATION_extended'
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (adaptiveExtensions.length > 0) {
|
|
97
|
+
adaptiveEvents.push(...adaptiveExtensions);
|
|
98
|
+
reasons.push(DETERMINISM_REASON.ADAPTIVE_STABILIZATION_USED);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Check for retries
|
|
102
|
+
const retries = decisionRecorder.getByCategory('RETRY');
|
|
103
|
+
if (retries.length > 0) {
|
|
104
|
+
adaptiveEvents.push(...retries);
|
|
105
|
+
reasons.push(DETERMINISM_REASON.RETRY_TRIGGERED);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check for truncations
|
|
109
|
+
const truncations = decisionRecorder.getByCategory('TRUNCATION');
|
|
110
|
+
if (truncations.length > 0) {
|
|
111
|
+
adaptiveEvents.push(...truncations);
|
|
112
|
+
reasons.push(DETERMINISM_REASON.TRUNCATION_OCCURRED);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// HARD RULE: If any adaptive events → NON_DETERMINISTIC
|
|
116
|
+
if (adaptiveEvents.length > 0) {
|
|
117
|
+
return {
|
|
118
|
+
verdict: DETERMINISM_VERDICT.NON_DETERMINISTIC,
|
|
119
|
+
reasons,
|
|
120
|
+
adaptiveEvents: adaptiveEvents.map(e => ({
|
|
121
|
+
decision_id: e.decision_id,
|
|
122
|
+
category: e.category,
|
|
123
|
+
timestamp: e.timestamp,
|
|
124
|
+
reason: e.reason,
|
|
125
|
+
context: e.context || null
|
|
126
|
+
})),
|
|
127
|
+
message: `Non-deterministic: ${adaptiveEvents.length} adaptive event(s) detected`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// No adaptive events → DETERMINISTIC
|
|
132
|
+
return {
|
|
133
|
+
verdict: DETERMINISM_VERDICT.DETERMINISTIC,
|
|
134
|
+
reasons: [DETERMINISM_REASON.NO_ADAPTIVE_EVENTS],
|
|
135
|
+
adaptiveEvents: [],
|
|
136
|
+
message: 'Deterministic: No adaptive events detected'
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|