@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
package/src/cli/commands/run.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { resolve
|
|
1
|
+
import { resolve } from 'path';
|
|
2
2
|
import { existsSync, readFileSync } from 'fs';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { dirname } from 'path';
|
|
5
|
-
import { UsageError, DataError
|
|
5
|
+
import { UsageError, DataError } from '../util/errors.js';
|
|
6
6
|
import { generateRunId } from '../util/run-id.js';
|
|
7
7
|
import { getRunPaths, ensureRunDirectories } from '../util/paths.js';
|
|
8
8
|
import { atomicWriteJson, atomicWriteText } from '../util/atomic-write.js';
|
|
@@ -16,6 +16,10 @@ import { writeObserveJson } from '../util/observe-writer.js';
|
|
|
16
16
|
import { detectFindings } from '../util/detection-engine.js';
|
|
17
17
|
import { writeFindingsJson } from '../util/findings-writer.js';
|
|
18
18
|
import { writeSummaryJson } from '../util/summary-writer.js';
|
|
19
|
+
import { computeRuntimeBudget, withTimeout } from '../util/runtime-budget.js';
|
|
20
|
+
import { assertHasLocalSource } from '../util/source-requirement.js';
|
|
21
|
+
import { runWithDeterminism } from '../util/determinism-runner.js';
|
|
22
|
+
import { runDeterminismCheck } from '../../verax/core/determinism/engine.js';
|
|
19
23
|
|
|
20
24
|
const __filename = fileURLToPath(import.meta.url);
|
|
21
25
|
const __dirname = dirname(__filename);
|
|
@@ -26,7 +30,7 @@ function getVersion() {
|
|
|
26
30
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
27
31
|
return pkg.version;
|
|
28
32
|
} catch {
|
|
29
|
-
return '0.
|
|
33
|
+
return '0.3.0';
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
|
|
@@ -35,17 +39,68 @@ function getVersion() {
|
|
|
35
39
|
* Strict, non-interactive CLI mode with explicit flags
|
|
36
40
|
*/
|
|
37
41
|
export async function runCommand(options) {
|
|
42
|
+
return await runCommandInternal(options);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Internal run command implementation
|
|
47
|
+
*/
|
|
48
|
+
async function runCommandInternal(options) {
|
|
38
49
|
const {
|
|
39
50
|
url,
|
|
51
|
+
fixture,
|
|
40
52
|
src = '.',
|
|
41
53
|
out = '.verax',
|
|
42
54
|
json = false,
|
|
43
55
|
verbose = false,
|
|
56
|
+
determinism = false,
|
|
57
|
+
determinismRuns = 2,
|
|
44
58
|
} = options;
|
|
45
59
|
|
|
60
|
+
// PHASE 25: Support fixture mode for determinism
|
|
61
|
+
let resolvedUrl = url;
|
|
62
|
+
let fixtureId = null;
|
|
63
|
+
|
|
64
|
+
if (fixture && !url) {
|
|
65
|
+
// Extract URL from fixture
|
|
66
|
+
const { resolve } = await import('path');
|
|
67
|
+
const { existsSync, readFileSync } = await import('fs');
|
|
68
|
+
const { fileURLToPath } = await import('url');
|
|
69
|
+
const { dirname } = await import('path');
|
|
70
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
71
|
+
const __dirname = dirname(__filename);
|
|
72
|
+
|
|
73
|
+
const fixturePath = resolve(__dirname, '..', '..', '..', 'test', 'fixtures', 'realistic', fixture);
|
|
74
|
+
if (existsSync(fixturePath)) {
|
|
75
|
+
// Try to read package.json or index.html to extract URL
|
|
76
|
+
const packagePath = resolve(fixturePath, 'package.json');
|
|
77
|
+
const indexPath = resolve(fixturePath, 'index.html');
|
|
78
|
+
|
|
79
|
+
if (existsSync(packagePath)) {
|
|
80
|
+
try {
|
|
81
|
+
const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
82
|
+
if (pkg.verax && pkg.verax.url) {
|
|
83
|
+
resolvedUrl = pkg.verax.url;
|
|
84
|
+
fixtureId = fixture;
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// Ignore parse errors
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// If no URL found, use default localhost URL for fixture
|
|
92
|
+
if (!resolvedUrl) {
|
|
93
|
+
resolvedUrl = `http://localhost:5173`; // Default Vite dev server
|
|
94
|
+
fixtureId = fixture;
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
throw new DataError(`Fixture not found: ${fixture}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
46
101
|
// Validate required arguments
|
|
47
|
-
if (!
|
|
48
|
-
throw new UsageError('Missing required argument: --url <url>');
|
|
102
|
+
if (!resolvedUrl) {
|
|
103
|
+
throw new UsageError('Missing required argument: --url <url> or --fixture <fixture>');
|
|
49
104
|
}
|
|
50
105
|
|
|
51
106
|
const projectRoot = resolve(process.cwd());
|
|
@@ -55,12 +110,16 @@ export async function runCommand(options) {
|
|
|
55
110
|
if (!existsSync(srcPath)) {
|
|
56
111
|
throw new DataError(`Source directory not found: ${srcPath}`);
|
|
57
112
|
}
|
|
113
|
+
|
|
114
|
+
// Enforce local source availability (no URL-only scans)
|
|
115
|
+
assertHasLocalSource(srcPath);
|
|
58
116
|
|
|
59
117
|
// Create event emitter
|
|
60
118
|
const events = new RunEventEmitter();
|
|
61
119
|
|
|
62
120
|
// Setup event handlers
|
|
63
121
|
if (json) {
|
|
122
|
+
// In JSON mode, emit events as JSONL (one JSON object per line)
|
|
64
123
|
events.on('*', (event) => {
|
|
65
124
|
console.log(JSON.stringify(event));
|
|
66
125
|
});
|
|
@@ -75,6 +134,141 @@ export async function runCommand(options) {
|
|
|
75
134
|
let runId = null;
|
|
76
135
|
let paths = null;
|
|
77
136
|
let startedAt = null;
|
|
137
|
+
let watchdogTimer = null;
|
|
138
|
+
let budget = null;
|
|
139
|
+
let timedOut = false;
|
|
140
|
+
|
|
141
|
+
// Graceful finalization function
|
|
142
|
+
const finalizeOnTimeout = async (reason) => {
|
|
143
|
+
if (timedOut) return; // Prevent double finalization
|
|
144
|
+
timedOut = true;
|
|
145
|
+
|
|
146
|
+
events.stopHeartbeat();
|
|
147
|
+
|
|
148
|
+
if (paths && runId && startedAt) {
|
|
149
|
+
try {
|
|
150
|
+
const failedAt = new Date().toISOString();
|
|
151
|
+
atomicWriteJson(paths.runStatusJson, {
|
|
152
|
+
contractVersion: 1,
|
|
153
|
+
artifactVersions: paths.artifactVersions,
|
|
154
|
+
status: 'FAILED',
|
|
155
|
+
runId,
|
|
156
|
+
startedAt,
|
|
157
|
+
failedAt,
|
|
158
|
+
error: reason,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
atomicWriteJson(paths.runMetaJson, {
|
|
162
|
+
contractVersion: 1,
|
|
163
|
+
artifactVersions: paths.artifactVersions,
|
|
164
|
+
veraxVersion: getVersion(),
|
|
165
|
+
nodeVersion: process.version,
|
|
166
|
+
platform: process.platform,
|
|
167
|
+
cwd: projectRoot,
|
|
168
|
+
command: 'run',
|
|
169
|
+
args: { url, src, out },
|
|
170
|
+
url,
|
|
171
|
+
src: srcPath,
|
|
172
|
+
startedAt,
|
|
173
|
+
completedAt: failedAt,
|
|
174
|
+
error: reason,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
writeSummaryJson(paths.summaryJson, {
|
|
179
|
+
runId,
|
|
180
|
+
status: 'FAILED',
|
|
181
|
+
startedAt,
|
|
182
|
+
completedAt: failedAt,
|
|
183
|
+
command: 'run',
|
|
184
|
+
url: resolvedUrl,
|
|
185
|
+
notes: `Run timed out: ${reason}`,
|
|
186
|
+
}, {
|
|
187
|
+
expectationsTotal: 0,
|
|
188
|
+
attempted: 0,
|
|
189
|
+
observed: 0,
|
|
190
|
+
silentFailures: 0,
|
|
191
|
+
coverageGaps: 0,
|
|
192
|
+
unproven: 0,
|
|
193
|
+
informational: 0,
|
|
194
|
+
});
|
|
195
|
+
} catch (summaryError) {
|
|
196
|
+
// Ignore summary write errors during timeout handling
|
|
197
|
+
}
|
|
198
|
+
} catch (statusError) {
|
|
199
|
+
// Ignore errors when writing failure status
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
events.emit('error', {
|
|
204
|
+
message: reason,
|
|
205
|
+
type: 'timeout',
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
// PHASE 25: If determinism mode, wrap execution
|
|
210
|
+
if (determinism) {
|
|
211
|
+
const scanFn = async (runConfig) => {
|
|
212
|
+
// Execute a single scan run
|
|
213
|
+
const singleRunId = generateRunId();
|
|
214
|
+
const singlePaths = getRunPaths(projectRoot, out, singleRunId);
|
|
215
|
+
ensureRunDirectories(singlePaths);
|
|
216
|
+
|
|
217
|
+
// Execute scan (reuse existing logic but with single run)
|
|
218
|
+
// This is a simplified version - in production, you'd extract the scan logic
|
|
219
|
+
const { scan } = await import('../../verax/index.js');
|
|
220
|
+
const scanResult = await scan(
|
|
221
|
+
projectRoot,
|
|
222
|
+
resolvedUrl,
|
|
223
|
+
null, // manifestPath
|
|
224
|
+
null, // scanBudgetOverride
|
|
225
|
+
{}, // safetyFlags
|
|
226
|
+
singleRunId
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
runId: singleRunId,
|
|
231
|
+
artifactPaths: {
|
|
232
|
+
findings: singlePaths.findingsJson,
|
|
233
|
+
runStatus: singlePaths.runStatusJson,
|
|
234
|
+
summary: singlePaths.summaryJson,
|
|
235
|
+
learn: singlePaths.learnJson,
|
|
236
|
+
observe: singlePaths.observeJson,
|
|
237
|
+
runDir: singlePaths.baseDir
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const determinismResult = await runWithDeterminism(scanFn, {
|
|
243
|
+
runs: determinismRuns,
|
|
244
|
+
out,
|
|
245
|
+
url: resolvedUrl,
|
|
246
|
+
fixture: fixtureId,
|
|
247
|
+
src,
|
|
248
|
+
verbose,
|
|
249
|
+
json
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// PHASE 25: Output determinism report path
|
|
253
|
+
if (!json) {
|
|
254
|
+
console.log(`\nDeterminism check complete.`);
|
|
255
|
+
console.log(`Verdict: ${determinismResult.verdict}`);
|
|
256
|
+
console.log(`Report: ${determinismResult.reportPath}`);
|
|
257
|
+
} else {
|
|
258
|
+
console.log(JSON.stringify({
|
|
259
|
+
type: 'determinism:complete',
|
|
260
|
+
verdict: determinismResult.verdict,
|
|
261
|
+
reportPath: determinismResult.reportPath,
|
|
262
|
+
summary: determinismResult.summary
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
success: determinismResult.verdict === 'DETERMINISTIC' || determinismResult.verdict === 'NON_DETERMINISTIC_EXPECTED',
|
|
268
|
+
verdict: determinismResult.verdict,
|
|
269
|
+
reportPath: determinismResult.reportPath
|
|
270
|
+
};
|
|
271
|
+
}
|
|
78
272
|
|
|
79
273
|
try {
|
|
80
274
|
// Generate run ID
|
|
@@ -128,6 +322,8 @@ export async function runCommand(options) {
|
|
|
128
322
|
startedAt = now.toISOString();
|
|
129
323
|
|
|
130
324
|
atomicWriteJson(paths.runStatusJson, {
|
|
325
|
+
contractVersion: 1,
|
|
326
|
+
artifactVersions: paths.artifactVersions,
|
|
131
327
|
status: 'RUNNING',
|
|
132
328
|
runId,
|
|
133
329
|
startedAt,
|
|
@@ -135,27 +331,69 @@ export async function runCommand(options) {
|
|
|
135
331
|
|
|
136
332
|
// Write metadata
|
|
137
333
|
atomicWriteJson(paths.runMetaJson, {
|
|
334
|
+
contractVersion: 1,
|
|
335
|
+
artifactVersions: paths.artifactVersions,
|
|
138
336
|
veraxVersion: getVersion(),
|
|
139
337
|
nodeVersion: process.version,
|
|
140
338
|
platform: process.platform,
|
|
141
339
|
cwd: projectRoot,
|
|
142
340
|
command: 'run',
|
|
143
|
-
args: { url, src, out },
|
|
144
|
-
url,
|
|
341
|
+
args: { url: resolvedUrl, fixture: fixtureId, src, out },
|
|
342
|
+
url: resolvedUrl,
|
|
343
|
+
fixtureId: fixtureId,
|
|
145
344
|
src: srcPath,
|
|
146
345
|
startedAt,
|
|
147
346
|
completedAt: null,
|
|
148
347
|
error: null,
|
|
149
348
|
});
|
|
150
349
|
|
|
151
|
-
//
|
|
350
|
+
// Extract expectations first to compute budget
|
|
152
351
|
events.emit('phase:started', {
|
|
153
352
|
phase: 'Learn',
|
|
154
353
|
message: 'Analyzing project structure...',
|
|
155
354
|
});
|
|
156
355
|
|
|
157
|
-
|
|
158
|
-
|
|
356
|
+
events.startHeartbeat('Learn', json);
|
|
357
|
+
|
|
358
|
+
let expectations, skipped;
|
|
359
|
+
try {
|
|
360
|
+
// Extract expectations (quick operation, no timeout needed here)
|
|
361
|
+
const result = await extractExpectations(projectProfile, projectProfile.sourceRoot);
|
|
362
|
+
expectations = result.expectations;
|
|
363
|
+
skipped = result.skipped;
|
|
364
|
+
} finally {
|
|
365
|
+
events.stopHeartbeat();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Compute runtime budget based on expectations count
|
|
369
|
+
budget = computeRuntimeBudget({
|
|
370
|
+
expectationsCount: expectations.length,
|
|
371
|
+
mode: 'run',
|
|
372
|
+
framework: projectProfile.framework,
|
|
373
|
+
fileCount: projectProfile.fileCount || expectations.length,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// Set up global watchdog timer
|
|
377
|
+
watchdogTimer = setTimeout(async () => {
|
|
378
|
+
await finalizeOnTimeout(`Global timeout exceeded: ${budget.totalMaxMs}ms`);
|
|
379
|
+
// Exit with code 0 (tool executed, just timed out)
|
|
380
|
+
process.exit(0);
|
|
381
|
+
}, budget.totalMaxMs);
|
|
382
|
+
|
|
383
|
+
// Wrap Learn phase with timeout
|
|
384
|
+
try {
|
|
385
|
+
await withTimeout(
|
|
386
|
+
budget.learnMaxMs,
|
|
387
|
+
Promise.resolve(), // Learn phase already completed
|
|
388
|
+
'Learn'
|
|
389
|
+
);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
if (error.message.includes('timeout')) {
|
|
392
|
+
await finalizeOnTimeout(`Learn phase timeout: ${budget.learnMaxMs}ms`);
|
|
393
|
+
process.exit(0);
|
|
394
|
+
}
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
159
397
|
|
|
160
398
|
// For now, emit a placeholder trace event
|
|
161
399
|
events.emit('phase:completed', {
|
|
@@ -163,39 +401,60 @@ export async function runCommand(options) {
|
|
|
163
401
|
message: 'Project analysis complete',
|
|
164
402
|
});
|
|
165
403
|
|
|
166
|
-
// Observe phase
|
|
404
|
+
// Observe phase with timeout
|
|
167
405
|
events.emit('phase:started', {
|
|
168
406
|
phase: 'Observe',
|
|
169
407
|
message: 'Launching browser and observing expectations...',
|
|
170
408
|
});
|
|
171
409
|
|
|
410
|
+
events.startHeartbeat('Observe', json);
|
|
411
|
+
|
|
172
412
|
let observeData = null;
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
413
|
+
try {
|
|
414
|
+
if (expectations.length > 0) {
|
|
415
|
+
try {
|
|
416
|
+
observeData = await withTimeout(
|
|
417
|
+
budget.observeMaxMs,
|
|
418
|
+
observeExpectations(
|
|
419
|
+
expectations,
|
|
420
|
+
url,
|
|
421
|
+
paths.evidenceDir,
|
|
422
|
+
(progress) => {
|
|
423
|
+
events.emit(progress.event, progress);
|
|
424
|
+
}
|
|
425
|
+
),
|
|
426
|
+
'Observe'
|
|
427
|
+
);
|
|
428
|
+
} catch (error) {
|
|
429
|
+
if (error.message.includes('timeout')) {
|
|
430
|
+
events.emit('observe:error', {
|
|
431
|
+
message: `Observe phase timeout: ${budget.observeMaxMs}ms`,
|
|
432
|
+
});
|
|
433
|
+
observeData = {
|
|
434
|
+
observations: [],
|
|
435
|
+
stats: { attempted: 0, observed: 0, notObserved: 0 },
|
|
436
|
+
observedAt: new Date().toISOString(),
|
|
437
|
+
};
|
|
438
|
+
} else {
|
|
439
|
+
events.emit('observe:error', {
|
|
440
|
+
message: error.message,
|
|
441
|
+
});
|
|
442
|
+
observeData = {
|
|
443
|
+
observations: [],
|
|
444
|
+
stats: { attempted: 0, observed: 0, notObserved: 0 },
|
|
445
|
+
observedAt: new Date().toISOString(),
|
|
446
|
+
};
|
|
181
447
|
}
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
events.emit('observe:error', {
|
|
185
|
-
message: error.message,
|
|
186
|
-
});
|
|
448
|
+
}
|
|
449
|
+
} else {
|
|
187
450
|
observeData = {
|
|
188
451
|
observations: [],
|
|
189
452
|
stats: { attempted: 0, observed: 0, notObserved: 0 },
|
|
190
453
|
observedAt: new Date().toISOString(),
|
|
191
454
|
};
|
|
192
455
|
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
observations: [],
|
|
196
|
-
stats: { attempted: 0, observed: 0, notObserved: 0 },
|
|
197
|
-
observedAt: new Date().toISOString(),
|
|
198
|
-
};
|
|
456
|
+
} finally {
|
|
457
|
+
events.stopHeartbeat();
|
|
199
458
|
}
|
|
200
459
|
|
|
201
460
|
events.emit('phase:completed', {
|
|
@@ -203,32 +462,53 @@ export async function runCommand(options) {
|
|
|
203
462
|
message: 'Browser observation complete',
|
|
204
463
|
});
|
|
205
464
|
|
|
206
|
-
// Detect phase
|
|
465
|
+
// Detect phase with timeout
|
|
207
466
|
events.emit('phase:started', {
|
|
208
467
|
phase: 'Detect',
|
|
209
468
|
message: 'Analyzing findings and detecting silent failures...',
|
|
210
469
|
});
|
|
211
470
|
|
|
471
|
+
events.startHeartbeat('Detect', json);
|
|
472
|
+
|
|
212
473
|
let detectData = null;
|
|
213
474
|
try {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
475
|
+
try {
|
|
476
|
+
// Use already-extracted expectations
|
|
477
|
+
const learnData = {
|
|
478
|
+
expectations,
|
|
479
|
+
skipped,
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
detectData = await withTimeout(
|
|
483
|
+
budget.detectMaxMs,
|
|
484
|
+
detectFindings(learnData, observeData, projectRoot, (progress) => {
|
|
485
|
+
events.emit(progress.event, progress);
|
|
486
|
+
}),
|
|
487
|
+
'Detect'
|
|
488
|
+
);
|
|
489
|
+
} catch (error) {
|
|
490
|
+
if (error.message.includes('timeout')) {
|
|
491
|
+
events.emit('detect:error', {
|
|
492
|
+
message: `Detect phase timeout: ${budget.detectMaxMs}ms`,
|
|
493
|
+
});
|
|
494
|
+
detectData = {
|
|
495
|
+
findings: [],
|
|
496
|
+
stats: { total: 0, silentFailures: 0, observed: 0, coverageGaps: 0, unproven: 0, informational: 0 },
|
|
497
|
+
detectedAt: new Date().toISOString(),
|
|
498
|
+
};
|
|
499
|
+
} else {
|
|
500
|
+
events.emit('detect:error', {
|
|
501
|
+
message: error.message,
|
|
502
|
+
});
|
|
503
|
+
detectData = {
|
|
504
|
+
findings: [],
|
|
505
|
+
stats: { total: 0, silentFailures: 0, observed: 0, coverageGaps: 0, unproven: 0, informational: 0 },
|
|
506
|
+
detectedAt: new Date().toISOString(),
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
} finally {
|
|
511
|
+
events.stopHeartbeat();
|
|
232
512
|
}
|
|
233
513
|
|
|
234
514
|
events.emit('phase:completed', {
|
|
@@ -236,37 +516,39 @@ export async function runCommand(options) {
|
|
|
236
516
|
message: 'Silent failure detection complete',
|
|
237
517
|
});
|
|
238
518
|
|
|
519
|
+
// Clear watchdog timer on successful completion
|
|
520
|
+
if (watchdogTimer) {
|
|
521
|
+
clearTimeout(watchdogTimer);
|
|
522
|
+
watchdogTimer = null;
|
|
523
|
+
}
|
|
524
|
+
|
|
239
525
|
// Emit finalize phase
|
|
240
526
|
events.emit('phase:started', {
|
|
241
527
|
phase: 'Finalize Artifacts',
|
|
242
528
|
message: 'Writing run results...',
|
|
243
529
|
});
|
|
244
530
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
// Write completed status
|
|
248
|
-
atomicWriteJson(paths.runStatusJson, {
|
|
249
|
-
status: 'COMPLETE',
|
|
250
|
-
runId,
|
|
251
|
-
startedAt,
|
|
252
|
-
completedAt,
|
|
253
|
-
});
|
|
531
|
+
events.stopHeartbeat();
|
|
254
532
|
|
|
255
|
-
|
|
256
|
-
atomicWriteJson(paths.runMetaJson, {
|
|
257
|
-
veraxVersion: getVersion(),
|
|
258
|
-
nodeVersion: process.version,
|
|
259
|
-
platform: process.platform,
|
|
260
|
-
cwd: projectRoot,
|
|
261
|
-
command: 'run',
|
|
262
|
-
args: { url, src, out },
|
|
263
|
-
url,
|
|
264
|
-
src: srcPath,
|
|
265
|
-
startedAt,
|
|
266
|
-
completedAt,
|
|
267
|
-
error: null,
|
|
268
|
-
});
|
|
533
|
+
const completedAt = new Date().toISOString();
|
|
269
534
|
|
|
535
|
+
const runDurationMs = completedAt && startedAt ? (Date.parse(completedAt) - Date.parse(startedAt)) : 0;
|
|
536
|
+
const metrics = {
|
|
537
|
+
learnMs: observeData?.timings?.learnMs || 0,
|
|
538
|
+
observeMs: observeData?.timings?.observeMs || observeData?.timings?.totalMs || 0,
|
|
539
|
+
detectMs: detectData?.timings?.detectMs || detectData?.timings?.totalMs || 0,
|
|
540
|
+
totalMs: runDurationMs > 0 ? runDurationMs : (budget?.ms || 0)
|
|
541
|
+
};
|
|
542
|
+
const findingsCounts = detectData?.findingsCounts || {
|
|
543
|
+
HIGH: 0,
|
|
544
|
+
MEDIUM: 0,
|
|
545
|
+
LOW: 0,
|
|
546
|
+
UNKNOWN: 0,
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// Write detect results (or empty if detection failed)
|
|
550
|
+
const findingsResult = writeFindingsJson(paths.baseDir, detectData);
|
|
551
|
+
|
|
270
552
|
// Write summary with stable digest
|
|
271
553
|
writeSummaryJson(paths.summaryJson, {
|
|
272
554
|
runId,
|
|
@@ -276,6 +558,8 @@ export async function runCommand(options) {
|
|
|
276
558
|
command: 'run',
|
|
277
559
|
url,
|
|
278
560
|
notes: 'Run completed successfully',
|
|
561
|
+
metrics,
|
|
562
|
+
findingsCounts,
|
|
279
563
|
}, {
|
|
280
564
|
expectationsTotal: expectations.length,
|
|
281
565
|
attempted: observeData.stats?.attempted || 0,
|
|
@@ -284,36 +568,15 @@ export async function runCommand(options) {
|
|
|
284
568
|
coverageGaps: detectData.stats?.coverageGaps || 0,
|
|
285
569
|
unproven: detectData.stats?.unproven || 0,
|
|
286
570
|
informational: detectData.stats?.informational || 0,
|
|
571
|
+
...metrics,
|
|
572
|
+
...findingsCounts,
|
|
287
573
|
});
|
|
288
574
|
|
|
289
|
-
// Write
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
{
|
|
295
|
-
type: 'phase:started',
|
|
296
|
-
timestamp: startedAt,
|
|
297
|
-
phase: 'Detect Project',
|
|
298
|
-
},
|
|
299
|
-
{
|
|
300
|
-
type: 'phase:completed',
|
|
301
|
-
timestamp: new Date().toISOString(),
|
|
302
|
-
phase: 'Detect Project',
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
type: 'phase:started',
|
|
306
|
-
timestamp: new Date().toISOString(),
|
|
307
|
-
phase: 'Learn',
|
|
308
|
-
},
|
|
309
|
-
{
|
|
310
|
-
type: 'phase:completed',
|
|
311
|
-
timestamp: completedAt,
|
|
312
|
-
phase: 'Learn',
|
|
313
|
-
},
|
|
314
|
-
];
|
|
315
|
-
|
|
316
|
-
const tracesContent = traces.map(t => JSON.stringify(t)).join('\n') + '\n';
|
|
575
|
+
// Write traces (include all events including heartbeats)
|
|
576
|
+
const allEvents = events.getEvents();
|
|
577
|
+
const tracesContent = allEvents
|
|
578
|
+
.map(e => JSON.stringify(e))
|
|
579
|
+
.join('\n') + '\n';
|
|
317
580
|
atomicWriteText(paths.tracesJsonl, tracesContent);
|
|
318
581
|
|
|
319
582
|
// Write project profile
|
|
@@ -324,26 +587,155 @@ export async function runCommand(options) {
|
|
|
324
587
|
|
|
325
588
|
// Write observe results
|
|
326
589
|
writeObserveJson(paths.baseDir, observeData);
|
|
590
|
+
|
|
591
|
+
// PHASE 6: Verify artifacts after all writers finish
|
|
592
|
+
const { verifyRun } = await import('../../verax/core/artifacts/verifier.js');
|
|
593
|
+
const verification = verifyRun(paths.baseDir, paths.artifactVersions);
|
|
594
|
+
|
|
595
|
+
// Determine final status based on verification
|
|
596
|
+
let finalStatus = 'COMPLETE';
|
|
597
|
+
if (!verification.ok) {
|
|
598
|
+
finalStatus = 'INVALID';
|
|
599
|
+
} else if (verification.warnings.length > 0) {
|
|
600
|
+
finalStatus = 'VALID_WITH_WARNINGS';
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// PHASE 21.2: Compute determinism summary for run.status.json
|
|
604
|
+
let determinismSummary = null;
|
|
605
|
+
try {
|
|
606
|
+
const decisionsPath = resolve(paths.baseDir, 'decisions.json');
|
|
607
|
+
if (existsSync(decisionsPath)) {
|
|
608
|
+
const decisions = JSON.parse(readFileSync(decisionsPath, 'utf-8'));
|
|
609
|
+
const { DecisionRecorder } = await import('../../verax/core/determinism-model.js');
|
|
610
|
+
const recorder = DecisionRecorder.fromExport(decisions);
|
|
611
|
+
const { computeDeterminismVerdict } = await import('../../verax/core/determinism/contract.js');
|
|
612
|
+
const verdict = computeDeterminismVerdict(recorder);
|
|
613
|
+
|
|
614
|
+
determinismSummary = {
|
|
615
|
+
verdict: verdict.verdict, // DETERMINISTIC or NON_DETERMINISTIC
|
|
616
|
+
message: verdict.message,
|
|
617
|
+
adaptiveEventsCount: verdict.adaptiveEvents.length
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
} catch (error) {
|
|
621
|
+
// Ignore errors - determinism summary is optional
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// Write completed status with contract + enforcement snapshot + verification + determinism
|
|
625
|
+
atomicWriteJson(paths.runStatusJson, {
|
|
626
|
+
contractVersion: 1,
|
|
627
|
+
artifactVersions: paths.artifactVersions,
|
|
628
|
+
status: finalStatus,
|
|
629
|
+
runId,
|
|
630
|
+
startedAt,
|
|
631
|
+
completedAt,
|
|
632
|
+
enforcement: findingsResult?.payload?.enforcement || null,
|
|
633
|
+
verification: {
|
|
634
|
+
ok: verification.ok,
|
|
635
|
+
status: finalStatus,
|
|
636
|
+
errorsCount: verification.errors.length,
|
|
637
|
+
warningsCount: verification.warnings.length,
|
|
638
|
+
verifiedAt: verification.verifiedAt
|
|
639
|
+
},
|
|
640
|
+
// PHASE 21.2: Determinism summary
|
|
641
|
+
determinismSummary: determinismSummary
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
// Update metadata with completion time
|
|
645
|
+
atomicWriteJson(paths.runMetaJson, {
|
|
646
|
+
contractVersion: 1,
|
|
647
|
+
artifactVersions: paths.artifactVersions,
|
|
648
|
+
veraxVersion: getVersion(),
|
|
649
|
+
nodeVersion: process.version,
|
|
650
|
+
platform: process.platform,
|
|
651
|
+
cwd: projectRoot,
|
|
652
|
+
command: 'run',
|
|
653
|
+
args: { url: resolvedUrl, fixture: fixtureId, src, out },
|
|
654
|
+
url: resolvedUrl,
|
|
655
|
+
fixtureId: fixtureId,
|
|
656
|
+
src: srcPath,
|
|
657
|
+
startedAt,
|
|
658
|
+
completedAt,
|
|
659
|
+
error: null,
|
|
660
|
+
});
|
|
327
661
|
|
|
328
662
|
events.emit('phase:completed', {
|
|
329
663
|
phase: 'Finalize Artifacts',
|
|
330
664
|
message: 'Run artifacts written',
|
|
331
665
|
});
|
|
332
666
|
|
|
667
|
+
// Emit final summary event
|
|
668
|
+
if (json) {
|
|
669
|
+
events.emit('run:complete', {
|
|
670
|
+
runId,
|
|
671
|
+
url,
|
|
672
|
+
command: 'run',
|
|
673
|
+
findingsCounts,
|
|
674
|
+
metrics,
|
|
675
|
+
digest: {
|
|
676
|
+
expectationsTotal: expectations.length,
|
|
677
|
+
attempted: observeData.stats?.attempted || 0,
|
|
678
|
+
observed: observeData.stats?.observed || 0,
|
|
679
|
+
silentFailures: detectData.stats?.silentFailures || 0,
|
|
680
|
+
coverageGaps: detectData.stats?.coverageGaps || 0,
|
|
681
|
+
unproven: detectData.stats?.unproven || 0,
|
|
682
|
+
informational: detectData.stats?.informational || 0,
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
|
|
333
687
|
// Print summary if not JSON mode
|
|
334
688
|
if (!json) {
|
|
335
689
|
console.log('\nRun complete.');
|
|
336
690
|
console.log(`Run ID: ${runId}`);
|
|
337
691
|
console.log(`Artifacts: ${paths.baseDir}`);
|
|
692
|
+
|
|
693
|
+
// PHASE 21.2: Display determinism truth
|
|
694
|
+
if (determinismSummary) {
|
|
695
|
+
console.log('');
|
|
696
|
+
if (determinismSummary.verdict === 'DETERMINISTIC') {
|
|
697
|
+
console.log('Deterministic: YES');
|
|
698
|
+
} else {
|
|
699
|
+
console.log(`Deterministic: NO (${determinismSummary.message})`);
|
|
700
|
+
if (determinismSummary.adaptiveEventsCount > 0) {
|
|
701
|
+
console.log(` Adaptive events detected: ${determinismSummary.adaptiveEventsCount}`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// PHASE 6: Display verification results
|
|
707
|
+
const { formatVerificationOutput } = await import('../../verax/core/artifacts/verifier.js');
|
|
708
|
+
const verificationOutput = formatVerificationOutput(verification, verbose);
|
|
709
|
+
console.log('');
|
|
710
|
+
console.log(verificationOutput);
|
|
711
|
+
|
|
712
|
+
// PHASE 21.9: Display performance metrics
|
|
713
|
+
const { loadPerformanceReport } = await import('../../verax/core/perf/perf.report.js');
|
|
714
|
+
const { formatPerformanceMetrics } = await import('../../verax/core/perf/perf.display.js');
|
|
715
|
+
const perfReport = loadPerformanceReport(projectRoot, runId);
|
|
716
|
+
if (perfReport) {
|
|
717
|
+
console.log('');
|
|
718
|
+
console.log(formatPerformanceMetrics(perfReport));
|
|
719
|
+
}
|
|
338
720
|
}
|
|
339
721
|
|
|
340
722
|
return { runId, paths, success: true };
|
|
341
723
|
} catch (error) {
|
|
724
|
+
// Clear watchdog timer on error
|
|
725
|
+
if (watchdogTimer) {
|
|
726
|
+
clearTimeout(watchdogTimer);
|
|
727
|
+
watchdogTimer = null;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
events.stopHeartbeat();
|
|
731
|
+
|
|
342
732
|
// Mark run as FAILED if we have paths
|
|
343
733
|
if (paths && runId && startedAt) {
|
|
344
734
|
try {
|
|
345
735
|
const failedAt = new Date().toISOString();
|
|
346
736
|
atomicWriteJson(paths.runStatusJson, {
|
|
737
|
+
contractVersion: 1,
|
|
738
|
+
artifactVersions: paths.artifactVersions,
|
|
347
739
|
status: 'FAILED',
|
|
348
740
|
runId,
|
|
349
741
|
startedAt,
|
|
@@ -353,13 +745,16 @@ export async function runCommand(options) {
|
|
|
353
745
|
|
|
354
746
|
// Update metadata
|
|
355
747
|
atomicWriteJson(paths.runMetaJson, {
|
|
748
|
+
contractVersion: 1,
|
|
749
|
+
artifactVersions: paths.artifactVersions,
|
|
356
750
|
veraxVersion: getVersion(),
|
|
357
751
|
nodeVersion: process.version,
|
|
358
752
|
platform: process.platform,
|
|
359
753
|
cwd: projectRoot,
|
|
360
754
|
command: 'run',
|
|
361
|
-
args: { url, src, out },
|
|
362
|
-
url,
|
|
755
|
+
args: { url: resolvedUrl || url, fixture: fixtureId, src, out },
|
|
756
|
+
url: resolvedUrl || url,
|
|
757
|
+
fixtureId: fixtureId,
|
|
363
758
|
src: srcPath,
|
|
364
759
|
startedAt,
|
|
365
760
|
completedAt: failedAt,
|
|
@@ -374,7 +769,7 @@ export async function runCommand(options) {
|
|
|
374
769
|
startedAt,
|
|
375
770
|
completedAt: failedAt,
|
|
376
771
|
command: 'run',
|
|
377
|
-
url,
|
|
772
|
+
url: resolvedUrl || url,
|
|
378
773
|
notes: `Run failed: ${error.message}`,
|
|
379
774
|
}, {
|
|
380
775
|
expectationsTotal: 0,
|