@crvouga/atlas 0.4.0 → 0.5.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 (379) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/README.md +178 -617
  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 +30 -1
  9. package/dist/core/src/config.d.ts +70 -4
  10. package/dist/core/src/config.js +62 -3
  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 +69 -16
  30. package/dist/core/src/run/execute.js +67 -16
  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 +83 -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 +52 -0
  120. package/dist/reports/src/http-store.js +145 -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 +364 -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/media-types.d.ts +11 -0
  147. package/dist/reports/src/media-types.js +22 -0
  148. package/dist/reports/src/namespace-guard.d.ts +10 -1
  149. package/dist/reports/src/namespace-guard.js +10 -1
  150. package/dist/reports/src/navigation.d.ts +12 -0
  151. package/dist/reports/src/navigation.js +7 -0
  152. package/dist/reports/src/object-io.d.ts +11 -4
  153. package/dist/reports/src/object-io.js +4 -4
  154. package/dist/reports/src/object-store-conformance.d.ts +27 -0
  155. package/dist/reports/src/object-store-conformance.js +87 -0
  156. package/dist/reports/src/object-store.d.ts +33 -24
  157. package/dist/reports/src/object-store.js +17 -24
  158. package/dist/reports/src/parse-with.d.ts +16 -0
  159. package/dist/reports/src/parse-with.js +14 -0
  160. package/dist/reports/src/performance-part.d.ts +25 -1
  161. package/dist/reports/src/performance-part.js +15 -1
  162. package/dist/reports/src/performance.d.ts +22 -2
  163. package/dist/reports/src/performance.js +12 -2
  164. package/dist/reports/src/persist.d.ts +12 -3
  165. package/dist/reports/src/persist.js +12 -3
  166. package/dist/reports/src/phone.d.ts +18 -1
  167. package/dist/reports/src/phone.js +18 -1
  168. package/dist/reports/src/protocol.d.ts +26 -4
  169. package/dist/reports/src/protocol.js +15 -4
  170. package/dist/reports/src/publication.d.ts +21 -1
  171. package/dist/reports/src/publication.js +21 -1
  172. package/dist/reports/src/publish-checks.d.ts +35 -0
  173. package/dist/reports/src/publish-checks.js +122 -0
  174. package/dist/reports/src/publish.d.ts +42 -9
  175. package/dist/reports/src/publish.js +36 -7
  176. package/dist/reports/src/read.d.ts +46 -16
  177. package/dist/reports/src/read.js +38 -14
  178. package/dist/reports/src/recording-bundle.d.ts +13 -2
  179. package/dist/reports/src/recording-bundle.js +4 -2
  180. package/dist/reports/src/recording-keys.d.ts +15 -1
  181. package/dist/reports/src/recording-keys.js +15 -1
  182. package/dist/reports/src/remote-report.d.ts +28 -7
  183. package/dist/reports/src/remote-report.js +15 -7
  184. package/dist/reports/src/report-cli.d.ts +51 -0
  185. package/dist/reports/src/report-cli.js +485 -0
  186. package/dist/reports/src/report-index.d.ts +99 -0
  187. package/dist/reports/src/report-index.js +337 -0
  188. package/dist/reports/src/report-loader.d.ts +10 -0
  189. package/dist/reports/src/report-loader.js +6 -0
  190. package/dist/reports/src/report-parts.d.ts +29 -3
  191. package/dist/reports/src/report-parts.js +37 -3
  192. package/dist/reports/src/report-status.d.ts +48 -0
  193. package/dist/reports/src/report-status.js +156 -0
  194. package/dist/reports/src/report-types.d.ts +73 -0
  195. package/dist/reports/src/report-types.js +3 -0
  196. package/dist/reports/src/reporter.d.ts +67 -0
  197. package/dist/reports/src/reporter.js +128 -0
  198. package/dist/reports/src/reports-explorer-route.d.ts +541 -0
  199. package/dist/reports/src/reports-explorer-route.js +11 -0
  200. package/dist/reports/src/reports-list-route.d.ts +9 -0
  201. package/dist/reports/src/reports-list-route.js +9 -0
  202. package/dist/reports/src/reports-route.d.ts +561 -22
  203. package/dist/reports/src/reports-route.js +11 -3
  204. package/dist/reports/src/reports-store.d.ts +19 -2
  205. package/dist/reports/src/reports-store.js +10 -2
  206. package/dist/reports/src/resilient-cache.d.ts +14 -1
  207. package/dist/reports/src/resilient-cache.js +8 -1
  208. package/dist/reports/src/retention.d.ts +17 -1
  209. package/dist/reports/src/retention.js +5 -1
  210. package/dist/reports/src/run-bundle.d.ts +63 -0
  211. package/dist/reports/src/run-bundle.js +159 -0
  212. package/dist/reports/src/s3-store.d.ts +79 -0
  213. package/dist/reports/src/s3-store.js +295 -0
  214. package/dist/reports/src/scenario.d.ts +14 -4
  215. package/dist/reports/src/scenario.js +5 -4
  216. package/dist/reports/src/sigv4.d.ts +72 -0
  217. package/dist/reports/src/sigv4.js +96 -0
  218. package/dist/reports/src/snapshot-bundle.d.ts +24 -1
  219. package/dist/reports/src/snapshot-bundle.js +27 -2
  220. package/dist/reports/src/snapshot-surface.d.ts +20 -9
  221. package/dist/reports/src/snapshot-surface.js +32 -19
  222. package/dist/reports/src/source-content.d.ts +23 -0
  223. package/dist/reports/src/source-content.js +30 -1
  224. package/dist/reports/src/source-entries.d.ts +13 -0
  225. package/dist/reports/src/source-entries.js +6 -0
  226. package/dist/reports/src/source-part.d.ts +15 -0
  227. package/dist/reports/src/source-part.js +8 -0
  228. package/dist/reports/src/source-reader.d.ts +11 -0
  229. package/dist/reports/src/source-reader.js +5 -0
  230. package/dist/reports/src/spec-evidence.d.ts +12 -2
  231. package/dist/reports/src/spec-evidence.js +6 -2
  232. package/dist/reports/src/spec-inventory.d.ts +15 -1
  233. package/dist/reports/src/spec-inventory.js +22 -1
  234. package/dist/reports/src/spec-link.d.ts +28 -0
  235. package/dist/reports/src/spec-link.js +70 -0
  236. package/dist/reports/src/spec-set.d.ts +63 -0
  237. package/dist/reports/src/spec-set.js +177 -0
  238. package/dist/reports/src/status.d.ts +10 -0
  239. package/dist/reports/src/status.js +5 -0
  240. package/dist/reports/src/store-prefix.d.ts +28 -0
  241. package/dist/reports/src/store-prefix.js +22 -0
  242. package/dist/reports/src/store-url.d.ts +89 -0
  243. package/dist/reports/src/store-url.js +160 -0
  244. package/dist/reports/src/test-catalog-entity.d.ts +11 -0
  245. package/dist/reports/src/test-catalog-entity.js +6 -0
  246. package/dist/reports/src/types.d.ts +6 -1
  247. package/dist/reports/src/use-report-nav.d.ts +35 -0
  248. package/dist/reports/src/use-report-nav.js +12 -0
  249. package/dist/schema/src/api.d.ts +799 -0
  250. package/dist/schema/src/api.js +201 -0
  251. package/dist/schema/src/cli-results.d.ts +210 -0
  252. package/dist/schema/src/cli-results.js +124 -0
  253. package/dist/schema/src/common.d.ts +53 -0
  254. package/dist/schema/src/common.js +42 -0
  255. package/dist/schema/src/index.d.ts +14 -0
  256. package/dist/schema/src/index.js +14 -0
  257. package/dist/schema/src/layer.d.ts +2273 -0
  258. package/dist/schema/src/layer.js +462 -0
  259. package/dist/schema/src/manifest.d.ts +95 -0
  260. package/dist/schema/src/manifest.js +84 -0
  261. package/dist/schema/src/report-sources.d.ts +28 -0
  262. package/dist/schema/src/report-sources.js +25 -0
  263. package/dist/schema/src/runs-index.d.ts +16 -0
  264. package/dist/schema/src/runs-index.js +15 -0
  265. package/dist/schema/src/server-config.d.ts +93 -0
  266. package/dist/schema/src/server-config.js +73 -0
  267. package/dist/schema/src/spec.d.ts +69 -0
  268. package/dist/schema/src/spec.js +58 -0
  269. package/dist/server/src/app.d.ts +62 -0
  270. package/dist/server/src/app.js +419 -0
  271. package/dist/server/src/auth.d.ts +41 -0
  272. package/dist/server/src/auth.js +59 -0
  273. package/dist/server/src/config.d.ts +83 -0
  274. package/dist/server/src/config.js +187 -0
  275. package/dist/server/src/dev.d.ts +12 -0
  276. package/dist/server/src/dev.js +80 -0
  277. package/dist/server/src/events.d.ts +29 -0
  278. package/dist/server/src/events.js +101 -0
  279. package/dist/server/src/host.d.ts +30 -0
  280. package/dist/server/src/host.js +115 -0
  281. package/dist/server/src/index.d.ts +16 -0
  282. package/dist/server/src/index.js +16 -0
  283. package/dist/server/src/llms.d.ts +19 -0
  284. package/dist/server/src/llms.js +63 -0
  285. package/dist/server/src/main.d.ts +30 -0
  286. package/dist/server/src/main.js +112 -0
  287. package/dist/server/src/openapi.d.ts +59 -0
  288. package/dist/server/src/openapi.js +374 -0
  289. package/dist/server/src/problem.d.ts +8 -0
  290. package/dist/server/src/problem.js +39 -0
  291. package/dist/server/src/site.d.ts +13 -0
  292. package/dist/server/src/site.js +43 -0
  293. package/dist/server/src/store-api.d.ts +28 -0
  294. package/dist/server/src/store-api.js +159 -0
  295. package/dist/server/src/validate.d.ts +71 -0
  296. package/dist/server/src/validate.js +197 -0
  297. package/dist/vitest/src/index.d.ts +27 -5
  298. package/dist/vitest/src/index.js +18 -5
  299. package/dist/webdriver/src/driver.d.ts +63 -11
  300. package/dist/webdriver/src/driver.js +57 -6
  301. package/dist/webdriver/src/index.d.ts +8 -0
  302. package/dist/webdriver/src/index.js +1 -0
  303. package/dist/webdriver/src/protocol.d.ts +75 -6
  304. package/dist/webdriver/src/protocol.js +79 -6
  305. package/docs/agents.md +137 -0
  306. package/docs/deploying.md +345 -0
  307. package/docs/dogfooding.md +175 -0
  308. package/docs/extending.md +242 -0
  309. package/docs/integrating.md +485 -0
  310. package/docs/reference/bun.md +141 -0
  311. package/docs/reference/cli.md +146 -0
  312. package/docs/reference/core.md +2426 -0
  313. package/docs/reference/detox.md +150 -0
  314. package/docs/reference/http-api.md +303 -0
  315. package/docs/reference/playwright.md +303 -0
  316. package/docs/reference/reports.md +7098 -0
  317. package/docs/reference/schema.md +1651 -0
  318. package/docs/reference/server.md +685 -0
  319. package/docs/reference/vitest.md +45 -0
  320. package/docs/reference/webdriver.md +330 -0
  321. package/docs/report-backends.md +144 -0
  322. package/docs/report-contract.md +400 -0
  323. package/docs/reporting-issues.md +160 -0
  324. package/docs/server.md +285 -0
  325. package/docs/statecharts.md +641 -0
  326. package/llms-full.txt +3506 -0
  327. package/llms.txt +47 -0
  328. package/package.json +32 -4
  329. package/packages/core/README.md +2 -1
  330. package/packages/reports/README.md +65 -0
  331. package/packages/reports/STORAGE.md +4 -0
  332. package/packages/schema/README.md +14 -0
  333. package/packages/schema/schemas/check-detail.schema.json +1032 -0
  334. package/packages/schema/schemas/checks-result.schema.json +26 -0
  335. package/packages/schema/schemas/command-error.schema.json +35 -0
  336. package/packages/schema/schemas/doctor-result.schema.json +60 -0
  337. package/packages/schema/schemas/effective-delta.schema.json +490 -0
  338. package/packages/schema/schemas/effective-evidence.schema.json +471 -0
  339. package/packages/schema/schemas/evidence-page.schema.json +271 -0
  340. package/packages/schema/schemas/hello-event.schema.json +22 -0
  341. package/packages/schema/schemas/init-plan.schema.json +347 -0
  342. package/packages/schema/schemas/layer-rows.schema.json +199 -0
  343. package/packages/schema/schemas/layer.schema.json +2888 -0
  344. package/packages/schema/schemas/merge-result.schema.json +40 -0
  345. package/packages/schema/schemas/outline.schema.json +62 -0
  346. package/packages/schema/schemas/problem.schema.json +37 -0
  347. package/packages/schema/schemas/publish-checks.schema.json +1413 -0
  348. package/packages/schema/schemas/publish-result.schema.json +168 -0
  349. package/packages/schema/schemas/ready.schema.json +68 -0
  350. package/packages/schema/schemas/report-event.schema.json +100 -0
  351. package/packages/schema/schemas/report-status.schema.json +370 -0
  352. package/packages/schema/schemas/server-config.schema.json +194 -0
  353. package/packages/schema/schemas/server-info.schema.json +114 -0
  354. package/packages/schema/schemas/spec-set.schema.json +103 -0
  355. package/packages/schema/schemas/store-listing.schema.json +29 -0
  356. package/packages/schema/schemas/store-write-result.schema.json +28 -0
  357. package/packages/schema/schemas/stream-result.schema.json +59 -0
  358. package/packages/server/README.md +43 -0
  359. package/packages/server/openapi.json +6699 -0
  360. package/packages/visualizer/README.md +21 -0
  361. package/packages/visualizer/src/app/AppShell.tsx +9 -2
  362. package/packages/visualizer/src/app/report-sources.tsx +6 -4
  363. package/packages/visualizer/src/app/router.tsx +3 -3
  364. package/packages/visualizer/src/components/Header.module.css +35 -0
  365. package/packages/visualizer/src/components/Header.tsx +40 -3
  366. package/packages/visualizer/src/data/adapters/adapter.ts +5 -17
  367. package/packages/visualizer/src/data/adapters/index.ts +10 -1
  368. package/packages/visualizer/src/evidence/api.ts +14 -2
  369. package/packages/visualizer/src/evidence/artifacts.module.css +213 -0
  370. package/packages/visualizer/src/evidence/artifacts.tsx +272 -0
  371. package/packages/visualizer/src/evidence/check-detail.tsx +3 -1
  372. package/packages/visualizer/src/evidence/evidence-context.tsx +74 -12
  373. package/packages/visualizer/src/evidence/spec-page.tsx +23 -16
  374. package/packages/visualizer/src/find/Finder.tsx +1 -7
  375. package/packages/visualizer/src/find/open.ts +4 -0
  376. package/packages/visualizer/src/live/live-stream.ts +204 -0
  377. package/packages/visualizer/src/runtime.ts +19 -0
  378. package/packages/visualizer/vite/atlas-server-proxy.ts +36 -0
  379. package/packages/visualizer/vite.config.ts +11 -3
@@ -0,0 +1,400 @@
1
+ # The report contract
2
+
3
+ This is the normative description of what Atlas reads and writes when it reports on specs of any
4
+ kind: Gherkin features, Quint models, DMN tables, Alloy structures, OpenSLO objectives, statecharts,
5
+ unit tests, linters, anything that ends in "this check passed or failed". If this page and the code
6
+ disagree, that is a bug: [file it](reporting-issues.md).
7
+
8
+ The contract is small on purpose. It is one document (the **layer**), one rule (how layers compose),
9
+ and one interface (the **backend** that stores them). Everything else, the viewer, the gate, the CLI,
10
+ is built on those three and can be replaced.
11
+
12
+ - [Concepts](#concepts)
13
+ - [The layer document](#the-layer-document)
14
+ - [How layers compose](#how-layers-compose)
15
+ - [Guarantees](#guarantees)
16
+ - [Statuses and the gate](#statuses-and-the-gate)
17
+ - [Backends](#backends)
18
+ - [Where bytes go](#where-bytes-go)
19
+ - [Versioning and stability](#versioning-and-stability)
20
+ - [Failure behaviour](#failure-behaviour)
21
+ - [Speed](#speed)
22
+ - [Other documents of the contract](#other-documents-of-the-contract)
23
+
24
+ ## Concepts
25
+
26
+ | Term | Meaning |
27
+ | --- | --- |
28
+ | **Check** | One thing a producer verified, with a stable `id` and a status: `passed`, `failed`, `running` or `not-run`. |
29
+ | **Layer** | One focused recording: the checks one **producer** ran, on one **environment**, from one version and branch, at one time. Immutable, addressed by its content. |
30
+ | **Producer** | A stable name for the suite or tool that makes layers: `gherkin`, `web-e2e`, `quint`, `openslo`. |
31
+ | **Environment** | The client that was checked: `desktop-web`, `mobile-web`, `ios`, `android`, or any other lowercase name. |
32
+ | **Slot** | The cell a check fills: producer, kind, spec, check id, environment and parameters. |
33
+ | **Backend** | A store that holds layers. Any [`ObjectStore`](#backends): a directory, a bucket, a device lab. |
34
+ | **Report** | The result of laying every layer of every backend over the current specs. Computed, never stored. |
35
+
36
+ Two ideas follow, and the rest of this page is their consequences.
37
+
38
+ 1. **A report is a function of the set of layers.** There is no "the run". Nothing partitions a
39
+ report by run, commit, branch, machine or backend; those are provenance written on each layer.
40
+ 2. **Partial runs are the normal way to build a report.** A node that runs one shard, a branch that
41
+ re-runs one scenario, a device lab that checks one environment: each publishes the layer of what it
42
+ ran. The report fills in as layers arrive, and no node needs to know about any other.
43
+
44
+ ## The layer document
45
+
46
+ `layers/manifests/<id>.json`, schema version **1**. The JSON Schema (draft 2020-12) is shipped as
47
+ [`@crvouga/atlas/schemas/layer.schema.json`](../packages/schema/schemas/layer.schema.json), and the
48
+ Zod schema as `LayerDocumentSchema` from `@crvouga/atlas/schema`.
49
+
50
+ ```json
51
+ {
52
+ "schemaVersion": 1,
53
+ "id": "9f2c…64 hex digits",
54
+ "producer": "gherkin",
55
+ "recordedAtMs": 1760000000000,
56
+ "sha": "0123456789abcdef0123456789abcdef01234567",
57
+ "branch": "main",
58
+ "runUrl": "https://ci.example.com/runs/42",
59
+ "environment": "ios",
60
+ "parameters": { "device": "iphone-15" },
61
+ "specs": [{ "path": "specs/join.feature", "domain": "list", "kind": "gherkin", "title": "Join a list", "fingerprint": "13:1a2b3c:4d5e6f" }],
62
+ "parts": [{
63
+ "id": "gherkin", "kind": "behavior", "title": "Gherkin", "recordedAtMs": 1760000000000, "sha": "0123…",
64
+ "checks": [
65
+ { "id": "specs/join.feature > Joins", "title": "Joins", "status": "passed", "detail": "", "specPath": "specs/join.feature", "measurements": [], "durationMs": 120 },
66
+ { "id": "specs/join.feature > Leaves", "title": "Leaves", "status": "failed", "detail": "expected 2 members, saw 3", "specPath": "specs/join.feature", "measurements": [{ "name": "latency", "value": 1400, "unit": "ms", "limit": 2500 }] }
67
+ ]
68
+ }],
69
+ "source": null,
70
+ "recording": null,
71
+ "bundle": {}
72
+ }
73
+ ```
74
+
75
+ | Field | Rule |
76
+ | --- | --- |
77
+ | `schemaVersion` | `1`. A layer without it, written before it existed, is version 1. |
78
+ | `id` | 64 lowercase hex digits: the SHA-256 of this document without `id`. Opaque to readers. The same checks recorded the same way give the same id, so publishing twice is harmless. |
79
+ | `producer` | Required, non-empty. Part of the [slot](#how-layers-compose). |
80
+ | `recordedAtMs` | When the checks ran, epoch milliseconds, `>= 0`. The newest completed result wins its slot. Take it from the test run, never from the moment of publishing, or a retried publish would claim to be newer than a later run. |
81
+ | `sha`, `branch`, `runUrl` | Provenance, or `null`. **Never part of the slot.** |
82
+ | `environment` | The client checked, or absent. Normalised to a lowercase slug: `iOS` → `ios`, `Desktop Web` → `desktop-web`, `iphone` → `ios`, `mobile browser` → `mobile-web`. Any other name is kept. Part of the slot. |
83
+ | `parameters` | Other string choices that make a result its own slot, such as `{ "device": "iphone-15" }`. A parameter named `environment` is read as the environment. Part of the slot. |
84
+ | `freshness` | `modified`: recorded from a checkout with uncommitted changes. `unknown`: origin not captured. Advisory only; see [Statuses](#statuses-and-the-gate). |
85
+ | `specs` | The specs the layer was recorded against, with a `fingerprint` of their text, so a reader can warn when a spec changed since. Optional. |
86
+ | `parts[].kind` | An open category (`behavior`, `performance`, `accessibility`, …). A new kind needs no change to any reader. Part of the slot. |
87
+ | `parts[].checks[]` | `id` (required, stable), `title`, `status`, `detail`, `specPath`, `measurements`, `durationMs`. |
88
+ | `tags`, `labels`, `links`, `attachments`, `annotations`, `extensions` | Evidence, on the layer, a part or a check: what a result carries beyond its outcome. All optional and open. See [evidence](#evidence) and [the guide](extending.md). |
89
+ | `checks[].steps`, `checks[].attempts` | A check's timeline, and every try when it was retried. Optional. |
90
+ | `checks[].specPath` | The spec the check belongs to, as `specs/<path inside the specs directory>`. Anything before the first `specs/` is dropped, so `packages/app/specs/join.feature` and `specs/join.feature` are the same spec. |
91
+ | `measurements[]` | `{ name, value, unit, limit? }`, finite numbers. A measurement over its `limit` is shown, and never fails the check by itself. |
92
+ | `source`, `recording`, `bundle` | Optional timelines and captured files (screenshots, frames) a suite recorded. `null` and `{}` for a layer of plain checks. |
93
+
94
+ **Unknown fields are allowed and preserved.** Every object in the schema is loose. A later minor
95
+ version may add fields; an older reader ignores them and a copy carries them along.
96
+
97
+ ### Evidence
98
+
99
+ A check, a part and a layer may carry evidence beyond their outcome. It is the part of the format meant to grow:
100
+ every field is optional, every object is open, every vocabulary is an open string, and none of it ever decides
101
+ a result. [Evidence: attachments, timelines, snapshots and everything else](extending.md) is the guide; the rules
102
+ are these.
103
+
104
+ | Field | Contents |
105
+ | --- | --- |
106
+ | `attachments[]` | `{ name, role?, contentType?, bundleKey? \| uri? \| text?, bytes?, sha256?, width?, height?, durationMs?, description?, at?, poster?, captions?, id?, extensions? }`. A screenshot, a video, a trace, a log, a DOM snapshot, a HAR, anything. `bundleKey` is a key in the layer's `bundle` (`attachments/<sha256>`); `uri` is elsewhere; `text` is a short inline value. |
107
+ | `steps[]` | `{ id?, parentId?, name, kind?, status?, offsetMs?, durationMs?, detail?, attachments?, annotations?, extensions? }`. A flat, ordered timeline, nested by `parentId`. |
108
+ | `annotations[]` | `{ type, message, level?, at?, source?, links?, extensions? }`. |
109
+ | `attempts[]` | `{ index, status, durationMs?, startedAtMs?, detail?, steps?, attachments?, annotations?, extensions? }`. |
110
+ | `tags[]`, `labels{}`, `links[]` | Facts to filter by. `labels` are facts about the result; unlike `parameters` they never make it a different slot. |
111
+ | `extensions{}` | Structured data under a name the producer owns (`com.example.coverage`). |
112
+
113
+ The rules, which hold for every version of the format:
114
+
115
+ 1. A new field is **optional**. No field is ever made required, renamed, removed or given a new meaning.
116
+ 2. **Every object is open.** Unknown fields are kept at every level, and a copy carries them.
117
+ 3. **Vocabularies are open.** `role`, `kind`, `type`, `level` and a step's `status` are strings with conventional
118
+ values; an unknown one is shown generically, never rejected.
119
+ 4. **Only `status`, `freshness` and `schemaVersion` are closed.** They are the whole of what decides a result.
120
+ 5. **Evidence never decides a result.** A reader that cannot read a piece of evidence drops that piece and keeps
121
+ the check, its status and its place in the report.
122
+ 6. A file is stored once, in the layer, by content: `attachments/<sha256>` in `layers/blobs/`. Identical files
123
+ attached anywhere in a layer share it, and the layer's id still depends only on what was recorded.
124
+ 7. A path inside a report is data from a test run. A publisher reads the files it names only from the report's own
125
+ folder and from folders the person chose, never a hidden file or a link out.
126
+ 8. Attachment bytes are untrusted. They are served with the type the producer gave (when it is a well-formed media
127
+ type), never sniffed, under a sandboxing content-security policy, with `cross-origin-resource-policy:
128
+ same-origin`, and a `uri` is followed only if it is `http` or `https`.
129
+
130
+ ### Choosing a check id
131
+
132
+ The check `id` is the one thing only the producer can get right, and everything depends on it: the
133
+ newer result replaces the older one only if both used the same id.
134
+
135
+ - Make it **stable** across runs: derive it from the check's identity (file, suite, name), never from
136
+ an index, a timestamp, a random seed or a path's position in a plan.
137
+ - Make it **unique** within the layer. Publishing refuses a layer with a repeated id. When importing
138
+ CTRF or JUnit, a repeated id gets `#2`, `#3`, … so no check is silently counted as another.
139
+ - Keep it **human**. It is what the gate prints.
140
+ - Changing an id retires the old check: its last result stays in the report until the layers that
141
+ hold it are left out. Rename deliberately.
142
+
143
+ Atlas's own importers build ids this way:
144
+
145
+ | Source | Check id | `specPath` |
146
+ | --- | --- | --- |
147
+ | CTRF | `filePath > suite… > name` (parts that exist) | `filePath`, when present |
148
+ | JUnit XML | `file or classname > name` | the `file` attribute, when present |
149
+ | Atlas run (`manifest.json`) | the path's `key`, which the same start and events give in every run | the root chart's file |
150
+ | `publishChecks` / `--checks` | whatever you give | whatever you give |
151
+
152
+ ## How layers compose
153
+
154
+ A **slot** is the tuple
155
+
156
+ ```
157
+ (producer, kind, specPath, check id, environment, parameters)
158
+ ```
159
+
160
+ with `parameters` compared as sorted key/value pairs. Branch, commit, backend, layer id and run are
161
+ **not** in the slot. Each slot shows exactly one result: the winner among every layer that filled it.
162
+
163
+ The winner is decided by these rules, in order:
164
+
165
+ 1. A **completed** result (`passed` or `failed`) beats an **unfinished** one (`running` or `not-run`),
166
+ whatever their times. An unfinished layer never erases a completed outcome.
167
+ 2. Between results of the same completeness, the **newer** `recordedAtMs` wins.
168
+ 3. At the same time, **`failed` beats `passed`**, and `running` beats `not-run`. A tie fails.
169
+ 4. At the same status, the greater `layerId|backendId` wins, so no input order can change the answer.
170
+
171
+ Consequences you can rely on:
172
+
173
+ - Desktop web, mobile web, iOS and Android results of the same check sit side by side.
174
+ - A layer that ran one scenario leaves every other scenario as the newest layer that ran it.
175
+ - Re-running a failing check on any branch and passing replaces the failure. Re-running and failing
176
+ replaces the pass.
177
+ - A spec in the current specs with no layer at all stays visible as having no results. A spec that
178
+ was removed keeps its evidence, outside the outline.
179
+ - An immutable layer held by several backends is **one** layer, reported by the backend whose id sorts
180
+ first.
181
+
182
+ Leaving a layer, backend, environment, branch or version out of the input is how earlier outcomes are
183
+ inspected.
184
+
185
+ ### What takes part
186
+
187
+ By default **every layer takes part**, wherever it was recorded: that is what a unified report is. The
188
+ publisher records where it came from, and the reader chooses what to include:
189
+
190
+ - `freshness` is set by the publisher from git: `modified` when the worktree has uncommitted edits to
191
+ tracked files, `unknown` outside git, and absent for a CI checkout or a clean worktree at its commit.
192
+ Untracked files do not count. A modified layer takes part with a warning and is never mistaken for the
193
+ commit's own result.
194
+ - `atlas report status` and `atlas serve` (and `GET /api/status?branch=&ignoreModified=1` on a server) take `--branch <name>` (repeatable) and `--ignore-modified`
195
+ (`ATLAS_ONLY_BRANCH`, `ATLAS_IGNORE_MODIFIED`). They only choose which layers are read; the rules above are
196
+ unchanged. A pull request's gate is `--branch "$PR_BRANCH" --branch main --ignore-modified`: the branch
197
+ laid over `main`, so what it did not re-run is inherited.
198
+
199
+ ## Guarantees
200
+
201
+ These are tested (`packages/reports/tests/composition-laws.test.ts`, over random partial runs) and
202
+ are part of the contract:
203
+
204
+ | Guarantee | Meaning |
205
+ | --- | --- |
206
+ | **Order independence** | The report does not depend on the order backends or layers are read in. |
207
+ | **Partition independence** | Splitting the same layers across any number of backends, machines or branches, with any overlap, gives the same report. This is what makes partial runs on separate nodes safe. |
208
+ | **Idempotence** | A layer present twice, in one backend or several, counts once. Publishing the same checks again writes the same layer. |
209
+ | **Monotonic** | A layer changes only the slots it fills. Adding a layer never changes a result it did not record. |
210
+ | **Deletion-free** | Nothing in the contract removes or rewrites a layer. Retention is the operator's choice. |
211
+ | **Unknown is not a pass** | A layer that cannot be read, a backend that is down, and a check that is `running` or `not-run` never count as passing. The gate fails closed. |
212
+ | **Aggregation by copying** | Layers are immutable and never share a key with different bytes, so combining stores is copying files (`atlas report merge`, `cp -R`, `rsync`, a CI artifact download). Copying is idempotent and commutative. |
213
+
214
+ ## Statuses and the gate
215
+
216
+ | Status | Meaning | Counts as |
217
+ | --- | --- | --- |
218
+ | `passed` | The check ran and its expectation held. | Pass |
219
+ | `failed` | The check ran and its expectation did not hold. | Fail |
220
+ | `running` | The check has started and not finished. | Not done: fails the gate |
221
+ | `not-run` | The check was skipped, pending, blocked, or never reached. | Nothing: never a pass, never a failure |
222
+
223
+ Tools map to these four. CTRF `skipped`, `pending` and `other`, and JUnit `<skipped>`, are `not-run`.
224
+ An Atlas path that passed on retry (`flaky`) is `passed`, with the detail "Passed on retry (flaky)".
225
+
226
+ Timings that only qualify a check (parts of kind `performance`) are not counted as checks when
227
+ anything else is recorded. A measurement over its allowance is reported and never fails a check.
228
+
229
+ **Freshness never changes a status.** A layer whose spec fingerprint no longer matches, whose
230
+ checkout was modified, or whose related code changed still shows its recorded outcome, with a
231
+ warning ("Potentially outdated: spec changed"). Atlas favours quick, possibly stale evidence over
232
+ none, and never invents a result.
233
+
234
+ ### `atlas report status`
235
+
236
+ The gate passes (exit 0) only when **all** of these hold:
237
+
238
+ - at least one check has completed (`passed` or `failed`): a report where everything was skipped has
239
+ no evidence;
240
+ - no slot's winning result is `failed`;
241
+ - no slot's winning result is `running`;
242
+ - every backend and every layer could be read (a notice is a failure);
243
+ - every environment named by `--require-environments` has at least one check;
244
+ - with `--require-all-specs`, every spec in `--specs <dir>` has at least one result.
245
+
246
+ Exit codes: `0` the report passes, `1` it does not, `2` the command itself is wrong (a store
247
+ directory that does not exist, a missing flag). A CI job can tell a broken invocation from a red report.
248
+
249
+ `--format json` prints a [`ReportStatus`](../packages/schema/schemas/report-status.schema.json)
250
+ (`schemaVersion: 1`): `ok`, `reasons`, `checks`, `byEnvironment`, `layers`, `backends`, `notices`,
251
+ `specsWithoutEvidence`, `specs` (each spec's counts, per environment) and `failing`. The text and markdown formats are for people and are not a
252
+ contract; parse the JSON.
253
+
254
+ ## Backends
255
+
256
+ A backend is any implementation of `ObjectStore` (`@crvouga/atlas/reports/object-store`), seven
257
+ async methods over keys and bytes:
258
+
259
+ | Method | Rule |
260
+ | --- | --- |
261
+ | `get(key)` | The object with its `contentType` and `size`, or `null` when missing. Transport errors throw. |
262
+ | `put(key, bytes, contentType)` | Idempotent: writing a key twice overwrites without error. The content type round-trips. |
263
+ | `head(key)` | `true`/`false`. Never throws for a missing key. |
264
+ | `delete(key)`, `deleteMany(keys)` | A missing key is a no-op. |
265
+ | `getUri(key)` | A URL the host can load directly, or `null`. |
266
+ | `listKeys(prefix?)` | Keys in code-unit order, optionally filtered by prefix. |
267
+ | `withPrefix(prefix)` | A store scoped to a prefix. Keys relative to it. Prefixes compose. |
268
+
269
+ It needs nothing else: no transactions, no rename, no locks, no conditional writes. That is what
270
+ lets a plain bucket, a directory and a device lab compose.
271
+
272
+ Atlas ships two backends, both named by one URL (`openStore`, `--store`, `ATLAS_STORE`):
273
+
274
+ - `directoryStore(dir)` (`@crvouga/atlas/reports/directory-store`): atomic writes, for a CI workspace, a
275
+ mount or a laptop.
276
+ - `s3Store({ bucket, prefix, region, endpoint })` (`@crvouga/atlas/reports/s3-store`): AWS S3 and every store
277
+ that speaks its protocol, such as R2 and MinIO, written without an SDK. `s3://bucket/folder?region=…&endpoint=…`.
278
+ Requests are signed with Signature Version 4, retried with backoff on throttling and server errors,
279
+ capped in flight, and follow a bucket's region. Credentials are never in a URL: they come from the
280
+ environment the way an AWS SDK finds them. It is tested against AWS's published signing vectors and a
281
+ live S3 server in CI. On S3 a missing object answers 403 instead of 404 for an account that cannot list
282
+ the bucket, so publishers need `s3:ListBucket` too (`atlas report policy` prints the permissions).
283
+
284
+ For anything else, write an adapter and prove it:
285
+
286
+ ```ts
287
+ import { verifyObjectStore } from '@crvouga/atlas/reports/object-store-conformance';
288
+
289
+ const { ok, failures } = await verifyObjectStore(myS3Store); // writes under a scratch prefix, cleans up
290
+ ```
291
+
292
+ Several backends are one report: pass them all to `combineBackends`, to the viewer
293
+ (`reportsHost`), or to the CLI (`--store` for directories, `--backends <module>` for the rest). A
294
+ backend that cannot be reached is named in the notices and leaves the others usable; asking again
295
+ retries it.
296
+
297
+ ## Where bytes go
298
+
299
+ All keys are relative to the backend. Layers are written **bottom up**, so a reader never meets an
300
+ entry whose content has not arrived:
301
+
302
+ A layer's checks are the contract. Next to them the store holds the two other things a whole project
303
+ needs, written the same way (content first, catalog entry last, nothing overwritten):
304
+
305
+ - **Spec sets** (`layers/specs/…`): the specs of one commit, so a viewer needs no checkout. A spec set is
306
+ `{ schemaVersion, id, branch, sha, recordedAtMs, freshness?, files: [{ path, digest, fingerprint, kind,
307
+ title, domain, bytes }] }` ([`spec-set.schema.json`](../packages/schema/schemas/spec-set.schema.json)). Its
308
+ id is the digest of the branch, commit and file digests, so one commit's specs are one set however many
309
+ nodes publish them. Bodies are `layers/specs/blobs/<sha256>.txt`, shared by every set that holds them.
310
+ The outline a report is laid over is the **newest set from `main`** (then `master`, then the newest);
311
+ a set from a modified checkout is used only if no committed set exists.
312
+ - **Run bundles** (`layers/runs/<run>/…` and `layers/runs/index/…`): a finished statechart run with its
313
+ screenshots and clips, for the viewer. Uploaded only when the run's privacy scan passed; the manifest is
314
+ written last, then the catalog entry.
315
+
316
+ The keys of a layer, bottom up:
317
+
318
+ 1. `layers/blobs/<sha256>.gz`: recorded files (screenshots, frames), content-addressed and shared
319
+ between layers. Skipped entirely for a layer of plain checks.
320
+ 2. `layers/manifests/<id>.json`: the layer document, written once.
321
+ 3. `layers/index/<inverted-time>-<id>.json`: `{"id": "<id>"}`, the catalog entry, written last. The
322
+ time is inverted so a listing is newest first.
323
+
324
+ Two producers never write different bytes to one key: every key is derived from content. A crash
325
+ between steps leaves unreferenced blobs, never a catalog entry without its layer.
326
+
327
+ Everything under `layers/` is immutable and addressed by content, so `copyStore` and `atlas report merge`
328
+ carry spec sets and runs along with layers.
329
+
330
+ Stores written before layers existed (`commits/<sha>/…`, `versions/…`, `index/…`) are read in place
331
+ and composed with layers; Atlas never rewrites them. `copyStore` and `atlas report merge` copy
332
+ `layers/` only, because commit-keyed objects are mutable.
333
+
334
+ ## Versioning and stability
335
+
336
+ - `schemaVersion` is the **major** version of the layer document. This is version 1.
337
+ - Within a version, changes are **additive**: a new optional field, a new open `kind`. Readers ignore
338
+ unknown fields, so an older Atlas reads a newer minor version.
339
+ - The closed vocabularies, `status` and `freshness`, change only with a new major version. A reader
340
+ never has to guess what an unknown status means.
341
+ - A reader reads every version **at or below its own**. A layer of a newer major version is refused by
342
+ name ("Written with layer schema version 2; … Upgrade @crvouga/atlas") and shows up as a notice. It
343
+ is never half understood and never counted as a pass.
344
+ - A breaking change to the document, to the [slot](#how-layers-compose) or to the
345
+ [winner rules](#how-layers-compose) is a major version of the contract **and** a major release of
346
+ `@crvouga/atlas` (a `!` commit). It also ships a migration note.
347
+ - Adding evidence (a new optional field, a new open vocabulary value, a new extension namespace) is a minor
348
+ change and needs no new `schemaVersion`. `packages/reports/tests/fixtures/layer-v1-extended.json` carries every
349
+ kind of evidence, and `layer-extensibility.test.ts` proves unknown fields survive at every level and that a
350
+ reader of the first version of the format still reads it.
351
+ - The contract is guarded by `packages/reports/tests/fixtures/layer-v1.json`, a layer that must keep
352
+ parsing, and by the emitted JSON Schemas, which a test keeps equal to the Zod schemas. **Never edit
353
+ the fixture to make a test pass.**
354
+
355
+ ## Failure behaviour
356
+
357
+ | What happens | What Atlas does |
358
+ | --- | --- |
359
+ | A backend is unreachable | Named in `notices` and in the backend list as unavailable; every other backend is used. The gate fails. |
360
+ | A layer is not JSON, fails the schema, or is a newer major version | Skipped with a notice naming the layer and why. The gate fails. |
361
+ | A catalog entry names a layer that is missing | A notice; the gate fails. |
362
+ | Two layers disagree | The [winner rules](#how-layers-compose) decide, the same way everywhere. |
363
+ | A publish is interrupted | Unreferenced blobs may remain. No partial layer is ever visible. Publish again. |
364
+ | A publish is retried | The same layer; nothing changes. |
365
+ | Two nodes publish at once | They write different keys, or identical bytes to the same key. |
366
+ | A required field is missing at publish | `publishChecks` throws, naming each problem by its path. Nothing is written. |
367
+ | A check id repeats within a layer | `publishChecks` throws. Importers number repeats (`#2`). |
368
+
369
+ ## Speed
370
+
371
+ - Publishing a layer of plain checks is one `head` and two small writes.
372
+ - A report is one linear pass over the checks of every layer. Reading dominates it: each layer is
373
+ one catalog read and one manifest read, a few at a time (16 by default, 64 for a node on S3).
374
+ - A long-lived reader does not redo that pass. `ReportIndex` reads each immutable layer once, folds it into
375
+ the winning result of every slot it fills, and drops it unless it still holds a winner, so memory follows
376
+ the number of checks (plus a few bytes for each layer ever read), not the age of the project, and a refresh
377
+ with nothing new is one request for the newest page of the catalog. Folding is exact in any order because the winner
378
+ rule is a total order; a test proves the incremental report equals combining every layer from scratch.
379
+ The node sends the viewer only that: one request, only winning rows, with an ETag.
380
+ - Measured on a laptop with a directory backend: 300 layers of 50 checks in about 0.2 s; 1,000 layers
381
+ of 100 checks, each recorded against 1,000 specs (100,000 results), in about 0.9 s. Composing two
382
+ thousand layers of 100 checks (200,000 results) already in memory takes about 0.3 s.
383
+ `performance.test.ts` holds budgets ten times looser, to catch an accidental quadratic step on a
384
+ slow machine.
385
+ - A directory backend lists only the directories a prefix can reach. The viewer polls a small index,
386
+ reads an immutable layer once and caches it forever, and loads recordings only when opened.
387
+
388
+ ## Other documents of the contract
389
+
390
+ | Document | Schema | Purpose |
391
+ | --- | --- | --- |
392
+ | Layer | `layer.schema.json` | What a producer publishes. This page. |
393
+ | Publish checks | `publish-checks.schema.json` | The input of `publishChecks` and `atlas report publish --checks`: a layer without the parts the publisher fills in. |
394
+ | Report status | `report-status.schema.json` | The output of `atlas report status --format json`. |
395
+ | Spec set | `spec-set.schema.json` | The specs of one commit, as published beside the layers. |
396
+ | Run manifest | `manifest.schema.json` | What `atlas run` writes for a statechart run. Publish it as a layer with `--manifest`. |
397
+ | Runs index, sources, change notices | `runs-index`, `report-sources`, `atlas-change` | How the viewer finds runs. See [report backends](report-backends.md). |
398
+ | CloudEvents | `cloud-event.schema.json` | Business events a run observed. |
399
+
400
+ All of them are JSON Schema 2020-12 and ship in the package at `@crvouga/atlas/schemas/<name>.schema.json`.
@@ -0,0 +1,160 @@
1
+ # Reporting issues
2
+
3
+ How people and coding agents in a project that uses Atlas report a bug or a missing feature. Issues
4
+ are the only way a project changes Atlas: maintainers and agents in this repository pick them up,
5
+ fix them here, and the fix reaches you as a new version ([dogfooding.md](dogfooding.md)).
6
+
7
+ This guide is written for coding agents. Read it top to bottom once, then use
8
+ [Quick reference](#quick-reference) as the checklist every time you file.
9
+
10
+ ## Quick reference
11
+
12
+ | You found | Kind | Title | Template |
13
+ | --- | --- | --- | --- |
14
+ | Atlas crashes, gives a wrong result, contradicts its README or docs, or does not install, build or type-check | `bug` | `[<part>] bug: <what breaks>` | [bug.md][t-bug] |
15
+ | Atlas lacks an API, option, driver capability, CLI flag, schema field or visualizer view you need | `feature` | `[<part>] feature: <what is missing>` | [feature.md][t-feature] |
16
+
17
+ `<part>` is the part of `@crvouga/atlas` the issue is about: `core` (the main import and the
18
+ `atlas` command), or the import path after the package name: `playwright`, `bun`, `webdriver`,
19
+ `detox`, `vitest`, `schema`, `reports`; `visualizer` for the `atlas-visualizer` command. Use `core`
20
+ when you cannot tell.
21
+
22
+ 1. [Search for an existing issue](#1-search-first). Comment on it instead of opening a duplicate.
23
+ 2. [Leave out](#never-include) secrets, personal data and anything that identifies your product.
24
+ 3. Copy the kind's template, fill in every section, and save it to a file.
25
+ 4. File it:
26
+
27
+ ```sh
28
+ gh issue create --repo crvouga/atlas \
29
+ --title "[core] bug: plan skips a transition guarded by a parallel region" \
30
+ --label agent-reported,bug \
31
+ --body-file issue.md
32
+ ```
33
+
34
+ 5. Give your human the issue URL, and note the workaround you are using next to the code it is in.
35
+
36
+ File one issue per bug or missing feature, so each can be fixed, tested and closed on its own. The
37
+ title prefix alone is enough: a workflow applies the `agent-reported` and kind labels from it,
38
+ including when your account cannot set labels.
39
+
40
+ ## When to file, and when not to
41
+
42
+ File when:
43
+
44
+ - **bug**: Atlas throws or hangs; lint, plan or a run gives a result the spec does not support;
45
+ a driver taps, types, waits or records differently from what its README says; the manifest or a
46
+ report does not match its JSON Schema; the visualizer shows something other than what the run
47
+ recorded; the package fails to install, import, build or type-check.
48
+ - **feature**: you need something Atlas does not have: a spec construct, a config option, a driver
49
+ capability, a CLI flag, a field in the manifest, a view in the visualizer, an export format. Check
50
+ that part's README and `docs/` first.
51
+
52
+ Do not file when:
53
+
54
+ - The cause is your own spec, implementation or configuration: a recogniser that matches the wrong
55
+ screen, a missing `implementation`, state left over from an earlier run. `atlas lint` findings
56
+ about your charts are Atlas working.
57
+ - You are on an old version. Upgrade (`bun update @crvouga/atlas --latest`) and try again first.
58
+ - You only suspect a problem. Reproduce it, and say what you ran and what happened.
59
+
60
+ Never fix Atlas from your project instead of filing: no edits to `node_modules`, no patch, no fork.
61
+
62
+ ## Never include
63
+
64
+ Issues are public. Before you file, remove:
65
+
66
+ - API keys, tokens, passwords, cookies and signed URLs. Replace each with `<redacted>`.
67
+ - Real personal data. Use obviously fake values (`jane@example.com`).
68
+ - Your product: its name, real screen and event names, screenshots, clips and run manifests, and
69
+ your project's source. Rename states and events (`Screen A`, `Submits the form`) and reduce the
70
+ spec until it shows only the problem.
71
+
72
+ When you cannot show the problem without something on this list, stop and ask your human.
73
+
74
+ ## 1. Search first
75
+
76
+ ```sh
77
+ gh issue list --repo crvouga/atlas --state all --search "<part> <what breaks or is missing> in:title,body"
78
+ ```
79
+
80
+ If an open issue covers the same thing, add a comment with your evidence (versions, reproduction,
81
+ the behavior you need) instead of opening a duplicate. A 👍 on a `feature` issue also counts as
82
+ demand. If a closed issue covers it, check that you are on the version its release comment names,
83
+ or a later one, before you reopen it.
84
+
85
+ ## 2. Bugs: reduce to a reproduction
86
+
87
+ A bug report is ready when someone can reproduce it from a clean install without your project.
88
+
89
+ - **Versions.** The exact installed version (`bun pm ls`, or your package manager's equivalent), Bun
90
+ or Node, the driver and its browser or device.
91
+ - **The smallest spec.** One chart with only the states and transitions that show the problem.
92
+ `atlas lint --specs <dir>` needs no config and no implementation; say so when that alone
93
+ reproduces it.
94
+ - **The smallest config and command.** `atlas.config.ts` with a `functionDriver` or `httpDriver`
95
+ when the bug is not about a browser; the exact command line.
96
+ - **Expected and actual.** Quote the README or doc sentence Atlas contradicts. Paste the error and
97
+ stack trace, the lint finding, or the wrong part of the manifest, not the whole file.
98
+ - **Workaround.** What your project does in the meantime, or that you are blocked.
99
+
100
+ ## 3. Features: describe the behavior
101
+
102
+ Describe what Atlas should do, not how your project is built.
103
+
104
+ - **What is missing.** What you are trying to specify, run or see, and where Atlas stops.
105
+ - **Proposed surface.** The option, API, spec metadata, CLI flag or view as you would want to use
106
+ it. A short code block is better than a paragraph.
107
+ - **Behaviors.** Numbered Given / When / Then statements, each one checkable by a test in this
108
+ repository, covering the failures as well as the happy path:
109
+
110
+ ```text
111
+ B1. Given a chart with a `<parallel>` state, when I run `atlas plan --state Sync`, then only
112
+ paths that enter the Sync region are planned.
113
+ B2. Given `--state` names a state that does not exist, then the command exits 1 and names the
114
+ states it could have been.
115
+ ```
116
+
117
+ - **Out of scope.** What you do not need, so the feature stays small.
118
+
119
+ A feature with a clear surface and concrete behaviors is ready to build. One that only names a goal
120
+ waits, labelled `needs-info`, until someone supplies them.
121
+
122
+ ## 4. File the issue
123
+
124
+ Copy the template for the kind, fill in every section, delete the HTML comments, and save it to a
125
+ file. Then run:
126
+
127
+ ```sh
128
+ gh issue create --repo crvouga/atlas \
129
+ --title "<title in the format from the quick reference>" \
130
+ --label agent-reported,<kind> \
131
+ --body-file issue.md
132
+ ```
133
+
134
+ | Kind | Template (raw, for copying) |
135
+ | --- | --- |
136
+ | `bug` | <https://raw.githubusercontent.com/crvouga/atlas/main/.github/ISSUE_TEMPLATE/bug.md> |
137
+ | `feature` | <https://raw.githubusercontent.com/crvouga/atlas/main/.github/ISSUE_TEMPLATE/feature.md> |
138
+
139
+ Each template begins with a `---` front-matter block; leave it out of the body file.
140
+
141
+ If `gh` is not installed or not authenticated, do not install it or log in on your own. Give your
142
+ human the finished title and body and the link to the matching form, for example
143
+ <https://github.com/crvouga/atlas/issues/new?template=bug.md>.
144
+
145
+ ## What happens next
146
+
147
+ Maintainers and agents in this repository work the `agent-reported` queue.
148
+
149
+ - **bug**: they reproduce it, add a failing regression test, fix it, and merge a change that closes
150
+ the issue. If Atlas behaves as documented, they comment with what they observed and close it.
151
+ - **feature**: they settle the surface with you in the comments if it is not clear, then ship it
152
+ with a test for each behavior you listed.
153
+
154
+ An issue labelled `needs-info` is waiting on you: answer the question in the comments.
155
+
156
+ Merging the fix publishes it. The release then comments on the issue with the exact version that
157
+ carries it. Upgrade (`bun update @crvouga/atlas --latest`) and remove your workaround.
158
+
159
+ [t-bug]: https://github.com/crvouga/atlas/blob/main/.github/ISSUE_TEMPLATE/bug.md
160
+ [t-feature]: https://github.com/crvouga/atlas/blob/main/.github/ISSUE_TEMPLATE/feature.md