@odavl/guardian 2.0.0 → 2.0.1

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 (172) hide show
  1. package/CHANGELOG.md +210 -210
  2. package/LICENSE +21 -21
  3. package/README.md +297 -184
  4. package/bin/guardian.js +2242 -2221
  5. package/config/README.md +59 -59
  6. package/config/guardian.config.json +54 -54
  7. package/config/guardian.policy.json +12 -12
  8. package/config/profiles/docs.yaml +18 -18
  9. package/config/profiles/ecommerce.yaml +17 -17
  10. package/config/profiles/landing-demo.yaml +16 -16
  11. package/config/profiles/marketing.yaml +18 -18
  12. package/config/profiles/saas.yaml +21 -21
  13. package/flows/example-login-flow.json +36 -36
  14. package/flows/example-signup-flow.json +44 -44
  15. package/package.json +124 -116
  16. package/policies/enterprise.json +12 -12
  17. package/policies/landing-demo.json +22 -22
  18. package/policies/saas.json +12 -12
  19. package/policies/startup.json +12 -12
  20. package/src/enterprise/audit-logger.js +166 -166
  21. package/src/enterprise/pdf-exporter.js +267 -267
  22. package/src/enterprise/rbac-gate.js +142 -142
  23. package/src/enterprise/rbac.js +239 -239
  24. package/src/enterprise/site-manager.js +180 -180
  25. package/src/founder/feedback-system.js +156 -156
  26. package/src/founder/founder-tracker.js +213 -213
  27. package/src/founder/usage-signals.js +141 -141
  28. package/src/guardian/action-hints.js +439 -439
  29. package/src/guardian/alert-ledger.js +121 -121
  30. package/src/guardian/artifact-sanitizer.js +56 -56
  31. package/src/guardian/attempt-engine.js +1069 -1029
  32. package/src/guardian/attempt-registry.js +267 -267
  33. package/src/guardian/attempt-relevance.js +106 -106
  34. package/src/guardian/attempt-reporter.js +513 -507
  35. package/src/guardian/attempt.js +274 -273
  36. package/src/guardian/attempts-filter.js +63 -63
  37. package/src/guardian/auto-attempt-builder.js +283 -283
  38. package/src/guardian/baseline-registry.js +177 -177
  39. package/src/guardian/baseline-reporter.js +143 -143
  40. package/src/guardian/baseline-storage.js +285 -285
  41. package/src/guardian/baseline.js +535 -534
  42. package/src/guardian/behavioral-signals.js +261 -261
  43. package/src/guardian/breakage-intelligence.js +224 -224
  44. package/src/guardian/browser-pool.js +131 -131
  45. package/src/guardian/browser.js +119 -119
  46. package/src/guardian/canonical-truth.js +308 -308
  47. package/src/guardian/ci-cli.js +121 -121
  48. package/src/guardian/ci-gate.js +96 -96
  49. package/src/guardian/ci-mode.js +15 -15
  50. package/src/guardian/ci-output.js +55 -38
  51. package/src/guardian/cli-summary.js +102 -102
  52. package/src/guardian/confidence-signals.js +251 -251
  53. package/src/guardian/config-loader.js +161 -161
  54. package/src/guardian/config-validator.js +285 -283
  55. package/src/guardian/coverage-model.js +239 -239
  56. package/src/guardian/coverage-packs.js +58 -58
  57. package/src/guardian/crawler.js +142 -142
  58. package/src/guardian/data-guardian-detector.js +189 -189
  59. package/src/guardian/decision-authority.js +746 -725
  60. package/src/guardian/detection-layers.js +271 -271
  61. package/src/guardian/determinism.js +146 -146
  62. package/src/guardian/discovery-engine.js +661 -661
  63. package/src/guardian/drift-detector.js +100 -100
  64. package/src/guardian/enhanced-html-reporter.js +522 -522
  65. package/src/guardian/env-guard.js +128 -127
  66. package/src/guardian/error-clarity.js +399 -399
  67. package/src/guardian/export-contract.js +196 -196
  68. package/src/guardian/fail-safe.js +212 -212
  69. package/src/guardian/failure-intelligence.js +173 -173
  70. package/src/guardian/failure-taxonomy.js +169 -169
  71. package/src/guardian/final-outcome.js +206 -206
  72. package/src/guardian/first-run-profile.js +89 -89
  73. package/src/guardian/first-run.js +65 -67
  74. package/src/guardian/flag-validator.js +111 -111
  75. package/src/guardian/flow-executor.js +641 -639
  76. package/src/guardian/flow-registry.js +67 -67
  77. package/src/guardian/honesty.js +394 -394
  78. package/src/guardian/html-reporter.js +416 -416
  79. package/src/guardian/human-intent-resolver.js +296 -296
  80. package/src/guardian/human-interaction-model.js +351 -351
  81. package/src/guardian/human-journey-context.js +184 -184
  82. package/src/guardian/human-navigator.js +544 -544
  83. package/src/guardian/human-reporter.js +435 -431
  84. package/src/guardian/index.js +226 -221
  85. package/src/guardian/init-command.js +143 -143
  86. package/src/guardian/intent-detector.js +148 -146
  87. package/src/guardian/journey-definitions.js +132 -132
  88. package/src/guardian/journey-scan-cli.js +142 -145
  89. package/src/guardian/journey-scanner.js +583 -583
  90. package/src/guardian/junit-reporter.js +281 -281
  91. package/src/guardian/language-detection.js +99 -99
  92. package/src/guardian/live-alert.js +56 -56
  93. package/src/guardian/live-baseline-compare.js +146 -146
  94. package/src/guardian/live-cli.js +95 -95
  95. package/src/guardian/live-guardian.js +210 -210
  96. package/src/guardian/live-scheduler-runner.js +137 -137
  97. package/src/guardian/live-scheduler-state.js +167 -168
  98. package/src/guardian/live-scheduler.js +146 -146
  99. package/src/guardian/live-state.js +110 -110
  100. package/src/guardian/market-criticality.js +335 -335
  101. package/src/guardian/market-reporter.js +577 -577
  102. package/src/guardian/network-trace.js +178 -178
  103. package/src/guardian/obs-logger.js +110 -110
  104. package/src/guardian/observed-capabilities.js +427 -427
  105. package/src/guardian/output-contract.js +154 -0
  106. package/src/guardian/output-readability.js +264 -264
  107. package/src/guardian/parallel-executor.js +116 -116
  108. package/src/guardian/path-safety.js +56 -56
  109. package/src/guardian/pattern-analyzer.js +348 -348
  110. package/src/guardian/policy.js +432 -434
  111. package/src/guardian/prelaunch-gate.js +193 -193
  112. package/src/guardian/prerequisite-checker.js +101 -101
  113. package/src/guardian/preset-loader.js +152 -157
  114. package/src/guardian/profile-loader.js +96 -96
  115. package/src/guardian/reality.js +3025 -2826
  116. package/src/guardian/realworld-scenarios.js +94 -94
  117. package/src/guardian/reporter.js +167 -167
  118. package/src/guardian/retry-policy.js +123 -123
  119. package/src/guardian/root-cause-analysis.js +171 -171
  120. package/src/guardian/rules-engine.js +558 -558
  121. package/src/guardian/run-artifacts.js +212 -212
  122. package/src/guardian/run-cleanup.js +207 -207
  123. package/src/guardian/run-export.js +522 -522
  124. package/src/guardian/run-latest.js +90 -90
  125. package/src/guardian/run-list.js +211 -211
  126. package/src/guardian/run-summary.js +20 -20
  127. package/src/guardian/runtime-root.js +246 -246
  128. package/src/guardian/safety.js +248 -248
  129. package/src/guardian/scan-presets.js +133 -149
  130. package/src/guardian/screenshot.js +152 -152
  131. package/src/guardian/secret-hygiene.js +44 -44
  132. package/src/guardian/selector-fallbacks.js +394 -394
  133. package/src/guardian/semantic-contact-detection.js +255 -255
  134. package/src/guardian/semantic-contact-finder.js +201 -201
  135. package/src/guardian/semantic-targets.js +234 -234
  136. package/src/guardian/site-intelligence.js +588 -588
  137. package/src/guardian/site-introspection.js +257 -257
  138. package/src/guardian/sitemap.js +225 -225
  139. package/src/guardian/smoke.js +283 -258
  140. package/src/guardian/snapshot-schema.js +177 -290
  141. package/src/guardian/snapshot.js +430 -397
  142. package/src/guardian/stability-scorer.js +169 -169
  143. package/src/guardian/success-evaluator.js +214 -214
  144. package/src/guardian/template-command.js +184 -184
  145. package/src/guardian/text-formatters.js +426 -426
  146. package/src/guardian/timeout-profiles.js +57 -57
  147. package/src/guardian/truth/attempt.contract.js +158 -0
  148. package/src/guardian/truth/decision.contract.js +275 -0
  149. package/src/guardian/truth/snapshot.contract.js +363 -0
  150. package/src/guardian/validators.js +323 -323
  151. package/src/guardian/verdict-card.js +474 -474
  152. package/src/guardian/verdict-clarity.js +298 -298
  153. package/src/guardian/verdict-policy.js +363 -363
  154. package/src/guardian/verdict.js +333 -333
  155. package/src/guardian/verdicts.js +79 -74
  156. package/src/guardian/visual-diff.js +247 -247
  157. package/src/guardian/wait-for-outcome.js +119 -119
  158. package/src/guardian/watch-runner.js +181 -181
  159. package/src/guardian/watchdog-diff.js +167 -167
  160. package/src/guardian/webhook.js +206 -206
  161. package/src/payments/stripe-checkout.js +169 -169
  162. package/src/plans/plan-definitions.js +148 -148
  163. package/src/plans/plan-manager.js +211 -211
  164. package/src/plans/usage-tracker.js +210 -210
  165. package/src/recipes/recipe-engine.js +188 -188
  166. package/src/recipes/recipe-failure-analysis.js +159 -159
  167. package/src/recipes/recipe-registry.js +134 -134
  168. package/src/recipes/recipe-runtime.js +507 -507
  169. package/src/recipes/recipe-store.js +410 -410
  170. package/SECURITY.md +0 -77
  171. package/VERSIONING.md +0 -100
  172. package/guardian-contract-v1.md +0 -502
@@ -1,290 +1,177 @@
1
- /**
2
- * Market Reality Snapshot v1 Schema Definition
3
- *
4
- * A snapshot captures a complete market reality test run:
5
- * - what was discovered (crawl)
6
- * - what was attempted (attempts)
7
- * - what was observed (evidence: screenshots, traces, reports)
8
- * - what signals were detected (friction, failures, regressions)
9
- * - what the baseline was and how current differs
10
- */
11
-
12
- const SNAPSHOT_SCHEMA_VERSION = 'v1';
13
-
14
- /**
15
- * @typedef {Object} SnapshotMeta
16
- * @property {string} schemaVersion - always 'v1'
17
- * @property {string} createdAt - ISO timestamp
18
- * @property {string} toolVersion - package.json version
19
- * @property {string} url - base URL tested
20
- * @property {string} runId - unique run identifier
21
- * @property {string} [environment] - optional deployment environment
22
- */
23
-
24
- /**
25
- * @typedef {Object} CrawlResult
26
- * @property {string[]} discoveredUrls - all unique URLs found
27
- * @property {number} visitedCount - pages successfully loaded
28
- * @property {number} failedCount - pages that failed to load
29
- * @property {number} safetyBlockedCount - pages blocked by safety rules
30
- * @property {Array<{url: string, statusCode: number, error: string}>} [httpFailures] - detailed failures
31
- * @property {string} [notes] - human-readable summary
32
- */
33
-
34
- /**
35
- * @typedef {Object} ValidatorResult
36
- * @property {string} id - unique validator ID
37
- * @property {string} type - validator type (urlIncludes, elementVisible, etc)
38
- * @property {string} status - 'PASS', 'FAIL', or 'WARN'
39
- * @property {string} message - human readable result
40
- * @property {Object} [evidence] - supporting data (selector, url, snippet, etc)
41
- */
42
-
43
- /**
44
- * @typedef {Object} AttemptResult
45
- * @property {string} attemptId - unique attempt identifier
46
- * @property {string} attemptName - human-readable name
47
- * @property {string} goal - what the user tried to achieve
48
- * @property {string} outcome - 'SUCCESS', 'FAILURE', 'FRICTION', 'NOT_APPLICABLE', 'DISCOVERY_FAILED', 'SKIPPED'
49
- * @property {number} totalDurationMs - elapsed time
50
- * @property {number} stepCount - how many steps executed
51
- * @property {number} failedStepIndex - index of first failed step, or -1 if all succeeded
52
- * @property {Object} friction - friction signals for this attempt
53
- * @property {ValidatorResult[]} [validators] - soft failure detectors (Phase 2)
54
- * @property {number} [softFailureCount] - count of failed validators
55
- * @property {string} [riskCategory] - 'LEAD', 'REVENUE', 'TRUST/UX' (Phase 2)
56
- * @property {string} [skipReason] - reason if SKIPPED, NOT_APPLICABLE, or DISCOVERY_FAILED
57
- * @property {string[]} [selectorChainTried] - selectors attempted during discovery
58
- * @property {Object} [discoverySignals] - element discovery signals and heuristics
59
- * @property {string} [finalSelection] - which selector/strategy successfully matched element
60
- */
61
-
62
- /**
63
- * @typedef {Object} Evidence
64
- * @property {string} artifactDir - root directory where all artifacts were saved
65
- * @property {string} [marketReportJson] - path to market-report.json
66
- * @property {string} [marketReportHtml] - path to market-report.html
67
- * @property {string} [traceZip] - path to trace.zip if enabled
68
- * @property {Object<string, string>} [attemptArtifacts] - { attemptId => { reportJson, reportHtml, screenshotDir } }
69
- */
70
-
71
- /**
72
- * @typedef {Object} Signal
73
- * @property {string} id - unique signal ID
74
- * @property {string} severity - 'low', 'medium', 'high', 'critical'
75
- * @property {string} type - 'friction', 'failure', 'regression', 'timeout', 'missing_element', 'soft_failure'
76
- * @property {string} description - human readable
77
- * @property {string} [affectedAttemptId] - if specific to an attempt
78
- */
79
-
80
- /**
81
- * @typedef {Object} BaselineInfo
82
- * @property {boolean} baselineFound - whether a baseline was loaded
83
- * @property {boolean} baselineCreatedThisRun - true if baseline was auto-created in this run
84
- * @property {string} [baselineCreatedAt] - ISO timestamp when baseline was first created
85
- * @property {string} [baselinePath] - file system path to baseline
86
- * @property {Object} [diff] - comparison result if baseline exists
87
- * @property {Object} [diff.regressions] - { attemptId => {before, after, reason} }
88
- * @property {Object} [diff.improvements] - { attemptId => {before, after, reason} }
89
- * @property {number} [diff.attemptsDriftCount] - how many attempts changed outcome
90
- * @property {Array} [diff.validatorsChanged] - validator regression details (Phase 2)
91
- */
92
-
93
- /**
94
- * @typedef {Object} MarketRisk
95
- * @property {string} attemptId - which attempt
96
- * @property {string} validatorId - which validator or friction signal
97
- * @property {string} category - REVENUE|LEAD|TRUST|UX
98
- * @property {string} severity - CRITICAL|WARNING|INFO
99
- * @property {number} impactScore - 0-100 deterministic score
100
- * @property {string} humanReadableReason - explanation
101
- */
102
-
103
- /**
104
- * @typedef {Object} MarketImpactSummary
105
- * @property {string} highestSeverity - CRITICAL|WARNING|INFO
106
- * @property {number} totalRiskCount - total number of identified risks
107
- * @property {Object} countsBySeverity - { CRITICAL: N, WARNING: N, INFO: N }
108
- * @property {MarketRisk[]} topRisks - top 10 risks, sorted by impact score
109
- */
110
-
111
- /**
112
- * @typedef {Object} InteractionResult
113
- * @property {string} interactionId - unique ID
114
- * @property {string} pageUrl - URL where found
115
- * @property {string} type - NAVIGATE|CLICK|FORM_FILL
116
- * @property {string} selector - CSS selector to find element
117
- * @property {string} outcome - SUCCESS|FAILURE|FRICTION
118
- * @property {string} [notes] - details (target URL, error, etc)
119
- * @property {number} [durationMs] - execution time
120
- * @property {string} [errorMessage] - if FAILURE
121
- * @property {string} [evidencePath] - path to screenshot
122
- */
123
-
124
- /**
125
- * @typedef {Object} DiscoverySummary
126
- * @property {string[]} pagesVisited - URLs crawled
127
- * @property {number} pagesVisitedCount - total pages
128
- * @property {number} interactionsDiscovered - total candidates found
129
- * @property {number} interactionsExecuted - candidates executed
130
- * @property {Object} interactionsByType - { NAVIGATE: N, CLICK: N, FORM_FILL: N }
131
- * @property {Object} interactionsByRisk - { safe: N, risky: N }
132
- * @property {InteractionResult[]} results - execution results (failures + top successes)
133
- * @property {string} [summary] - human readable summary
134
- */
135
-
136
- /**
137
- * @typedef {Object} MarketRealitySnapshot
138
- * @property {string} schemaVersion - always 'v1'
139
- * @property {SnapshotMeta} meta
140
- * @property {Object} [verdict] - unified run-level verdict
141
- * @property {('READY'|'DO_NOT_LAUNCH'|'FRICTION')} verdict.verdict
142
- * @property {{ level: ('low'|'medium'|'high'), score: number, reasons: string[] }} verdict.confidence
143
- * @property {string} verdict.why
144
- * @property {string[]} verdict.keyFindings
145
- * @property {{ screenshots?: string[], traces?: string[], reportPaths?: string[], affectedPages?: string[] }} verdict.evidence
146
- * @property {string[]} verdict.limits
147
- * @property {CrawlResult} [crawl]
148
- * @property {AttemptResult[]} attempts
149
- * @property {Array} flows
150
- * @property {Signal[]} signals
151
- * @property {Object} [riskSummary] - market risk analysis (Phase 2)
152
- * @property {MarketImpactSummary} [marketImpactSummary] - market criticality (Phase 3)
153
- * @property {DiscoverySummary} [discovery] - auto-discovered interactions (Phase 4)
154
- * @property {Evidence} evidence
155
- * @property {BaselineInfo} baseline
156
- */
157
-
158
- function createEmptySnapshot(baseUrl, runId, toolVersion) {
159
- return {
160
- schemaVersion: SNAPSHOT_SCHEMA_VERSION,
161
- meta: {
162
- schemaVersion: SNAPSHOT_SCHEMA_VERSION,
163
- createdAt: new Date().toISOString(),
164
- toolVersion,
165
- url: baseUrl,
166
- runId,
167
- environment: process.env.GUARDIAN_ENV || 'production'
168
- },
169
- crawl: {
170
- discoveredUrls: [],
171
- visitedCount: 0,
172
- failedCount: 0,
173
- safetyBlockedCount: 0,
174
- httpFailures: [],
175
- notes: ''
176
- },
177
- attempts: [],
178
- flows: [],
179
- signals: [],
180
- verdict: null,
181
- riskSummary: {
182
- totalSoftFailures: 0,
183
- totalFriction: 0,
184
- failuresByCategory: {},
185
- topRisks: []
186
- },
187
- marketImpactSummary: {
188
- highestSeverity: 'INFO',
189
- totalRiskCount: 0,
190
- countsBySeverity: {
191
- CRITICAL: 0,
192
- WARNING: 0,
193
- INFO: 0
194
- },
195
- topRisks: []
196
- },
197
- discovery: {
198
- pagesVisited: [],
199
- pagesVisitedCount: 0,
200
- interactionsDiscovered: 0,
201
- interactionsExecuted: 0,
202
- interactionsByType: {
203
- NAVIGATE: 0,
204
- CLICK: 0,
205
- FORM_FILL: 0
206
- },
207
- interactionsByRisk: {
208
- safe: 0,
209
- risky: 0
210
- },
211
- results: [],
212
- summary: ''
213
- },
214
- evidence: {
215
- artifactDir: '',
216
- attemptArtifacts: {},
217
- flowArtifacts: {}
218
- },
219
- intelligence: {
220
- totalFailures: 0,
221
- failures: [],
222
- byDomain: {},
223
- bySeverity: {},
224
- escalationSignals: [],
225
- summary: ''
226
- },
227
- baseline: {
228
- baselineFound: false,
229
- baselineCreatedThisRun: false,
230
- baselineCreatedAt: null,
231
- baselinePath: null,
232
- diff: null
233
- }
234
- };
235
- }
236
-
237
- function validateSnapshot(snapshot) {
238
- const errors = [];
239
-
240
- if (!snapshot.schemaVersion || snapshot.schemaVersion !== SNAPSHOT_SCHEMA_VERSION) {
241
- errors.push('Missing or invalid schemaVersion');
242
- }
243
-
244
- if (!snapshot.meta || !snapshot.meta.createdAt || !snapshot.meta.url || !snapshot.meta.runId) {
245
- errors.push('Missing required meta fields: createdAt, url, runId');
246
- }
247
-
248
- if (!Array.isArray(snapshot.attempts)) {
249
- errors.push('attempts must be an array');
250
- }
251
-
252
- if (!Array.isArray(snapshot.signals)) {
253
- errors.push('signals must be an array');
254
- }
255
-
256
- if (!Array.isArray(snapshot.flows)) {
257
- errors.push('flows must be an array');
258
- }
259
-
260
- if (!snapshot.evidence || !snapshot.evidence.artifactDir) {
261
- errors.push('Missing evidence.artifactDir');
262
- }
263
-
264
- if (!snapshot.baseline) {
265
- errors.push('Missing baseline section');
266
- }
267
-
268
- // Basic verdict validation (if present)
269
- if (snapshot.verdict) {
270
- const v = snapshot.verdict;
271
- const allowed = ['READY', 'DO_NOT_LAUNCH', 'FRICTION'];
272
- if (!v.verdict || !allowed.includes(v.verdict)) {
273
- errors.push('Invalid verdict.verdict');
274
- }
275
- if (!v.confidence || typeof v.confidence.score !== 'number' || v.confidence.score < 0 || v.confidence.score > 1) {
276
- errors.push('Invalid verdict.confidence.score');
277
- }
278
- }
279
-
280
- return {
281
- valid: errors.length === 0,
282
- errors
283
- };
284
- }
285
-
286
- module.exports = {
287
- SNAPSHOT_SCHEMA_VERSION,
288
- createEmptySnapshot,
289
- validateSnapshot
290
- };
1
+ /**
2
+ * Market Reality Snapshot v1 Schema Definition
3
+ *
4
+ * A snapshot captures a complete market reality test run:
5
+ * - what was discovered (crawl)
6
+ * - what was attempted (attempts)
7
+ * - what was observed (evidence: screenshots, traces, reports)
8
+ * - what signals were detected (friction, failures, regressions)
9
+ * - what the baseline was and how current differs
10
+ *
11
+ * NOTE: Type definitions are now in src/guardian/truth/snapshot.contract.js
12
+ * This file provides the implementation functions.
13
+ */
14
+
15
+ /**
16
+ * @typedef {import('./truth/snapshot.contract.js').MarketRealitySnapshot} MarketRealitySnapshot
17
+ * @typedef {import('./truth/snapshot.contract.js').SnapshotMeta} SnapshotMeta
18
+ * @typedef {import('./truth/snapshot.contract.js').CrawlResult} CrawlResult
19
+ * @typedef {import('./truth/snapshot.contract.js').SnapshotAttemptEntry} SnapshotAttemptEntry
20
+ * @typedef {import('./truth/snapshot.contract.js').FlowResult} FlowResult
21
+ * @typedef {import('./truth/snapshot.contract.js').Signal} Signal
22
+ * @typedef {import('./truth/snapshot.contract.js').BaselineInfo} BaselineInfo
23
+ * @typedef {import('./truth/snapshot.contract.js').SnapshotEvidence} SnapshotEvidence
24
+ * @typedef {import('./truth/snapshot.contract.js').RiskSummary} RiskSummary
25
+ * @typedef {import('./truth/snapshot.contract.js').MarketImpactSummary} MarketImpactSummary
26
+ * @typedef {import('./truth/snapshot.contract.js').DiscoverySummary} DiscoverySummary
27
+ * @typedef {import('./truth/snapshot.contract.js').IntelligenceData} IntelligenceData
28
+ */
29
+
30
+ const SNAPSHOT_SCHEMA_VERSION = 'v1';
31
+
32
+ /**
33
+ * Create an empty snapshot with default structure
34
+ * @param {string} baseUrl - Base URL tested
35
+ * @param {string} runId - Unique run identifier
36
+ * @param {string} toolVersion - Guardian tool version
37
+ * @returns {MarketRealitySnapshot}
38
+ */
39
+
40
+ function createEmptySnapshot(baseUrl, runId, toolVersion) {
41
+ return {
42
+ schemaVersion: SNAPSHOT_SCHEMA_VERSION,
43
+ meta: {
44
+ schemaVersion: SNAPSHOT_SCHEMA_VERSION,
45
+ createdAt: new Date().toISOString(),
46
+ toolVersion,
47
+ url: baseUrl,
48
+ runId,
49
+ environment: process.env.GUARDIAN_ENV || 'production'
50
+ },
51
+ crawl: {
52
+ discoveredUrls: [],
53
+ visitedCount: 0,
54
+ failedCount: 0,
55
+ safetyBlockedCount: 0,
56
+ httpFailures: [],
57
+ notes: ''
58
+ },
59
+ attempts: [],
60
+ flows: [],
61
+ signals: [],
62
+ verdict: null,
63
+ riskSummary: {
64
+ totalSoftFailures: 0,
65
+ totalFriction: 0,
66
+ failuresByCategory: {},
67
+ topRisks: []
68
+ },
69
+ marketImpactSummary: {
70
+ highestSeverity: 'INFO',
71
+ totalRiskCount: 0,
72
+ countsBySeverity: {
73
+ CRITICAL: 0,
74
+ WARNING: 0,
75
+ INFO: 0
76
+ },
77
+ topRisks: []
78
+ },
79
+ discovery: {
80
+ pagesVisited: [],
81
+ pagesVisitedCount: 0,
82
+ interactionsDiscovered: 0,
83
+ interactionsExecuted: 0,
84
+ interactionsByType: {
85
+ NAVIGATE: 0,
86
+ CLICK: 0,
87
+ FORM_FILL: 0
88
+ },
89
+ interactionsByRisk: {
90
+ safe: 0,
91
+ risky: 0
92
+ },
93
+ results: [],
94
+ summary: ''
95
+ },
96
+ evidence: {
97
+ artifactDir: '',
98
+ attemptArtifacts: {},
99
+ flowArtifacts: {}
100
+ },
101
+ intelligence: {
102
+ totalFailures: 0,
103
+ failures: [],
104
+ byDomain: {},
105
+ bySeverity: {},
106
+ escalationSignals: [],
107
+ summary: ''
108
+ },
109
+ baseline: {
110
+ baselineFound: false,
111
+ baselineCreatedThisRun: false,
112
+ baselineCreatedAt: null,
113
+ baselinePath: null,
114
+ diff: null
115
+ }
116
+ };
117
+ }
118
+
119
+ /**
120
+ * Validate snapshot structure
121
+ * @param {MarketRealitySnapshot} snapshot - Snapshot to validate
122
+ * @returns {{valid: boolean, errors: string[]}}
123
+ */
124
+ function validateSnapshot(snapshot) {
125
+ const errors = [];
126
+
127
+ if (!snapshot.schemaVersion || snapshot.schemaVersion !== SNAPSHOT_SCHEMA_VERSION) {
128
+ errors.push('Missing or invalid schemaVersion');
129
+ }
130
+
131
+ if (!snapshot.meta || !snapshot.meta.createdAt || !snapshot.meta.url || !snapshot.meta.runId) {
132
+ errors.push('Missing required meta fields: createdAt, url, runId');
133
+ }
134
+
135
+ if (!Array.isArray(snapshot.attempts)) {
136
+ errors.push('attempts must be an array');
137
+ }
138
+
139
+ if (!Array.isArray(snapshot.signals)) {
140
+ errors.push('signals must be an array');
141
+ }
142
+
143
+ if (!Array.isArray(snapshot.flows)) {
144
+ errors.push('flows must be an array');
145
+ }
146
+
147
+ if (!snapshot.evidence || !snapshot.evidence.artifactDir) {
148
+ errors.push('Missing evidence.artifactDir');
149
+ }
150
+
151
+ if (!snapshot.baseline) {
152
+ errors.push('Missing baseline section');
153
+ }
154
+
155
+ // Basic verdict validation (if present)
156
+ if (snapshot.verdict) {
157
+ const v = snapshot.verdict;
158
+ const allowed = ['READY', 'DO_NOT_LAUNCH', 'FRICTION'];
159
+ if (!v.verdict || !allowed.includes(v.verdict)) {
160
+ errors.push('Invalid verdict.verdict');
161
+ }
162
+ if (!v.confidence || typeof v.confidence.score !== 'number' || v.confidence.score < 0 || v.confidence.score > 1) {
163
+ errors.push('Invalid verdict.confidence.score');
164
+ }
165
+ }
166
+
167
+ return {
168
+ valid: errors.length === 0,
169
+ errors
170
+ };
171
+ }
172
+
173
+ module.exports = {
174
+ SNAPSHOT_SCHEMA_VERSION,
175
+ createEmptySnapshot,
176
+ validateSnapshot
177
+ };