@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/verax/index.js
CHANGED
|
@@ -9,8 +9,29 @@ import SilenceTracker from './core/silence-model.js';
|
|
|
9
9
|
import { generateRunId, getRunArtifactDir, getArtifactPath } from './core/run-id.js';
|
|
10
10
|
import { createRunManifest, updateRunManifestHashes } from './core/run-manifest.js';
|
|
11
11
|
import { computeArtifactHashes } from './core/run-id.js';
|
|
12
|
+
import { FailureLedger } from './core/failures/failure.ledger.js';
|
|
13
|
+
import { errorToFailure, createIOFailure, createInternalFailure } from './core/failures/failure.factory.js';
|
|
14
|
+
import { FAILURE_CODE, EXECUTION_PHASE } from './core/failures/failure.types.js';
|
|
15
|
+
import { createPerformanceMonitor } from './core/perf/perf.monitor.js';
|
|
16
|
+
import { generatePerformanceReport, writePerformanceReport } from './core/perf/perf.report.js';
|
|
17
|
+
import { recordPerformanceViolations } from './core/perf/perf.enforcer.js';
|
|
18
|
+
import { PipelineTracker, PIPELINE_STAGES } from './core/pipeline-tracker.js';
|
|
19
|
+
import { verifyRun } from './core/artifacts/verifier.js';
|
|
20
|
+
import { getArtifactVersions } from './core/artifacts/registry.js';
|
|
12
21
|
|
|
13
22
|
export async function scan(projectDir, url, manifestPath = null, scanBudgetOverride = null, safetyFlags = {}) {
|
|
23
|
+
// PHASE 21.5: Initialize failure ledger (runId will be set after learn)
|
|
24
|
+
const scanBudget = scanBudgetOverride || createScanBudgetWithProfile();
|
|
25
|
+
const failureLedger = new FailureLedger(null, projectDir);
|
|
26
|
+
|
|
27
|
+
// PHASE 21.9: Initialize performance monitor
|
|
28
|
+
const perfMonitor = createPerformanceMonitor();
|
|
29
|
+
perfMonitor.startPhase('LEARN');
|
|
30
|
+
|
|
31
|
+
// ARCHITECTURAL HARDENING: Initialize pipeline tracker early (before runId is known)
|
|
32
|
+
// We'll set runId on the tracker once it's generated
|
|
33
|
+
let pipelineTracker = null;
|
|
34
|
+
|
|
14
35
|
// If manifestPath is provided, read it first before learn() overwrites it
|
|
15
36
|
let loadedManifest = null;
|
|
16
37
|
if (manifestPath) {
|
|
@@ -19,26 +40,62 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
|
|
|
19
40
|
const manifestContent = JSON.parse(readFileSync(manifestPath, 'utf-8'));
|
|
20
41
|
loadedManifest = manifestContent;
|
|
21
42
|
} catch (e) {
|
|
22
|
-
//
|
|
43
|
+
// PHASE 21.5: Record I/O failure
|
|
44
|
+
const failure = createIOFailure(
|
|
45
|
+
FAILURE_CODE.IO_READ_FAILED,
|
|
46
|
+
`Failed to read manifest: ${e.message}`,
|
|
47
|
+
'scan',
|
|
48
|
+
{ manifestPath, error: e.message },
|
|
49
|
+
true // Recoverable - will fall through to learn
|
|
50
|
+
);
|
|
51
|
+
failureLedger.record(failure);
|
|
23
52
|
}
|
|
24
53
|
}
|
|
25
54
|
|
|
55
|
+
// ARCHITECTURAL HARDENING: Track LEARN stage
|
|
56
|
+
// Note: We can't track LEARN yet because we don't have runId, but we'll track it retroactively
|
|
57
|
+
const learnStartTime = Date.now();
|
|
26
58
|
const learnedManifest = await learn(projectDir);
|
|
59
|
+
const learnEndTime = Date.now();
|
|
60
|
+
perfMonitor.endPhase('LEARN');
|
|
27
61
|
|
|
28
|
-
//
|
|
62
|
+
// ARCHITECTURAL HARDENING: Explicit manifest merging with deterministic precedence
|
|
63
|
+
// Rule: Loaded manifest routes take precedence, but learned manifest provides truth and project type
|
|
29
64
|
let manifest;
|
|
30
65
|
if (loadedManifest) {
|
|
66
|
+
// Explicit merge: loaded manifest routes override learned, but learned provides truth
|
|
31
67
|
manifest = {
|
|
32
68
|
...learnedManifest,
|
|
69
|
+
// Routes from loaded manifest (if present) override learned routes
|
|
70
|
+
publicRoutes: loadedManifest.publicRoutes || learnedManifest.publicRoutes || [],
|
|
71
|
+
routes: loadedManifest.routes || learnedManifest.routes || [],
|
|
72
|
+
internalRoutes: loadedManifest.internalRoutes || learnedManifest.internalRoutes || [],
|
|
73
|
+
staticExpectations: loadedManifest.staticExpectations || learnedManifest.staticExpectations || [],
|
|
74
|
+
// Project type: prefer loaded, fallback to learned
|
|
33
75
|
projectType: loadedManifest.projectType || learnedManifest.projectType,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
manifestPath: manifestPath
|
|
76
|
+
// Always preserve learned truth (this is the source of truth)
|
|
77
|
+
learnTruth: learnedManifest.learnTruth || {},
|
|
78
|
+
// Track manifest source for auditability
|
|
79
|
+
manifestSource: 'merged',
|
|
80
|
+
manifestPath: manifestPath,
|
|
81
|
+
mergeMetadata: {
|
|
82
|
+
loadedManifestProvided: true,
|
|
83
|
+
learnedManifestProvided: true,
|
|
84
|
+
routesSource: loadedManifest.routes ? 'loaded' : 'learned',
|
|
85
|
+
projectTypeSource: loadedManifest.projectType ? 'loaded' : 'learned'
|
|
86
|
+
}
|
|
39
87
|
};
|
|
40
88
|
} else {
|
|
41
|
-
manifest =
|
|
89
|
+
manifest = {
|
|
90
|
+
...learnedManifest,
|
|
91
|
+
manifestSource: 'learned',
|
|
92
|
+
mergeMetadata: {
|
|
93
|
+
loadedManifestProvided: false,
|
|
94
|
+
learnedManifestProvided: true,
|
|
95
|
+
routesSource: 'learned',
|
|
96
|
+
projectTypeSource: 'learned'
|
|
97
|
+
}
|
|
98
|
+
};
|
|
42
99
|
}
|
|
43
100
|
|
|
44
101
|
if (!url) {
|
|
@@ -53,8 +110,8 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
|
|
|
53
110
|
manifestPath: manifest.manifestPath
|
|
54
111
|
};
|
|
55
112
|
|
|
113
|
+
// ARCHITECTURAL HARDENING: validateRoutes now always returns explicit validation object
|
|
56
114
|
const validation = await validateRoutes(manifestForValidation, url);
|
|
57
|
-
|
|
58
115
|
if (validation.warnings && validation.warnings.length > 0) {
|
|
59
116
|
if (!manifest.learnTruth.warnings) {
|
|
60
117
|
manifest.learnTruth.warnings = [];
|
|
@@ -62,9 +119,6 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
|
|
|
62
119
|
manifest.learnTruth.warnings.push(...validation.warnings);
|
|
63
120
|
}
|
|
64
121
|
|
|
65
|
-
// Use budget profile if no override provided
|
|
66
|
-
const scanBudget = scanBudgetOverride || createScanBudgetWithProfile();
|
|
67
|
-
|
|
68
122
|
// PHASE 5: Generate deterministic runId and create run manifest
|
|
69
123
|
const { getBaseOrigin } = await import('./observe/domain-boundary.js');
|
|
70
124
|
const baseOrigin = getBaseOrigin(url);
|
|
@@ -76,6 +130,41 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
|
|
|
76
130
|
manifestPath
|
|
77
131
|
});
|
|
78
132
|
|
|
133
|
+
// PHASE 21.5: Set runId in failure ledger
|
|
134
|
+
failureLedger.runId = runId;
|
|
135
|
+
|
|
136
|
+
// ARCHITECTURAL HARDENING: Initialize pipeline tracker now that we have runId
|
|
137
|
+
// PHASE 25: Pass runFingerprint params to PipelineTracker
|
|
138
|
+
const runFingerprintParams = url ? {
|
|
139
|
+
url,
|
|
140
|
+
projectDir,
|
|
141
|
+
manifestPath: null,
|
|
142
|
+
fixtureId: null
|
|
143
|
+
} : null;
|
|
144
|
+
pipelineTracker = new PipelineTracker(projectDir, runId, runFingerprintParams);
|
|
145
|
+
|
|
146
|
+
// Record LEARN stage retroactively (it completed before we had runId)
|
|
147
|
+
try {
|
|
148
|
+
pipelineTracker.stages[PIPELINE_STAGES.LEARN] = {
|
|
149
|
+
name: PIPELINE_STAGES.LEARN,
|
|
150
|
+
startedAt: new Date(learnStartTime).toISOString(),
|
|
151
|
+
completedAt: new Date(learnEndTime).toISOString(),
|
|
152
|
+
durationMs: learnEndTime - learnStartTime,
|
|
153
|
+
status: 'COMPLETE'
|
|
154
|
+
};
|
|
155
|
+
pipelineTracker.completedStages.push(PIPELINE_STAGES.LEARN);
|
|
156
|
+
pipelineTracker._writeMeta();
|
|
157
|
+
} catch (error) {
|
|
158
|
+
const failure = createInternalFailure(
|
|
159
|
+
FAILURE_CODE.INTERNAL_UNEXPECTED_ERROR,
|
|
160
|
+
`Failed to record LEARN stage: ${error.message}`,
|
|
161
|
+
'scan',
|
|
162
|
+
{ error: error.message },
|
|
163
|
+
error.stack
|
|
164
|
+
);
|
|
165
|
+
failureLedger.record(failure);
|
|
166
|
+
}
|
|
167
|
+
|
|
79
168
|
// Create run manifest at start of execution
|
|
80
169
|
const runManifest = createRunManifest(projectDir, runId, {
|
|
81
170
|
url,
|
|
@@ -87,14 +176,62 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
|
|
|
87
176
|
});
|
|
88
177
|
|
|
89
178
|
const usedManifestPath = manifestPath || manifest.manifestPath;
|
|
90
|
-
|
|
179
|
+
|
|
180
|
+
// ARCHITECTURAL HARDENING: Track OBSERVE stage
|
|
181
|
+
pipelineTracker.startStage(PIPELINE_STAGES.OBSERVE);
|
|
182
|
+
perfMonitor.startPhase('OBSERVE');
|
|
183
|
+
let observation;
|
|
184
|
+
try {
|
|
185
|
+
observation = await observe(url, usedManifestPath, scanBudget, safetyFlags, projectDir, runId);
|
|
186
|
+
perfMonitor.endPhase('OBSERVE');
|
|
187
|
+
pipelineTracker.completeStage(PIPELINE_STAGES.OBSERVE, {
|
|
188
|
+
pagesVisited: observation.observeTruth?.pagesVisited || 0,
|
|
189
|
+
interactionsExecuted: observation.observeTruth?.interactionsExecuted || 0
|
|
190
|
+
});
|
|
191
|
+
} catch (error) {
|
|
192
|
+
perfMonitor.endPhase('OBSERVE');
|
|
193
|
+
pipelineTracker.failStage(PIPELINE_STAGES.OBSERVE, error);
|
|
194
|
+
const failure = errorToFailure(
|
|
195
|
+
error,
|
|
196
|
+
FAILURE_CODE.OBSERVE_EXECUTION_FAILED,
|
|
197
|
+
'OBSERVE',
|
|
198
|
+
EXECUTION_PHASE.OBSERVE,
|
|
199
|
+
'observe',
|
|
200
|
+
{ manifestPath: usedManifestPath }
|
|
201
|
+
);
|
|
202
|
+
failureLedger.record(failure);
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Update performance monitor with observed metrics
|
|
207
|
+
if (observation.observeTruth) {
|
|
208
|
+
const pagesVisited = observation.observeTruth.pagesVisited || 0;
|
|
209
|
+
const interactionsExecuted = observation.observeTruth.interactionsExecuted || observation.observeTruth.candidatesSelected || 0;
|
|
210
|
+
|
|
211
|
+
for (let i = 0; i < pagesVisited; i++) {
|
|
212
|
+
perfMonitor.incrementPages();
|
|
213
|
+
}
|
|
214
|
+
for (let i = 0; i < interactionsExecuted; i++) {
|
|
215
|
+
perfMonitor.incrementInteractions();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
91
218
|
|
|
92
219
|
// Write a copy of the manifest into canonical run directory for replay integrity
|
|
93
220
|
try {
|
|
94
221
|
const { writeFileSync } = await import('fs');
|
|
95
222
|
const manifestCopyPath = getArtifactPath(projectDir, runId, 'manifest.json');
|
|
96
223
|
writeFileSync(manifestCopyPath, JSON.stringify(manifest, null, 2));
|
|
97
|
-
} catch {
|
|
224
|
+
} catch (error) {
|
|
225
|
+
// PHASE 21.5: Record I/O failure (WARNING - recoverable)
|
|
226
|
+
const failure = createIOFailure(
|
|
227
|
+
FAILURE_CODE.IO_WRITE_FAILED,
|
|
228
|
+
`Failed to write manifest copy: ${error.message}`,
|
|
229
|
+
'scan',
|
|
230
|
+
{ manifestPath, error: error.message },
|
|
231
|
+
true
|
|
232
|
+
);
|
|
233
|
+
failureLedger.record(failure);
|
|
234
|
+
}
|
|
98
235
|
|
|
99
236
|
// Create silence tracker from observation silences
|
|
100
237
|
const silenceTracker = new SilenceTracker();
|
|
@@ -102,27 +239,69 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
|
|
|
102
239
|
silenceTracker.recordBatch(observation.silences.entries);
|
|
103
240
|
}
|
|
104
241
|
|
|
105
|
-
|
|
242
|
+
// ARCHITECTURAL HARDENING: Track DETECT stage
|
|
243
|
+
pipelineTracker.startStage(PIPELINE_STAGES.DETECT);
|
|
244
|
+
perfMonitor.startPhase('DETECT');
|
|
245
|
+
let findings;
|
|
246
|
+
try {
|
|
247
|
+
findings = await detect(usedManifestPath, observation.tracesPath, validation, observation.expectationCoverageGaps || [], silenceTracker);
|
|
248
|
+
perfMonitor.endPhase('DETECT');
|
|
249
|
+
pipelineTracker.completeStage(PIPELINE_STAGES.DETECT, {
|
|
250
|
+
findingsCount: findings.findings?.length || 0,
|
|
251
|
+
coverageGapsCount: findings.coverageGaps?.length || 0
|
|
252
|
+
});
|
|
253
|
+
} catch (error) {
|
|
254
|
+
perfMonitor.endPhase('DETECT');
|
|
255
|
+
pipelineTracker.failStage(PIPELINE_STAGES.DETECT, error);
|
|
256
|
+
const failure = errorToFailure(
|
|
257
|
+
error,
|
|
258
|
+
FAILURE_CODE.DETECT_FINDING_PROCESSING_FAILED,
|
|
259
|
+
'DETECT',
|
|
260
|
+
EXECUTION_PHASE.DETECT,
|
|
261
|
+
'detect',
|
|
262
|
+
{ manifestPath: usedManifestPath }
|
|
263
|
+
);
|
|
264
|
+
failureLedger.record(failure);
|
|
265
|
+
throw error; // Re-throw BLOCKING failures
|
|
266
|
+
}
|
|
106
267
|
|
|
107
268
|
const learnTruthWithValidation = {
|
|
108
269
|
...manifest.learnTruth,
|
|
109
270
|
validation: validation
|
|
110
271
|
};
|
|
111
272
|
|
|
273
|
+
// ARCHITECTURAL HARDENING: Track WRITE stage
|
|
274
|
+
pipelineTracker.startStage(PIPELINE_STAGES.WRITE);
|
|
112
275
|
const runDir = getRunArtifactDir(projectDir, runId);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
276
|
+
let scanSummary;
|
|
277
|
+
try {
|
|
278
|
+
scanSummary = await writeScanSummary(
|
|
279
|
+
projectDir,
|
|
280
|
+
url,
|
|
281
|
+
manifest.projectType,
|
|
282
|
+
learnTruthWithValidation,
|
|
283
|
+
observation.observeTruth,
|
|
284
|
+
findings.detectTruth,
|
|
285
|
+
manifest.manifestPath,
|
|
286
|
+
observation.tracesPath,
|
|
287
|
+
findings.findingsPath,
|
|
288
|
+
runDir,
|
|
289
|
+
findings.findings // PHASE 7: Pass findings array for decision snapshot
|
|
290
|
+
);
|
|
291
|
+
pipelineTracker.completeStage(PIPELINE_STAGES.WRITE);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
pipelineTracker.failStage(PIPELINE_STAGES.WRITE, error);
|
|
294
|
+
const failure = errorToFailure(
|
|
295
|
+
error,
|
|
296
|
+
FAILURE_CODE.IO_WRITE_FAILED,
|
|
297
|
+
'WRITE',
|
|
298
|
+
EXECUTION_PHASE.WRITE,
|
|
299
|
+
'writeScanSummary',
|
|
300
|
+
{ runDir }
|
|
301
|
+
);
|
|
302
|
+
failureLedger.record(failure);
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
126
305
|
|
|
127
306
|
// Compute observation summary from scan results (not a verdict)
|
|
128
307
|
// Pass observation object (which includes traces) to observation engine
|
|
@@ -151,9 +330,204 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
|
|
|
151
330
|
);
|
|
152
331
|
observationSummary.evidenceIndexPath = evidenceIndexPath;
|
|
153
332
|
|
|
154
|
-
// PHASE
|
|
155
|
-
|
|
156
|
-
|
|
333
|
+
// PHASE 21.4: Write policy artifacts
|
|
334
|
+
try {
|
|
335
|
+
const { writeFileSync } = await import('fs');
|
|
336
|
+
const { loadGuardrailsPolicy, getPolicyReport: getGuardrailsPolicyReport } = await import('./core/guardrails/policy.loader.js');
|
|
337
|
+
const { loadConfidencePolicy, getPolicyReport: getConfidencePolicyReport } = await import('./core/confidence/confidence.loader.js');
|
|
338
|
+
|
|
339
|
+
const guardrailsPolicy = loadGuardrailsPolicy();
|
|
340
|
+
const confidencePolicy = loadConfidencePolicy();
|
|
341
|
+
|
|
342
|
+
const guardrailsPolicyPath = getArtifactPath(projectDir, failureLedger.runId, 'guardrails.policy.json');
|
|
343
|
+
const confidencePolicyPath = getArtifactPath(projectDir, failureLedger.runId, 'confidence.policy.json');
|
|
344
|
+
|
|
345
|
+
writeFileSync(guardrailsPolicyPath, JSON.stringify({
|
|
346
|
+
...getGuardrailsPolicyReport(guardrailsPolicy),
|
|
347
|
+
rules: guardrailsPolicy.rules.map(r => ({
|
|
348
|
+
id: r.id,
|
|
349
|
+
category: r.category,
|
|
350
|
+
action: r.action,
|
|
351
|
+
mandatory: r.mandatory
|
|
352
|
+
}))
|
|
353
|
+
}, null, 2));
|
|
354
|
+
|
|
355
|
+
writeFileSync(confidencePolicyPath, JSON.stringify(getConfidencePolicyReport(confidencePolicy), null, 2));
|
|
356
|
+
} catch (error) {
|
|
357
|
+
// PHASE 21.5: Record I/O failure (WARNING - recoverable)
|
|
358
|
+
const failure = createIOFailure(
|
|
359
|
+
FAILURE_CODE.IO_WRITE_FAILED,
|
|
360
|
+
`Failed to write policy artifacts: ${error.message}`,
|
|
361
|
+
'scan',
|
|
362
|
+
{ error: error.message },
|
|
363
|
+
true
|
|
364
|
+
);
|
|
365
|
+
failureLedger.record(failure);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
// ARCHITECTURAL HARDENING: Write failure ledger with fallback to run.status.json
|
|
370
|
+
let ledgerPath;
|
|
371
|
+
try {
|
|
372
|
+
ledgerPath = failureLedger.write();
|
|
373
|
+
} catch (error) {
|
|
374
|
+
// If ledger write fails, write fallback entry to run.status.json
|
|
375
|
+
const failure = createInternalFailure(
|
|
376
|
+
FAILURE_CODE.INTERNAL_UNEXPECTED_ERROR,
|
|
377
|
+
`Failed to write failure ledger: ${error.message}`,
|
|
378
|
+
'scan',
|
|
379
|
+
{ error: error.message },
|
|
380
|
+
error.stack
|
|
381
|
+
);
|
|
382
|
+
failureLedger.record(failure);
|
|
383
|
+
|
|
384
|
+
// Write fallback failure entry to run.status.json
|
|
385
|
+
try {
|
|
386
|
+
const { writeFileSync, readFileSync } = await import('fs');
|
|
387
|
+
const runStatusPath = getArtifactPath(projectDir, runId, 'run.status.json');
|
|
388
|
+
let runStatus = { status: 'FAILED', failures: [] };
|
|
389
|
+
try {
|
|
390
|
+
const existing = readFileSync(runStatusPath, 'utf-8');
|
|
391
|
+
runStatus = JSON.parse(existing);
|
|
392
|
+
} catch {
|
|
393
|
+
// File doesn't exist or is invalid, use defaults
|
|
394
|
+
}
|
|
395
|
+
runStatus.failures = runStatus.failures || [];
|
|
396
|
+
runStatus.failures.push({
|
|
397
|
+
code: FAILURE_CODE.INTERNAL_UNEXPECTED_ERROR,
|
|
398
|
+
message: `Failed to write failure ledger: ${error.message}`,
|
|
399
|
+
timestamp: new Date().toISOString(),
|
|
400
|
+
recoverable: false
|
|
401
|
+
});
|
|
402
|
+
runStatus.ledgerWriteFailed = true;
|
|
403
|
+
runStatus.ledgerWriteError = error.message;
|
|
404
|
+
writeFileSync(runStatusPath, JSON.stringify(runStatus, null, 2) + '\n', 'utf-8');
|
|
405
|
+
} catch (fallbackError) {
|
|
406
|
+
// Even fallback write failed - log only
|
|
407
|
+
console.error('CRITICAL: Failed to write failure ledger and fallback:', error.message, fallbackError.message);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// PHASE 21.9: Generate and write performance report
|
|
412
|
+
try {
|
|
413
|
+
perfMonitor.stop();
|
|
414
|
+
const monitorReport = perfMonitor.getFinalReport();
|
|
415
|
+
const profileName = process.env.VERAX_BUDGET_PROFILE || 'STANDARD';
|
|
416
|
+
const perfReport = generatePerformanceReport(projectDir, runId, monitorReport, profileName);
|
|
417
|
+
writePerformanceReport(projectDir, runId, perfReport);
|
|
418
|
+
|
|
419
|
+
// Record performance violations in failure ledger
|
|
420
|
+
recordPerformanceViolations(projectDir, runId, failureLedger);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
// Record performance report failure (WARNING - recoverable)
|
|
423
|
+
const failure = createIOFailure(
|
|
424
|
+
FAILURE_CODE.IO_WRITE_FAILED,
|
|
425
|
+
`Failed to write performance report: ${error.message}`,
|
|
426
|
+
'scan',
|
|
427
|
+
{ error: error.message },
|
|
428
|
+
true
|
|
429
|
+
);
|
|
430
|
+
failureLedger.record(failure);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ARCHITECTURAL HARDENING: Track VERIFY stage
|
|
434
|
+
pipelineTracker.startStage(PIPELINE_STAGES.VERIFY);
|
|
435
|
+
let verification = null;
|
|
436
|
+
try {
|
|
437
|
+
const artifactVersions = getArtifactVersions();
|
|
438
|
+
verification = verifyRun(runDir, artifactVersions);
|
|
439
|
+
pipelineTracker.completeStage(PIPELINE_STAGES.VERIFY, {
|
|
440
|
+
ok: verification.ok,
|
|
441
|
+
errorsCount: verification.errors.length,
|
|
442
|
+
warningsCount: verification.warnings.length
|
|
443
|
+
});
|
|
444
|
+
} catch (error) {
|
|
445
|
+
pipelineTracker.failStage(PIPELINE_STAGES.VERIFY, error);
|
|
446
|
+
const failure = errorToFailure(
|
|
447
|
+
error,
|
|
448
|
+
FAILURE_CODE.VERIFICATION_FAILED,
|
|
449
|
+
'VERIFY',
|
|
450
|
+
EXECUTION_PHASE.VERIFY,
|
|
451
|
+
'verifyRun',
|
|
452
|
+
{ runDir }
|
|
453
|
+
);
|
|
454
|
+
failureLedger.record(failure);
|
|
455
|
+
// Verification failure is not blocking, but we record it
|
|
456
|
+
verification = {
|
|
457
|
+
ok: false,
|
|
458
|
+
errors: [error.message],
|
|
459
|
+
warnings: [],
|
|
460
|
+
verifiedAt: new Date().toISOString()
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// ARCHITECTURAL HARDENING: Track VERDICT stage (only after verification completes)
|
|
465
|
+
pipelineTracker.startStage(PIPELINE_STAGES.VERDICT);
|
|
466
|
+
try {
|
|
467
|
+
// PHASE 5: Compute artifact hashes and update run manifest
|
|
468
|
+
const artifactHashes = computeArtifactHashes(projectDir, runId);
|
|
469
|
+
updateRunManifestHashes(projectDir, runId, artifactHashes);
|
|
470
|
+
|
|
471
|
+
// Verdict computation (determinism and evidence law checks)
|
|
472
|
+
let determinismVerdict = null;
|
|
473
|
+
let evidenceLawViolated = false;
|
|
474
|
+
try {
|
|
475
|
+
const decisionsPath = getArtifactPath(projectDir, runId, 'decisions.json');
|
|
476
|
+
const { readFileSync, existsSync } = await import('fs');
|
|
477
|
+
if (existsSync(decisionsPath)) {
|
|
478
|
+
const decisions = JSON.parse(readFileSync(decisionsPath, 'utf-8'));
|
|
479
|
+
const { DecisionRecorder } = await import('./core/determinism-model.js');
|
|
480
|
+
const recorder = DecisionRecorder.fromExport(decisions);
|
|
481
|
+
const { computeDeterminismVerdict } = await import('./core/determinism/contract.js');
|
|
482
|
+
const verdict = computeDeterminismVerdict(recorder);
|
|
483
|
+
determinismVerdict = verdict.verdict;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Check for Evidence Law violations (incomplete evidence with CONFIRMED findings)
|
|
487
|
+
if (findings.findings) {
|
|
488
|
+
for (const finding of findings.findings) {
|
|
489
|
+
if ((finding.severity === 'CONFIRMED' || finding.status === 'CONFIRMED') &&
|
|
490
|
+
finding.evidencePackage && !finding.evidencePackage.isComplete) {
|
|
491
|
+
evidenceLawViolated = true;
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
} catch (error) {
|
|
497
|
+
// Record failure but don't block
|
|
498
|
+
const failure = createInternalFailure(
|
|
499
|
+
FAILURE_CODE.INTERNAL_UNEXPECTED_ERROR,
|
|
500
|
+
`Failed to compute determinism verdict: ${error.message}`,
|
|
501
|
+
'scan',
|
|
502
|
+
{ error: error.message },
|
|
503
|
+
error.stack
|
|
504
|
+
);
|
|
505
|
+
failureLedger.record(failure);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
pipelineTracker.completeStage(PIPELINE_STAGES.VERDICT, {
|
|
509
|
+
determinismVerdict,
|
|
510
|
+
evidenceLawViolated,
|
|
511
|
+
verificationOk: verification?.ok || false
|
|
512
|
+
});
|
|
513
|
+
} catch (error) {
|
|
514
|
+
pipelineTracker.failStage(PIPELINE_STAGES.VERDICT, error);
|
|
515
|
+
const failure = errorToFailure(
|
|
516
|
+
error,
|
|
517
|
+
FAILURE_CODE.VERDICT_COMPUTATION_FAILED,
|
|
518
|
+
'VERDICT',
|
|
519
|
+
EXECUTION_PHASE.VERDICT,
|
|
520
|
+
'computeVerdict',
|
|
521
|
+
{}
|
|
522
|
+
);
|
|
523
|
+
failureLedger.record(failure);
|
|
524
|
+
throw error;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Extract verdict data from pipeline tracker
|
|
528
|
+
const verdictStage = pipelineTracker.getStage(PIPELINE_STAGES.VERDICT);
|
|
529
|
+
const determinismVerdict = verdictStage?.determinismVerdict || null;
|
|
530
|
+
const evidenceLawViolated = verdictStage?.evidenceLawViolated || false;
|
|
157
531
|
|
|
158
532
|
return {
|
|
159
533
|
manifest,
|
|
@@ -163,8 +537,14 @@ export async function scan(projectDir, url, manifestPath = null, scanBudgetOverr
|
|
|
163
537
|
validation,
|
|
164
538
|
coverageGaps: findings.coverageGaps || [],
|
|
165
539
|
observationSummary,
|
|
166
|
-
runId,
|
|
167
|
-
runManifest
|
|
540
|
+
runId: failureLedger.runId,
|
|
541
|
+
runManifest,
|
|
542
|
+
failureLedger: failureLedger.export(),
|
|
543
|
+
ledgerPath,
|
|
544
|
+
determinismVerdict,
|
|
545
|
+
evidenceLawViolated,
|
|
546
|
+
verification,
|
|
547
|
+
pipelineStages: pipelineTracker.getAllStages()
|
|
168
548
|
};
|
|
169
549
|
}
|
|
170
550
|
|
|
@@ -65,7 +65,7 @@ function analyzeNode(node, sourceFile, projectRoot, eventType = null) {
|
|
|
65
65
|
// VALIDATION INTELLIGENCE v1: Check for return false in submit context
|
|
66
66
|
if (ts.isReturnStatement(node) && eventType === 'onSubmit') {
|
|
67
67
|
const expression = node.expression;
|
|
68
|
-
if (expression && ts.
|
|
68
|
+
if (expression && expression.kind === ts.SyntaxKind.FalseKeyword) {
|
|
69
69
|
const location = getNodeLocation(sourceFile, node, projectRoot);
|
|
70
70
|
return {
|
|
71
71
|
type: 'validation_block',
|
package/src/verax/intel/index.js
CHANGED
|
@@ -122,7 +122,7 @@ export async function runCodeIntelligence(projectRoot) {
|
|
|
122
122
|
matchAttribute: el.attrs.to ? 'to' : (el.attrs.href ? 'href' : null),
|
|
123
123
|
proof: 'PROVEN_EXPECTATION',
|
|
124
124
|
sourceRef: el.sourceRef,
|
|
125
|
-
selectorHint: extractSelectorHint({ attributes: { properties: [] } }) || `${el.tag}`,
|
|
125
|
+
selectorHint: extractSelectorHint(/** @type {any} */ ({ attributes: { properties: [] } })) || `${el.tag}`,
|
|
126
126
|
metadata: {
|
|
127
127
|
elementFile: el.file,
|
|
128
128
|
elementLine: el.line,
|
|
@@ -267,7 +267,7 @@ function generateExpectation(mapping, effect) {
|
|
|
267
267
|
* @param {Object} effect - Effect object
|
|
268
268
|
* @returns {string} - Expectation type
|
|
269
269
|
*/
|
|
270
|
-
function
|
|
270
|
+
function _determineExpectationType(effect) {
|
|
271
271
|
switch (effect.type) {
|
|
272
272
|
case 'navigation':
|
|
273
273
|
return 'navigation';
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import ts from 'typescript';
|
|
15
|
-
import { resolve, relative,
|
|
15
|
+
import { resolve, relative, basename, extname } from 'path';
|
|
16
16
|
import { existsSync, readdirSync, statSync } from 'fs';
|
|
17
|
-
import { parseFile, findNodes,
|
|
17
|
+
import { parseFile, findNodes, getNodeLocation } from './ts-program.js';
|
|
18
18
|
import { extractVueRoutes } from './vue-router-extractor.js';
|
|
19
19
|
import { normalizeDynamicRoute } from '../shared/dynamic-route-utils.js';
|
|
20
20
|
|
|
@@ -27,7 +27,7 @@ const INTERNAL_PATH_PATTERNS = [
|
|
|
27
27
|
/\/private/
|
|
28
28
|
];
|
|
29
29
|
|
|
30
|
-
function
|
|
30
|
+
function _isInternalRoute(path) {
|
|
31
31
|
return INTERNAL_PATH_PATTERNS.some(pattern => pattern.test(path));
|
|
32
32
|
}
|
|
33
33
|
|