@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
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EXECUTION MODE DETECTOR
|
|
3
|
+
*
|
|
4
|
+
* Automatically detects whether VERAX is running in:
|
|
5
|
+
* 1. PROJECT_SCAN: Local source code is available for analysis
|
|
6
|
+
* 2. WEB_SCAN_LIMITED: Only URL provided, no source code available
|
|
7
|
+
*
|
|
8
|
+
* This determines:
|
|
9
|
+
* - What analysis techniques can be used
|
|
10
|
+
* - Maximum confidence ceiling (0.45 for WEB_SCAN_LIMITED)
|
|
11
|
+
* - What explanations to provide to users
|
|
12
|
+
*
|
|
13
|
+
* PRINCIPLES:
|
|
14
|
+
* - Detection is automatic and implicit (no user configuration needed)
|
|
15
|
+
* - Behavior changes based on what's available, not explicit flags
|
|
16
|
+
* - Users understand limitations from generated explanations
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync, readdirSync, statSync } from 'fs';
|
|
20
|
+
import { join } from 'path';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Execution mode constants
|
|
24
|
+
*/
|
|
25
|
+
export const EXECUTION_MODES = {
|
|
26
|
+
PROJECT_SCAN: 'PROJECT_SCAN', // Full analysis with source code
|
|
27
|
+
WEB_SCAN_LIMITED: 'WEB_SCAN_LIMITED' // URL-only analysis without source
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Confidence ceiling by mode
|
|
32
|
+
*/
|
|
33
|
+
export const CONFIDENCE_CEILINGS = {
|
|
34
|
+
[EXECUTION_MODES.PROJECT_SCAN]: 1.0, // No ceiling, full range [0, 1]
|
|
35
|
+
[EXECUTION_MODES.WEB_SCAN_LIMITED]: 0.45 // Limited to 45% max confidence
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Detect execution mode based on available inputs
|
|
40
|
+
* @param {string} srcPath - Path to source code directory
|
|
41
|
+
* @param {string} url - Target URL
|
|
42
|
+
* @returns {Object} - { mode, ceiling, explanation }
|
|
43
|
+
*/
|
|
44
|
+
export function detectExecutionMode(srcPath, url) {
|
|
45
|
+
// Check if source code is available and viable
|
|
46
|
+
const hasSourceCode = isSourceCodeAvailable(srcPath);
|
|
47
|
+
|
|
48
|
+
if (hasSourceCode) {
|
|
49
|
+
return {
|
|
50
|
+
mode: EXECUTION_MODES.PROJECT_SCAN,
|
|
51
|
+
ceiling: CONFIDENCE_CEILINGS[EXECUTION_MODES.PROJECT_SCAN],
|
|
52
|
+
explanation: 'Full project analysis: source code is available for static analysis, expectations extraction, and dynamic behavior correlation.',
|
|
53
|
+
reason: 'Source code detected at ' + srcPath
|
|
54
|
+
};
|
|
55
|
+
} else {
|
|
56
|
+
return {
|
|
57
|
+
mode: EXECUTION_MODES.WEB_SCAN_LIMITED,
|
|
58
|
+
ceiling: CONFIDENCE_CEILINGS[EXECUTION_MODES.WEB_SCAN_LIMITED],
|
|
59
|
+
explanation: 'Limited web scan: no local source code available. Analysis is based solely on runtime behavior observation. Confidence is capped at 45% due to inability to correlate with code-level expectations.',
|
|
60
|
+
reason: 'No source code available; analyzing runtime behavior only'
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if source code is available and viable for analysis
|
|
67
|
+
* @param {string} srcPath - Path to source code directory
|
|
68
|
+
* @returns {boolean} - True if source code appears to be available
|
|
69
|
+
*/
|
|
70
|
+
function isSourceCodeAvailable(srcPath) {
|
|
71
|
+
// Check if path exists
|
|
72
|
+
if (!existsSync(srcPath)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check if it's a directory
|
|
77
|
+
try {
|
|
78
|
+
const stats = statSync(srcPath);
|
|
79
|
+
if (!stats.isDirectory()) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check for minimum viable source indicators
|
|
87
|
+
return hasSourceIndicators(srcPath);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check for presence of source code indicators
|
|
92
|
+
* @param {string} srcPath - Path to potential source directory
|
|
93
|
+
* @returns {boolean} - True if source indicators are present
|
|
94
|
+
*/
|
|
95
|
+
function hasSourceIndicators(srcPath) {
|
|
96
|
+
try {
|
|
97
|
+
const entries = readdirSync(srcPath, { withFileTypes: true });
|
|
98
|
+
|
|
99
|
+
// Look for common source code patterns
|
|
100
|
+
const indicators = {
|
|
101
|
+
hasJs: false, // .js, .jsx, .ts, .tsx files
|
|
102
|
+
hasSrc: false, // src/ directory
|
|
103
|
+
hasPackageJson: false,
|
|
104
|
+
hasPythonFiles: false,
|
|
105
|
+
hasGoFiles: false
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
for (const entry of entries) {
|
|
109
|
+
const name = entry.name;
|
|
110
|
+
const lowerName = name.toLowerCase();
|
|
111
|
+
|
|
112
|
+
// Check for source directories
|
|
113
|
+
if (entry.isDirectory() &&
|
|
114
|
+
(lowerName === 'src' || lowerName === 'lib' || lowerName === 'app' ||
|
|
115
|
+
lowerName === 'pages' || lowerName === 'components' || lowerName === 'python')) {
|
|
116
|
+
indicators.hasSrc = true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check for package.json (Node.js projects)
|
|
120
|
+
if (name === 'package.json') {
|
|
121
|
+
indicators.hasPackageJson = true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check for JS/TS files
|
|
125
|
+
if (lowerName.endsWith('.js') || lowerName.endsWith('.jsx') ||
|
|
126
|
+
lowerName.endsWith('.ts') || lowerName.endsWith('.tsx') ||
|
|
127
|
+
lowerName.endsWith('.mjs') || lowerName.endsWith('.cjs')) {
|
|
128
|
+
indicators.hasJs = true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check for Python files
|
|
132
|
+
if (lowerName.endsWith('.py')) {
|
|
133
|
+
indicators.hasPythonFiles = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check for Go files
|
|
137
|
+
if (lowerName.endsWith('.go')) {
|
|
138
|
+
indicators.hasGoFiles = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Multiple indicators needed to confirm source code is present
|
|
143
|
+
const indicatorCount = Object.values(indicators).filter(Boolean).length;
|
|
144
|
+
return indicatorCount >= 2; // Need at least 2 indicators
|
|
145
|
+
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Apply confidence ceiling to a score
|
|
153
|
+
* @param {number} score - Confidence score (0..1 float)
|
|
154
|
+
* @param {string} mode - Execution mode
|
|
155
|
+
* @returns {number} - Capped confidence score (0..1 float)
|
|
156
|
+
*/
|
|
157
|
+
export function applyConfidenceCeiling(score, mode) {
|
|
158
|
+
const ceiling = CONFIDENCE_CEILINGS[mode] || 1.0;
|
|
159
|
+
return Math.min(score, ceiling);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Generate execution mode explanation for output
|
|
164
|
+
* @param {string} mode - Execution mode
|
|
165
|
+
* @param {string} ceiling - Confidence ceiling
|
|
166
|
+
* @returns {string} - Human-readable explanation
|
|
167
|
+
*/
|
|
168
|
+
export function generateModeExplanation(mode, ceiling) {
|
|
169
|
+
switch (mode) {
|
|
170
|
+
case EXECUTION_MODES.PROJECT_SCAN:
|
|
171
|
+
return `Running in PROJECT_SCAN mode. Full analysis enabled with source code available.`;
|
|
172
|
+
|
|
173
|
+
case EXECUTION_MODES.WEB_SCAN_LIMITED:
|
|
174
|
+
return `Running in WEB_SCAN_LIMITED mode. No source code available; analysis limited to runtime observation. Confidence capped at ${Math.round(ceiling * 100)}%.`;
|
|
175
|
+
|
|
176
|
+
default:
|
|
177
|
+
return 'Execution mode unknown.';
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Format execution mode info for CLI output
|
|
183
|
+
* @param {Object} modeInfo - Result from detectExecutionMode()
|
|
184
|
+
* @returns {string} - Formatted text for console output
|
|
185
|
+
*/
|
|
186
|
+
export function formatModeForOutput(modeInfo) {
|
|
187
|
+
return `Execution Mode: ${modeInfo.mode}
|
|
188
|
+
Confidence Ceiling: ${Math.round(modeInfo.ceiling * 100)}%
|
|
189
|
+
Reason: ${modeInfo.reason}`;
|
|
190
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.5 — Exit Code Contract
|
|
3
|
+
*
|
|
4
|
+
* Enterprise-grade exit code mapping based on failure ledger.
|
|
5
|
+
* Exit codes must match failure severity and system state.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { FAILURE_SEVERITY } from './failure.types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Exit Code Meanings
|
|
12
|
+
*/
|
|
13
|
+
export const EXIT_CODE = {
|
|
14
|
+
CLEAN: 0, // Clean run, no failures
|
|
15
|
+
WARNINGS: 1, // Warnings only
|
|
16
|
+
DEGRADED: 2, // DEGRADED run (truth preserved, partial capability loss)
|
|
17
|
+
BLOCKING: 3, // BLOCKING failure (truth incomplete)
|
|
18
|
+
INVALID_INPUT: 64, // Invalid input / policy
|
|
19
|
+
INTERNAL_CORRUPTION: 70 // Internal corruption / invariant violation
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Determine exit code from failure ledger
|
|
24
|
+
*
|
|
25
|
+
* @param {Object} ledgerSummary - Failure ledger summary
|
|
26
|
+
* @param {string} determinismVerdict - Determinism verdict (DETERMINISTIC | NON_DETERMINISTIC)
|
|
27
|
+
* @param {boolean} evidenceLawViolated - Whether Evidence Law was violated
|
|
28
|
+
* @param {boolean} policyInvalid - Whether policy was invalid
|
|
29
|
+
* @returns {number} Exit code
|
|
30
|
+
*/
|
|
31
|
+
export function determineExitCode(ledgerSummary, determinismVerdict = null, evidenceLawViolated = false, policyInvalid = false) {
|
|
32
|
+
// Policy invalid → 64
|
|
33
|
+
if (policyInvalid) {
|
|
34
|
+
return EXIT_CODE.INVALID_INPUT;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Internal corruption → 70
|
|
38
|
+
const hasInternalCorruption = ledgerSummary.byCategory?.INTERNAL > 0 ||
|
|
39
|
+
ledgerSummary.byCategory?.CONTRACT > 0;
|
|
40
|
+
if (hasInternalCorruption) {
|
|
41
|
+
return EXIT_CODE.INTERNAL_CORRUPTION;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// BLOCKING failures → 3
|
|
45
|
+
if (ledgerSummary.bySeverity?.BLOCKING > 0) {
|
|
46
|
+
return EXIT_CODE.BLOCKING;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// DEGRADED failures → 2
|
|
50
|
+
if (ledgerSummary.bySeverity?.DEGRADED > 0) {
|
|
51
|
+
return EXIT_CODE.DEGRADED;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// WARNING only → 1
|
|
55
|
+
if (ledgerSummary.bySeverity?.WARNING > 0) {
|
|
56
|
+
return EXIT_CODE.WARNINGS;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Evidence Law violation → 3 (BLOCKING)
|
|
60
|
+
if (evidenceLawViolated) {
|
|
61
|
+
return EXIT_CODE.BLOCKING;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Clean run → 0
|
|
65
|
+
return EXIT_CODE.CLEAN;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get exit code meaning
|
|
70
|
+
*
|
|
71
|
+
* @param {number} exitCode - Exit code
|
|
72
|
+
* @returns {string} Human-readable meaning
|
|
73
|
+
*/
|
|
74
|
+
export function getExitCodeMeaning(exitCode) {
|
|
75
|
+
const meanings = {
|
|
76
|
+
[EXIT_CODE.CLEAN]: 'Clean run, no failures',
|
|
77
|
+
[EXIT_CODE.WARNINGS]: 'Warnings only',
|
|
78
|
+
[EXIT_CODE.DEGRADED]: 'DEGRADED run (truth preserved, partial capability loss)',
|
|
79
|
+
[EXIT_CODE.BLOCKING]: 'BLOCKING failure (truth incomplete)',
|
|
80
|
+
[EXIT_CODE.INVALID_INPUT]: 'Invalid input / policy',
|
|
81
|
+
[EXIT_CODE.INTERNAL_CORRUPTION]: 'Internal corruption / invariant violation'
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return meanings[exitCode] || `Unknown exit code: ${exitCode}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.5 — Failure Summary Formatter
|
|
3
|
+
*
|
|
4
|
+
* Formats failure summaries for CLI output.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { determineExitCode, getExitCodeMeaning } from './exit-codes.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format failure summary for CLI
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} ledgerSummary - Failure ledger summary
|
|
13
|
+
* @param {string} determinismVerdict - Determinism verdict
|
|
14
|
+
* @param {boolean} evidenceLawViolated - Whether Evidence Law was violated
|
|
15
|
+
* @param {string} ledgerPath - Path to failure ledger file
|
|
16
|
+
* @returns {string} Formatted summary
|
|
17
|
+
*/
|
|
18
|
+
export function formatFailureSummary(ledgerSummary, determinismVerdict = null, evidenceLawViolated = false, ledgerPath = null) {
|
|
19
|
+
const lines = [];
|
|
20
|
+
|
|
21
|
+
lines.push('\n═══════════════════════════════════════');
|
|
22
|
+
lines.push('EXECUTION VERDICT');
|
|
23
|
+
lines.push('═══════════════════════════════════════');
|
|
24
|
+
|
|
25
|
+
// Determine verdict
|
|
26
|
+
const highestSeverity = ledgerSummary.highestSeverity;
|
|
27
|
+
let verdict = 'CLEAN';
|
|
28
|
+
if (highestSeverity === 'BLOCKING') {
|
|
29
|
+
verdict = 'BLOCKING';
|
|
30
|
+
} else if (highestSeverity === 'DEGRADED') {
|
|
31
|
+
verdict = 'DEGRADED';
|
|
32
|
+
} else if (highestSeverity === 'WARNING') {
|
|
33
|
+
verdict = 'WARNINGS';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
lines.push(`Verdict: ${verdict}`);
|
|
37
|
+
|
|
38
|
+
// Determinism verdict
|
|
39
|
+
if (determinismVerdict) {
|
|
40
|
+
lines.push(`Deterministic: ${determinismVerdict === 'DETERMINISTIC' ? 'YES' : 'NO'}`);
|
|
41
|
+
} else {
|
|
42
|
+
lines.push('Deterministic: UNKNOWN');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Evidence Law status
|
|
46
|
+
lines.push(`Evidence Law: ${evidenceLawViolated ? 'VIOLATED' : 'ENFORCED'}`);
|
|
47
|
+
|
|
48
|
+
// Failure summary
|
|
49
|
+
lines.push('');
|
|
50
|
+
lines.push('Failures:');
|
|
51
|
+
lines.push(` BLOCKING: ${ledgerSummary.bySeverity?.BLOCKING || 0}`);
|
|
52
|
+
lines.push(` DEGRADED: ${ledgerSummary.bySeverity?.DEGRADED || 0}`);
|
|
53
|
+
lines.push(` WARNING: ${ledgerSummary.bySeverity?.WARNING || 0}`);
|
|
54
|
+
|
|
55
|
+
// Ledger path
|
|
56
|
+
if (ledgerPath) {
|
|
57
|
+
lines.push('');
|
|
58
|
+
lines.push(`See: ${ledgerPath}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return lines.join('\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get exit code from failure ledger and system state
|
|
66
|
+
*
|
|
67
|
+
* @param {Object} ledgerSummary - Failure ledger summary
|
|
68
|
+
* @param {string} determinismVerdict - Determinism verdict
|
|
69
|
+
* @param {boolean} evidenceLawViolated - Whether Evidence Law was violated
|
|
70
|
+
* @param {boolean} policyInvalid - Whether policy was invalid
|
|
71
|
+
* @returns {number} Exit code
|
|
72
|
+
*/
|
|
73
|
+
export function getExitCodeFromLedger(ledgerSummary, determinismVerdict = null, evidenceLawViolated = false, policyInvalid = false) {
|
|
74
|
+
return determineExitCode(ledgerSummary, determinismVerdict, evidenceLawViolated, policyInvalid);
|
|
75
|
+
}
|
|
76
|
+
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.5 — Failure Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates properly classified failure objects.
|
|
5
|
+
* No ad-hoc error creation allowed.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { FAILURE_CODE, FAILURE_CATEGORY, FAILURE_SEVERITY, EXECUTION_PHASE, validateFailure } from './failure.types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a failure object
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} params
|
|
14
|
+
* @param {string} params.code - Failure code
|
|
15
|
+
* @param {string} params.category - Failure category
|
|
16
|
+
* @param {string} params.severity - Failure severity
|
|
17
|
+
* @param {string} params.phase - Execution phase
|
|
18
|
+
* @param {boolean} params.isRecoverable - Whether recoverable
|
|
19
|
+
* @param {string} params.message - Human-readable message
|
|
20
|
+
* @param {string} params.component - Component name
|
|
21
|
+
* @param {Object} [params.context] - Additional context
|
|
22
|
+
* @param {string} [params.stack] - Stack trace
|
|
23
|
+
* @param {string} [params.recoveryAction] - Recovery action
|
|
24
|
+
* @param {string} [params.impact] - Impact description
|
|
25
|
+
* @returns {Failure} Validated failure object
|
|
26
|
+
*/
|
|
27
|
+
export function createFailure(params) {
|
|
28
|
+
const {
|
|
29
|
+
code,
|
|
30
|
+
category,
|
|
31
|
+
severity,
|
|
32
|
+
phase,
|
|
33
|
+
isRecoverable,
|
|
34
|
+
message,
|
|
35
|
+
component,
|
|
36
|
+
context = {},
|
|
37
|
+
stack = null,
|
|
38
|
+
recoveryAction = null,
|
|
39
|
+
impact = null
|
|
40
|
+
} = params;
|
|
41
|
+
|
|
42
|
+
const failure = {
|
|
43
|
+
code,
|
|
44
|
+
category,
|
|
45
|
+
severity,
|
|
46
|
+
phase,
|
|
47
|
+
isRecoverable,
|
|
48
|
+
message,
|
|
49
|
+
component,
|
|
50
|
+
timestamp: Date.now(),
|
|
51
|
+
context,
|
|
52
|
+
...(stack && { stack }),
|
|
53
|
+
...(recoveryAction && { recoveryAction }),
|
|
54
|
+
...(impact && { impact })
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
validateFailure(failure);
|
|
58
|
+
return failure;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create evidence law failure
|
|
63
|
+
*/
|
|
64
|
+
export function createEvidenceFailure(code, message, component, context = {}) {
|
|
65
|
+
return createFailure({
|
|
66
|
+
code,
|
|
67
|
+
category: FAILURE_CATEGORY.EVIDENCE,
|
|
68
|
+
severity: FAILURE_SEVERITY.BLOCKING,
|
|
69
|
+
phase: EXECUTION_PHASE.DETECT,
|
|
70
|
+
isRecoverable: false,
|
|
71
|
+
message,
|
|
72
|
+
component,
|
|
73
|
+
context,
|
|
74
|
+
impact: 'Cannot produce CONFIRMED findings without complete evidence'
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create determinism failure
|
|
80
|
+
*/
|
|
81
|
+
export function createDeterminismFailure(code, message, component, context = {}) {
|
|
82
|
+
return createFailure({
|
|
83
|
+
code,
|
|
84
|
+
category: FAILURE_CATEGORY.DETERMINISM,
|
|
85
|
+
severity: FAILURE_SEVERITY.DEGRADED,
|
|
86
|
+
phase: EXECUTION_PHASE.OBSERVE,
|
|
87
|
+
isRecoverable: false,
|
|
88
|
+
message,
|
|
89
|
+
component,
|
|
90
|
+
context,
|
|
91
|
+
impact: 'Determinism verdict downgraded to NON_DETERMINISTIC'
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create observation failure
|
|
97
|
+
*/
|
|
98
|
+
export function createObserveFailure(code, message, component, context = {}, isRecoverable = false) {
|
|
99
|
+
return createFailure({
|
|
100
|
+
code,
|
|
101
|
+
category: FAILURE_CATEGORY.OBSERVE,
|
|
102
|
+
severity: isRecoverable ? FAILURE_SEVERITY.DEGRADED : FAILURE_SEVERITY.BLOCKING,
|
|
103
|
+
phase: EXECUTION_PHASE.OBSERVE,
|
|
104
|
+
isRecoverable,
|
|
105
|
+
message,
|
|
106
|
+
component,
|
|
107
|
+
context
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Create detection failure
|
|
113
|
+
*/
|
|
114
|
+
export function createDetectFailure(code, message, component, context = {}, isRecoverable = false) {
|
|
115
|
+
return createFailure({
|
|
116
|
+
code,
|
|
117
|
+
category: FAILURE_CATEGORY.DETECT,
|
|
118
|
+
severity: isRecoverable ? FAILURE_SEVERITY.DEGRADED : FAILURE_SEVERITY.BLOCKING,
|
|
119
|
+
phase: EXECUTION_PHASE.DETECT,
|
|
120
|
+
isRecoverable,
|
|
121
|
+
message,
|
|
122
|
+
component,
|
|
123
|
+
context
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Create I/O failure
|
|
129
|
+
*/
|
|
130
|
+
export function createIOFailure(code, message, component, context = {}, isRecoverable = false) {
|
|
131
|
+
return createFailure({
|
|
132
|
+
code,
|
|
133
|
+
category: FAILURE_CATEGORY.IO,
|
|
134
|
+
severity: isRecoverable ? FAILURE_SEVERITY.WARNING : FAILURE_SEVERITY.BLOCKING,
|
|
135
|
+
phase: EXECUTION_PHASE.REPORT,
|
|
136
|
+
isRecoverable,
|
|
137
|
+
message,
|
|
138
|
+
component,
|
|
139
|
+
context
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Create policy failure
|
|
145
|
+
*/
|
|
146
|
+
export function createPolicyFailure(code, message, component, context = {}) {
|
|
147
|
+
return createFailure({
|
|
148
|
+
code,
|
|
149
|
+
category: FAILURE_CATEGORY.POLICY,
|
|
150
|
+
severity: FAILURE_SEVERITY.BLOCKING,
|
|
151
|
+
phase: EXECUTION_PHASE.DETECT,
|
|
152
|
+
isRecoverable: false,
|
|
153
|
+
message,
|
|
154
|
+
component,
|
|
155
|
+
context,
|
|
156
|
+
impact: 'Invalid policy prevents execution'
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Create contract failure
|
|
162
|
+
*/
|
|
163
|
+
export function createContractFailure(code, message, component, context = {}, stack = null) {
|
|
164
|
+
return createFailure({
|
|
165
|
+
code,
|
|
166
|
+
category: FAILURE_CATEGORY.CONTRACT,
|
|
167
|
+
severity: FAILURE_SEVERITY.BLOCKING,
|
|
168
|
+
phase: EXECUTION_PHASE.DETECT,
|
|
169
|
+
isRecoverable: false,
|
|
170
|
+
message,
|
|
171
|
+
component,
|
|
172
|
+
context,
|
|
173
|
+
stack,
|
|
174
|
+
impact: 'Invariant violation - system integrity compromised'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Create internal failure
|
|
180
|
+
*/
|
|
181
|
+
export function createInternalFailure(code, message, component, context = {}, stack = null) {
|
|
182
|
+
return createFailure({
|
|
183
|
+
code,
|
|
184
|
+
category: FAILURE_CATEGORY.INTERNAL,
|
|
185
|
+
severity: FAILURE_SEVERITY.BLOCKING,
|
|
186
|
+
phase: EXECUTION_PHASE.DETECT,
|
|
187
|
+
isRecoverable: false,
|
|
188
|
+
message,
|
|
189
|
+
component,
|
|
190
|
+
context,
|
|
191
|
+
stack,
|
|
192
|
+
impact: 'Internal error - system state may be corrupted'
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Convert error to failure
|
|
198
|
+
*
|
|
199
|
+
* @param {Error} error - JavaScript error
|
|
200
|
+
* @param {string} code - Failure code
|
|
201
|
+
* @param {string} category - Failure category
|
|
202
|
+
* @param {string} phase - Execution phase
|
|
203
|
+
* @param {string} component - Component name
|
|
204
|
+
* @param {Object} context - Additional context
|
|
205
|
+
* @returns {Failure} Failure object
|
|
206
|
+
*/
|
|
207
|
+
export function errorToFailure(error, code, category, phase, component, context = {}) {
|
|
208
|
+
return createFailure({
|
|
209
|
+
code,
|
|
210
|
+
category,
|
|
211
|
+
severity: FAILURE_SEVERITY.BLOCKING,
|
|
212
|
+
phase,
|
|
213
|
+
isRecoverable: false,
|
|
214
|
+
message: error.message || 'Unexpected error',
|
|
215
|
+
component,
|
|
216
|
+
context: {
|
|
217
|
+
...context,
|
|
218
|
+
errorName: error.name,
|
|
219
|
+
errorType: error.constructor?.name
|
|
220
|
+
},
|
|
221
|
+
stack: error.stack || null,
|
|
222
|
+
impact: 'Unexpected error - execution may be incomplete'
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 21.5 — Failure Ledger
|
|
3
|
+
*
|
|
4
|
+
* Enterprise artifact that records all failures (even recovered ones).
|
|
5
|
+
* Deterministic, append-only, never missing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { writeFileSync, mkdirSync } from 'fs';
|
|
9
|
+
import { resolve, dirname } from 'path';
|
|
10
|
+
import { validateFailure } from './failure.types.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Failure Ledger
|
|
14
|
+
*
|
|
15
|
+
* Maintains an ordered list of all failures during execution.
|
|
16
|
+
*/
|
|
17
|
+
export class FailureLedger {
|
|
18
|
+
constructor(runId, projectDir) {
|
|
19
|
+
this.runId = runId;
|
|
20
|
+
this.projectDir = projectDir;
|
|
21
|
+
this.failures = [];
|
|
22
|
+
this.startTime = Date.now();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Record a failure
|
|
27
|
+
*
|
|
28
|
+
* @param {Failure} failure - Failure object
|
|
29
|
+
*/
|
|
30
|
+
record(failure) {
|
|
31
|
+
validateFailure(failure);
|
|
32
|
+
|
|
33
|
+
// Add sequence number for deterministic ordering
|
|
34
|
+
const sequencedFailure = {
|
|
35
|
+
...failure,
|
|
36
|
+
sequence: this.failures.length,
|
|
37
|
+
relativeTime: Date.now() - this.startTime
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
this.failures.push(sequencedFailure);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get failure summary
|
|
45
|
+
*
|
|
46
|
+
* @returns {Object} Summary with counts by severity
|
|
47
|
+
*/
|
|
48
|
+
getSummary() {
|
|
49
|
+
const summary = {
|
|
50
|
+
total: this.failures.length,
|
|
51
|
+
bySeverity: {
|
|
52
|
+
BLOCKING: 0,
|
|
53
|
+
DEGRADED: 0,
|
|
54
|
+
WARNING: 0
|
|
55
|
+
},
|
|
56
|
+
byCategory: {},
|
|
57
|
+
byPhase: {},
|
|
58
|
+
highestSeverity: null
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
for (const failure of this.failures) {
|
|
62
|
+
summary.bySeverity[failure.severity] = (summary.bySeverity[failure.severity] || 0) + 1;
|
|
63
|
+
summary.byCategory[failure.category] = (summary.byCategory[failure.category] || 0) + 1;
|
|
64
|
+
summary.byPhase[failure.phase] = (summary.byPhase[failure.phase] || 0) + 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Determine highest severity
|
|
68
|
+
if (summary.bySeverity.BLOCKING > 0) {
|
|
69
|
+
summary.highestSeverity = 'BLOCKING';
|
|
70
|
+
} else if (summary.bySeverity.DEGRADED > 0) {
|
|
71
|
+
summary.highestSeverity = 'DEGRADED';
|
|
72
|
+
} else if (summary.bySeverity.WARNING > 0) {
|
|
73
|
+
summary.highestSeverity = 'WARNING';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return summary;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get highest severity for exit code determination
|
|
81
|
+
*
|
|
82
|
+
* @returns {string|null} Highest severity or null if no failures
|
|
83
|
+
*/
|
|
84
|
+
getHighestSeverity() {
|
|
85
|
+
const summary = this.getSummary();
|
|
86
|
+
return summary.highestSeverity;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Write ledger to file
|
|
91
|
+
*
|
|
92
|
+
* @returns {string} Path to ledger file
|
|
93
|
+
*/
|
|
94
|
+
write() {
|
|
95
|
+
if (!this.runId || !this.projectDir) {
|
|
96
|
+
throw new Error('Cannot write failure ledger: runId and projectDir required');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const runsDir = resolve(this.projectDir, '.verax', 'runs', this.runId);
|
|
100
|
+
mkdirSync(runsDir, { recursive: true });
|
|
101
|
+
|
|
102
|
+
const ledgerPath = resolve(runsDir, 'failure.ledger.json');
|
|
103
|
+
|
|
104
|
+
const ledgerData = {
|
|
105
|
+
runId: this.runId,
|
|
106
|
+
startTime: this.startTime,
|
|
107
|
+
endTime: Date.now(),
|
|
108
|
+
duration: Date.now() - this.startTime,
|
|
109
|
+
summary: this.getSummary(),
|
|
110
|
+
failures: this.failures
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
writeFileSync(ledgerPath, JSON.stringify(ledgerData, null, 2), 'utf-8');
|
|
114
|
+
|
|
115
|
+
return ledgerPath;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Export ledger data (for testing)
|
|
120
|
+
*
|
|
121
|
+
* @returns {Object} Ledger data
|
|
122
|
+
*/
|
|
123
|
+
export() {
|
|
124
|
+
return {
|
|
125
|
+
runId: this.runId,
|
|
126
|
+
startTime: this.startTime,
|
|
127
|
+
summary: this.getSummary(),
|
|
128
|
+
failures: this.failures
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|