@crvouga/atlas 1.1.0 → 1.3.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 (54) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/reports/src/artifact-query.d.ts +123 -0
  3. package/dist/reports/src/artifact-query.js +234 -0
  4. package/dist/reports/src/evidence-detail.d.ts +14 -3
  5. package/dist/reports/src/evidence-detail.js +34 -19
  6. package/dist/reports/src/evidence-scope.d.ts +83 -0
  7. package/dist/reports/src/evidence-scope.js +129 -0
  8. package/dist/reports/src/evidence-types.d.ts +5 -1
  9. package/dist/reports/src/explorer-types.d.ts +1 -1
  10. package/dist/reports/src/layer-merge.js +3 -2
  11. package/dist/reports/src/layer-types.d.ts +1 -1
  12. package/dist/reports/src/publish-checks.js +1 -0
  13. package/dist/reports/src/reporter.d.ts +1 -1
  14. package/dist/reports/src/reports-explorer-route.d.ts +276 -0
  15. package/dist/reports/src/reports-route.d.ts +276 -0
  16. package/dist/reports/src/test-catalog.d.ts +108 -6
  17. package/dist/reports/src/test-catalog.js +155 -8
  18. package/dist/schema/src/api.d.ts +745 -0
  19. package/dist/schema/src/api.js +48 -6
  20. package/dist/schema/src/layer.d.ts +981 -0
  21. package/dist/schema/src/layer.js +43 -4
  22. package/dist/server/src/app.js +16 -5
  23. package/dist/server/src/openapi.js +42 -3
  24. package/docs/extending.md +1 -0
  25. package/docs/reference/http-api.md +86 -2
  26. package/docs/reference/reports.md +550 -14
  27. package/docs/reference/schema.md +55 -0
  28. package/docs/rich-evidence.md +78 -0
  29. package/docs/server.md +4 -1
  30. package/docs/ui.md +1 -1
  31. package/llms-full.txt +170 -4
  32. package/package.json +1 -1
  33. package/packages/schema/schemas/check-detail.schema.json +673 -1
  34. package/packages/schema/schemas/effective-delta.schema.json +206 -0
  35. package/packages/schema/schemas/effective-evidence.schema.json +206 -0
  36. package/packages/schema/schemas/evidence-page.schema.json +103 -0
  37. package/packages/schema/schemas/layer-rows.schema.json +103 -0
  38. package/packages/schema/schemas/layer.schema.json +1598 -62
  39. package/packages/schema/schemas/publish-checks.schema.json +768 -0
  40. package/packages/schema/schemas/report-run.schema.json +131 -0
  41. package/packages/schema/schemas/report-runs.schema.json +221 -0
  42. package/packages/schema/schemas/test-catalog.schema.json +135 -0
  43. package/packages/schema/schemas/test-history.schema.json +216 -0
  44. package/packages/server/openapi.json +4731 -735
  45. package/packages/visualizer/src/evidence/artifacts.tsx +24 -18
  46. package/packages/visualizer/src/evidence/evidence-scope.tsx +106 -0
  47. package/packages/visualizer/src/evidence/format.ts +11 -0
  48. package/packages/visualizer/src/explorer/ArtifactsView.tsx +162 -0
  49. package/packages/visualizer/src/explorer/Explorer.tsx +47 -3
  50. package/packages/visualizer/src/explorer/RunsView.tsx +43 -19
  51. package/packages/visualizer/src/explorer/catalog.tsx +97 -12
  52. package/packages/visualizer/src/explorer/filters.tsx +279 -0
  53. package/packages/visualizer/src/explorer/search.ts +85 -6
  54. package/packages/visualizer/src/explorer/views.ts +75 -0
@@ -308,6 +308,22 @@ export const ReportRunSchema = …
308
308
 
309
309
  A run: the layers that name the same execution (else share a run URL, else a branch and commit), with what they recorded.
310
310
 
311
+ #### `QueryFacetValueSchema`
312
+
313
+ ```ts
314
+ export const QueryFacetValueSchema = …
315
+ ```
316
+
317
+ A value of a filter dimension and how many records have it.
318
+
319
+ #### `QueryFacetsSchema`
320
+
321
+ ```ts
322
+ export const QueryFacetsSchema = …
323
+ ```
324
+
325
+ The facets of a runs or artifacts query: per dimension, its values with disjunctive counts (the list's count if that value were the only one chosen, every other dimension's filter applied).
326
+
311
327
  #### `RunListSchema`
312
328
 
313
329
  ```ts
@@ -316,6 +332,22 @@ export const RunListSchema = …
316
332
 
317
333
  `GET /api/reports/runs`: runs, newest first.
318
334
 
335
+ #### `ArtifactResultSchema`
336
+
337
+ ```ts
338
+ export const ArtifactResultSchema = …
339
+ ```
340
+
341
+ One recorded result that carries evidence: where and when it was recorded and what it carries.
342
+
343
+ #### `ArtifactListSchema`
344
+
345
+ ```ts
346
+ export const ArtifactListSchema = …
347
+ ```
348
+
349
+ `GET /api/reports/artifacts`: recorded results that carry evidence, newest first, with the facets of the query.
350
+
319
351
  #### `RunDetailSchema`
320
352
 
321
353
  ```ts
@@ -744,6 +776,29 @@ export const MomentSchema = …
744
776
 
745
777
  Where in a check something happened: at a step, and/or this long after the check started.
746
778
 
779
+ #### `EvidenceScopeSchema`
780
+
781
+ ```ts
782
+ export const EvidenceScopeSchema = …
783
+ ```
784
+
785
+ What a piece of evidence does and does not show, declared by the producer that made it: where it ran, whether its
786
+ data and dependencies were real, whether audio was captured, how it was paced and which assertions the result
787
+ stands on. Every field is optional and a field that is absent is unknown, never assumed live, physical or end to end.
788
+
789
+ It is a declaration, not a finding: Atlas shows it as the producer's word, apart from the dimensions the producer
790
+ names in `verified` (facts something independent checked). It describes evidence and never decides a status, a slot
791
+ or a winner. The nearest declaration wins as a whole, never field by field: an attachment's `scope`, else its
792
+ attempt's, else its check's, else its part's, else its layer's.
793
+
794
+ #### `EvidenceScope`
795
+
796
+ ```ts
797
+ export type EvidenceScope = z.output<typeof EvidenceScopeSchema>;
798
+ ```
799
+
800
+ A producer-declared evidence scope as stored in a layer, parsed from `EvidenceScopeSchema`.
801
+
747
802
  #### `AttachmentSchema`
748
803
 
749
804
  ```ts
@@ -27,6 +27,7 @@ decided exactly as before ([the report contract](report-contract.md)). Evidence
27
27
  | `attempts` | check | Every try of a retried check, with its own status, detail, steps and attachments. A check that passed after failing is flaky. |
28
28
  | `tags`, `labels` | check, layer | Filters and groups (`smoke`, `flaky`, an owner, a browser). |
29
29
  | `tags` | attachment | Marks one recording or screenshot, for example `demo`: shown as a `#demo` chip, and searchable. See [Tagging artifacts and demos](#tagging-artifacts-and-demos). |
30
+ | `scope` | attachment, attempt, check, part, layer | What a recording does and does not show: where it ran, whether data and dependencies were real, whether audio was captured. See [Evidence scope](#evidence-scope). |
30
31
  | `annotations`, `links` | check, step, attempt, layer | Notes, warnings and issue links next to the result. |
31
32
  | `specPath` | check | The spec a check belongs to (a `.feature`, a test file), relative to the repository. Checks are grouped by it. |
32
33
  | `specs[]` (`path`, `domain`, `kind`, `title`) | layer | Names each spec and its group (`domain`), so a server can group checks by spec with a title even when no [spec set](integrating.md#tell-atlas-which-spec-a-check-belongs-to) has been published. |
@@ -62,6 +63,83 @@ attachments with that tag, and says how many match. Tags describe evidence; they
62
63
  - **Older layers.** A layer without attachment tags reads exactly as before. Tags on an attachment that cannot be read
63
64
  (not a list of strings) are dropped, and the attachment, check and layer stay.
64
65
 
66
+ ## Evidence scope
67
+
68
+ A green result and a video do not say whether the speech was real, the replies scripted, the audio recorded, the device
69
+ physical or a vendor substituted. Declare it where the evidence is, in an optional `scope`, and the viewer shows it as
70
+ short badges above the recording (before it is played), with the exact declaration on demand:
71
+
72
+ ```ts
73
+ {
74
+ name: 'checkout.webm', role: 'video',
75
+ scope: {
76
+ surface: 'browser', // where the product ran: browser, native-app, api…
77
+ deviceKind: 'simulator', // simulator, emulator, physical, virtual-machine, desktop…
78
+ syntheticData: true, // made-up data (false: real, absent: unknown)
79
+ dependencies: 'scripted', // scripted, live or mixed
80
+ audioCaptured: false, // false: silent, absent: unknown
81
+ pacing: 'paced', // paced, real-time
82
+ asserted: ['the receipt is shown'], // what the result stands on
83
+ limitations: ['Replies are scripted'],
84
+ declaredBy: 'checkout e2e suite',
85
+ verified: ['deviceKind'], // fields something independent checked
86
+ verifiedBy: 'device farm session log',
87
+ provenance: [{ url: 'https://farm.example/session/1', name: 'Device session' }]
88
+ }
89
+ }
90
+ ```
91
+
92
+ Every field is optional, and every value except the booleans is an open string.
93
+
94
+ - **Unknown is not live.** A recording with no scope, or a scope that does not say a thing, shows `Scope unknown` or
95
+ `Not declared`. It is never assumed live, physical, audible or end to end.
96
+ - **Declared is not verified.** Atlas shows `scope` as the producer's word. Only the fields named in `verified` are
97
+ marked verified (with `verifiedBy`), and `provenance` links where to check them. Atlas does not check any of it.
98
+ - **Only what was asserted.** `asserted` lists the boundaries the result stands on. The viewer says that other activity
99
+ visible in the clip is not verified behavior; with no `asserted` it says that no boundary was declared.
100
+ - **The nearest declaration, whole.** A recording takes the `scope` of the attachment, else its attempt, its check, its
101
+ part, its layer, in that order. It is never merged field by field, so a retry on a physical phone does not borrow
102
+ the limitations declared for the simulator try, and the badges change with the exact recording opened.
103
+ - **Filtering.** `GET /api/reports/tests?scope=device:physical,audio:recorded` (and the explorer's *Recording scope*
104
+ filter) lists tests with one playable recording that states every token: `surface:<v>`, `device:<v>`,
105
+ `data:synthetic` or `data:real`, `dependencies:<v>`, `audio:recorded` or `audio:silent`, `pacing:<v>`. A simulator
106
+ and a physical phone are separate values. Evidence that did not declare a value is never included, and `unknown`
107
+ asks for recordings that declared nothing. Each test lists its recordings' tokens as `scopeProfiles`, the facets as
108
+ `scopes`, and the search box finds the words in them.
109
+ - **It decides nothing.** A scope never changes a status, a slot or a winner. A scope an older Atlas cannot read is
110
+ dropped, and the attachment, check and layer stay.
111
+
112
+ ## Finding runs and artifacts
113
+
114
+ The viewer's **Runs** and **Artifacts** tabs and the API answer one query, so a link means the same thing everywhere.
115
+ "Demo videos for checkout on iPhone, including failed attempts" is:
116
+
117
+ ```sh
118
+ curl "$ATLAS_SERVER_URL/api/reports/artifacts?artifactTag=demo&type=video&domain=checkout&label=device%3DiPhone"
119
+ ```
120
+
121
+ - **Dimensions.** `artifactTag`, `type` (the media type as a role: `video`, `image`, `audio`, `snapshot`, `text`,
122
+ `file`; `video` means a playable recording), `label` (`key=value`, or `key`), `domain` and `spec` (artifacts only),
123
+ `environment` (the device or client), `producer`, `branch`, `sha` (seven or more characters), `outcome` (`passed`,
124
+ `failed`, `running`, `not-run`), `since` and `until` (epoch milliseconds or an ISO date), and `q` (words).
125
+ - **Semantics.** Filters combine with AND across dimensions and OR within one (`environment=ios,android`). With both
126
+ `artifactTag` and `type`, one attachment must be both where the report says which video carries which tag: a tagged
127
+ screenshot beside an untagged video is not a tagged video. `inherited=1` also counts the tags an attachment inherits
128
+ from its check and run. A comma or backslash inside a value is written `\,` or `\\`; the viewer encodes `#` and spaces.
129
+ - **History.** Artifacts are recorded results, not only the report's current winners: a recording tagged `demo` is
130
+ found with its original run, commit and status even when a later attempt of the test is untagged. Tags a reviewer
131
+ added after upload are kept apart from the layer and are not searched here.
132
+ - **Facets.** Each answer carries `facets`: for every dimension the values on offer. The count beside a value is what
133
+ the list would hold if that value alone were chosen in its dimension with every other filter applied, so the
134
+ chosen value's count is the list's and the others show what adding them would give.
135
+ - **Older servers.** A response names the filters it applied in `filters`. A viewer that asked for one that is missing
136
+ filters what a run summary can answer itself and says plainly that the server cannot filter by the rest (tags, types,
137
+ labels); it never presents an unfiltered list as narrowed. A server without `/api/reports/artifacts` is reported as
138
+ such on the Artifacts tab.
139
+ - **Views.** *Views* saves the current filters under a name in this browser, per project and tab. A saved view is a
140
+ **live query**: opening it asks again and shows what matches the newest report. It is not a snapshot; an immutable
141
+ review collection is a separate feature. *Copy link* gives the address, which holds the whole query.
142
+
65
143
  ## Labels describe, parameters split
66
144
 
67
145
  A result's **slot** is its producer, check id, part kind, spec, environment and **parameters**. The newest completed
package/docs/server.md CHANGED
@@ -375,7 +375,8 @@ with `format=markdown` (or `Accept: text/markdown`), and takes the project prefi
375
375
  | Only some: `q` (all words), `status` (or `flaky`), `environment`, `producer`, `domain`, `spec`, `tag`, `run`; `sort`, `offset`, `limit` | `GET /api/reports/tests?status=failed&environment=ios` |
376
376
  | One test: every recorded result (status, duration, environment, branch, commit, CI run), newest first | `GET /api/reports/tests/history?key=<key>` |
377
377
  | Why Specs, domains and counts disagree: ambiguous spec paths, shared domains, gaps, a reconciliation of the totals | `GET /api/reports/tests/health` |
378
- | The runs that recorded the report (by CI run URL, else branch and commit); `?video=true` keeps those with a playable video | `GET /api/reports/runs` |
378
+ | The runs that recorded the report (by CI run URL, else branch and commit); `?video=true` keeps those with a playable video; `artifactTag`, `type`, `label`, `environment`, `branch`, `outcome`, `since`, `until` narrow it ([filtering](rich-evidence.md#finding-runs-and-artifacts)) | `GET /api/reports/runs` |
379
+ | Recorded results that carry evidence, across the whole history, found by tag, media type, label, feature, environment, branch, revision, outcome and time | `GET /api/reports/artifacts?artifactTag=demo&type=video` |
379
380
  | Every test one run recorded | `GET /api/reports/runs/detail?key=<run key>` |
380
381
 
381
382
  ```sh
@@ -458,6 +459,8 @@ curl "$ATLAS_SERVER_URL/api/projects/$ATLAS_PROJECT/reports/tests?format=markdow
458
459
  | `POST /api/projects/{project}/reports/artifact-tags` | publish | Tag or untag one attachment after upload. |
459
460
  | `GET /api/reports/runs` | read | The runs that recorded the report. |
460
461
  | `GET /api/projects/{project}/reports/runs` | read | The runs that recorded the report. |
462
+ | `GET /api/reports/artifacts` | read | Recorded results that carry evidence, across the whole history. |
463
+ | `GET /api/projects/{project}/reports/artifacts` | read | Recorded results that carry evidence, across the whole history. |
461
464
  | `GET /api/reports/runs/detail` | read | Every test one run recorded. |
462
465
  | `GET /api/projects/{project}/reports/runs/detail` | read | Every test one run recorded. |
463
466
  | `GET /api/reports/outline` | read | The specs a report is laid over. |
package/docs/ui.md CHANGED
@@ -125,7 +125,7 @@ The narrowly scoped exceptions are:
125
125
  | Area | Screens/interactions | Mantine composition |
126
126
  | --- | --- | --- |
127
127
  | Shared shell | Project switching, spec navigation, run selection, connection status, diagnostics, mobile index | AppShell, Burger, NavLink, Menu, Popover, NativeSelect, Drawer |
128
- | Project home | Tests/runs tabs, metrics, search, status/environment/domain filters, grouping, sorting, show more | Container, Tabs, SimpleGrid, Paper, TextInput, Button, Accordion |
128
+ | Project home | Tests/runs/artifacts tabs, metrics, search, status/environment/domain/artifact-tag filters, run and artifact facets with saved views, grouping, sorting, show more | Container, Tabs, SimpleGrid, Paper, TextInput, MultiSelect, Popover, Button, Accordion |
129
129
  | Test detail | Current outcomes, statistics, duration graph, history, artifacts, recording; the `layer` address parameter pins it to one run's result, labelled "This run" against "Latest combined evidence" | Drawer, Table, Badge, Alert, Stage |
130
130
  | Specs | All six existing kinds, overview/index, Gherkin rules/scenarios/examples, Quint declarations/traces, DMN tables, source text | SpecHeader, Accordion, Table, CodeHighlightTabs, ResultRow |
131
131
  | Statecharts | Graph, screen/event/journey details, developer metadata, chart test results | Mantine cards/navigation and product compositions; graph exception above |
package/llms-full.txt CHANGED
@@ -1014,6 +1014,7 @@ decided exactly as before ([the report contract](report-contract.md)). Evidence
1014
1014
  | `attempts` | check | Every try of a retried check, with its own status, detail, steps and attachments. A check that passed after failing is flaky. |
1015
1015
  | `tags`, `labels` | check, layer | Filters and groups (`smoke`, `flaky`, an owner, a browser). |
1016
1016
  | `tags` | attachment | Marks one recording or screenshot, for example `demo`: shown as a `#demo` chip, and searchable. See [Tagging artifacts and demos](#tagging-artifacts-and-demos). |
1017
+ | `scope` | attachment, attempt, check, part, layer | What a recording does and does not show: where it ran, whether data and dependencies were real, whether audio was captured. See [Evidence scope](#evidence-scope). |
1017
1018
  | `annotations`, `links` | check, step, attempt, layer | Notes, warnings and issue links next to the result. |
1018
1019
  | `specPath` | check | The spec a check belongs to (a `.feature`, a test file), relative to the repository. Checks are grouped by it. |
1019
1020
  | `specs[]` (`path`, `domain`, `kind`, `title`) | layer | Names each spec and its group (`domain`), so a server can group checks by spec with a title even when no [spec set](integrating.md#tell-atlas-which-spec-a-check-belongs-to) has been published. |
@@ -1049,6 +1050,83 @@ attachments with that tag, and says how many match. Tags describe evidence; they
1049
1050
  - **Older layers.** A layer without attachment tags reads exactly as before. Tags on an attachment that cannot be read
1050
1051
  (not a list of strings) are dropped, and the attachment, check and layer stay.
1051
1052
 
1053
+ ## Evidence scope
1054
+
1055
+ A green result and a video do not say whether the speech was real, the replies scripted, the audio recorded, the device
1056
+ physical or a vendor substituted. Declare it where the evidence is, in an optional `scope`, and the viewer shows it as
1057
+ short badges above the recording (before it is played), with the exact declaration on demand:
1058
+
1059
+ ```ts
1060
+ {
1061
+ name: 'checkout.webm', role: 'video',
1062
+ scope: {
1063
+ surface: 'browser', // where the product ran: browser, native-app, api…
1064
+ deviceKind: 'simulator', // simulator, emulator, physical, virtual-machine, desktop…
1065
+ syntheticData: true, // made-up data (false: real, absent: unknown)
1066
+ dependencies: 'scripted', // scripted, live or mixed
1067
+ audioCaptured: false, // false: silent, absent: unknown
1068
+ pacing: 'paced', // paced, real-time
1069
+ asserted: ['the receipt is shown'], // what the result stands on
1070
+ limitations: ['Replies are scripted'],
1071
+ declaredBy: 'checkout e2e suite',
1072
+ verified: ['deviceKind'], // fields something independent checked
1073
+ verifiedBy: 'device farm session log',
1074
+ provenance: [{ url: 'https://farm.example/session/1', name: 'Device session' }]
1075
+ }
1076
+ }
1077
+ ```
1078
+
1079
+ Every field is optional, and every value except the booleans is an open string.
1080
+
1081
+ - **Unknown is not live.** A recording with no scope, or a scope that does not say a thing, shows `Scope unknown` or
1082
+ `Not declared`. It is never assumed live, physical, audible or end to end.
1083
+ - **Declared is not verified.** Atlas shows `scope` as the producer's word. Only the fields named in `verified` are
1084
+ marked verified (with `verifiedBy`), and `provenance` links where to check them. Atlas does not check any of it.
1085
+ - **Only what was asserted.** `asserted` lists the boundaries the result stands on. The viewer says that other activity
1086
+ visible in the clip is not verified behavior; with no `asserted` it says that no boundary was declared.
1087
+ - **The nearest declaration, whole.** A recording takes the `scope` of the attachment, else its attempt, its check, its
1088
+ part, its layer, in that order. It is never merged field by field, so a retry on a physical phone does not borrow
1089
+ the limitations declared for the simulator try, and the badges change with the exact recording opened.
1090
+ - **Filtering.** `GET /api/reports/tests?scope=device:physical,audio:recorded` (and the explorer's *Recording scope*
1091
+ filter) lists tests with one playable recording that states every token: `surface:<v>`, `device:<v>`,
1092
+ `data:synthetic` or `data:real`, `dependencies:<v>`, `audio:recorded` or `audio:silent`, `pacing:<v>`. A simulator
1093
+ and a physical phone are separate values. Evidence that did not declare a value is never included, and `unknown`
1094
+ asks for recordings that declared nothing. Each test lists its recordings' tokens as `scopeProfiles`, the facets as
1095
+ `scopes`, and the search box finds the words in them.
1096
+ - **It decides nothing.** A scope never changes a status, a slot or a winner. A scope an older Atlas cannot read is
1097
+ dropped, and the attachment, check and layer stay.
1098
+
1099
+ ## Finding runs and artifacts
1100
+
1101
+ The viewer's **Runs** and **Artifacts** tabs and the API answer one query, so a link means the same thing everywhere.
1102
+ "Demo videos for checkout on iPhone, including failed attempts" is:
1103
+
1104
+ ```sh
1105
+ curl "$ATLAS_SERVER_URL/api/reports/artifacts?artifactTag=demo&type=video&domain=checkout&label=device%3DiPhone"
1106
+ ```
1107
+
1108
+ - **Dimensions.** `artifactTag`, `type` (the media type as a role: `video`, `image`, `audio`, `snapshot`, `text`,
1109
+ `file`; `video` means a playable recording), `label` (`key=value`, or `key`), `domain` and `spec` (artifacts only),
1110
+ `environment` (the device or client), `producer`, `branch`, `sha` (seven or more characters), `outcome` (`passed`,
1111
+ `failed`, `running`, `not-run`), `since` and `until` (epoch milliseconds or an ISO date), and `q` (words).
1112
+ - **Semantics.** Filters combine with AND across dimensions and OR within one (`environment=ios,android`). With both
1113
+ `artifactTag` and `type`, one attachment must be both where the report says which video carries which tag: a tagged
1114
+ screenshot beside an untagged video is not a tagged video. `inherited=1` also counts the tags an attachment inherits
1115
+ from its check and run. A comma or backslash inside a value is written `\,` or `\\`; the viewer encodes `#` and spaces.
1116
+ - **History.** Artifacts are recorded results, not only the report's current winners: a recording tagged `demo` is
1117
+ found with its original run, commit and status even when a later attempt of the test is untagged. Tags a reviewer
1118
+ added after upload are kept apart from the layer and are not searched here.
1119
+ - **Facets.** Each answer carries `facets`: for every dimension the values on offer. The count beside a value is what
1120
+ the list would hold if that value alone were chosen in its dimension with every other filter applied, so the
1121
+ chosen value's count is the list's and the others show what adding them would give.
1122
+ - **Older servers.** A response names the filters it applied in `filters`. A viewer that asked for one that is missing
1123
+ filters what a run summary can answer itself and says plainly that the server cannot filter by the rest (tags, types,
1124
+ labels); it never presents an unfiltered list as narrowed. A server without `/api/reports/artifacts` is reported as
1125
+ such on the Artifacts tab.
1126
+ - **Views.** *Views* saves the current filters under a name in this browser, per project and tab. A saved view is a
1127
+ **live query**: opening it asks again and shows what matches the newest report. It is not a snapshot; an immutable
1128
+ review collection is a separate feature. *Copy link* gives the address, which holds the whole query.
1129
+
1052
1130
  ## Labels describe, parameters split
1053
1131
 
1054
1132
  A result's **slot** is its producer, check id, part kind, spec, environment and **parameters**. The newest completed
@@ -1620,7 +1698,8 @@ with `format=markdown` (or `Accept: text/markdown`), and takes the project prefi
1620
1698
  | Only some: `q` (all words), `status` (or `flaky`), `environment`, `producer`, `domain`, `spec`, `tag`, `run`; `sort`, `offset`, `limit` | `GET /api/reports/tests?status=failed&environment=ios` |
1621
1699
  | One test: every recorded result (status, duration, environment, branch, commit, CI run), newest first | `GET /api/reports/tests/history?key=<key>` |
1622
1700
  | Why Specs, domains and counts disagree: ambiguous spec paths, shared domains, gaps, a reconciliation of the totals | `GET /api/reports/tests/health` |
1623
- | The runs that recorded the report (by CI run URL, else branch and commit); `?video=true` keeps those with a playable video | `GET /api/reports/runs` |
1701
+ | The runs that recorded the report (by CI run URL, else branch and commit); `?video=true` keeps those with a playable video; `artifactTag`, `type`, `label`, `environment`, `branch`, `outcome`, `since`, `until` narrow it ([filtering](rich-evidence.md#finding-runs-and-artifacts)) | `GET /api/reports/runs` |
1702
+ | Recorded results that carry evidence, across the whole history, found by tag, media type, label, feature, environment, branch, revision, outcome and time | `GET /api/reports/artifacts?artifactTag=demo&type=video` |
1624
1703
  | Every test one run recorded | `GET /api/reports/runs/detail?key=<run key>` |
1625
1704
 
1626
1705
  ```sh
@@ -1703,6 +1782,8 @@ curl "$ATLAS_SERVER_URL/api/projects/$ATLAS_PROJECT/reports/tests?format=markdow
1703
1782
  | `POST /api/projects/{project}/reports/artifact-tags` | publish | Tag or untag one attachment after upload. |
1704
1783
  | `GET /api/reports/runs` | read | The runs that recorded the report. |
1705
1784
  | `GET /api/projects/{project}/reports/runs` | read | The runs that recorded the report. |
1785
+ | `GET /api/reports/artifacts` | read | Recorded results that carry evidence, across the whole history. |
1786
+ | `GET /api/projects/{project}/reports/artifacts` | read | Recorded results that carry evidence, across the whole history. |
1706
1787
  | `GET /api/reports/runs/detail` | read | Every test one run recorded. |
1707
1788
  | `GET /api/projects/{project}/reports/runs/detail` | read | Every test one run recorded. |
1708
1789
  | `GET /api/reports/outline` | read | The specs a report is laid over. |
@@ -2723,6 +2804,7 @@ carry any of these. Every one is optional.
2723
2804
  | `attempts[]` | Every try, when the check was retried. |
2724
2805
  | `tags[]` | Free labels to filter by: `smoke`, `payments`. |
2725
2806
  | `labels{}` | Facts about the result that are not its identity: `owner`, `severity`, `browser`, `build`. |
2807
+ | `scope{}` | What the evidence does and does not show: surface, device kind, synthetic data, scripted dependencies, audio, pacing, what was asserted and its limits. Unknown when absent. See [Evidence scope](rich-evidence.md#evidence-scope). |
2726
2808
  | `links[]` | Where to read more: the CI run, the issue, the trace client. |
2727
2809
  | `extensions{}` | Structured data of your own or a tool's, under a name you own. |
2728
2810
  | `measurements[]` | Numbers, with units and an allowance (already part of the contract). |
@@ -3852,7 +3934,7 @@ The narrowly scoped exceptions are:
3852
3934
  | Area | Screens/interactions | Mantine composition |
3853
3935
  | --- | --- | --- |
3854
3936
  | Shared shell | Project switching, spec navigation, run selection, connection status, diagnostics, mobile index | AppShell, Burger, NavLink, Menu, Popover, NativeSelect, Drawer |
3855
- | Project home | Tests/runs tabs, metrics, search, status/environment/domain filters, grouping, sorting, show more | Container, Tabs, SimpleGrid, Paper, TextInput, Button, Accordion |
3937
+ | Project home | Tests/runs/artifacts tabs, metrics, search, status/environment/domain/artifact-tag filters, run and artifact facets with saved views, grouping, sorting, show more | Container, Tabs, SimpleGrid, Paper, TextInput, MultiSelect, Popover, Button, Accordion |
3856
3938
  | Test detail | Current outcomes, statistics, duration graph, history, artifacts, recording; the `layer` address parameter pins it to one run's result, labelled "This run" against "Latest combined evidence" | Drawer, Table, Badge, Alert, Stage |
3857
3939
  | Specs | All six existing kinds, overview/index, Gherkin rules/scenarios/examples, Quint declarations/traces, DMN tables, source text | SpecHeader, Accordion, Table, CodeHighlightTabs, ResultRow |
3858
3940
  | Statecharts | Graph, screen/event/journey details, developer metadata, chart test results | Mantine cards/navigation and product compositions; graph exception above |
@@ -4889,6 +4971,7 @@ Send `format=markdown` (or `Accept: text/markdown`) for the same as a Markdown r
4889
4971
  - `tag` (query): Only tests with this tag. Repeatable or comma-separated.
4890
4972
  - `artifactTag` (query): Only tests with an attachment tagged this (`demo` and `#demo` are the same tag), not counting tags inherited from a check or run: the exact artifact query. `inherited=1` widens it to the tags inherited from the check and run. Repeatable or comma-separated.
4891
4973
  - `inherited` (query): With `artifactTag`: `1` also matches the tags an attachment inherits from its check and run, so a recording without a tag of its own under a run tagged `demo` is found. Without it the query is exact.
4974
+ - `scope` (query): Only tests with one playable recording that states every one of these producer-declared qualifications: `surface:<value>`, `device:<value>` (`device:physical` and `device:simulator` stay apart), `data:synthetic` or `data:real`, `dependencies:<value>`, `audio:recorded` or `audio:silent`, `pacing:<value>`. All must hold for the same recording. A recording that did not declare a value never matches it, and `unknown` matches only a recording that declared no scope. Repeatable or comma-separated.
4892
4975
  - `run` (query): Only tests recorded in this run (a `key` from `/api/reports/runs`).
4893
4976
  - `sort` (query): The order: `spec` (default: domain, spec, title), `title`, `recent`, `slowest`, `failures`, `flaky` or `runs`.
4894
4977
  - `offset` (query): Skip this many matching tests.
@@ -4920,6 +5003,7 @@ Scoped to one project. The same project may be selected on the unprefixed API us
4920
5003
  - `tag` (query): Only tests with this tag. Repeatable or comma-separated.
4921
5004
  - `artifactTag` (query): Only tests with an attachment tagged this (`demo` and `#demo` are the same tag), not counting tags inherited from a check or run: the exact artifact query. `inherited=1` widens it to the tags inherited from the check and run. Repeatable or comma-separated.
4922
5005
  - `inherited` (query): With `artifactTag`: `1` also matches the tags an attachment inherits from its check and run, so a recording without a tag of its own under a run tagged `demo` is found. Without it the query is exact.
5006
+ - `scope` (query): Only tests with one playable recording that states every one of these producer-declared qualifications: `surface:<value>`, `device:<value>` (`device:physical` and `device:simulator` stay apart), `data:synthetic` or `data:real`, `dependencies:<value>`, `audio:recorded` or `audio:silent`, `pacing:<value>`. All must hold for the same recording. A recording that did not declare a value never matches it, and `unknown` matches only a recording that declared no scope. Repeatable or comma-separated.
4923
5007
  - `run` (query): Only tests recorded in this run (a `key` from `/api/reports/runs`).
4924
5008
  - `sort` (query): The order: `spec` (default: domain, spec, title), `title`, `recent`, `slowest`, `failures`, `flaky` or `runs`.
4925
5009
  - `offset` (query): Skip this many matching tests.
@@ -5036,10 +5120,22 @@ Responses: `201` The recorded entry.; `401` No token, or an unknown one. Send `A
5036
5120
 
5037
5121
  `GET /api/reports/runs` · role: read
5038
5122
 
5039
- Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned, up to twenty failures and their video counts (references, distinct playable recordings, unavailable). Send `format=markdown` for Markdown.
5123
+ Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned, up to twenty failures, their video counts (references, distinct playable recordings, unavailable) and the artifact tags, media types and labels their results carry. Filters narrow the list before `limit`: AND across the parameters, OR within one; `filters` in the answer names those that were applied and `facets` offers each dimension's values with counts. A server that does not know a filter ignores it and does not name it in `filters`. Send `format=markdown` for Markdown.
5040
5124
 
5041
5125
  - `limit` (query): At most this many runs (default 100).
5042
5126
  - `video` (query): `true` to list only runs that have a playable video recording. Applied to every run before `limit`; `total` counts the matches. A server that does not know it answers with every run.
5127
+ - `artifactTag` (query): Artifact tags (`demo` and `#demo` are the same), repeatable or comma-separated. A comma or backslash inside a value is written `\,` or `\\`. OR within the parameter, AND with the other filters.
5128
+ - `inherited` (query): `1` with `artifactTag` to also match the tags attachments inherit from their check and run. Default: the exact artifact query.
5129
+ - `type` (query): Media types as roles (`video`, `image`, `audio`, `snapshot`, `text`, `file`, or a producer's own). `video` means a playable recording. With `artifactTag`, one artifact must be both where the report says which videos carry which tags.
5130
+ - `label` (query): `key=value`, or `key` for any value.
5131
+ - `environment` (query): Environments or devices (`ios`, `desktop-web`).
5132
+ - `producer` (query): Producers.
5133
+ - `branch` (query): Branches.
5134
+ - `sha` (query): Revisions: a full commit id or a prefix of at least seven characters.
5135
+ - `outcome` (query): Statuses: `passed`, `failed`, `running`, `not-run`. A run's outcome is `failed` when any result failed, else `running`, else `passed`, else `not-run`.
5136
+ - `q` (query): Words that must all appear in the title, id, spec, branch, producer, environment, tags or labels.
5137
+ - `since` (query): Only records at or after this time: epoch milliseconds or an ISO date (a date alone is UTC). A result's recording time; a run's end.
5138
+ - `until` (query): Only records at or before this time (same forms as `since`).
5043
5139
  - `format` (query): `markdown` for Markdown instead of JSON.
5044
5140
  - `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
5045
5141
 
@@ -5049,17 +5145,87 @@ Responses: `200` The runs.; `304` Unchanged since the `If-None-Match` ETag.; `40
5049
5145
 
5050
5146
  `GET /api/projects/{project}/reports/runs` · role: read
5051
5147
 
5052
- Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned, up to twenty failures and their video counts (references, distinct playable recordings, unavailable). Send `format=markdown` for Markdown.
5148
+ Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned, up to twenty failures, their video counts (references, distinct playable recordings, unavailable) and the artifact tags, media types and labels their results carry. Filters narrow the list before `limit`: AND across the parameters, OR within one; `filters` in the answer names those that were applied and `facets` offers each dimension's values with counts. A server that does not know a filter ignores it and does not name it in `filters`. Send `format=markdown` for Markdown.
5053
5149
 
5054
5150
  Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
5055
5151
 
5056
5152
  - `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
5057
5153
  - `limit` (query): At most this many runs (default 100).
5058
5154
  - `video` (query): `true` to list only runs that have a playable video recording. Applied to every run before `limit`; `total` counts the matches. A server that does not know it answers with every run.
5155
+ - `artifactTag` (query): Artifact tags (`demo` and `#demo` are the same), repeatable or comma-separated. A comma or backslash inside a value is written `\,` or `\\`. OR within the parameter, AND with the other filters.
5156
+ - `inherited` (query): `1` with `artifactTag` to also match the tags attachments inherit from their check and run. Default: the exact artifact query.
5157
+ - `type` (query): Media types as roles (`video`, `image`, `audio`, `snapshot`, `text`, `file`, or a producer's own). `video` means a playable recording. With `artifactTag`, one artifact must be both where the report says which videos carry which tags.
5158
+ - `label` (query): `key=value`, or `key` for any value.
5159
+ - `environment` (query): Environments or devices (`ios`, `desktop-web`).
5160
+ - `producer` (query): Producers.
5161
+ - `branch` (query): Branches.
5162
+ - `sha` (query): Revisions: a full commit id or a prefix of at least seven characters.
5163
+ - `outcome` (query): Statuses: `passed`, `failed`, `running`, `not-run`. A run's outcome is `failed` when any result failed, else `running`, else `passed`, else `not-run`.
5164
+ - `q` (query): Words that must all appear in the title, id, spec, branch, producer, environment, tags or labels.
5165
+ - `since` (query): Only records at or after this time: epoch milliseconds or an ISO date (a date alone is UTC). A result's recording time; a run's end.
5166
+ - `until` (query): Only records at or before this time (same forms as `since`).
5059
5167
  - `format` (query): `markdown` for Markdown instead of JSON.
5060
5168
 
5061
5169
  Responses: `200` The runs.; `304` Unchanged since the `If-None-Match` ETag.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
5062
5170
 
5171
+ ### Recorded results that carry evidence, across the whole history
5172
+
5173
+ `GET /api/reports/artifacts` · role: read
5174
+
5175
+ Every recorded result that has attachments (recordings, screenshots, traces), newest first, found in the history and not only among the current winners: a recording tagged `demo` is found with its original run, revision and status even when a later attempt is untagged. Narrow it by artifact tag, media type, label, feature (`domain`, `spec`), environment or device, producer, branch, revision, outcome and time: AND across the parameters, OR within one; with both `artifactTag` and `type`, one artifact must be both. `filters` names the filters that were applied and `facets` offers each dimension's values with counts: the count beside a value is what the list would hold if that value alone were chosen in its dimension, every other filter applied. Tags written by reviewers after upload are not searched; only those in the layer are. Send `format=markdown` for Markdown.
5176
+
5177
+ - `limit` (query): At most this many results (default 100).
5178
+ - `offset` (query): Skip this many matches (default 0).
5179
+ - `video` (query): `true` for results with a playable video recording (the same as `type=video`).
5180
+ - `domain` (query): The feature domain of the result's spec.
5181
+ - `spec` (query): Spec paths.
5182
+ - `artifactTag` (query): Artifact tags (`demo` and `#demo` are the same), repeatable or comma-separated. A comma or backslash inside a value is written `\,` or `\\`. OR within the parameter, AND with the other filters.
5183
+ - `inherited` (query): `1` with `artifactTag` to also match the tags attachments inherit from their check and run. Default: the exact artifact query.
5184
+ - `type` (query): Media types as roles (`video`, `image`, `audio`, `snapshot`, `text`, `file`, or a producer's own). `video` means a playable recording. With `artifactTag`, one artifact must be both where the report says which videos carry which tags.
5185
+ - `label` (query): `key=value`, or `key` for any value.
5186
+ - `environment` (query): Environments or devices (`ios`, `desktop-web`).
5187
+ - `producer` (query): Producers.
5188
+ - `branch` (query): Branches.
5189
+ - `sha` (query): Revisions: a full commit id or a prefix of at least seven characters.
5190
+ - `outcome` (query): Statuses: `passed`, `failed`, `running`, `not-run`. A run's outcome is `failed` when any result failed, else `running`, else `passed`, else `not-run`.
5191
+ - `q` (query): Words that must all appear in the title, id, spec, branch, producer, environment, tags or labels.
5192
+ - `since` (query): Only records at or after this time: epoch milliseconds or an ISO date (a date alone is UTC). A result's recording time; a run's end.
5193
+ - `until` (query): Only records at or before this time (same forms as `since`).
5194
+ - `format` (query): `markdown` for Markdown instead of JSON.
5195
+ - `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
5196
+
5197
+ Responses: `200` The matching results and the facets.; `304` Unchanged since the `If-None-Match` ETag.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
5198
+
5199
+ ### Recorded results that carry evidence, across the whole history
5200
+
5201
+ `GET /api/projects/{project}/reports/artifacts` · role: read
5202
+
5203
+ Every recorded result that has attachments (recordings, screenshots, traces), newest first, found in the history and not only among the current winners: a recording tagged `demo` is found with its original run, revision and status even when a later attempt is untagged. Narrow it by artifact tag, media type, label, feature (`domain`, `spec`), environment or device, producer, branch, revision, outcome and time: AND across the parameters, OR within one; with both `artifactTag` and `type`, one artifact must be both. `filters` names the filters that were applied and `facets` offers each dimension's values with counts: the count beside a value is what the list would hold if that value alone were chosen in its dimension, every other filter applied. Tags written by reviewers after upload are not searched; only those in the layer are. Send `format=markdown` for Markdown.
5204
+
5205
+ Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
5206
+
5207
+ - `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
5208
+ - `limit` (query): At most this many results (default 100).
5209
+ - `offset` (query): Skip this many matches (default 0).
5210
+ - `video` (query): `true` for results with a playable video recording (the same as `type=video`).
5211
+ - `domain` (query): The feature domain of the result's spec.
5212
+ - `spec` (query): Spec paths.
5213
+ - `artifactTag` (query): Artifact tags (`demo` and `#demo` are the same), repeatable or comma-separated. A comma or backslash inside a value is written `\,` or `\\`. OR within the parameter, AND with the other filters.
5214
+ - `inherited` (query): `1` with `artifactTag` to also match the tags attachments inherit from their check and run. Default: the exact artifact query.
5215
+ - `type` (query): Media types as roles (`video`, `image`, `audio`, `snapshot`, `text`, `file`, or a producer's own). `video` means a playable recording. With `artifactTag`, one artifact must be both where the report says which videos carry which tags.
5216
+ - `label` (query): `key=value`, or `key` for any value.
5217
+ - `environment` (query): Environments or devices (`ios`, `desktop-web`).
5218
+ - `producer` (query): Producers.
5219
+ - `branch` (query): Branches.
5220
+ - `sha` (query): Revisions: a full commit id or a prefix of at least seven characters.
5221
+ - `outcome` (query): Statuses: `passed`, `failed`, `running`, `not-run`. A run's outcome is `failed` when any result failed, else `running`, else `passed`, else `not-run`.
5222
+ - `q` (query): Words that must all appear in the title, id, spec, branch, producer, environment, tags or labels.
5223
+ - `since` (query): Only records at or after this time: epoch milliseconds or an ISO date (a date alone is UTC). A result's recording time; a run's end.
5224
+ - `until` (query): Only records at or before this time (same forms as `since`).
5225
+ - `format` (query): `markdown` for Markdown instead of JSON.
5226
+
5227
+ Responses: `200` The matching results and the facets.; `304` Unchanged since the `If-None-Match` ETag.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
5228
+
5063
5229
  ### Every test one run recorded
5064
5230
 
5065
5231
  `GET /api/reports/runs/detail` · role: read
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crvouga/atlas",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Atlas: specs of any kind, reported as layered evidence. Publish checks from any runner (JUnit, CTRF, a script), combine them across nodes, branches, environments and backends, gate CI on the result, and run statecharts against the real app.",
5
5
  "license": "MIT",
6
6
  "type": "module",