@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
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PHASE 19 — Capability Gates
|
|
3
|
+
*
|
|
4
|
+
* Enforces quality gates for capabilities based on maturity level.
|
|
5
|
+
* No capability can be marked STABLE (or introduced) unless it satisfies required gates.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFileSync, existsSync, readdirSync } from 'fs';
|
|
9
|
+
import { join, resolve } from 'path';
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
11
|
+
import { dirname } from 'path';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* PHASE 19: Gate failure reason codes
|
|
18
|
+
*/
|
|
19
|
+
export const GATE_REASON = {
|
|
20
|
+
MISSING_TEST_MATRIX: 'GATE_MISSING_TEST_MATRIX',
|
|
21
|
+
MISSING_FIXTURE: 'GATE_MISSING_FIXTURE',
|
|
22
|
+
MISSING_DOCS: 'GATE_MISSING_DOCS',
|
|
23
|
+
MISSING_DETERMINISM: 'GATE_MISSING_DETERMINISM',
|
|
24
|
+
MISSING_ARTIFACT_ASSERTIONS: 'GATE_MISSING_ARTIFACT_ASSERTIONS',
|
|
25
|
+
MISSING_GUARDRAILS: 'GATE_MISSING_GUARDRAILS',
|
|
26
|
+
INVALID_MATURITY: 'GATE_INVALID_MATURITY',
|
|
27
|
+
NOT_IN_REGISTRY: 'GATE_NOT_IN_REGISTRY',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* PHASE 19: Capability maturity levels
|
|
32
|
+
*/
|
|
33
|
+
export const CAPABILITY_MATURITY = {
|
|
34
|
+
EXPERIMENTAL: 'experimental',
|
|
35
|
+
PARTIAL: 'partial',
|
|
36
|
+
STABLE: 'stable',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* PHASE 19: Capability categories that require guardrails
|
|
41
|
+
*/
|
|
42
|
+
const GUARDRAILS_REQUIRED_CATEGORIES = [
|
|
43
|
+
'network',
|
|
44
|
+
'navigation',
|
|
45
|
+
'routes',
|
|
46
|
+
'ui-feedback',
|
|
47
|
+
'validation',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* PHASE 19: Evaluate capability gates
|
|
52
|
+
*
|
|
53
|
+
* @param {string} capabilityId - Capability ID
|
|
54
|
+
* @param {Object} context - Context including registry, testMatrix, fixtures, docs, etc.
|
|
55
|
+
* @returns {Object} { pass, failures[], warnings[], evidence }
|
|
56
|
+
*/
|
|
57
|
+
export function evaluateCapabilityGates(capabilityId, context) {
|
|
58
|
+
const {
|
|
59
|
+
capabilityRegistry,
|
|
60
|
+
testMatrix,
|
|
61
|
+
fixtureIndex,
|
|
62
|
+
docsIndex,
|
|
63
|
+
artifactsRegistry,
|
|
64
|
+
guardrailsRules,
|
|
65
|
+
determinismTests,
|
|
66
|
+
} = context;
|
|
67
|
+
|
|
68
|
+
const failures = [];
|
|
69
|
+
const warnings = [];
|
|
70
|
+
const evidence = {
|
|
71
|
+
inRegistry: false,
|
|
72
|
+
inTestMatrix: false,
|
|
73
|
+
hasFixture: false,
|
|
74
|
+
hasDocs: false,
|
|
75
|
+
hasDeterminism: false,
|
|
76
|
+
hasArtifactAssertions: false,
|
|
77
|
+
hasGuardrails: false,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Check if capability exists in registry
|
|
81
|
+
const capability = capabilityRegistry[capabilityId];
|
|
82
|
+
if (!capability) {
|
|
83
|
+
failures.push({
|
|
84
|
+
reasonCode: GATE_REASON.NOT_IN_REGISTRY,
|
|
85
|
+
message: `Capability "${capabilityId}" not found in registry`,
|
|
86
|
+
});
|
|
87
|
+
return { pass: false, failures, warnings, evidence };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
evidence.inRegistry = true;
|
|
91
|
+
|
|
92
|
+
// Get maturity level
|
|
93
|
+
const maturity = capability.maturity || CAPABILITY_MATURITY.EXPERIMENTAL;
|
|
94
|
+
|
|
95
|
+
// Gate 1: Test Matrix (required for all maturity levels)
|
|
96
|
+
const testMatrixEntry = testMatrix[capabilityId];
|
|
97
|
+
if (!testMatrixEntry || !Array.isArray(testMatrixEntry) || testMatrixEntry.length === 0) {
|
|
98
|
+
failures.push({
|
|
99
|
+
reasonCode: GATE_REASON.MISSING_TEST_MATRIX,
|
|
100
|
+
message: `Capability "${capabilityId}" has no test matrix entries`,
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
evidence.inTestMatrix = true;
|
|
104
|
+
|
|
105
|
+
// Check for artifact assertions in test matrix entries
|
|
106
|
+
const hasArtifactAssertions = testMatrixEntry.some(entry => {
|
|
107
|
+
const assertions = entry.expectedAssertions || [];
|
|
108
|
+
return assertions.some(assertion => {
|
|
109
|
+
const assertionLower = assertion.toLowerCase();
|
|
110
|
+
return assertionLower.includes('artifact') ||
|
|
111
|
+
assertionLower.includes('findings.json') ||
|
|
112
|
+
assertionLower.includes('learn.json') ||
|
|
113
|
+
assertionLower.includes('observe.json') ||
|
|
114
|
+
assertionLower.includes('traces') ||
|
|
115
|
+
assertionLower.includes('evidence') ||
|
|
116
|
+
assertionLower.includes('determinism');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
if (!hasArtifactAssertions && maturity === CAPABILITY_MATURITY.STABLE) {
|
|
121
|
+
failures.push({
|
|
122
|
+
reasonCode: GATE_REASON.MISSING_ARTIFACT_ASSERTIONS,
|
|
123
|
+
message: `Capability "${capabilityId}" (STABLE) has no artifact assertions in test matrix`,
|
|
124
|
+
});
|
|
125
|
+
} else if (hasArtifactAssertions) {
|
|
126
|
+
evidence.hasArtifactAssertions = true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Gate 2: Fixture (required for PARTIAL and STABLE)
|
|
131
|
+
if (maturity === CAPABILITY_MATURITY.PARTIAL || maturity === CAPABILITY_MATURITY.STABLE) {
|
|
132
|
+
const hasFixture = fixtureIndex.some(fixture => {
|
|
133
|
+
return fixture.capabilities && fixture.capabilities.includes(capabilityId);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (!hasFixture) {
|
|
137
|
+
failures.push({
|
|
138
|
+
reasonCode: GATE_REASON.MISSING_FIXTURE,
|
|
139
|
+
message: `Capability "${capabilityId}" (${maturity}) has no realistic fixture mapping`,
|
|
140
|
+
});
|
|
141
|
+
} else {
|
|
142
|
+
evidence.hasFixture = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Gate 3: Docs (required for PARTIAL and STABLE)
|
|
147
|
+
if (maturity === CAPABILITY_MATURITY.PARTIAL || maturity === CAPABILITY_MATURITY.STABLE) {
|
|
148
|
+
const hasDocs = docsIndex.some(doc => {
|
|
149
|
+
return doc.capabilities && doc.capabilities.includes(capabilityId);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (!hasDocs) {
|
|
153
|
+
failures.push({
|
|
154
|
+
reasonCode: GATE_REASON.MISSING_DOCS,
|
|
155
|
+
message: `Capability "${capabilityId}" (${maturity}) has no documentation`,
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
evidence.hasDocs = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Gate 4: Determinism (required for STABLE)
|
|
163
|
+
if (maturity === CAPABILITY_MATURITY.STABLE) {
|
|
164
|
+
const hasDeterminism = determinismTests.some(test => {
|
|
165
|
+
return test.capabilities && test.capabilities.includes(capabilityId);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (!hasDeterminism) {
|
|
169
|
+
failures.push({
|
|
170
|
+
reasonCode: GATE_REASON.MISSING_DETERMINISM,
|
|
171
|
+
message: `Capability "${capabilityId}" (STABLE) has no determinism test coverage`,
|
|
172
|
+
});
|
|
173
|
+
} else {
|
|
174
|
+
evidence.hasDeterminism = true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Gate 5: Guardrails (required for STABLE in certain categories)
|
|
179
|
+
if (maturity === CAPABILITY_MATURITY.STABLE) {
|
|
180
|
+
const category = capability.category || '';
|
|
181
|
+
const categoryLower = category.toLowerCase();
|
|
182
|
+
const requiresGuardrails = GUARDRAILS_REQUIRED_CATEGORIES.some(reqCat =>
|
|
183
|
+
categoryLower.includes(reqCat)
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (requiresGuardrails) {
|
|
187
|
+
// Check if guardrails test exists and covers this category
|
|
188
|
+
const hasGuardrails = guardrailsRules.some(rule => {
|
|
189
|
+
// Check if rule covers this category or capability
|
|
190
|
+
const ruleCategories = rule.capabilities || [];
|
|
191
|
+
return ruleCategories.some(cat =>
|
|
192
|
+
cat.toLowerCase() === categoryLower ||
|
|
193
|
+
cat === capabilityId ||
|
|
194
|
+
categoryLower.includes(cat.toLowerCase())
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
if (!hasGuardrails) {
|
|
199
|
+
failures.push({
|
|
200
|
+
reasonCode: GATE_REASON.MISSING_GUARDRAILS,
|
|
201
|
+
message: `Capability "${capabilityId}" (STABLE, category: ${category}) requires guardrails coverage`,
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
evidence.hasGuardrails = true;
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
// Not required for this category
|
|
208
|
+
evidence.hasGuardrails = true; // Mark as satisfied
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const pass = failures.length === 0;
|
|
213
|
+
|
|
214
|
+
return { pass, failures, warnings, evidence };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* PHASE 19: Evaluate all capability gates
|
|
219
|
+
*
|
|
220
|
+
* @param {Object} context - Context including registry, testMatrix, fixtures, docs, etc.
|
|
221
|
+
* @returns {Object} { pass, summary, perCapability }
|
|
222
|
+
*/
|
|
223
|
+
export function evaluateAllCapabilityGates(context) {
|
|
224
|
+
const { capabilityRegistry } = context;
|
|
225
|
+
|
|
226
|
+
const perCapability = {};
|
|
227
|
+
let totalPass = 0;
|
|
228
|
+
let totalFail = 0;
|
|
229
|
+
const allFailures = [];
|
|
230
|
+
|
|
231
|
+
for (const capabilityId of Object.keys(capabilityRegistry)) {
|
|
232
|
+
const result = evaluateCapabilityGates(capabilityId, context);
|
|
233
|
+
perCapability[capabilityId] = result;
|
|
234
|
+
|
|
235
|
+
if (result.pass) {
|
|
236
|
+
totalPass++;
|
|
237
|
+
} else {
|
|
238
|
+
totalFail++;
|
|
239
|
+
allFailures.push({
|
|
240
|
+
capabilityId,
|
|
241
|
+
failures: result.failures,
|
|
242
|
+
warnings: result.warnings,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const summary = {
|
|
248
|
+
total: Object.keys(capabilityRegistry).length,
|
|
249
|
+
pass: totalPass,
|
|
250
|
+
fail: totalFail,
|
|
251
|
+
allFailures,
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const pass = totalFail === 0;
|
|
255
|
+
|
|
256
|
+
return { pass, summary, perCapability };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* PHASE 19: Build context from file system
|
|
261
|
+
*
|
|
262
|
+
* @param {Object} options - Options
|
|
263
|
+
* @returns {Promise<Object>} Context object
|
|
264
|
+
*/
|
|
265
|
+
export async function buildGateContext(options = {}) {
|
|
266
|
+
const projectRoot = options.projectRoot || resolve(__dirname, '../../../');
|
|
267
|
+
|
|
268
|
+
// Load capability registry
|
|
269
|
+
const registryModule = await import('./registry.js');
|
|
270
|
+
const capabilityRegistry = registryModule.CAPABILITY_REGISTRY || {};
|
|
271
|
+
|
|
272
|
+
// Load test matrix
|
|
273
|
+
const testMatrixPath = resolve(projectRoot, 'test/test-matrix.js');
|
|
274
|
+
let testMatrix = {};
|
|
275
|
+
if (existsSync(testMatrixPath)) {
|
|
276
|
+
try {
|
|
277
|
+
// Convert to file:// URL for cross-platform compatibility
|
|
278
|
+
const testMatrixUrl = pathToFileURL(testMatrixPath).href;
|
|
279
|
+
const testMatrixModule = await import(testMatrixUrl);
|
|
280
|
+
testMatrix = testMatrixModule.TEST_MATRIX || testMatrixModule.default || {};
|
|
281
|
+
} catch (error) {
|
|
282
|
+
// Test matrix not available - log error for debugging
|
|
283
|
+
console.error('Failed to load test matrix:', error.message);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Build fixture index
|
|
288
|
+
const fixtureIndex = buildFixtureIndex(projectRoot);
|
|
289
|
+
|
|
290
|
+
// Build docs index
|
|
291
|
+
const docsIndex = buildDocsIndex(projectRoot);
|
|
292
|
+
|
|
293
|
+
// Build guardrails rules index
|
|
294
|
+
const guardrailsRules = buildGuardrailsIndex(projectRoot);
|
|
295
|
+
|
|
296
|
+
// Build determinism tests index
|
|
297
|
+
const determinismTests = buildDeterminismTestsIndex(projectRoot);
|
|
298
|
+
|
|
299
|
+
// Load artifacts registry
|
|
300
|
+
const { ARTIFACT_REGISTRY } = await import('../artifacts/registry.js');
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
capabilityRegistry,
|
|
304
|
+
testMatrix,
|
|
305
|
+
fixtureIndex,
|
|
306
|
+
docsIndex,
|
|
307
|
+
artifactsRegistry: ARTIFACT_REGISTRY,
|
|
308
|
+
guardrailsRules,
|
|
309
|
+
determinismTests,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Build fixture index by scanning fixtures folder
|
|
315
|
+
*/
|
|
316
|
+
function buildFixtureIndex(projectRoot) {
|
|
317
|
+
const fixturesDir = resolve(projectRoot, 'test/fixtures/realistic');
|
|
318
|
+
const index = [];
|
|
319
|
+
|
|
320
|
+
if (!existsSync(fixturesDir)) {
|
|
321
|
+
return index;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
const entries = readdirSync(fixturesDir, { withFileTypes: true });
|
|
326
|
+
for (const entry of entries) {
|
|
327
|
+
if (entry.isDirectory()) {
|
|
328
|
+
const fixturePath = join(fixturesDir, entry.name);
|
|
329
|
+
const readmePath = join(fixturePath, 'README.md');
|
|
330
|
+
|
|
331
|
+
let capabilities = [];
|
|
332
|
+
if (existsSync(readmePath)) {
|
|
333
|
+
const readmeContent = readFileSync(readmePath, 'utf-8');
|
|
334
|
+
// Extract capability IDs from README (look for patterns like "capability-id" or "capabilityId")
|
|
335
|
+
// Also check for capability mentions in test matrix format
|
|
336
|
+
const capabilityMatches = readmeContent.match(/(['"])([a-z0-9-]+)(['"])/g);
|
|
337
|
+
if (capabilityMatches) {
|
|
338
|
+
capabilities = capabilityMatches.map(m => {
|
|
339
|
+
const match = m.match(/(['"])([a-z0-9-]+)(['"])/);
|
|
340
|
+
return match ? match[2] : null;
|
|
341
|
+
}).filter(Boolean);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Also look for capability registry format
|
|
345
|
+
const registryMatches = readmeContent.match(/([a-z0-9-]+-[a-z0-9-]+)/g);
|
|
346
|
+
if (registryMatches) {
|
|
347
|
+
capabilities.push(...registryMatches.filter(m => m.includes('-')));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
index.push({
|
|
352
|
+
name: entry.name,
|
|
353
|
+
path: fixturePath,
|
|
354
|
+
capabilities,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
} catch (error) {
|
|
359
|
+
// Ignore errors
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return index;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Build docs index by scanning docs folder
|
|
367
|
+
*/
|
|
368
|
+
function buildDocsIndex(projectRoot) {
|
|
369
|
+
const docsDir = resolve(projectRoot, 'docs');
|
|
370
|
+
const index = [];
|
|
371
|
+
|
|
372
|
+
if (!existsSync(docsDir)) {
|
|
373
|
+
return index;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
try {
|
|
377
|
+
const entries = readdirSync(docsDir, { withFileTypes: true });
|
|
378
|
+
for (const entry of entries) {
|
|
379
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
380
|
+
const docPath = join(docsDir, entry.name);
|
|
381
|
+
const docContent = readFileSync(docPath, 'utf-8');
|
|
382
|
+
|
|
383
|
+
// Extract capability IDs from docs (look for patterns)
|
|
384
|
+
const capabilities = [];
|
|
385
|
+
const capabilityMatches = docContent.match(/([a-z0-9-]+-[a-z0-9-]+)/g);
|
|
386
|
+
if (capabilityMatches) {
|
|
387
|
+
// Filter to likely capability IDs (contain hyphens, reasonable length)
|
|
388
|
+
for (const match of capabilityMatches) {
|
|
389
|
+
if (match.includes('-') && match.length > 5 && match.length < 50) {
|
|
390
|
+
capabilities.push(match);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
index.push({
|
|
396
|
+
name: entry.name,
|
|
397
|
+
path: docPath,
|
|
398
|
+
capabilities: [...new Set(capabilities)], // Deduplicate
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
} catch (error) {
|
|
403
|
+
// Ignore errors
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return index;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Build guardrails rules index by scanning test files
|
|
411
|
+
*/
|
|
412
|
+
function buildGuardrailsIndex(projectRoot) {
|
|
413
|
+
const testDir = resolve(projectRoot, 'test');
|
|
414
|
+
const index = [];
|
|
415
|
+
|
|
416
|
+
if (!existsSync(testDir)) {
|
|
417
|
+
return index;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
try {
|
|
421
|
+
const entries = readdirSync(testDir, { withFileTypes: true });
|
|
422
|
+
for (const entry of entries) {
|
|
423
|
+
if (entry.isFile() && entry.name.includes('guardrail')) {
|
|
424
|
+
const testPath = join(testDir, entry.name);
|
|
425
|
+
const testContent = readFileSync(testPath, 'utf-8');
|
|
426
|
+
|
|
427
|
+
// Extract capability categories from test content
|
|
428
|
+
const capabilities = [];
|
|
429
|
+
// Look for capability mentions or category mentions
|
|
430
|
+
const categoryMatches = testContent.match(/(network|navigation|routes|ui-feedback|validation)/gi);
|
|
431
|
+
if (categoryMatches) {
|
|
432
|
+
capabilities.push(...categoryMatches.map(m => m.toLowerCase()));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
index.push({
|
|
436
|
+
name: entry.name,
|
|
437
|
+
path: testPath,
|
|
438
|
+
capabilities: [...new Set(capabilities)],
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
} catch (error) {
|
|
443
|
+
// Ignore errors
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return index;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Build determinism tests index by scanning test files
|
|
451
|
+
*/
|
|
452
|
+
function buildDeterminismTestsIndex(projectRoot) {
|
|
453
|
+
const testDir = resolve(projectRoot, 'test');
|
|
454
|
+
const index = [];
|
|
455
|
+
|
|
456
|
+
if (!existsSync(testDir)) {
|
|
457
|
+
return index;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
try {
|
|
461
|
+
const entries = readdirSync(testDir, { withFileTypes: true });
|
|
462
|
+
for (const entry of entries) {
|
|
463
|
+
if (entry.isFile() && entry.name.includes('determinism')) {
|
|
464
|
+
const testPath = join(testDir, entry.name);
|
|
465
|
+
const testContent = readFileSync(testPath, 'utf-8');
|
|
466
|
+
|
|
467
|
+
// Extract capability IDs from test content by looking at test matrix references
|
|
468
|
+
const capabilities = [];
|
|
469
|
+
// Look for test matrix references or capability mentions
|
|
470
|
+
const testMatrixMatches = testContent.match(/testMatrix\[['"]([a-z0-9-]+)['"]\]/g);
|
|
471
|
+
if (testMatrixMatches) {
|
|
472
|
+
for (const match of testMatrixMatches) {
|
|
473
|
+
const capabilityMatch = match.match(/['"]([a-z0-9-]+)['"]/);
|
|
474
|
+
if (capabilityMatch) {
|
|
475
|
+
capabilities.push(capabilityMatch[1]);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Also look for capability mentions in test descriptions
|
|
481
|
+
const capabilityMentions = testContent.match(/([a-z0-9-]+-[a-z0-9-]+)/g);
|
|
482
|
+
if (capabilityMentions) {
|
|
483
|
+
capabilities.push(...capabilityMentions.filter(m => m.includes('-')));
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
index.push({
|
|
487
|
+
name: entry.name,
|
|
488
|
+
path: testPath,
|
|
489
|
+
capabilities: [...new Set(capabilities)],
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
} catch (error) {
|
|
494
|
+
// Ignore errors
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return index;
|
|
498
|
+
}
|
|
499
|
+
|