@crvouga/atlas 0.14.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 +12 -0
- package/dist/reports/src/artifact-tag-store.d.ts +20 -0
- package/dist/reports/src/artifact-tag-store.js +70 -0
- package/dist/reports/src/artifact-tags.d.ts +96 -0
- package/dist/reports/src/artifact-tags.js +96 -0
- package/dist/reports/src/evidence-detail.d.ts +20 -3
- package/dist/reports/src/evidence-detail.js +66 -13
- package/dist/reports/src/evidence-types.d.ts +33 -0
- package/dist/reports/src/layer-merge.js +2 -2
- package/dist/reports/src/reports-explorer-route.d.ts +8 -0
- package/dist/reports/src/reports-route.d.ts +8 -0
- package/dist/reports/src/test-catalog.d.ts +35 -26
- package/dist/reports/src/test-catalog.js +79 -15
- package/dist/schema/src/api.d.ts +246 -0
- package/dist/schema/src/api.js +59 -5
- package/dist/schema/src/layer.d.ts +33 -0
- package/dist/schema/src/layer.js +11 -1
- package/dist/server/src/app.js +35 -3
- package/dist/server/src/llms.js +2 -1
- package/dist/server/src/openapi.d.ts +1 -1
- package/dist/server/src/openapi.js +20 -3
- package/docs/extending.md +3 -0
- package/docs/reference/http-api.md +52 -2
- package/docs/reference/reports.md +327 -5
- package/docs/reference/schema.md +48 -0
- package/docs/rich-evidence.md +28 -0
- package/docs/server.md +25 -1
- package/llms-full.txt +108 -3
- package/package.json +1 -1
- package/packages/schema/schemas/check-detail.schema.json +28 -0
- package/packages/schema/schemas/effective-delta.schema.json +96 -0
- package/packages/schema/schemas/effective-evidence.schema.json +96 -0
- package/packages/schema/schemas/evidence-page.schema.json +48 -0
- package/packages/schema/schemas/layer-rows.schema.json +48 -0
- package/packages/schema/schemas/layer.schema.json +70 -0
- package/packages/schema/schemas/publish-checks.schema.json +35 -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 +83 -0
- package/packages/schema/schemas/test-history.schema.json +110 -0
- package/packages/server/openapi.json +1259 -33
- package/packages/visualizer/src/evidence/artifact-tags.tsx +285 -0
- package/packages/visualizer/src/evidence/artifacts.tsx +18 -12
- 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
|
@@ -121,6 +121,7 @@ export declare const AttachmentSchema: z.ZodObject<{
|
|
|
121
121
|
height: z.ZodOptional<z.ZodNumber>;
|
|
122
122
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
123
123
|
description: z.ZodOptional<z.ZodString>;
|
|
124
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
124
125
|
at: z.ZodOptional<z.ZodObject<{
|
|
125
126
|
step: z.ZodOptional<z.ZodString>;
|
|
126
127
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -181,6 +182,7 @@ export declare const StepSchema: z.ZodObject<{
|
|
|
181
182
|
height: z.ZodOptional<z.ZodNumber>;
|
|
182
183
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
183
184
|
description: z.ZodOptional<z.ZodString>;
|
|
185
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
184
186
|
at: z.ZodOptional<z.ZodObject<{
|
|
185
187
|
step: z.ZodOptional<z.ZodString>;
|
|
186
188
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -248,6 +250,7 @@ export declare const AttemptSchema: z.ZodObject<{
|
|
|
248
250
|
height: z.ZodOptional<z.ZodNumber>;
|
|
249
251
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
250
252
|
description: z.ZodOptional<z.ZodString>;
|
|
253
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
251
254
|
at: z.ZodOptional<z.ZodObject<{
|
|
252
255
|
step: z.ZodOptional<z.ZodString>;
|
|
253
256
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -292,6 +295,7 @@ export declare const AttemptSchema: z.ZodObject<{
|
|
|
292
295
|
height: z.ZodOptional<z.ZodNumber>;
|
|
293
296
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
294
297
|
description: z.ZodOptional<z.ZodString>;
|
|
298
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
295
299
|
at: z.ZodOptional<z.ZodObject<{
|
|
296
300
|
step: z.ZodOptional<z.ZodString>;
|
|
297
301
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -355,6 +359,7 @@ export declare const CheckSchema: z.ZodObject<{
|
|
|
355
359
|
height: z.ZodOptional<z.ZodNumber>;
|
|
356
360
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
357
361
|
description: z.ZodOptional<z.ZodString>;
|
|
362
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
358
363
|
at: z.ZodOptional<z.ZodObject<{
|
|
359
364
|
step: z.ZodOptional<z.ZodString>;
|
|
360
365
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -419,6 +424,7 @@ export declare const CheckSchema: z.ZodObject<{
|
|
|
419
424
|
height: z.ZodOptional<z.ZodNumber>;
|
|
420
425
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
421
426
|
description: z.ZodOptional<z.ZodString>;
|
|
427
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
422
428
|
at: z.ZodOptional<z.ZodObject<{
|
|
423
429
|
step: z.ZodOptional<z.ZodString>;
|
|
424
430
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -463,6 +469,7 @@ export declare const CheckSchema: z.ZodObject<{
|
|
|
463
469
|
height: z.ZodOptional<z.ZodNumber>;
|
|
464
470
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
465
471
|
description: z.ZodOptional<z.ZodString>;
|
|
472
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
466
473
|
at: z.ZodOptional<z.ZodObject<{
|
|
467
474
|
step: z.ZodOptional<z.ZodString>;
|
|
468
475
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -514,6 +521,7 @@ export declare const CheckSchema: z.ZodObject<{
|
|
|
514
521
|
height: z.ZodOptional<z.ZodNumber>;
|
|
515
522
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
516
523
|
description: z.ZodOptional<z.ZodString>;
|
|
524
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
517
525
|
at: z.ZodOptional<z.ZodObject<{
|
|
518
526
|
step: z.ZodOptional<z.ZodString>;
|
|
519
527
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -600,6 +608,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
600
608
|
height: z.ZodOptional<z.ZodNumber>;
|
|
601
609
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
602
610
|
description: z.ZodOptional<z.ZodString>;
|
|
611
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
603
612
|
at: z.ZodOptional<z.ZodObject<{
|
|
604
613
|
step: z.ZodOptional<z.ZodString>;
|
|
605
614
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -663,6 +672,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
663
672
|
height: z.ZodOptional<z.ZodNumber>;
|
|
664
673
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
665
674
|
description: z.ZodOptional<z.ZodString>;
|
|
675
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
666
676
|
at: z.ZodOptional<z.ZodObject<{
|
|
667
677
|
step: z.ZodOptional<z.ZodString>;
|
|
668
678
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -727,6 +737,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
727
737
|
height: z.ZodOptional<z.ZodNumber>;
|
|
728
738
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
729
739
|
description: z.ZodOptional<z.ZodString>;
|
|
740
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
730
741
|
at: z.ZodOptional<z.ZodObject<{
|
|
731
742
|
step: z.ZodOptional<z.ZodString>;
|
|
732
743
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -771,6 +782,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
771
782
|
height: z.ZodOptional<z.ZodNumber>;
|
|
772
783
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
773
784
|
description: z.ZodOptional<z.ZodString>;
|
|
785
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
774
786
|
at: z.ZodOptional<z.ZodObject<{
|
|
775
787
|
step: z.ZodOptional<z.ZodString>;
|
|
776
788
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -822,6 +834,7 @@ export declare const ReportPartSchema: z.ZodObject<{
|
|
|
822
834
|
height: z.ZodOptional<z.ZodNumber>;
|
|
823
835
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
824
836
|
description: z.ZodOptional<z.ZodString>;
|
|
837
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
825
838
|
at: z.ZodOptional<z.ZodObject<{
|
|
826
839
|
step: z.ZodOptional<z.ZodString>;
|
|
827
840
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -910,6 +923,7 @@ export declare const LayerSourceSchema: z.ZodObject<{
|
|
|
910
923
|
height: z.ZodOptional<z.ZodNumber>;
|
|
911
924
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
912
925
|
description: z.ZodOptional<z.ZodString>;
|
|
926
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
913
927
|
at: z.ZodOptional<z.ZodObject<{
|
|
914
928
|
step: z.ZodOptional<z.ZodString>;
|
|
915
929
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -974,6 +988,7 @@ export declare const LayerSourceSchema: z.ZodObject<{
|
|
|
974
988
|
height: z.ZodOptional<z.ZodNumber>;
|
|
975
989
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
976
990
|
description: z.ZodOptional<z.ZodString>;
|
|
991
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
977
992
|
at: z.ZodOptional<z.ZodObject<{
|
|
978
993
|
step: z.ZodOptional<z.ZodString>;
|
|
979
994
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1018,6 +1033,7 @@ export declare const LayerSourceSchema: z.ZodObject<{
|
|
|
1018
1033
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1019
1034
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1020
1035
|
description: z.ZodOptional<z.ZodString>;
|
|
1036
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1021
1037
|
at: z.ZodOptional<z.ZodObject<{
|
|
1022
1038
|
step: z.ZodOptional<z.ZodString>;
|
|
1023
1039
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1069,6 +1085,7 @@ export declare const LayerSourceSchema: z.ZodObject<{
|
|
|
1069
1085
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1070
1086
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1071
1087
|
description: z.ZodOptional<z.ZodString>;
|
|
1088
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1072
1089
|
at: z.ZodOptional<z.ZodObject<{
|
|
1073
1090
|
step: z.ZodOptional<z.ZodString>;
|
|
1074
1091
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1183,6 +1200,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1183
1200
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1184
1201
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1185
1202
|
description: z.ZodOptional<z.ZodString>;
|
|
1203
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1186
1204
|
at: z.ZodOptional<z.ZodObject<{
|
|
1187
1205
|
step: z.ZodOptional<z.ZodString>;
|
|
1188
1206
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1260,6 +1278,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1260
1278
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1261
1279
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1262
1280
|
description: z.ZodOptional<z.ZodString>;
|
|
1281
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1263
1282
|
at: z.ZodOptional<z.ZodObject<{
|
|
1264
1283
|
step: z.ZodOptional<z.ZodString>;
|
|
1265
1284
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1323,6 +1342,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1323
1342
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1324
1343
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1325
1344
|
description: z.ZodOptional<z.ZodString>;
|
|
1345
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1326
1346
|
at: z.ZodOptional<z.ZodObject<{
|
|
1327
1347
|
step: z.ZodOptional<z.ZodString>;
|
|
1328
1348
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1387,6 +1407,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1387
1407
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1388
1408
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1389
1409
|
description: z.ZodOptional<z.ZodString>;
|
|
1410
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1390
1411
|
at: z.ZodOptional<z.ZodObject<{
|
|
1391
1412
|
step: z.ZodOptional<z.ZodString>;
|
|
1392
1413
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1431,6 +1452,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1431
1452
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1432
1453
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1433
1454
|
description: z.ZodOptional<z.ZodString>;
|
|
1455
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1434
1456
|
at: z.ZodOptional<z.ZodObject<{
|
|
1435
1457
|
step: z.ZodOptional<z.ZodString>;
|
|
1436
1458
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1482,6 +1504,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1482
1504
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1483
1505
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1484
1506
|
description: z.ZodOptional<z.ZodString>;
|
|
1507
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1485
1508
|
at: z.ZodOptional<z.ZodObject<{
|
|
1486
1509
|
step: z.ZodOptional<z.ZodString>;
|
|
1487
1510
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1569,6 +1592,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1569
1592
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1570
1593
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1571
1594
|
description: z.ZodOptional<z.ZodString>;
|
|
1595
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1572
1596
|
at: z.ZodOptional<z.ZodObject<{
|
|
1573
1597
|
step: z.ZodOptional<z.ZodString>;
|
|
1574
1598
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1633,6 +1657,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1633
1657
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1634
1658
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1635
1659
|
description: z.ZodOptional<z.ZodString>;
|
|
1660
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1636
1661
|
at: z.ZodOptional<z.ZodObject<{
|
|
1637
1662
|
step: z.ZodOptional<z.ZodString>;
|
|
1638
1663
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1677,6 +1702,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1677
1702
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1678
1703
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1679
1704
|
description: z.ZodOptional<z.ZodString>;
|
|
1705
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1680
1706
|
at: z.ZodOptional<z.ZodObject<{
|
|
1681
1707
|
step: z.ZodOptional<z.ZodString>;
|
|
1682
1708
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1728,6 +1754,7 @@ export declare const LayerDocumentSchema: z.ZodObject<{
|
|
|
1728
1754
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1729
1755
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1730
1756
|
description: z.ZodOptional<z.ZodString>;
|
|
1757
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1731
1758
|
at: z.ZodOptional<z.ZodObject<{
|
|
1732
1759
|
step: z.ZodOptional<z.ZodString>;
|
|
1733
1760
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1855,6 +1882,7 @@ export declare const AttachmentInputSchema: z.ZodObject<{
|
|
|
1855
1882
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1856
1883
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1857
1884
|
description: z.ZodOptional<z.ZodString>;
|
|
1885
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1858
1886
|
at: z.ZodOptional<z.ZodObject<{
|
|
1859
1887
|
step: z.ZodOptional<z.ZodString>;
|
|
1860
1888
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1935,6 +1963,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
1935
1963
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1936
1964
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1937
1965
|
description: z.ZodOptional<z.ZodString>;
|
|
1966
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1938
1967
|
at: z.ZodOptional<z.ZodObject<{
|
|
1939
1968
|
step: z.ZodOptional<z.ZodString>;
|
|
1940
1969
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1990,6 +2019,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
1990
2019
|
height: z.ZodOptional<z.ZodNumber>;
|
|
1991
2020
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
1992
2021
|
description: z.ZodOptional<z.ZodString>;
|
|
2022
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1993
2023
|
at: z.ZodOptional<z.ZodObject<{
|
|
1994
2024
|
step: z.ZodOptional<z.ZodString>;
|
|
1995
2025
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -2078,6 +2108,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
2078
2108
|
height: z.ZodOptional<z.ZodNumber>;
|
|
2079
2109
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
2080
2110
|
description: z.ZodOptional<z.ZodString>;
|
|
2111
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2081
2112
|
at: z.ZodOptional<z.ZodObject<{
|
|
2082
2113
|
step: z.ZodOptional<z.ZodString>;
|
|
2083
2114
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -2104,6 +2135,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
2104
2135
|
height: z.ZodOptional<z.ZodNumber>;
|
|
2105
2136
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
2106
2137
|
description: z.ZodOptional<z.ZodString>;
|
|
2138
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2107
2139
|
at: z.ZodOptional<z.ZodObject<{
|
|
2108
2140
|
step: z.ZodOptional<z.ZodString>;
|
|
2109
2141
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
|
@@ -2163,6 +2195,7 @@ export declare const PublishChecksSchema: z.ZodObject<{
|
|
|
2163
2195
|
height: z.ZodOptional<z.ZodNumber>;
|
|
2164
2196
|
durationMs: z.ZodOptional<z.ZodNumber>;
|
|
2165
2197
|
description: z.ZodOptional<z.ZodString>;
|
|
2198
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
2166
2199
|
at: z.ZodOptional<z.ZodObject<{
|
|
2167
2200
|
step: z.ZodOptional<z.ZodString>;
|
|
2168
2201
|
offsetMs: z.ZodOptional<z.ZodNumber>;
|
package/dist/schema/src/layer.js
CHANGED
|
@@ -109,6 +109,7 @@ export const AttachmentSchema = z.looseObject({
|
|
|
109
109
|
height: z.number().nonnegative().optional(),
|
|
110
110
|
durationMs: z.number().nonnegative().optional().describe('For video and audio'),
|
|
111
111
|
description: z.string().optional(),
|
|
112
|
+
tags: z.array(z.string()).optional().describe('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'),
|
|
112
113
|
at: MomentSchema.optional(),
|
|
113
114
|
poster: z.string().optional().describe('The `id` of an attachment that stands for this one before it is opened'),
|
|
114
115
|
captions: z.array(z.string()).optional().describe('The `id`s of attachments (WebVTT, SRT) that caption this one'),
|
|
@@ -285,6 +286,15 @@ function lenientList(value, schema, prepare = (item) => item) {
|
|
|
285
286
|
});
|
|
286
287
|
}
|
|
287
288
|
const object = (value) => (typeof value === 'object' && value !== null && !Array.isArray(value) ? value : null);
|
|
289
|
+
/** An attachment whose `tags` cannot be read keeps its file: only the tags that are strings are kept. */
|
|
290
|
+
function lenientAttachment(item) {
|
|
291
|
+
const attachment = object(item);
|
|
292
|
+
if (!attachment || !('tags' in attachment))
|
|
293
|
+
return item;
|
|
294
|
+
const { tags, ...rest } = attachment;
|
|
295
|
+
const kept = lenientList(tags, z.string());
|
|
296
|
+
return kept ? { ...rest, tags: kept } : rest;
|
|
297
|
+
}
|
|
288
298
|
function lenientEvidence(holder, nested = []) {
|
|
289
299
|
const out = { ...holder };
|
|
290
300
|
const set = (key, value) => { if (value === undefined)
|
|
@@ -296,7 +306,7 @@ function lenientEvidence(holder, nested = []) {
|
|
|
296
306
|
if ('links' in out)
|
|
297
307
|
set('links', lenientList(out.links, LinkSchema));
|
|
298
308
|
if ('attachments' in out)
|
|
299
|
-
set('attachments', lenientList(out.attachments, AttachmentSchema));
|
|
309
|
+
set('attachments', lenientList(out.attachments, AttachmentSchema, lenientAttachment));
|
|
300
310
|
if ('annotations' in out)
|
|
301
311
|
set('annotations', lenientList(out.annotations, AnnotationSchema));
|
|
302
312
|
if ('labels' in out)
|
package/dist/server/src/app.js
CHANGED
|
@@ -20,6 +20,7 @@ import { combineBackends, summarize } from '../../reports/src/compose.js';
|
|
|
20
20
|
import { combineEvidence } from '../../reports/src/layer-merge.js';
|
|
21
21
|
import { layerCatalogKey } from '../../reports/src/layer-store.js';
|
|
22
22
|
import { createLayersApp } from '../../reports/src/layers-route.js';
|
|
23
|
+
import { readArtifactTagEntries, recordArtifactTags, ArtifactTagError } from '../../reports/src/artifact-tag-store.js';
|
|
23
24
|
import { publishChecks } from '../../reports/src/publish-checks.js';
|
|
24
25
|
import { reportStatus } from '../../reports/src/report-status.js';
|
|
25
26
|
import { ReportIndex } from '../../reports/src/report-index.js';
|
|
@@ -254,7 +255,7 @@ function createProjectApplication(options) {
|
|
|
254
255
|
name: 'atlas', version: options.version ?? null, role: identity?.role ?? 'read', open: auth.open,
|
|
255
256
|
write: options.write, ready: index.ready, viewers: events.count, time: now(),
|
|
256
257
|
backends: snapshot.backends.map(({ id, label, available }) => ({ id, label, available, write: id === options.write })),
|
|
257
|
-
...(({ backends: _unused, ...rest }) => rest)(index.stats()), capabilities: { events: true, ingest: true, store: true, checks: true, status: true, effective: true, live: true, tests: true }
|
|
258
|
+
...(({ backends: _unused, ...rest }) => rest)(index.stats()), capabilities: { events: true, ingest: true, store: true, checks: true, status: true, effective: true, live: true, tests: true, artifactTags: true }
|
|
258
259
|
});
|
|
259
260
|
});
|
|
260
261
|
app.get('/api/live', async (c) => c.json({ runs: await live.list() }, 200, { 'cache-control': 'no-store' }));
|
|
@@ -425,8 +426,9 @@ function createProjectApplication(options) {
|
|
|
425
426
|
return notReady(c);
|
|
426
427
|
const found = await catalog();
|
|
427
428
|
const limit = /^\d+$/.test(c.req.query('limit') ?? '') ? Number(c.req.query('limit')) : 100;
|
|
428
|
-
const
|
|
429
|
-
|
|
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)}`);
|
|
430
432
|
});
|
|
431
433
|
app.get('/api/reports/runs/detail', async (c) => {
|
|
432
434
|
if (!index.ready)
|
|
@@ -440,6 +442,36 @@ function createProjectApplication(options) {
|
|
|
440
442
|
return problemResponse(404, 'No run has that key.');
|
|
441
443
|
return answer(c, found.version, () => document, () => runMarkdown(document), `run|${key}|${wantsMarkdown(c)}`);
|
|
442
444
|
});
|
|
445
|
+
// Reviewer tags for attachments: an audit trail of edits kept beside the layers, which they never change. Read
|
|
446
|
+
// with a read token; an edit needs a publish token in a Bearer header, and is recorded under that token's name.
|
|
447
|
+
const tagStore = options.liveStore ?? writeBackend.store;
|
|
448
|
+
// Edits are ordered by time, so two in one millisecond are told apart by moving the later one on.
|
|
449
|
+
let lastTagAt = 0;
|
|
450
|
+
const artifactTags = cached(() => readArtifactTagEntries(tagStore), 5_000);
|
|
451
|
+
app.get('/api/reports/artifact-tags', async (c) => c.json({ schemaVersion: 1, entries: await artifactTags() }, 200, { 'cache-control': 'no-store' }));
|
|
452
|
+
app.post('/api/reports/artifact-tags', async (c) => {
|
|
453
|
+
const verdict = auth.check(c.req.raw, 'publish');
|
|
454
|
+
if (!verdict.ok)
|
|
455
|
+
return unauthorized(verdict);
|
|
456
|
+
try {
|
|
457
|
+
const bytes = await readLimited(c.req.raw, Math.min(limits.json, 64 * 1024));
|
|
458
|
+
let input;
|
|
459
|
+
try {
|
|
460
|
+
input = JSON.parse(new TextDecoder().decode(bytes));
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
return problemResponse(422, 'The body is not JSON.');
|
|
464
|
+
}
|
|
465
|
+
const entry = await recordArtifactTags(tagStore, input, verdict.identity.name, lastTagAt = Math.max(now(), lastTagAt + 1));
|
|
466
|
+
await artifactTags.invalidate();
|
|
467
|
+
return c.json(entry, 201, { 'cache-control': 'no-store' });
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
if (error instanceof ArtifactTagError || error instanceof Refusal)
|
|
471
|
+
return problemResponse(error.status, error.message);
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
474
|
+
});
|
|
443
475
|
app.route('/api/reports', layers);
|
|
444
476
|
// Upload: the store protocol, and a simple JSON way in for anything that can make a request.
|
|
445
477
|
app.route('/api/store', storeApi({
|
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
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The version of the API described, `major.minor.patch`: the minor grows when an operation or field is added, the major never within `/api`. */
|
|
2
|
-
export declare const API_VERSION = "1.
|
|
2
|
+
export declare const API_VERSION = "1.5.0";
|
|
3
3
|
/** Who may call an operation: `none` (anyone), or the least role that may (`read` < `publish` < `admin`). */
|
|
4
4
|
export type Role = 'none' | 'read' | 'publish' | 'admin';
|
|
5
5
|
type JsonSchema = Record<string, unknown>;
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { z } from 'zod/v4';
|
|
16
16
|
import { ProjectsSchema } from '../../schema/src/project.js';
|
|
17
|
-
import { EffectiveDeltaSchema, EffectiveEvidenceSchema, EvidencePageSchema, HealthSchema, LayerRowsSchema, CheckDetailSchema, OutlineSchema, ProblemSchema, ReadySchema, ServerInfoSchema, StoreListingSchema, StoreWriteResultSchema, ChecksResultSchema, HelloEventSchema, ReportEventSchema, PingEventSchema, TestCatalogSchema, CatalogHealthSchema, TestHistorySchema, RunListSchema, RunDetailSchema } from '../../schema/src/api.js';
|
|
17
|
+
import { EffectiveDeltaSchema, EffectiveEvidenceSchema, EvidencePageSchema, HealthSchema, LayerRowsSchema, CheckDetailSchema, OutlineSchema, ProblemSchema, ReadySchema, ServerInfoSchema, StoreListingSchema, StoreWriteResultSchema, ChecksResultSchema, HelloEventSchema, ReportEventSchema, PingEventSchema, TestCatalogSchema, CatalogHealthSchema, TestHistorySchema, RunListSchema, RunDetailSchema, ArtifactTagEditSchema, ArtifactTagEntrySchema, ArtifactTagsSchema } from '../../schema/src/api.js';
|
|
18
18
|
import { LiveRunSchema, LiveRunStartSchema, LiveRunUpdateSchema, ServerEditableSettingsSchema, ServerSettingsSnapshotSchema, AtlasChangeSchema, PublishChecksSchema, ReportSourcesSchema, ReportStatusSchema, RunsIndexSchema, SpecIndexSchema } from '../../schema/src/index.js';
|
|
19
19
|
/** The version of the API described, `major.minor.patch`: the minor grows when an operation or field is added, the major never within `/api`. */
|
|
20
|
-
export const API_VERSION = '1.
|
|
20
|
+
export const API_VERSION = '1.5.0';
|
|
21
21
|
/** The JSON Schema of a Zod schema, as OpenAPI 3.1 embeds it. `output` for what the server sends, `input` for what it accepts. */
|
|
22
22
|
function schemaOf(schema, io = 'output') {
|
|
23
23
|
const { $schema: _dialect, ...json } = z.toJSONSchema(schema, { target: 'draft-2020-12', unrepresentable: 'any', io });
|
|
@@ -35,6 +35,7 @@ const COMPONENTS = {
|
|
|
35
35
|
PublishChecks: [PublishChecksSchema.partial({ recordedAtMs: true }), 'input'], ChecksResult: [ChecksResultSchema, 'output'],
|
|
36
36
|
StoreListing: [StoreListingSchema, 'output'], StoreWriteResult: [StoreWriteResultSchema, 'output'],
|
|
37
37
|
Outline: [OutlineSchema, 'output'], EvidencePage: [EvidencePageSchema, 'output'], LayerRows: [LayerRowsSchema, 'output'], CheckDetail: [CheckDetailSchema, 'output'],
|
|
38
|
+
ArtifactTagEdit: [ArtifactTagEditSchema, 'input'], ArtifactTagEntry: [ArtifactTagEntrySchema, 'output'], ArtifactTags: [ArtifactTagsSchema, 'output'],
|
|
38
39
|
TestCatalog: [TestCatalogSchema, 'output'], CatalogHealth: [CatalogHealthSchema, 'output'], TestHistory: [TestHistorySchema, 'output'], RunList: [RunListSchema, 'output'], RunDetail: [RunDetailSchema, 'output'],
|
|
39
40
|
SpecIndex: [SpecIndexSchema, 'output'], ReportSources: [ReportSourcesSchema, 'output'], RunsIndex: [RunsIndexSchema, 'output'],
|
|
40
41
|
RunEvent: [LiveRunSchema, 'output'], HelloEvent: [HelloEventSchema, 'output'], ReportEvent: [ReportEventSchema, 'output'], AtlasChangeEvent: [AtlasChangeSchema, 'output'], PingEvent: [PingEventSchema, 'output']
|
|
@@ -271,6 +272,8 @@ const BASE_OPERATIONS = [
|
|
|
271
272
|
{ name: 'domain', in: 'query', description: 'Only tests whose spec is in this domain (`(no spec)` for none). Repeatable or comma-separated.', schema: str },
|
|
272
273
|
{ name: 'spec', in: 'query', description: 'Only tests of this spec path. Repeatable or comma-separated.', schema: str, example: 'specs/checkout/pay.feature' },
|
|
273
274
|
{ name: 'tag', in: 'query', description: 'Only tests with this tag. Repeatable or comma-separated.', schema: str },
|
|
275
|
+
{ name: 'artifactTag', in: 'query', description: 'Only tests with an attachment tagged this (`demo` and `#demo` are the same tag), not counting tags inherited from a check or run: the exact artifact query. `inherited=1` widens it to the tags inherited from the check and run. Repeatable or comma-separated.', schema: str, example: 'demo' },
|
|
276
|
+
{ name: 'inherited', in: 'query', description: 'With `artifactTag`: `1` also matches the tags an attachment inherits from its check and run, so a recording without a tag of its own under a run tagged `demo` is found. Without it the query is exact.', schema: { type: 'string', enum: ['1', 'true'] } },
|
|
274
277
|
{ name: 'run', in: 'query', description: 'Only tests recorded in this run (a `key` from `/api/reports/runs`).', schema: str },
|
|
275
278
|
{ name: 'sort', in: 'query', description: 'The order: `spec` (default: domain, spec, title), `title`, `recent`, `slowest`, `failures`, `flaky` or `runs`.', schema: { type: 'string', enum: ['spec', 'title', 'recent', 'slowest', 'failures', 'flaky', 'runs'] } },
|
|
276
279
|
{ name: 'offset', in: 'query', description: 'Skip this many matching tests.', schema: { type: 'integer', minimum: 0 } },
|
|
@@ -305,12 +308,26 @@ const BASE_OPERATIONS = [
|
|
|
305
308
|
],
|
|
306
309
|
responses: { '200': json('The test and its history.', 'TestHistory'), '304': { description: 'Unchanged since the `If-None-Match` ETag.' }, '400': problem('No `key`.'), '401': UNAUTHORIZED, '404': problem('No test in the report has that key.'), '503': NOT_READY }
|
|
307
310
|
},
|
|
311
|
+
{
|
|
312
|
+
route: 'GET /api/reports/artifact-tags', path: '/api/reports/artifact-tags', operationId: 'getArtifactTags', tag: 'Report', role: 'read',
|
|
313
|
+
summary: 'Reviewer tags of attachments: every recorded edit',
|
|
314
|
+
description: 'The audit trail of tags that reviewers added to and removed from attachments after upload, oldest first. Each entry says which attachment (`target`), which normalized tags were added and removed, the name of the token that made the edit (`by`, never the token) and when (`atMs`). The layers themselves are never rewritten, so a producer\'s own `tags` on an attachment stay as uploaded; fold the entries (oldest first, the last edit of a tag wins) to get the reviewer tags that stand now. Tags are normalized: `#Demo` and `demo` are `demo`.',
|
|
315
|
+
responses: { '200': json('The edits.', 'ArtifactTags'), '401': UNAUTHORIZED }
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
route: 'POST /api/reports/artifact-tags', path: '/api/reports/artifact-tags', operationId: 'editArtifactTags', tag: 'Report', role: 'publish',
|
|
319
|
+
summary: 'Tag or untag one attachment after upload',
|
|
320
|
+
description: 'Records one edit of the reviewer tags of one attachment, by the name of the publish (or admin) token that sends it. It adds an immutable entry to the audit trail and changes nothing else: not the layer, not the movie, not any result. The tags it removes are reviewer tags only; a producer\'s tags are part of the layer and stay. A reader-only token, a browser sign-in box and an anonymous reader are refused. Name the attachment as `artifactRef` does (`id:<id>`, else `key:<bundleKey>`, else `name:<name>`).',
|
|
321
|
+
body: { description: 'The attachment and the tags to add and remove. At most 20 of each, 64 KiB.', contentType: 'application/json', required: true, schema: ref('ArtifactTagEdit') },
|
|
322
|
+
responses: { '201': json('The recorded entry.', 'ArtifactTagEntry'), '401': UNAUTHORIZED, '403': FORBIDDEN, '413': problem('Request too large.'), '422': problem('Not a tag edit: a bad target, an empty or over-long tag, or nothing to change.') }
|
|
323
|
+
},
|
|
308
324
|
{
|
|
309
325
|
route: 'GET /api/reports/runs', path: '/api/reports/runs', operationId: 'getReportRuns', tag: 'Report', role: 'read',
|
|
310
326
|
summary: 'The runs that recorded the report',
|
|
311
|
-
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.',
|
|
312
328
|
parameters: [
|
|
313
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' } },
|
|
314
331
|
{ name: 'format', in: 'query', description: '`markdown` for Markdown instead of JSON.', schema: { type: 'string', enum: ['json', 'markdown'] } }
|
|
315
332
|
],
|
|
316
333
|
responses: { '200': json('The runs.', 'RunList'), '304': { description: 'Unchanged since the `If-None-Match` ETag.' }, '401': UNAUTHORIZED, '503': NOT_READY }
|
package/docs/extending.md
CHANGED
|
@@ -35,6 +35,9 @@ carry any of these. Every one is optional.
|
|
|
35
35
|
| `extensions{}` | Structured data of your own or a tool's, under a name you own. |
|
|
36
36
|
| `measurements[]` | Numbers, with units and an allowance (already part of the contract). |
|
|
37
37
|
|
|
38
|
+
An attachment may carry its own `tags[]` (`demo`): shown as `#demo` and searchable; see
|
|
39
|
+
[Tagging artifacts and demos](rich-evidence.md#tagging-artifacts-and-demos).
|
|
40
|
+
|
|
38
41
|
On a **layer** the same fields describe the whole run: its CI log, a coverage report, an archive of traces, the
|
|
39
42
|
build number, a link to the pipeline.
|
|
40
43
|
|
|
@@ -467,6 +467,8 @@ Send `format=markdown` (or `Accept: text/markdown`) for the same as a Markdown r
|
|
|
467
467
|
- `domain` (query): Only tests whose spec is in this domain (`(no spec)` for none). Repeatable or comma-separated.
|
|
468
468
|
- `spec` (query): Only tests of this spec path. Repeatable or comma-separated.
|
|
469
469
|
- `tag` (query): Only tests with this tag. Repeatable or comma-separated.
|
|
470
|
+
- `artifactTag` (query): Only tests with an attachment tagged this (`demo` and `#demo` are the same tag), not counting tags inherited from a check or run: the exact artifact query. `inherited=1` widens it to the tags inherited from the check and run. Repeatable or comma-separated.
|
|
471
|
+
- `inherited` (query): With `artifactTag`: `1` also matches the tags an attachment inherits from its check and run, so a recording without a tag of its own under a run tagged `demo` is found. Without it the query is exact.
|
|
470
472
|
- `run` (query): Only tests recorded in this run (a `key` from `/api/reports/runs`).
|
|
471
473
|
- `sort` (query): The order: `spec` (default: domain, spec, title), `title`, `recent`, `slowest`, `failures`, `flaky` or `runs`.
|
|
472
474
|
- `offset` (query): Skip this many matching tests.
|
|
@@ -496,6 +498,8 @@ Scoped to one project. The same project may be selected on the unprefixed API us
|
|
|
496
498
|
- `domain` (query): Only tests whose spec is in this domain (`(no spec)` for none). Repeatable or comma-separated.
|
|
497
499
|
- `spec` (query): Only tests of this spec path. Repeatable or comma-separated.
|
|
498
500
|
- `tag` (query): Only tests with this tag. Repeatable or comma-separated.
|
|
501
|
+
- `artifactTag` (query): Only tests with an attachment tagged this (`demo` and `#demo` are the same tag), not counting tags inherited from a check or run: the exact artifact query. `inherited=1` widens it to the tags inherited from the check and run. Repeatable or comma-separated.
|
|
502
|
+
- `inherited` (query): With `artifactTag`: `1` also matches the tags an attachment inherits from its check and run, so a recording without a tag of its own under a run tagged `demo` is found. Without it the query is exact.
|
|
499
503
|
- `run` (query): Only tests recorded in this run (a `key` from `/api/reports/runs`).
|
|
500
504
|
- `sort` (query): The order: `spec` (default: domain, spec, title), `title`, `recent`, `slowest`, `failures`, `flaky` or `runs`.
|
|
501
505
|
- `offset` (query): Skip this many matching tests.
|
|
@@ -564,13 +568,58 @@ Scoped to one project. The same project may be selected on the unprefixed API us
|
|
|
564
568
|
|
|
565
569
|
Responses: `200` The test and its history.; `304` Unchanged since the `If-None-Match` ETag.; `400` No `key`.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `404` No test in the report has that key.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
|
|
566
570
|
|
|
571
|
+
### Reviewer tags of attachments: every recorded edit
|
|
572
|
+
|
|
573
|
+
`GET /api/reports/artifact-tags` · role: read
|
|
574
|
+
|
|
575
|
+
The audit trail of tags that reviewers added to and removed from attachments after upload, oldest first. Each entry says which attachment (`target`), which normalized tags were added and removed, the name of the token that made the edit (`by`, never the token) and when (`atMs`). The layers themselves are never rewritten, so a producer's own `tags` on an attachment stay as uploaded; fold the entries (oldest first, the last edit of a tag wins) to get the reviewer tags that stand now. Tags are normalized: `#Demo` and `demo` are `demo`.
|
|
576
|
+
|
|
577
|
+
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
578
|
+
|
|
579
|
+
Responses: `200` The edits.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.
|
|
580
|
+
|
|
581
|
+
### Reviewer tags of attachments: every recorded edit
|
|
582
|
+
|
|
583
|
+
`GET /api/projects/{project}/reports/artifact-tags` · role: read
|
|
584
|
+
|
|
585
|
+
The audit trail of tags that reviewers added to and removed from attachments after upload, oldest first. Each entry says which attachment (`target`), which normalized tags were added and removed, the name of the token that made the edit (`by`, never the token) and when (`atMs`). The layers themselves are never rewritten, so a producer's own `tags` on an attachment stay as uploaded; fold the entries (oldest first, the last edit of a tag wins) to get the reviewer tags that stand now. Tags are normalized: `#Demo` and `demo` are `demo`.
|
|
586
|
+
|
|
587
|
+
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.
|
|
588
|
+
|
|
589
|
+
- `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.
|
|
590
|
+
|
|
591
|
+
Responses: `200` The edits.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.
|
|
592
|
+
|
|
593
|
+
### Tag or untag one attachment after upload
|
|
594
|
+
|
|
595
|
+
`POST /api/reports/artifact-tags` · role: publish
|
|
596
|
+
|
|
597
|
+
Records one edit of the reviewer tags of one attachment, by the name of the publish (or admin) token that sends it. It adds an immutable entry to the audit trail and changes nothing else: not the layer, not the movie, not any result. The tags it removes are reviewer tags only; a producer's tags are part of the layer and stay. A reader-only token, a browser sign-in box and an anonymous reader are refused. Name the attachment as `artifactRef` does (`id:<id>`, else `key:<bundleKey>`, else `name:<name>`).
|
|
598
|
+
|
|
599
|
+
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
600
|
+
|
|
601
|
+
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.
|
|
602
|
+
|
|
603
|
+
### Tag or untag one attachment after upload
|
|
604
|
+
|
|
605
|
+
`POST /api/projects/{project}/reports/artifact-tags` · role: publish
|
|
606
|
+
|
|
607
|
+
Records one edit of the reviewer tags of one attachment, by the name of the publish (or admin) token that sends it. It adds an immutable entry to the audit trail and changes nothing else: not the layer, not the movie, not any result. The tags it removes are reviewer tags only; a producer's tags are part of the layer and stay. A reader-only token, a browser sign-in box and an anonymous reader are refused. Name the attachment as `artifactRef` does (`id:<id>`, else `key:<bundleKey>`, else `name:<name>`).
|
|
608
|
+
|
|
609
|
+
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.
|
|
610
|
+
|
|
611
|
+
- `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.
|
|
612
|
+
|
|
613
|
+
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.
|
|
614
|
+
|
|
567
615
|
### The runs that recorded the report
|
|
568
616
|
|
|
569
617
|
`GET /api/reports/runs` · role: read
|
|
570
618
|
|
|
571
|
-
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.
|
|
572
620
|
|
|
573
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.
|
|
574
623
|
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
575
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.
|
|
576
625
|
|
|
@@ -580,12 +629,13 @@ Responses: `200` The runs.; `304` Unchanged since the `If-None-Match` ETag.; `40
|
|
|
580
629
|
|
|
581
630
|
`GET /api/projects/{project}/reports/runs` · role: read
|
|
582
631
|
|
|
583
|
-
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.
|
|
584
633
|
|
|
585
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.
|
|
586
635
|
|
|
587
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.
|
|
588
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.
|
|
589
639
|
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
590
640
|
|
|
591
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`.
|