@crvouga/atlas 1.19.0 → 1.20.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.
package/llms-full.txt CHANGED
@@ -1493,6 +1493,18 @@ two ways without touching a check's result:
1493
1493
  `--format json` prints the `verify-recordings-result` document. `--layer <id>` and `--limit <n>` narrow the work, since
1494
1494
  it reads blobs. A store that cannot answer is `unreadable`, never verified. Test results are unchanged either way.
1495
1495
 
1496
+ - **In the viewer.** A publish token starts the same read-back on the server with
1497
+ `POST /api/reports/recordings/verification` (body optional: `{ "layers": ["<id prefix>"], "limit": 20 }`; the newest
1498
+ 20 layers by default, at most 200, one read-back at a time; a reader token, a browser sign-in box or an anonymous
1499
+ caller is refused because it reads blobs). Anyone who can read the report then sees the result with
1500
+ `GET /api/reports/recordings/verification` (which reads no blob): per layer, its run, how many videos were read back, how
1501
+ many passed and each gap. The Runs tab and an opened run show `Recording unavailable: 1 of 3 stored videos failed the
1502
+ read-back (1 file gone from storage)` beside the green outcomes, or `3 stored videos verified`, and a run whose stored
1503
+ videos all failed offers no Watch action. A run whose layers were never read back shows nothing: unknown is not
1504
+ verified. The result lives in the server's memory only, so a restart empties it, and a layer that garbage collection
1505
+ removed drops out. An older server has no such route and the viewer then shows nothing. No status, slot or winner is
1506
+ affected; `capabilities.recordingVerification` in `GET /api/server` says a server offers it.
1507
+
1496
1508
  Both are statements about stored bytes. Atlas does not check that a stored video plays in a given browser (a valid header
1497
1509
  can still have an unsupported codec or be truncated), and acknowledging an upload is not playback. Videos given only by
1498
1510
  `uri` are counted as referenced and not checked.
@@ -2015,6 +2027,14 @@ edit, and the server's time. Nothing about the layer, its results or the report'
2015
2027
  See [Tagging artifacts and demos](rich-evidence.md#tagging-artifacts-and-demos) for the tag rules. Reviewer tags are
2016
2028
  shown in the viewer's evidence; the tests API (`artifactTag`) searches the producers' tags.
2017
2029
 
2030
+ ## Reading recordings back
2031
+
2032
+ `POST /api/reports/recordings/verification` (publish, `Authorization: Bearer`) reads the stored videos of the newest layers
2033
+ back and checks them; `GET` (read) returns the newest result per layer without reading any blob, so the viewer can mark a
2034
+ run whose recording was stored and then lost, apart from its test results. The result is in memory only and layers
2035
+ that were never read back are absent (unknown, never "fine"). See
2036
+ [Is the recording still there?](rich-evidence.md#is-the-recording-still-there) for what is checked and what is not.
2037
+
2018
2038
  ## Review collections
2019
2039
 
2020
2040
  A team can publish a named, curated review of a few behaviors: recordings from several explicit executions, notes on
@@ -2194,6 +2214,10 @@ curl "$ATLAS_SERVER_URL/api/projects/$ATLAS_PROJECT/reports/tests?format=markdow
2194
2214
  | `GET /api/projects/{project}/reports/artifact-tags` | read | Reviewer tags of attachments: every recorded edit. |
2195
2215
  | `POST /api/reports/artifact-tags` | publish | Tag or untag one attachment after upload. |
2196
2216
  | `POST /api/projects/{project}/reports/artifact-tags` | publish | Tag or untag one attachment after upload. |
2217
+ | `GET /api/reports/recordings/verification` | read | Which stored recordings the server last read back, and which were gone or damaged. |
2218
+ | `GET /api/projects/{project}/reports/recordings/verification` | read | Which stored recordings the server last read back, and which were gone or damaged. |
2219
+ | `POST /api/reports/recordings/verification` | publish | Read stored recordings back and check them (publish token). |
2220
+ | `POST /api/projects/{project}/reports/recordings/verification` | publish | Read stored recordings back and check them (publish token). |
2197
2221
  | `GET /api/reports/review-collections` | read | Review collections: curated sets of recordings, newest first. |
2198
2222
  | `GET /api/projects/{project}/reports/review-collections` | read | Review collections: curated sets of recordings, newest first. |
2199
2223
  | `POST /api/reports/review-collections` | publish | Publish an immutable review collection (or a new revision of one). |
@@ -6086,6 +6110,58 @@ Scoped to one project. The same project may be selected on the unprefixed API us
6086
6110
 
6087
6111
  Responses: `201` The recorded entry.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `403` The token is known but its role may not do this.; `413` Request too large.; `422` Not a tag edit: a bad target, an empty or over-long tag, or nothing to change.
6088
6112
 
6113
+ ### Which stored recordings the server last read back, and which were gone or damaged
6114
+
6115
+ `GET /api/reports/recordings/verification` · role: read
6116
+
6117
+ The newest read-back the server did of stored videos, per layer: how many it checked, how many passed and the `gaps` (a blob that is gone, bytes of another size or hash than were published, or a file that does not begin like its container), each naming the check that holds the video. `run` is the key of the run the layer belongs to (a `RunSummary.key`), so a viewer can mark that run's recordings as stored-then-unavailable beside its test results.
6118
+
6119
+ This route never reads a blob: it returns what a publish token's `POST` found earlier. A layer that was never read back is absent, which means "not verified", not "fine"; the result is kept in the server's memory only, so a restart empties it. Only layers a store still holds are listed (one removed by garbage collection drops out). No status, slot or winner depends on it. It checks stored bytes, not browser playback. Narrow with `?run=` (a run key) and `?layer=` (an id prefix of at least 7 hex digits, repeatable).
6120
+
6121
+ - `run` (query): Only layers of this run (a key from `GET /api/reports/runs`).
6122
+ - `layer` (query): Only layers whose id starts with this prefix. Repeatable or comma-separated.
6123
+ - `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
6124
+
6125
+ Responses: `200` The held read-backs.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.
6126
+
6127
+ ### Which stored recordings the server last read back, and which were gone or damaged
6128
+
6129
+ `GET /api/projects/{project}/reports/recordings/verification` · role: read
6130
+
6131
+ The newest read-back the server did of stored videos, per layer: how many it checked, how many passed and the `gaps` (a blob that is gone, bytes of another size or hash than were published, or a file that does not begin like its container), each naming the check that holds the video. `run` is the key of the run the layer belongs to (a `RunSummary.key`), so a viewer can mark that run's recordings as stored-then-unavailable beside its test results.
6132
+
6133
+ This route never reads a blob: it returns what a publish token's `POST` found earlier. A layer that was never read back is absent, which means "not verified", not "fine"; the result is kept in the server's memory only, so a restart empties it. Only layers a store still holds are listed (one removed by garbage collection drops out). No status, slot or winner depends on it. It checks stored bytes, not browser playback. Narrow with `?run=` (a run key) and `?layer=` (an id prefix of at least 7 hex digits, repeatable).
6134
+
6135
+ 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.
6136
+
6137
+ - `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.
6138
+ - `run` (query): Only layers of this run (a key from `GET /api/reports/runs`).
6139
+ - `layer` (query): Only layers whose id starts with this prefix. Repeatable or comma-separated.
6140
+
6141
+ Responses: `200` The held read-backs.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.
6142
+
6143
+ ### Read stored recordings back and check them (publish token)
6144
+
6145
+ `POST /api/reports/recordings/verification` · role: publish
6146
+
6147
+ Reads the videos of the newest layers (`limit`, default 20, at most 200; or the layers `layers` names) back from storage and checks that each is still stored, still has the size and SHA-256 published, and still begins like its media type's container. This reads blobs, so it needs a publish (or admin) token in a Bearer header; a reader-only token, a browser sign-in box and an anonymous reader are refused. One read-back runs at a time (`409` otherwise). It writes nothing to storage and changes no status. The answer is the same document `GET` returns, with `verified`: how many layers this pass read back.
6148
+
6149
+ - `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
6150
+
6151
+ Responses: `200` The read-backs now held.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `403` The token is known but its role may not do this.; `409` A read-back is already running.; `413` Request too large.; `422` Not a verification request.; `502` A store could not be read.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
6152
+
6153
+ ### Read stored recordings back and check them (publish token)
6154
+
6155
+ `POST /api/projects/{project}/reports/recordings/verification` · role: publish
6156
+
6157
+ Reads the videos of the newest layers (`limit`, default 20, at most 200; or the layers `layers` names) back from storage and checks that each is still stored, still has the size and SHA-256 published, and still begins like its media type's container. This reads blobs, so it needs a publish (or admin) token in a Bearer header; a reader-only token, a browser sign-in box and an anonymous reader are refused. One read-back runs at a time (`409` otherwise). It writes nothing to storage and changes no status. The answer is the same document `GET` returns, with `verified`: how many layers this pass read back.
6158
+
6159
+ 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.
6160
+
6161
+ - `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.
6162
+
6163
+ Responses: `200` The read-backs now held.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `403` The token is known but its role may not do this.; `409` A read-back is already running.; `413` Request too large.; `422` Not a verification request.; `502` A store could not be read.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
6164
+
6089
6165
  ### Review collections: curated sets of recordings, newest first
6090
6166
 
6091
6167
  `GET /api/reports/review-collections` · role: read
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crvouga/atlas",
3
- "version": "1.19.0",
3
+ "version": "1.20.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",
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "Atlas server API",
5
- "version": "1.6.0",
5
+ "version": "1.7.0",
6
6
  "summary": "Take in test results from every environment, keep them, and show everyone the combined report, live.",
7
7
  "description": "One Atlas node serves multiple projects. Clients select a project using ?project=<slug>, X-Atlas-Project, or /api/projects/<slug>/… . Each project has independent specs, layers, recordings, status, and event streams. Within each project, the newest completed result of every check wins its slot. Unqualified requests address the legacy default project.\n\n**Authentication.** With tokens configured, send `Authorization: Bearer <token>`. Roles: `read` < `critic` < `publish` < `admin`. A `critic` token may read, and publish critiques (layers of automated reviewers) and nothing else. Uploading needs a Bearer token; a browser's HTTP Basic sign-in (any user name, the token as the password) is accepted for reading only. A server with no tokens is open and listens on this machine only unless told otherwise.\n\n**Errors** are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem documents (`application/problem+json`) with a `detail` that says what to do. `401` send a token, `403` that token may not, `409` upload what this depends on first or you are changing something immutable, `413` too large, `422` not valid, `503` still starting (retry after `Retry-After`).\n\n**Compatibility.** Within `/api` the API only grows: optional parameters, optional response fields, new events. Ignore what you do not know.\n\nGuides: `/llms.txt`. Source: https://github.com/crvouga/atlas.",
8
8
  "license": {
@@ -4802,6 +4802,394 @@
4802
4802
  }
4803
4803
  }
4804
4804
  },
4805
+ "/api/reports/recordings/verification": {
4806
+ "get": {
4807
+ "operationId": "getRecordingVerification",
4808
+ "tags": [
4809
+ "Report"
4810
+ ],
4811
+ "summary": "Which stored recordings the server last read back, and which were gone or damaged",
4812
+ "description": "The newest read-back the server did of stored videos, per layer: how many it checked, how many passed and the `gaps` (a blob that is gone, bytes of another size or hash than were published, or a file that does not begin like its container), each naming the check that holds the video. `run` is the key of the run the layer belongs to (a `RunSummary.key`), so a viewer can mark that run's recordings as stored-then-unavailable beside its test results.\n\nThis route never reads a blob: it returns what a publish token's `POST` found earlier. A layer that was never read back is absent, which means \"not verified\", not \"fine\"; the result is kept in the server's memory only, so a restart empties it. Only layers a store still holds are listed (one removed by garbage collection drops out). No status, slot or winner depends on it. It checks stored bytes, not browser playback. Narrow with `?run=` (a run key) and `?layer=` (an id prefix of at least 7 hex digits, repeatable).\n\nRole needed: `read`.",
4813
+ "x-atlas-role": "read",
4814
+ "security": [
4815
+ {
4816
+ "bearer": []
4817
+ },
4818
+ {
4819
+ "basic": []
4820
+ }
4821
+ ],
4822
+ "parameters": [
4823
+ {
4824
+ "required": false,
4825
+ "name": "run",
4826
+ "in": "query",
4827
+ "description": "Only layers of this run (a key from `GET /api/reports/runs`).",
4828
+ "schema": {
4829
+ "type": "string"
4830
+ }
4831
+ },
4832
+ {
4833
+ "required": false,
4834
+ "name": "layer",
4835
+ "in": "query",
4836
+ "description": "Only layers whose id starts with this prefix. Repeatable or comma-separated.",
4837
+ "schema": {
4838
+ "type": "string"
4839
+ }
4840
+ },
4841
+ {
4842
+ "required": false,
4843
+ "name": "project",
4844
+ "in": "query",
4845
+ "description": "Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.",
4846
+ "example": "todo-app",
4847
+ "schema": {
4848
+ "type": "string",
4849
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
4850
+ "maxLength": 64
4851
+ }
4852
+ }
4853
+ ],
4854
+ "responses": {
4855
+ "200": {
4856
+ "description": "The held read-backs.",
4857
+ "content": {
4858
+ "application/json": {
4859
+ "schema": {
4860
+ "$ref": "#/components/schemas/RecordingVerification"
4861
+ }
4862
+ }
4863
+ }
4864
+ },
4865
+ "401": {
4866
+ "description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
4867
+ "content": {
4868
+ "application/problem+json": {
4869
+ "schema": {
4870
+ "$ref": "#/components/schemas/Problem"
4871
+ }
4872
+ }
4873
+ }
4874
+ }
4875
+ }
4876
+ },
4877
+ "post": {
4878
+ "operationId": "verifyRecordings",
4879
+ "tags": [
4880
+ "Report"
4881
+ ],
4882
+ "summary": "Read stored recordings back and check them (publish token)",
4883
+ "description": "Reads the videos of the newest layers (`limit`, default 20, at most 200; or the layers `layers` names) back from storage and checks that each is still stored, still has the size and SHA-256 published, and still begins like its media type's container. This reads blobs, so it needs a publish (or admin) token in a Bearer header; a reader-only token, a browser sign-in box and an anonymous reader are refused. One read-back runs at a time (`409` otherwise). It writes nothing to storage and changes no status. The answer is the same document `GET` returns, with `verified`: how many layers this pass read back.\n\nRole needed: `publish`.",
4884
+ "x-atlas-role": "publish",
4885
+ "security": [
4886
+ {
4887
+ "bearer": []
4888
+ }
4889
+ ],
4890
+ "parameters": [
4891
+ {
4892
+ "required": false,
4893
+ "name": "project",
4894
+ "in": "query",
4895
+ "description": "Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.",
4896
+ "example": "todo-app",
4897
+ "schema": {
4898
+ "type": "string",
4899
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
4900
+ "maxLength": 64
4901
+ }
4902
+ }
4903
+ ],
4904
+ "requestBody": {
4905
+ "required": false,
4906
+ "description": "Optional `layers` (id prefixes) and `limit`. An empty body reads back the newest layers.",
4907
+ "content": {
4908
+ "application/json": {
4909
+ "schema": {
4910
+ "$ref": "#/components/schemas/RecordingVerifyRequest"
4911
+ }
4912
+ }
4913
+ }
4914
+ },
4915
+ "responses": {
4916
+ "200": {
4917
+ "description": "The read-backs now held.",
4918
+ "content": {
4919
+ "application/json": {
4920
+ "schema": {
4921
+ "$ref": "#/components/schemas/RecordingVerification"
4922
+ }
4923
+ }
4924
+ }
4925
+ },
4926
+ "401": {
4927
+ "description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
4928
+ "content": {
4929
+ "application/problem+json": {
4930
+ "schema": {
4931
+ "$ref": "#/components/schemas/Problem"
4932
+ }
4933
+ }
4934
+ }
4935
+ },
4936
+ "403": {
4937
+ "description": "The token is known but its role may not do this.",
4938
+ "content": {
4939
+ "application/problem+json": {
4940
+ "schema": {
4941
+ "$ref": "#/components/schemas/Problem"
4942
+ }
4943
+ }
4944
+ }
4945
+ },
4946
+ "409": {
4947
+ "description": "A read-back is already running.",
4948
+ "content": {
4949
+ "application/problem+json": {
4950
+ "schema": {
4951
+ "$ref": "#/components/schemas/Problem"
4952
+ }
4953
+ }
4954
+ }
4955
+ },
4956
+ "413": {
4957
+ "description": "Request too large.",
4958
+ "content": {
4959
+ "application/problem+json": {
4960
+ "schema": {
4961
+ "$ref": "#/components/schemas/Problem"
4962
+ }
4963
+ }
4964
+ }
4965
+ },
4966
+ "422": {
4967
+ "description": "Not a verification request.",
4968
+ "content": {
4969
+ "application/problem+json": {
4970
+ "schema": {
4971
+ "$ref": "#/components/schemas/Problem"
4972
+ }
4973
+ }
4974
+ }
4975
+ },
4976
+ "502": {
4977
+ "description": "A store could not be read.",
4978
+ "content": {
4979
+ "application/problem+json": {
4980
+ "schema": {
4981
+ "$ref": "#/components/schemas/Problem"
4982
+ }
4983
+ }
4984
+ }
4985
+ },
4986
+ "503": {
4987
+ "description": "The server is still reading its backends. Retry after the number of seconds in `Retry-After`.",
4988
+ "content": {
4989
+ "application/problem+json": {
4990
+ "schema": {
4991
+ "$ref": "#/components/schemas/Problem"
4992
+ }
4993
+ }
4994
+ }
4995
+ }
4996
+ }
4997
+ }
4998
+ },
4999
+ "/api/projects/{project}/reports/recordings/verification": {
5000
+ "get": {
5001
+ "operationId": "getRecordingVerificationForProject",
5002
+ "tags": [
5003
+ "Report"
5004
+ ],
5005
+ "summary": "Which stored recordings the server last read back, and which were gone or damaged",
5006
+ "description": "The newest read-back the server did of stored videos, per layer: how many it checked, how many passed and the `gaps` (a blob that is gone, bytes of another size or hash than were published, or a file that does not begin like its container), each naming the check that holds the video. `run` is the key of the run the layer belongs to (a `RunSummary.key`), so a viewer can mark that run's recordings as stored-then-unavailable beside its test results.\n\nThis route never reads a blob: it returns what a publish token's `POST` found earlier. A layer that was never read back is absent, which means \"not verified\", not \"fine\"; the result is kept in the server's memory only, so a restart empties it. Only layers a store still holds are listed (one removed by garbage collection drops out). No status, slot or winner depends on it. It checks stored bytes, not browser playback. Narrow with `?run=` (a run key) and `?layer=` (an id prefix of at least 7 hex digits, repeatable).\n\nScoped 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.\n\nRole needed: `read`.",
5007
+ "x-atlas-role": "read",
5008
+ "security": [
5009
+ {
5010
+ "bearer": []
5011
+ },
5012
+ {
5013
+ "basic": []
5014
+ }
5015
+ ],
5016
+ "parameters": [
5017
+ {
5018
+ "required": true,
5019
+ "name": "project",
5020
+ "in": "path",
5021
+ "description": "Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.",
5022
+ "example": "todo-app",
5023
+ "schema": {
5024
+ "type": "string",
5025
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
5026
+ "maxLength": 64
5027
+ }
5028
+ },
5029
+ {
5030
+ "required": false,
5031
+ "name": "run",
5032
+ "in": "query",
5033
+ "description": "Only layers of this run (a key from `GET /api/reports/runs`).",
5034
+ "schema": {
5035
+ "type": "string"
5036
+ }
5037
+ },
5038
+ {
5039
+ "required": false,
5040
+ "name": "layer",
5041
+ "in": "query",
5042
+ "description": "Only layers whose id starts with this prefix. Repeatable or comma-separated.",
5043
+ "schema": {
5044
+ "type": "string"
5045
+ }
5046
+ }
5047
+ ],
5048
+ "responses": {
5049
+ "200": {
5050
+ "description": "The held read-backs.",
5051
+ "content": {
5052
+ "application/json": {
5053
+ "schema": {
5054
+ "$ref": "#/components/schemas/RecordingVerification"
5055
+ }
5056
+ }
5057
+ }
5058
+ },
5059
+ "401": {
5060
+ "description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
5061
+ "content": {
5062
+ "application/problem+json": {
5063
+ "schema": {
5064
+ "$ref": "#/components/schemas/Problem"
5065
+ }
5066
+ }
5067
+ }
5068
+ }
5069
+ }
5070
+ },
5071
+ "post": {
5072
+ "operationId": "verifyRecordingsForProject",
5073
+ "tags": [
5074
+ "Report"
5075
+ ],
5076
+ "summary": "Read stored recordings back and check them (publish token)",
5077
+ "description": "Reads the videos of the newest layers (`limit`, default 20, at most 200; or the layers `layers` names) back from storage and checks that each is still stored, still has the size and SHA-256 published, and still begins like its media type's container. This reads blobs, so it needs a publish (or admin) token in a Bearer header; a reader-only token, a browser sign-in box and an anonymous reader are refused. One read-back runs at a time (`409` otherwise). It writes nothing to storage and changes no status. The answer is the same document `GET` returns, with `verified`: how many layers this pass read back.\n\nScoped 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.\n\nRole needed: `publish`.",
5078
+ "x-atlas-role": "publish",
5079
+ "security": [
5080
+ {
5081
+ "bearer": []
5082
+ }
5083
+ ],
5084
+ "parameters": [
5085
+ {
5086
+ "required": true,
5087
+ "name": "project",
5088
+ "in": "path",
5089
+ "description": "Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.",
5090
+ "example": "todo-app",
5091
+ "schema": {
5092
+ "type": "string",
5093
+ "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
5094
+ "maxLength": 64
5095
+ }
5096
+ }
5097
+ ],
5098
+ "requestBody": {
5099
+ "required": false,
5100
+ "description": "Optional `layers` (id prefixes) and `limit`. An empty body reads back the newest layers.",
5101
+ "content": {
5102
+ "application/json": {
5103
+ "schema": {
5104
+ "$ref": "#/components/schemas/RecordingVerifyRequest"
5105
+ }
5106
+ }
5107
+ }
5108
+ },
5109
+ "responses": {
5110
+ "200": {
5111
+ "description": "The read-backs now held.",
5112
+ "content": {
5113
+ "application/json": {
5114
+ "schema": {
5115
+ "$ref": "#/components/schemas/RecordingVerification"
5116
+ }
5117
+ }
5118
+ }
5119
+ },
5120
+ "401": {
5121
+ "description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
5122
+ "content": {
5123
+ "application/problem+json": {
5124
+ "schema": {
5125
+ "$ref": "#/components/schemas/Problem"
5126
+ }
5127
+ }
5128
+ }
5129
+ },
5130
+ "403": {
5131
+ "description": "The token is known but its role may not do this.",
5132
+ "content": {
5133
+ "application/problem+json": {
5134
+ "schema": {
5135
+ "$ref": "#/components/schemas/Problem"
5136
+ }
5137
+ }
5138
+ }
5139
+ },
5140
+ "409": {
5141
+ "description": "A read-back is already running.",
5142
+ "content": {
5143
+ "application/problem+json": {
5144
+ "schema": {
5145
+ "$ref": "#/components/schemas/Problem"
5146
+ }
5147
+ }
5148
+ }
5149
+ },
5150
+ "413": {
5151
+ "description": "Request too large.",
5152
+ "content": {
5153
+ "application/problem+json": {
5154
+ "schema": {
5155
+ "$ref": "#/components/schemas/Problem"
5156
+ }
5157
+ }
5158
+ }
5159
+ },
5160
+ "422": {
5161
+ "description": "Not a verification request.",
5162
+ "content": {
5163
+ "application/problem+json": {
5164
+ "schema": {
5165
+ "$ref": "#/components/schemas/Problem"
5166
+ }
5167
+ }
5168
+ }
5169
+ },
5170
+ "502": {
5171
+ "description": "A store could not be read.",
5172
+ "content": {
5173
+ "application/problem+json": {
5174
+ "schema": {
5175
+ "$ref": "#/components/schemas/Problem"
5176
+ }
5177
+ }
5178
+ }
5179
+ },
5180
+ "503": {
5181
+ "description": "The server is still reading its backends. Retry after the number of seconds in `Retry-After`.",
5182
+ "content": {
5183
+ "application/problem+json": {
5184
+ "schema": {
5185
+ "$ref": "#/components/schemas/Problem"
5186
+ }
5187
+ }
5188
+ }
5189
+ }
5190
+ }
5191
+ }
5192
+ },
4805
5193
  "/api/reports/review-collections": {
4806
5194
  "get": {
4807
5195
  "operationId": "listReviewCollections",
@@ -20225,6 +20613,144 @@
20225
20613
  ],
20226
20614
  "additionalProperties": {}
20227
20615
  },
20616
+ "RecordingVerification": {
20617
+ "type": "object",
20618
+ "properties": {
20619
+ "schemaVersion": {
20620
+ "type": "number",
20621
+ "const": 1
20622
+ },
20623
+ "layers": {
20624
+ "type": "array",
20625
+ "items": {
20626
+ "type": "object",
20627
+ "properties": {
20628
+ "layer": {
20629
+ "type": "string",
20630
+ "description": "The layer id"
20631
+ },
20632
+ "run": {
20633
+ "type": "string",
20634
+ "description": "The key of the run the layer belongs to (a `RunSummary.key`)"
20635
+ },
20636
+ "verifiedAtMs": {
20637
+ "type": "number",
20638
+ "description": "When the server read the videos back, epoch milliseconds"
20639
+ },
20640
+ "checked": {
20641
+ "type": "integer",
20642
+ "minimum": -9007199254740991,
20643
+ "maximum": 9007199254740991,
20644
+ "description": "Videos read back: those that name a bundle key"
20645
+ },
20646
+ "ok": {
20647
+ "type": "integer",
20648
+ "minimum": -9007199254740991,
20649
+ "maximum": 9007199254740991,
20650
+ "description": "Of those, the ones that passed every check"
20651
+ },
20652
+ "referenced": {
20653
+ "type": "integer",
20654
+ "minimum": -9007199254740991,
20655
+ "maximum": 9007199254740991,
20656
+ "description": "Videos given only by address, which Atlas cannot read back and so does not vouch for"
20657
+ },
20658
+ "gaps": {
20659
+ "type": "array",
20660
+ "items": {
20661
+ "type": "object",
20662
+ "properties": {
20663
+ "check": {
20664
+ "description": "The id of the check that holds the video, or null for a video attached to the run or a part",
20665
+ "type": [
20666
+ "string",
20667
+ "null"
20668
+ ]
20669
+ },
20670
+ "name": {
20671
+ "type": "string",
20672
+ "description": "The attachment's name"
20673
+ },
20674
+ "bundleKey": {
20675
+ "type": "string",
20676
+ "description": "The key the layer names the file by"
20677
+ },
20678
+ "problem": {
20679
+ "type": "string",
20680
+ "description": "`not-in-bundle`, `blob-missing`, `unreadable`, `size-mismatch`, `checksum-mismatch` or `undecodable`. An open vocabulary: treat an unknown value as a gap"
20681
+ },
20682
+ "reason": {
20683
+ "type": "string",
20684
+ "description": "A short explanation without secrets or paths"
20685
+ }
20686
+ },
20687
+ "required": [
20688
+ "check",
20689
+ "name",
20690
+ "bundleKey",
20691
+ "problem",
20692
+ "reason"
20693
+ ],
20694
+ "additionalProperties": {}
20695
+ },
20696
+ "description": "The videos that did not pass. Never a test result: statuses are unchanged"
20697
+ }
20698
+ },
20699
+ "required": [
20700
+ "layer",
20701
+ "run",
20702
+ "verifiedAtMs",
20703
+ "checked",
20704
+ "ok",
20705
+ "referenced",
20706
+ "gaps"
20707
+ ],
20708
+ "additionalProperties": {}
20709
+ },
20710
+ "description": "Layers the server has read back and still holds, newest first"
20711
+ },
20712
+ "notices": {
20713
+ "type": "array",
20714
+ "items": {
20715
+ "type": "string"
20716
+ },
20717
+ "description": "Layers the last pass could not read at all: their recordings are unknown, not verified"
20718
+ },
20719
+ "verified": {
20720
+ "description": "POST only: how many layers this pass read back",
20721
+ "type": "integer",
20722
+ "minimum": -9007199254740991,
20723
+ "maximum": 9007199254740991
20724
+ }
20725
+ },
20726
+ "required": [
20727
+ "schemaVersion",
20728
+ "layers",
20729
+ "notices"
20730
+ ],
20731
+ "additionalProperties": {}
20732
+ },
20733
+ "RecordingVerifyRequest": {
20734
+ "type": "object",
20735
+ "properties": {
20736
+ "layers": {
20737
+ "description": "Only layers whose id starts with one of these (at least 7 hex digits)",
20738
+ "maxItems": 50,
20739
+ "type": "array",
20740
+ "items": {
20741
+ "type": "string",
20742
+ "pattern": "^[0-9a-f]{7,64}$"
20743
+ }
20744
+ },
20745
+ "limit": {
20746
+ "description": "How many of the newest layers to read back. Default 20, at most 200",
20747
+ "type": "integer",
20748
+ "minimum": 1,
20749
+ "maximum": 200
20750
+ }
20751
+ },
20752
+ "additionalProperties": {}
20753
+ },
20228
20754
  "TestCatalog": {
20229
20755
  "type": "object",
20230
20756
  "properties": {