@veraxhq/verax 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -18
- package/bin/verax.js +7 -0
- package/package.json +3 -3
- package/src/cli/commands/baseline.js +104 -0
- package/src/cli/commands/default.js +79 -25
- package/src/cli/commands/ga.js +243 -0
- package/src/cli/commands/gates.js +95 -0
- package/src/cli/commands/inspect.js +131 -2
- package/src/cli/commands/release-check.js +213 -0
- package/src/cli/commands/run.js +246 -35
- package/src/cli/commands/security-check.js +211 -0
- package/src/cli/commands/truth.js +114 -0
- package/src/cli/entry.js +304 -67
- package/src/cli/util/angular-component-extractor.js +179 -0
- package/src/cli/util/angular-navigation-detector.js +141 -0
- package/src/cli/util/angular-network-detector.js +161 -0
- package/src/cli/util/angular-state-detector.js +162 -0
- package/src/cli/util/ast-interactive-detector.js +546 -0
- package/src/cli/util/ast-network-detector.js +603 -0
- package/src/cli/util/ast-usestate-detector.js +602 -0
- package/src/cli/util/bootstrap-guard.js +86 -0
- package/src/cli/util/determinism-runner.js +123 -0
- package/src/cli/util/determinism-writer.js +129 -0
- package/src/cli/util/env-url.js +4 -0
- package/src/cli/util/expectation-extractor.js +369 -73
- package/src/cli/util/findings-writer.js +126 -16
- package/src/cli/util/learn-writer.js +3 -1
- package/src/cli/util/observe-writer.js +3 -1
- package/src/cli/util/paths.js +3 -12
- package/src/cli/util/project-discovery.js +3 -0
- package/src/cli/util/project-writer.js +3 -1
- package/src/cli/util/run-resolver.js +64 -0
- package/src/cli/util/source-requirement.js +55 -0
- package/src/cli/util/summary-writer.js +1 -0
- package/src/cli/util/svelte-navigation-detector.js +163 -0
- package/src/cli/util/svelte-network-detector.js +80 -0
- package/src/cli/util/svelte-sfc-extractor.js +147 -0
- package/src/cli/util/svelte-state-detector.js +243 -0
- package/src/cli/util/vue-navigation-detector.js +177 -0
- package/src/cli/util/vue-sfc-extractor.js +162 -0
- package/src/cli/util/vue-state-detector.js +215 -0
- package/src/verax/cli/finding-explainer.js +56 -3
- package/src/verax/core/artifacts/registry.js +154 -0
- package/src/verax/core/artifacts/verifier.js +980 -0
- package/src/verax/core/baseline/baseline.enforcer.js +137 -0
- package/src/verax/core/baseline/baseline.snapshot.js +231 -0
- package/src/verax/core/capabilities/gates.js +499 -0
- package/src/verax/core/capabilities/registry.js +475 -0
- package/src/verax/core/confidence/confidence-compute.js +137 -0
- package/src/verax/core/confidence/confidence-invariants.js +234 -0
- package/src/verax/core/confidence/confidence-report-writer.js +112 -0
- package/src/verax/core/confidence/confidence-weights.js +44 -0
- package/src/verax/core/confidence/confidence.defaults.js +65 -0
- package/src/verax/core/confidence/confidence.loader.js +79 -0
- package/src/verax/core/confidence/confidence.schema.js +94 -0
- package/src/verax/core/confidence-engine-refactor.js +484 -0
- package/src/verax/core/confidence-engine.js +486 -0
- package/src/verax/core/confidence-engine.js.backup +471 -0
- package/src/verax/core/contracts/index.js +29 -0
- package/src/verax/core/contracts/types.js +185 -0
- package/src/verax/core/contracts/validators.js +381 -0
- package/src/verax/core/decision-snapshot.js +30 -3
- package/src/verax/core/decisions/decision.trace.js +276 -0
- package/src/verax/core/determinism/contract-writer.js +89 -0
- package/src/verax/core/determinism/contract.js +139 -0
- package/src/verax/core/determinism/diff.js +364 -0
- package/src/verax/core/determinism/engine.js +221 -0
- package/src/verax/core/determinism/finding-identity.js +148 -0
- package/src/verax/core/determinism/normalize.js +438 -0
- package/src/verax/core/determinism/report-writer.js +92 -0
- package/src/verax/core/determinism/run-fingerprint.js +118 -0
- package/src/verax/core/dynamic-route-intelligence.js +528 -0
- package/src/verax/core/evidence/evidence-capture-service.js +307 -0
- package/src/verax/core/evidence/evidence-intent-ledger.js +165 -0
- package/src/verax/core/evidence-builder.js +487 -0
- package/src/verax/core/execution-mode-context.js +77 -0
- package/src/verax/core/execution-mode-detector.js +190 -0
- package/src/verax/core/failures/exit-codes.js +86 -0
- package/src/verax/core/failures/failure-summary.js +76 -0
- package/src/verax/core/failures/failure.factory.js +225 -0
- package/src/verax/core/failures/failure.ledger.js +132 -0
- package/src/verax/core/failures/failure.types.js +196 -0
- package/src/verax/core/failures/index.js +10 -0
- package/src/verax/core/ga/ga-report-writer.js +43 -0
- package/src/verax/core/ga/ga.artifact.js +49 -0
- package/src/verax/core/ga/ga.contract.js +434 -0
- package/src/verax/core/ga/ga.enforcer.js +86 -0
- package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
- package/src/verax/core/guardrails/policy.defaults.js +210 -0
- package/src/verax/core/guardrails/policy.loader.js +83 -0
- package/src/verax/core/guardrails/policy.schema.js +110 -0
- package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
- package/src/verax/core/guardrails-engine.js +505 -0
- package/src/verax/core/observe/run-timeline.js +316 -0
- package/src/verax/core/perf/perf.contract.js +186 -0
- package/src/verax/core/perf/perf.display.js +65 -0
- package/src/verax/core/perf/perf.enforcer.js +91 -0
- package/src/verax/core/perf/perf.monitor.js +209 -0
- package/src/verax/core/perf/perf.report.js +198 -0
- package/src/verax/core/pipeline-tracker.js +238 -0
- package/src/verax/core/product-definition.js +127 -0
- package/src/verax/core/release/provenance.builder.js +271 -0
- package/src/verax/core/release/release-report-writer.js +40 -0
- package/src/verax/core/release/release.enforcer.js +159 -0
- package/src/verax/core/release/reproducibility.check.js +221 -0
- package/src/verax/core/release/sbom.builder.js +283 -0
- package/src/verax/core/report/cross-index.js +192 -0
- package/src/verax/core/report/human-summary.js +222 -0
- package/src/verax/core/route-intelligence.js +419 -0
- package/src/verax/core/security/secrets.scan.js +326 -0
- package/src/verax/core/security/security-report.js +50 -0
- package/src/verax/core/security/security.enforcer.js +124 -0
- package/src/verax/core/security/supplychain.defaults.json +38 -0
- package/src/verax/core/security/supplychain.policy.js +326 -0
- package/src/verax/core/security/vuln.scan.js +265 -0
- package/src/verax/core/truth/truth.certificate.js +250 -0
- package/src/verax/core/ui-feedback-intelligence.js +515 -0
- package/src/verax/detect/confidence-engine.js +628 -40
- package/src/verax/detect/confidence-helper.js +33 -0
- package/src/verax/detect/detection-engine.js +18 -1
- package/src/verax/detect/dynamic-route-findings.js +335 -0
- package/src/verax/detect/expectation-chain-detector.js +417 -0
- package/src/verax/detect/expectation-model.js +3 -1
- package/src/verax/detect/findings-writer.js +141 -5
- package/src/verax/detect/index.js +229 -5
- package/src/verax/detect/journey-stall-detector.js +558 -0
- package/src/verax/detect/route-findings.js +218 -0
- package/src/verax/detect/ui-feedback-findings.js +207 -0
- package/src/verax/detect/verdict-engine.js +57 -3
- package/src/verax/detect/view-switch-correlator.js +242 -0
- package/src/verax/index.js +413 -45
- package/src/verax/learn/action-contract-extractor.js +682 -64
- package/src/verax/learn/route-validator.js +4 -1
- package/src/verax/observe/index.js +88 -843
- package/src/verax/observe/interaction-runner.js +25 -8
- package/src/verax/observe/observe-context.js +205 -0
- package/src/verax/observe/observe-helpers.js +191 -0
- package/src/verax/observe/observe-runner.js +226 -0
- package/src/verax/observe/observers/budget-observer.js +185 -0
- package/src/verax/observe/observers/console-observer.js +102 -0
- package/src/verax/observe/observers/coverage-observer.js +107 -0
- package/src/verax/observe/observers/interaction-observer.js +471 -0
- package/src/verax/observe/observers/navigation-observer.js +132 -0
- package/src/verax/observe/observers/network-observer.js +87 -0
- package/src/verax/observe/observers/safety-observer.js +82 -0
- package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
- package/src/verax/observe/ui-feedback-detector.js +742 -0
- package/src/verax/observe/ui-signal-sensor.js +148 -2
- package/src/verax/scan-summary-writer.js +42 -8
- package/src/verax/shared/artifact-manager.js +8 -5
- package/src/verax/shared/css-spinner-rules.js +204 -0
- package/src/verax/shared/view-switch-rules.js +208 -0
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# 🛡️ VERAX
|
|
2
2
|
|
|
3
|
+
> **SOURCE CODE REQUIRED** — VERAX requires local access to source code. It is not a public website scanner. See [docs/README.md](docs/README.md) for the canonical product contract.
|
|
4
|
+
|
|
3
5
|
A forensic observation engine for real user outcomes
|
|
4
6
|
|
|
5
7
|
VERAX observes and reports gaps between what your code explicitly promises and what users can actually observe.
|
|
@@ -229,27 +231,21 @@ MEDIUM (60–79) — likely discrepancy with some ambiguity
|
|
|
229
231
|
|
|
230
232
|
LOW (<60) — weak or partial evidence; interpret cautiously
|
|
231
233
|
|
|
232
|
-
🧭
|
|
233
|
-
|
|
234
|
-
SaaS signup and pricing flows
|
|
235
|
-
|
|
236
|
-
React and Next.js projects
|
|
237
|
-
|
|
238
|
-
CI pipelines that need UX reality checks
|
|
239
|
-
|
|
240
|
-
Teams that value evidence over assumptions
|
|
241
|
-
|
|
242
|
-
🚫 When VERAX is NOT a good fit
|
|
243
|
-
|
|
244
|
-
Internal admin dashboards
|
|
245
|
-
|
|
246
|
-
Authentication-heavy systems
|
|
234
|
+
🧭 Supported use cases
|
|
247
235
|
|
|
248
|
-
|
|
236
|
+
- React, Next.js, or static HTML projects where you can provide local source code
|
|
237
|
+
- CI pipelines that can mount the repository so VERAX can analyze it
|
|
238
|
+
- Developer workstations that need evidence-backed silent failure detection
|
|
239
|
+
- Teams that value deterministic evidence over heuristics or AI guesses
|
|
240
|
+
- Demo projects in this repo (see [demos/](demos/))
|
|
249
241
|
|
|
250
|
-
|
|
242
|
+
🚫 Not supported
|
|
251
243
|
|
|
252
|
-
|
|
244
|
+
- URL-only scans without source code (fails fast with guidance)
|
|
245
|
+
- Production monitoring or hosted/public scanning
|
|
246
|
+
- Highly dynamic routing without static promises to analyze
|
|
247
|
+
- Closed-source third-party targets where code is unavailable
|
|
248
|
+
- Using VERAX as a drop-in QA replacement
|
|
253
249
|
|
|
254
250
|
🧪 Project status
|
|
255
251
|
|
package/bin/verax.js
CHANGED
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* VERAX CLI Shim
|
|
5
|
+
* PHASE 21.6.1: Verified entry point
|
|
5
6
|
* Delegates to src/cli/entry.js
|
|
7
|
+
*
|
|
8
|
+
* This file MUST be the only entry point for the verax CLI.
|
|
9
|
+
* package.json "bin" field points to this file.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
12
|
import('../src/cli/entry.js').catch((error) => {
|
|
9
13
|
console.error(`Failed to load CLI: ${error.message}`);
|
|
14
|
+
if (error.stack) {
|
|
15
|
+
console.error(error.stack);
|
|
16
|
+
}
|
|
10
17
|
process.exit(2);
|
|
11
18
|
});
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veraxhq/verax",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "VERAX - Silent failure detection for websites",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"verax": "
|
|
8
|
+
"verax": "bin/verax.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"bin/",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@babel/parser": "^7.28.5",
|
|
35
35
|
"@babel/traverse": "^7.28.5",
|
|
36
36
|
"@reduxjs/toolkit": "^2.11.2",
|
|
37
|
-
"@veraxhq/verax": "file:veraxhq-verax-0.
|
|
37
|
+
"@veraxhq/verax": "file:veraxhq-verax-0.3.0.tgz",
|
|
38
38
|
"eslint": "^8.57.0",
|
|
39
39
|
"next": "^16.1.1",
|
|
40
40
|
"react": "^19.2.3",
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.11 — Baseline Command
|
|
3
|
+
*
|
|
4
|
+
* `verax baseline` - Shows baseline hash and drift status
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { resolve } from 'path';
|
|
8
|
+
import { loadBaselineSnapshot, buildBaselineSnapshot } from '../../verax/core/baseline/baseline.snapshot.js';
|
|
9
|
+
import { compareBaselines, isBaselineFrozen } from '../../verax/core/baseline/baseline.enforcer.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Baseline command
|
|
13
|
+
*
|
|
14
|
+
* @param {string} projectDir - Project directory
|
|
15
|
+
* @param {Object} options - Command options
|
|
16
|
+
*/
|
|
17
|
+
export async function baselineCommand(projectDir, options = {}) {
|
|
18
|
+
const { json = false } = options;
|
|
19
|
+
|
|
20
|
+
const frozen = loadBaselineSnapshot(projectDir);
|
|
21
|
+
const current = buildBaselineSnapshot(projectDir);
|
|
22
|
+
|
|
23
|
+
if (!frozen) {
|
|
24
|
+
if (json) {
|
|
25
|
+
console.log(JSON.stringify({
|
|
26
|
+
status: 'NO_BASELINE',
|
|
27
|
+
message: 'No baseline snapshot found (pre-GA)',
|
|
28
|
+
current: {
|
|
29
|
+
hash: current.baselineHash,
|
|
30
|
+
version: current.veraxVersion,
|
|
31
|
+
commit: current.gitCommit
|
|
32
|
+
}
|
|
33
|
+
}, null, 2));
|
|
34
|
+
} else {
|
|
35
|
+
console.log('\n=== Baseline Status ===\n');
|
|
36
|
+
console.log('Status: NO_BASELINE (pre-GA)');
|
|
37
|
+
console.log(`Current baseline hash: ${current.baselineHash}`);
|
|
38
|
+
console.log(`Version: ${current.veraxVersion}`);
|
|
39
|
+
console.log(`Commit: ${current.gitCommit}`);
|
|
40
|
+
console.log(`Dirty: ${current.gitDirty ? 'YES' : 'NO'}`);
|
|
41
|
+
console.log('\nNote: Baseline will be frozen when GA-READY is achieved.\n');
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const comparison = compareBaselines(current, frozen);
|
|
47
|
+
const frozenStatus = frozen.frozen ? 'FROZEN' : 'NOT_FROZEN';
|
|
48
|
+
|
|
49
|
+
if (json) {
|
|
50
|
+
console.log(JSON.stringify({
|
|
51
|
+
status: frozenStatus,
|
|
52
|
+
frozen: frozen.frozen,
|
|
53
|
+
drifted: comparison.drifted,
|
|
54
|
+
message: comparison.message,
|
|
55
|
+
frozenBaseline: {
|
|
56
|
+
hash: frozen.baselineHash,
|
|
57
|
+
version: frozen.veraxVersion,
|
|
58
|
+
commit: frozen.gitCommit,
|
|
59
|
+
timestamp: frozen.timestamp
|
|
60
|
+
},
|
|
61
|
+
currentBaseline: {
|
|
62
|
+
hash: current.baselineHash,
|
|
63
|
+
version: current.veraxVersion,
|
|
64
|
+
commit: current.gitCommit
|
|
65
|
+
},
|
|
66
|
+
differences: comparison.differences
|
|
67
|
+
}, null, 2));
|
|
68
|
+
} else {
|
|
69
|
+
console.log('\n=== Baseline Status ===\n');
|
|
70
|
+
console.log(`Status: ${frozenStatus}`);
|
|
71
|
+
console.log(`Frozen: ${frozen.frozen ? 'YES' : 'NO'}`);
|
|
72
|
+
console.log(`Drifted: ${comparison.drifted ? 'YES' : 'NO'}`);
|
|
73
|
+
console.log(`\nMessage: ${comparison.message}`);
|
|
74
|
+
|
|
75
|
+
console.log('\nFrozen Baseline:');
|
|
76
|
+
console.log(` Hash: ${frozen.baselineHash}`);
|
|
77
|
+
console.log(` Version: ${frozen.veraxVersion}`);
|
|
78
|
+
console.log(` Commit: ${frozen.gitCommit}`);
|
|
79
|
+
console.log(` Timestamp: ${frozen.timestamp}`);
|
|
80
|
+
|
|
81
|
+
console.log('\nCurrent Baseline:');
|
|
82
|
+
console.log(` Hash: ${current.baselineHash}`);
|
|
83
|
+
console.log(` Version: ${current.veraxVersion}`);
|
|
84
|
+
console.log(` Commit: ${current.gitCommit}`);
|
|
85
|
+
console.log(` Dirty: ${current.gitDirty ? 'YES' : 'NO'}`);
|
|
86
|
+
|
|
87
|
+
if (comparison.drifted) {
|
|
88
|
+
console.log('\n⚠️ BASELINE DRIFT DETECTED:');
|
|
89
|
+
for (const diff of comparison.differences) {
|
|
90
|
+
console.log(` - ${diff.message}`);
|
|
91
|
+
if (diff.component) {
|
|
92
|
+
console.log(` Component: ${diff.component}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
console.log('\n⚠️ Changes to core contracts/policies after GA require:');
|
|
96
|
+
console.log(' 1. MAJOR version bump');
|
|
97
|
+
console.log(' 2. Baseline regeneration');
|
|
98
|
+
console.log(' 3. GA re-evaluation\n');
|
|
99
|
+
} else {
|
|
100
|
+
console.log('\n✓ Baseline integrity maintained\n');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from 'fs';
|
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { dirname } from 'path';
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
|
+
import { assertExecutionBootstrapAllowed } from '../util/bootstrap-guard.js';
|
|
6
7
|
import { DataError } from '../util/errors.js';
|
|
7
8
|
import { generateRunId } from '../util/run-id.js';
|
|
8
9
|
import { getRunPaths, ensureRunDirectories } from '../util/paths.js';
|
|
@@ -19,6 +20,7 @@ import { detectFindings } from '../util/detection-engine.js';
|
|
|
19
20
|
import { writeFindingsJson } from '../util/findings-writer.js';
|
|
20
21
|
import { writeSummaryJson } from '../util/summary-writer.js';
|
|
21
22
|
import { computeRuntimeBudget, withTimeout } from '../util/runtime-budget.js';
|
|
23
|
+
import { assertHasLocalSource } from '../util/source-requirement.js';
|
|
22
24
|
|
|
23
25
|
const __filename = fileURLToPath(import.meta.url);
|
|
24
26
|
const __dirname = dirname(__filename);
|
|
@@ -29,7 +31,7 @@ function getVersion() {
|
|
|
29
31
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
30
32
|
return pkg.version;
|
|
31
33
|
} catch {
|
|
32
|
-
return '0.
|
|
34
|
+
return '0.3.0';
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -44,6 +46,8 @@ export async function defaultCommand(options = {}) {
|
|
|
44
46
|
url = null,
|
|
45
47
|
json = false,
|
|
46
48
|
verbose = false,
|
|
49
|
+
determinism = false,
|
|
50
|
+
determinismRuns = 2,
|
|
47
51
|
} = options;
|
|
48
52
|
|
|
49
53
|
const projectRoot = resolve(process.cwd());
|
|
@@ -53,6 +57,9 @@ export async function defaultCommand(options = {}) {
|
|
|
53
57
|
if (!existsSync(srcPath)) {
|
|
54
58
|
throw new DataError(`Source directory not found: ${srcPath}`);
|
|
55
59
|
}
|
|
60
|
+
|
|
61
|
+
// Enforce local source availability (no URL-only scans)
|
|
62
|
+
assertHasLocalSource(srcPath);
|
|
56
63
|
|
|
57
64
|
// Create event emitter
|
|
58
65
|
const events = new RunEventEmitter();
|
|
@@ -93,6 +100,8 @@ export async function defaultCommand(options = {}) {
|
|
|
93
100
|
try {
|
|
94
101
|
const failedAt = new Date().toISOString();
|
|
95
102
|
atomicWriteJson(paths.runStatusJson, {
|
|
103
|
+
contractVersion: 1,
|
|
104
|
+
artifactVersions: paths.artifactVersions,
|
|
96
105
|
status: 'FAILED',
|
|
97
106
|
runId,
|
|
98
107
|
startedAt,
|
|
@@ -101,6 +110,8 @@ export async function defaultCommand(options = {}) {
|
|
|
101
110
|
});
|
|
102
111
|
|
|
103
112
|
atomicWriteJson(paths.runMetaJson, {
|
|
113
|
+
contractVersion: 1,
|
|
114
|
+
artifactVersions: paths.artifactVersions,
|
|
104
115
|
veraxVersion: getVersion(),
|
|
105
116
|
nodeVersion: process.version,
|
|
106
117
|
platform: process.platform,
|
|
@@ -229,6 +240,9 @@ export async function defaultCommand(options = {}) {
|
|
|
229
240
|
console.log(''); // blank line
|
|
230
241
|
}
|
|
231
242
|
|
|
243
|
+
// PHASE 21.6.1: Runtime guard - crash if called during inspection
|
|
244
|
+
assertExecutionBootstrapAllowed('inquirer.prompt');
|
|
245
|
+
|
|
232
246
|
const answer = await inquirer.prompt([
|
|
233
247
|
{
|
|
234
248
|
type: 'input',
|
|
@@ -275,12 +289,16 @@ export async function defaultCommand(options = {}) {
|
|
|
275
289
|
let startedAt = now.toISOString();
|
|
276
290
|
|
|
277
291
|
atomicWriteJson(paths.runStatusJson, {
|
|
292
|
+
contractVersion: 1,
|
|
293
|
+
artifactVersions: paths.artifactVersions,
|
|
278
294
|
status: 'RUNNING',
|
|
279
295
|
runId,
|
|
280
296
|
startedAt,
|
|
281
297
|
});
|
|
282
298
|
|
|
283
299
|
atomicWriteJson(paths.runMetaJson, {
|
|
300
|
+
contractVersion: 1,
|
|
301
|
+
artifactVersions: paths.artifactVersions,
|
|
284
302
|
veraxVersion: getVersion(),
|
|
285
303
|
nodeVersion: process.version,
|
|
286
304
|
platform: process.platform,
|
|
@@ -537,27 +555,9 @@ export async function defaultCommand(options = {}) {
|
|
|
537
555
|
|
|
538
556
|
const completedAt = new Date().toISOString();
|
|
539
557
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
startedAt,
|
|
544
|
-
completedAt,
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
atomicWriteJson(paths.runMetaJson, {
|
|
548
|
-
veraxVersion: getVersion(),
|
|
549
|
-
nodeVersion: process.version,
|
|
550
|
-
platform: process.platform,
|
|
551
|
-
cwd: projectRoot,
|
|
552
|
-
command: 'default',
|
|
553
|
-
args: { url: resolvedUrl, src },
|
|
554
|
-
url: resolvedUrl,
|
|
555
|
-
src: srcPath,
|
|
556
|
-
startedAt,
|
|
557
|
-
completedAt,
|
|
558
|
-
error: null,
|
|
559
|
-
});
|
|
560
|
-
|
|
558
|
+
// Write detect results (or empty if detection failed)
|
|
559
|
+
const findingsResult = writeFindingsJson(paths.baseDir, detectData);
|
|
560
|
+
|
|
561
561
|
// Write summary with stable digest
|
|
562
562
|
writeSummaryJson(paths.summaryJson, {
|
|
563
563
|
runId,
|
|
@@ -577,9 +577,6 @@ export async function defaultCommand(options = {}) {
|
|
|
577
577
|
informational: detectData.stats?.informational || 0,
|
|
578
578
|
});
|
|
579
579
|
|
|
580
|
-
// Write detect results (or empty if detection failed)
|
|
581
|
-
writeFindingsJson(paths.baseDir, detectData);
|
|
582
|
-
|
|
583
580
|
// Write traces (include all events including heartbeats)
|
|
584
581
|
const allEvents = events.getEvents();
|
|
585
582
|
const tracesContent = allEvents
|
|
@@ -595,6 +592,53 @@ export async function defaultCommand(options = {}) {
|
|
|
595
592
|
|
|
596
593
|
// Write observe results
|
|
597
594
|
writeObserveJson(paths.baseDir, observeData);
|
|
595
|
+
|
|
596
|
+
// PHASE 6: Verify artifacts after all writers finish
|
|
597
|
+
const { verifyRun } = await import('../../verax/core/artifacts/verifier.js');
|
|
598
|
+
const verification = verifyRun(paths.baseDir, paths.artifactVersions);
|
|
599
|
+
|
|
600
|
+
// Determine final status based on verification
|
|
601
|
+
let finalStatus = 'COMPLETE';
|
|
602
|
+
if (!verification.ok) {
|
|
603
|
+
finalStatus = 'INVALID';
|
|
604
|
+
} else if (verification.warnings.length > 0) {
|
|
605
|
+
finalStatus = 'VALID_WITH_WARNINGS';
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Write completed status with contract + enforcement snapshot + verification
|
|
609
|
+
atomicWriteJson(paths.runStatusJson, {
|
|
610
|
+
contractVersion: 1,
|
|
611
|
+
artifactVersions: paths.artifactVersions,
|
|
612
|
+
status: finalStatus,
|
|
613
|
+
runId,
|
|
614
|
+
startedAt,
|
|
615
|
+
completedAt,
|
|
616
|
+
enforcement: findingsResult?.payload?.enforcement || null,
|
|
617
|
+
verification: {
|
|
618
|
+
ok: verification.ok,
|
|
619
|
+
status: finalStatus,
|
|
620
|
+
errorsCount: verification.errors.length,
|
|
621
|
+
warningsCount: verification.warnings.length,
|
|
622
|
+
verifiedAt: verification.verifiedAt
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// Update metadata with completion time
|
|
627
|
+
atomicWriteJson(paths.runMetaJson, {
|
|
628
|
+
contractVersion: 1,
|
|
629
|
+
artifactVersions: paths.artifactVersions,
|
|
630
|
+
veraxVersion: getVersion(),
|
|
631
|
+
nodeVersion: process.version,
|
|
632
|
+
platform: process.platform,
|
|
633
|
+
cwd: projectRoot,
|
|
634
|
+
command: 'default',
|
|
635
|
+
args: { url: resolvedUrl, src },
|
|
636
|
+
url: resolvedUrl,
|
|
637
|
+
src: srcPath,
|
|
638
|
+
startedAt,
|
|
639
|
+
completedAt,
|
|
640
|
+
error: null,
|
|
641
|
+
});
|
|
598
642
|
|
|
599
643
|
events.emit('phase:completed', {
|
|
600
644
|
phase: 'Finalize Artifacts',
|
|
@@ -606,6 +650,12 @@ export async function defaultCommand(options = {}) {
|
|
|
606
650
|
console.log('\nRun complete.');
|
|
607
651
|
console.log(`Run ID: ${runId}`);
|
|
608
652
|
console.log(`Artifacts: ${paths.baseDir}`);
|
|
653
|
+
|
|
654
|
+
// PHASE 6: Display verification results
|
|
655
|
+
const { formatVerificationOutput } = await import('../../verax/core/artifacts/verifier.js');
|
|
656
|
+
const verificationOutput = formatVerificationOutput(verification, verbose);
|
|
657
|
+
console.log('');
|
|
658
|
+
console.log(verificationOutput);
|
|
609
659
|
}
|
|
610
660
|
|
|
611
661
|
return { runId, paths, url: resolvedUrl, success: true };
|
|
@@ -623,6 +673,8 @@ export async function defaultCommand(options = {}) {
|
|
|
623
673
|
try {
|
|
624
674
|
const failedAt = new Date().toISOString();
|
|
625
675
|
atomicWriteJson(paths.runStatusJson, {
|
|
676
|
+
contractVersion: 1,
|
|
677
|
+
artifactVersions: paths.artifactVersions,
|
|
626
678
|
status: 'FAILED',
|
|
627
679
|
runId,
|
|
628
680
|
startedAt,
|
|
@@ -632,6 +684,8 @@ export async function defaultCommand(options = {}) {
|
|
|
632
684
|
|
|
633
685
|
// Update metadata
|
|
634
686
|
atomicWriteJson(paths.runMetaJson, {
|
|
687
|
+
contractVersion: 1,
|
|
688
|
+
artifactVersions: paths.artifactVersions,
|
|
635
689
|
veraxVersion: getVersion(),
|
|
636
690
|
nodeVersion: process.version,
|
|
637
691
|
platform: process.platform,
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.6 — GA Readiness CLI Command
|
|
3
|
+
*
|
|
4
|
+
* Pure inspection command. Evaluates GA readiness using existing artifacts only.
|
|
5
|
+
* No URL, no browser, no project execution.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { evaluateGAReadiness } from '../../verax/core/ga/ga.contract.js';
|
|
9
|
+
import { writeGAStatus } from '../../verax/core/ga/ga.artifact.js';
|
|
10
|
+
import { writeGAReport } from '../../verax/core/ga/ga-report-writer.js';
|
|
11
|
+
import { GA_BLOCKER_CODE } from '../../verax/core/ga/ga.contract.js';
|
|
12
|
+
import { resolve } from 'path';
|
|
13
|
+
import { readFileSync, existsSync } from 'fs';
|
|
14
|
+
import { findLatestRunId, validateRunId } from '../util/run-resolver.js';
|
|
15
|
+
import { UsageError } from '../util/errors.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Load failure ledger summary
|
|
19
|
+
*
|
|
20
|
+
* @param {string} projectDir - Project directory
|
|
21
|
+
* @param {string} runId - Run ID
|
|
22
|
+
* @returns {Object|null} Failure ledger summary or null
|
|
23
|
+
*/
|
|
24
|
+
function loadFailureLedger(projectDir, runId) {
|
|
25
|
+
const ledgerPath = resolve(projectDir, '.verax', 'runs', runId, 'failure.ledger.json');
|
|
26
|
+
if (!existsSync(ledgerPath)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const content = readFileSync(ledgerPath, 'utf-8');
|
|
32
|
+
const ledger = JSON.parse(content);
|
|
33
|
+
return ledger.summary || null;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load determinism verdict
|
|
41
|
+
*
|
|
42
|
+
* @param {string} projectDir - Project directory
|
|
43
|
+
* @param {string} runId - Run ID
|
|
44
|
+
* @returns {Promise<string|null>} Determinism verdict or null
|
|
45
|
+
*/
|
|
46
|
+
async function loadDeterminismVerdict(projectDir, runId) {
|
|
47
|
+
const decisionsPath = resolve(projectDir, '.verax', 'runs', runId, 'decisions.json');
|
|
48
|
+
if (!existsSync(decisionsPath)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const decisions = JSON.parse(readFileSync(decisionsPath, 'utf-8'));
|
|
54
|
+
const { DecisionRecorder } = await import('../../verax/core/determinism-model.js');
|
|
55
|
+
const recorder = DecisionRecorder.fromExport(decisions);
|
|
56
|
+
const { computeDeterminismVerdict } = await import('../../verax/core/determinism/contract.js');
|
|
57
|
+
const verdict = computeDeterminismVerdict(recorder);
|
|
58
|
+
return verdict.verdict;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check for Evidence Law violations
|
|
66
|
+
*
|
|
67
|
+
* @param {string} projectDir - Project directory
|
|
68
|
+
* @param {string} runId - Run ID
|
|
69
|
+
* @returns {boolean} Whether Evidence Law was violated
|
|
70
|
+
*/
|
|
71
|
+
function checkEvidenceLawViolations(projectDir, runId) {
|
|
72
|
+
const findingsPath = resolve(projectDir, '.verax', 'runs', runId, 'findings.json');
|
|
73
|
+
if (!existsSync(findingsPath)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const content = readFileSync(findingsPath, 'utf-8');
|
|
79
|
+
const findings = JSON.parse(content);
|
|
80
|
+
|
|
81
|
+
if (!Array.isArray(findings.findings)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check for CONFIRMED findings with incomplete evidence
|
|
86
|
+
for (const finding of findings.findings) {
|
|
87
|
+
if ((finding.severity === 'CONFIRMED' || finding.status === 'CONFIRMED') &&
|
|
88
|
+
finding.evidencePackage && !finding.evidencePackage.isComplete) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return false;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* PHASE 21.6.1: `verax ga` command
|
|
101
|
+
*
|
|
102
|
+
* Pure inspection command. No URL, no browser, no execution.
|
|
103
|
+
*
|
|
104
|
+
* @param {Object} options - Options
|
|
105
|
+
* @param {string} [options.runId] - Run ID (defaults to latest)
|
|
106
|
+
* @param {boolean} [options.json] - Output as JSON
|
|
107
|
+
*/
|
|
108
|
+
export async function gaCommand(options = {}) {
|
|
109
|
+
const { runId: providedRunId = null, json = false } = options;
|
|
110
|
+
|
|
111
|
+
const projectDir = resolve(process.cwd());
|
|
112
|
+
|
|
113
|
+
// Resolve run ID: use provided or find latest
|
|
114
|
+
let runId = providedRunId;
|
|
115
|
+
|
|
116
|
+
if (!runId) {
|
|
117
|
+
// Find latest run
|
|
118
|
+
runId = findLatestRunId(projectDir);
|
|
119
|
+
|
|
120
|
+
if (!runId) {
|
|
121
|
+
// No runs found - GA is BLOCKED
|
|
122
|
+
const gaResult = {
|
|
123
|
+
pass: false,
|
|
124
|
+
blockers: [{
|
|
125
|
+
code: GA_BLOCKER_CODE.NO_RUNS_FOUND || 'GA_NO_RUNS_FOUND',
|
|
126
|
+
message: 'No runs found in .verax/runs/. Run a scan first.',
|
|
127
|
+
context: {}
|
|
128
|
+
}],
|
|
129
|
+
warnings: [],
|
|
130
|
+
summary: {
|
|
131
|
+
pass: false,
|
|
132
|
+
blockersCount: 1,
|
|
133
|
+
warningsCount: 0,
|
|
134
|
+
checkedAt: new Date().toISOString()
|
|
135
|
+
},
|
|
136
|
+
inputs: {
|
|
137
|
+
gates: null,
|
|
138
|
+
determinism: null,
|
|
139
|
+
evidenceLaw: null,
|
|
140
|
+
failureLedger: null
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
if (json) {
|
|
145
|
+
console.log(JSON.stringify({
|
|
146
|
+
gaReady: false,
|
|
147
|
+
blockers: gaResult.blockers,
|
|
148
|
+
warnings: [],
|
|
149
|
+
summary: gaResult.summary
|
|
150
|
+
}, null, 2));
|
|
151
|
+
} else {
|
|
152
|
+
console.log('\n' + '='.repeat(80));
|
|
153
|
+
console.log('GA READINESS EVALUATION');
|
|
154
|
+
console.log('='.repeat(80));
|
|
155
|
+
console.log('\nGA STATUS: ❌ BLOCKED');
|
|
156
|
+
console.log('\nBlockers:');
|
|
157
|
+
console.log('- No runs found in .verax/runs/. Run a scan first.');
|
|
158
|
+
console.log('='.repeat(80) + '\n');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
process.exit(4);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
// Validate provided run ID
|
|
166
|
+
if (!validateRunId(projectDir, runId)) {
|
|
167
|
+
const error = new UsageError(`Run ID not found: ${runId}`);
|
|
168
|
+
// UsageError already has exit code 64
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Load context from artifacts (pure filesystem reads)
|
|
174
|
+
const failureLedger = loadFailureLedger(projectDir, runId);
|
|
175
|
+
const determinismVerdict = await loadDeterminismVerdict(projectDir, runId);
|
|
176
|
+
const evidenceLawViolated = checkEvidenceLawViolations(projectDir, runId);
|
|
177
|
+
|
|
178
|
+
// Evaluate GA readiness
|
|
179
|
+
const gaResult = await evaluateGAReadiness({
|
|
180
|
+
projectDir,
|
|
181
|
+
runId,
|
|
182
|
+
determinismVerdict,
|
|
183
|
+
evidenceLawViolated,
|
|
184
|
+
failureLedger
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Write status artifact
|
|
188
|
+
const artifactPath = writeGAStatus(projectDir, runId, gaResult);
|
|
189
|
+
|
|
190
|
+
// Write GA report
|
|
191
|
+
const reportPath = writeGAReport(projectDir, runId, gaResult);
|
|
192
|
+
|
|
193
|
+
// Output
|
|
194
|
+
if (json) {
|
|
195
|
+
console.log(JSON.stringify({
|
|
196
|
+
gaReady: gaResult.pass,
|
|
197
|
+
blockers: gaResult.blockers,
|
|
198
|
+
warnings: gaResult.warnings,
|
|
199
|
+
summary: gaResult.summary,
|
|
200
|
+
artifactPath,
|
|
201
|
+
reportPath
|
|
202
|
+
}, null, 2));
|
|
203
|
+
} else {
|
|
204
|
+
console.log('\n' + '='.repeat(80));
|
|
205
|
+
console.log('GA READINESS EVALUATION');
|
|
206
|
+
console.log('='.repeat(80));
|
|
207
|
+
console.log(`\nGA STATUS: ${gaResult.pass ? '✅ READY' : '❌ BLOCKED'}`);
|
|
208
|
+
|
|
209
|
+
if (gaResult.blockers.length > 0) {
|
|
210
|
+
console.log('\nBlockers:');
|
|
211
|
+
for (const blocker of gaResult.blockers) {
|
|
212
|
+
console.log(`- ${blocker.message}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (gaResult.warnings.length > 0) {
|
|
217
|
+
console.log('\nWarnings:');
|
|
218
|
+
for (const warning of gaResult.warnings) {
|
|
219
|
+
console.log(`- ${warning.message}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
console.log(`\nSee: ${artifactPath}`);
|
|
224
|
+
console.log(`Report: ${reportPath}`);
|
|
225
|
+
console.log('='.repeat(80) + '\n');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Exit codes: 0 = GA-READY, 2 = GA-BLOCKED, 70 = Internal corruption
|
|
229
|
+
if (!gaResult.pass) {
|
|
230
|
+
// Check if it's an internal corruption issue
|
|
231
|
+
const hasInternalBlocker = gaResult.blockers.some(b =>
|
|
232
|
+
b.code === 'GA_INTERNAL_FAILURES' ||
|
|
233
|
+
b.code === 'GA_CONTRACT_FAILURES'
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
if (hasInternalBlocker) {
|
|
237
|
+
process.exit(70);
|
|
238
|
+
} else {
|
|
239
|
+
process.exit(2);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|