@crvouga/atlas 1.8.0 → 1.9.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 (32) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/dist/reports/src/evidence-detail.js +6 -1
  3. package/dist/reports/src/evidence-types.d.ts +2 -0
  4. package/dist/reports/src/reliability.d.ts +181 -0
  5. package/dist/reports/src/reliability.js +294 -0
  6. package/dist/reports/src/report-index.js +1 -1
  7. package/dist/reports/src/test-catalog.d.ts +8 -2
  8. package/dist/reports/src/test-catalog.js +10 -5
  9. package/dist/schema/src/api.d.ts +226 -0
  10. package/dist/schema/src/api.js +51 -0
  11. package/dist/server/src/app.js +15 -0
  12. package/dist/server/src/llms.js +1 -0
  13. package/dist/server/src/openapi.js +25 -2
  14. package/docs/reference/http-api.md +48 -0
  15. package/docs/reference/reports.md +272 -1
  16. package/docs/reference/schema.md +11 -0
  17. package/docs/server.md +18 -0
  18. package/docs/ui.md +10 -0
  19. package/llms-full.txt +76 -0
  20. package/package.json +1 -1
  21. package/packages/schema/schemas/effective-delta.schema.json +26 -0
  22. package/packages/schema/schemas/effective-evidence.schema.json +26 -0
  23. package/packages/schema/schemas/evidence-page.schema.json +13 -0
  24. package/packages/schema/schemas/layer-rows.schema.json +13 -0
  25. package/packages/schema/schemas/test-catalog.schema.json +17 -0
  26. package/packages/schema/schemas/test-history.schema.json +34 -0
  27. package/packages/schema/schemas/test-reliability.schema.json +629 -0
  28. package/packages/server/openapi.json +1722 -568
  29. package/packages/visualizer/src/explorer/Reliability.tsx +337 -0
  30. package/packages/visualizer/src/explorer/TestDetail.tsx +3 -0
  31. package/packages/visualizer/src/explorer/catalog.tsx +1 -1
  32. package/packages/visualizer/src/explorer/search.ts +36 -0
@@ -121,6 +121,12 @@ export declare const EvidenceSummarySchema: z.ZodObject<{
121
121
  steps: z.ZodNumber;
122
122
  annotations: z.ZodNumber;
123
123
  attempts: z.ZodNumber;
124
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
125
+ passed: "passed";
126
+ failed: "failed";
127
+ running: "running";
128
+ "not-run": "not-run";
129
+ }>>>;
124
130
  links: z.ZodNumber;
125
131
  tags: z.ZodArray<z.ZodString>;
126
132
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -191,6 +197,12 @@ export declare const LayerHeaderSchema: z.ZodObject<{
191
197
  steps: z.ZodNumber;
192
198
  annotations: z.ZodNumber;
193
199
  attempts: z.ZodNumber;
200
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
201
+ passed: "passed";
202
+ failed: "failed";
203
+ running: "running";
204
+ "not-run": "not-run";
205
+ }>>>;
194
206
  links: z.ZodNumber;
195
207
  tags: z.ZodArray<z.ZodString>;
196
208
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -272,6 +284,12 @@ export declare const LayerRowSchema: z.ZodObject<{
272
284
  steps: z.ZodNumber;
273
285
  annotations: z.ZodNumber;
274
286
  attempts: z.ZodNumber;
287
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
288
+ passed: "passed";
289
+ failed: "failed";
290
+ running: "running";
291
+ "not-run": "not-run";
292
+ }>>>;
275
293
  links: z.ZodNumber;
276
294
  tags: z.ZodArray<z.ZodString>;
277
295
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -351,6 +369,12 @@ export declare const LayerRowsSchema: z.ZodObject<{
351
369
  steps: z.ZodNumber;
352
370
  annotations: z.ZodNumber;
353
371
  attempts: z.ZodNumber;
372
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
373
+ passed: "passed";
374
+ failed: "failed";
375
+ running: "running";
376
+ "not-run": "not-run";
377
+ }>>>;
354
378
  links: z.ZodNumber;
355
379
  tags: z.ZodArray<z.ZodString>;
356
380
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -432,6 +456,12 @@ export declare const LayerEvidenceSchema: z.ZodObject<{
432
456
  steps: z.ZodNumber;
433
457
  annotations: z.ZodNumber;
434
458
  attempts: z.ZodNumber;
459
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
460
+ passed: "passed";
461
+ failed: "failed";
462
+ running: "running";
463
+ "not-run": "not-run";
464
+ }>>>;
435
465
  links: z.ZodNumber;
436
466
  tags: z.ZodArray<z.ZodString>;
437
467
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -507,6 +537,12 @@ export declare const LayerEvidenceSchema: z.ZodObject<{
507
537
  steps: z.ZodNumber;
508
538
  annotations: z.ZodNumber;
509
539
  attempts: z.ZodNumber;
540
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
541
+ passed: "passed";
542
+ failed: "failed";
543
+ running: "running";
544
+ "not-run": "not-run";
545
+ }>>>;
510
546
  links: z.ZodNumber;
511
547
  tags: z.ZodArray<z.ZodString>;
512
548
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -597,6 +633,12 @@ export declare const EffectiveEvidenceSchema: z.ZodObject<{
597
633
  steps: z.ZodNumber;
598
634
  annotations: z.ZodNumber;
599
635
  attempts: z.ZodNumber;
636
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
637
+ passed: "passed";
638
+ failed: "failed";
639
+ running: "running";
640
+ "not-run": "not-run";
641
+ }>>>;
600
642
  links: z.ZodNumber;
601
643
  tags: z.ZodArray<z.ZodString>;
602
644
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -672,6 +714,12 @@ export declare const EffectiveEvidenceSchema: z.ZodObject<{
672
714
  steps: z.ZodNumber;
673
715
  annotations: z.ZodNumber;
674
716
  attempts: z.ZodNumber;
717
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
718
+ passed: "passed";
719
+ failed: "failed";
720
+ running: "running";
721
+ "not-run": "not-run";
722
+ }>>>;
675
723
  links: z.ZodNumber;
676
724
  tags: z.ZodArray<z.ZodString>;
677
725
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -783,6 +831,12 @@ export declare const EffectiveDeltaSchema: z.ZodObject<{
783
831
  steps: z.ZodNumber;
784
832
  annotations: z.ZodNumber;
785
833
  attempts: z.ZodNumber;
834
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
835
+ passed: "passed";
836
+ failed: "failed";
837
+ running: "running";
838
+ "not-run": "not-run";
839
+ }>>>;
786
840
  links: z.ZodNumber;
787
841
  tags: z.ZodArray<z.ZodString>;
788
842
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -858,6 +912,12 @@ export declare const EffectiveDeltaSchema: z.ZodObject<{
858
912
  steps: z.ZodNumber;
859
913
  annotations: z.ZodNumber;
860
914
  attempts: z.ZodNumber;
915
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
916
+ passed: "passed";
917
+ failed: "failed";
918
+ running: "running";
919
+ "not-run": "not-run";
920
+ }>>>;
861
921
  links: z.ZodNumber;
862
922
  tags: z.ZodArray<z.ZodString>;
863
923
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -961,6 +1021,12 @@ export declare const EvidencePageSchema: z.ZodObject<{
961
1021
  steps: z.ZodNumber;
962
1022
  annotations: z.ZodNumber;
963
1023
  attempts: z.ZodNumber;
1024
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1025
+ passed: "passed";
1026
+ failed: "failed";
1027
+ running: "running";
1028
+ "not-run": "not-run";
1029
+ }>>>;
964
1030
  links: z.ZodNumber;
965
1031
  tags: z.ZodArray<z.ZodString>;
966
1032
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1525,11 +1591,18 @@ export declare const TestResultEntrySchema: z.ZodObject<{
1525
1591
  layer: z.ZodString;
1526
1592
  backend: z.ZodString;
1527
1593
  detail: z.ZodString;
1594
+ specRevision: z.ZodOptional<z.ZodString>;
1528
1595
  evidence: z.ZodOptional<z.ZodObject<{
1529
1596
  attachments: z.ZodNumber;
1530
1597
  steps: z.ZodNumber;
1531
1598
  annotations: z.ZodNumber;
1532
1599
  attempts: z.ZodNumber;
1600
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1601
+ passed: "passed";
1602
+ failed: "failed";
1603
+ running: "running";
1604
+ "not-run": "not-run";
1605
+ }>>>;
1533
1606
  links: z.ZodNumber;
1534
1607
  tags: z.ZodArray<z.ZodString>;
1535
1608
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1622,11 +1695,18 @@ export declare const TestSummarySchema: z.ZodObject<{
1622
1695
  layer: z.ZodString;
1623
1696
  backend: z.ZodString;
1624
1697
  detail: z.ZodString;
1698
+ specRevision: z.ZodOptional<z.ZodString>;
1625
1699
  evidence: z.ZodOptional<z.ZodObject<{
1626
1700
  attachments: z.ZodNumber;
1627
1701
  steps: z.ZodNumber;
1628
1702
  annotations: z.ZodNumber;
1629
1703
  attempts: z.ZodNumber;
1704
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1705
+ passed: "passed";
1706
+ failed: "failed";
1707
+ running: "running";
1708
+ "not-run": "not-run";
1709
+ }>>>;
1630
1710
  links: z.ZodNumber;
1631
1711
  tags: z.ZodArray<z.ZodString>;
1632
1712
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1914,11 +1994,18 @@ export declare const TestCatalogSchema: z.ZodObject<{
1914
1994
  layer: z.ZodString;
1915
1995
  backend: z.ZodString;
1916
1996
  detail: z.ZodString;
1997
+ specRevision: z.ZodOptional<z.ZodString>;
1917
1998
  evidence: z.ZodOptional<z.ZodObject<{
1918
1999
  attachments: z.ZodNumber;
1919
2000
  steps: z.ZodNumber;
1920
2001
  annotations: z.ZodNumber;
1921
2002
  attempts: z.ZodNumber;
2003
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2004
+ passed: "passed";
2005
+ failed: "failed";
2006
+ running: "running";
2007
+ "not-run": "not-run";
2008
+ }>>>;
1922
2009
  links: z.ZodNumber;
1923
2010
  tags: z.ZodArray<z.ZodString>;
1924
2011
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -2133,11 +2220,18 @@ export declare const TestHistorySchema: z.ZodObject<{
2133
2220
  layer: z.ZodString;
2134
2221
  backend: z.ZodString;
2135
2222
  detail: z.ZodString;
2223
+ specRevision: z.ZodOptional<z.ZodString>;
2136
2224
  evidence: z.ZodOptional<z.ZodObject<{
2137
2225
  attachments: z.ZodNumber;
2138
2226
  steps: z.ZodNumber;
2139
2227
  annotations: z.ZodNumber;
2140
2228
  attempts: z.ZodNumber;
2229
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2230
+ passed: "passed";
2231
+ failed: "failed";
2232
+ running: "running";
2233
+ "not-run": "not-run";
2234
+ }>>>;
2141
2235
  links: z.ZodNumber;
2142
2236
  tags: z.ZodArray<z.ZodString>;
2143
2237
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -2249,11 +2343,18 @@ export declare const TestHistorySchema: z.ZodObject<{
2249
2343
  layer: z.ZodString;
2250
2344
  backend: z.ZodString;
2251
2345
  detail: z.ZodString;
2346
+ specRevision: z.ZodOptional<z.ZodString>;
2252
2347
  evidence: z.ZodOptional<z.ZodObject<{
2253
2348
  attachments: z.ZodNumber;
2254
2349
  steps: z.ZodNumber;
2255
2350
  annotations: z.ZodNumber;
2256
2351
  attempts: z.ZodNumber;
2352
+ attemptStatuses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2353
+ passed: "passed";
2354
+ failed: "failed";
2355
+ running: "running";
2356
+ "not-run": "not-run";
2357
+ }>>>;
2257
2358
  links: z.ZodNumber;
2258
2359
  tags: z.ZodArray<z.ZodString>;
2259
2360
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -2307,6 +2408,131 @@ export declare const TestHistorySchema: z.ZodObject<{
2307
2408
  }, z.core.$loose>>>;
2308
2409
  }, z.core.$loose>>;
2309
2410
  }, z.core.$loose>;
2411
+ /**
2412
+ * `GET /api/reports/tests/reliability`: how reliable one test is inside a scope, and exactly how that figure was counted.
2413
+ * The snapshots one run recorded for one producer, environment and parameter set are one independent execution; only
2414
+ * `passed` and `failed` executions are trials; `notes` say when the sample is too small, pooled, selectively rerun or
2415
+ * bounded. It reads results and never changes a status. It is not a production failure probability.
2416
+ */
2417
+ export declare const TestReliabilitySchema: z.ZodObject<{
2418
+ schemaVersion: z.ZodLiteral<1>;
2419
+ key: z.ZodString;
2420
+ scope: z.ZodObject<{
2421
+ environment: z.ZodOptional<z.ZodArray<z.ZodString>>;
2422
+ producer: z.ZodOptional<z.ZodArray<z.ZodString>>;
2423
+ parameter: z.ZodOptional<z.ZodArray<z.ZodString>>;
2424
+ branch: z.ZodOptional<z.ZodArray<z.ZodString>>;
2425
+ sha: z.ZodOptional<z.ZodArray<z.ZodString>>;
2426
+ specRevision: z.ZodOptional<z.ZodArray<z.ZodString>>;
2427
+ revision: z.ZodOptional<z.ZodEnum<{
2428
+ any: "any";
2429
+ current: "current";
2430
+ }>>;
2431
+ since: z.ZodOptional<z.ZodNumber>;
2432
+ until: z.ZodOptional<z.ZodNumber>;
2433
+ }, z.core.$loose>;
2434
+ minimumExecutions: z.ZodNumber;
2435
+ sample: z.ZodEnum<{
2436
+ none: "none";
2437
+ insufficient: "insufficient";
2438
+ sufficient: "sufficient";
2439
+ }>;
2440
+ eventual: z.ZodObject<{
2441
+ executions: z.ZodNumber;
2442
+ passed: z.ZodNumber;
2443
+ failed: z.ZodNumber;
2444
+ rate: z.ZodNullable<z.ZodNumber>;
2445
+ }, z.core.$loose>;
2446
+ interval: z.ZodNullable<z.ZodObject<{
2447
+ method: z.ZodLiteral<"wilson">;
2448
+ level: z.ZodNumber;
2449
+ lower: z.ZodNumber;
2450
+ upper: z.ZodNumber;
2451
+ assumptions: z.ZodString;
2452
+ }, z.core.$loose>>;
2453
+ firstAttempt: z.ZodObject<{
2454
+ executions: z.ZodNumber;
2455
+ passed: z.ZodNumber;
2456
+ failed: z.ZodNumber;
2457
+ rate: z.ZodNullable<z.ZodNumber>;
2458
+ }, z.core.$loose>;
2459
+ unrecordedAttempts: z.ZodNumber;
2460
+ attempts: z.ZodObject<{
2461
+ total: z.ZodNumber;
2462
+ retried: z.ZodNumber;
2463
+ recovered: z.ZodNumber;
2464
+ }, z.core.$loose>;
2465
+ currentRevision: z.ZodNullable<z.ZodString>;
2466
+ historical: z.ZodObject<{
2467
+ executions: z.ZodNumber;
2468
+ passed: z.ZodNumber;
2469
+ failed: z.ZodNumber;
2470
+ revisions: z.ZodArray<z.ZodObject<{
2471
+ specRevision: z.ZodString;
2472
+ executions: z.ZodNumber;
2473
+ }, z.core.$loose>>;
2474
+ }, z.core.$loose>;
2475
+ excluded: z.ZodRecord<z.ZodString, z.ZodNumber>;
2476
+ outsideScope: z.ZodNumber;
2477
+ snapshots: z.ZodNumber;
2478
+ cohorts: z.ZodArray<z.ZodObject<{
2479
+ executions: z.ZodNumber;
2480
+ passed: z.ZodNumber;
2481
+ failed: z.ZodNumber;
2482
+ rate: z.ZodNullable<z.ZodNumber>;
2483
+ environment: z.ZodNullable<z.ZodString>;
2484
+ producer: z.ZodString;
2485
+ parameters: z.ZodRecord<z.ZodString, z.ZodString>;
2486
+ }, z.core.$loose>>;
2487
+ available: z.ZodObject<{
2488
+ environments: z.ZodArray<z.ZodObject<{
2489
+ value: z.ZodString;
2490
+ count: z.ZodNumber;
2491
+ }, z.core.$loose>>;
2492
+ producers: z.ZodArray<z.ZodObject<{
2493
+ value: z.ZodString;
2494
+ count: z.ZodNumber;
2495
+ }, z.core.$loose>>;
2496
+ branches: z.ZodArray<z.ZodObject<{
2497
+ value: z.ZodString;
2498
+ count: z.ZodNumber;
2499
+ }, z.core.$loose>>;
2500
+ specRevisions: z.ZodArray<z.ZodObject<{
2501
+ value: z.ZodString;
2502
+ count: z.ZodNumber;
2503
+ }, z.core.$loose>>;
2504
+ }, z.core.$loose>;
2505
+ notes: z.ZodArray<z.ZodObject<{
2506
+ code: z.ZodString;
2507
+ severity: z.ZodEnum<{
2508
+ info: "info";
2509
+ warning: "warning";
2510
+ }>;
2511
+ message: z.ZodString;
2512
+ }, z.core.$loose>>;
2513
+ executions: z.ZodArray<z.ZodObject<{
2514
+ run: z.ZodString;
2515
+ at: z.ZodNumber;
2516
+ status: z.ZodString;
2517
+ counted: z.ZodBoolean;
2518
+ excluded: z.ZodOptional<z.ZodString>;
2519
+ environment: z.ZodNullable<z.ZodString>;
2520
+ producer: z.ZodString;
2521
+ kind: z.ZodString;
2522
+ parameters: z.ZodRecord<z.ZodString, z.ZodString>;
2523
+ branch: z.ZodNullable<z.ZodString>;
2524
+ sha: z.ZodNullable<z.ZodString>;
2525
+ runUrl: z.ZodNullable<z.ZodString>;
2526
+ layer: z.ZodString;
2527
+ backend: z.ZodString;
2528
+ durationMs: z.ZodNullable<z.ZodNumber>;
2529
+ specRevision: z.ZodOptional<z.ZodString>;
2530
+ snapshots: z.ZodNumber;
2531
+ attempts: z.ZodOptional<z.ZodArray<z.ZodString>>;
2532
+ firstAttempt: z.ZodOptional<z.ZodString>;
2533
+ republished: z.ZodBoolean;
2534
+ }, z.core.$loose>>;
2535
+ }, z.core.$loose>;
2310
2536
  /** A run: the layers that name the same execution (else share a run URL, else a branch and commit), with what they recorded. */
2311
2537
  export declare const ReportRunSchema: z.ZodObject<{
2312
2538
  key: z.ZodString;
@@ -85,6 +85,7 @@ export const EvidenceSummarySchema = z.looseObject({
85
85
  steps: z.number().int().nonnegative(),
86
86
  annotations: z.number().int().nonnegative(),
87
87
  attempts: z.number().int().nonnegative(),
88
+ attemptStatuses: z.array(EvidenceStatusSchema).optional().describe('The status of each recorded try, in the order they were made. Absent when no try was recorded, and in reports written before this was recorded: a first try is then unknown, never assumed to have passed'),
88
89
  links: z.number().int().nonnegative(),
89
90
  tags: z.array(z.string()),
90
91
  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'),
@@ -270,6 +271,7 @@ export const TestResultEntrySchema = z.looseObject({
270
271
  layer: z.string().describe('The id of the layer that recorded it'),
271
272
  backend: z.string(),
272
273
  detail: z.string().describe('Why it has its status, at most 500 characters'),
274
+ specRevision: z.string().optional().describe('The fingerprint of its spec text that the layer recorded, when it recorded one. Two results with different revisions were recorded against different promises. Absent: not recorded, never assumed to match'),
273
275
  evidence: EvidenceSummarySchema.optional(),
274
276
  latency: z.array(MeasurementSchema).optional().describe('Its product-latency measurements (`kind: product-latency`), when it recorded any. Separate from `durationMs`, which is how long the test took')
275
277
  });
@@ -390,6 +392,55 @@ export const TestHistorySchema = z.looseObject({
390
392
  test: TestSummarySchema,
391
393
  history: z.array(TestResultEntrySchema)
392
394
  });
395
+ const ReliabilityRateSchema = z.looseObject({
396
+ executions: z.number().int().nonnegative().describe('Independent executions that passed or failed'),
397
+ passed: z.number().int().nonnegative(),
398
+ failed: z.number().int().nonnegative(),
399
+ rate: z.number().nullable().describe('passed / executions, or null with none')
400
+ });
401
+ /**
402
+ * `GET /api/reports/tests/reliability`: how reliable one test is inside a scope, and exactly how that figure was counted.
403
+ * The snapshots one run recorded for one producer, environment and parameter set are one independent execution; only
404
+ * `passed` and `failed` executions are trials; `notes` say when the sample is too small, pooled, selectively rerun or
405
+ * bounded. It reads results and never changes a status. It is not a production failure probability.
406
+ */
407
+ export const TestReliabilitySchema = z.looseObject({
408
+ schemaVersion: z.literal(1),
409
+ key: z.string().describe('The test\'s key'),
410
+ scope: z.looseObject({
411
+ environment: z.array(z.string()).optional(), producer: z.array(z.string()).optional(), parameter: z.array(z.string()).optional(),
412
+ branch: z.array(z.string()).optional(), sha: z.array(z.string()).optional(), specRevision: z.array(z.string()).optional(),
413
+ revision: z.enum(['current', 'any']).optional(), since: z.number().optional(), until: z.number().optional()
414
+ }).describe('What was asked: the selection the figures are about'),
415
+ minimumExecutions: z.number().int().positive().describe('Completed executions below which the sample is `insufficient`'),
416
+ sample: z.enum(['none', 'insufficient', 'sufficient']).describe('A count only: it says nothing about whether the executions are representative; read `notes`'),
417
+ eventual: ReliabilityRateSchema.describe('Executions that passed in the end, over those that passed or failed'),
418
+ interval: z.looseObject({ method: z.literal('wilson'), level: z.number(), lower: z.number(), upper: z.number(), assumptions: z.string() }).nullable().describe('An interval estimate with the assumptions it needs; null without an execution'),
419
+ firstAttempt: ReliabilityRateSchema.describe('Over the executions that recorded their tries: those whose first try passed'),
420
+ unrecordedAttempts: z.number().int().nonnegative().describe('Counted executions that recorded no tries: their first try is unknown, never assumed to have passed'),
421
+ attempts: z.looseObject({ total: z.number().int().nonnegative(), retried: z.number().int().nonnegative(), recovered: z.number().int().nonnegative() }),
422
+ currentRevision: z.string().nullable().describe('The spec revision `revision=current` counted, or null'),
423
+ historical: z.looseObject({
424
+ executions: z.number().int().nonnegative(), passed: z.number().int().nonnegative(), failed: z.number().int().nonnegative(),
425
+ revisions: z.array(z.looseObject({ specRevision: z.string(), executions: z.number().int().nonnegative() }))
426
+ }).describe('Earlier spec revisions: context only, counted nowhere above'),
427
+ excluded: z.record(z.string(), z.number().int().nonnegative()).describe('Executions in the scope that are not trials, by reason: `not-run`, `unfinished`, `unknown-status`, `historical-revision`, `revision-not-recorded`'),
428
+ outsideScope: z.number().int().nonnegative().describe('Executions the scope did not select'),
429
+ snapshots: z.number().int().nonnegative().describe('Raw recorded snapshots behind the counted executions'),
430
+ cohorts: z.array(ReliabilityRateSchema.extend({ environment: z.string().nullable(), producer: z.string(), parameters: z.record(z.string(), z.string()) })),
431
+ available: z.looseObject({
432
+ environments: z.array(FacetValueSchema), producers: z.array(FacetValueSchema), branches: z.array(FacetValueSchema), specRevisions: z.array(FacetValueSchema)
433
+ }).describe('What a scope could choose among, with independent executions'),
434
+ notes: z.array(z.looseObject({ code: z.string().describe('An open set: treat a code you do not know as informational'), severity: z.enum(['warning', 'info']), message: z.string() })),
435
+ executions: z.array(z.looseObject({
436
+ run: z.string(), at: z.number(), status: z.string(), counted: z.boolean(), excluded: z.string().optional(),
437
+ environment: z.string().nullable(), producer: z.string(), kind: z.string(), parameters: z.record(z.string(), z.string()),
438
+ branch: z.string().nullable(), sha: z.string().nullable(), runUrl: z.string().nullable(), layer: z.string(), backend: z.string(),
439
+ durationMs: z.number().nullable(), specRevision: z.string().optional(), snapshots: z.number().int().positive(),
440
+ attempts: z.array(z.string()).optional().describe('The status of each recorded try, in order'), firstAttempt: z.string().optional(),
441
+ republished: z.boolean().describe('A failed snapshot of this execution was followed by a later one')
442
+ })).describe('Every independent execution of the selection, newest first: the denominator, open to inspection')
443
+ });
393
444
  /** A run: the layers that name the same execution (else share a run URL, else a branch and commit), with what they recorded. */
394
445
  export const ReportRunSchema = z.looseObject({
395
446
  key: z.string().describe('`run:<id>` for an execution a producer named, else the run URL, else `branch@sha`. Pass it to `/api/reports/runs/detail?key=` or `/api/reports/tests?run=`'),
@@ -28,6 +28,7 @@ import { listRunSummaries, readRunFile } from '../../reports/src/run-bundle.js';
28
28
  import { latestSpecSet, readSpecBody } from '../../reports/src/spec-set.js';
29
29
  import { catalogHealth, healthMarkdown, parseRootMappings } from '../../reports/src/catalog-health.js';
30
30
  import { parseArtifactFilter } from '../../reports/src/artifact-query.js';
31
+ import { parseReliabilityScope, reliability, reliabilityMarkdown } from '../../reports/src/reliability.js';
31
32
  import { artifactList, artifactsMarkdown, catalogMarkdown, catalogModel, historyMarkdown, parseTestQuery, runDetail, runList, runMarkdown, runsMarkdown, testCatalog, testHistory } from '../../reports/src/test-catalog.js';
32
33
  import { llmsTxt } from './llms.js';
33
34
  import { openApiDocument } from './openapi.js';
@@ -467,6 +468,20 @@ function createProjectApplication(options) {
467
468
  return problemResponse(404, 'No test in the report has that key.');
468
469
  return answer(c, found.version, () => document, () => historyMarkdown(document), `history|${key}|${wantsMarkdown(c)}`);
469
470
  });
471
+ app.get('/api/reports/tests/reliability', async (c) => {
472
+ if (!index.ready)
473
+ return notReady(c);
474
+ const key = c.req.query('key');
475
+ if (!key)
476
+ return problemResponse(400, 'Name the test with ?key=, a key from GET /api/reports/tests.');
477
+ const found = await catalog();
478
+ const recorded = testHistory(found, key);
479
+ if (!recorded)
480
+ return problemResponse(404, 'No test in the report has that key.');
481
+ const search = new URL(c.req.url).searchParams;
482
+ const document = () => reliability(key, recorded.history, parseReliabilityScope(search));
483
+ return answer(c, found.version, document, () => reliabilityMarkdown(document()), `reliability|${search.toString()}|${wantsMarkdown(c)}`);
484
+ });
470
485
  app.get('/api/reports/runs', async (c) => {
471
486
  if (!index.ready)
472
487
  return notReady(c);
@@ -68,6 +68,7 @@ Every endpoint below answers JSON, or Markdown with \`format=markdown\`. Prefix
68
68
  curl "${url}/api/projects/$ATLAS_PROJECT/reports/tests?format=markdown&limit=50" # summary, environments, domains, failing, flaky, slowest, then the tests
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
+ curl "${url}/api/projects/$ATLAS_PROJECT/reports/tests/reliability?key=<url-encoded test key>&environment=phone&format=markdown" # how reliable one test is in a scope: independent executions, first-try vs eventual success, what was excluded and why
71
72
  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
73
  curl "${url}/api/projects/$ATLAS_PROJECT/reports/runs?format=markdown" # CI runs, newest first, with their results by status and video counts
73
74
  curl "${url}/api/projects/$ATLAS_PROJECT/reports/runs?video=true&format=markdown" # only runs with a playable video recording
@@ -14,7 +14,7 @@
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, ArtifactListSchema, ArtifactTagEditSchema, ArtifactTagEntrySchema, ArtifactTagsSchema } 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, TestReliabilitySchema, RunListSchema, RunDetailSchema, ArtifactListSchema, ArtifactTagEditSchema, ArtifactTagEntrySchema, ArtifactTagsSchema } from '../../schema/src/api.js';
18
18
  import { LiveRunSchema, LiveRunStartSchema, LiveRunUpdateSchema, ServerEditableSettingsSchema, ServerSettingsSnapshotSchema, GcPolicySchema, GcSnapshotSchema, GcRunResultSchema, 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
20
  export const API_VERSION = '1.5.0';
@@ -37,7 +37,7 @@ const COMPONENTS = {
37
37
  StoreListing: [StoreListingSchema, 'output'], StoreWriteResult: [StoreWriteResultSchema, 'output'],
38
38
  Outline: [OutlineSchema, 'output'], EvidencePage: [EvidencePageSchema, 'output'], LayerRows: [LayerRowsSchema, 'output'], CheckDetail: [CheckDetailSchema, 'output'],
39
39
  ArtifactTagEdit: [ArtifactTagEditSchema, 'input'], ArtifactTagEntry: [ArtifactTagEntrySchema, 'output'], ArtifactTags: [ArtifactTagsSchema, 'output'],
40
- TestCatalog: [TestCatalogSchema, 'output'], CatalogHealth: [CatalogHealthSchema, 'output'], TestHistory: [TestHistorySchema, 'output'], RunList: [RunListSchema, 'output'], ArtifactList: [ArtifactListSchema, 'output'], RunDetail: [RunDetailSchema, 'output'],
40
+ TestCatalog: [TestCatalogSchema, 'output'], CatalogHealth: [CatalogHealthSchema, 'output'], TestHistory: [TestHistorySchema, 'output'], TestReliability: [TestReliabilitySchema, 'output'], RunList: [RunListSchema, 'output'], ArtifactList: [ArtifactListSchema, 'output'], RunDetail: [RunDetailSchema, 'output'],
41
41
  SpecIndex: [SpecIndexSchema, 'output'], ReportSources: [ReportSourcesSchema, 'output'], RunsIndex: [RunsIndexSchema, 'output'],
42
42
  RunEvent: [LiveRunSchema, 'output'], HelloEvent: [HelloEventSchema, 'output'], ReportEvent: [ReportEventSchema, 'output'], AtlasChangeEvent: [AtlasChangeSchema, 'output'], PingEvent: [PingEventSchema, 'output']
43
43
  };
@@ -328,6 +328,29 @@ const BASE_OPERATIONS = [
328
328
  ],
329
329
  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 }
330
330
  },
331
+ {
332
+ route: 'GET /api/reports/tests/reliability', path: '/api/reports/tests/reliability', operationId: 'getTestReliability', tag: 'Report', role: 'read',
333
+ summary: 'How reliable one test is in a scope, and how that was counted',
334
+ description: [
335
+ 'A success rate for one test that says what it counted. The snapshots one run recorded for one producer, environment and parameter set (running, finished, enriched) are one independent execution; its last word counts once with the tries it recorded. `eventual` is the share of executions that passed in the end, `firstAttempt` the share whose first try passed (over executions that recorded their tries; the rest are `unrecordedAttempts`, never assumed to have passed first time).',
336
+ '',
337
+ 'Choose the scope with `environment`, `producer`, `parameter` (`key=value`), `branch`, `sha`, `specRevision`, `since` and `until`; executions elsewhere never count. By default only the newest recorded spec revision counts and earlier ones appear under `historical`; send `revision=any` to pool them. Only `passed` and `failed` executions are trials: `not-run` and unfinished ones are listed under `excluded` and never counted as a pass. `sample` is `none`, `insufficient` (fewer than `minimumExecutions`) or `sufficient`, a count only: `notes` say when environments, producers or branches are pooled, failed cases were published again, or history is bounded to the newest 100 results. `executions` is the denominator, open to inspection. The interval states its assumptions. This is not a production failure probability. Send `format=markdown` for Markdown.'
338
+ ].join('\n'),
339
+ parameters: [
340
+ { name: 'key', in: 'query', required: true, description: 'The test\'s `key` from `/api/reports/tests`.', schema: str, example: '["specs/checkout/pay.feature","Pay with a card"]' },
341
+ { name: 'environment', in: 'query', description: 'Environments or devices to count. Comma-separated.', schema: str, example: 'phone' },
342
+ { name: 'producer', in: 'query', description: 'Producers to count. Comma-separated.', schema: str },
343
+ { name: 'parameter', in: 'query', description: 'Parameter choices `key=value`; all must match. Comma-separated.', schema: str, example: 'device=small' },
344
+ { name: 'branch', in: 'query', description: 'Branches to count. Comma-separated.', schema: str },
345
+ { name: 'sha', in: 'query', description: 'Commits: a full id or a prefix of at least seven characters. Comma-separated.', schema: str },
346
+ { name: 'specRevision', in: 'query', description: 'Only executions recorded against these spec fingerprints (overrides `revision`). Comma-separated.', schema: str },
347
+ { name: 'revision', in: 'query', description: '`any` pools every spec revision; otherwise only the newest recorded one counts.', schema: { type: 'string', enum: ['current', 'any'] } },
348
+ { name: 'since', in: 'query', description: 'Only executions at or after this time: epoch milliseconds or a date.', schema: str, example: '2026-01-01' },
349
+ { name: 'until', in: 'query', description: 'Only executions at or before this time: epoch milliseconds or a date.', schema: str },
350
+ { name: 'format', in: 'query', description: '`markdown` for Markdown instead of JSON.', schema: { type: 'string', enum: ['json', 'markdown'] } }
351
+ ],
352
+ responses: { '200': json('The reliability of the test in the scope.', 'TestReliability'), '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 }
353
+ },
331
354
  {
332
355
  route: 'GET /api/reports/artifact-tags', path: '/api/reports/artifact-tags', operationId: 'getArtifactTags', tag: 'Report', role: 'read',
333
356
  summary: 'Reviewer tags of attachments: every recorded edit',
@@ -598,6 +598,54 @@ Scoped to one project. The same project may be selected on the unprefixed API us
598
598
 
599
599
  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`.
600
600
 
601
+ ### How reliable one test is in a scope, and how that was counted
602
+
603
+ `GET /api/reports/tests/reliability` · role: read
604
+
605
+ A success rate for one test that says what it counted. The snapshots one run recorded for one producer, environment and parameter set (running, finished, enriched) are one independent execution; its last word counts once with the tries it recorded. `eventual` is the share of executions that passed in the end, `firstAttempt` the share whose first try passed (over executions that recorded their tries; the rest are `unrecordedAttempts`, never assumed to have passed first time).
606
+
607
+ Choose the scope with `environment`, `producer`, `parameter` (`key=value`), `branch`, `sha`, `specRevision`, `since` and `until`; executions elsewhere never count. By default only the newest recorded spec revision counts and earlier ones appear under `historical`; send `revision=any` to pool them. Only `passed` and `failed` executions are trials: `not-run` and unfinished ones are listed under `excluded` and never counted as a pass. `sample` is `none`, `insufficient` (fewer than `minimumExecutions`) or `sufficient`, a count only: `notes` say when environments, producers or branches are pooled, failed cases were published again, or history is bounded to the newest 100 results. `executions` is the denominator, open to inspection. The interval states its assumptions. This is not a production failure probability. Send `format=markdown` for Markdown.
608
+
609
+ - `key` (query, required): The test's `key` from `/api/reports/tests`.
610
+ - `environment` (query): Environments or devices to count. Comma-separated.
611
+ - `producer` (query): Producers to count. Comma-separated.
612
+ - `parameter` (query): Parameter choices `key=value`; all must match. Comma-separated.
613
+ - `branch` (query): Branches to count. Comma-separated.
614
+ - `sha` (query): Commits: a full id or a prefix of at least seven characters. Comma-separated.
615
+ - `specRevision` (query): Only executions recorded against these spec fingerprints (overrides `revision`). Comma-separated.
616
+ - `revision` (query): `any` pools every spec revision; otherwise only the newest recorded one counts.
617
+ - `since` (query): Only executions at or after this time: epoch milliseconds or a date.
618
+ - `until` (query): Only executions at or before this time: epoch milliseconds or a date.
619
+ - `format` (query): `markdown` for Markdown instead of JSON.
620
+ - `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
621
+
622
+ Responses: `200` The reliability of the test in the scope.; `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`.
623
+
624
+ ### How reliable one test is in a scope, and how that was counted
625
+
626
+ `GET /api/projects/{project}/reports/tests/reliability` · role: read
627
+
628
+ A success rate for one test that says what it counted. The snapshots one run recorded for one producer, environment and parameter set (running, finished, enriched) are one independent execution; its last word counts once with the tries it recorded. `eventual` is the share of executions that passed in the end, `firstAttempt` the share whose first try passed (over executions that recorded their tries; the rest are `unrecordedAttempts`, never assumed to have passed first time).
629
+
630
+ Choose the scope with `environment`, `producer`, `parameter` (`key=value`), `branch`, `sha`, `specRevision`, `since` and `until`; executions elsewhere never count. By default only the newest recorded spec revision counts and earlier ones appear under `historical`; send `revision=any` to pool them. Only `passed` and `failed` executions are trials: `not-run` and unfinished ones are listed under `excluded` and never counted as a pass. `sample` is `none`, `insufficient` (fewer than `minimumExecutions`) or `sufficient`, a count only: `notes` say when environments, producers or branches are pooled, failed cases were published again, or history is bounded to the newest 100 results. `executions` is the denominator, open to inspection. The interval states its assumptions. This is not a production failure probability. Send `format=markdown` for Markdown.
631
+
632
+ 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.
633
+
634
+ - `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.
635
+ - `key` (query, required): The test's `key` from `/api/reports/tests`.
636
+ - `environment` (query): Environments or devices to count. Comma-separated.
637
+ - `producer` (query): Producers to count. Comma-separated.
638
+ - `parameter` (query): Parameter choices `key=value`; all must match. Comma-separated.
639
+ - `branch` (query): Branches to count. Comma-separated.
640
+ - `sha` (query): Commits: a full id or a prefix of at least seven characters. Comma-separated.
641
+ - `specRevision` (query): Only executions recorded against these spec fingerprints (overrides `revision`). Comma-separated.
642
+ - `revision` (query): `any` pools every spec revision; otherwise only the newest recorded one counts.
643
+ - `since` (query): Only executions at or after this time: epoch milliseconds or a date.
644
+ - `until` (query): Only executions at or before this time: epoch milliseconds or a date.
645
+ - `format` (query): `markdown` for Markdown instead of JSON.
646
+
647
+ Responses: `200` The reliability of the test in the scope.; `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`.
648
+
601
649
  ### Reviewer tags of attachments: every recorded edit
602
650
 
603
651
  `GET /api/reports/artifact-tags` · role: read