@crvouga/atlas 1.16.0 → 1.18.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 (51) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/dist/reports/src/artifact-query.d.ts +14 -2
  3. package/dist/reports/src/artifact-query.js +13 -4
  4. package/dist/reports/src/evidence-detail.js +7 -4
  5. package/dist/reports/src/evidence-types.d.ts +4 -0
  6. package/dist/reports/src/recording-receipt.d.ts +23 -0
  7. package/dist/reports/src/recording-receipt.js +16 -0
  8. package/dist/reports/src/recording-verify.d.ts +74 -0
  9. package/dist/reports/src/recording-verify.js +142 -0
  10. package/dist/reports/src/report-cli.d.ts +2 -2
  11. package/dist/reports/src/report-cli.js +53 -3
  12. package/dist/reports/src/test-catalog.d.ts +20 -1
  13. package/dist/reports/src/test-catalog.js +31 -9
  14. package/dist/schema/src/api.d.ts +113 -0
  15. package/dist/schema/src/api.js +17 -3
  16. package/dist/schema/src/cli-results.d.ts +51 -1
  17. package/dist/schema/src/cli-results.js +25 -1
  18. package/dist/server/src/openapi.js +1 -0
  19. package/docs/agents.md +1 -0
  20. package/docs/extending.md +1 -1
  21. package/docs/reference/cli.md +11 -2
  22. package/docs/reference/http-api.md +2 -0
  23. package/docs/reference/reports.md +202 -9
  24. package/docs/reference/schema.md +19 -1
  25. package/docs/rich-evidence.md +32 -5
  26. package/docs/server.md +1 -1
  27. package/docs/ui.md +8 -0
  28. package/llms-full.txt +56 -9
  29. package/package.json +1 -1
  30. package/packages/schema/schemas/effective-delta.schema.json +8 -0
  31. package/packages/schema/schemas/effective-evidence.schema.json +8 -0
  32. package/packages/schema/schemas/evidence-page.schema.json +4 -0
  33. package/packages/schema/schemas/layer-rows.schema.json +4 -0
  34. package/packages/schema/schemas/report-run.schema.json +74 -0
  35. package/packages/schema/schemas/report-runs.schema.json +86 -0
  36. package/packages/schema/schemas/review-collection.schema.json +4 -0
  37. package/packages/schema/schemas/review-manifest.schema.json +4 -0
  38. package/packages/schema/schemas/test-catalog.schema.json +30 -0
  39. package/packages/schema/schemas/test-history.schema.json +48 -0
  40. package/packages/schema/schemas/verify-recordings-result.schema.json +97 -0
  41. package/packages/server/openapi.json +318 -0
  42. package/packages/visualizer/src/evidence/applicability-view.tsx +46 -20
  43. package/packages/visualizer/src/evidence/applicability.test.ts +68 -1
  44. package/packages/visualizer/src/evidence/applicability.ts +121 -26
  45. package/packages/visualizer/src/explorer/ArtifactsView.tsx +3 -2
  46. package/packages/visualizer/src/explorer/Explorer.tsx +96 -8
  47. package/packages/visualizer/src/explorer/RunsView.tsx +28 -3
  48. package/packages/visualizer/src/explorer/TestDetail.tsx +14 -1
  49. package/packages/visualizer/src/explorer/applicability.ts +43 -0
  50. package/packages/visualizer/src/explorer/filters.tsx +11 -3
  51. package/packages/visualizer/src/explorer/search.ts +20 -6
@@ -1,11 +1,19 @@
1
1
  import { type ArtifactFilter, type FilterFields, type FilterName, type QueryFacets } from './artifact-query.js';
2
2
  import type { EvidenceMeasurement, EvidenceStatus, SpecEntry } from './explorer-types.js';
3
3
  import type { EvidenceSummary, LayerEvidence, LayerHeader, LayerRow, StatusCounts, VideoCounts, VideoRef } from './evidence-types.js';
4
+ import { type RunRecordings } from './recording-receipt.js';
4
5
  import { RecordedSpecs } from './recorded-specs.js';
5
6
  /** The version of the documents this module writes (`TestCatalog`, `TestHistoryDocument`, `RunList`, `RunDetail`). Only optional fields are added within it. */
6
7
  export declare const TEST_CATALOG_VERSION = 1;
7
8
  /** Recorded results kept per test, newest first. Older ones are counted in no statistic. */
8
9
  export declare const HISTORY_PER_TEST = 100;
10
+ /**
11
+ * The open `labels` (see docs/extending.md) that say what a result's evidence ran against and who vouches for it: `build`,
12
+ * `platform`, `applies-to` (builds or commits it is asserted to still hold for), `applies-to-by` (the policy or person that
13
+ * asserts it), `published-at` and `enriched-at` (when the evidence was published or enriched, epoch milliseconds or an ISO
14
+ * date: never when the checks ran). A test's current results carry them as `provenance`.
15
+ */
16
+ export declare const APPLICABILITY_LABELS: readonly ["build", "platform", "applies-to", "applies-to-by", "published-at", "enriched-at"];
9
17
  /** One recorded result of a test: when, where, how it went and how long it took, and the layer and run it came from. */
10
18
  export type TestResultEntry = {
11
19
  /** When it was recorded, epoch ms. */
@@ -32,6 +40,10 @@ export type TestResultEntry = {
32
40
  readonly detail: string;
33
41
  /** The fingerprint of its spec text the layer recorded, when it recorded one: results with different revisions were recorded against different promises. Absent means not recorded, never "the same". */
34
42
  readonly specRevision?: string;
43
+ /** `modified`: recorded from a checkout with uncommitted changes. `unknown`: origin not captured. Absent for a clean checkout, and on results other than a test's current ones. Advisory; never changes a status. */
44
+ readonly freshness?: 'unknown' | 'modified';
45
+ /** The labels of its layer and check that say what it ran against and who vouches for it: only those named in {@link APPLICABILITY_LABELS}. Present on a test's current results when it has any. Read by the viewer's evidence applicability; never decides a result. */
46
+ readonly provenance?: Readonly<Record<string, string>>;
35
47
  /** What it carried beyond its outcome (attachments, steps, retries, tags, labels), when anything. */
36
48
  readonly evidence?: EvidenceSummary;
37
49
  /** Its product-latency measurements (`kind: product-latency`), when it recorded any. Separate from `durationMs`, which is how long the test took (setup, waits and pacing included). */
@@ -82,6 +94,8 @@ export type CatalogSpec = {
82
94
  /** The group it belongs to: the spec's own domain, else the folder under `specs/`. */
83
95
  readonly domain: string;
84
96
  readonly kind: string;
97
+ /** The fingerprint of the spec text now (the promise), when the spec set or a layer recorded one. A result's `specRevision` is compared with it: different means the promise changed after the result was recorded. Absent: unknown, never "the same". */
98
+ readonly fingerprint?: string;
85
99
  };
86
100
  /** One test: what it is, its current status per environment, and how it has gone. */
87
101
  export type TestSummary = {
@@ -274,6 +288,11 @@ export type RunSummary = {
274
288
  * and references that cannot be played. Absent when it has none, and in a report that carries no video information.
275
289
  */
276
290
  readonly videos?: VideoCounts;
291
+ /**
292
+ * Whether the recordings its layers expected were published, apart from its outcomes (see `runRecordings`). Absent when
293
+ * none of its layers declared a recording expectation: its completeness is unknown, never complete. Never changes a status.
294
+ */
295
+ readonly recordings?: RunRecordings;
277
296
  /**
278
297
  * Files attached to the run itself, in its layers (and their parts), not to any one check: review movies, archives,
279
298
  * summaries. All kinds, videos included. Absent when none, and in a report that carries no such information.
@@ -543,7 +562,7 @@ export declare function fieldsOfRun(run: RunSummary): FilterFields;
543
562
  * list.results.map(r => [r.title, r.run, r.sha, r.status]);
544
563
  * ```
545
564
  */
546
- export declare function artifactList(history: TestHistory, version: string, query?: ArtifactFilter, page?: {
565
+ export declare function artifactList(history: TestHistory, version: string, asked?: ArtifactFilter, page?: {
547
566
  readonly limit?: number;
548
567
  readonly offset?: number;
549
568
  readonly domainOf?: (specPath: string) => string | undefined;
@@ -23,7 +23,8 @@
23
23
  * @see docs/server.md#reading-the-report-as-an-agent
24
24
  */
25
25
  import { displayTag, normalizeTag, normalizeTags } from './artifact-tags.js';
26
- import { facetsOf, forRuns, labelList, matchesFilter, outcomeOfCounts, splitList, usedFilters } from './artifact-query.js';
26
+ import { facetsOf, forArtifacts, forRuns, labelList, matchesFilter, outcomeOfCounts, splitList, usedFilters } from './artifact-query.js';
27
+ import { runRecordings } from './recording-receipt.js';
27
28
  import { videoCounts } from './evidence-detail.js';
28
29
  import { productLatency } from './latency.js';
29
30
  import { matchesScope, scopeTokens, UNKNOWN_SCOPE } from './evidence-scope.js';
@@ -34,6 +35,13 @@ import { RecordedSpecs } from './recorded-specs.js';
34
35
  export const TEST_CATALOG_VERSION = 1;
35
36
  /** Recorded results kept per test, newest first. Older ones are counted in no statistic. */
36
37
  export const HISTORY_PER_TEST = 100;
38
+ /**
39
+ * The open `labels` (see docs/extending.md) that say what a result's evidence ran against and who vouches for it: `build`,
40
+ * `platform`, `applies-to` (builds or commits it is asserted to still hold for), `applies-to-by` (the policy or person that
41
+ * asserts it), `published-at` and `enriched-at` (when the evidence was published or enriched, epoch milliseconds or an ISO
42
+ * date: never when the checks ran). A test's current results carry them as `provenance`.
43
+ */
44
+ export const APPLICABILITY_LABELS = ['build', 'platform', 'applies-to', 'applies-to-by', 'published-at', 'enriched-at'];
37
45
  const FAILURE_RANK = { passed: 0, 'not-run': 1, running: 2, failed: 3 };
38
46
  /** How tests are ordered. */
39
47
  export const TEST_SORTS = ['spec', 'title', 'recent', 'slowest', 'failures', 'flaky', 'runs'];
@@ -141,7 +149,7 @@ export class TestHistory {
141
149
  const key = runKey(layer);
142
150
  let run = this.#runs.get(key);
143
151
  if (!run) {
144
- run = { key, runUrl: layer.runUrl, branch: layer.branch, sha: layer.sha, start: layer.recordedAtMs, end: layer.recordedAtMs, firstAt: layer.recordedAtMs, first: layer.id, producers: new Set(), environments: new Set(), layers: 0, runId: layer.run?.id, title: undefined, titledAt: 0, titledBy: '', freshness: undefined, videos: [], attachments: 0, members: [], evidence: { tags: new Set(), artifactTags: new Set(), roles: new Set(), labels: new Set() } };
152
+ run = { key, runUrl: layer.runUrl, branch: layer.branch, sha: layer.sha, start: layer.recordedAtMs, end: layer.recordedAtMs, firstAt: layer.recordedAtMs, first: layer.id, producers: new Set(), environments: new Set(), layers: 0, runId: layer.run?.id, title: undefined, titledAt: 0, titledBy: '', freshness: undefined, videos: [], receipts: [], attachments: 0, members: [], evidence: { tags: new Set(), artifactTags: new Set(), roles: new Set(), labels: new Set() } };
145
153
  this.#runs.set(key, run);
146
154
  }
147
155
  // A run is told by its first layer (by time, then id), whatever order its layers arrive in.
@@ -170,6 +178,7 @@ export class TestHistory {
170
178
  run.layers += 1;
171
179
  if (layer.evidence?.videos)
172
180
  run.videos.push(...layer.evidence.videos);
181
+ run.receipts.push(layer.recordings);
173
182
  const held = layer.evidence?.attachments ?? 0;
174
183
  run.attachments += held;
175
184
  const layerVideos = videoCounts(layer.evidence?.videos);
@@ -369,6 +378,7 @@ function runSummary(run, cells) {
369
378
  }
370
379
  failed.sort((a, b) => compareText(a.i.slot, b.i.slot) || compareText(a.r.o.place, b.r.o.place) || compareText(a.r.k, b.r.k));
371
380
  const video = videoCounts(videos);
381
+ const recordings = runRecordings(run.receipts);
372
382
  const failing = failed.slice(0, 20).map(cell => ({ key: cell.i.slot, title: cell.i.title, environment: cell.r.o.environment, detail: (cell.r.t.split('\n')[0] ?? '').slice(0, 500) }));
373
383
  return {
374
384
  key: run.key, ...(run.runId ? { runId: run.runId } : {}), ...(run.title ? { title: run.title } : {}), ...(run.freshness ? { freshness: run.freshness } : {}),
@@ -377,6 +387,7 @@ function runSummary(run, cells) {
377
387
  producers: [...run.producers].sort(), environments: [...run.environments].sort(),
378
388
  layers: run.layers, tests: tests.size, counts, totalDurationMs: measured ? total : null, failing,
379
389
  ...(video ? { videos: video } : {}),
390
+ ...(recordings ? { recordings } : {}),
380
391
  ...(run.attachments ? { attachments: run.attachments } : {}),
381
392
  ...(carried.artifactTags.size ? { artifactTags: [...carried.artifactTags].sort() } : {}),
382
393
  ...(carried.roles.size ? { types: typesOf(carried.roles, (video?.recordings ?? 0) > 0) } : {}),
@@ -409,6 +420,7 @@ export function catalogSpec(path, known) {
409
420
  title: entry?.title || (base ? base[0].toUpperCase() + base.slice(1) : path),
410
421
  domain: entry?.domain || folder,
411
422
  kind: entry?.kind || (/\.([^./]+)$/.exec(path)?.[1] ?? 'unknown'),
423
+ ...(entry?.fingerprint ? { fingerprint: entry.fingerprint } : {}),
412
424
  };
413
425
  }
414
426
  function statsOf(history, outcomes, tags, labels) {
@@ -464,11 +476,18 @@ function statsOf(history, outcomes, tags, labels) {
464
476
  firstSeenMs, lastSeenMs, lastPassedAtMs, lastFailedAtMs, runs: runs.size, branches,
465
477
  };
466
478
  }
467
- function outcomeOf(result) {
468
- return entryOf(result.layer, result.row, result.layer.backend);
479
+ /** A current result with what the layer recorded about its spec and about what it ran against (see {@link APPLICABILITY_LABELS}). */
480
+ function outcomeOf(result, fingerprints = {}) {
481
+ const labels = { ...(result.layer.evidence?.labels ?? {}), ...(result.row.evidence?.labels ?? {}) };
482
+ const provenance = Object.fromEntries(APPLICABILITY_LABELS.filter(name => typeof labels[name] === 'string').map(name => [name, labels[name]]));
483
+ return {
484
+ ...entryOf(result.layer, result.row, result.layer.backend, fingerprints),
485
+ ...(result.layer.freshness ? { freshness: result.layer.freshness } : {}),
486
+ ...(Object.keys(provenance).length ? { provenance } : {}),
487
+ };
469
488
  }
470
- function summaryOf(slot, history, known) {
471
- const outcomes = slot.outcomes.map(outcomeOf);
489
+ function summaryOf(slot, history, known, fingerprintsOf) {
490
+ const outcomes = slot.outcomes.map(result => outcomeOf(result, fingerprintsOf(result.layer.id)));
472
491
  const tags = new Set();
473
492
  const artifactTags = new Set();
474
493
  const inheritedTags = new Set();
@@ -586,7 +605,8 @@ export function catalogModel(layers, sources = {}) {
586
605
  const known = (path) => recorded.get(path);
587
606
  const combined = combineEvidence({ specs: [], sha: null, changedFiles: [] }, layers);
588
607
  const slots = checkSlots(combined.unassigned);
589
- const tests = slots.map(slot => summaryOf(slot, history, known));
608
+ const fingerprints = new Map(layers.map(entry => [entry.layer.id, entry.fingerprints]));
609
+ const tests = slots.map(slot => summaryOf(slot, history, known, id => fingerprints.get(id)));
590
610
  const byKey = new Map(tests.map(test => [test.key, test]));
591
611
  const haystacks = new Map(tests.map(test => [test.key, haystackOf(test)]));
592
612
  const counts = emptyCounts();
@@ -721,7 +741,7 @@ export function fieldsOfRun(run) {
721
741
  text: [run.title ?? '', run.key, run.branch ?? '', run.sha ?? '', ...run.producers, ...run.environments, ...artifactTags, ...(run.tags ?? []), ...labels].join('\n').toLowerCase(),
722
742
  artifactTags, tags: [...new Set([...(run.tags ?? []), ...artifactTags])], types, media: types.map(type => ({ type, tags: artifactTags })), labels,
723
743
  environments: run.environments, producers: run.producers, branches: run.branch ? [run.branch] : [], shas: run.sha ? [run.sha.toLowerCase()] : [],
724
- outcomes: [outcomeOfCounts(run.counts)], at: run.endedAtMs,
744
+ outcomes: [outcomeOfCounts(run.counts)], recordings: [run.recordings?.state ?? 'unknown'], at: run.endedAtMs,
725
745
  };
726
746
  }
727
747
  /**
@@ -736,7 +756,9 @@ export function fieldsOfRun(run) {
736
756
  * list.results.map(r => [r.title, r.run, r.sha, r.status]);
737
757
  * ```
738
758
  */
739
- export function artifactList(history, version, query = {}, page = {}) {
759
+ export function artifactList(history, version, asked = {}, page = {}) {
760
+ // Recording completeness is a property of a run: a recorded result is not asked about it.
761
+ const query = forArtifacts(asked);
740
762
  const domains = new Map();
741
763
  const domainOf = (path) => {
742
764
  if (path === null)
@@ -90,6 +90,7 @@ export declare const VideoRefSchema: z.ZodObject<{
90
90
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
91
91
  recording: z.ZodOptional<z.ZodString>;
92
92
  available: z.ZodBoolean;
93
+ reason: z.ZodOptional<z.ZodString>;
93
94
  scope: z.ZodOptional<z.ZodObject<{
94
95
  surface: z.ZodOptional<z.ZodString>;
95
96
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -115,6 +116,7 @@ export declare const VideoCountsSchema: z.ZodObject<{
115
116
  references: z.ZodNumber;
116
117
  recordings: z.ZodNumber;
117
118
  unavailable: z.ZodNumber;
119
+ missing: z.ZodOptional<z.ZodNumber>;
118
120
  }, z.core.$loose>;
119
121
  /** What a result carries beyond its outcome, as counts and short facts. Present only when there is something. */
120
122
  export declare const EvidenceSummarySchema: z.ZodObject<{
@@ -141,6 +143,7 @@ export declare const EvidenceSummarySchema: z.ZodObject<{
141
143
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
142
144
  recording: z.ZodOptional<z.ZodString>;
143
145
  available: z.ZodBoolean;
146
+ reason: z.ZodOptional<z.ZodString>;
144
147
  scope: z.ZodOptional<z.ZodObject<{
145
148
  surface: z.ZodOptional<z.ZodString>;
146
149
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -217,6 +220,7 @@ export declare const LayerHeaderSchema: z.ZodObject<{
217
220
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
218
221
  recording: z.ZodOptional<z.ZodString>;
219
222
  available: z.ZodBoolean;
223
+ reason: z.ZodOptional<z.ZodString>;
220
224
  scope: z.ZodOptional<z.ZodObject<{
221
225
  surface: z.ZodOptional<z.ZodString>;
222
226
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -304,6 +308,7 @@ export declare const LayerRowSchema: z.ZodObject<{
304
308
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
305
309
  recording: z.ZodOptional<z.ZodString>;
306
310
  available: z.ZodBoolean;
311
+ reason: z.ZodOptional<z.ZodString>;
307
312
  scope: z.ZodOptional<z.ZodObject<{
308
313
  surface: z.ZodOptional<z.ZodString>;
309
314
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -409,6 +414,7 @@ export declare const LayerRowsSchema: z.ZodObject<{
409
414
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
410
415
  recording: z.ZodOptional<z.ZodString>;
411
416
  available: z.ZodBoolean;
417
+ reason: z.ZodOptional<z.ZodString>;
412
418
  scope: z.ZodOptional<z.ZodObject<{
413
419
  surface: z.ZodOptional<z.ZodString>;
414
420
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -516,6 +522,7 @@ export declare const LayerEvidenceSchema: z.ZodObject<{
516
522
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
517
523
  recording: z.ZodOptional<z.ZodString>;
518
524
  available: z.ZodBoolean;
525
+ reason: z.ZodOptional<z.ZodString>;
519
526
  scope: z.ZodOptional<z.ZodObject<{
520
527
  surface: z.ZodOptional<z.ZodString>;
521
528
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -617,6 +624,7 @@ export declare const LayerEvidenceSchema: z.ZodObject<{
617
624
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
618
625
  recording: z.ZodOptional<z.ZodString>;
619
626
  available: z.ZodBoolean;
627
+ reason: z.ZodOptional<z.ZodString>;
620
628
  scope: z.ZodOptional<z.ZodObject<{
621
629
  surface: z.ZodOptional<z.ZodString>;
622
630
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -713,6 +721,7 @@ export declare const EffectiveEvidenceSchema: z.ZodObject<{
713
721
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
714
722
  recording: z.ZodOptional<z.ZodString>;
715
723
  available: z.ZodBoolean;
724
+ reason: z.ZodOptional<z.ZodString>;
716
725
  scope: z.ZodOptional<z.ZodObject<{
717
726
  surface: z.ZodOptional<z.ZodString>;
718
727
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -814,6 +823,7 @@ export declare const EffectiveEvidenceSchema: z.ZodObject<{
814
823
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
815
824
  recording: z.ZodOptional<z.ZodString>;
816
825
  available: z.ZodBoolean;
826
+ reason: z.ZodOptional<z.ZodString>;
817
827
  scope: z.ZodOptional<z.ZodObject<{
818
828
  surface: z.ZodOptional<z.ZodString>;
819
829
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -931,6 +941,7 @@ export declare const EffectiveDeltaSchema: z.ZodObject<{
931
941
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
932
942
  recording: z.ZodOptional<z.ZodString>;
933
943
  available: z.ZodBoolean;
944
+ reason: z.ZodOptional<z.ZodString>;
934
945
  scope: z.ZodOptional<z.ZodObject<{
935
946
  surface: z.ZodOptional<z.ZodString>;
936
947
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -1032,6 +1043,7 @@ export declare const EffectiveDeltaSchema: z.ZodObject<{
1032
1043
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1033
1044
  recording: z.ZodOptional<z.ZodString>;
1034
1045
  available: z.ZodBoolean;
1046
+ reason: z.ZodOptional<z.ZodString>;
1035
1047
  scope: z.ZodOptional<z.ZodObject<{
1036
1048
  surface: z.ZodOptional<z.ZodString>;
1037
1049
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -1141,6 +1153,7 @@ export declare const EvidencePageSchema: z.ZodObject<{
1141
1153
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1142
1154
  recording: z.ZodOptional<z.ZodString>;
1143
1155
  available: z.ZodBoolean;
1156
+ reason: z.ZodOptional<z.ZodString>;
1144
1157
  scope: z.ZodOptional<z.ZodObject<{
1145
1158
  surface: z.ZodOptional<z.ZodString>;
1146
1159
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -1721,6 +1734,7 @@ export declare const ReviewSnapshotSchema: z.ZodObject<{
1721
1734
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1722
1735
  recording: z.ZodOptional<z.ZodString>;
1723
1736
  available: z.ZodBoolean;
1737
+ reason: z.ZodOptional<z.ZodString>;
1724
1738
  scope: z.ZodOptional<z.ZodObject<{
1725
1739
  surface: z.ZodOptional<z.ZodString>;
1726
1740
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -1784,6 +1798,7 @@ export declare const ReviewEntrySchema: z.ZodObject<{
1784
1798
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1785
1799
  recording: z.ZodOptional<z.ZodString>;
1786
1800
  available: z.ZodBoolean;
1801
+ reason: z.ZodOptional<z.ZodString>;
1787
1802
  scope: z.ZodOptional<z.ZodObject<{
1788
1803
  surface: z.ZodOptional<z.ZodString>;
1789
1804
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -1866,6 +1881,7 @@ export declare const ReviewCollectionSchema: z.ZodObject<{
1866
1881
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
1867
1882
  recording: z.ZodOptional<z.ZodString>;
1868
1883
  available: z.ZodBoolean;
1884
+ reason: z.ZodOptional<z.ZodString>;
1869
1885
  scope: z.ZodOptional<z.ZodObject<{
1870
1886
  surface: z.ZodOptional<z.ZodString>;
1871
1887
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -2025,6 +2041,7 @@ export declare const ReviewCollectionDetailSchema: z.ZodObject<{
2025
2041
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
2026
2042
  recording: z.ZodOptional<z.ZodString>;
2027
2043
  available: z.ZodBoolean;
2044
+ reason: z.ZodOptional<z.ZodString>;
2028
2045
  scope: z.ZodOptional<z.ZodObject<{
2029
2046
  surface: z.ZodOptional<z.ZodString>;
2030
2047
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -2156,6 +2173,7 @@ export declare const ReviewManifestSchema: z.ZodObject<{
2156
2173
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
2157
2174
  recording: z.ZodOptional<z.ZodString>;
2158
2175
  available: z.ZodBoolean;
2176
+ reason: z.ZodOptional<z.ZodString>;
2159
2177
  scope: z.ZodOptional<z.ZodObject<{
2160
2178
  surface: z.ZodOptional<z.ZodString>;
2161
2179
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -2255,6 +2273,11 @@ export declare const TestResultEntrySchema: z.ZodObject<{
2255
2273
  backend: z.ZodString;
2256
2274
  detail: z.ZodString;
2257
2275
  specRevision: z.ZodOptional<z.ZodString>;
2276
+ freshness: z.ZodOptional<z.ZodEnum<{
2277
+ unknown: "unknown";
2278
+ modified: "modified";
2279
+ }>>;
2280
+ provenance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
2258
2281
  evidence: z.ZodOptional<z.ZodObject<{
2259
2282
  attachments: z.ZodNumber;
2260
2283
  steps: z.ZodNumber;
@@ -2279,6 +2302,7 @@ export declare const TestResultEntrySchema: z.ZodObject<{
2279
2302
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
2280
2303
  recording: z.ZodOptional<z.ZodString>;
2281
2304
  available: z.ZodBoolean;
2305
+ reason: z.ZodOptional<z.ZodString>;
2282
2306
  scope: z.ZodOptional<z.ZodObject<{
2283
2307
  surface: z.ZodOptional<z.ZodString>;
2284
2308
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -2329,6 +2353,7 @@ export declare const TestSummarySchema: z.ZodObject<{
2329
2353
  title: z.ZodString;
2330
2354
  domain: z.ZodString;
2331
2355
  kind: z.ZodString;
2356
+ fingerprint: z.ZodOptional<z.ZodString>;
2332
2357
  }, z.core.$loose>>;
2333
2358
  status: z.ZodEnum<{
2334
2359
  passed: "passed";
@@ -2359,6 +2384,11 @@ export declare const TestSummarySchema: z.ZodObject<{
2359
2384
  backend: z.ZodString;
2360
2385
  detail: z.ZodString;
2361
2386
  specRevision: z.ZodOptional<z.ZodString>;
2387
+ freshness: z.ZodOptional<z.ZodEnum<{
2388
+ unknown: "unknown";
2389
+ modified: "modified";
2390
+ }>>;
2391
+ provenance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
2362
2392
  evidence: z.ZodOptional<z.ZodObject<{
2363
2393
  attachments: z.ZodNumber;
2364
2394
  steps: z.ZodNumber;
@@ -2383,6 +2413,7 @@ export declare const TestSummarySchema: z.ZodObject<{
2383
2413
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
2384
2414
  recording: z.ZodOptional<z.ZodString>;
2385
2415
  available: z.ZodBoolean;
2416
+ reason: z.ZodOptional<z.ZodString>;
2386
2417
  scope: z.ZodOptional<z.ZodObject<{
2387
2418
  surface: z.ZodOptional<z.ZodString>;
2388
2419
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -2586,6 +2617,7 @@ export declare const TestCatalogSchema: z.ZodObject<{
2586
2617
  title: z.ZodString;
2587
2618
  domain: z.ZodString;
2588
2619
  kind: z.ZodString;
2620
+ fingerprint: z.ZodOptional<z.ZodString>;
2589
2621
  tests: z.ZodNumber;
2590
2622
  counts: z.ZodRecord<z.ZodEnum<{
2591
2623
  passed: "passed";
@@ -2628,6 +2660,7 @@ export declare const TestCatalogSchema: z.ZodObject<{
2628
2660
  title: z.ZodString;
2629
2661
  domain: z.ZodString;
2630
2662
  kind: z.ZodString;
2663
+ fingerprint: z.ZodOptional<z.ZodString>;
2631
2664
  }, z.core.$loose>>;
2632
2665
  status: z.ZodEnum<{
2633
2666
  passed: "passed";
@@ -2658,6 +2691,11 @@ export declare const TestCatalogSchema: z.ZodObject<{
2658
2691
  backend: z.ZodString;
2659
2692
  detail: z.ZodString;
2660
2693
  specRevision: z.ZodOptional<z.ZodString>;
2694
+ freshness: z.ZodOptional<z.ZodEnum<{
2695
+ unknown: "unknown";
2696
+ modified: "modified";
2697
+ }>>;
2698
+ provenance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
2661
2699
  evidence: z.ZodOptional<z.ZodObject<{
2662
2700
  attachments: z.ZodNumber;
2663
2701
  steps: z.ZodNumber;
@@ -2682,6 +2720,7 @@ export declare const TestCatalogSchema: z.ZodObject<{
2682
2720
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
2683
2721
  recording: z.ZodOptional<z.ZodString>;
2684
2722
  available: z.ZodBoolean;
2723
+ reason: z.ZodOptional<z.ZodString>;
2685
2724
  scope: z.ZodOptional<z.ZodObject<{
2686
2725
  surface: z.ZodOptional<z.ZodString>;
2687
2726
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -2860,6 +2899,7 @@ export declare const TestHistorySchema: z.ZodObject<{
2860
2899
  title: z.ZodString;
2861
2900
  domain: z.ZodString;
2862
2901
  kind: z.ZodString;
2902
+ fingerprint: z.ZodOptional<z.ZodString>;
2863
2903
  }, z.core.$loose>>;
2864
2904
  status: z.ZodEnum<{
2865
2905
  passed: "passed";
@@ -2890,6 +2930,11 @@ export declare const TestHistorySchema: z.ZodObject<{
2890
2930
  backend: z.ZodString;
2891
2931
  detail: z.ZodString;
2892
2932
  specRevision: z.ZodOptional<z.ZodString>;
2933
+ freshness: z.ZodOptional<z.ZodEnum<{
2934
+ unknown: "unknown";
2935
+ modified: "modified";
2936
+ }>>;
2937
+ provenance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
2893
2938
  evidence: z.ZodOptional<z.ZodObject<{
2894
2939
  attachments: z.ZodNumber;
2895
2940
  steps: z.ZodNumber;
@@ -2914,6 +2959,7 @@ export declare const TestHistorySchema: z.ZodObject<{
2914
2959
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
2915
2960
  recording: z.ZodOptional<z.ZodString>;
2916
2961
  available: z.ZodBoolean;
2962
+ reason: z.ZodOptional<z.ZodString>;
2917
2963
  scope: z.ZodOptional<z.ZodObject<{
2918
2964
  surface: z.ZodOptional<z.ZodString>;
2919
2965
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -3013,6 +3059,11 @@ export declare const TestHistorySchema: z.ZodObject<{
3013
3059
  backend: z.ZodString;
3014
3060
  detail: z.ZodString;
3015
3061
  specRevision: z.ZodOptional<z.ZodString>;
3062
+ freshness: z.ZodOptional<z.ZodEnum<{
3063
+ unknown: "unknown";
3064
+ modified: "modified";
3065
+ }>>;
3066
+ provenance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
3016
3067
  evidence: z.ZodOptional<z.ZodObject<{
3017
3068
  attachments: z.ZodNumber;
3018
3069
  steps: z.ZodNumber;
@@ -3037,6 +3088,7 @@ export declare const TestHistorySchema: z.ZodObject<{
3037
3088
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
3038
3089
  recording: z.ZodOptional<z.ZodString>;
3039
3090
  available: z.ZodBoolean;
3091
+ reason: z.ZodOptional<z.ZodString>;
3040
3092
  scope: z.ZodOptional<z.ZodObject<{
3041
3093
  surface: z.ZodOptional<z.ZodString>;
3042
3094
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -3238,6 +3290,21 @@ export declare const ReportRunSchema: z.ZodObject<{
3238
3290
  references: z.ZodNumber;
3239
3291
  recordings: z.ZodNumber;
3240
3292
  unavailable: z.ZodNumber;
3293
+ missing: z.ZodOptional<z.ZodNumber>;
3294
+ }, z.core.$loose>>;
3295
+ recordings: z.ZodOptional<z.ZodObject<{
3296
+ state: z.ZodEnum<{
3297
+ unknown: "unknown";
3298
+ complete: "complete";
3299
+ incomplete: "incomplete";
3300
+ "none-expected": "none-expected";
3301
+ }>;
3302
+ expected: z.ZodNumber;
3303
+ published: z.ZodNumber;
3304
+ skipped: z.ZodNumber;
3305
+ unresolved: z.ZodNumber;
3306
+ notApplicable: z.ZodNumber;
3307
+ layersWithoutReceipt: z.ZodNumber;
3241
3308
  }, z.core.$loose>>;
3242
3309
  attachments: z.ZodOptional<z.ZodNumber>;
3243
3310
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -3284,6 +3351,10 @@ export declare const QueryFacetsSchema: z.ZodObject<{
3284
3351
  value: z.ZodString;
3285
3352
  count: z.ZodNumber;
3286
3353
  }, z.core.$loose>>>;
3354
+ recordings: z.ZodOptional<z.ZodArray<z.ZodObject<{
3355
+ value: z.ZodString;
3356
+ count: z.ZodNumber;
3357
+ }, z.core.$loose>>>;
3287
3358
  }, z.core.$loose>;
3288
3359
  /** `GET /api/reports/runs`: runs, newest first. */
3289
3360
  export declare const RunListSchema: z.ZodObject<{
@@ -3325,6 +3396,21 @@ export declare const RunListSchema: z.ZodObject<{
3325
3396
  references: z.ZodNumber;
3326
3397
  recordings: z.ZodNumber;
3327
3398
  unavailable: z.ZodNumber;
3399
+ missing: z.ZodOptional<z.ZodNumber>;
3400
+ }, z.core.$loose>>;
3401
+ recordings: z.ZodOptional<z.ZodObject<{
3402
+ state: z.ZodEnum<{
3403
+ unknown: "unknown";
3404
+ complete: "complete";
3405
+ incomplete: "incomplete";
3406
+ "none-expected": "none-expected";
3407
+ }>;
3408
+ expected: z.ZodNumber;
3409
+ published: z.ZodNumber;
3410
+ skipped: z.ZodNumber;
3411
+ unresolved: z.ZodNumber;
3412
+ notApplicable: z.ZodNumber;
3413
+ layersWithoutReceipt: z.ZodNumber;
3328
3414
  }, z.core.$loose>>;
3329
3415
  attachments: z.ZodOptional<z.ZodNumber>;
3330
3416
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -3367,6 +3453,10 @@ export declare const RunListSchema: z.ZodObject<{
3367
3453
  value: z.ZodString;
3368
3454
  count: z.ZodNumber;
3369
3455
  }, z.core.$loose>>>;
3456
+ recordings: z.ZodOptional<z.ZodArray<z.ZodObject<{
3457
+ value: z.ZodString;
3458
+ count: z.ZodNumber;
3459
+ }, z.core.$loose>>>;
3370
3460
  }, z.core.$loose>>;
3371
3461
  }, z.core.$loose>;
3372
3462
  /** One recorded result that carries evidence: where and when it was recorded and what it carries. */
@@ -3406,6 +3496,7 @@ export declare const ArtifactResultSchema: z.ZodObject<{
3406
3496
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
3407
3497
  recording: z.ZodOptional<z.ZodString>;
3408
3498
  available: z.ZodBoolean;
3499
+ reason: z.ZodOptional<z.ZodString>;
3409
3500
  scope: z.ZodOptional<z.ZodObject<{
3410
3501
  surface: z.ZodOptional<z.ZodString>;
3411
3502
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -3476,6 +3567,7 @@ export declare const ArtifactListSchema: z.ZodObject<{
3476
3567
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
3477
3568
  recording: z.ZodOptional<z.ZodString>;
3478
3569
  available: z.ZodBoolean;
3570
+ reason: z.ZodOptional<z.ZodString>;
3479
3571
  scope: z.ZodOptional<z.ZodObject<{
3480
3572
  surface: z.ZodOptional<z.ZodString>;
3481
3573
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -3537,6 +3629,10 @@ export declare const ArtifactListSchema: z.ZodObject<{
3537
3629
  value: z.ZodString;
3538
3630
  count: z.ZodNumber;
3539
3631
  }, z.core.$loose>>>;
3632
+ recordings: z.ZodOptional<z.ZodArray<z.ZodObject<{
3633
+ value: z.ZodString;
3634
+ count: z.ZodNumber;
3635
+ }, z.core.$loose>>>;
3540
3636
  }, z.core.$loose>;
3541
3637
  }, z.core.$loose>;
3542
3638
  /** `GET /api/reports/runs/detail`: one run and its last result for every test, environment and producer it recorded. */
@@ -3578,6 +3674,21 @@ export declare const RunDetailSchema: z.ZodObject<{
3578
3674
  references: z.ZodNumber;
3579
3675
  recordings: z.ZodNumber;
3580
3676
  unavailable: z.ZodNumber;
3677
+ missing: z.ZodOptional<z.ZodNumber>;
3678
+ }, z.core.$loose>>;
3679
+ recordings: z.ZodOptional<z.ZodObject<{
3680
+ state: z.ZodEnum<{
3681
+ unknown: "unknown";
3682
+ complete: "complete";
3683
+ incomplete: "incomplete";
3684
+ "none-expected": "none-expected";
3685
+ }>;
3686
+ expected: z.ZodNumber;
3687
+ published: z.ZodNumber;
3688
+ skipped: z.ZodNumber;
3689
+ unresolved: z.ZodNumber;
3690
+ notApplicable: z.ZodNumber;
3691
+ layersWithoutReceipt: z.ZodNumber;
3581
3692
  }, z.core.$loose>>;
3582
3693
  attachments: z.ZodOptional<z.ZodNumber>;
3583
3694
  artifactTags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -3610,6 +3721,7 @@ export declare const RunDetailSchema: z.ZodObject<{
3610
3721
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
3611
3722
  recording: z.ZodOptional<z.ZodString>;
3612
3723
  available: z.ZodBoolean;
3724
+ reason: z.ZodOptional<z.ZodString>;
3613
3725
  scope: z.ZodOptional<z.ZodObject<{
3614
3726
  surface: z.ZodOptional<z.ZodString>;
3615
3727
  deviceKind: z.ZodOptional<z.ZodString>;
@@ -3647,6 +3759,7 @@ export declare const RunDetailSchema: z.ZodObject<{
3647
3759
  references: z.ZodNumber;
3648
3760
  recordings: z.ZodNumber;
3649
3761
  unavailable: z.ZodNumber;
3762
+ missing: z.ZodOptional<z.ZodNumber>;
3650
3763
  }, z.core.$loose>>;
3651
3764
  }, z.core.$loose>>>;
3652
3765
  }, z.core.$loose>;
@@ -71,13 +71,15 @@ export const VideoRefSchema = z.looseObject({
71
71
  tags: z.array(z.string()).optional().describe('The normalized tags of this attachment itself. Absent when it has none (and in documents of older servers)'),
72
72
  recording: z.string().optional().describe('What identifies the bytes; references with the same value are one recording. Absent when it cannot be played'),
73
73
  available: z.boolean().describe('False when the reference names no bytes: no bundle key, address or inline text, or a bundle key that the layer\'s own bundle does not hold'),
74
+ reason: z.string().optional().describe('Why it is unavailable, when the report can tell: `not-in-bundle` is a file the layer names but does not hold (stored, then gone). An open vocabulary: treat an unknown reason as plain unavailable'),
74
75
  scope: EvidenceScopeSchema.optional().describe('What this recording shows as its producer declared it: its own `scope`, else its attempt\'s, check\'s, part\'s or layer\'s. Absent: the scope is unknown, never live, physical or audible')
75
76
  });
76
77
  /** Video references as counts: how many are referenced, how many distinct recordings can be played, how many cannot. */
77
78
  export const VideoCountsSchema = z.looseObject({
78
79
  references: z.number().int().nonnegative().describe('Every reference to a video; identical bytes referenced twice count twice'),
79
80
  recordings: z.number().int().nonnegative().describe('Distinct playable recordings'),
80
- unavailable: z.number().int().nonnegative().describe('References that name no bytes, or bytes the layer does not hold')
81
+ unavailable: z.number().int().nonnegative().describe('References that name no bytes, or bytes the layer does not hold'),
82
+ missing: z.number().int().positive().optional().describe('Of `unavailable`, those that name a file the layer\'s bundle does not hold (stored, then gone). Absent when there are none')
81
83
  });
82
84
  /** What a result carries beyond its outcome, as counts and short facts. Present only when there is something. */
83
85
  export const EvidenceSummarySchema = z.looseObject({
@@ -432,13 +434,18 @@ export const TestResultEntrySchema = z.looseObject({
432
434
  backend: z.string(),
433
435
  detail: z.string().describe('Why it has its status, at most 500 characters'),
434
436
  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'),
437
+ freshness: z.enum(['modified', 'unknown']).optional().describe('`modified`: recorded from a checkout with uncommitted changes. `unknown`: origin not captured. Absent for a clean checkout, on results other than a test\'s current ones, and from an older server. Advisory; never changes a status'),
438
+ provenance: z.record(z.string(), z.string()).optional().describe('The labels of its layer and check that say what it ran against and who vouches for it: `build`, `platform`, `applies-to`, `applies-to-by`, `published-at`, `enriched-at`. Present on a test\'s current results when it has any, and absent from an older server. Never decides a result'),
435
439
  evidence: EvidenceSummarySchema.optional(),
436
440
  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')
437
441
  });
438
442
  const DurationStatsSchema = z.looseObject({
439
443
  count: z.number().int().nonnegative(), totalMs: z.number(), avgMs: z.number(), p50Ms: z.number(), p95Ms: z.number(), minMs: z.number(), maxMs: z.number(), lastMs: z.number()
440
444
  });
441
- const CatalogSpecSchema = z.looseObject({ path: z.string(), title: z.string(), domain: z.string(), kind: z.string() });
445
+ const CatalogSpecSchema = z.looseObject({
446
+ path: z.string(), title: z.string(), domain: z.string(), kind: z.string(),
447
+ fingerprint: z.string().optional().describe('The fingerprint of the spec text now (the promise). Compare it with a result\'s `specRevision`: different means the promise changed after the result was recorded. Absent: unknown, never "the same". Absent from an older server')
448
+ });
442
449
  /** One test of the catalog: what it is, its current result per environment, and how it has gone over its recorded history. */
443
450
  export const TestSummarySchema = z.looseObject({
444
451
  key: z.string().describe('The test\'s identity, `JSON.stringify([specPath, id])`. Pass it to `/api/reports/tests/history?key=`'),
@@ -620,6 +627,12 @@ export const ReportRunSchema = z.looseObject({
620
627
  totalDurationMs: z.number().nullable(),
621
628
  failing: z.array(z.looseObject({ key: z.string(), title: z.string(), environment: z.string().nullable(), detail: z.string() })),
622
629
  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'),
630
+ recordings: z.looseObject({
631
+ state: z.enum(['complete', 'incomplete', 'none-expected', 'unknown']).describe('`incomplete` when an expected recording was skipped or unresolved; else `unknown` when a layer declared no expectations; else `complete` when something was expected and `none-expected` when nothing was'),
632
+ expected: z.number().int().nonnegative(), published: z.number().int().nonnegative().describe('Of the expected recordings, uploaded or referenced by address'),
633
+ skipped: z.number().int().nonnegative(), unresolved: z.number().int().nonnegative(), notApplicable: z.number().int().nonnegative(),
634
+ layersWithoutReceipt: z.number().int().nonnegative().describe('Layers of the run that declared no recording expectations: nothing is claimed for them')
635
+ }).optional().describe('Whether the recordings its layers expected were published, apart from its outcomes. Absent when no layer declared an expectation: its completeness is unknown, never complete. Matched by the `recordings` filter'),
623
636
  attachments: z.number().int().positive().optional().describe('Files attached to the run itself, in its layers, not to a check. Absent when there are none'),
624
637
  artifactTags: z.array(z.string()).optional().describe('The normalized tags of the attachments of its results, sorted and distinct. Absent when none is tagged, and in reports of older servers'),
625
638
  tags: z.array(z.string()).optional().describe('The tags of its checks and layers, which its attachments inherit (what `inherited=1` also matches). Absent when none'),
@@ -632,7 +645,8 @@ export const QueryFacetValueSchema = z.looseObject({ value: z.string(), count: z
632
645
  export const QueryFacetsSchema = z.looseObject({
633
646
  artifactTags: z.array(QueryFacetValueSchema), types: z.array(QueryFacetValueSchema), labels: z.array(QueryFacetValueSchema).describe('Values are `key=value`'),
634
647
  environments: z.array(QueryFacetValueSchema), producers: z.array(QueryFacetValueSchema), branches: z.array(QueryFacetValueSchema), outcomes: z.array(QueryFacetValueSchema),
635
- domains: z.array(QueryFacetValueSchema).optional().describe('Only for artifacts: runs have no domain')
648
+ domains: z.array(QueryFacetValueSchema).optional().describe('Only for artifacts: runs have no domain'),
649
+ recordings: z.array(QueryFacetValueSchema).optional().describe('Only for runs: `complete`, `incomplete`, `none-expected` and `unknown` with how many runs have each. Absent on servers that predate the filter')
636
650
  });
637
651
  /** `GET /api/reports/runs`: runs, newest first. */
638
652
  export const RunListSchema = z.looseObject({