@crvouga/atlas 0.16.0 → 1.0.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 (91) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +52 -8
  3. package/dist/core/src/cli.d.ts +1 -1
  4. package/dist/core/src/cli.js +1 -1
  5. package/dist/reports/src/attachment-files.d.ts +3 -1
  6. package/dist/reports/src/attachment-files.js +14 -4
  7. package/dist/reports/src/evidence-types.d.ts +22 -0
  8. package/dist/reports/src/garbage.d.ts +39 -0
  9. package/dist/reports/src/garbage.js +82 -0
  10. package/dist/reports/src/layer-merge.js +5 -0
  11. package/dist/reports/src/layer-types.d.ts +1 -1
  12. package/dist/reports/src/publish-checks.js +11 -3
  13. package/dist/reports/src/recording-receipt.d.ts +48 -0
  14. package/dist/reports/src/recording-receipt.js +103 -0
  15. package/dist/reports/src/report-cli.d.ts +1 -1
  16. package/dist/reports/src/report-cli.js +20 -2
  17. package/dist/reports/src/report-index.d.ts +6 -0
  18. package/dist/reports/src/report-index.js +14 -0
  19. package/dist/reports/src/report-status.d.ts +2 -0
  20. package/dist/reports/src/report-status.js +17 -1
  21. package/dist/reports/src/reports-explorer-route.d.ts +8 -0
  22. package/dist/reports/src/reports-route.d.ts +8 -0
  23. package/dist/schema/src/api.d.ts +65 -0
  24. package/dist/schema/src/api.js +7 -2
  25. package/dist/schema/src/cli-results.d.ts +107 -2
  26. package/dist/schema/src/cli-results.js +11 -3
  27. package/dist/schema/src/index.d.ts +1 -0
  28. package/dist/schema/src/index.js +1 -0
  29. package/dist/schema/src/layer.d.ts +165 -0
  30. package/dist/schema/src/layer.js +72 -1
  31. package/dist/schema/src/server-config.d.ts +7 -49
  32. package/dist/schema/src/server-config.js +8 -21
  33. package/dist/schema/src/user-state.d.ts +115 -0
  34. package/dist/schema/src/user-state.js +54 -0
  35. package/dist/server/src/app.d.ts +16 -2
  36. package/dist/server/src/app.js +62 -0
  37. package/dist/server/src/config.d.ts +16 -21
  38. package/dist/server/src/config.js +48 -57
  39. package/dist/server/src/dev.d.ts +1 -1
  40. package/dist/server/src/dev.js +20 -7
  41. package/dist/server/src/index.d.ts +5 -1
  42. package/dist/server/src/index.js +5 -1
  43. package/dist/server/src/main.d.ts +23 -7
  44. package/dist/server/src/main.js +72 -26
  45. package/dist/server/src/openapi.js +24 -5
  46. package/dist/server/src/projects.js +89 -14
  47. package/dist/server/src/settings.d.ts +23 -7
  48. package/dist/server/src/settings.js +50 -60
  49. package/dist/server/src/storage.d.ts +68 -0
  50. package/dist/server/src/storage.js +125 -0
  51. package/dist/server/src/user-state.d.ts +31 -0
  52. package/dist/server/src/user-state.js +74 -0
  53. package/docs/agents.md +1 -1
  54. package/docs/deploying.md +38 -39
  55. package/docs/reference/cli.md +17 -7
  56. package/docs/reference/http-api.md +30 -2
  57. package/docs/reference/reports.md +165 -2
  58. package/docs/reference/schema.md +161 -16
  59. package/docs/reference/server.md +263 -41
  60. package/docs/report-backends.md +0 -144
  61. package/docs/report-contract.md +1 -1
  62. package/docs/rich-evidence.md +29 -0
  63. package/docs/runners.md +1 -1
  64. package/docs/server.md +141 -36
  65. package/docs/statecharts.md +1 -1
  66. package/docs/ui.md +1 -1
  67. package/docs/workspaces.md +5 -2
  68. package/llms-full.txt +265 -234
  69. package/llms.txt +1 -1
  70. package/package.json +2 -2
  71. package/packages/reports/STORAGE.md +1 -1
  72. package/packages/schema/schemas/check-detail.schema.json +20 -0
  73. package/packages/schema/schemas/effective-delta.schema.json +61 -0
  74. package/packages/schema/schemas/effective-evidence.schema.json +61 -0
  75. package/packages/schema/schemas/evidence-page.schema.json +45 -0
  76. package/packages/schema/schemas/gc-policy.schema.json +36 -0
  77. package/packages/schema/schemas/gc-run-result.schema.json +106 -0
  78. package/packages/schema/schemas/gc-snapshot.schema.json +122 -0
  79. package/packages/schema/schemas/layer-rows.schema.json +16 -0
  80. package/packages/schema/schemas/layer.schema.json +153 -0
  81. package/packages/schema/schemas/publish-checks.schema.json +24 -0
  82. package/packages/schema/schemas/publish-result.schema.json +161 -1
  83. package/packages/schema/schemas/report-status.schema.json +104 -0
  84. package/packages/schema/schemas/server-config.schema.json +0 -41
  85. package/packages/schema/schemas/server-editable-settings.schema.json +0 -41
  86. package/packages/schema/schemas/server-settings-snapshot.schema.json +0 -82
  87. package/packages/schema/schemas/user-state.schema.json +140 -0
  88. package/packages/server/README.md +11 -6
  89. package/packages/server/openapi.json +843 -128
  90. package/packages/visualizer/src/admin/SettingsPage.tsx +183 -88
  91. package/packages/visualizer/src/evidence/marks.tsx +16 -0
@@ -1019,7 +1019,7 @@
1019
1019
  "Operations"
1020
1020
  ],
1021
1021
  "summary": "Read server settings",
1022
- "description": "Returns editable persisted storage/report settings, active settings, a revision, and configuration precedence. Authentication settings are excluded. Requires an admin Bearer token. Embedded applications may manage settings outside this API.\n\nRole needed: `admin`.",
1022
+ "description": "Returns the editable report and live-run settings saved in the user state in S3, active settings, a revision, and configuration precedence. Storage and authentication settings are excluded. Requires an admin Bearer token. Embedded applications may manage settings outside this API.\n\nRole needed: `admin`.",
1023
1023
  "x-atlas-role": "admin",
1024
1024
  "security": [
1025
1025
  {
@@ -1075,7 +1075,7 @@
1075
1075
  "Operations"
1076
1076
  ],
1077
1077
  "summary": "Save settings for the next restart",
1078
- "description": "Replaces editable storage/report settings, preserving authentication and other file fields. Atomically persists a validated configuration; no report data is moved. Environment variables and flags override these values after restart. Requires an admin Bearer token, same-origin requests in browsers, and a current revision.\n\nRole needed: `admin`.",
1078
+ "description": "Replaces the editable report and live-run settings in the user state in S3 (`server/state.json`), preserving the garbage collection policy and other fields. Persists a validated configuration; no report data is moved. Environment variables and flags override these values after restart. Requires an admin Bearer token, same-origin requests in browsers, and a current revision.\n\nRole needed: `admin`.",
1079
1079
  "x-atlas-role": "admin",
1080
1080
  "security": [
1081
1081
  {
@@ -1095,7 +1095,7 @@
1095
1095
  ],
1096
1096
  "requestBody": {
1097
1097
  "required": true,
1098
- "description": "Editable fields from ServerConfig, excluding authentication, host, port and site. Maximum 64 KiB.",
1098
+ "description": "Editable fields from ServerConfig, excluding storage, authentication, host, port and site. Maximum 64 KiB.",
1099
1099
  "content": {
1100
1100
  "application/json": {
1101
1101
  "schema": {
@@ -1188,6 +1188,251 @@
1188
1188
  }
1189
1189
  }
1190
1190
  },
1191
+ "/api/settings/gc": {
1192
+ "get": {
1193
+ "operationId": "getGarbageCollectionPolicy",
1194
+ "tags": [
1195
+ "Operations"
1196
+ ],
1197
+ "summary": "Read the garbage collection policy",
1198
+ "description": "Returns the policy kept in the user state in S3 (`server/state.json`), or `policy: null, saved: false` when none has been saved yet, in which case garbage collection is off and nothing is deleted. Also returns the revision to send back when saving and the last pass over each project. Requires an admin Bearer token. If the stored user state is unreadable the answer is 503 and the stored object is left untouched.\n\nRole needed: `admin`.",
1199
+ "x-atlas-role": "admin",
1200
+ "security": [
1201
+ {
1202
+ "bearer": []
1203
+ }
1204
+ ],
1205
+ "responses": {
1206
+ "200": {
1207
+ "description": "The policy, its revision and the last passes.",
1208
+ "content": {
1209
+ "application/json": {
1210
+ "schema": {
1211
+ "$ref": "#/components/schemas/GcSnapshot"
1212
+ }
1213
+ }
1214
+ }
1215
+ },
1216
+ "401": {
1217
+ "description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
1218
+ "content": {
1219
+ "application/problem+json": {
1220
+ "schema": {
1221
+ "$ref": "#/components/schemas/Problem"
1222
+ }
1223
+ }
1224
+ }
1225
+ },
1226
+ "403": {
1227
+ "description": "The token is known but its role may not do this.",
1228
+ "content": {
1229
+ "application/problem+json": {
1230
+ "schema": {
1231
+ "$ref": "#/components/schemas/Problem"
1232
+ }
1233
+ }
1234
+ }
1235
+ },
1236
+ "503": {
1237
+ "description": "The user state in S3 cannot be read, or settings are managed by the embedding application.",
1238
+ "content": {
1239
+ "application/problem+json": {
1240
+ "schema": {
1241
+ "$ref": "#/components/schemas/Problem"
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+ }
1247
+ },
1248
+ "put": {
1249
+ "operationId": "saveGarbageCollectionPolicy",
1250
+ "tags": [
1251
+ "Operations"
1252
+ ],
1253
+ "summary": "Save the garbage collection policy",
1254
+ "description": "Replaces the policy in the user state in S3 and leaves the rest of that document alone. The server reads the policy from S3 each time it collects, so the new policy applies at the next pass, without a restart. Requires an admin Bearer token, same-origin requests in browsers, and the current revision.\n\nRole needed: `admin`.",
1255
+ "x-atlas-role": "admin",
1256
+ "security": [
1257
+ {
1258
+ "bearer": []
1259
+ }
1260
+ ],
1261
+ "parameters": [
1262
+ {
1263
+ "required": true,
1264
+ "name": "If-Match",
1265
+ "in": "header",
1266
+ "description": "The quoted revision returned by GET /api/settings/gc.",
1267
+ "schema": {
1268
+ "type": "string"
1269
+ }
1270
+ }
1271
+ ],
1272
+ "requestBody": {
1273
+ "required": true,
1274
+ "description": "The policy. Maximum 16 KiB.",
1275
+ "content": {
1276
+ "application/json": {
1277
+ "schema": {
1278
+ "$ref": "#/components/schemas/GcPolicy"
1279
+ }
1280
+ }
1281
+ }
1282
+ },
1283
+ "responses": {
1284
+ "200": {
1285
+ "description": "The saved policy and the new revision.",
1286
+ "content": {
1287
+ "application/json": {
1288
+ "schema": {
1289
+ "$ref": "#/components/schemas/GcSnapshot"
1290
+ }
1291
+ }
1292
+ }
1293
+ },
1294
+ "401": {
1295
+ "description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
1296
+ "content": {
1297
+ "application/problem+json": {
1298
+ "schema": {
1299
+ "$ref": "#/components/schemas/Problem"
1300
+ }
1301
+ }
1302
+ }
1303
+ },
1304
+ "403": {
1305
+ "description": "The token is known but its role may not do this.",
1306
+ "content": {
1307
+ "application/problem+json": {
1308
+ "schema": {
1309
+ "$ref": "#/components/schemas/Problem"
1310
+ }
1311
+ }
1312
+ }
1313
+ },
1314
+ "409": {
1315
+ "description": "The state changed; reload before saving.",
1316
+ "content": {
1317
+ "application/problem+json": {
1318
+ "schema": {
1319
+ "$ref": "#/components/schemas/Problem"
1320
+ }
1321
+ }
1322
+ }
1323
+ },
1324
+ "413": {
1325
+ "description": "The body exceeds the limit.",
1326
+ "content": {
1327
+ "application/problem+json": {
1328
+ "schema": {
1329
+ "$ref": "#/components/schemas/Problem"
1330
+ }
1331
+ }
1332
+ }
1333
+ },
1334
+ "422": {
1335
+ "description": "Invalid policy.",
1336
+ "content": {
1337
+ "application/problem+json": {
1338
+ "schema": {
1339
+ "$ref": "#/components/schemas/Problem"
1340
+ }
1341
+ }
1342
+ }
1343
+ },
1344
+ "428": {
1345
+ "description": "A quoted revision is required.",
1346
+ "content": {
1347
+ "application/problem+json": {
1348
+ "schema": {
1349
+ "$ref": "#/components/schemas/Problem"
1350
+ }
1351
+ }
1352
+ }
1353
+ },
1354
+ "503": {
1355
+ "description": "The user state in S3 cannot be read, or settings are managed by the embedding application.",
1356
+ "content": {
1357
+ "application/problem+json": {
1358
+ "schema": {
1359
+ "$ref": "#/components/schemas/Problem"
1360
+ }
1361
+ }
1362
+ }
1363
+ }
1364
+ }
1365
+ }
1366
+ },
1367
+ "/api/settings/gc/run": {
1368
+ "post": {
1369
+ "operationId": "runGarbageCollection",
1370
+ "tags": [
1371
+ "Operations"
1372
+ ],
1373
+ "summary": "Collect garbage now, or preview it",
1374
+ "description": "Applies the policy saved in S3 to every project now, instead of waiting for the schedule. Only report layers that were read and no longer decide any result, older than the policy allows and beyond its `keepLatest`, are deleted, with the captured files only they used. With `dryRun=1` nothing is deleted and each run reports what it would delete (a disabled policy is previewed as if enabled). Answers 409 when no policy has been saved, and, without `dryRun`, when the saved policy is disabled. Requires an admin Bearer token and a same-origin request in browsers.\n\nRole needed: `admin`.",
1375
+ "x-atlas-role": "admin",
1376
+ "security": [
1377
+ {
1378
+ "bearer": []
1379
+ }
1380
+ ],
1381
+ "parameters": [
1382
+ {
1383
+ "required": false,
1384
+ "name": "dryRun",
1385
+ "in": "query",
1386
+ "description": "`1` or `true` to only count what would be deleted.",
1387
+ "schema": {
1388
+ "type": "string"
1389
+ }
1390
+ }
1391
+ ],
1392
+ "responses": {
1393
+ "200": {
1394
+ "description": "One run per project.",
1395
+ "content": {
1396
+ "application/json": {
1397
+ "schema": {
1398
+ "$ref": "#/components/schemas/GcRunResult"
1399
+ }
1400
+ }
1401
+ }
1402
+ },
1403
+ "401": {
1404
+ "description": "No token, or an unknown one. Send `Authorization: Bearer <token>`.",
1405
+ "content": {
1406
+ "application/problem+json": {
1407
+ "schema": {
1408
+ "$ref": "#/components/schemas/Problem"
1409
+ }
1410
+ }
1411
+ }
1412
+ },
1413
+ "403": {
1414
+ "description": "The token is known but its role may not do this.",
1415
+ "content": {
1416
+ "application/problem+json": {
1417
+ "schema": {
1418
+ "$ref": "#/components/schemas/Problem"
1419
+ }
1420
+ }
1421
+ }
1422
+ },
1423
+ "409": {
1424
+ "description": "No policy has been saved, or it is disabled.",
1425
+ "content": {
1426
+ "application/problem+json": {
1427
+ "schema": {
1428
+ "$ref": "#/components/schemas/Problem"
1429
+ }
1430
+ }
1431
+ }
1432
+ }
1433
+ }
1434
+ }
1435
+ },
1191
1436
  "/healthz": {
1192
1437
  "get": {
1193
1438
  "operationId": "getHealth",
@@ -7032,47 +7277,6 @@
7032
7277
  "ServerEditableSettings": {
7033
7278
  "type": "object",
7034
7279
  "properties": {
7035
- "data": {
7036
- "default": "./atlas-data",
7037
- "description": "The folder that is the default backend and the default write target: layers, specs and runs live here",
7038
- "type": "string"
7039
- },
7040
- "backends": {
7041
- "default": [],
7042
- "description": "Further backends, read together with the data folder: S3, another server, a device lab",
7043
- "type": "array",
7044
- "items": {
7045
- "type": "object",
7046
- "properties": {
7047
- "id": {
7048
- "type": "string",
7049
- "pattern": "^[a-z0-9-]+$",
7050
- "description": "Lowercase letters, digits and hyphens. Names where a result came from"
7051
- },
7052
- "label": {
7053
- "type": "string"
7054
- },
7055
- "url": {
7056
- "type": "string",
7057
- "minLength": 1,
7058
- "description": "A folder, s3://bucket/prefix, r2://, gs://, or another Atlas server (https://…)"
7059
- },
7060
- "write": {
7061
- "description": "Uploads go here instead of the data folder. At most one backend is the write target",
7062
- "type": "boolean"
7063
- }
7064
- },
7065
- "required": [
7066
- "id",
7067
- "url"
7068
- ],
7069
- "additionalProperties": false
7070
- }
7071
- },
7072
- "write": {
7073
- "description": "The id of the backend uploads go to. Default: the data folder",
7074
- "type": "string"
7075
- },
7076
7280
  "outlineBranch": {
7077
7281
  "description": "Which branch's specs the report is laid over. Default main, then master, then the newest",
7078
7282
  "type": "string"
@@ -7169,49 +7373,8 @@
7169
7373
  "settings": {
7170
7374
  "type": "object",
7171
7375
  "properties": {
7172
- "data": {
7173
- "default": "./atlas-data",
7174
- "description": "The folder that is the default backend and the default write target: layers, specs and runs live here",
7175
- "type": "string"
7176
- },
7177
- "backends": {
7178
- "default": [],
7179
- "description": "Further backends, read together with the data folder: S3, another server, a device lab",
7180
- "type": "array",
7181
- "items": {
7182
- "type": "object",
7183
- "properties": {
7184
- "id": {
7185
- "type": "string",
7186
- "pattern": "^[a-z0-9-]+$",
7187
- "description": "Lowercase letters, digits and hyphens. Names where a result came from"
7188
- },
7189
- "label": {
7190
- "type": "string"
7191
- },
7192
- "url": {
7193
- "type": "string",
7194
- "minLength": 1,
7195
- "description": "A folder, s3://bucket/prefix, r2://, gs://, or another Atlas server (https://…)"
7196
- },
7197
- "write": {
7198
- "description": "Uploads go here instead of the data folder. At most one backend is the write target",
7199
- "type": "boolean"
7200
- }
7201
- },
7202
- "required": [
7203
- "id",
7204
- "url"
7205
- ],
7206
- "additionalProperties": false
7207
- }
7208
- },
7209
- "write": {
7210
- "description": "The id of the backend uploads go to. Default: the data folder",
7211
- "type": "string"
7212
- },
7213
- "outlineBranch": {
7214
- "description": "Which branch's specs the report is laid over. Default main, then master, then the newest",
7376
+ "outlineBranch": {
7377
+ "description": "Which branch's specs the report is laid over. Default main, then master, then the newest",
7215
7378
  "type": "string"
7216
7379
  },
7217
7380
  "include": {
@@ -7304,47 +7467,6 @@
7304
7467
  "active": {
7305
7468
  "type": "object",
7306
7469
  "properties": {
7307
- "data": {
7308
- "default": "./atlas-data",
7309
- "description": "The folder that is the default backend and the default write target: layers, specs and runs live here",
7310
- "type": "string"
7311
- },
7312
- "backends": {
7313
- "default": [],
7314
- "description": "Further backends, read together with the data folder: S3, another server, a device lab",
7315
- "type": "array",
7316
- "items": {
7317
- "type": "object",
7318
- "properties": {
7319
- "id": {
7320
- "type": "string",
7321
- "pattern": "^[a-z0-9-]+$",
7322
- "description": "Lowercase letters, digits and hyphens. Names where a result came from"
7323
- },
7324
- "label": {
7325
- "type": "string"
7326
- },
7327
- "url": {
7328
- "type": "string",
7329
- "minLength": 1,
7330
- "description": "A folder, s3://bucket/prefix, r2://, gs://, or another Atlas server (https://…)"
7331
- },
7332
- "write": {
7333
- "description": "Uploads go here instead of the data folder. At most one backend is the write target",
7334
- "type": "boolean"
7335
- }
7336
- },
7337
- "required": [
7338
- "id",
7339
- "url"
7340
- ],
7341
- "additionalProperties": false
7342
- }
7343
- },
7344
- "write": {
7345
- "description": "The id of the backend uploads go to. Default: the data folder",
7346
- "type": "string"
7347
- },
7348
7470
  "outlineBranch": {
7349
7471
  "description": "Which branch's specs the report is laid over. Default main, then master, then the newest",
7350
7472
  "type": "string"
@@ -7452,6 +7574,268 @@
7452
7574
  ],
7453
7575
  "additionalProperties": {}
7454
7576
  },
7577
+ "GcPolicy": {
7578
+ "type": "object",
7579
+ "properties": {
7580
+ "enabled": {
7581
+ "type": "boolean",
7582
+ "description": "Delete eligible layers on a schedule. `false` keeps the policy but deletes nothing"
7583
+ },
7584
+ "maxAgeDays": {
7585
+ "type": "integer",
7586
+ "minimum": 1,
7587
+ "maximum": 3650,
7588
+ "description": "A superseded layer is eligible once it was recorded more than this many days ago"
7589
+ },
7590
+ "keepLatest": {
7591
+ "default": 20,
7592
+ "description": "Always keep this many of the newest layers, however old, so recent history stays",
7593
+ "type": "integer",
7594
+ "minimum": 0,
7595
+ "maximum": 1000000
7596
+ },
7597
+ "intervalHours": {
7598
+ "default": 24,
7599
+ "description": "How often the server applies the policy",
7600
+ "type": "number",
7601
+ "minimum": 1,
7602
+ "maximum": 720
7603
+ }
7604
+ },
7605
+ "required": [
7606
+ "enabled",
7607
+ "maxAgeDays"
7608
+ ],
7609
+ "additionalProperties": false
7610
+ },
7611
+ "GcSnapshot": {
7612
+ "type": "object",
7613
+ "properties": {
7614
+ "revision": {
7615
+ "type": "string",
7616
+ "pattern": "^[a-f0-9]{64}$"
7617
+ },
7618
+ "policy": {
7619
+ "anyOf": [
7620
+ {
7621
+ "type": "object",
7622
+ "properties": {
7623
+ "enabled": {
7624
+ "type": "boolean",
7625
+ "description": "Delete eligible layers on a schedule. `false` keeps the policy but deletes nothing"
7626
+ },
7627
+ "maxAgeDays": {
7628
+ "type": "integer",
7629
+ "minimum": 1,
7630
+ "maximum": 3650,
7631
+ "description": "A superseded layer is eligible once it was recorded more than this many days ago"
7632
+ },
7633
+ "keepLatest": {
7634
+ "default": 20,
7635
+ "description": "Always keep this many of the newest layers, however old, so recent history stays",
7636
+ "type": "integer",
7637
+ "minimum": 0,
7638
+ "maximum": 1000000
7639
+ },
7640
+ "intervalHours": {
7641
+ "default": 24,
7642
+ "description": "How often the server applies the policy",
7643
+ "type": "number",
7644
+ "minimum": 1,
7645
+ "maximum": 720
7646
+ }
7647
+ },
7648
+ "required": [
7649
+ "enabled",
7650
+ "maxAgeDays",
7651
+ "keepLatest",
7652
+ "intervalHours"
7653
+ ],
7654
+ "additionalProperties": false
7655
+ },
7656
+ {
7657
+ "type": "null"
7658
+ }
7659
+ ]
7660
+ },
7661
+ "saved": {
7662
+ "type": "boolean",
7663
+ "description": "Whether a policy exists in S3. `false` means garbage collection is off: nothing is deleted"
7664
+ },
7665
+ "lastRuns": {
7666
+ "type": "array",
7667
+ "items": {
7668
+ "type": "object",
7669
+ "properties": {
7670
+ "project": {
7671
+ "type": "string"
7672
+ },
7673
+ "dryRun": {
7674
+ "type": "boolean"
7675
+ },
7676
+ "at": {
7677
+ "type": "string",
7678
+ "description": "When the pass finished, ISO 8601"
7679
+ },
7680
+ "candidates": {
7681
+ "type": "integer",
7682
+ "minimum": 0,
7683
+ "maximum": 9007199254740991,
7684
+ "description": "Layers the policy would delete"
7685
+ },
7686
+ "deletedLayers": {
7687
+ "type": "integer",
7688
+ "minimum": 0,
7689
+ "maximum": 9007199254740991
7690
+ },
7691
+ "deletedBlobs": {
7692
+ "type": "integer",
7693
+ "minimum": 0,
7694
+ "maximum": 9007199254740991
7695
+ },
7696
+ "keptLayers": {
7697
+ "type": "integer",
7698
+ "minimum": 0,
7699
+ "maximum": 9007199254740991
7700
+ },
7701
+ "error": {
7702
+ "description": "Why the pass stopped early, when it did",
7703
+ "type": "string"
7704
+ }
7705
+ },
7706
+ "required": [
7707
+ "project",
7708
+ "dryRun",
7709
+ "at",
7710
+ "candidates",
7711
+ "deletedLayers",
7712
+ "deletedBlobs",
7713
+ "keptLayers"
7714
+ ],
7715
+ "additionalProperties": {}
7716
+ },
7717
+ "description": "The most recent pass over each project, newest first; empty before the first pass"
7718
+ },
7719
+ "stateKey": {
7720
+ "type": "string",
7721
+ "description": "Where the user state lives in the bucket, relative to the configured prefix"
7722
+ }
7723
+ },
7724
+ "required": [
7725
+ "revision",
7726
+ "policy",
7727
+ "saved",
7728
+ "lastRuns",
7729
+ "stateKey"
7730
+ ],
7731
+ "additionalProperties": {}
7732
+ },
7733
+ "GcRunResult": {
7734
+ "type": "object",
7735
+ "properties": {
7736
+ "runs": {
7737
+ "type": "array",
7738
+ "items": {
7739
+ "type": "object",
7740
+ "properties": {
7741
+ "project": {
7742
+ "type": "string"
7743
+ },
7744
+ "dryRun": {
7745
+ "type": "boolean"
7746
+ },
7747
+ "at": {
7748
+ "type": "string",
7749
+ "description": "When the pass finished, ISO 8601"
7750
+ },
7751
+ "candidates": {
7752
+ "type": "integer",
7753
+ "minimum": 0,
7754
+ "maximum": 9007199254740991,
7755
+ "description": "Layers the policy would delete"
7756
+ },
7757
+ "deletedLayers": {
7758
+ "type": "integer",
7759
+ "minimum": 0,
7760
+ "maximum": 9007199254740991
7761
+ },
7762
+ "deletedBlobs": {
7763
+ "type": "integer",
7764
+ "minimum": 0,
7765
+ "maximum": 9007199254740991
7766
+ },
7767
+ "keptLayers": {
7768
+ "type": "integer",
7769
+ "minimum": 0,
7770
+ "maximum": 9007199254740991
7771
+ },
7772
+ "error": {
7773
+ "description": "Why the pass stopped early, when it did",
7774
+ "type": "string"
7775
+ }
7776
+ },
7777
+ "required": [
7778
+ "project",
7779
+ "dryRun",
7780
+ "at",
7781
+ "candidates",
7782
+ "deletedLayers",
7783
+ "deletedBlobs",
7784
+ "keptLayers"
7785
+ ],
7786
+ "additionalProperties": {}
7787
+ }
7788
+ },
7789
+ "policy": {
7790
+ "anyOf": [
7791
+ {
7792
+ "type": "object",
7793
+ "properties": {
7794
+ "enabled": {
7795
+ "type": "boolean",
7796
+ "description": "Delete eligible layers on a schedule. `false` keeps the policy but deletes nothing"
7797
+ },
7798
+ "maxAgeDays": {
7799
+ "type": "integer",
7800
+ "minimum": 1,
7801
+ "maximum": 3650,
7802
+ "description": "A superseded layer is eligible once it was recorded more than this many days ago"
7803
+ },
7804
+ "keepLatest": {
7805
+ "default": 20,
7806
+ "description": "Always keep this many of the newest layers, however old, so recent history stays",
7807
+ "type": "integer",
7808
+ "minimum": 0,
7809
+ "maximum": 1000000
7810
+ },
7811
+ "intervalHours": {
7812
+ "default": 24,
7813
+ "description": "How often the server applies the policy",
7814
+ "type": "number",
7815
+ "minimum": 1,
7816
+ "maximum": 720
7817
+ }
7818
+ },
7819
+ "required": [
7820
+ "enabled",
7821
+ "maxAgeDays",
7822
+ "keepLatest",
7823
+ "intervalHours"
7824
+ ],
7825
+ "additionalProperties": false
7826
+ },
7827
+ {
7828
+ "type": "null"
7829
+ }
7830
+ ]
7831
+ }
7832
+ },
7833
+ "required": [
7834
+ "runs",
7835
+ "policy"
7836
+ ],
7837
+ "additionalProperties": {}
7838
+ },
7455
7839
  "Problem": {
7456
7840
  "type": "object",
7457
7841
  "properties": {
@@ -7882,6 +8266,22 @@
7882
8266
  "roles"
7883
8267
  ],
7884
8268
  "additionalProperties": {}
8269
+ },
8270
+ "recording": {
8271
+ "description": "What became of the check's expected recording: uploaded, referenced, skipped, unresolved or not-applicable. Independent of `status`",
8272
+ "type": "object",
8273
+ "properties": {
8274
+ "state": {
8275
+ "type": "string"
8276
+ },
8277
+ "reason": {
8278
+ "type": "string"
8279
+ }
8280
+ },
8281
+ "required": [
8282
+ "state"
8283
+ ],
8284
+ "additionalProperties": {}
7885
8285
  }
7886
8286
  },
7887
8287
  "required": [
@@ -8156,6 +8556,51 @@
8156
8556
  "roles"
8157
8557
  ],
8158
8558
  "additionalProperties": {}
8559
+ },
8560
+ "recordings": {
8561
+ "description": "How many of the recordings the layer expected were published. Absent: the layer declared no expectations, so its completeness is unknown, not complete",
8562
+ "type": "object",
8563
+ "properties": {
8564
+ "expected": {
8565
+ "type": "integer",
8566
+ "minimum": 0,
8567
+ "maximum": 9007199254740991
8568
+ },
8569
+ "uploaded": {
8570
+ "type": "integer",
8571
+ "minimum": 0,
8572
+ "maximum": 9007199254740991
8573
+ },
8574
+ "referenced": {
8575
+ "type": "integer",
8576
+ "minimum": 0,
8577
+ "maximum": 9007199254740991
8578
+ },
8579
+ "skipped": {
8580
+ "type": "integer",
8581
+ "minimum": 0,
8582
+ "maximum": 9007199254740991
8583
+ },
8584
+ "unresolved": {
8585
+ "type": "integer",
8586
+ "minimum": 0,
8587
+ "maximum": 9007199254740991
8588
+ },
8589
+ "notApplicable": {
8590
+ "type": "integer",
8591
+ "minimum": 0,
8592
+ "maximum": 9007199254740991
8593
+ }
8594
+ },
8595
+ "required": [
8596
+ "expected",
8597
+ "uploaded",
8598
+ "referenced",
8599
+ "skipped",
8600
+ "unresolved",
8601
+ "notApplicable"
8602
+ ],
8603
+ "additionalProperties": {}
8159
8604
  }
8160
8605
  },
8161
8606
  "required": [
@@ -8451,6 +8896,22 @@
8451
8896
  "roles"
8452
8897
  ],
8453
8898
  "additionalProperties": {}
8899
+ },
8900
+ "recording": {
8901
+ "description": "What became of the check's expected recording: uploaded, referenced, skipped, unresolved or not-applicable. Independent of `status`",
8902
+ "type": "object",
8903
+ "properties": {
8904
+ "state": {
8905
+ "type": "string"
8906
+ },
8907
+ "reason": {
8908
+ "type": "string"
8909
+ }
8910
+ },
8911
+ "required": [
8912
+ "state"
8913
+ ],
8914
+ "additionalProperties": {}
8454
8915
  }
8455
8916
  },
8456
8917
  "required": [
@@ -8725,6 +9186,51 @@
8725
9186
  "roles"
8726
9187
  ],
8727
9188
  "additionalProperties": {}
9189
+ },
9190
+ "recordings": {
9191
+ "description": "How many of the recordings the layer expected were published. Absent: the layer declared no expectations, so its completeness is unknown, not complete",
9192
+ "type": "object",
9193
+ "properties": {
9194
+ "expected": {
9195
+ "type": "integer",
9196
+ "minimum": 0,
9197
+ "maximum": 9007199254740991
9198
+ },
9199
+ "uploaded": {
9200
+ "type": "integer",
9201
+ "minimum": 0,
9202
+ "maximum": 9007199254740991
9203
+ },
9204
+ "referenced": {
9205
+ "type": "integer",
9206
+ "minimum": 0,
9207
+ "maximum": 9007199254740991
9208
+ },
9209
+ "skipped": {
9210
+ "type": "integer",
9211
+ "minimum": 0,
9212
+ "maximum": 9007199254740991
9213
+ },
9214
+ "unresolved": {
9215
+ "type": "integer",
9216
+ "minimum": 0,
9217
+ "maximum": 9007199254740991
9218
+ },
9219
+ "notApplicable": {
9220
+ "type": "integer",
9221
+ "minimum": 0,
9222
+ "maximum": 9007199254740991
9223
+ }
9224
+ },
9225
+ "required": [
9226
+ "expected",
9227
+ "uploaded",
9228
+ "referenced",
9229
+ "skipped",
9230
+ "unresolved",
9231
+ "notApplicable"
9232
+ ],
9233
+ "additionalProperties": {}
8728
9234
  }
8729
9235
  },
8730
9236
  "required": [
@@ -9164,6 +9670,110 @@
9164
9670
  "additionalProperties": {}
9165
9671
  },
9166
9672
  "description": "Each slot whose newest completed result failed, or that is still running"
9673
+ },
9674
+ "recordings": {
9675
+ "description": "Whether the recordings the producers expected were published, separately from `ok`. It never changes `ok`",
9676
+ "type": "object",
9677
+ "properties": {
9678
+ "state": {
9679
+ "type": "string",
9680
+ "enum": [
9681
+ "complete",
9682
+ "incomplete",
9683
+ "none-expected",
9684
+ "unknown"
9685
+ ]
9686
+ },
9687
+ "expected": {
9688
+ "type": "integer",
9689
+ "minimum": 0,
9690
+ "maximum": 9007199254740991
9691
+ },
9692
+ "uploaded": {
9693
+ "type": "integer",
9694
+ "minimum": 0,
9695
+ "maximum": 9007199254740991
9696
+ },
9697
+ "referenced": {
9698
+ "type": "integer",
9699
+ "minimum": 0,
9700
+ "maximum": 9007199254740991
9701
+ },
9702
+ "skipped": {
9703
+ "type": "integer",
9704
+ "minimum": 0,
9705
+ "maximum": 9007199254740991
9706
+ },
9707
+ "unresolved": {
9708
+ "type": "integer",
9709
+ "minimum": 0,
9710
+ "maximum": 9007199254740991
9711
+ },
9712
+ "notApplicable": {
9713
+ "type": "integer",
9714
+ "minimum": 0,
9715
+ "maximum": 9007199254740991
9716
+ },
9717
+ "layersWithoutReceipt": {
9718
+ "type": "integer",
9719
+ "minimum": 0,
9720
+ "maximum": 9007199254740991,
9721
+ "description": "Layers that contribute results but declared no recording expectations: their completeness is unknown"
9722
+ },
9723
+ "gaps": {
9724
+ "type": "array",
9725
+ "items": {
9726
+ "type": "object",
9727
+ "properties": {
9728
+ "spec": {
9729
+ "type": [
9730
+ "string",
9731
+ "null"
9732
+ ]
9733
+ },
9734
+ "check": {
9735
+ "type": "string"
9736
+ },
9737
+ "layer": {
9738
+ "type": "string"
9739
+ },
9740
+ "environment": {
9741
+ "type": [
9742
+ "string",
9743
+ "null"
9744
+ ]
9745
+ },
9746
+ "state": {
9747
+ "type": "string"
9748
+ },
9749
+ "reason": {
9750
+ "type": "string"
9751
+ }
9752
+ },
9753
+ "required": [
9754
+ "spec",
9755
+ "check",
9756
+ "layer",
9757
+ "environment",
9758
+ "state"
9759
+ ],
9760
+ "additionalProperties": {}
9761
+ },
9762
+ "description": "Each expected recording that is skipped or unresolved"
9763
+ }
9764
+ },
9765
+ "required": [
9766
+ "state",
9767
+ "expected",
9768
+ "uploaded",
9769
+ "referenced",
9770
+ "skipped",
9771
+ "unresolved",
9772
+ "notApplicable",
9773
+ "layersWithoutReceipt",
9774
+ "gaps"
9775
+ ],
9776
+ "additionalProperties": {}
9167
9777
  }
9168
9778
  },
9169
9779
  "required": [
@@ -9254,6 +9864,10 @@
9254
9864
  "type": "number",
9255
9865
  "minimum": 0
9256
9866
  },
9867
+ "recordingExpectation": {
9868
+ "description": "`required`: expect a video of this check, and report whether it was published. `not-applicable`: intentionally unrecorded. Absent: no expectation. Never changes the check's status",
9869
+ "type": "string"
9870
+ },
9257
9871
  "tags": {
9258
9872
  "description": "Free labels to filter and group by: smoke, slow, flaky…",
9259
9873
  "type": "array",
@@ -9350,6 +9964,10 @@
9350
9964
  "description": {
9351
9965
  "type": "string"
9352
9966
  },
9967
+ "omitted": {
9968
+ "description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
9969
+ "type": "string"
9970
+ },
9353
9971
  "tags": {
9354
9972
  "description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
9355
9973
  "type": "array",
@@ -9594,6 +10212,10 @@
9594
10212
  "description": {
9595
10213
  "type": "string"
9596
10214
  },
10215
+ "omitted": {
10216
+ "description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
10217
+ "type": "string"
10218
+ },
9597
10219
  "tags": {
9598
10220
  "description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
9599
10221
  "type": "array",
@@ -9875,6 +10497,10 @@
9875
10497
  "description": {
9876
10498
  "type": "string"
9877
10499
  },
10500
+ "omitted": {
10501
+ "description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
10502
+ "type": "string"
10503
+ },
9878
10504
  "tags": {
9879
10505
  "description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
9880
10506
  "type": "array",
@@ -10092,6 +10718,10 @@
10092
10718
  "description": {
10093
10719
  "type": "string"
10094
10720
  },
10721
+ "omitted": {
10722
+ "description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
10723
+ "type": "string"
10724
+ },
10095
10725
  "tags": {
10096
10726
  "description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
10097
10727
  "type": "array",
@@ -10479,6 +11109,10 @@
10479
11109
  "description": {
10480
11110
  "type": "string"
10481
11111
  },
11112
+ "omitted": {
11113
+ "description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
11114
+ "type": "string"
11115
+ },
10482
11116
  "tags": {
10483
11117
  "description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
10484
11118
  "type": "array",
@@ -11021,6 +11655,51 @@
11021
11655
  "roles"
11022
11656
  ],
11023
11657
  "additionalProperties": {}
11658
+ },
11659
+ "recordings": {
11660
+ "description": "How many of the recordings the layer expected were published. Absent: the layer declared no expectations, so its completeness is unknown, not complete",
11661
+ "type": "object",
11662
+ "properties": {
11663
+ "expected": {
11664
+ "type": "integer",
11665
+ "minimum": 0,
11666
+ "maximum": 9007199254740991
11667
+ },
11668
+ "uploaded": {
11669
+ "type": "integer",
11670
+ "minimum": 0,
11671
+ "maximum": 9007199254740991
11672
+ },
11673
+ "referenced": {
11674
+ "type": "integer",
11675
+ "minimum": 0,
11676
+ "maximum": 9007199254740991
11677
+ },
11678
+ "skipped": {
11679
+ "type": "integer",
11680
+ "minimum": 0,
11681
+ "maximum": 9007199254740991
11682
+ },
11683
+ "unresolved": {
11684
+ "type": "integer",
11685
+ "minimum": 0,
11686
+ "maximum": 9007199254740991
11687
+ },
11688
+ "notApplicable": {
11689
+ "type": "integer",
11690
+ "minimum": 0,
11691
+ "maximum": 9007199254740991
11692
+ }
11693
+ },
11694
+ "required": [
11695
+ "expected",
11696
+ "uploaded",
11697
+ "referenced",
11698
+ "skipped",
11699
+ "unresolved",
11700
+ "notApplicable"
11701
+ ],
11702
+ "additionalProperties": {}
11024
11703
  }
11025
11704
  },
11026
11705
  "required": [
@@ -11296,6 +11975,22 @@
11296
11975
  "roles"
11297
11976
  ],
11298
11977
  "additionalProperties": {}
11978
+ },
11979
+ "recording": {
11980
+ "description": "What became of the check's expected recording: uploaded, referenced, skipped, unresolved or not-applicable. Independent of `status`",
11981
+ "type": "object",
11982
+ "properties": {
11983
+ "state": {
11984
+ "type": "string"
11985
+ },
11986
+ "reason": {
11987
+ "type": "string"
11988
+ }
11989
+ },
11990
+ "required": [
11991
+ "state"
11992
+ ],
11993
+ "additionalProperties": {}
11299
11994
  }
11300
11995
  },
11301
11996
  "required": [
@@ -11404,6 +12099,10 @@
11404
12099
  "type": "number",
11405
12100
  "minimum": 0
11406
12101
  },
12102
+ "recordingExpectation": {
12103
+ "description": "`required`: expect a video of this check, and report whether it was published. `not-applicable`: intentionally unrecorded. Absent: no expectation. Never changes the check's status",
12104
+ "type": "string"
12105
+ },
11407
12106
  "tags": {
11408
12107
  "description": "Free labels to filter and group by: smoke, slow, flaky…",
11409
12108
  "type": "array",
@@ -11500,6 +12199,10 @@
11500
12199
  "description": {
11501
12200
  "type": "string"
11502
12201
  },
12202
+ "omitted": {
12203
+ "description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
12204
+ "type": "string"
12205
+ },
11503
12206
  "tags": {
11504
12207
  "description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
11505
12208
  "type": "array",
@@ -11735,6 +12438,10 @@
11735
12438
  "description": {
11736
12439
  "type": "string"
11737
12440
  },
12441
+ "omitted": {
12442
+ "description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
12443
+ "type": "string"
12444
+ },
11738
12445
  "tags": {
11739
12446
  "description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
11740
12447
  "type": "array",
@@ -12007,6 +12714,10 @@
12007
12714
  "description": {
12008
12715
  "type": "string"
12009
12716
  },
12717
+ "omitted": {
12718
+ "description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
12719
+ "type": "string"
12720
+ },
12010
12721
  "tags": {
12011
12722
  "description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
12012
12723
  "type": "array",
@@ -12214,6 +12925,10 @@
12214
12925
  "description": {
12215
12926
  "type": "string"
12216
12927
  },
12928
+ "omitted": {
12929
+ "description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
12930
+ "type": "string"
12931
+ },
12217
12932
  "tags": {
12218
12933
  "description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
12219
12934
  "type": "array",