@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,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 20 — Vue SFC (Single File Component) Extractor
|
|
3
|
+
*
|
|
4
|
+
* Extracts <script>, <script setup>, and <template> content from .vue files.
|
|
5
|
+
* Deterministic and robust (no external runtime execution).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* PHASE 20: Extract Vue SFC blocks
|
|
10
|
+
*
|
|
11
|
+
* @param {string} content - Full .vue file content
|
|
12
|
+
* @param {string} filePath - Path to the .vue file (for context)
|
|
13
|
+
* @returns {Object} { scriptBlocks: [{content, lang, startLine}], template: {content, startLine} }
|
|
14
|
+
*/
|
|
15
|
+
export function extractVueSFC(content) {
|
|
16
|
+
const scriptBlocks = [];
|
|
17
|
+
let template = null;
|
|
18
|
+
|
|
19
|
+
// Extract <script> blocks (including <script setup>)
|
|
20
|
+
const scriptRegex = /<script(?:\s+setup)?(?:\s+lang=["']([^"']+)["'])?[^>]*>([\s\S]*?)<\/script>/gi;
|
|
21
|
+
let scriptMatch;
|
|
22
|
+
let lineOffset = 1;
|
|
23
|
+
|
|
24
|
+
while ((scriptMatch = scriptRegex.exec(content)) !== null) {
|
|
25
|
+
const isSetup = scriptMatch[0].includes('setup');
|
|
26
|
+
const lang = scriptMatch[1] || 'js';
|
|
27
|
+
const scriptContent = scriptMatch[2];
|
|
28
|
+
|
|
29
|
+
// Calculate start line
|
|
30
|
+
const beforeMatch = content.substring(0, scriptMatch.index);
|
|
31
|
+
const startLine = (beforeMatch.match(/\n/g) || []).length + 1;
|
|
32
|
+
|
|
33
|
+
scriptBlocks.push({
|
|
34
|
+
content: scriptContent.trim(),
|
|
35
|
+
lang: lang.toLowerCase(),
|
|
36
|
+
startLine,
|
|
37
|
+
isSetup,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Extract <template> block
|
|
42
|
+
const templateRegex = /<template[^>]*>([\s\S]*?)<\/template>/i;
|
|
43
|
+
const templateMatch = content.match(templateRegex);
|
|
44
|
+
|
|
45
|
+
if (templateMatch) {
|
|
46
|
+
const beforeTemplate = content.substring(0, templateMatch.index);
|
|
47
|
+
const templateStartLine = (beforeTemplate.match(/\n/g) || []).length + 1;
|
|
48
|
+
|
|
49
|
+
template = {
|
|
50
|
+
content: templateMatch[1].trim(),
|
|
51
|
+
startLine: templateStartLine,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
scriptBlocks,
|
|
57
|
+
template,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* PHASE 20: Extract template bindings and references
|
|
63
|
+
*
|
|
64
|
+
* @param {string} templateContent - Template content
|
|
65
|
+
* @returns {Object} { bindings: string[], routerLinks: Array, eventHandlers: Array }
|
|
66
|
+
*/
|
|
67
|
+
export function extractTemplateBindings(templateContent) {
|
|
68
|
+
const bindings = [];
|
|
69
|
+
const routerLinks = [];
|
|
70
|
+
const eventHandlers = [];
|
|
71
|
+
|
|
72
|
+
// Extract variable bindings: {{ var }}, :prop="var", v-if="var", etc.
|
|
73
|
+
const bindingPatterns = [
|
|
74
|
+
/\{\{\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\}\}/g, // {{ var }}
|
|
75
|
+
/:([a-zA-Z-]+)=["']([^"']+)["']/g, // :prop="value"
|
|
76
|
+
/v-if=["']([^"']+)["']/g, // v-if="condition"
|
|
77
|
+
/v-show=["']([^"']+)["']/g, // v-show="condition"
|
|
78
|
+
/v-model=["']([^"']+)["']/g, // v-model="value"
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
for (const pattern of bindingPatterns) {
|
|
82
|
+
let match;
|
|
83
|
+
while ((match = pattern.exec(templateContent)) !== null) {
|
|
84
|
+
const binding = match[1] || match[2];
|
|
85
|
+
if (binding && !bindings.includes(binding)) {
|
|
86
|
+
bindings.push(binding);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Extract <router-link> usage
|
|
92
|
+
const routerLinkRegex = /<router-link[^>]*\s+to=["']([^"']+)["'][^>]*>/gi;
|
|
93
|
+
let routerLinkMatch;
|
|
94
|
+
while ((routerLinkMatch = routerLinkRegex.exec(templateContent)) !== null) {
|
|
95
|
+
routerLinks.push({
|
|
96
|
+
to: routerLinkMatch[1],
|
|
97
|
+
fullMatch: routerLinkMatch[0],
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Extract event handlers: @click="handler", @submit.prevent="handler"
|
|
102
|
+
const eventHandlerRegex = /@([a-z]+)(?:\.([a-z]+)*)?=["']([^"']+)["']/gi;
|
|
103
|
+
let eventMatch;
|
|
104
|
+
while ((eventMatch = eventHandlerRegex.exec(templateContent)) !== null) {
|
|
105
|
+
eventHandlers.push({
|
|
106
|
+
event: eventMatch[1],
|
|
107
|
+
modifiers: eventMatch[2] ? eventMatch[2].split('.') : [],
|
|
108
|
+
handler: eventMatch[3],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
bindings,
|
|
114
|
+
routerLinks,
|
|
115
|
+
eventHandlers,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* PHASE 20: Map template handlers to script functions
|
|
121
|
+
*
|
|
122
|
+
* @param {Object} templateBindings - Result from extractTemplateBindings
|
|
123
|
+
* @param {Array} scriptBlocks - Script blocks from extractVueSFC
|
|
124
|
+
* @returns {Map} handlerName -> { scriptBlock, functionInfo }
|
|
125
|
+
*/
|
|
126
|
+
export function mapTemplateHandlersToScript(templateBindings, scriptBlocks) {
|
|
127
|
+
const handlerMap = new Map();
|
|
128
|
+
|
|
129
|
+
for (const handler of templateBindings.eventHandlers) {
|
|
130
|
+
const handlerName = handler.handler;
|
|
131
|
+
|
|
132
|
+
// Find handler in script blocks
|
|
133
|
+
for (const scriptBlock of scriptBlocks) {
|
|
134
|
+
const scriptContent = scriptBlock.content;
|
|
135
|
+
|
|
136
|
+
// Look for function declarations: function handlerName() {}
|
|
137
|
+
const functionRegex = new RegExp(`(?:function|const|let|var)\\s+${handlerName}\\s*[=(]`, 'g');
|
|
138
|
+
if (functionRegex.test(scriptContent)) {
|
|
139
|
+
handlerMap.set(handlerName, {
|
|
140
|
+
scriptBlock,
|
|
141
|
+
handler,
|
|
142
|
+
type: 'function',
|
|
143
|
+
});
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Look for method definitions: methods: { handlerName() {} }
|
|
148
|
+
const methodRegex = new RegExp(`(?:methods|setup)\\s*:\\s*\\{[^}]*${handlerName}\\s*[:(]`, 's');
|
|
149
|
+
if (methodRegex.test(scriptContent)) {
|
|
150
|
+
handlerMap.set(handlerName, {
|
|
151
|
+
scriptBlock,
|
|
152
|
+
handler,
|
|
153
|
+
type: 'method',
|
|
154
|
+
});
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return handlerMap;
|
|
161
|
+
}
|
|
162
|
+
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 20 — Vue State Promise Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects ref/reactive mutations that are UI-bound:
|
|
5
|
+
* - ref declarations: const count = ref(0);
|
|
6
|
+
* - reactive: const state = reactive({ x: 1 });
|
|
7
|
+
* - Only emit if identifiers are used in template bindings
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { parse } from '@babel/parser';
|
|
11
|
+
import _traverse from '@babel/traverse';
|
|
12
|
+
|
|
13
|
+
const traverse = _traverse.default || _traverse;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* PHASE 20: Detect Vue state promises
|
|
17
|
+
*
|
|
18
|
+
* @param {string} scriptContent - Script block content
|
|
19
|
+
* @param {string} filePath - File path
|
|
20
|
+
* @param {string} relPath - Relative path
|
|
21
|
+
* @param {Object} scriptBlock - Script block metadata
|
|
22
|
+
* @param {Object} templateBindings - Template bindings
|
|
23
|
+
* @returns {Array} State promises
|
|
24
|
+
*/
|
|
25
|
+
export function detectVueStatePromises(scriptContent, filePath, relPath, scriptBlock, templateBindings) {
|
|
26
|
+
const promises = [];
|
|
27
|
+
const templateVars = new Set(templateBindings.bindings || []);
|
|
28
|
+
|
|
29
|
+
if (templateVars.size === 0) {
|
|
30
|
+
return promises; // No template bindings, skip
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const ast = parse(scriptContent, {
|
|
35
|
+
sourceType: 'module',
|
|
36
|
+
plugins: [
|
|
37
|
+
'typescript',
|
|
38
|
+
'classProperties',
|
|
39
|
+
'optionalChaining',
|
|
40
|
+
'nullishCoalescingOperator',
|
|
41
|
+
'dynamicImport',
|
|
42
|
+
'topLevelAwait',
|
|
43
|
+
'objectRestSpread',
|
|
44
|
+
],
|
|
45
|
+
errorRecovery: true,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const lines = scriptContent.split('\n');
|
|
49
|
+
const refDeclarations = new Map(); // varName -> { loc, astSource }
|
|
50
|
+
const reactiveDeclarations = new Map();
|
|
51
|
+
|
|
52
|
+
traverse(ast, {
|
|
53
|
+
// Detect ref() declarations
|
|
54
|
+
VariableDeclarator(path) {
|
|
55
|
+
const node = path.node;
|
|
56
|
+
const init = node.init;
|
|
57
|
+
|
|
58
|
+
if (init && init.type === 'CallExpression') {
|
|
59
|
+
const callee = init.callee;
|
|
60
|
+
|
|
61
|
+
// ref(0) or ref({})
|
|
62
|
+
if (callee.type === 'Identifier' && callee.name === 'ref') {
|
|
63
|
+
const varName = node.id.name;
|
|
64
|
+
if (templateVars.has(varName)) {
|
|
65
|
+
const loc = node.loc;
|
|
66
|
+
const line = loc ? loc.start.line : 1;
|
|
67
|
+
const astSource = lines.slice(line - 1, loc ? loc.end.line : line)
|
|
68
|
+
.join('\n')
|
|
69
|
+
.substring(0, 200);
|
|
70
|
+
|
|
71
|
+
refDeclarations.set(varName, {
|
|
72
|
+
loc,
|
|
73
|
+
astSource,
|
|
74
|
+
line,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// reactive({})
|
|
80
|
+
if (callee.type === 'Identifier' && callee.name === 'reactive') {
|
|
81
|
+
const varName = node.id.name;
|
|
82
|
+
if (templateVars.has(varName)) {
|
|
83
|
+
const loc = node.loc;
|
|
84
|
+
const line = loc ? loc.start.line : 1;
|
|
85
|
+
const astSource = lines.slice(line - 1, loc ? loc.end.line : line)
|
|
86
|
+
.join('\n')
|
|
87
|
+
.substring(0, 200);
|
|
88
|
+
|
|
89
|
+
reactiveDeclarations.set(varName, {
|
|
90
|
+
loc,
|
|
91
|
+
astSource,
|
|
92
|
+
line,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
// Detect mutations: count.value = ... or state.x = ...
|
|
100
|
+
AssignmentExpression(path) {
|
|
101
|
+
const node = path.node;
|
|
102
|
+
const left = node.left;
|
|
103
|
+
|
|
104
|
+
// count.value = ...
|
|
105
|
+
if (left.type === 'MemberExpression' &&
|
|
106
|
+
left.property.name === 'value' &&
|
|
107
|
+
left.object.type === 'Identifier') {
|
|
108
|
+
const varName = left.object.name;
|
|
109
|
+
|
|
110
|
+
if (refDeclarations.has(varName) && templateVars.has(varName)) {
|
|
111
|
+
const decl = refDeclarations.get(varName);
|
|
112
|
+
const loc = node.loc;
|
|
113
|
+
const line = loc ? loc.start.line : 1;
|
|
114
|
+
const column = loc ? loc.start.column : 0;
|
|
115
|
+
|
|
116
|
+
const astSource = lines.slice(line - 1, loc ? loc.end.line : line)
|
|
117
|
+
.join('\n')
|
|
118
|
+
.substring(0, 200);
|
|
119
|
+
|
|
120
|
+
const context = buildContext(path);
|
|
121
|
+
|
|
122
|
+
promises.push({
|
|
123
|
+
type: 'state',
|
|
124
|
+
promise: {
|
|
125
|
+
kind: 'state-change',
|
|
126
|
+
value: `${varName}.value`,
|
|
127
|
+
stateVar: varName,
|
|
128
|
+
},
|
|
129
|
+
source: {
|
|
130
|
+
file: relPath,
|
|
131
|
+
line,
|
|
132
|
+
column,
|
|
133
|
+
context,
|
|
134
|
+
astSource,
|
|
135
|
+
},
|
|
136
|
+
confidence: 0.9,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// state.x = ...
|
|
142
|
+
if (left.type === 'MemberExpression' &&
|
|
143
|
+
left.object.type === 'Identifier') {
|
|
144
|
+
const varName = left.object.name;
|
|
145
|
+
|
|
146
|
+
if (reactiveDeclarations.has(varName) && templateVars.has(varName)) {
|
|
147
|
+
const loc = node.loc;
|
|
148
|
+
const line = loc ? loc.start.line : 1;
|
|
149
|
+
const column = loc ? loc.start.column : 0;
|
|
150
|
+
|
|
151
|
+
const astSource = lines.slice(line - 1, loc ? loc.end.line : line)
|
|
152
|
+
.join('\n')
|
|
153
|
+
.substring(0, 200);
|
|
154
|
+
|
|
155
|
+
const context = buildContext(path);
|
|
156
|
+
const propName = left.property.name || '<property>';
|
|
157
|
+
|
|
158
|
+
promises.push({
|
|
159
|
+
type: 'state',
|
|
160
|
+
promise: {
|
|
161
|
+
kind: 'state-change',
|
|
162
|
+
value: `${varName}.${propName}`,
|
|
163
|
+
stateVar: varName,
|
|
164
|
+
},
|
|
165
|
+
source: {
|
|
166
|
+
file: relPath,
|
|
167
|
+
line,
|
|
168
|
+
column,
|
|
169
|
+
context,
|
|
170
|
+
astSource,
|
|
171
|
+
},
|
|
172
|
+
confidence: 0.9,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
} catch (error) {
|
|
179
|
+
// Parse error - skip
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return promises;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Build context chain from AST path
|
|
187
|
+
*/
|
|
188
|
+
function buildContext(path) {
|
|
189
|
+
const context = [];
|
|
190
|
+
let current = path;
|
|
191
|
+
|
|
192
|
+
while (current) {
|
|
193
|
+
if (current.isFunctionDeclaration()) {
|
|
194
|
+
context.push({
|
|
195
|
+
type: 'function',
|
|
196
|
+
name: current.node.id?.name || '<anonymous>',
|
|
197
|
+
});
|
|
198
|
+
} else if (current.isArrowFunctionExpression()) {
|
|
199
|
+
context.push({
|
|
200
|
+
type: 'arrow-function',
|
|
201
|
+
name: '<arrow>',
|
|
202
|
+
});
|
|
203
|
+
} else if (current.isMethodDefinition()) {
|
|
204
|
+
context.push({
|
|
205
|
+
type: 'method',
|
|
206
|
+
name: current.node.key?.name || '<method>',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
current = current.parentPath;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return context.reverse().map(c => `${c.type}:${c.name}`).join(' > ');
|
|
214
|
+
}
|
|
215
|
+
|
|
@@ -14,11 +14,22 @@
|
|
|
14
14
|
export function formatFinding(finding, expectation = null) {
|
|
15
15
|
const lines = [];
|
|
16
16
|
|
|
17
|
-
// Finding type and confidence
|
|
18
|
-
const confidenceLevel = finding.confidence?.level || '
|
|
19
|
-
const confidenceScore = finding.confidence
|
|
17
|
+
// PHASE 15: Finding type and unified confidence
|
|
18
|
+
const confidenceLevel = finding.confidenceLevel || finding.confidence?.level || 'UNPROVEN';
|
|
19
|
+
const confidenceScore = finding.confidence !== undefined
|
|
20
|
+
? (finding.confidence <= 1 ? Math.round(finding.confidence * 100) : finding.confidence)
|
|
21
|
+
: (finding.confidence?.score || 0);
|
|
22
|
+
const confidenceReasons = finding.confidenceReasons || [];
|
|
23
|
+
|
|
20
24
|
lines.push(` [${confidenceLevel} (${confidenceScore}%)] ${finding.type || 'unknown'}`);
|
|
21
25
|
|
|
26
|
+
// PHASE 15: Show top confidence reasons
|
|
27
|
+
if (confidenceReasons.length > 0) {
|
|
28
|
+
const topReasons = confidenceReasons.slice(0, 3);
|
|
29
|
+
const reasonText = topReasons.join(', ');
|
|
30
|
+
lines.push(` └─ Confidence: ${reasonText}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
22
33
|
// Expectation (what was promised)
|
|
23
34
|
if (expectation) {
|
|
24
35
|
let expectationDesc = '';
|
|
@@ -84,6 +95,48 @@ export function formatFinding(finding, expectation = null) {
|
|
|
84
95
|
lines.push(` └─ Source: ${expectation.source.file}${sourceLine}`);
|
|
85
96
|
}
|
|
86
97
|
|
|
98
|
+
// PHASE 16: Show evidence completeness
|
|
99
|
+
if (finding.evidencePackage) {
|
|
100
|
+
if (finding.evidencePackage.isComplete) {
|
|
101
|
+
lines.push(` └─ Evidence: Complete`);
|
|
102
|
+
} else {
|
|
103
|
+
lines.push(` └─ Evidence: Incomplete (missing: ${finding.evidencePackage.missingEvidence.join(', ')})`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// PHASE 16: Show downgrade reason if evidence was incomplete
|
|
108
|
+
if (finding.evidenceCompleteness?.downgraded) {
|
|
109
|
+
lines.push(` └─ Downgraded: ${finding.evidenceCompleteness.downgradeReason}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// PHASE 17/23: Show guardrails applied and reconciliation
|
|
113
|
+
if (finding.guardrails) {
|
|
114
|
+
const guardrails = finding.guardrails;
|
|
115
|
+
if (guardrails.appliedRules && guardrails.appliedRules.length > 0) {
|
|
116
|
+
const ruleCodes = guardrails.appliedRules.map(r => r.code || r.ruleId).filter(Boolean);
|
|
117
|
+
if (ruleCodes.length > 0) {
|
|
118
|
+
lines.push(` └─ Final status due to guardrails: ${ruleCodes.join(', ')}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (guardrails.contradictions && guardrails.contradictions.length > 0) {
|
|
122
|
+
lines.push(` └─ Contradiction: ${guardrails.contradictions[0].message}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// PHASE 23: Show confidence reconciliation if present
|
|
126
|
+
if (guardrails.reconciliation) {
|
|
127
|
+
const recon = guardrails.reconciliation;
|
|
128
|
+
if (recon.confidenceBefore !== recon.confidenceAfter) {
|
|
129
|
+
const beforePercent = Math.round(recon.confidenceBefore * 100);
|
|
130
|
+
const afterPercent = Math.round(recon.confidenceAfter * 100);
|
|
131
|
+
lines.push(` └─ Confidence: ${beforePercent}% → ${afterPercent}% (${recon.confidenceLevelBefore} → ${recon.confidenceLevelAfter})`);
|
|
132
|
+
}
|
|
133
|
+
if (recon.reconciliationReasons && recon.reconciliationReasons.length > 0) {
|
|
134
|
+
const topReasons = recon.reconciliationReasons.slice(0, 2);
|
|
135
|
+
lines.push(` └─ Reconciliation: ${topReasons.join(', ')}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
87
140
|
return lines.join('\n');
|
|
88
141
|
}
|
|
89
142
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
|
|
3
|
+
export const ARTIFACT_REGISTRY = {
|
|
4
|
+
runStatus: {
|
|
5
|
+
key: 'runStatus',
|
|
6
|
+
filename: 'run.status.json',
|
|
7
|
+
stage: 'init',
|
|
8
|
+
contractVersion: 1,
|
|
9
|
+
type: 'file'
|
|
10
|
+
},
|
|
11
|
+
runMeta: {
|
|
12
|
+
key: 'runMeta',
|
|
13
|
+
filename: 'run.meta.json',
|
|
14
|
+
stage: 'init',
|
|
15
|
+
contractVersion: 1,
|
|
16
|
+
type: 'file'
|
|
17
|
+
},
|
|
18
|
+
summary: {
|
|
19
|
+
key: 'summary',
|
|
20
|
+
filename: 'summary.json',
|
|
21
|
+
stage: 'finalize',
|
|
22
|
+
contractVersion: 1,
|
|
23
|
+
type: 'file'
|
|
24
|
+
},
|
|
25
|
+
findings: {
|
|
26
|
+
key: 'findings',
|
|
27
|
+
filename: 'findings.json',
|
|
28
|
+
stage: 'detect',
|
|
29
|
+
contractVersion: 1,
|
|
30
|
+
type: 'file'
|
|
31
|
+
},
|
|
32
|
+
learn: {
|
|
33
|
+
key: 'learn',
|
|
34
|
+
filename: 'learn.json',
|
|
35
|
+
stage: 'learn',
|
|
36
|
+
contractVersion: 1,
|
|
37
|
+
type: 'file'
|
|
38
|
+
},
|
|
39
|
+
observe: {
|
|
40
|
+
key: 'observe',
|
|
41
|
+
filename: 'observe.json',
|
|
42
|
+
stage: 'observe',
|
|
43
|
+
contractVersion: 1,
|
|
44
|
+
type: 'file'
|
|
45
|
+
},
|
|
46
|
+
project: {
|
|
47
|
+
key: 'project',
|
|
48
|
+
filename: 'project.json',
|
|
49
|
+
stage: 'init',
|
|
50
|
+
contractVersion: 1,
|
|
51
|
+
type: 'file'
|
|
52
|
+
},
|
|
53
|
+
traces: {
|
|
54
|
+
key: 'traces',
|
|
55
|
+
filename: 'traces.jsonl',
|
|
56
|
+
stage: 'finalize',
|
|
57
|
+
contractVersion: 1,
|
|
58
|
+
type: 'file'
|
|
59
|
+
},
|
|
60
|
+
evidence: {
|
|
61
|
+
key: 'evidence',
|
|
62
|
+
filename: 'evidence',
|
|
63
|
+
stage: 'observe',
|
|
64
|
+
contractVersion: 1,
|
|
65
|
+
type: 'directory'
|
|
66
|
+
},
|
|
67
|
+
scanSummary: {
|
|
68
|
+
key: 'scanSummary',
|
|
69
|
+
filename: 'scan-summary.json',
|
|
70
|
+
stage: 'finalize',
|
|
71
|
+
contractVersion: 1,
|
|
72
|
+
type: 'file'
|
|
73
|
+
},
|
|
74
|
+
determinismReport: {
|
|
75
|
+
key: 'determinismReport',
|
|
76
|
+
filename: 'determinism.report.json',
|
|
77
|
+
stage: 'finalize',
|
|
78
|
+
contractVersion: 1,
|
|
79
|
+
type: 'file'
|
|
80
|
+
},
|
|
81
|
+
evidenceIntent: {
|
|
82
|
+
key: 'evidenceIntent',
|
|
83
|
+
filename: 'evidence.intent.json',
|
|
84
|
+
stage: 'detect',
|
|
85
|
+
contractVersion: 1,
|
|
86
|
+
type: 'file'
|
|
87
|
+
},
|
|
88
|
+
guardrailsReport: {
|
|
89
|
+
key: 'guardrailsReport',
|
|
90
|
+
filename: 'guardrails.report.json',
|
|
91
|
+
stage: 'detect',
|
|
92
|
+
contractVersion: 1,
|
|
93
|
+
type: 'file'
|
|
94
|
+
},
|
|
95
|
+
confidenceReport: {
|
|
96
|
+
key: 'confidenceReport',
|
|
97
|
+
filename: 'confidence.report.json',
|
|
98
|
+
stage: 'detect',
|
|
99
|
+
contractVersion: 1,
|
|
100
|
+
type: 'file'
|
|
101
|
+
},
|
|
102
|
+
determinismContract: {
|
|
103
|
+
key: 'determinismContract',
|
|
104
|
+
filename: 'determinism.contract.json',
|
|
105
|
+
stage: 'observe',
|
|
106
|
+
contractVersion: 1,
|
|
107
|
+
type: 'file'
|
|
108
|
+
},
|
|
109
|
+
determinismReport: {
|
|
110
|
+
key: 'determinismReport',
|
|
111
|
+
filename: 'determinism.report.json',
|
|
112
|
+
stage: 'verify',
|
|
113
|
+
contractVersion: 1,
|
|
114
|
+
type: 'file'
|
|
115
|
+
},
|
|
116
|
+
runMeta: {
|
|
117
|
+
key: 'runMeta',
|
|
118
|
+
filename: 'run.meta.json',
|
|
119
|
+
stage: 'learn',
|
|
120
|
+
contractVersion: 1,
|
|
121
|
+
type: 'file'
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export function getArtifactVersions() {
|
|
126
|
+
const versions = {};
|
|
127
|
+
for (const [key, def] of Object.entries(ARTIFACT_REGISTRY)) {
|
|
128
|
+
versions[key] = def.contractVersion;
|
|
129
|
+
}
|
|
130
|
+
return versions;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function buildRunArtifactPaths(baseDir) {
|
|
134
|
+
return {
|
|
135
|
+
baseDir,
|
|
136
|
+
runStatusJson: join(baseDir, ARTIFACT_REGISTRY.runStatus.filename),
|
|
137
|
+
runMetaJson: join(baseDir, ARTIFACT_REGISTRY.runMeta.filename),
|
|
138
|
+
summaryJson: join(baseDir, ARTIFACT_REGISTRY.summary.filename),
|
|
139
|
+
findingsJson: join(baseDir, ARTIFACT_REGISTRY.findings.filename),
|
|
140
|
+
tracesJsonl: join(baseDir, ARTIFACT_REGISTRY.traces.filename),
|
|
141
|
+
evidenceDir: join(baseDir, ARTIFACT_REGISTRY.evidence.filename),
|
|
142
|
+
learnJson: join(baseDir, ARTIFACT_REGISTRY.learn.filename),
|
|
143
|
+
observeJson: join(baseDir, ARTIFACT_REGISTRY.observe.filename),
|
|
144
|
+
projectJson: join(baseDir, ARTIFACT_REGISTRY.project.filename),
|
|
145
|
+
scanSummaryJson: join(baseDir, ARTIFACT_REGISTRY.scanSummary.filename),
|
|
146
|
+
evidenceIntentJson: join(baseDir, ARTIFACT_REGISTRY.evidenceIntent.filename),
|
|
147
|
+
guardrailsReportJson: join(baseDir, ARTIFACT_REGISTRY.guardrailsReport.filename),
|
|
148
|
+
confidenceReportJson: join(baseDir, ARTIFACT_REGISTRY.confidenceReport.filename),
|
|
149
|
+
determinismContractJson: join(baseDir, ARTIFACT_REGISTRY.determinismContract.filename),
|
|
150
|
+
determinismReportJson: join(baseDir, ARTIFACT_REGISTRY.determinismReport.filename),
|
|
151
|
+
runMetaJson: join(baseDir, ARTIFACT_REGISTRY.runMeta.filename),
|
|
152
|
+
artifactVersions: getArtifactVersions()
|
|
153
|
+
};
|
|
154
|
+
}
|