@crvouga/atlas 0.4.0 → 0.6.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 (444) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/README.md +191 -611
  3. package/dist/bun/src/driver.d.ts +41 -7
  4. package/dist/bun/src/driver.js +47 -6
  5. package/dist/bun/src/index.d.ts +8 -0
  6. package/dist/bun/src/index.js +8 -0
  7. package/dist/core/src/cli.d.ts +14 -1
  8. package/dist/core/src/cli.js +35 -1
  9. package/dist/core/src/config.d.ts +77 -4
  10. package/dist/core/src/config.js +110 -17
  11. package/dist/core/src/events/cloudevents.d.ts +54 -9
  12. package/dist/core/src/events/cloudevents.js +60 -9
  13. package/dist/core/src/graph.d.ts +21 -0
  14. package/dist/core/src/graph.js +26 -0
  15. package/dist/core/src/index.d.ts +11 -0
  16. package/dist/core/src/index.js +11 -0
  17. package/dist/core/src/lint.d.ts +30 -0
  18. package/dist/core/src/lint.js +13 -0
  19. package/dist/core/src/plan.d.ts +39 -1
  20. package/dist/core/src/plan.js +28 -1
  21. package/dist/core/src/privacy.d.ts +29 -2
  22. package/dist/core/src/privacy.js +19 -2
  23. package/dist/core/src/report/formats.d.ts +34 -4
  24. package/dist/core/src/report/formats.js +24 -4
  25. package/dist/core/src/report/markdown.d.ts +15 -0
  26. package/dist/core/src/report/markdown.js +8 -0
  27. package/dist/core/src/run/clients.d.ts +44 -5
  28. package/dist/core/src/run/clients.js +46 -2
  29. package/dist/core/src/run/execute.d.ts +75 -16
  30. package/dist/core/src/run/execute.js +71 -18
  31. package/dist/core/src/run/frames.d.ts +13 -4
  32. package/dist/core/src/run/frames.js +19 -4
  33. package/dist/core/src/run/http.d.ts +34 -1
  34. package/dist/core/src/run/http.js +14 -1
  35. package/dist/core/src/run/types.d.ts +76 -3
  36. package/dist/core/src/run/types.js +19 -3
  37. package/dist/core/src/signals.d.ts +49 -5
  38. package/dist/core/src/signals.js +22 -5
  39. package/dist/core/src/spec/compose.d.ts +23 -1
  40. package/dist/core/src/spec/compose.js +10 -1
  41. package/dist/core/src/spec/index.d.ts +9 -1
  42. package/dist/core/src/spec/index.js +9 -1
  43. package/dist/core/src/spec/load.d.ts +22 -1
  44. package/dist/core/src/spec/load.js +31 -1
  45. package/dist/core/src/spec/parse.d.ts +16 -2
  46. package/dist/core/src/spec/parse.js +25 -2
  47. package/dist/core/src/spec/scxml.d.ts +25 -4
  48. package/dist/core/src/spec/scxml.js +36 -4
  49. package/dist/core/src/spec/types.d.ts +52 -1
  50. package/dist/core/src/spec/types.js +24 -0
  51. package/dist/detox/src/index.d.ts +54 -11
  52. package/dist/detox/src/index.js +45 -7
  53. package/dist/playwright/src/driver.d.ts +39 -3
  54. package/dist/playwright/src/driver.js +27 -3
  55. package/dist/playwright/src/index.d.ts +10 -0
  56. package/dist/playwright/src/index.js +10 -0
  57. package/dist/playwright/src/media.d.ts +50 -4
  58. package/dist/playwright/src/media.js +55 -4
  59. package/dist/playwright/src/signals.d.ts +44 -5
  60. package/dist/playwright/src/signals.js +29 -5
  61. package/dist/reports/src/attachment-files.d.ts +44 -0
  62. package/dist/reports/src/attachment-files.js +121 -0
  63. package/dist/reports/src/attachment-response.d.ts +31 -0
  64. package/dist/reports/src/attachment-response.js +55 -0
  65. package/dist/reports/src/aws-credentials.d.ts +48 -0
  66. package/dist/reports/src/aws-credentials.js +221 -0
  67. package/dist/reports/src/browser-navigation.d.ts +14 -0
  68. package/dist/reports/src/browser-navigation.js +7 -0
  69. package/dist/reports/src/bundle.d.ts +34 -2
  70. package/dist/reports/src/bundle.js +38 -2
  71. package/dist/reports/src/catalog.d.ts +34 -3
  72. package/dist/reports/src/catalog.js +17 -3
  73. package/dist/reports/src/cli-stores.d.ts +36 -0
  74. package/dist/reports/src/cli-stores.js +73 -0
  75. package/dist/reports/src/clock.d.ts +7 -2
  76. package/dist/reports/src/clock.js +5 -2
  77. package/dist/reports/src/completion-order.d.ts +18 -5
  78. package/dist/reports/src/completion-order.js +18 -5
  79. package/dist/reports/src/compose.d.ts +80 -0
  80. package/dist/reports/src/compose.js +117 -0
  81. package/dist/reports/src/copy-store.d.ts +29 -0
  82. package/dist/reports/src/copy-store.js +52 -0
  83. package/dist/reports/src/directory-outline.d.ts +38 -0
  84. package/dist/reports/src/directory-outline.js +103 -0
  85. package/dist/reports/src/directory-store.d.ts +20 -0
  86. package/dist/reports/src/directory-store.js +166 -0
  87. package/dist/reports/src/doctor.d.ts +29 -0
  88. package/dist/reports/src/doctor.js +85 -0
  89. package/dist/reports/src/effective-delta.d.ts +34 -0
  90. package/dist/reports/src/effective-delta.js +25 -0
  91. package/dist/reports/src/evidence-detail.d.ts +64 -0
  92. package/dist/reports/src/evidence-detail.js +92 -0
  93. package/dist/reports/src/evidence-live.d.ts +35 -5
  94. package/dist/reports/src/evidence-live.js +18 -2
  95. package/dist/reports/src/evidence-types.d.ts +68 -1
  96. package/dist/reports/src/evidence-types.js +5 -1
  97. package/dist/reports/src/evidence-view.d.ts +43 -4
  98. package/dist/reports/src/evidence-view.js +13 -2
  99. package/dist/reports/src/explorer-types.d.ts +40 -3
  100. package/dist/reports/src/explorer-types.js +8 -0
  101. package/dist/reports/src/explorer-versions.d.ts +9 -1
  102. package/dist/reports/src/explorer-versions.js +18 -1
  103. package/dist/reports/src/export-evidence.d.ts +16 -1
  104. package/dist/reports/src/export-evidence.js +52 -1
  105. package/dist/reports/src/fetcher.d.ts +27 -3
  106. package/dist/reports/src/fetcher.js +11 -3
  107. package/dist/reports/src/format-date.d.ts +6 -0
  108. package/dist/reports/src/format-date.js +6 -0
  109. package/dist/reports/src/format.d.ts +16 -0
  110. package/dist/reports/src/format.js +10 -0
  111. package/dist/reports/src/frames.d.ts +36 -3
  112. package/dist/reports/src/frames.js +22 -3
  113. package/dist/reports/src/hash.d.ts +18 -0
  114. package/dist/reports/src/hash.js +13 -0
  115. package/dist/reports/src/host.d.ts +29 -0
  116. package/dist/reports/src/host.js +64 -0
  117. package/dist/reports/src/html-frame.d.ts +5 -6
  118. package/dist/reports/src/html-frame.js +12 -1
  119. package/dist/reports/src/http-store.d.ts +56 -0
  120. package/dist/reports/src/http-store.js +148 -0
  121. package/dist/reports/src/http.d.ts +7 -0
  122. package/dist/reports/src/http.js +6 -0
  123. package/dist/reports/src/ingest.d.ts +54 -0
  124. package/dist/reports/src/ingest.js +210 -0
  125. package/dist/reports/src/init.d.ts +40 -0
  126. package/dist/reports/src/init.js +369 -0
  127. package/dist/reports/src/junit.d.ts +33 -6
  128. package/dist/reports/src/junit.js +87 -21
  129. package/dist/reports/src/keyboard.d.ts +20 -0
  130. package/dist/reports/src/keyboard.js +11 -0
  131. package/dist/reports/src/keys.d.ts +21 -3
  132. package/dist/reports/src/keys.js +21 -3
  133. package/dist/reports/src/layer-axes.d.ts +35 -5
  134. package/dist/reports/src/layer-axes.js +25 -5
  135. package/dist/reports/src/layer-catalog.d.ts +28 -1
  136. package/dist/reports/src/layer-catalog.js +18 -1
  137. package/dist/reports/src/layer-merge.d.ts +98 -9
  138. package/dist/reports/src/layer-merge.js +147 -31
  139. package/dist/reports/src/layer-source.d.ts +18 -0
  140. package/dist/reports/src/layer-source.js +11 -0
  141. package/dist/reports/src/layer-store.d.ts +42 -0
  142. package/dist/reports/src/layer-store.js +63 -5
  143. package/dist/reports/src/layer-types.d.ts +32 -1
  144. package/dist/reports/src/layers-route.d.ts +47 -4
  145. package/dist/reports/src/layers-route.js +86 -7
  146. package/dist/reports/src/live-reporter.d.ts +34 -0
  147. package/dist/reports/src/live-reporter.js +122 -0
  148. package/dist/reports/src/media-types.d.ts +11 -0
  149. package/dist/reports/src/media-types.js +22 -0
  150. package/dist/reports/src/namespace-guard.d.ts +10 -1
  151. package/dist/reports/src/namespace-guard.js +10 -1
  152. package/dist/reports/src/navigation.d.ts +12 -0
  153. package/dist/reports/src/navigation.js +7 -0
  154. package/dist/reports/src/object-io.d.ts +11 -4
  155. package/dist/reports/src/object-io.js +4 -4
  156. package/dist/reports/src/object-store-conformance.d.ts +27 -0
  157. package/dist/reports/src/object-store-conformance.js +87 -0
  158. package/dist/reports/src/object-store.d.ts +33 -24
  159. package/dist/reports/src/object-store.js +17 -24
  160. package/dist/reports/src/parse-with.d.ts +16 -0
  161. package/dist/reports/src/parse-with.js +14 -0
  162. package/dist/reports/src/performance-part.d.ts +25 -1
  163. package/dist/reports/src/performance-part.js +15 -1
  164. package/dist/reports/src/performance.d.ts +22 -2
  165. package/dist/reports/src/performance.js +12 -2
  166. package/dist/reports/src/persist.d.ts +12 -3
  167. package/dist/reports/src/persist.js +12 -3
  168. package/dist/reports/src/phone.d.ts +18 -1
  169. package/dist/reports/src/phone.js +18 -1
  170. package/dist/reports/src/protocol.d.ts +26 -4
  171. package/dist/reports/src/protocol.js +15 -4
  172. package/dist/reports/src/publication.d.ts +21 -1
  173. package/dist/reports/src/publication.js +21 -1
  174. package/dist/reports/src/publish-checks.d.ts +35 -0
  175. package/dist/reports/src/publish-checks.js +122 -0
  176. package/dist/reports/src/publish.d.ts +42 -9
  177. package/dist/reports/src/publish.js +36 -7
  178. package/dist/reports/src/read.d.ts +46 -16
  179. package/dist/reports/src/read.js +38 -14
  180. package/dist/reports/src/recording-bundle.d.ts +13 -2
  181. package/dist/reports/src/recording-bundle.js +4 -2
  182. package/dist/reports/src/recording-keys.d.ts +15 -1
  183. package/dist/reports/src/recording-keys.js +15 -1
  184. package/dist/reports/src/remote-report.d.ts +28 -7
  185. package/dist/reports/src/remote-report.js +15 -7
  186. package/dist/reports/src/report-cli.d.ts +51 -0
  187. package/dist/reports/src/report-cli.js +486 -0
  188. package/dist/reports/src/report-index.d.ts +99 -0
  189. package/dist/reports/src/report-index.js +337 -0
  190. package/dist/reports/src/report-loader.d.ts +10 -0
  191. package/dist/reports/src/report-loader.js +6 -0
  192. package/dist/reports/src/report-parts.d.ts +29 -3
  193. package/dist/reports/src/report-parts.js +37 -3
  194. package/dist/reports/src/report-status.d.ts +48 -0
  195. package/dist/reports/src/report-status.js +156 -0
  196. package/dist/reports/src/report-types.d.ts +73 -0
  197. package/dist/reports/src/report-types.js +3 -0
  198. package/dist/reports/src/reporter.d.ts +67 -0
  199. package/dist/reports/src/reporter.js +128 -0
  200. package/dist/reports/src/reports-explorer-route.d.ts +541 -0
  201. package/dist/reports/src/reports-explorer-route.js +11 -0
  202. package/dist/reports/src/reports-list-route.d.ts +9 -0
  203. package/dist/reports/src/reports-list-route.js +9 -0
  204. package/dist/reports/src/reports-route.d.ts +561 -22
  205. package/dist/reports/src/reports-route.js +11 -3
  206. package/dist/reports/src/reports-store.d.ts +19 -2
  207. package/dist/reports/src/reports-store.js +10 -2
  208. package/dist/reports/src/resilient-cache.d.ts +14 -1
  209. package/dist/reports/src/resilient-cache.js +8 -1
  210. package/dist/reports/src/retention.d.ts +17 -1
  211. package/dist/reports/src/retention.js +5 -1
  212. package/dist/reports/src/run-bundle.d.ts +63 -0
  213. package/dist/reports/src/run-bundle.js +159 -0
  214. package/dist/reports/src/s3-store.d.ts +79 -0
  215. package/dist/reports/src/s3-store.js +295 -0
  216. package/dist/reports/src/scenario.d.ts +14 -4
  217. package/dist/reports/src/scenario.js +5 -4
  218. package/dist/reports/src/sigv4.d.ts +72 -0
  219. package/dist/reports/src/sigv4.js +96 -0
  220. package/dist/reports/src/snapshot-bundle.d.ts +24 -1
  221. package/dist/reports/src/snapshot-bundle.js +27 -2
  222. package/dist/reports/src/snapshot-surface.d.ts +20 -9
  223. package/dist/reports/src/snapshot-surface.js +32 -19
  224. package/dist/reports/src/source-content.d.ts +23 -0
  225. package/dist/reports/src/source-content.js +30 -1
  226. package/dist/reports/src/source-entries.d.ts +13 -0
  227. package/dist/reports/src/source-entries.js +6 -0
  228. package/dist/reports/src/source-part.d.ts +15 -0
  229. package/dist/reports/src/source-part.js +8 -0
  230. package/dist/reports/src/source-reader.d.ts +11 -0
  231. package/dist/reports/src/source-reader.js +5 -0
  232. package/dist/reports/src/spec-evidence.d.ts +12 -2
  233. package/dist/reports/src/spec-evidence.js +6 -2
  234. package/dist/reports/src/spec-inventory.d.ts +15 -1
  235. package/dist/reports/src/spec-inventory.js +22 -1
  236. package/dist/reports/src/spec-link.d.ts +28 -0
  237. package/dist/reports/src/spec-link.js +70 -0
  238. package/dist/reports/src/spec-set.d.ts +63 -0
  239. package/dist/reports/src/spec-set.js +177 -0
  240. package/dist/reports/src/status.d.ts +10 -0
  241. package/dist/reports/src/status.js +5 -0
  242. package/dist/reports/src/store-prefix.d.ts +28 -0
  243. package/dist/reports/src/store-prefix.js +22 -0
  244. package/dist/reports/src/store-url.d.ts +89 -0
  245. package/dist/reports/src/store-url.js +160 -0
  246. package/dist/reports/src/test-catalog-entity.d.ts +11 -0
  247. package/dist/reports/src/test-catalog-entity.js +6 -0
  248. package/dist/reports/src/types.d.ts +6 -1
  249. package/dist/reports/src/use-report-nav.d.ts +35 -0
  250. package/dist/reports/src/use-report-nav.js +12 -0
  251. package/dist/runners/src/codecept.d.ts +14 -0
  252. package/dist/runners/src/codecept.js +16 -0
  253. package/dist/runners/src/jest.d.ts +50 -0
  254. package/dist/runners/src/jest.js +52 -0
  255. package/dist/runners/src/mocha.d.ts +40 -0
  256. package/dist/runners/src/mocha.js +39 -0
  257. package/dist/runners/src/node-test.d.ts +28 -0
  258. package/dist/runners/src/node-test.js +44 -0
  259. package/dist/runners/src/publish.d.ts +30 -0
  260. package/dist/runners/src/publish.js +68 -0
  261. package/dist/schema/src/api.d.ts +800 -0
  262. package/dist/schema/src/api.js +201 -0
  263. package/dist/schema/src/cli-results.d.ts +210 -0
  264. package/dist/schema/src/cli-results.js +124 -0
  265. package/dist/schema/src/common.d.ts +53 -0
  266. package/dist/schema/src/common.js +42 -0
  267. package/dist/schema/src/index.d.ts +16 -0
  268. package/dist/schema/src/index.js +16 -0
  269. package/dist/schema/src/layer.d.ts +2273 -0
  270. package/dist/schema/src/layer.js +462 -0
  271. package/dist/schema/src/live-run.d.ts +123 -0
  272. package/dist/schema/src/live-run.js +30 -0
  273. package/dist/schema/src/manifest.d.ts +97 -2
  274. package/dist/schema/src/manifest.js +84 -0
  275. package/dist/schema/src/project.d.ts +20 -0
  276. package/dist/schema/src/project.js +10 -0
  277. package/dist/schema/src/report-sources.d.ts +29 -0
  278. package/dist/schema/src/report-sources.js +27 -1
  279. package/dist/schema/src/runs-index.d.ts +17 -1
  280. package/dist/schema/src/runs-index.js +15 -0
  281. package/dist/schema/src/server-config.d.ts +177 -0
  282. package/dist/schema/src/server-config.js +85 -0
  283. package/dist/schema/src/spec.d.ts +69 -0
  284. package/dist/schema/src/spec.js +58 -0
  285. package/dist/server/src/app.d.ts +72 -0
  286. package/dist/server/src/app.js +557 -0
  287. package/dist/server/src/auth.d.ts +41 -0
  288. package/dist/server/src/auth.js +59 -0
  289. package/dist/server/src/config.d.ts +83 -0
  290. package/dist/server/src/config.js +204 -0
  291. package/dist/server/src/dev.d.ts +12 -0
  292. package/dist/server/src/dev.js +89 -0
  293. package/dist/server/src/events.d.ts +29 -0
  294. package/dist/server/src/events.js +101 -0
  295. package/dist/server/src/host.d.ts +30 -0
  296. package/dist/server/src/host.js +115 -0
  297. package/dist/server/src/index.d.ts +18 -0
  298. package/dist/server/src/index.js +18 -0
  299. package/dist/server/src/live-runs.d.ts +52 -0
  300. package/dist/server/src/live-runs.js +229 -0
  301. package/dist/server/src/llms.d.ts +19 -0
  302. package/dist/server/src/llms.js +63 -0
  303. package/dist/server/src/main.d.ts +30 -0
  304. package/dist/server/src/main.js +114 -0
  305. package/dist/server/src/openapi.d.ts +56 -0
  306. package/dist/server/src/openapi.js +438 -0
  307. package/dist/server/src/problem.d.ts +8 -0
  308. package/dist/server/src/problem.js +39 -0
  309. package/dist/server/src/projects.d.ts +3 -0
  310. package/dist/server/src/projects.js +154 -0
  311. package/dist/server/src/settings-page.d.ts +9 -0
  312. package/dist/server/src/settings-page.js +93 -0
  313. package/dist/server/src/settings.d.ts +23 -0
  314. package/dist/server/src/settings.js +72 -0
  315. package/dist/server/src/site.d.ts +15 -0
  316. package/dist/server/src/site.js +78 -0
  317. package/dist/server/src/store-api.d.ts +28 -0
  318. package/dist/server/src/store-api.js +180 -0
  319. package/dist/server/src/validate.d.ts +71 -0
  320. package/dist/server/src/validate.js +197 -0
  321. package/dist/vitest/src/index.d.ts +27 -5
  322. package/dist/vitest/src/index.js +18 -5
  323. package/dist/webdriver/src/driver.d.ts +63 -11
  324. package/dist/webdriver/src/driver.js +57 -6
  325. package/dist/webdriver/src/index.d.ts +8 -0
  326. package/dist/webdriver/src/index.js +1 -0
  327. package/dist/webdriver/src/protocol.d.ts +75 -6
  328. package/dist/webdriver/src/protocol.js +79 -6
  329. package/docs/agents.md +137 -0
  330. package/docs/deploying.md +345 -0
  331. package/docs/dogfooding.md +175 -0
  332. package/docs/extending.md +242 -0
  333. package/docs/integrating.md +485 -0
  334. package/docs/reference/bun.md +141 -0
  335. package/docs/reference/cli.md +148 -0
  336. package/docs/reference/core.md +2432 -0
  337. package/docs/reference/detox.md +150 -0
  338. package/docs/reference/http-api.md +799 -0
  339. package/docs/reference/playwright.md +303 -0
  340. package/docs/reference/reports.md +7152 -0
  341. package/docs/reference/runners.md +169 -0
  342. package/docs/reference/schema.md +1762 -0
  343. package/docs/reference/server.md +825 -0
  344. package/docs/reference/vitest.md +45 -0
  345. package/docs/reference/webdriver.md +330 -0
  346. package/docs/report-backends.md +144 -0
  347. package/docs/report-contract.md +400 -0
  348. package/docs/reporting-issues.md +160 -0
  349. package/docs/runners.md +69 -0
  350. package/docs/server.md +321 -0
  351. package/docs/statecharts.md +643 -0
  352. package/llms-full.txt +4114 -0
  353. package/llms.txt +49 -0
  354. package/package.json +61 -11
  355. package/packages/core/README.md +2 -1
  356. package/packages/reports/README.md +65 -0
  357. package/packages/reports/STORAGE.md +4 -0
  358. package/packages/runners/README.md +6 -0
  359. package/packages/runners/compat/codecept.cjs +2 -0
  360. package/packages/runners/compat/jest.cjs +2 -0
  361. package/packages/runners/compat/mocha.cjs +2 -0
  362. package/packages/schema/README.md +14 -0
  363. package/packages/schema/schemas/atlas-change.schema.json +4 -0
  364. package/packages/schema/schemas/check-detail.schema.json +1032 -0
  365. package/packages/schema/schemas/checks-result.schema.json +26 -0
  366. package/packages/schema/schemas/command-error.schema.json +35 -0
  367. package/packages/schema/schemas/doctor-result.schema.json +60 -0
  368. package/packages/schema/schemas/effective-delta.schema.json +490 -0
  369. package/packages/schema/schemas/effective-evidence.schema.json +471 -0
  370. package/packages/schema/schemas/evidence-page.schema.json +271 -0
  371. package/packages/schema/schemas/hello-event.schema.json +26 -0
  372. package/packages/schema/schemas/init-plan.schema.json +347 -0
  373. package/packages/schema/schemas/layer-rows.schema.json +199 -0
  374. package/packages/schema/schemas/layer.schema.json +2888 -0
  375. package/packages/schema/schemas/live-run-start.schema.json +42 -0
  376. package/packages/schema/schemas/live-run-update.schema.json +93 -0
  377. package/packages/schema/schemas/live-run.schema.json +225 -0
  378. package/packages/schema/schemas/merge-result.schema.json +40 -0
  379. package/packages/schema/schemas/outline.schema.json +62 -0
  380. package/packages/schema/schemas/problem.schema.json +37 -0
  381. package/packages/schema/schemas/project-slug.schema.json +8 -0
  382. package/packages/schema/schemas/project.schema.json +22 -0
  383. package/packages/schema/schemas/projects.schema.json +33 -0
  384. package/packages/schema/schemas/publish-checks.schema.json +1413 -0
  385. package/packages/schema/schemas/publish-result.schema.json +168 -0
  386. package/packages/schema/schemas/ready.schema.json +68 -0
  387. package/packages/schema/schemas/report-event.schema.json +100 -0
  388. package/packages/schema/schemas/report-status.schema.json +370 -0
  389. package/packages/schema/schemas/server-config.schema.json +208 -0
  390. package/packages/schema/schemas/server-editable-settings.schema.json +132 -0
  391. package/packages/schema/schemas/server-info.schema.json +114 -0
  392. package/packages/schema/schemas/server-settings-snapshot.schema.json +285 -0
  393. package/packages/schema/schemas/spec-set.schema.json +103 -0
  394. package/packages/schema/schemas/store-listing.schema.json +29 -0
  395. package/packages/schema/schemas/store-write-result.schema.json +28 -0
  396. package/packages/schema/schemas/stream-result.schema.json +59 -0
  397. package/packages/server/README.md +43 -0
  398. package/packages/server/openapi.json +11021 -0
  399. package/packages/visualizer/README.md +32 -11
  400. package/packages/visualizer/bin/atlas-client.mjs +176 -0
  401. package/packages/visualizer/bin/atlas-visualizer.mjs +2 -175
  402. package/packages/visualizer/src/app/AppShell.tsx +32 -16
  403. package/packages/visualizer/src/app/report-sources.tsx +7 -4
  404. package/packages/visualizer/src/app/router.tsx +3 -3
  405. package/packages/visualizer/src/components/ActivityIndicator.module.css +4 -0
  406. package/packages/visualizer/src/components/ActivityIndicator.tsx +6 -0
  407. package/packages/visualizer/src/components/Header.module.css +101 -192
  408. package/packages/visualizer/src/components/Header.tsx +88 -13
  409. package/packages/visualizer/src/components/ProjectPicker.tsx +64 -0
  410. package/packages/visualizer/src/data/adapters/adapter.ts +5 -17
  411. package/packages/visualizer/src/data/adapters/index.ts +10 -1
  412. package/packages/visualizer/src/data/adapters/report-backend.test.ts +3 -2
  413. package/packages/visualizer/src/data/adapters/report-backend.ts +1 -1
  414. package/packages/visualizer/src/data/queries.ts +14 -7
  415. package/packages/visualizer/src/evidence/api.ts +16 -3
  416. package/packages/visualizer/src/evidence/artifacts.module.css +213 -0
  417. package/packages/visualizer/src/evidence/artifacts.tsx +272 -0
  418. package/packages/visualizer/src/evidence/check-detail.tsx +21 -4
  419. package/packages/visualizer/src/evidence/document-page.tsx +6 -2
  420. package/packages/visualizer/src/evidence/evidence-context.tsx +79 -12
  421. package/packages/visualizer/src/evidence/evidence.module.css +11 -1
  422. package/packages/visualizer/src/evidence/feature-page.tsx +7 -3
  423. package/packages/visualizer/src/evidence/model-page.tsx +6 -2
  424. package/packages/visualizer/src/evidence/project-home.tsx +45 -0
  425. package/packages/visualizer/src/evidence/recording-page.tsx +4 -1
  426. package/packages/visualizer/src/evidence/spec-index.tsx +2 -1
  427. package/packages/visualizer/src/evidence/spec-page.tsx +23 -16
  428. package/packages/visualizer/src/evidence/test-filter.tsx +21 -0
  429. package/packages/visualizer/src/find/Finder.tsx +1 -7
  430. package/packages/visualizer/src/find/open.ts +4 -0
  431. package/packages/visualizer/src/live/live-query.ts +21 -0
  432. package/packages/visualizer/src/live/live-runs.tsx +69 -0
  433. package/packages/visualizer/src/live/live-stream.ts +225 -0
  434. package/packages/visualizer/src/live/report-progress.ts +62 -0
  435. package/packages/visualizer/src/live/test-progress.tsx +34 -0
  436. package/packages/visualizer/src/runtime.ts +35 -0
  437. package/packages/visualizer/src/state/ui-store.ts +3 -2
  438. package/packages/visualizer/src/views/ChartPage.tsx +2 -0
  439. package/packages/visualizer/src/views/ChartTests.tsx +43 -0
  440. package/packages/visualizer/src/views/SidePanel.tsx +2 -0
  441. package/packages/visualizer/vite/atlas-files.test.ts +4 -4
  442. package/packages/visualizer/vite/atlas-files.ts +1 -5
  443. package/packages/visualizer/vite/atlas-server-proxy.ts +37 -0
  444. package/packages/visualizer/vite.config.ts +11 -3
package/docs/server.md ADDED
@@ -0,0 +1,321 @@
1
+ # The Atlas server
2
+
3
+ One node for multiple projects that **takes in results from every environment, keeps them, and shows everyone the
4
+ combined report, live**. It is a Bun API that serves a Vite-built single-page client: one host, one port, in
5
+ development and in production.
6
+
7
+ ```
8
+ laptop ──────┐ ┌── browsers (the client: reads /api, listens on /api/events)
9
+ CI job ──────┼── PUT /api/store/… POST /api/checks ─▶│
10
+ device lab ──┘ atlas report publish | stream │ atlas report status (the gate: asks /api/status)
11
+ │
12
+ backends: ./atlas-data (default), S3, other folders, another Atlas server
13
+ ```
14
+
15
+ - **Clients upload directly to it.** `atlas report publish`, `atlas report stream`, `createReporter` and a plain
16
+ `curl` all end up as layers on the server. Nothing else needs deploying: no bucket access for CI, no artifact
17
+ merging, no static site to rebuild.
18
+ - **Its default backend is its own filesystem** (`./atlas-data`). That folder is the whole state: back it up,
19
+ mount it on a volume, or put the state in a bucket instead ([backends](#backends)). Several backends are one
20
+ report, as ever.
21
+ - **The report is live.** Every client holds one server-sent-events connection; when results arrive the server
22
+ says so and each client reads only what changed ([streaming](#streaming)).
23
+ - **Layered, never partitioned**, as everywhere in Atlas: the server only stores immutable, content-addressed
24
+ layers and works out the report from them. Replace it, restore it, or run a second one on a copy of the data:
25
+ the same layers give the same report ([the contract](report-contract.md)).
26
+
27
+ - [Run it](#run-it) · [Configuration](#configuration) · [Backends](#backends) · [Tokens and roles](#tokens-and-roles)
28
+ - [Sending results](#sending-results) · [Streaming](#streaming) · [API reference](#api-reference)
29
+ - [What the server refuses](#what-the-server-refuses) · [Performance](#performance) · [Operating it](#operating-it)
30
+
31
+ ## Run it
32
+
33
+ ```sh
34
+ bun add -d @crvouga/atlas
35
+ export ATLAS_TOKEN=$(openssl rand -hex 24) # admin token; see "Tokens and roles"
36
+ bunx atlas serve # http://127.0.0.1:8080, data in ./atlas-data
37
+ ```
38
+
39
+ It runs on Bun and on Node 22.18 or later. The first start builds the client once into `~/.cache/atlas/site`
40
+ (or serves `./atlas-site`, or `--site <dir>`); the build is the same for every project, because the server writes
41
+ its own address book into the page (`window.__ATLAS_CONFIG__`).
42
+
43
+ **Working on Atlas or on the client:** `atlas dev` runs the server on a port of its own and the client's Vite dev
44
+ server in front of it. Vite proxies `/api` (server-sent events included) to the server, so the browser, the
45
+ uploaders and the client all use **one host**, exactly as in production, and a change to the client shows at once.
46
+
47
+ ```sh
48
+ bunx atlas dev --data .atlas-data # http://127.0.0.1:5180
49
+ ATLAS_STORE=http://127.0.0.1:5180 bunx atlas report publish --producer unit --junit out.xml
50
+ ```
51
+
52
+ For containers, Kubernetes and CI see [running Atlas for a project](deploying.md).
53
+
54
+ ## Configuration
55
+
56
+ Everything has a default, so no configuration is a working server for this machine. Settings come from
57
+ `atlas.server.json` (or `--config <file>` / `ATLAS_CONFIG`), then environment variables, then flags; a later one
58
+ wins. A mistake (an unknown key, a short token, two write targets, listening beyond this machine without tokens)
59
+ is said in words and the server does not start. The file has a published
60
+ [JSON Schema](https://github.com/crvouga/atlas/blob/main/packages/schema/schemas/server-config.schema.json)
61
+ (`@crvouga/atlas/schemas/server-config.schema.json`).
62
+
63
+ ```json
64
+ {
65
+ "$schema": "./node_modules/@crvouga/atlas/packages/schema/schemas/server-config.schema.json",
66
+ "host": "0.0.0.0",
67
+ "port": 8080,
68
+ "data": "./atlas-data",
69
+ "backends": [{ "id": "archive", "url": "s3://my-bucket/atlas/my-project?region=us-east-1", "label": "Archive" }],
70
+ "write": "data",
71
+ "auth": {
72
+ "tokens": [
73
+ { "name": "ci", "role": "publish", "tokenEnv": "ATLAS_CI_TOKEN" },
74
+ { "name": "dashboard", "role": "read", "tokenEnv": "ATLAS_DASHBOARD_TOKEN" }
75
+ ]
76
+ },
77
+ "include": { "branches": ["main"], "ignoreModified": true },
78
+ "refreshSeconds": 10,
79
+ "limits": { "maxObjectMiB": 256, "maxJsonMiB": 32, "maxStreams": 500 }
80
+ }
81
+ ```
82
+
83
+ <!-- generated:server-settings -->
84
+ <!-- /generated:server-settings -->
85
+
86
+ `ATLAS_HOST` and `ATLAS_PORT` win over `HOST` and `PORT`, which other tools on a machine often set for their own
87
+ purposes.
88
+
89
+ ## Backends
90
+
91
+ A backend is anything that implements `ObjectStore`: a folder, an S3-compatible bucket (`s3://`, `r2://`,
92
+ `gs://`), or **another Atlas server** (`https://…`, with `ATLAS_TOKEN` for it). The server reads every backend
93
+ and shows one report; one that is down is named in the client and skipped, and the server recovers by itself.
94
+
95
+ - **Uploads go to one backend**: the `data` folder, or the one named by `write` / `ATLAS_WRITE`. Name a bucket
96
+ to make it the system of record, so the server holds nothing a replacement would lose:
97
+
98
+ ```sh
99
+ ATLAS_STORE=project=s3://my-bucket/atlas/my-project ATLAS_WRITE=project atlas serve
100
+ ```
101
+
102
+ - **Layers written to a backend by something else** (CI with bucket access, `atlas report merge`) are found at
103
+ the next refresh. Layers uploaded through the server are in the report at once.
104
+ - **A server can be a backend of another**: a local server reading the project's shared one shows both.
105
+
106
+ ## Tokens and roles
107
+
108
+ | Role | May |
109
+ | --- | --- |
110
+ | `read` | look: the client, the report, `/api/status` |
111
+ | `publish` | also upload layers, specs and runs, and remove the scratch objects `atlas report doctor` writes |
112
+ | `admin` | all server operations |
113
+
114
+ Tokens are at least 12 characters (`openssl rand -hex 24`) and different from one another. Give them as
115
+ `ATLAS_TOKEN` (admin), `ATLAS_PUBLISH_TOKEN`, `ATLAS_READ_TOKEN`, or under `auth.tokens` in the file, preferably
116
+ with `tokenEnv` so the file holds no secret.
117
+
118
+ - **Sending a token:** `Authorization: Bearer <token>`. A browser's sign-in box also works for *looking* (any
119
+ user name, the token as the password). **Uploading needs the Bearer header, never the sign-in box**: a browser
120
+ sends those credentials by itself to any page that asks, which would let another site upload with them.
121
+ - **No tokens** means open, and then the server refuses to listen beyond this machine unless told
122
+ `--insecure` (for when a proxy in front does the signing in).
123
+ - `/healthz` and `/readyz` never need a token, so a load balancer can use them.
124
+ - Only this server's own clients need tokens; Atlas does not do accounts. Put it behind what your company already
125
+ uses (Cloudflare Access, an ALB with OIDC, `oauth2-proxy`, Tailscale) and TLS ends there too.
126
+
127
+ ## Sending results
128
+
129
+ Every route ends as the same immutable layers, so use what fits:
130
+
131
+ | From | How |
132
+ | --- | --- |
133
+ | a CI job or laptop, after the run | `ATLAS_STORE=https://atlas.example.com ATLAS_TOKEN=… atlas report publish --producer unit --junit out.xml --specs ./specs` |
134
+ | a suite that can print JSON lines | `my-suite --json \| atlas report stream --producer e2e --environment ios` |
135
+ | code | `createReporter(identity, { store: httpStore({ url, token }) })`, below |
136
+ | anything that can make a request | `POST /api/checks` with a JSON body |
137
+ | a folder of layers you already have | `atlas report merge --store ./layers --into https://atlas.example.com` |
138
+
139
+ `ATLAS_STORE` may name the server instead of a folder or bucket: **every `atlas report` command works against it**
140
+ (`publish`, `stream`, `merge --into`, `doctor`, `status`). Keys never go in the URL; put the token in
141
+ `ATLAS_TOKEN` (or the first line of the file named by `ATLAS_TOKEN_FILE`). **A token belongs to one server.** With
142
+ one server in `ATLAS_STORE`, `ATLAS_TOKEN` is its token; with several, name each (`lab=https://lab.example.com`) and give
143
+ it `ATLAS_TOKEN_LAB`, because `ATLAS_TOKEN` is then sent to none of them. A token is never sent over plain `http://`
144
+ beyond this machine (`ATLAS_TRUST_NETWORK=1` says the network is yours).
145
+
146
+ ### Streaming a run as it happens
147
+
148
+ `atlas report stream` reads one check per line (the fields of a check: `id`, `title`, `status`, and any
149
+ [evidence](extending.md)) and sends what has finished every second as a layer. A check sent first as `running` and
150
+ then as `passed` is two layers; the later, completed one wins, so the report follows the suite while it runs and
151
+ agrees with it when it ends. A layer that cannot be sent is kept and sent again, byte for byte the same, so a
152
+ retry never makes a second layer. It exits 1 if anything could not be sent.
153
+
154
+ ```ts
155
+ import { createReporter } from '@crvouga/atlas/reports/reporter';
156
+ import { httpStore } from '@crvouga/atlas/reports/http-store';
157
+
158
+ const reporter = createReporter(
159
+ { producer: 'web-e2e', environment: 'desktop-web', branch: process.env.GITHUB_REF_NAME ?? null },
160
+ { store: httpStore({ url: 'https://atlas.example.com', token: process.env.ATLAS_TOKEN }), flushMs: 1000 }
161
+ );
162
+ reporter.check({ id: 'checkout > pays', title: 'pays', status: 'running', specPath: 'specs/checkout.feature' });
163
+ // …
164
+ reporter.check({ id: 'checkout > pays', title: 'pays', status: 'passed', specPath: 'specs/checkout.feature', attachments: [{ name: 'end.png', data: png }] });
165
+ const { layers, checks, lost } = await reporter.close(); // never throws into the suite; `lost` counts what never arrived
166
+ ```
167
+
168
+ ### `POST /api/checks`
169
+
170
+ ```sh
171
+ curl -X POST https://atlas.example.com/api/checks -H "authorization: Bearer $ATLAS_TOKEN" -d '{
172
+ "producer": "smoke", "environment": "prod",
173
+ "checks": [{ "id": "login", "title": "can log in", "status": "passed" }]
174
+ }'
175
+ # 201 {"ok":true,"id":"…","checks":1}
176
+ ```
177
+
178
+ The body is [the checks file](integrating.md) (`recordedAtMs` defaults to now). Binary evidence goes in as
179
+ `base64`. Anything wrong is a `422` that says what.
180
+
181
+ ### The store protocol
182
+
183
+ `httpStore` is an `ObjectStore` over `/api/store`, so `publishChecks`, spec sets, run uploads and `copyStore` work
184
+ against a server unchanged. A client uploads **in dependency order**: blobs, then the manifest, then the catalog
185
+ entry that makes it visible. The server checks each step and says no to a catalog entry whose manifest is not
186
+ there (`409`), so nothing half-uploaded is ever shown.
187
+
188
+ ## Streaming
189
+
190
+ `GET /api/events` is a server-sent-events stream:
191
+
192
+ | Event | Data | Meaning |
193
+ | --- | --- | --- |
194
+ | `hello` | `{ version, ready, time, heartbeatMs? }` | Sent first. The report's current version. |
195
+ | `report` | `{ version, totalLayers, added: [{ id, producer, environment, branch, sha, recordedAtMs, counts }], addedTotal }` | Layers arrived. Enough to say who sent what, without asking. |
196
+ | `atlas:change` | `{ scope: "specs" \| "runs", files, runIds }` | New specs or runs. |
197
+ | `run` | A bounded `LiveRun` snapshot | Start, progress logs, heartbeat, completion, and interruption; no producer credentials. |
198
+ | `projects` | `{}` | Another project was discovered; refresh the project picker. |
199
+ | `ping` | `{}` | Every 15 s, so proxies keep the connection and the client can tell a quiet server from a dead connection. |
200
+
201
+ Run events update progress directly in the client cache. Report and specification events trigger coalesced reads of the affected data. Events arriving during a read cause a trailing refresh rather than cancelling that read. A client that missed some (a dropped
202
+ connection, a laptop that slept) loses nothing: it reads again when it reconnects, and the client also reads on a
203
+ slow timer behind the stream. Past `limits.maxStreams` viewers the server answers `503` with `Retry-After` and the
204
+ client polls instead; a client that stops reading is let go rather than held in memory.
205
+
206
+ ### Reading only what changed
207
+
208
+ `GET /api/reports/evidence/effective` is the whole report: only the layers that win a check, each with only the
209
+ rows that win, with an ETag. A client that holds a version asks `?since=<version>` and is sent **only the layers
210
+ that changed since, and the ids that no longer win anything** (`"delta": true`). Applying that gives exactly the
211
+ report the server would have sent whole (`applyEffectiveDelta`, tested against every earlier version of random
212
+ runs); a version the server does not remember, or an older server, answers with the whole report, which a client
213
+ takes as it always did. A result streamed into a 100 000-check report costs a client a few kilobytes.
214
+
215
+ ## API reference
216
+
217
+ <!-- generated:server-operations -->
218
+ <!-- /generated:server-operations -->
219
+
220
+ Errors are always [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem documents (`application/problem+json`: `type`,
221
+ `title`, `status`, and a `detail` that says what to do; `error` repeats `detail` for older clients) with a status that says
222
+ what to do: `401` send a token, `403` that token may not, `409` upload what this depends on first or you are changing
223
+ something immutable, `413` too large, `422` not valid, `503` still starting (see `Retry-After`).
224
+
225
+ **For agents:** a server describes itself. `GET /api/openapi.json` is the OpenAPI 3.1 description of everything above (also
226
+ shipped as `@crvouga/atlas/openapi.json`) and `GET /llms.txt` is a guide for an agent that was handed only the server's address;
227
+ neither needs a token. See [Atlas for AI agents](agents.md).
228
+
229
+ ## What the server refuses
230
+
231
+ What is checked is what would otherwise corrupt the report for everyone, and it is checked from the bytes in hand:
232
+
233
+ - **Everything is named by its content.** A manifest whose content does not hash to its id (it was edited, or written
234
+ by something that does not follow the format) is refused, as is one that is not a valid layer; so is a blob
235
+ (the digest of what is in it) or a spec file that is not what its name says.
236
+ - **Dependencies come first.** A manifest needs its blobs; a catalog entry needs its manifest; a spec set needs its
237
+ files; a run's catalog entry needs its manifest.
238
+ - **Layers are immutable.** The same bytes again are fine (a retry); other bytes under the same key are a `409`.
239
+ - **A run that did not pass its privacy scan is never shared.**
240
+ - **Only keys Atlas writes**, under `layers/`, without traversal, backslashes or control characters.
241
+ - **Size.** Bodies over `limits.maxObjectMiB` are refused as they arrive, not after they were held.
242
+ - **Attachment bytes are untrusted.** They are served only through one function that forces a safe type and a
243
+ sandbox, whatever the producer claimed.
244
+
245
+ ## Performance
246
+
247
+ `bun packages/server/bench/bench.ts [layers] [checks]` seeds a long history and measures the server on it. On a
248
+ laptop with 2 000 layers of 100 checks (200 000 results, 10 000 slots) and with 1 000 layers of 1 000 checks
249
+ (100 000 slots, a 22 MB report):
250
+
251
+ | | 10 000 slots | 100 000 slots |
252
+ | --- | --- | --- |
253
+ | Start: read and fold every layer | 0.4 s | 1.7 s |
254
+ | A refresh with nothing new | 4 ms | 4 ms |
255
+ | Upload one layer → every client told | 4 ms | 5 ms |
256
+ | `GET effective`, cached / `304` | 1 ms / 0.2 ms | 13 ms / 0.5 ms |
257
+ | `GET status`, cached for the version | 12 ms first | 70 ms first |
258
+
259
+ What makes it so, none of it at the cost of the contract:
260
+
261
+ - **A layer is read once.** The report is folded incrementally; only layers that still hold a winner are kept, so
262
+ memory follows the number of checks and not the age of the project.
263
+ - **A change costs one layer.** Each layer remembers the JSON of what it sends, the version is a hash of per-layer
264
+ digests, and a delta lists only layers that changed, so the work to take in a result does not grow with the
265
+ report.
266
+ - **An upload is read once.** A layer's manifest is parsed and verified when it arrives and handed to the report,
267
+ not read again; blob existence is asked 16 at a time.
268
+ - **The newest end of the catalog is all a refresh looks at**, with a full look now and then.
269
+ - **Compressed once.** The client's files are brotli-compressed once (gzip for clients that cannot read it), a
270
+ report version is gzipped once however many viewers fetch it, and events are encoded once for every client.
271
+ - **A small client.** The graph, the Gherkin parser, the recording player and the finder load when used: the
272
+ first page is about 250 KB over the wire.
273
+
274
+ ## Operating it
275
+
276
+ - **One node for multiple projects.** Local environments and CI select their project slug and talk to the same node, which tells
277
+ every client at once. Two servers on one backend both work and show the same report, but a client hears about
278
+ an upload only from the server that took it and about the other's at the next refresh, so give a team one.
279
+ - **State is the write backend.** Back up `./atlas-data` (or the bucket). Restoring it, or starting a new server
280
+ on it, shows the same report.
281
+ - **Stopping.** `SIGTERM` stops accepting, lets requests in flight finish, ends the event streams and exits 0.
282
+ - **Logs.** One JSON line per request (method, path, status, ms) to stdout, none for the probes. The first lines
283
+ say what it reads and which backend takes uploads, and warn when there are no tokens.
284
+ - **A backend goes down.** The server keeps serving what it has, names the backend in the client and in
285
+ `/readyz`, and recovers by itself. The gate fails closed: unknown is not a pass.
286
+ - **Upgrades.** The layer format is a stable, versioned contract; upgrade the server before the clients that
287
+ publish when a release says the format moved.
288
+
289
+ ## Configure from the client
290
+
291
+ Open **Settings** (`/admin`) and enter an admin token. The token stays in page memory. Read and publish tokens cannot edit settings; the browser sign-in box only grants reading. The API uses `Authorization: Bearer` and refuses cross-origin browser writes.
292
+
293
+ Use the Storage, Live updates, and Report rules forms to edit saved settings. Advanced includes request limits, logging, a JSON editor, and a read-only view of the active configuration. Save changes and restart Atlas to apply them. Atlas validates the configuration and atomically replaces `atlas.server.json`, preserving authentication and deployment settings. Changes apply at the next restart; environment variables and flags retain precedence. Concurrent edits require reloading the current revision. Data is never moved or removed by changing settings. Keep an old backend as a read source when moving future uploads to a new one. Embedded servers can provide their own settings persistence or manage configuration outside this API.
294
+
295
+ Native test reporters for CodeceptJS, Mocha, Jest and Node upload directly to this server; see [runner setup](runners.md). Every publisher uses the same server address and only a publish token. Storage credentials belong in the server environment.
296
+
297
+ ## Live test runs and heartbeats
298
+
299
+ The default native reporters and statechart runner stream tests and steps through `/api/live`, before the invocation finishes. Running tests appear inside their report with an animated status mark and the latest step. Open a test to read its progress log; its previous recorded result stays available separately. The **Running** filter narrows both the spec list and the test rows, and counts update as tests finish or time out. Statechart tests appear inside the chart and screen details. Selecting an archived run leaves live progress out of that historical view. Finished checks immediately enter normal immutable report layers. Operational progress is separate from historical winners, so a previous passed result never implies that the current invocation passed.
300
+
301
+ Start with `POST /api/live/<uuid>`, send ordered check deltas with `PATCH /api/live/<uuid>`, and renew the producer lease with `POST /api/live/<uuid>/heartbeat` every 5 seconds. Finish with a terminal update. Only the publisher token that registered the invocation can update it. Replayed sequences must have identical content; late writes cannot revive an expired invocation. `createLiveReporter` handles batching, retries, heartbeat timers and the final flush.
302
+
303
+ The server uses its own receipt clock. By default it interrupts a run after 30 seconds without a heartbeat (`ATLAS_RUN_TIMEOUT_SECONDS`). Heartbeats do not reset progress: after 15 minutes with no changed tests/steps, a separate stall timeout interrupts it (`ATLAS_RUN_STALL_SECONDS`, 0 disables). Both settings are editable in the admin UI. Healthy long tests continue renewing heartbeats; increase or disable the progress timeout for tests that legitimately make no observable progress for longer.
304
+
305
+ Interrupted runs retain completed results and show why the remaining work stopped. Missing outcomes never become passes. The server persists bounded summaries under `server/live/` in its write backend (an HTTP relay keeps operational leases in its own data folder; the remote server only accepts immutable report objects) and restores outstanding leases as interrupted after a server restart. S3 server credentials need GetObject/PutObject for that prefix; generate the updated `atlas report policy --role both` policy. Producers still receive no backend credentials. Live `run` events carry bounded snapshots directly into test rows, including step logs and interruption reasons. The legacy `atlas:change` announcement remains available for older clients; its optional `liveRunId` identifies lease updates. Reconnecting viewers re-fetch progress, reports, and specifications. With SSE connected, live progress uses a 60-second safety refresh; without SSE it polls every two seconds. Returning to the tab or coming back online also revalidates reports and progress.
306
+
307
+ In this repository, use `bun atlas` and `bun example:run` in two terminals. Every example Gherkin scenario executes, including sharing and renaming; `@unrecorded` no longer excludes tests.
308
+
309
+ ## Multiple projects on one node
310
+
311
+ Start the node with `atlas server` (or `bun atlas` in this repository). The server builds or finds the Atlas client and serves it with the API from one process. It never imports a consumer or an example. In this repository the node uses port 5180 and `./atlas-data`; `bun example:run` publishes the Todo app to project `todo-app`.
312
+
313
+ Set `ATLAS_PROJECT=todo-app` alongside `ATLAS_SERVER_URL` for native reporters and statechart runs. Report CLI commands accept `--project todo-app` (or `ATLAS_PROJECT`) with an HTTP store. Programmatic HTTP stores, live reporters and native reporter options accept `project: 'todo-app'`. Keep the same slug for the invocation's live progress, final results, spec sets and recordings. Slugs use 1–64 lowercase letters, digits or hyphens, starting and ending with a letter or digit.
314
+
315
+ Every project API is available as `/api/projects/<slug>/…`, for example `/api/projects/todo-app/checks`, `/api/projects/todo-app/events` and `/api/projects/todo-app/reports/evidence/effective`. The unprefixed API accepts optional `?project=todo-app` or `X-Atlas-Project: todo-app`. Conflicting selectors are refused. `GET /api/projects` discovers projects. A successful authenticated upload creates a project; reads of an unknown project return 404.
316
+
317
+ Projects isolate check identities, specs, run IDs, recordings, lease summaries, event streams and report caches. Identical layer bytes are preserved as uploaded. The node stores each project's objects under `projects/<slug>/` and discovery markers under `server/projects/`. Unqualified requests continue reading the legacy default namespace, advertised as **Default** when it has reports. Existing data is never implicitly moved. For S3, use the combined server policy (`atlas report policy --role both`) with access to both project namespaces and the catalog. An HTTP relay selects the same project on its upstream node and keeps discovery and leases locally.
318
+
319
+ The project picker sits at the top right; the header also provides spec dropdown breadcrumbs. Project selection lives in the page URL, so links and refreshes preserve it. Switching projects reloads the client, clearing report queries, media and event connections. Custom report-source preferences are keyed by the project's data URL. Project namespaces share the node's authentication roles: they are organizational partitions, not separate tenant authorization policies. Settings and server identity remain node-wide.
320
+
321
+ Project indexes are opened lazily; at most 64 are active in memory. An index with no active requests or event subscribers is closed after five idle minutes. Event subscribers share the node-wide stream limit. Project discovery lists only small catalog markers, never every report layer.