@veraxhq/verax 0.3.0 → 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 (191) hide show
  1. package/README.md +28 -20
  2. package/bin/verax.js +11 -18
  3. package/package.json +28 -7
  4. package/src/cli/commands/baseline.js +1 -2
  5. package/src/cli/commands/default.js +72 -81
  6. package/src/cli/commands/doctor.js +29 -0
  7. package/src/cli/commands/ga.js +3 -0
  8. package/src/cli/commands/gates.js +1 -1
  9. package/src/cli/commands/inspect.js +6 -133
  10. package/src/cli/commands/release-check.js +2 -0
  11. package/src/cli/commands/run.js +74 -246
  12. package/src/cli/commands/security-check.js +2 -1
  13. package/src/cli/commands/truth.js +0 -1
  14. package/src/cli/entry.js +82 -309
  15. package/src/cli/util/angular-component-extractor.js +2 -2
  16. package/src/cli/util/angular-navigation-detector.js +2 -2
  17. package/src/cli/util/ast-interactive-detector.js +4 -6
  18. package/src/cli/util/ast-network-detector.js +3 -3
  19. package/src/cli/util/ast-promise-extractor.js +581 -0
  20. package/src/cli/util/ast-usestate-detector.js +3 -3
  21. package/src/cli/util/atomic-write.js +12 -1
  22. package/src/cli/util/console-reporter.js +72 -0
  23. package/src/cli/util/detection-engine.js +105 -41
  24. package/src/cli/util/determinism-runner.js +2 -1
  25. package/src/cli/util/determinism-writer.js +1 -1
  26. package/src/cli/util/digest-engine.js +359 -0
  27. package/src/cli/util/dom-diff.js +226 -0
  28. package/src/cli/util/env-url.js +0 -4
  29. package/src/cli/util/evidence-engine.js +287 -0
  30. package/src/cli/util/expectation-extractor.js +217 -367
  31. package/src/cli/util/findings-writer.js +19 -126
  32. package/src/cli/util/framework-detector.js +572 -0
  33. package/src/cli/util/idgen.js +1 -1
  34. package/src/cli/util/interaction-planner.js +529 -0
  35. package/src/cli/util/learn-writer.js +2 -2
  36. package/src/cli/util/ledger-writer.js +110 -0
  37. package/src/cli/util/monorepo-resolver.js +162 -0
  38. package/src/cli/util/observation-engine.js +127 -278
  39. package/src/cli/util/observe-writer.js +2 -2
  40. package/src/cli/util/paths.js +12 -3
  41. package/src/cli/util/project-discovery.js +284 -3
  42. package/src/cli/util/project-writer.js +2 -2
  43. package/src/cli/util/run-id.js +23 -27
  44. package/src/cli/util/run-result.js +778 -0
  45. package/src/cli/util/selector-resolver.js +235 -0
  46. package/src/cli/util/summary-writer.js +2 -1
  47. package/src/cli/util/svelte-navigation-detector.js +3 -3
  48. package/src/cli/util/svelte-sfc-extractor.js +0 -1
  49. package/src/cli/util/svelte-state-detector.js +1 -2
  50. package/src/cli/util/trust-activation-integration.js +496 -0
  51. package/src/cli/util/trust-activation-wrapper.js +85 -0
  52. package/src/cli/util/trust-integration-hooks.js +164 -0
  53. package/src/cli/util/types.js +153 -0
  54. package/src/cli/util/url-validation.js +40 -0
  55. package/src/cli/util/vue-navigation-detector.js +4 -3
  56. package/src/cli/util/vue-sfc-extractor.js +1 -2
  57. package/src/cli/util/vue-state-detector.js +1 -1
  58. package/src/types/fs-augment.d.ts +23 -0
  59. package/src/types/global.d.ts +137 -0
  60. package/src/types/internal-types.d.ts +35 -0
  61. package/src/verax/cli/finding-explainer.js +3 -56
  62. package/src/verax/cli/init.js +4 -18
  63. package/src/verax/core/action-classifier.js +4 -3
  64. package/src/verax/core/artifacts/registry.js +0 -15
  65. package/src/verax/core/artifacts/verifier.js +18 -8
  66. package/src/verax/core/baseline/baseline.snapshot.js +2 -0
  67. package/src/verax/core/capabilities/gates.js +7 -1
  68. package/src/verax/core/confidence/confidence-compute.js +14 -7
  69. package/src/verax/core/confidence/confidence.loader.js +1 -0
  70. package/src/verax/core/confidence-engine-refactor.js +8 -3
  71. package/src/verax/core/confidence-engine.js +162 -23
  72. package/src/verax/core/contracts/types.js +1 -0
  73. package/src/verax/core/contracts/validators.js +79 -4
  74. package/src/verax/core/decision-snapshot.js +3 -30
  75. package/src/verax/core/decisions/decision.trace.js +2 -0
  76. package/src/verax/core/determinism/contract-writer.js +2 -2
  77. package/src/verax/core/determinism/contract.js +1 -1
  78. package/src/verax/core/determinism/diff.js +42 -1
  79. package/src/verax/core/determinism/engine.js +7 -6
  80. package/src/verax/core/determinism/finding-identity.js +3 -2
  81. package/src/verax/core/determinism/normalize.js +32 -4
  82. package/src/verax/core/determinism/report-writer.js +1 -0
  83. package/src/verax/core/determinism/run-fingerprint.js +7 -2
  84. package/src/verax/core/dynamic-route-intelligence.js +8 -7
  85. package/src/verax/core/evidence/evidence-capture-service.js +1 -0
  86. package/src/verax/core/evidence/evidence-intent-ledger.js +2 -1
  87. package/src/verax/core/evidence-builder.js +2 -2
  88. package/src/verax/core/execution-mode-context.js +1 -1
  89. package/src/verax/core/execution-mode-detector.js +5 -3
  90. package/src/verax/core/failures/exit-codes.js +39 -37
  91. package/src/verax/core/failures/failure-summary.js +1 -1
  92. package/src/verax/core/failures/failure.factory.js +3 -3
  93. package/src/verax/core/failures/failure.ledger.js +3 -2
  94. package/src/verax/core/ga/ga.artifact.js +1 -1
  95. package/src/verax/core/ga/ga.contract.js +3 -2
  96. package/src/verax/core/ga/ga.enforcer.js +1 -0
  97. package/src/verax/core/guardrails/policy.loader.js +1 -0
  98. package/src/verax/core/guardrails/truth-reconciliation.js +1 -1
  99. package/src/verax/core/guardrails-engine.js +2 -2
  100. package/src/verax/core/incremental-store.js +1 -0
  101. package/src/verax/core/integrity/budget.js +138 -0
  102. package/src/verax/core/integrity/determinism.js +342 -0
  103. package/src/verax/core/integrity/integrity.js +208 -0
  104. package/src/verax/core/integrity/poisoning.js +108 -0
  105. package/src/verax/core/integrity/transaction.js +140 -0
  106. package/src/verax/core/observe/run-timeline.js +2 -0
  107. package/src/verax/core/perf/perf.report.js +2 -0
  108. package/src/verax/core/pipeline-tracker.js +5 -0
  109. package/src/verax/core/release/provenance.builder.js +73 -214
  110. package/src/verax/core/release/release.enforcer.js +14 -9
  111. package/src/verax/core/release/reproducibility.check.js +1 -0
  112. package/src/verax/core/release/sbom.builder.js +32 -23
  113. package/src/verax/core/replay-validator.js +2 -0
  114. package/src/verax/core/replay.js +4 -0
  115. package/src/verax/core/report/cross-index.js +6 -3
  116. package/src/verax/core/report/human-summary.js +141 -1
  117. package/src/verax/core/route-intelligence.js +4 -3
  118. package/src/verax/core/run-id.js +6 -3
  119. package/src/verax/core/run-manifest.js +4 -3
  120. package/src/verax/core/security/secrets.scan.js +10 -7
  121. package/src/verax/core/security/security.enforcer.js +4 -0
  122. package/src/verax/core/security/supplychain.policy.js +9 -1
  123. package/src/verax/core/security/vuln.scan.js +2 -2
  124. package/src/verax/core/truth/truth.certificate.js +3 -1
  125. package/src/verax/core/ui-feedback-intelligence.js +12 -46
  126. package/src/verax/detect/conditional-ui-silent-failure.js +84 -0
  127. package/src/verax/detect/confidence-engine.js +100 -660
  128. package/src/verax/detect/confidence-helper.js +1 -0
  129. package/src/verax/detect/detection-engine.js +1 -18
  130. package/src/verax/detect/dynamic-route-findings.js +17 -14
  131. package/src/verax/detect/expectation-chain-detector.js +1 -1
  132. package/src/verax/detect/expectation-model.js +3 -5
  133. package/src/verax/detect/failure-cause-inference.js +293 -0
  134. package/src/verax/detect/findings-writer.js +126 -166
  135. package/src/verax/detect/flow-detector.js +2 -2
  136. package/src/verax/detect/form-silent-failure.js +98 -0
  137. package/src/verax/detect/index.js +51 -234
  138. package/src/verax/detect/invariants-enforcer.js +147 -0
  139. package/src/verax/detect/journey-stall-detector.js +4 -4
  140. package/src/verax/detect/navigation-silent-failure.js +82 -0
  141. package/src/verax/detect/problem-aggregator.js +361 -0
  142. package/src/verax/detect/route-findings.js +7 -6
  143. package/src/verax/detect/summary-writer.js +477 -0
  144. package/src/verax/detect/test-failure-cause-inference.js +314 -0
  145. package/src/verax/detect/ui-feedback-findings.js +18 -18
  146. package/src/verax/detect/verdict-engine.js +3 -57
  147. package/src/verax/detect/view-switch-correlator.js +2 -2
  148. package/src/verax/flow/flow-engine.js +2 -1
  149. package/src/verax/flow/flow-spec.js +0 -6
  150. package/src/verax/index.js +48 -412
  151. package/src/verax/intel/ts-program.js +1 -0
  152. package/src/verax/intel/vue-navigation-extractor.js +3 -0
  153. package/src/verax/learn/action-contract-extractor.js +67 -682
  154. package/src/verax/learn/ast-contract-extractor.js +1 -1
  155. package/src/verax/learn/flow-extractor.js +1 -0
  156. package/src/verax/learn/project-detector.js +5 -0
  157. package/src/verax/learn/react-router-extractor.js +2 -0
  158. package/src/verax/learn/route-validator.js +1 -4
  159. package/src/verax/learn/source-instrumenter.js +1 -0
  160. package/src/verax/learn/state-extractor.js +2 -1
  161. package/src/verax/learn/static-extractor.js +1 -0
  162. package/src/verax/observe/coverage-gaps.js +132 -0
  163. package/src/verax/observe/expectation-handler.js +126 -0
  164. package/src/verax/observe/incremental-skip.js +46 -0
  165. package/src/verax/observe/index.js +735 -84
  166. package/src/verax/observe/interaction-executor.js +192 -0
  167. package/src/verax/observe/interaction-runner.js +782 -530
  168. package/src/verax/observe/network-firewall.js +86 -0
  169. package/src/verax/observe/observation-builder.js +169 -0
  170. package/src/verax/observe/observe-context.js +1 -1
  171. package/src/verax/observe/observe-helpers.js +2 -1
  172. package/src/verax/observe/observe-runner.js +28 -24
  173. package/src/verax/observe/observers/budget-observer.js +3 -3
  174. package/src/verax/observe/observers/console-observer.js +4 -4
  175. package/src/verax/observe/observers/coverage-observer.js +4 -4
  176. package/src/verax/observe/observers/interaction-observer.js +3 -3
  177. package/src/verax/observe/observers/navigation-observer.js +4 -4
  178. package/src/verax/observe/observers/network-observer.js +4 -4
  179. package/src/verax/observe/observers/safety-observer.js +1 -1
  180. package/src/verax/observe/observers/ui-feedback-observer.js +4 -4
  181. package/src/verax/observe/page-traversal.js +138 -0
  182. package/src/verax/observe/snapshot-ops.js +94 -0
  183. package/src/verax/observe/ui-signal-sensor.js +2 -148
  184. package/src/verax/scan-summary-writer.js +10 -42
  185. package/src/verax/shared/artifact-manager.js +30 -13
  186. package/src/verax/shared/caching.js +1 -0
  187. package/src/verax/shared/expectation-tracker.js +1 -0
  188. package/src/verax/shared/zip-artifacts.js +6 -0
  189. package/src/verax/core/confidence-engine.js.backup +0 -471
  190. package/src/verax/shared/config-loader.js +0 -169
  191. /package/src/verax/shared/{expectation-proof.js → expectation-validation.js} +0 -0
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test: Failure Cause Inference (FCI)
5
+ *
6
+ * Tests:
7
+ * 1. No evidence -> no causes (Evidence Law enforcement)
8
+ * 2. Determinism: same input -> same causes, same ordering, same wording
9
+ * 3. Cause inference for C2 (state mutation no UI)
10
+ * 4. Cause inference for C3 (dead click)
11
+ * 5. Cause inference for C7 (network silent)
12
+ */
13
+
14
+ import {
15
+ inferCauses,
16
+ inferCausesForFindings,
17
+ findingsWithCauses,
18
+ attachCausesToFinding
19
+ } from './failure-cause-inference.js';
20
+
21
+ let testCount = 0;
22
+ let passCount = 0;
23
+
24
+ function test(name, fn) {
25
+ testCount++;
26
+ try {
27
+ fn();
28
+ console.log(`✓ Test ${testCount}: ${name}`);
29
+ passCount++;
30
+ } catch (e) {
31
+ console.error(`✗ Test ${testCount}: ${name}`);
32
+ console.error(` ${e.message}`);
33
+ }
34
+ }
35
+
36
+ function assert(condition, message) {
37
+ if (!condition) {
38
+ throw new Error(message);
39
+ }
40
+ }
41
+
42
+ // Test 1: No evidence -> no causes
43
+ test('No evidence => no causes (Evidence Law)', () => {
44
+ const finding = {
45
+ id: 'test-1',
46
+ type: 'silent_failure',
47
+ evidence: {}
48
+ };
49
+ const causes = inferCauses(finding);
50
+ assert(Array.isArray(causes), 'Should return array');
51
+ assert(causes.length === 0, `Expected 0 causes, got ${causes.length}`);
52
+ });
53
+
54
+ // Test 2: Null/undefined finding
55
+ test('Null finding => no causes', () => {
56
+ const causes = inferCauses(null);
57
+ assert(causes.length === 0, 'Should return empty array for null');
58
+ });
59
+
60
+ // Test 3: Missing evidence field
61
+ test('Missing evidence field => no causes', () => {
62
+ const finding = {
63
+ id: 'test-2',
64
+ type: 'silent_failure'
65
+ };
66
+ const causes = inferCauses(finding);
67
+ assert(causes.length === 0, 'Should return empty array');
68
+ });
69
+
70
+ // Test 4: Determinism - run twice, get identical output
71
+ test('Determinism: same finding => same causes', () => {
72
+ const finding = {
73
+ id: 'test-det-1',
74
+ type: 'silent_failure',
75
+ evidence: {
76
+ stateMutation: true,
77
+ domChanged: false,
78
+ navigationOccurred: false,
79
+ uiFeedback: false
80
+ }
81
+ };
82
+ const run1 = inferCauses(finding);
83
+ const run2 = inferCauses(finding);
84
+
85
+ assert(JSON.stringify(run1) === JSON.stringify(run2), 'Runs should be identical');
86
+ assert(run1.length > 0, 'Should detect C2 cause');
87
+ assert(run1[0].id === 'C2_STATE_MUTATION_NO_UI', 'Should detect C2');
88
+ });
89
+
90
+ // Test 5: Determinism - ordering
91
+ test('Determinism: causes ordered by id', () => {
92
+ const finding = {
93
+ id: 'test-ord-1',
94
+ type: 'silent_failure',
95
+ evidence: {
96
+ stateMutation: true,
97
+ domChanged: false,
98
+ navigationOccurred: false,
99
+ uiFeedback: false,
100
+ interactionPerformed: true,
101
+ networkActivity: false,
102
+ userFeedback: false
103
+ }
104
+ };
105
+ const causes = inferCauses(finding);
106
+ // Should have both C2 and C3
107
+ assert(causes.length >= 1, 'Should detect at least 1 cause');
108
+
109
+ // Check ordering
110
+ for (let i = 1; i < causes.length; i++) {
111
+ assert(
112
+ causes[i].id.localeCompare(causes[i-1].id) > 0,
113
+ `Causes not ordered: ${causes[i].id} should come after ${causes[i-1].id}`
114
+ );
115
+ }
116
+ });
117
+
118
+ // Test 6: C2 inference - state mutation no UI
119
+ test('C2: State mutation + no DOM change + no feedback', () => {
120
+ const finding = {
121
+ id: 'c2-test',
122
+ type: 'state_action',
123
+ evidence: {
124
+ stateMutation: true,
125
+ domChanged: false,
126
+ navigationOccurred: false,
127
+ uiFeedback: false
128
+ }
129
+ };
130
+ const causes = inferCauses(finding);
131
+ assert(causes.length >= 1, 'Should find at least one cause');
132
+ assert(causes.some(c => c.id === 'C2_STATE_MUTATION_NO_UI'), 'Should find C2');
133
+
134
+ const c2 = causes.find(c => c.id === 'C2_STATE_MUTATION_NO_UI');
135
+ assert(c2.statement.includes('Likely cause:'), 'Should start with "Likely cause:"');
136
+ assert(c2.confidence === 'MEDIUM', 'Should be MEDIUM confidence');
137
+ assert(Array.isArray(c2.evidence_refs), 'Should have evidence_refs array');
138
+ assert(c2.evidence_refs.length > 0, 'Should have evidence references');
139
+ });
140
+
141
+ // Test 7: C3 inference - dead click
142
+ test('C3: Interaction but no network/nav/DOM/feedback', () => {
143
+ const finding = {
144
+ id: 'c3-test',
145
+ type: 'silent_failure',
146
+ evidence: {
147
+ interactionPerformed: true,
148
+ networkActivity: false,
149
+ navigationOccurred: false,
150
+ domChanged: false,
151
+ userFeedback: false
152
+ }
153
+ };
154
+ const causes = inferCauses(finding);
155
+ assert(causes.some(c => c.id === 'C3_DEAD_CLICK'), 'Should find C3');
156
+
157
+ const c3 = causes.find(c => c.id === 'C3_DEAD_CLICK');
158
+ assert(c3.title.includes('dead'), 'Title should mention dead');
159
+ assert(c3.confidence === 'MEDIUM', 'Should be MEDIUM confidence');
160
+ });
161
+
162
+ // Test 8: C7 inference - network silent
163
+ test('C7: Network failure + no feedback', () => {
164
+ const finding = {
165
+ id: 'c7-test',
166
+ type: 'network_silent_failure',
167
+ evidence: {
168
+ networkFailure: true,
169
+ uiFeedback: false,
170
+ domChanged: false
171
+ }
172
+ };
173
+ const causes = inferCauses(finding);
174
+ assert(causes.some(c => c.id === 'C7_NETWORK_SILENT'), 'Should find C7');
175
+
176
+ const c7 = causes.find(c => c.id === 'C7_NETWORK_SILENT');
177
+ assert(c7.statement.includes('network'), 'Should mention network');
178
+ assert(c7.confidence === 'MEDIUM', 'Should be MEDIUM confidence');
179
+ });
180
+
181
+ // Test 9: Batch inference
182
+ test('Batch inference on multiple findings', () => {
183
+ const findings = [
184
+ {
185
+ id: 'batch-1',
186
+ type: 'silent_failure',
187
+ evidence: { }
188
+ },
189
+ {
190
+ id: 'batch-2',
191
+ type: 'state_action',
192
+ evidence: {
193
+ stateMutation: true,
194
+ domChanged: false,
195
+ navigationOccurred: false,
196
+ uiFeedback: false
197
+ }
198
+ }
199
+ ];
200
+ const causesMap = inferCausesForFindings(findings);
201
+ assert('batch-1' in causesMap === false, 'batch-1 has no evidence, should not be in map');
202
+ assert('batch-2' in causesMap, 'batch-2 should be in map');
203
+ assert(causesMap['batch-2'].some(c => c.id === 'C2_STATE_MUTATION_NO_UI'), 'batch-2 should have C2');
204
+ });
205
+
206
+ // Test 10: findingsWithCauses filter
207
+ test('findingsWithCauses filters correctly', () => {
208
+ const findings = [
209
+ {
210
+ id: 'with-evidence',
211
+ type: 'state_action',
212
+ evidence: {
213
+ stateMutation: true,
214
+ domChanged: false,
215
+ navigationOccurred: false,
216
+ uiFeedback: false
217
+ }
218
+ },
219
+ {
220
+ id: 'no-evidence',
221
+ type: 'silent_failure',
222
+ evidence: { }
223
+ }
224
+ ];
225
+ const filtered = findingsWithCauses(findings);
226
+ assert(filtered.length === 1, `Expected 1 finding with causes, got ${filtered.length}`);
227
+ assert(filtered[0].id === 'with-evidence', 'Should only include finding with causes');
228
+ });
229
+
230
+ // Test 11: attachCausesToFinding mutation
231
+ test('attachCausesToFinding mutates finding', () => {
232
+ const finding = {
233
+ id: 'mutate-test',
234
+ type: 'state_action',
235
+ evidence: {
236
+ stateMutation: true,
237
+ domChanged: false,
238
+ navigationOccurred: false,
239
+ uiFeedback: false
240
+ }
241
+ };
242
+ assert(!('causes' in finding), 'Should not have causes before');
243
+ attachCausesToFinding(finding);
244
+ assert('causes' in finding, 'Should have causes after');
245
+ assert(finding.causes.length > 0, 'Should have non-empty causes array');
246
+ });
247
+
248
+ // Test 12: Cause statement format
249
+ test('Cause statements start with "Likely cause:"', () => {
250
+ const finding = {
251
+ id: 'format-test',
252
+ type: 'state_action',
253
+ evidence: {
254
+ stateMutation: true,
255
+ domChanged: false,
256
+ navigationOccurred: false,
257
+ uiFeedback: false
258
+ }
259
+ };
260
+ const causes = inferCauses(finding);
261
+ causes.forEach(cause => {
262
+ assert(
263
+ cause.statement.startsWith('Likely cause:'),
264
+ `Statement should start with "Likely cause:", got: ${cause.statement}`
265
+ );
266
+ });
267
+ });
268
+
269
+ // Test 13: Confidence never HIGH
270
+ test('Confidence is never HIGH', () => {
271
+ const findings = [
272
+ {
273
+ id: 't1',
274
+ type: 'state_action',
275
+ evidence: {
276
+ stateMutation: true,
277
+ domChanged: false,
278
+ navigationOccurred: false,
279
+ uiFeedback: false
280
+ }
281
+ },
282
+ {
283
+ id: 't2',
284
+ type: 'silent_failure',
285
+ evidence: {
286
+ interactionPerformed: true,
287
+ networkActivity: false,
288
+ navigationOccurred: false,
289
+ domChanged: false,
290
+ userFeedback: false
291
+ }
292
+ }
293
+ ];
294
+
295
+ findings.forEach(finding => {
296
+ const causes = inferCauses(finding);
297
+ causes.forEach(cause => {
298
+ assert(
299
+ cause.confidence === 'LOW' || cause.confidence === 'MEDIUM',
300
+ `Confidence should be LOW or MEDIUM, got ${cause.confidence}`
301
+ );
302
+ });
303
+ });
304
+ });
305
+
306
+ // Results
307
+ console.log(`\n${passCount}/${testCount} tests passed`);
308
+ if (passCount === testCount) {
309
+ console.log('✓ All tests passed');
310
+ process.exit(0);
311
+ } else {
312
+ console.log(`✗ ${testCount - passCount} test(s) failed`);
313
+ process.exit(1);
314
+ }
@@ -22,10 +22,11 @@ import { applyGuardrails } from '../core/guardrails-engine.js';
22
22
  *
23
23
  * @param {Array} traces - Interaction traces
24
24
  * @param {Object} manifest - Project manifest with expectations
25
- * @param {Array} findings - Findings array to append to
25
+ * @param {Array} _findings - Findings array to append to
26
+ * @ts-expect-error - JSDoc param documented but unused
26
27
  * @returns {Array} UI feedback-related findings
27
28
  */
28
- export function detectUIFeedbackFindings(traces, manifest, findings) {
29
+ export function detectUIFeedbackFindings(traces, manifest, _findings) {
29
30
  const feedbackFindings = [];
30
31
 
31
32
  // Process each trace
@@ -56,6 +57,16 @@ export function detectUIFeedbackFindings(traces, manifest, findings) {
56
57
  (evidence.feedback.signals.length > 0 ||
57
58
  evidence.feedback.score === FEEDBACK_SCORE.MISSING);
58
59
 
60
+ // Determine finding type early (before use in confidence call)
61
+ let findingType = 'ui_feedback_silent_failure';
62
+ if (expectation.type === 'network_action' || expectation.type === 'network') {
63
+ findingType = 'network_feedback_missing';
64
+ } else if (expectation.type === 'navigation' || expectation.type === 'spa_navigation') {
65
+ findingType = 'navigation_feedback_missing';
66
+ } else if (expectation.type === 'validation' || expectation.type === 'form_submission') {
67
+ findingType = 'validation_feedback_missing';
68
+ }
69
+
59
70
  // PHASE 15: Compute unified confidence
60
71
  const unifiedConfidence = computeConfidenceForFinding({
61
72
  findingType: findingType,
@@ -63,10 +74,11 @@ export function detectUIFeedbackFindings(traces, manifest, findings) {
63
74
  sensors: trace.sensors || {},
64
75
  comparisons: {},
65
76
  evidence,
77
+ options: {}
66
78
  });
67
79
 
68
80
  // Legacy confidence for backward compatibility
69
- const confidence = computeConfidence({
81
+ const _confidence = computeConfidence({
70
82
  findingType: 'ui_feedback_silent_failure',
71
83
  expectation,
72
84
  sensors: trace.sensors || {},
@@ -75,28 +87,16 @@ export function detectUIFeedbackFindings(traces, manifest, findings) {
75
87
  });
76
88
 
77
89
  // Determine severity based on evidence
78
- const severity = hasSufficientEvidence && correlation.outcome === 'CONFIRMED' && unifiedConfidence.score >= 0.8
90
+ const severity = hasSufficientEvidence && correlation.outcome === 'CONFIRMED' && (unifiedConfidence.score01 || unifiedConfidence.score || 0) >= 0.8
79
91
  ? 'CONFIRMED'
80
92
  : 'SUSPECTED';
81
93
 
82
- // Determine finding type
83
- let findingType = 'ui_feedback_silent_failure';
84
- if (expectation.type === 'network_action' || expectation.type === 'network') {
85
- findingType = 'network_feedback_missing';
86
- } else if (expectation.type === 'navigation' || expectation.type === 'spa_navigation') {
87
- findingType = 'navigation_feedback_missing';
88
- } else if (expectation.type === 'validation' || expectation.type === 'form_submission') {
89
- findingType = 'validation_feedback_missing';
90
- } else if (expectation.type === 'state_action' || expectation.type === 'state') {
91
- findingType = 'state_feedback_missing';
92
- }
93
-
94
94
  const finding = {
95
95
  type: findingType,
96
96
  severity,
97
- confidence: unifiedConfidence.score, // PHASE 15: Use unified confidence score (0..1)
97
+ confidence: unifiedConfidence.score01 || unifiedConfidence.score || 0, // Contract v1: score01 canonical
98
98
  confidenceLevel: unifiedConfidence.level, // PHASE 15: Add confidence level
99
- confidenceReasons: unifiedConfidence.reasons, // PHASE 15: Add stable reason codes
99
+ confidenceReasons: unifiedConfidence.topReasons || unifiedConfidence.reasons || [], // Contract v1: topReasons
100
100
  interaction: {
101
101
  type: interaction.type,
102
102
  selector: interaction.selector,
@@ -55,20 +55,19 @@ export function computeObservationSummary(findings, observeTruth, learnTruth, co
55
55
  );
56
56
  const skippedCount = coverage.skippedInteractions || 0;
57
57
 
58
- // PHASE 15: Count findings by unified confidence level
58
+ // Count findings by confidence (for transparency, not judgment)
59
59
  const findingsByConfidence = {
60
60
  HIGH: 0,
61
61
  MEDIUM: 0,
62
62
  LOW: 0,
63
- UNPROVEN: 0 // PHASE 15: Changed from UNKNOWN to UNPROVEN
63
+ UNKNOWN: 0
64
64
  };
65
65
  const findingsByType = {};
66
66
  const findingsByOutcome = {}; // PHASE 2: Added outcome tracking
67
67
  const findingsByPromise = {}; // PHASE 3: Added promise tracking
68
68
 
69
69
  for (const finding of (findings || [])) {
70
- // PHASE 15: Use unified confidence level
71
- const confidence = finding.confidenceLevel || finding.confidence?.level || 'UNPROVEN';
70
+ const confidence = finding.confidence?.level || 'UNKNOWN';
72
71
  const type = finding.type || 'unknown';
73
72
  const outcome = finding.outcome || CANONICAL_OUTCOMES.SILENT_FAILURE; // Default for legacy findings
74
73
  const promiseType = finding.promise?.type || 'UNKNOWN_PROMISE'; // PHASE 3
@@ -79,39 +78,6 @@ export function computeObservationSummary(findings, observeTruth, learnTruth, co
79
78
  findingsByType[type] = (findingsByType[type] || 0) + 1;
80
79
  findingsByOutcome[outcome] = (findingsByOutcome[outcome] || 0) + 1; // PHASE 2
81
80
  findingsByPromise[promiseType] = (findingsByPromise[promiseType] || 0) + 1; // PHASE 3
82
-
83
- // PHASE 16: Track evidence completeness
84
- evidenceCompleteness.totalFindings++;
85
- if (finding.evidencePackage) {
86
- if (finding.evidencePackage.isComplete) {
87
- evidenceCompleteness.completeEvidence++;
88
- } else {
89
- evidenceCompleteness.incompleteEvidence++;
90
- }
91
- }
92
- if (finding.evidenceCompleteness?.downgraded) {
93
- evidenceCompleteness.downgradedCount++;
94
- }
95
-
96
- // PHASE 17: Track guardrails
97
- if (finding.guardrails) {
98
- guardrailsSummary.totalFindingsProcessed++;
99
- if (finding.guardrails.appliedRules) {
100
- guardrailsSummary.appliedRulesCount += finding.guardrails.appliedRules.length;
101
- }
102
- if (finding.guardrails.contradictions) {
103
- guardrailsSummary.contradictionsCount += finding.guardrails.contradictions.length;
104
- }
105
- if (finding.guardrails.finalDecision === 'SUSPECTED' && finding.severity === 'CONFIRMED') {
106
- guardrailsSummary.downgradedCount++;
107
- }
108
- if (finding.guardrails.finalDecision === 'INFORMATIONAL') {
109
- guardrailsSummary.informationalCount++;
110
- }
111
- if (finding.guardrails.recommendedStatus !== finding.severity && finding.severity === 'CONFIRMED') {
112
- guardrailsSummary.preventedConfirmedCount++;
113
- }
114
- }
115
81
  }
116
82
 
117
83
  // Calculate ratios (factual, not judgmental)
@@ -128,24 +94,6 @@ export function computeObservationSummary(findings, observeTruth, learnTruth, co
128
94
  unprovenResults: unprovenTraces.length
129
95
  };
130
96
 
131
- // PHASE 16: Track evidence completeness summary
132
- const evidenceCompleteness = {
133
- totalFindings: 0,
134
- completeEvidence: 0,
135
- incompleteEvidence: 0,
136
- downgradedCount: 0,
137
- };
138
-
139
- // PHASE 17: Track guardrails summary
140
- const guardrailsSummary = {
141
- totalFindingsProcessed: 0,
142
- preventedConfirmedCount: 0,
143
- downgradedCount: 0,
144
- informationalCount: 0,
145
- appliedRulesCount: 0,
146
- contradictionsCount: 0,
147
- };
148
-
149
97
  // Build gap details
150
98
  const gapDetails = [];
151
99
  if (isBudgetExceeded) {
@@ -202,8 +150,6 @@ export function computeObservationSummary(findings, observeTruth, learnTruth, co
202
150
  discrepanciesByPromise: findingsByPromise, // PHASE 3: Promise types
203
151
  findings: findings || []
204
152
  },
205
- evidenceCompleteness: evidenceCompleteness, // PHASE 16: Evidence completeness summary
206
- guardrailsSummary: guardrailsSummary, // PHASE 17: Guardrails summary
207
153
  coverage: {
208
154
  pagesEvaluated,
209
155
  pagesDiscovered,
@@ -39,8 +39,8 @@ export function correlateViewSwitch(expectation, trace, beforeUrl, afterUrl) {
39
39
 
40
40
  const sensors = trace.sensors || {};
41
41
  const navigation = sensors.navigation || {};
42
- const uiSignals = sensors.uiSignals || {};
43
- const stateUi = sensors.stateUi || {};
42
+ const _uiSignals = sensors.uiSignals || {};
43
+ const _stateUi = sensors.stateUi || {};
44
44
  const uiFeedback = sensors.uiFeedback || {};
45
45
 
46
46
  // Check if URL changed (if so, this is not a state-driven navigation)
@@ -195,7 +195,8 @@ async function stepClick(page, step, result, spec) {
195
195
 
196
196
  const fullContent = `${elementText} ${elementAriaLabel} ${elementValue}`.toLowerCase();
197
197
 
198
- for (const keyword of spec.denyKeywords) {
198
+ const denyKeywords = spec.denyKeywords || [];
199
+ for (const keyword of denyKeywords) {
199
200
  if (fullContent.includes(keyword.toLowerCase())) {
200
201
  result.reason = `Click blocked by safety gate: denyKeyword "${keyword}" found in element`;
201
202
  result.findingType = 'blocked_by_safety_gate';
@@ -41,11 +41,6 @@ export function validateFlowSpec(spec) {
41
41
  allowlist.pathsPrefix = ['/'];
42
42
  }
43
43
 
44
- // Validate denyKeywords
45
- const denyKeywords = spec.denyKeywords || [];
46
- if (!Array.isArray(denyKeywords)) {
47
- throw new Error('denyKeywords must be an array');
48
- }
49
44
 
50
45
  // Validate steps
51
46
  const steps = spec.steps.map((step, idx) => {
@@ -100,7 +95,6 @@ export function validateFlowSpec(spec) {
100
95
  name: spec.name,
101
96
  baseUrl: spec.baseUrl,
102
97
  allowlist,
103
- denyKeywords,
104
98
  secrets: spec.secrets || {},
105
99
  steps
106
100
  };