@veraxhq/verax 0.2.1 → 0.4.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.
Files changed (213) hide show
  1. package/README.md +10 -6
  2. package/bin/verax.js +11 -11
  3. package/package.json +29 -8
  4. package/src/cli/commands/baseline.js +103 -0
  5. package/src/cli/commands/default.js +51 -6
  6. package/src/cli/commands/doctor.js +29 -0
  7. package/src/cli/commands/ga.js +246 -0
  8. package/src/cli/commands/gates.js +95 -0
  9. package/src/cli/commands/inspect.js +4 -2
  10. package/src/cli/commands/release-check.js +215 -0
  11. package/src/cli/commands/run.js +45 -6
  12. package/src/cli/commands/security-check.js +212 -0
  13. package/src/cli/commands/truth.js +113 -0
  14. package/src/cli/entry.js +30 -20
  15. package/src/cli/util/angular-component-extractor.js +179 -0
  16. package/src/cli/util/angular-navigation-detector.js +141 -0
  17. package/src/cli/util/angular-network-detector.js +161 -0
  18. package/src/cli/util/angular-state-detector.js +162 -0
  19. package/src/cli/util/ast-interactive-detector.js +544 -0
  20. package/src/cli/util/ast-network-detector.js +603 -0
  21. package/src/cli/util/ast-promise-extractor.js +581 -0
  22. package/src/cli/util/ast-usestate-detector.js +602 -0
  23. package/src/cli/util/atomic-write.js +12 -1
  24. package/src/cli/util/bootstrap-guard.js +86 -0
  25. package/src/cli/util/console-reporter.js +72 -0
  26. package/src/cli/util/detection-engine.js +105 -41
  27. package/src/cli/util/determinism-runner.js +124 -0
  28. package/src/cli/util/determinism-writer.js +129 -0
  29. package/src/cli/util/digest-engine.js +359 -0
  30. package/src/cli/util/dom-diff.js +226 -0
  31. package/src/cli/util/evidence-engine.js +287 -0
  32. package/src/cli/util/expectation-extractor.js +151 -5
  33. package/src/cli/util/findings-writer.js +3 -0
  34. package/src/cli/util/framework-detector.js +572 -0
  35. package/src/cli/util/idgen.js +1 -1
  36. package/src/cli/util/interaction-planner.js +529 -0
  37. package/src/cli/util/learn-writer.js +2 -0
  38. package/src/cli/util/ledger-writer.js +110 -0
  39. package/src/cli/util/monorepo-resolver.js +162 -0
  40. package/src/cli/util/observation-engine.js +127 -278
  41. package/src/cli/util/observe-writer.js +2 -0
  42. package/src/cli/util/project-discovery.js +284 -0
  43. package/src/cli/util/project-writer.js +2 -0
  44. package/src/cli/util/run-id.js +23 -27
  45. package/src/cli/util/run-resolver.js +64 -0
  46. package/src/cli/util/run-result.js +778 -0
  47. package/src/cli/util/selector-resolver.js +235 -0
  48. package/src/cli/util/source-requirement.js +55 -0
  49. package/src/cli/util/summary-writer.js +2 -0
  50. package/src/cli/util/svelte-navigation-detector.js +163 -0
  51. package/src/cli/util/svelte-network-detector.js +80 -0
  52. package/src/cli/util/svelte-sfc-extractor.js +146 -0
  53. package/src/cli/util/svelte-state-detector.js +242 -0
  54. package/src/cli/util/trust-activation-integration.js +496 -0
  55. package/src/cli/util/trust-activation-wrapper.js +85 -0
  56. package/src/cli/util/trust-integration-hooks.js +164 -0
  57. package/src/cli/util/types.js +153 -0
  58. package/src/cli/util/url-validation.js +40 -0
  59. package/src/cli/util/vue-navigation-detector.js +178 -0
  60. package/src/cli/util/vue-sfc-extractor.js +161 -0
  61. package/src/cli/util/vue-state-detector.js +215 -0
  62. package/src/types/fs-augment.d.ts +23 -0
  63. package/src/types/global.d.ts +137 -0
  64. package/src/types/internal-types.d.ts +35 -0
  65. package/src/verax/cli/init.js +4 -18
  66. package/src/verax/core/action-classifier.js +4 -3
  67. package/src/verax/core/artifacts/registry.js +139 -0
  68. package/src/verax/core/artifacts/verifier.js +990 -0
  69. package/src/verax/core/baseline/baseline.enforcer.js +137 -0
  70. package/src/verax/core/baseline/baseline.snapshot.js +233 -0
  71. package/src/verax/core/capabilities/gates.js +505 -0
  72. package/src/verax/core/capabilities/registry.js +475 -0
  73. package/src/verax/core/confidence/confidence-compute.js +144 -0
  74. package/src/verax/core/confidence/confidence-invariants.js +234 -0
  75. package/src/verax/core/confidence/confidence-report-writer.js +112 -0
  76. package/src/verax/core/confidence/confidence-weights.js +44 -0
  77. package/src/verax/core/confidence/confidence.defaults.js +65 -0
  78. package/src/verax/core/confidence/confidence.loader.js +80 -0
  79. package/src/verax/core/confidence/confidence.schema.js +94 -0
  80. package/src/verax/core/confidence-engine-refactor.js +489 -0
  81. package/src/verax/core/confidence-engine.js +625 -0
  82. package/src/verax/core/contracts/index.js +29 -0
  83. package/src/verax/core/contracts/types.js +186 -0
  84. package/src/verax/core/contracts/validators.js +456 -0
  85. package/src/verax/core/decisions/decision.trace.js +278 -0
  86. package/src/verax/core/determinism/contract-writer.js +89 -0
  87. package/src/verax/core/determinism/contract.js +139 -0
  88. package/src/verax/core/determinism/diff.js +405 -0
  89. package/src/verax/core/determinism/engine.js +222 -0
  90. package/src/verax/core/determinism/finding-identity.js +149 -0
  91. package/src/verax/core/determinism/normalize.js +466 -0
  92. package/src/verax/core/determinism/report-writer.js +93 -0
  93. package/src/verax/core/determinism/run-fingerprint.js +123 -0
  94. package/src/verax/core/dynamic-route-intelligence.js +529 -0
  95. package/src/verax/core/evidence/evidence-capture-service.js +308 -0
  96. package/src/verax/core/evidence/evidence-intent-ledger.js +166 -0
  97. package/src/verax/core/evidence-builder.js +487 -0
  98. package/src/verax/core/execution-mode-context.js +77 -0
  99. package/src/verax/core/execution-mode-detector.js +192 -0
  100. package/src/verax/core/failures/exit-codes.js +88 -0
  101. package/src/verax/core/failures/failure-summary.js +76 -0
  102. package/src/verax/core/failures/failure.factory.js +225 -0
  103. package/src/verax/core/failures/failure.ledger.js +133 -0
  104. package/src/verax/core/failures/failure.types.js +196 -0
  105. package/src/verax/core/failures/index.js +10 -0
  106. package/src/verax/core/ga/ga-report-writer.js +43 -0
  107. package/src/verax/core/ga/ga.artifact.js +49 -0
  108. package/src/verax/core/ga/ga.contract.js +435 -0
  109. package/src/verax/core/ga/ga.enforcer.js +87 -0
  110. package/src/verax/core/guardrails/guardrails-report-writer.js +109 -0
  111. package/src/verax/core/guardrails/policy.defaults.js +210 -0
  112. package/src/verax/core/guardrails/policy.loader.js +84 -0
  113. package/src/verax/core/guardrails/policy.schema.js +110 -0
  114. package/src/verax/core/guardrails/truth-reconciliation.js +136 -0
  115. package/src/verax/core/guardrails-engine.js +505 -0
  116. package/src/verax/core/incremental-store.js +1 -0
  117. package/src/verax/core/integrity/budget.js +138 -0
  118. package/src/verax/core/integrity/determinism.js +342 -0
  119. package/src/verax/core/integrity/integrity.js +208 -0
  120. package/src/verax/core/integrity/poisoning.js +108 -0
  121. package/src/verax/core/integrity/transaction.js +140 -0
  122. package/src/verax/core/observe/run-timeline.js +318 -0
  123. package/src/verax/core/perf/perf.contract.js +186 -0
  124. package/src/verax/core/perf/perf.display.js +65 -0
  125. package/src/verax/core/perf/perf.enforcer.js +91 -0
  126. package/src/verax/core/perf/perf.monitor.js +209 -0
  127. package/src/verax/core/perf/perf.report.js +200 -0
  128. package/src/verax/core/pipeline-tracker.js +243 -0
  129. package/src/verax/core/product-definition.js +127 -0
  130. package/src/verax/core/release/provenance.builder.js +130 -0
  131. package/src/verax/core/release/release-report-writer.js +40 -0
  132. package/src/verax/core/release/release.enforcer.js +164 -0
  133. package/src/verax/core/release/reproducibility.check.js +222 -0
  134. package/src/verax/core/release/sbom.builder.js +292 -0
  135. package/src/verax/core/replay-validator.js +2 -0
  136. package/src/verax/core/replay.js +4 -0
  137. package/src/verax/core/report/cross-index.js +195 -0
  138. package/src/verax/core/report/human-summary.js +362 -0
  139. package/src/verax/core/route-intelligence.js +420 -0
  140. package/src/verax/core/run-id.js +6 -3
  141. package/src/verax/core/run-manifest.js +4 -3
  142. package/src/verax/core/security/secrets.scan.js +329 -0
  143. package/src/verax/core/security/security-report.js +50 -0
  144. package/src/verax/core/security/security.enforcer.js +128 -0
  145. package/src/verax/core/security/supplychain.defaults.json +38 -0
  146. package/src/verax/core/security/supplychain.policy.js +334 -0
  147. package/src/verax/core/security/vuln.scan.js +265 -0
  148. package/src/verax/core/truth/truth.certificate.js +252 -0
  149. package/src/verax/core/ui-feedback-intelligence.js +481 -0
  150. package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
  151. package/src/verax/detect/confidence-engine.js +62 -34
  152. package/src/verax/detect/confidence-helper.js +34 -0
  153. package/src/verax/detect/dynamic-route-findings.js +338 -0
  154. package/src/verax/detect/expectation-chain-detector.js +417 -0
  155. package/src/verax/detect/expectation-model.js +2 -2
  156. package/src/verax/detect/failure-cause-inference.js +293 -0
  157. package/src/verax/detect/findings-writer.js +131 -35
  158. package/src/verax/detect/flow-detector.js +2 -2
  159. package/src/verax/detect/form-silent-failure.js +98 -0
  160. package/src/verax/detect/index.js +46 -5
  161. package/src/verax/detect/invariants-enforcer.js +147 -0
  162. package/src/verax/detect/journey-stall-detector.js +558 -0
  163. package/src/verax/detect/navigation-silent-failure.js +82 -0
  164. package/src/verax/detect/problem-aggregator.js +361 -0
  165. package/src/verax/detect/route-findings.js +219 -0
  166. package/src/verax/detect/summary-writer.js +477 -0
  167. package/src/verax/detect/test-failure-cause-inference.js +314 -0
  168. package/src/verax/detect/ui-feedback-findings.js +207 -0
  169. package/src/verax/detect/view-switch-correlator.js +242 -0
  170. package/src/verax/flow/flow-engine.js +2 -1
  171. package/src/verax/flow/flow-spec.js +0 -6
  172. package/src/verax/index.js +4 -0
  173. package/src/verax/intel/ts-program.js +1 -0
  174. package/src/verax/intel/vue-navigation-extractor.js +3 -0
  175. package/src/verax/learn/action-contract-extractor.js +3 -0
  176. package/src/verax/learn/ast-contract-extractor.js +1 -1
  177. package/src/verax/learn/flow-extractor.js +1 -0
  178. package/src/verax/learn/project-detector.js +5 -0
  179. package/src/verax/learn/react-router-extractor.js +2 -0
  180. package/src/verax/learn/source-instrumenter.js +1 -0
  181. package/src/verax/learn/state-extractor.js +2 -1
  182. package/src/verax/learn/static-extractor.js +1 -0
  183. package/src/verax/observe/coverage-gaps.js +132 -0
  184. package/src/verax/observe/expectation-handler.js +126 -0
  185. package/src/verax/observe/incremental-skip.js +46 -0
  186. package/src/verax/observe/index.js +51 -155
  187. package/src/verax/observe/interaction-executor.js +192 -0
  188. package/src/verax/observe/interaction-runner.js +782 -513
  189. package/src/verax/observe/network-firewall.js +86 -0
  190. package/src/verax/observe/observation-builder.js +169 -0
  191. package/src/verax/observe/observe-context.js +205 -0
  192. package/src/verax/observe/observe-helpers.js +192 -0
  193. package/src/verax/observe/observe-runner.js +230 -0
  194. package/src/verax/observe/observers/budget-observer.js +185 -0
  195. package/src/verax/observe/observers/console-observer.js +102 -0
  196. package/src/verax/observe/observers/coverage-observer.js +107 -0
  197. package/src/verax/observe/observers/interaction-observer.js +471 -0
  198. package/src/verax/observe/observers/navigation-observer.js +132 -0
  199. package/src/verax/observe/observers/network-observer.js +87 -0
  200. package/src/verax/observe/observers/safety-observer.js +82 -0
  201. package/src/verax/observe/observers/ui-feedback-observer.js +99 -0
  202. package/src/verax/observe/page-traversal.js +138 -0
  203. package/src/verax/observe/snapshot-ops.js +94 -0
  204. package/src/verax/observe/ui-feedback-detector.js +742 -0
  205. package/src/verax/scan-summary-writer.js +2 -0
  206. package/src/verax/shared/artifact-manager.js +25 -5
  207. package/src/verax/shared/caching.js +1 -0
  208. package/src/verax/shared/css-spinner-rules.js +204 -0
  209. package/src/verax/shared/expectation-tracker.js +1 -0
  210. package/src/verax/shared/view-switch-rules.js +208 -0
  211. package/src/verax/shared/zip-artifacts.js +6 -0
  212. package/src/verax/shared/config-loader.js +0 -169
  213. /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
package/README.md CHANGED
@@ -51,11 +51,11 @@ It means the observation produced no verifiable effect for the promise being eva
51
51
 
52
52
  🧠 Extracts expectations from source code using static analysis:
53
53
 
54
- Navigation from HTML links and React Router / Next.js routes
54
+ Navigation from HTML links, React Router, Vue Router, and Next.js routes
55
55
 
56
56
  Network actions from fetch / axios calls with static URLs
57
57
 
58
- State mutations from React useState, Redux dispatch, Zustand set
58
+ State mutations from React useState, Redux, Vuex, Pinia, Zustand set operations
59
59
 
60
60
  🖱️ Observes websites like a real user using Playwright
61
61
  (clicks, forms, navigation, scrolling)
@@ -76,11 +76,15 @@ DOM and state changes
76
76
 
77
77
  🧱 Supports real-world projects:
78
78
 
79
- Static HTML sites
79
+ **Fully verified (production-ready):**
80
+ - Static HTML sites
81
+ - React SPAs (with react-router-dom)
80
82
 
81
- React SPAs
82
-
83
- Next.js (App Router & Pages Router)
83
+ **Supported (learn-only / partial observation):**
84
+ - Next.js (App Router & Pages Router)
85
+ - Vue.js (with Vue Router)
86
+ - Angular
87
+ - SvelteKit
84
88
 
85
89
  🔐 Protects privacy by automatically redacting secrets and sensitive data
86
90
 
package/bin/verax.js CHANGED
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * VERAX CLI Shim
5
- * Delegates to src/cli/entry.js
6
- */
7
-
8
- import('../src/cli/entry.js').catch((error) => {
9
- console.error(`Failed to load CLI: ${error.message}`);
10
- process.exit(2);
11
- });
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * VERAX CLI Shim
5
+ * Delegates to src/cli/entry.js
6
+ */
7
+
8
+ import('../src/cli/entry.js').catch((error) => {
9
+ console.error(`Failed to load CLI: ${error.message}`);
10
+ process.exit(2);
11
+ });
package/package.json CHANGED
@@ -1,12 +1,30 @@
1
1
  {
2
2
  "name": "@veraxhq/verax",
3
- "version": "0.2.1",
4
- "description": "VERAX - Silent failure detection for websites",
3
+ "version": "0.4.0",
4
+ "description": "Public Flow Sanity Guard Trust-Locked, Deterministic, CI-Safe.",
5
+ "keywords": [
6
+ "public-flows",
7
+ "pre-auth",
8
+ "sanity-guard",
9
+ "silent-failures",
10
+ "deterministic",
11
+ "trust-lock",
12
+ "ci-safe",
13
+ "playwright"
14
+ ],
5
15
  "license": "MIT",
6
16
  "type": "module",
7
17
  "bin": {
8
- "verax": "./bin/verax.js"
18
+ "verax": "bin/verax.js"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/odavlstudio/verax.git"
9
23
  },
24
+ "bugs": {
25
+ "url": "https://github.com/odavlstudio/verax/issues"
26
+ },
27
+ "homepage": "https://github.com/odavlstudio/verax#readme",
10
28
  "files": [
11
29
  "bin/",
12
30
  "src/",
@@ -14,27 +32,30 @@
14
32
  "LICENSE"
15
33
  ],
16
34
  "scripts": {
17
- "test": "node scripts/test-runner-wrapper.js",
18
- "test:pack": "node scripts/test-pack.js",
35
+ "test": "node test/infrastructure/test-runner-wrapper.js",
36
+ "test:pack": "node test/infrastructure/test-pack.js",
19
37
  "verify-release": "node scripts/verify-release.js",
20
38
  "lint": "eslint . --max-warnings 0",
21
39
  "typecheck": "tsc -p tsconfig.json --noEmit"
22
40
  },
23
41
  "dependencies": {
42
+ "@babel/parser": "^7.28.5",
43
+ "@babel/traverse": "^7.28.5",
24
44
  "glob": "^10.3.10",
25
45
  "inquirer": "^9.2.15",
26
46
  "node-html-parser": "^7.0.1",
27
47
  "playwright": "^1.40.0",
28
48
  "typescript": "^5.9.3"
29
49
  },
50
+ "optionalDependencies": {
51
+ "pngjs": "^7.0.0"
52
+ },
30
53
  "engines": {
31
54
  "node": ">=18.0.0"
32
55
  },
33
56
  "devDependencies": {
34
- "@babel/parser": "^7.28.5",
35
- "@babel/traverse": "^7.28.5",
36
57
  "@reduxjs/toolkit": "^2.11.2",
37
- "@veraxhq/verax": "file:veraxhq-verax-0.2.1.tgz",
58
+ "@types/node": "^18.0.0",
38
59
  "eslint": "^8.57.0",
39
60
  "next": "^16.1.1",
40
61
  "react": "^19.2.3",
@@ -0,0 +1,103 @@
1
+ /**
2
+ * PHASE 21.11 — Baseline Command
3
+ *
4
+ * `verax baseline` - Shows baseline hash and drift status
5
+ */
6
+
7
+ import { loadBaselineSnapshot, buildBaselineSnapshot } from '../../verax/core/baseline/baseline.snapshot.js';
8
+ import { compareBaselines } from '../../verax/core/baseline/baseline.enforcer.js';
9
+
10
+ /**
11
+ * Baseline command
12
+ *
13
+ * @param {string} projectDir - Project directory
14
+ * @param {Object} options - Command options
15
+ */
16
+ export async function baselineCommand(projectDir, options = {}) {
17
+ const { json = false } = options;
18
+
19
+ const frozen = loadBaselineSnapshot(projectDir);
20
+ const current = buildBaselineSnapshot(projectDir);
21
+
22
+ if (!frozen) {
23
+ if (json) {
24
+ console.log(JSON.stringify({
25
+ status: 'NO_BASELINE',
26
+ message: 'No baseline snapshot found (pre-GA)',
27
+ current: {
28
+ hash: current.baselineHash,
29
+ version: current.veraxVersion,
30
+ commit: current.gitCommit
31
+ }
32
+ }, null, 2));
33
+ } else {
34
+ console.log('\n=== Baseline Status ===\n');
35
+ console.log('Status: NO_BASELINE (pre-GA)');
36
+ console.log(`Current baseline hash: ${current.baselineHash}`);
37
+ console.log(`Version: ${current.veraxVersion}`);
38
+ console.log(`Commit: ${current.gitCommit}`);
39
+ console.log(`Dirty: ${current.gitDirty ? 'YES' : 'NO'}`);
40
+ console.log('\nNote: Baseline will be frozen when GA-READY is achieved.\n');
41
+ }
42
+ return;
43
+ }
44
+
45
+ const comparison = compareBaselines(current, frozen);
46
+ const frozenStatus = frozen.frozen ? 'FROZEN' : 'NOT_FROZEN';
47
+
48
+ if (json) {
49
+ console.log(JSON.stringify({
50
+ status: frozenStatus,
51
+ frozen: frozen.frozen,
52
+ drifted: comparison.drifted,
53
+ message: comparison.message,
54
+ frozenBaseline: {
55
+ hash: frozen.baselineHash,
56
+ version: frozen.veraxVersion,
57
+ commit: frozen.gitCommit,
58
+ timestamp: frozen.timestamp
59
+ },
60
+ currentBaseline: {
61
+ hash: current.baselineHash,
62
+ version: current.veraxVersion,
63
+ commit: current.gitCommit
64
+ },
65
+ differences: comparison.differences
66
+ }, null, 2));
67
+ } else {
68
+ console.log('\n=== Baseline Status ===\n');
69
+ console.log(`Status: ${frozenStatus}`);
70
+ console.log(`Frozen: ${frozen.frozen ? 'YES' : 'NO'}`);
71
+ console.log(`Drifted: ${comparison.drifted ? 'YES' : 'NO'}`);
72
+ console.log(`\nMessage: ${comparison.message}`);
73
+
74
+ console.log('\nFrozen Baseline:');
75
+ console.log(` Hash: ${frozen.baselineHash}`);
76
+ console.log(` Version: ${frozen.veraxVersion}`);
77
+ console.log(` Commit: ${frozen.gitCommit}`);
78
+ console.log(` Timestamp: ${frozen.timestamp}`);
79
+
80
+ console.log('\nCurrent Baseline:');
81
+ console.log(` Hash: ${current.baselineHash}`);
82
+ console.log(` Version: ${current.veraxVersion}`);
83
+ console.log(` Commit: ${current.gitCommit}`);
84
+ console.log(` Dirty: ${current.gitDirty ? 'YES' : 'NO'}`);
85
+
86
+ if (comparison.drifted) {
87
+ console.log('\n⚠️ BASELINE DRIFT DETECTED:');
88
+ for (const diff of comparison.differences) {
89
+ console.log(` - ${diff.message}`);
90
+ if (diff.component) {
91
+ console.log(` Component: ${diff.component}`);
92
+ }
93
+ }
94
+ console.log('\n⚠️ Changes to core contracts/policies after GA require:');
95
+ console.log(' 1. MAJOR version bump');
96
+ console.log(' 2. Baseline regeneration');
97
+ console.log(' 3. GA re-evaluation\n');
98
+ } else {
99
+ console.log('\n✓ Baseline integrity maintained\n');
100
+ }
101
+ }
102
+ }
103
+
@@ -19,6 +19,8 @@ import { detectFindings } from '../util/detection-engine.js';
19
19
  import { writeFindingsJson } from '../util/findings-writer.js';
20
20
  import { writeSummaryJson } from '../util/summary-writer.js';
21
21
  import { computeRuntimeBudget, withTimeout } from '../util/runtime-budget.js';
22
+ import { saveDigest } from '../util/digest-engine.js';
23
+ import { ARTIFACT_REGISTRY, getArtifactVersions } from '../../verax/core/artifacts/registry.js';
22
24
 
23
25
  const __filename = fileURLToPath(import.meta.url);
24
26
  const __dirname = dirname(__filename);
@@ -26,7 +28,8 @@ const __dirname = dirname(__filename);
26
28
  function getVersion() {
27
29
  try {
28
30
  const pkgPath = resolve(__dirname, '../../../package.json');
29
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
31
+ // @ts-expect-error - readFileSync with encoding returns string
32
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
30
33
  return pkg.version;
31
34
  } catch {
32
35
  return '0.2.0';
@@ -38,6 +41,10 @@ function getVersion() {
38
41
  * Interactive mode with intelligent URL detection
39
42
  */
40
43
  export async function defaultCommand(options = {}) {
44
+ // Interactive mode disabled by constitution: require explicit verax run --url
45
+ throw new DataError('Interactive mode is disabled. Use: verax run --url <url>');
46
+
47
+ /* eslint-disable no-unreachable */
41
48
  const {
42
49
  src = '.',
43
50
  out = '.verax',
@@ -93,6 +100,8 @@ export async function defaultCommand(options = {}) {
93
100
  try {
94
101
  const failedAt = new Date().toISOString();
95
102
  atomicWriteJson(paths.runStatusJson, {
103
+ contractVersion: 1,
104
+ artifactVersions: getArtifactVersions(),
96
105
  status: 'FAILED',
97
106
  runId,
98
107
  startedAt,
@@ -101,6 +110,7 @@ export async function defaultCommand(options = {}) {
101
110
  });
102
111
 
103
112
  atomicWriteJson(paths.runMetaJson, {
113
+ contractVersion: ARTIFACT_REGISTRY.runMeta.contractVersion,
104
114
  veraxVersion: getVersion(),
105
115
  nodeVersion: process.version,
106
116
  platform: process.platform,
@@ -259,7 +269,7 @@ export async function defaultCommand(options = {}) {
259
269
  });
260
270
 
261
271
  // Generate run ID
262
- let runId = generateRunId();
272
+ let runId = generateRunId(resolvedUrl);
263
273
  if (verbose && !json) console.log(`Run ID: ${runId}`);
264
274
 
265
275
  let paths = getRunPaths(projectRoot, out, runId);
@@ -275,12 +285,15 @@ export async function defaultCommand(options = {}) {
275
285
  let startedAt = now.toISOString();
276
286
 
277
287
  atomicWriteJson(paths.runStatusJson, {
288
+ contractVersion: 1,
289
+ artifactVersions: getArtifactVersions(),
278
290
  status: 'RUNNING',
279
291
  runId,
280
292
  startedAt,
281
293
  });
282
294
 
283
295
  atomicWriteJson(paths.runMetaJson, {
296
+ contractVersion: ARTIFACT_REGISTRY.runMeta.contractVersion,
284
297
  veraxVersion: getVersion(),
285
298
  nodeVersion: process.version,
286
299
  platform: process.platform,
@@ -398,7 +411,8 @@ export async function defaultCommand(options = {}) {
398
411
  const status = progress.observed ? '✓' : '✗';
399
412
  console.log(` ${status} ${progress.index}/${expectations.length}`);
400
413
  }
401
- }
414
+ },
415
+ {}
402
416
  ),
403
417
  'Observe'
404
418
  );
@@ -542,9 +556,12 @@ export async function defaultCommand(options = {}) {
542
556
  runId,
543
557
  startedAt,
544
558
  completedAt,
559
+ contractVersion: 1,
560
+ artifactVersions: getArtifactVersions(),
545
561
  });
546
562
 
547
563
  atomicWriteJson(paths.runMetaJson, {
564
+ contractVersion: ARTIFACT_REGISTRY.runMeta.contractVersion,
548
565
  veraxVersion: getVersion(),
549
566
  nodeVersion: process.version,
550
567
  platform: process.platform,
@@ -596,6 +613,11 @@ export async function defaultCommand(options = {}) {
596
613
  // Write observe results
597
614
  writeObserveJson(paths.baseDir, observeData);
598
615
 
616
+ // H5: Write deterministic digest for reproducibility proof
617
+ if (observeData && observeData.digest) {
618
+ saveDigest(resolve(paths.baseDir, 'run.digest.json'), observeData.digest);
619
+ }
620
+
599
621
  events.emit('phase:completed', {
600
622
  phase: 'Finalize Artifacts',
601
623
  message: 'Run artifacts written',
@@ -603,9 +625,28 @@ export async function defaultCommand(options = {}) {
603
625
 
604
626
  // Print summary if not JSON mode
605
627
  if (!json) {
606
- console.log('\nRun complete.');
607
- console.log(`Run ID: ${runId}`);
608
- console.log(`Artifacts: ${paths.baseDir}`);
628
+ const relativePath = paths.baseDir.replace(/\\/g, '/').split('/').slice(-1)[0];
629
+ console.log('');
630
+ console.log('VERAX — Silent Failure Detection');
631
+ console.log('');
632
+ console.log(`✔ Project detected: ${projectProfile.framework}`);
633
+ console.log(`✔ URL resolved: ${resolvedUrl}`);
634
+ console.log('');
635
+ console.log('Learn phase:');
636
+ console.log(` → Extracted ${expectations.length} promises`);
637
+ console.log('');
638
+ console.log('Observe phase:');
639
+ console.log(` → Executed ${observeData.stats?.attempted || 0} interactions`);
640
+ console.log(` → Observed: ${observeData.stats?.observed || 0}/${observeData.stats?.attempted || 0}`);
641
+ console.log('');
642
+ console.log('Detect phase:');
643
+ console.log(` → Silent failures: ${detectData.stats?.silentFailures || 0}`);
644
+ console.log(` → Unproven: ${detectData.stats?.unproven || 0}`);
645
+ console.log(` → Coverage gaps: ${detectData.stats?.coverageGaps || 0}`);
646
+ console.log('');
647
+ console.log('Artifacts written to:');
648
+ console.log(` .verax/runs/${relativePath}/`);
649
+ console.log('');
609
650
  }
610
651
 
611
652
  return { runId, paths, url: resolvedUrl, success: true };
@@ -628,10 +669,13 @@ export async function defaultCommand(options = {}) {
628
669
  startedAt,
629
670
  failedAt,
630
671
  error: error.message,
672
+ contractVersion: 1,
673
+ artifactVersions: getArtifactVersions(),
631
674
  });
632
675
 
633
676
  // Update metadata
634
677
  atomicWriteJson(paths.runMetaJson, {
678
+ contractVersion: ARTIFACT_REGISTRY.runMeta.contractVersion,
635
679
  veraxVersion: getVersion(),
636
680
  nodeVersion: process.version,
637
681
  platform: process.platform,
@@ -678,4 +722,5 @@ export async function defaultCommand(options = {}) {
678
722
  });
679
723
  throw error;
680
724
  }
725
+ /* eslint-enable no-unreachable */
681
726
  }
@@ -32,6 +32,35 @@ export async function doctorCommand(options = {}) {
32
32
  }
33
33
  };
34
34
 
35
+ // Test-mode / smoke fast path: skip heavyweight checks to keep runtime under tight budgets
36
+ if (process.env.VERAX_TEST_MODE === '1' || process.env.VERAX_DOCTOR_SMOKE_TIMEOUT_MS) {
37
+ addCheck('Test mode', 'pass', 'Diagnostics skipped in test/smoke mode');
38
+ addCheck('Node.js version', 'pass', `Detected v${nodeVersion}`);
39
+ addCheck('Playwright package', 'pass', 'Skipped (smoke mode)');
40
+ addCheck('Headless smoke test', 'pass', 'Skipped (smoke mode)');
41
+ const ok = true;
42
+ if (json) {
43
+ const report = {
44
+ ok,
45
+ platform: platformName,
46
+ nodeVersion,
47
+ playwrightVersion: null,
48
+ checks,
49
+ recommendations,
50
+ };
51
+ console.log(JSON.stringify(report, null, 2));
52
+ return report;
53
+ }
54
+
55
+ console.log('VERAX Doctor — Environment Diagnostics (test mode)');
56
+ checks.forEach((c) => {
57
+ const label = c.status === 'pass' ? 'PASS' : 'FAIL';
58
+ console.log(`[${label}] ${c.name}: ${c.details}`);
59
+ });
60
+ console.log('\nOverall: OK (test mode)');
61
+ return { ok, checks, recommendations };
62
+ }
63
+
35
64
  // 1) Node.js version
36
65
  const nodeMajor = parseInt(nodeVersion.split('.')[0], 10);
37
66
  if (Number.isFinite(nodeMajor) && nodeMajor >= 18) {
@@ -0,0 +1,246 @@
1
+ /**
2
+ * PHASE 21.6 — GA Readiness CLI Command
3
+ *
4
+ * Pure inspection command. Evaluates GA readiness using existing artifacts only.
5
+ * No URL, no browser, no project execution.
6
+ */
7
+
8
+ import { evaluateGAReadiness } from '../../verax/core/ga/ga.contract.js';
9
+ import { writeGAStatus } from '../../verax/core/ga/ga.artifact.js';
10
+ import { writeGAReport } from '../../verax/core/ga/ga-report-writer.js';
11
+ import { GA_BLOCKER_CODE } from '../../verax/core/ga/ga.contract.js';
12
+ import { resolve } from 'path';
13
+ import { readFileSync, existsSync } from 'fs';
14
+ import { findLatestRunId, validateRunId } from '../util/run-resolver.js';
15
+ import { UsageError } from '../util/errors.js';
16
+
17
+ /**
18
+ * Load failure ledger summary
19
+ *
20
+ * @param {string} projectDir - Project directory
21
+ * @param {string} runId - Run ID
22
+ * @returns {Object|null} Failure ledger summary or null
23
+ */
24
+ function loadFailureLedger(projectDir, runId) {
25
+ const ledgerPath = resolve(projectDir, '.verax', 'runs', runId, 'failure.ledger.json');
26
+ if (!existsSync(ledgerPath)) {
27
+ return null;
28
+ }
29
+
30
+ try {
31
+ const content = readFileSync(ledgerPath, 'utf-8');
32
+ // @ts-expect-error - readFileSync with encoding returns string
33
+ const ledger = JSON.parse(content);
34
+ return ledger.summary || null;
35
+ } catch (error) {
36
+ return null;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Load determinism verdict
42
+ *
43
+ * @param {string} projectDir - Project directory
44
+ * @param {string} runId - Run ID
45
+ * @returns {Promise<string|null>} Determinism verdict or null
46
+ */
47
+ async function loadDeterminismVerdict(projectDir, runId) {
48
+ const decisionsPath = resolve(projectDir, '.verax', 'runs', runId, 'decisions.json');
49
+ if (!existsSync(decisionsPath)) {
50
+ return null;
51
+ }
52
+
53
+ try {
54
+ // @ts-expect-error - readFileSync with encoding returns string
55
+ const decisions = JSON.parse(readFileSync(decisionsPath, 'utf-8'));
56
+ const { DecisionRecorder } = await import('../../verax/core/determinism-model.js');
57
+ const recorder = DecisionRecorder.fromExport(decisions);
58
+ const { computeDeterminismVerdict } = await import('../../verax/core/determinism/contract.js');
59
+ const verdict = computeDeterminismVerdict(recorder);
60
+ return verdict.verdict;
61
+ } catch (error) {
62
+ return null;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Check for Evidence Law violations
68
+ *
69
+ * @param {string} projectDir - Project directory
70
+ * @param {string} runId - Run ID
71
+ * @returns {boolean} Whether Evidence Law was violated
72
+ */
73
+ function checkEvidenceLawViolations(projectDir, runId) {
74
+ const findingsPath = resolve(projectDir, '.verax', 'runs', runId, 'findings.json');
75
+ if (!existsSync(findingsPath)) {
76
+ return false;
77
+ }
78
+
79
+ try {
80
+ const content = readFileSync(findingsPath, 'utf-8');
81
+ // @ts-expect-error - readFileSync with encoding returns string
82
+ const findings = JSON.parse(content);
83
+
84
+ if (!Array.isArray(findings.findings)) {
85
+ return false;
86
+ }
87
+
88
+ // Check for CONFIRMED findings with incomplete evidence
89
+ for (const finding of findings.findings) {
90
+ if ((finding.severity === 'CONFIRMED' || finding.status === 'CONFIRMED') &&
91
+ finding.evidencePackage && !finding.evidencePackage.isComplete) {
92
+ return true;
93
+ }
94
+ }
95
+
96
+ return false;
97
+ } catch (error) {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * PHASE 21.6.1: `verax ga` command
104
+ *
105
+ * Pure inspection command. No URL, no browser, no execution.
106
+ *
107
+ * @param {Object} options - Options
108
+ * @param {string} [options.runId] - Run ID (defaults to latest)
109
+ * @param {boolean} [options.json] - Output as JSON
110
+ */
111
+ export async function gaCommand(options = {}) {
112
+ const { runId: providedRunId = null, json = false } = options;
113
+
114
+ const projectDir = resolve(process.cwd());
115
+
116
+ // Resolve run ID: use provided or find latest
117
+ let runId = providedRunId;
118
+
119
+ if (!runId) {
120
+ // Find latest run
121
+ runId = findLatestRunId(projectDir);
122
+
123
+ if (!runId) {
124
+ // No runs found - GA is BLOCKED
125
+ const gaResult = {
126
+ pass: false,
127
+ blockers: [{
128
+ code: GA_BLOCKER_CODE.NO_RUNS_FOUND || 'GA_NO_RUNS_FOUND',
129
+ message: 'No runs found in .verax/runs/. Run a scan first.',
130
+ context: {}
131
+ }],
132
+ warnings: [],
133
+ summary: {
134
+ pass: false,
135
+ blockersCount: 1,
136
+ warningsCount: 0,
137
+ checkedAt: new Date().toISOString()
138
+ },
139
+ inputs: {
140
+ gates: null,
141
+ determinism: null,
142
+ evidenceLaw: null,
143
+ failureLedger: null
144
+ }
145
+ };
146
+
147
+ if (json) {
148
+ console.log(JSON.stringify({
149
+ gaReady: false,
150
+ blockers: gaResult.blockers,
151
+ warnings: [],
152
+ summary: gaResult.summary
153
+ }, null, 2));
154
+ } else {
155
+ console.log('\n' + '='.repeat(80));
156
+ console.log('GA READINESS EVALUATION');
157
+ console.log('='.repeat(80));
158
+ console.log('\nGA STATUS: ❌ BLOCKED');
159
+ console.log('\nBlockers:');
160
+ console.log('- No runs found in .verax/runs/. Run a scan first.');
161
+ console.log('='.repeat(80) + '\n');
162
+ }
163
+
164
+ process.exit(4);
165
+ return;
166
+ }
167
+ } else {
168
+ // Validate provided run ID
169
+ if (!validateRunId(projectDir, runId)) {
170
+ const error = new UsageError(`Run ID not found: ${runId}`);
171
+ // UsageError already has exit code 64
172
+ throw error;
173
+ }
174
+ }
175
+
176
+ // Load context from artifacts (pure filesystem reads)
177
+ const failureLedger = loadFailureLedger(projectDir, runId);
178
+ const determinismVerdict = await loadDeterminismVerdict(projectDir, runId);
179
+ const evidenceLawViolated = checkEvidenceLawViolations(projectDir, runId);
180
+
181
+ // Evaluate GA readiness
182
+ const gaResult = await evaluateGAReadiness({
183
+ projectDir,
184
+ runId,
185
+ determinismVerdict,
186
+ evidenceLawViolated,
187
+ failureLedger
188
+ });
189
+
190
+ // Write status artifact
191
+ const artifactPath = writeGAStatus(projectDir, runId, gaResult);
192
+
193
+ // Write GA report
194
+ const reportPath = writeGAReport(projectDir, runId, gaResult);
195
+
196
+ // Output
197
+ if (json) {
198
+ console.log(JSON.stringify({
199
+ gaReady: gaResult.pass,
200
+ blockers: gaResult.blockers,
201
+ warnings: gaResult.warnings,
202
+ summary: gaResult.summary,
203
+ artifactPath,
204
+ reportPath
205
+ }, null, 2));
206
+ } else {
207
+ console.log('\n' + '='.repeat(80));
208
+ console.log('GA READINESS EVALUATION');
209
+ console.log('='.repeat(80));
210
+ console.log(`\nGA STATUS: ${gaResult.pass ? '✅ READY' : '❌ BLOCKED'}`);
211
+
212
+ if (gaResult.blockers.length > 0) {
213
+ console.log('\nBlockers:');
214
+ for (const blocker of gaResult.blockers) {
215
+ console.log(`- ${blocker.message}`);
216
+ }
217
+ }
218
+
219
+ if (gaResult.warnings.length > 0) {
220
+ console.log('\nWarnings:');
221
+ for (const warning of gaResult.warnings) {
222
+ console.log(`- ${warning.message}`);
223
+ }
224
+ }
225
+
226
+ console.log(`\nSee: ${artifactPath}`);
227
+ console.log(`Report: ${reportPath}`);
228
+ console.log('='.repeat(80) + '\n');
229
+ }
230
+
231
+ // Exit codes: 0 = GA-READY, 2 = GA-BLOCKED, 70 = Internal corruption
232
+ if (!gaResult.pass) {
233
+ // Check if it's an internal corruption issue
234
+ const hasInternalBlocker = gaResult.blockers.some(b =>
235
+ b.code === 'GA_INTERNAL_FAILURES' ||
236
+ b.code === 'GA_CONTRACT_FAILURES'
237
+ );
238
+
239
+ if (hasInternalBlocker) {
240
+ process.exit(70);
241
+ } else {
242
+ process.exit(2);
243
+ }
244
+ }
245
+ }
246
+