@crvouga/atlas 0.15.0 → 0.16.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/CHANGELOG.md +6 -0
- package/dist/reports/src/evidence-detail.d.ts +19 -2
- package/dist/reports/src/evidence-detail.js +63 -14
- package/dist/reports/src/evidence-types.d.ts +31 -0
- package/dist/reports/src/layer-merge.js +2 -2
- package/dist/reports/src/test-catalog.d.ts +24 -3
- package/dist/reports/src/test-catalog.js +37 -11
- package/dist/schema/src/api.d.ts +167 -0
- package/dist/schema/src/api.js +28 -4
- package/dist/server/src/app.js +3 -2
- package/dist/server/src/llms.js +2 -1
- package/dist/server/src/openapi.js +2 -1
- package/docs/reference/http-api.md +4 -2
- package/docs/reference/reports.md +110 -3
- package/docs/reference/schema.md +16 -0
- package/docs/server.md +1 -1
- package/llms-full.txt +5 -3
- package/package.json +1 -1
- package/packages/schema/schemas/effective-delta.schema.json +82 -0
- package/packages/schema/schemas/effective-evidence.schema.json +82 -0
- package/packages/schema/schemas/evidence-page.schema.json +41 -0
- package/packages/schema/schemas/layer-rows.schema.json +41 -0
- package/packages/schema/schemas/report-run.schema.json +71 -0
- package/packages/schema/schemas/report-runs.schema.json +38 -1
- package/packages/schema/schemas/test-catalog.schema.json +41 -0
- package/packages/schema/schemas/test-history.schema.json +82 -0
- package/packages/server/openapi.json +498 -3
- package/packages/visualizer/src/explorer/RunsView.tsx +128 -10
- package/packages/visualizer/src/explorer/catalog.tsx +16 -6
- package/packages/visualizer/src/explorer/search.ts +4 -1
package/dist/schema/src/api.d.ts
CHANGED
|
@@ -80,6 +80,21 @@ export declare const BackendInfoSchema: z.ZodObject<{
|
|
|
80
80
|
live: z.ZodBoolean;
|
|
81
81
|
available: z.ZodBoolean;
|
|
82
82
|
}, z.core.$loose>;
|
|
83
|
+
/** One reference to a video held by a layer, check, retry attempt or step, found by the attachment's effective media type. */
|
|
84
|
+
export declare const VideoRefSchema: z.ZodObject<{
|
|
85
|
+
name: z.ZodString;
|
|
86
|
+
owner: z.ZodString;
|
|
87
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
88
|
+
step: z.ZodOptional<z.ZodString>;
|
|
89
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
90
|
+
available: z.ZodBoolean;
|
|
91
|
+
}, z.core.$loose>;
|
|
92
|
+
/** Video references as counts: how many are referenced, how many distinct recordings can be played, how many cannot. */
|
|
93
|
+
export declare const VideoCountsSchema: z.ZodObject<{
|
|
94
|
+
references: z.ZodNumber;
|
|
95
|
+
recordings: z.ZodNumber;
|
|
96
|
+
unavailable: z.ZodNumber;
|
|
97
|
+
}, z.core.$loose>;
|
|
83
98
|
/** What a result carries beyond its outcome, as counts and short facts. Present only when there is something. */
|
|
84
99
|
export declare const EvidenceSummarySchema: z.ZodObject<{
|
|
85
100
|
attachments: z.ZodNumber;
|
|
@@ -91,6 +106,14 @@ export declare const EvidenceSummarySchema: z.ZodObject<{
|
|
|
91
106
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
92
107
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
93
108
|
roles: z.ZodArray<z.ZodString>;
|
|
109
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
110
|
+
name: z.ZodString;
|
|
111
|
+
owner: z.ZodString;
|
|
112
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
113
|
+
step: z.ZodOptional<z.ZodString>;
|
|
114
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
115
|
+
available: z.ZodBoolean;
|
|
116
|
+
}, z.core.$loose>>>;
|
|
94
117
|
}, z.core.$loose>;
|
|
95
118
|
/** A layer as the report shows it: its origin and its checks by status. */
|
|
96
119
|
export declare const LayerHeaderSchema: z.ZodObject<{
|
|
@@ -133,6 +156,14 @@ export declare const LayerHeaderSchema: z.ZodObject<{
|
|
|
133
156
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
134
157
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
135
158
|
roles: z.ZodArray<z.ZodString>;
|
|
159
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
160
|
+
name: z.ZodString;
|
|
161
|
+
owner: z.ZodString;
|
|
162
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
163
|
+
step: z.ZodOptional<z.ZodString>;
|
|
164
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
165
|
+
available: z.ZodBoolean;
|
|
166
|
+
}, z.core.$loose>>>;
|
|
136
167
|
}, z.core.$loose>>;
|
|
137
168
|
}, z.core.$loose>;
|
|
138
169
|
/** One check of a layer as the report shows it. */
|
|
@@ -167,6 +198,14 @@ export declare const LayerRowSchema: z.ZodObject<{
|
|
|
167
198
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
168
199
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
169
200
|
roles: z.ZodArray<z.ZodString>;
|
|
201
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
202
|
+
name: z.ZodString;
|
|
203
|
+
owner: z.ZodString;
|
|
204
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
205
|
+
step: z.ZodOptional<z.ZodString>;
|
|
206
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
207
|
+
available: z.ZodBoolean;
|
|
208
|
+
}, z.core.$loose>>>;
|
|
170
209
|
}, z.core.$loose>>;
|
|
171
210
|
}, z.core.$loose>;
|
|
172
211
|
/** A layer with its checks: the rows of one layer, read apart from its header. */
|
|
@@ -203,6 +242,14 @@ export declare const LayerRowsSchema: z.ZodObject<{
|
|
|
203
242
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
204
243
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
205
244
|
roles: z.ZodArray<z.ZodString>;
|
|
245
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
246
|
+
name: z.ZodString;
|
|
247
|
+
owner: z.ZodString;
|
|
248
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
249
|
+
step: z.ZodOptional<z.ZodString>;
|
|
250
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
251
|
+
available: z.ZodBoolean;
|
|
252
|
+
}, z.core.$loose>>>;
|
|
206
253
|
}, z.core.$loose>>;
|
|
207
254
|
}, z.core.$loose>>;
|
|
208
255
|
fingerprints: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
@@ -241,6 +288,14 @@ export declare const LayerEvidenceSchema: z.ZodObject<{
|
|
|
241
288
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
242
289
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
243
290
|
roles: z.ZodArray<z.ZodString>;
|
|
291
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
292
|
+
name: z.ZodString;
|
|
293
|
+
owner: z.ZodString;
|
|
294
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
295
|
+
step: z.ZodOptional<z.ZodString>;
|
|
296
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
297
|
+
available: z.ZodBoolean;
|
|
298
|
+
}, z.core.$loose>>>;
|
|
244
299
|
}, z.core.$loose>>;
|
|
245
300
|
}, z.core.$loose>>;
|
|
246
301
|
fingerprints: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
@@ -284,6 +339,14 @@ export declare const LayerEvidenceSchema: z.ZodObject<{
|
|
|
284
339
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
285
340
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
286
341
|
roles: z.ZodArray<z.ZodString>;
|
|
342
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
343
|
+
name: z.ZodString;
|
|
344
|
+
owner: z.ZodString;
|
|
345
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
346
|
+
step: z.ZodOptional<z.ZodString>;
|
|
347
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
348
|
+
available: z.ZodBoolean;
|
|
349
|
+
}, z.core.$loose>>>;
|
|
287
350
|
}, z.core.$loose>>;
|
|
288
351
|
}, z.core.$loose>;
|
|
289
352
|
}, z.core.$loose>;
|
|
@@ -327,6 +390,14 @@ export declare const EffectiveEvidenceSchema: z.ZodObject<{
|
|
|
327
390
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
328
391
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
329
392
|
roles: z.ZodArray<z.ZodString>;
|
|
393
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
394
|
+
name: z.ZodString;
|
|
395
|
+
owner: z.ZodString;
|
|
396
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
397
|
+
step: z.ZodOptional<z.ZodString>;
|
|
398
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
399
|
+
available: z.ZodBoolean;
|
|
400
|
+
}, z.core.$loose>>>;
|
|
330
401
|
}, z.core.$loose>>;
|
|
331
402
|
}, z.core.$loose>>;
|
|
332
403
|
fingerprints: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
@@ -370,6 +441,14 @@ export declare const EffectiveEvidenceSchema: z.ZodObject<{
|
|
|
370
441
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
371
442
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
372
443
|
roles: z.ZodArray<z.ZodString>;
|
|
444
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
445
|
+
name: z.ZodString;
|
|
446
|
+
owner: z.ZodString;
|
|
447
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
448
|
+
step: z.ZodOptional<z.ZodString>;
|
|
449
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
450
|
+
available: z.ZodBoolean;
|
|
451
|
+
}, z.core.$loose>>>;
|
|
373
452
|
}, z.core.$loose>>;
|
|
374
453
|
}, z.core.$loose>;
|
|
375
454
|
}, z.core.$loose>>;
|
|
@@ -434,6 +513,14 @@ export declare const EffectiveDeltaSchema: z.ZodObject<{
|
|
|
434
513
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
435
514
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
436
515
|
roles: z.ZodArray<z.ZodString>;
|
|
516
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
517
|
+
name: z.ZodString;
|
|
518
|
+
owner: z.ZodString;
|
|
519
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
520
|
+
step: z.ZodOptional<z.ZodString>;
|
|
521
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
522
|
+
available: z.ZodBoolean;
|
|
523
|
+
}, z.core.$loose>>>;
|
|
437
524
|
}, z.core.$loose>>;
|
|
438
525
|
}, z.core.$loose>>;
|
|
439
526
|
fingerprints: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
@@ -477,6 +564,14 @@ export declare const EffectiveDeltaSchema: z.ZodObject<{
|
|
|
477
564
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
478
565
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
479
566
|
roles: z.ZodArray<z.ZodString>;
|
|
567
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
568
|
+
name: z.ZodString;
|
|
569
|
+
owner: z.ZodString;
|
|
570
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
571
|
+
step: z.ZodOptional<z.ZodString>;
|
|
572
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
573
|
+
available: z.ZodBoolean;
|
|
574
|
+
}, z.core.$loose>>>;
|
|
480
575
|
}, z.core.$loose>>;
|
|
481
576
|
}, z.core.$loose>;
|
|
482
577
|
}, z.core.$loose>>;
|
|
@@ -544,6 +639,14 @@ export declare const EvidencePageSchema: z.ZodObject<{
|
|
|
544
639
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
545
640
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
546
641
|
roles: z.ZodArray<z.ZodString>;
|
|
642
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
643
|
+
name: z.ZodString;
|
|
644
|
+
owner: z.ZodString;
|
|
645
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
646
|
+
step: z.ZodOptional<z.ZodString>;
|
|
647
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
648
|
+
available: z.ZodBoolean;
|
|
649
|
+
}, z.core.$loose>>>;
|
|
547
650
|
}, z.core.$loose>>;
|
|
548
651
|
}, z.core.$loose>>;
|
|
549
652
|
cursor: z.ZodNullable<z.ZodString>;
|
|
@@ -923,6 +1026,14 @@ export declare const TestResultEntrySchema: z.ZodObject<{
|
|
|
923
1026
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
924
1027
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
925
1028
|
roles: z.ZodArray<z.ZodString>;
|
|
1029
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1030
|
+
name: z.ZodString;
|
|
1031
|
+
owner: z.ZodString;
|
|
1032
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
1033
|
+
step: z.ZodOptional<z.ZodString>;
|
|
1034
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
1035
|
+
available: z.ZodBoolean;
|
|
1036
|
+
}, z.core.$loose>>>;
|
|
926
1037
|
}, z.core.$loose>>;
|
|
927
1038
|
}, z.core.$loose>;
|
|
928
1039
|
/** One test of the catalog: what it is, its current result per environment, and how it has gone over its recorded history. */
|
|
@@ -975,6 +1086,14 @@ export declare const TestSummarySchema: z.ZodObject<{
|
|
|
975
1086
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
976
1087
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
977
1088
|
roles: z.ZodArray<z.ZodString>;
|
|
1089
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1090
|
+
name: z.ZodString;
|
|
1091
|
+
owner: z.ZodString;
|
|
1092
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
1093
|
+
step: z.ZodOptional<z.ZodString>;
|
|
1094
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
1095
|
+
available: z.ZodBoolean;
|
|
1096
|
+
}, z.core.$loose>>>;
|
|
978
1097
|
}, z.core.$loose>>;
|
|
979
1098
|
}, z.core.$loose>>;
|
|
980
1099
|
stats: z.ZodObject<{
|
|
@@ -1217,6 +1336,14 @@ export declare const TestCatalogSchema: z.ZodObject<{
|
|
|
1217
1336
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1218
1337
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
1219
1338
|
roles: z.ZodArray<z.ZodString>;
|
|
1339
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1340
|
+
name: z.ZodString;
|
|
1341
|
+
owner: z.ZodString;
|
|
1342
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
1343
|
+
step: z.ZodOptional<z.ZodString>;
|
|
1344
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
1345
|
+
available: z.ZodBoolean;
|
|
1346
|
+
}, z.core.$loose>>>;
|
|
1220
1347
|
}, z.core.$loose>>;
|
|
1221
1348
|
}, z.core.$loose>>;
|
|
1222
1349
|
stats: z.ZodObject<{
|
|
@@ -1390,6 +1517,14 @@ export declare const TestHistorySchema: z.ZodObject<{
|
|
|
1390
1517
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1391
1518
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
1392
1519
|
roles: z.ZodArray<z.ZodString>;
|
|
1520
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1521
|
+
name: z.ZodString;
|
|
1522
|
+
owner: z.ZodString;
|
|
1523
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
1524
|
+
step: z.ZodOptional<z.ZodString>;
|
|
1525
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
1526
|
+
available: z.ZodBoolean;
|
|
1527
|
+
}, z.core.$loose>>>;
|
|
1393
1528
|
}, z.core.$loose>>;
|
|
1394
1529
|
}, z.core.$loose>>;
|
|
1395
1530
|
stats: z.ZodObject<{
|
|
@@ -1460,6 +1595,14 @@ export declare const TestHistorySchema: z.ZodObject<{
|
|
|
1460
1595
|
artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1461
1596
|
labels: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
1462
1597
|
roles: z.ZodArray<z.ZodString>;
|
|
1598
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1599
|
+
name: z.ZodString;
|
|
1600
|
+
owner: z.ZodString;
|
|
1601
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
1602
|
+
step: z.ZodOptional<z.ZodString>;
|
|
1603
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
1604
|
+
available: z.ZodBoolean;
|
|
1605
|
+
}, z.core.$loose>>>;
|
|
1463
1606
|
}, z.core.$loose>>;
|
|
1464
1607
|
}, z.core.$loose>>;
|
|
1465
1608
|
}, z.core.$loose>;
|
|
@@ -1495,6 +1638,11 @@ export declare const ReportRunSchema: z.ZodObject<{
|
|
|
1495
1638
|
environment: z.ZodNullable<z.ZodString>;
|
|
1496
1639
|
detail: z.ZodString;
|
|
1497
1640
|
}, z.core.$loose>>;
|
|
1641
|
+
videos: z.ZodOptional<z.ZodObject<{
|
|
1642
|
+
references: z.ZodNumber;
|
|
1643
|
+
recordings: z.ZodNumber;
|
|
1644
|
+
unavailable: z.ZodNumber;
|
|
1645
|
+
}, z.core.$loose>>;
|
|
1498
1646
|
}, z.core.$loose>;
|
|
1499
1647
|
/** `GET /api/reports/runs`: runs, newest first. */
|
|
1500
1648
|
export declare const RunListSchema: z.ZodObject<{
|
|
@@ -1532,7 +1680,13 @@ export declare const RunListSchema: z.ZodObject<{
|
|
|
1532
1680
|
environment: z.ZodNullable<z.ZodString>;
|
|
1533
1681
|
detail: z.ZodString;
|
|
1534
1682
|
}, z.core.$loose>>;
|
|
1683
|
+
videos: z.ZodOptional<z.ZodObject<{
|
|
1684
|
+
references: z.ZodNumber;
|
|
1685
|
+
recordings: z.ZodNumber;
|
|
1686
|
+
unavailable: z.ZodNumber;
|
|
1687
|
+
}, z.core.$loose>>;
|
|
1535
1688
|
}, z.core.$loose>>;
|
|
1689
|
+
withVideo: z.ZodOptional<z.ZodNumber>;
|
|
1536
1690
|
}, z.core.$loose>;
|
|
1537
1691
|
/** `GET /api/reports/runs/detail`: one run and its last result for every test, environment and producer it recorded. */
|
|
1538
1692
|
export declare const RunDetailSchema: z.ZodObject<{
|
|
@@ -1569,6 +1723,11 @@ export declare const RunDetailSchema: z.ZodObject<{
|
|
|
1569
1723
|
environment: z.ZodNullable<z.ZodString>;
|
|
1570
1724
|
detail: z.ZodString;
|
|
1571
1725
|
}, z.core.$loose>>;
|
|
1726
|
+
videos: z.ZodOptional<z.ZodObject<{
|
|
1727
|
+
references: z.ZodNumber;
|
|
1728
|
+
recordings: z.ZodNumber;
|
|
1729
|
+
unavailable: z.ZodNumber;
|
|
1730
|
+
}, z.core.$loose>>;
|
|
1572
1731
|
}, z.core.$loose>;
|
|
1573
1732
|
results: z.ZodArray<z.ZodObject<{
|
|
1574
1733
|
key: z.ZodString;
|
|
@@ -1587,5 +1746,13 @@ export declare const RunDetailSchema: z.ZodObject<{
|
|
|
1587
1746
|
at: z.ZodNumber;
|
|
1588
1747
|
layer: z.ZodString;
|
|
1589
1748
|
detail: z.ZodString;
|
|
1749
|
+
videos: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
1750
|
+
name: z.ZodString;
|
|
1751
|
+
owner: z.ZodString;
|
|
1752
|
+
attempt: z.ZodOptional<z.ZodNumber>;
|
|
1753
|
+
step: z.ZodOptional<z.ZodString>;
|
|
1754
|
+
recording: z.ZodOptional<z.ZodString>;
|
|
1755
|
+
available: z.ZodBoolean;
|
|
1756
|
+
}, z.core.$loose>>>;
|
|
1590
1757
|
}, z.core.$loose>>;
|
|
1591
1758
|
}, z.core.$loose>;
|
package/dist/schema/src/api.js
CHANGED
|
@@ -62,6 +62,21 @@ export const BackendInfoSchema = z.looseObject({
|
|
|
62
62
|
live: z.boolean().describe('Always false on a server'),
|
|
63
63
|
available: z.boolean().describe('False when the backend could not be read; its results are then unknown, never passed')
|
|
64
64
|
});
|
|
65
|
+
/** One reference to a video held by a layer, check, retry attempt or step, found by the attachment's effective media type. */
|
|
66
|
+
export const VideoRefSchema = z.looseObject({
|
|
67
|
+
name: z.string().describe('The attachment name'),
|
|
68
|
+
owner: z.string().describe('Who holds it: `layer`, `check`, `attempt` or `step`. An open vocabulary: treat an unknown owner as a check'),
|
|
69
|
+
attempt: z.number().int().nonnegative().optional().describe('The retry attempt that holds it, or that holds the step that does'),
|
|
70
|
+
step: z.string().optional().describe('The name of the step that holds it'),
|
|
71
|
+
recording: z.string().optional().describe('What identifies the bytes; references with the same value are one recording. Absent when it cannot be played'),
|
|
72
|
+
available: z.boolean().describe('False when the reference names no bytes (no bundle key, address or inline text)')
|
|
73
|
+
});
|
|
74
|
+
/** Video references as counts: how many are referenced, how many distinct recordings can be played, how many cannot. */
|
|
75
|
+
export const VideoCountsSchema = z.looseObject({
|
|
76
|
+
references: z.number().int().nonnegative().describe('Every reference to a video; identical bytes referenced twice count twice'),
|
|
77
|
+
recordings: z.number().int().nonnegative().describe('Distinct playable recordings'),
|
|
78
|
+
unavailable: z.number().int().nonnegative().describe('References that name no bytes')
|
|
79
|
+
});
|
|
65
80
|
/** What a result carries beyond its outcome, as counts and short facts. Present only when there is something. */
|
|
66
81
|
export const EvidenceSummarySchema = z.looseObject({
|
|
67
82
|
attachments: z.number().int().nonnegative(),
|
|
@@ -72,7 +87,8 @@ export const EvidenceSummarySchema = z.looseObject({
|
|
|
72
87
|
tags: z.array(z.string()),
|
|
73
88
|
artifactTags: z.array(z.string()).optional().describe('Normalized tags of the attachments themselves, sorted and distinct. Absent when no attachment is tagged; never inherited from a check or run'),
|
|
74
89
|
labels: z.record(z.string(), z.string()),
|
|
75
|
-
roles: z.array(z.string())
|
|
90
|
+
roles: z.array(z.string()),
|
|
91
|
+
videos: z.array(VideoRefSchema).optional().describe('Its references to videos and who holds each. Absent when it has none, and in reports written before this was recorded')
|
|
76
92
|
});
|
|
77
93
|
/** A layer as the report shows it: its origin and its checks by status. */
|
|
78
94
|
export const LayerHeaderSchema = z.looseObject({
|
|
@@ -374,10 +390,17 @@ export const ReportRunSchema = z.looseObject({
|
|
|
374
390
|
producers: z.array(z.string()), environments: z.array(z.string()),
|
|
375
391
|
layers: z.number().int().nonnegative(), tests: z.number().int().nonnegative(), counts: StatusCountsSchema,
|
|
376
392
|
totalDurationMs: z.number().nullable(),
|
|
377
|
-
failing: z.array(z.looseObject({ key: z.string(), title: z.string(), environment: z.string().nullable(), detail: z.string() }))
|
|
393
|
+
failing: z.array(z.looseObject({ key: z.string(), title: z.string(), environment: z.string().nullable(), detail: z.string() })),
|
|
394
|
+
videos: VideoCountsSchema.optional().describe('Its videos, from its layers and the last result of each test. Absent when it has none or the report carries no video information')
|
|
378
395
|
});
|
|
379
396
|
/** `GET /api/reports/runs`: runs, newest first. */
|
|
380
|
-
export const RunListSchema = z.looseObject({
|
|
397
|
+
export const RunListSchema = z.looseObject({
|
|
398
|
+
schemaVersion: z.literal(1),
|
|
399
|
+
version: z.string(),
|
|
400
|
+
total: z.number().int().nonnegative().describe('Runs that matched, before `limit`'),
|
|
401
|
+
runs: z.array(ReportRunSchema),
|
|
402
|
+
withVideo: z.number().int().nonnegative().optional().describe('How many of all runs (before `video` and `limit`) have a playable video recording')
|
|
403
|
+
});
|
|
381
404
|
/** `GET /api/reports/runs/detail`: one run and its last result for every test, environment and producer it recorded. */
|
|
382
405
|
export const RunDetailSchema = z.looseObject({
|
|
383
406
|
schemaVersion: z.literal(1),
|
|
@@ -385,6 +408,7 @@ export const RunDetailSchema = z.looseObject({
|
|
|
385
408
|
run: ReportRunSchema,
|
|
386
409
|
results: z.array(z.looseObject({
|
|
387
410
|
key: z.string(), id: z.string(), title: z.string(), specPath: z.string().nullable(), environment: z.string().nullable(), producer: z.string(),
|
|
388
|
-
status: EvidenceStatusSchema, durationMs: z.number().nullable(), at: z.number(), layer: z.string(), detail: z.string()
|
|
411
|
+
status: EvidenceStatusSchema, durationMs: z.number().nullable(), at: z.number(), layer: z.string(), detail: z.string(),
|
|
412
|
+
videos: z.array(VideoRefSchema).optional().describe('Every video its evidence references, with who holds it')
|
|
389
413
|
}))
|
|
390
414
|
});
|
package/dist/server/src/app.js
CHANGED
|
@@ -426,8 +426,9 @@ function createProjectApplication(options) {
|
|
|
426
426
|
return notReady(c);
|
|
427
427
|
const found = await catalog();
|
|
428
428
|
const limit = /^\d+$/.test(c.req.query('limit') ?? '') ? Number(c.req.query('limit')) : 100;
|
|
429
|
-
const
|
|
430
|
-
|
|
429
|
+
const video = ['1', 'true'].includes(c.req.query('video') ?? '');
|
|
430
|
+
const document = () => runList(index.history, found.version, limit, { video });
|
|
431
|
+
return answer(c, found.version, document, () => runsMarkdown(document()), `runs|${limit}|${video}|${wantsMarkdown(c)}`);
|
|
431
432
|
});
|
|
432
433
|
app.get('/api/reports/runs/detail', async (c) => {
|
|
433
434
|
if (!index.ready)
|
package/dist/server/src/llms.js
CHANGED
|
@@ -69,7 +69,8 @@ curl "${url}/api/projects/$ATLAS_PROJECT/reports/tests?format=markdown&limit=50"
|
|
|
69
69
|
curl "${url}/api/projects/$ATLAS_PROJECT/reports/tests?status=failed,flaky&environment=desktop-web" # filter: q, status, environment, producer, domain, spec, tag, run; sort, offset, limit
|
|
70
70
|
curl "${url}/api/projects/$ATLAS_PROJECT/reports/tests/history?key=<url-encoded test key>" # one test: every recorded result, durations, branches, CI runs
|
|
71
71
|
curl "${url}/api/projects/$ATLAS_PROJECT/reports/tests/health?format=markdown" # why Specs, domains and counts disagree; add root=<from>><to> to declare a path-root mapping
|
|
72
|
-
curl "${url}/api/projects/$ATLAS_PROJECT/reports/runs?format=markdown" # CI runs, newest first, with their results by status
|
|
72
|
+
curl "${url}/api/projects/$ATLAS_PROJECT/reports/runs?format=markdown" # CI runs, newest first, with their results by status and video counts
|
|
73
|
+
curl "${url}/api/projects/$ATLAS_PROJECT/reports/runs?video=true&format=markdown" # only runs with a playable video recording
|
|
73
74
|
curl "${url}/api/projects/$ATLAS_PROJECT/reports/runs/detail?key=<url-encoded run key>" # every test one run recorded
|
|
74
75
|
\`\`\`
|
|
75
76
|
|
|
@@ -324,9 +324,10 @@ const BASE_OPERATIONS = [
|
|
|
324
324
|
{
|
|
325
325
|
route: 'GET /api/reports/runs', path: '/api/reports/runs', operationId: 'getReportRuns', tag: 'Report', role: 'read',
|
|
326
326
|
summary: 'The runs that recorded the report',
|
|
327
|
-
description: '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
|
|
327
|
+
description: '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.',
|
|
328
328
|
parameters: [
|
|
329
329
|
{ name: 'limit', in: 'query', description: 'At most this many runs (default 100).', schema: { type: 'integer', minimum: 0 } },
|
|
330
|
+
{ name: 'video', in: 'query', description: '`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.', schema: { type: 'boolean' } },
|
|
330
331
|
{ name: 'format', in: 'query', description: '`markdown` for Markdown instead of JSON.', schema: { type: 'string', enum: ['json', 'markdown'] } }
|
|
331
332
|
],
|
|
332
333
|
responses: { '200': json('The runs.', 'RunList'), '304': { description: 'Unchanged since the `If-None-Match` ETag.' }, '401': UNAUTHORIZED, '503': NOT_READY }
|
|
@@ -616,9 +616,10 @@ Responses: `201` The recorded entry.; `401` No token, or an unknown one. Send `A
|
|
|
616
616
|
|
|
617
617
|
`GET /api/reports/runs` · role: read
|
|
618
618
|
|
|
619
|
-
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
|
|
619
|
+
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.
|
|
620
620
|
|
|
621
621
|
- `limit` (query): At most this many runs (default 100).
|
|
622
|
+
- `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.
|
|
622
623
|
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
623
624
|
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
624
625
|
|
|
@@ -628,12 +629,13 @@ Responses: `200` The runs.; `304` Unchanged since the `If-None-Match` ETag.; `40
|
|
|
628
629
|
|
|
629
630
|
`GET /api/projects/{project}/reports/runs` · role: read
|
|
630
631
|
|
|
631
|
-
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
|
|
632
|
+
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.
|
|
632
633
|
|
|
633
634
|
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.
|
|
634
635
|
|
|
635
636
|
- `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.
|
|
636
637
|
- `limit` (query): At most this many runs (default 100).
|
|
638
|
+
- `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.
|
|
637
639
|
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
638
640
|
|
|
639
641
|
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`.
|
|
@@ -1426,7 +1426,36 @@ read when someone asks for it. Undefined when there is none.
|
|
|
1426
1426
|
Several holders (for example a check and its layer) are summed: `attachments` also counts those on steps and
|
|
1427
1427
|
attempts, `steps` counts only each holder's own top-level steps, tags are de-duplicated, and for a repeated
|
|
1428
1428
|
label the later holder wins. `artifactTags` are the attachments' own normalized tags and are never inherited. `roles` are the attachments' roles (or, without one, what their media type or file
|
|
1429
|
-
name implies), sorted and distinct.
|
|
1429
|
+
name implies), sorted and distinct. `videos` lists every attachment whose effective media type is video, with the
|
|
1430
|
+
check, retry attempt or step that holds it (see {@link isVideoAttachment}).
|
|
1431
|
+
|
|
1432
|
+
#### `summarizeLayerEvidence`
|
|
1433
|
+
|
|
1434
|
+
```ts
|
|
1435
|
+
export function summarizeLayerEvidence(...holders: readonly EvidenceHolder[]): EvidenceSummary | undefined
|
|
1436
|
+
```
|
|
1437
|
+
|
|
1438
|
+
Like {@link summarizeEvidence} for a layer and its parts: the same summary, but a video attached to the layer or a
|
|
1439
|
+
part is owned by the `layer`, not by a check.
|
|
1440
|
+
|
|
1441
|
+
#### `isVideoAttachment`
|
|
1442
|
+
|
|
1443
|
+
```ts
|
|
1444
|
+
export function isVideoAttachment(attachment: Pick<Attachment, 'name' | 'role' | 'contentType'>): boolean
|
|
1445
|
+
```
|
|
1446
|
+
|
|
1447
|
+
Whether an attachment is a video, by its effective media type: the producer's `contentType`, else the one its file
|
|
1448
|
+
name implies. A role never decides this on its own (`demo` on a PNG is not a video), except that a `video` role
|
|
1449
|
+
settles a file whose type nothing says (`application/octet-stream`).
|
|
1450
|
+
|
|
1451
|
+
#### `videoCounts`
|
|
1452
|
+
|
|
1453
|
+
```ts
|
|
1454
|
+
export function videoCounts(videos: readonly VideoRef[] | undefined): VideoCounts | undefined
|
|
1455
|
+
```
|
|
1456
|
+
|
|
1457
|
+
Counts references, distinct playable recordings and unplayable references. Undefined when there is no reference, so
|
|
1458
|
+
"no video" and "nothing known about video" look the same on the wire.
|
|
1430
1459
|
|
|
1431
1460
|
#### `roleOfType`
|
|
1432
1461
|
|
|
@@ -1609,6 +1638,51 @@ export type StatusCounts = Readonly<Record<EvidenceStatus, number>>;
|
|
|
1609
1638
|
|
|
1610
1639
|
How many results have each status (`passed`, `failed`, `running`, `not-run`).
|
|
1611
1640
|
|
|
1641
|
+
#### `VideoOwner`
|
|
1642
|
+
|
|
1643
|
+
```ts
|
|
1644
|
+
export type VideoOwner = 'layer' | 'check' | 'attempt' | 'step';
|
|
1645
|
+
```
|
|
1646
|
+
|
|
1647
|
+
Who holds a recording: the whole layer (or one of its parts), one check, one retry attempt of a check, or one step.
|
|
1648
|
+
|
|
1649
|
+
#### `VideoRef`
|
|
1650
|
+
|
|
1651
|
+
```ts
|
|
1652
|
+
export type VideoRef = {
|
|
1653
|
+
/** The attachment's name, for a person. */
|
|
1654
|
+
readonly name: string;
|
|
1655
|
+
readonly owner: VideoOwner;
|
|
1656
|
+
/** The try that holds it, when `owner` is `attempt` or the step is inside one. */
|
|
1657
|
+
readonly attempt?: number;
|
|
1658
|
+
/** The name of the step that holds it, when `owner` is `step`. */
|
|
1659
|
+
readonly step?: string;
|
|
1660
|
+
/** What identifies the bytes (digest, bundle key or address); references with the same one are the same recording. Absent when `available` is false. */
|
|
1661
|
+
readonly recording?: string;
|
|
1662
|
+
/** False when the reference names no bytes at all (no bundle key, address or inline text), so it can never be played. */
|
|
1663
|
+
readonly available: boolean;
|
|
1664
|
+
};
|
|
1665
|
+
```
|
|
1666
|
+
|
|
1667
|
+
One reference to a video, found by its effective media type (the producer's `contentType`, else the one the file
|
|
1668
|
+
name implies; a `video` role alone counts only when nothing else says what the file is). A reference is not a
|
|
1669
|
+
recording: the same bytes referenced from three checks are three references and one recording.
|
|
1670
|
+
|
|
1671
|
+
#### `VideoCounts`
|
|
1672
|
+
|
|
1673
|
+
```ts
|
|
1674
|
+
export type VideoCounts = {
|
|
1675
|
+
/** Every reference to a video, playable or not. */
|
|
1676
|
+
readonly references: number;
|
|
1677
|
+
/** Distinct playable recordings: identical bytes referenced twice are one. */
|
|
1678
|
+
readonly recordings: number;
|
|
1679
|
+
/** References that name no bytes. A report that predates availability metadata has none of these and none of the others. */
|
|
1680
|
+
readonly unavailable: number;
|
|
1681
|
+
};
|
|
1682
|
+
```
|
|
1683
|
+
|
|
1684
|
+
Video availability as counts: references, distinct playable recordings (deduplicated), and references that cannot be played.
|
|
1685
|
+
|
|
1612
1686
|
#### `EvidenceSummary`
|
|
1613
1687
|
|
|
1614
1688
|
```ts
|
|
@@ -1626,6 +1700,8 @@ export type EvidenceSummary = {
|
|
|
1626
1700
|
readonly labels: Readonly<Record<string, string>>;
|
|
1627
1701
|
/** The distinct roles of its attachments (or what their media types say), to show what kind of evidence there is. */
|
|
1628
1702
|
readonly roles: readonly string[];
|
|
1703
|
+
/** Its references to videos, wherever they are held (layer, check, attempt, step). Only there when it has any. */
|
|
1704
|
+
readonly videos?: readonly VideoRef[];
|
|
1629
1705
|
};
|
|
1630
1706
|
```
|
|
1631
1707
|
|
|
@@ -7930,20 +8006,39 @@ export type RunSummary = {
|
|
|
7930
8006
|
readonly totalDurationMs: number | null;
|
|
7931
8007
|
/** Up to twenty tests that failed in it. */
|
|
7932
8008
|
readonly failing: readonly { readonly key: string; readonly title: string; readonly environment: string | null; readonly detail: string }[];
|
|
8009
|
+
/**
|
|
8010
|
+
* Its videos, from the run's layers and from the last result of each test: references, distinct playable recordings
|
|
8011
|
+
* and references that cannot be played. Absent when it has none, and in a report that carries no video information.
|
|
8012
|
+
*/
|
|
8013
|
+
readonly videos?: VideoCounts;
|
|
7933
8014
|
};
|
|
7934
8015
|
```
|
|
7935
8016
|
|
|
7936
8017
|
A run: the layers that name the same execution (`run.id`), else share a CI run URL, else a branch and commit. When,
|
|
7937
8018
|
where, and how its tests went.
|
|
7938
8019
|
|
|
8020
|
+
#### `RunQuery`
|
|
8021
|
+
|
|
8022
|
+
```ts
|
|
8023
|
+
export type RunQuery = {
|
|
8024
|
+
/** Only runs that have at least one playable video recording. `total` then counts only those. */
|
|
8025
|
+
readonly video?: boolean;
|
|
8026
|
+
};
|
|
8027
|
+
```
|
|
8028
|
+
|
|
8029
|
+
What `runList` can be asked.
|
|
8030
|
+
|
|
7939
8031
|
#### `RunList`
|
|
7940
8032
|
|
|
7941
8033
|
```ts
|
|
7942
8034
|
export type RunList = {
|
|
7943
8035
|
readonly schemaVersion: typeof TEST_CATALOG_VERSION;
|
|
7944
8036
|
readonly version: string;
|
|
8037
|
+
/** Runs that matched, before `limit`. */
|
|
7945
8038
|
readonly total: number;
|
|
7946
8039
|
readonly runs: readonly RunSummary[];
|
|
8040
|
+
/** How many of all runs (whatever was asked, before `limit`) have a playable video recording. */
|
|
8041
|
+
readonly withVideo?: number;
|
|
7947
8042
|
};
|
|
7948
8043
|
```
|
|
7949
8044
|
|
|
@@ -7964,6 +8059,8 @@ export type RunResult = {
|
|
|
7964
8059
|
readonly at: number;
|
|
7965
8060
|
readonly layer: string;
|
|
7966
8061
|
readonly detail: string;
|
|
8062
|
+
/** Every video its evidence references, with who holds it (check, retry attempt or step). Absent when there is none. */
|
|
8063
|
+
readonly videos?: readonly VideoRef[];
|
|
7967
8064
|
};
|
|
7968
8065
|
```
|
|
7969
8066
|
|
|
@@ -8114,13 +8211,23 @@ export function testHistory(model: CatalogModel, key: string): TestHistoryDocume
|
|
|
8114
8211
|
|
|
8115
8212
|
One test and its recorded results, or null when the report has no test with that key.
|
|
8116
8213
|
|
|
8214
|
+
#### `hasVideo`
|
|
8215
|
+
|
|
8216
|
+
```ts
|
|
8217
|
+
export function hasVideo(run: Pick<RunSummary, 'videos'>): boolean
|
|
8218
|
+
```
|
|
8219
|
+
|
|
8220
|
+
Whether a run has at least one playable video recording.
|
|
8221
|
+
|
|
8117
8222
|
#### `runList`
|
|
8118
8223
|
|
|
8119
8224
|
```ts
|
|
8120
|
-
export function runList(history: TestHistory, version: string, limit = 100): RunList
|
|
8225
|
+
export function runList(history: TestHistory, version: string, limit = 100, query: RunQuery = {}): RunList
|
|
8121
8226
|
```
|
|
8122
8227
|
|
|
8123
|
-
Every run the history knows of, newest first, `limit` at most (default 100).
|
|
8228
|
+
Every run the history knows of, newest first, `limit` at most (default 100). With `query.video` only the runs that
|
|
8229
|
+
have a playable video recording: the filter and `total` apply to every run before `limit`, and nothing is read from
|
|
8230
|
+
any recording's bytes. `withVideo` always counts every run that has one.
|
|
8124
8231
|
|
|
8125
8232
|
#### `runDetail`
|
|
8126
8233
|
|
package/docs/reference/schema.md
CHANGED
|
@@ -69,6 +69,22 @@ export const BackendInfoSchema = …
|
|
|
69
69
|
|
|
70
70
|
A backend as the report shows it: where results came from, and whether it could be read.
|
|
71
71
|
|
|
72
|
+
#### `VideoRefSchema`
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
export const VideoRefSchema = …
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
One reference to a video held by a layer, check, retry attempt or step, found by the attachment's effective media type.
|
|
79
|
+
|
|
80
|
+
#### `VideoCountsSchema`
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
export const VideoCountsSchema = …
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Video references as counts: how many are referenced, how many distinct recordings can be played, how many cannot.
|
|
87
|
+
|
|
72
88
|
#### `EvidenceSummarySchema`
|
|
73
89
|
|
|
74
90
|
```ts
|
package/docs/server.md
CHANGED
|
@@ -276,7 +276,7 @@ with `format=markdown` (or `Accept: text/markdown`), and takes the project prefi
|
|
|
276
276
|
| 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` |
|
|
277
277
|
| One test: every recorded result (status, duration, environment, branch, commit, CI run), newest first | `GET /api/reports/tests/history?key=<key>` |
|
|
278
278
|
| Why Specs, domains and counts disagree: ambiguous spec paths, shared domains, gaps, a reconciliation of the totals | `GET /api/reports/tests/health` |
|
|
279
|
-
| The runs that recorded the report (by CI run URL, else branch and commit) | `GET /api/reports/runs` |
|
|
279
|
+
| 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` |
|
|
280
280
|
| Every test one run recorded | `GET /api/reports/runs/detail?key=<run key>` |
|
|
281
281
|
|
|
282
282
|
```sh
|