@crvouga/atlas 1.1.0 → 1.3.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 (54) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/reports/src/artifact-query.d.ts +123 -0
  3. package/dist/reports/src/artifact-query.js +234 -0
  4. package/dist/reports/src/evidence-detail.d.ts +14 -3
  5. package/dist/reports/src/evidence-detail.js +34 -19
  6. package/dist/reports/src/evidence-scope.d.ts +83 -0
  7. package/dist/reports/src/evidence-scope.js +129 -0
  8. package/dist/reports/src/evidence-types.d.ts +5 -1
  9. package/dist/reports/src/explorer-types.d.ts +1 -1
  10. package/dist/reports/src/layer-merge.js +3 -2
  11. package/dist/reports/src/layer-types.d.ts +1 -1
  12. package/dist/reports/src/publish-checks.js +1 -0
  13. package/dist/reports/src/reporter.d.ts +1 -1
  14. package/dist/reports/src/reports-explorer-route.d.ts +276 -0
  15. package/dist/reports/src/reports-route.d.ts +276 -0
  16. package/dist/reports/src/test-catalog.d.ts +108 -6
  17. package/dist/reports/src/test-catalog.js +155 -8
  18. package/dist/schema/src/api.d.ts +745 -0
  19. package/dist/schema/src/api.js +48 -6
  20. package/dist/schema/src/layer.d.ts +981 -0
  21. package/dist/schema/src/layer.js +43 -4
  22. package/dist/server/src/app.js +16 -5
  23. package/dist/server/src/openapi.js +42 -3
  24. package/docs/extending.md +1 -0
  25. package/docs/reference/http-api.md +86 -2
  26. package/docs/reference/reports.md +550 -14
  27. package/docs/reference/schema.md +55 -0
  28. package/docs/rich-evidence.md +78 -0
  29. package/docs/server.md +4 -1
  30. package/docs/ui.md +1 -1
  31. package/llms-full.txt +170 -4
  32. package/package.json +1 -1
  33. package/packages/schema/schemas/check-detail.schema.json +673 -1
  34. package/packages/schema/schemas/effective-delta.schema.json +206 -0
  35. package/packages/schema/schemas/effective-evidence.schema.json +206 -0
  36. package/packages/schema/schemas/evidence-page.schema.json +103 -0
  37. package/packages/schema/schemas/layer-rows.schema.json +103 -0
  38. package/packages/schema/schemas/layer.schema.json +1598 -62
  39. package/packages/schema/schemas/publish-checks.schema.json +768 -0
  40. package/packages/schema/schemas/report-run.schema.json +131 -0
  41. package/packages/schema/schemas/report-runs.schema.json +221 -0
  42. package/packages/schema/schemas/test-catalog.schema.json +135 -0
  43. package/packages/schema/schemas/test-history.schema.json +216 -0
  44. package/packages/server/openapi.json +4731 -735
  45. package/packages/visualizer/src/evidence/artifacts.tsx +24 -18
  46. package/packages/visualizer/src/evidence/evidence-scope.tsx +106 -0
  47. package/packages/visualizer/src/evidence/format.ts +11 -0
  48. package/packages/visualizer/src/explorer/ArtifactsView.tsx +162 -0
  49. package/packages/visualizer/src/explorer/Explorer.tsx +47 -3
  50. package/packages/visualizer/src/explorer/RunsView.tsx +43 -19
  51. package/packages/visualizer/src/explorer/catalog.tsx +97 -12
  52. package/packages/visualizer/src/explorer/filters.tsx +279 -0
  53. package/packages/visualizer/src/explorer/search.ts +85 -6
  54. package/packages/visualizer/src/explorer/views.ts +75 -0
@@ -0,0 +1,129 @@
1
+ /** The fields of a scope that say something about the evidence and that `verified` may name, in the order they are shown. */
2
+ export const SCOPE_DIMENSIONS = ['surface', 'deviceKind', 'syntheticData', 'dependencies', 'audioCaptured', 'pacing'];
3
+ /** The filter token of a recording that declares no scope at all. */
4
+ export const UNKNOWN_SCOPE = 'unknown';
5
+ /**
6
+ * The declaration that applies to something: the nearest one wins as a whole, never field by field, so a recording
7
+ * never shows a limitation or a device that was declared for another source. Pass the candidates nearest first
8
+ * (attachment, attempt, check, part, layer); absent ones are skipped. Undefined when none declares a scope.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * effectiveScope(attachment.scope, attempt.scope, check.scope, layer.scope);
13
+ * ```
14
+ */
15
+ export function effectiveScope(...candidates) {
16
+ return candidates.find((candidate) => candidate !== undefined);
17
+ }
18
+ const word = (value) => value.normalize('NFC').trim().toLowerCase().replace(/\s+/g, '-');
19
+ /** A stable string that is equal for equal declarations (key order never matters), to tell one scope from another. */
20
+ export function scopeKey(scope) {
21
+ if (!scope)
22
+ return UNKNOWN_SCOPE;
23
+ const sort = (value) => Array.isArray(value) ? value.map(sort) : value && typeof value === 'object' ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)).map(([key, item]) => [key, sort(item)])) : value;
24
+ return JSON.stringify(sort(scope));
25
+ }
26
+ /**
27
+ * The filter tokens one declaration states, normalized (letter case and spaces never matter): `surface:<value>`,
28
+ * `device:<value>`, `data:synthetic` or `data:real`, `dependencies:<value>`, `audio:recorded` or `audio:silent`,
29
+ * `pacing:<value>`. A field that is not declared adds no token. No scope at all is the single token
30
+ * {@link UNKNOWN_SCOPE}, so a filter can ask for the unknown on purpose; a scope that declares nothing readable is
31
+ * also unknown.
32
+ */
33
+ export function scopeTokens(scope) {
34
+ const tokens = [];
35
+ if (scope?.surface)
36
+ tokens.push(`surface:${word(scope.surface)}`);
37
+ if (scope?.deviceKind)
38
+ tokens.push(`device:${word(scope.deviceKind)}`);
39
+ if (scope?.syntheticData !== undefined)
40
+ tokens.push(scope.syntheticData ? 'data:synthetic' : 'data:real');
41
+ if (scope?.dependencies)
42
+ tokens.push(`dependencies:${word(scope.dependencies)}`);
43
+ if (scope?.audioCaptured !== undefined)
44
+ tokens.push(scope.audioCaptured ? 'audio:recorded' : 'audio:silent');
45
+ if (scope?.pacing)
46
+ tokens.push(`pacing:${word(scope.pacing)}`);
47
+ return tokens.length ? tokens : [UNKNOWN_SCOPE];
48
+ }
49
+ /**
50
+ * Whether one recording's tokens satisfy a filter: every wanted token must be among them. Tokens compare normalized.
51
+ * An empty filter matches everything. The token {@link UNKNOWN_SCOPE} matches only a recording with no declared
52
+ * fields, so asking for `device:physical` never includes a recording that did not say what it ran on.
53
+ */
54
+ export function matchesScope(tokens, wanted) {
55
+ const have = new Set(tokens.map(word));
56
+ return wanted.every(token => have.has(word(token)));
57
+ }
58
+ const title = (value) => { const text = value.replace(/[-_]+/g, ' ').trim(); return text.charAt(0).toUpperCase() + text.slice(1); };
59
+ const LABELS = { surface: 'Surface', deviceKind: 'Device', syntheticData: 'Data', dependencies: 'Dependencies', audioCaptured: 'Audio', pacing: 'Pacing' };
60
+ function valueOf(scope, dimension) {
61
+ switch (dimension) {
62
+ case 'surface': return scope.surface ? title(scope.surface) : undefined;
63
+ case 'deviceKind': return scope.deviceKind ? title(scope.deviceKind) : undefined;
64
+ case 'syntheticData': return scope.syntheticData === undefined ? undefined : scope.syntheticData ? 'Synthetic' : 'Real';
65
+ case 'dependencies': return scope.dependencies ? title(scope.dependencies) : undefined;
66
+ case 'audioCaptured': return scope.audioCaptured === undefined ? undefined : scope.audioCaptured ? 'Recorded' : 'Silent';
67
+ case 'pacing': return scope.pacing ? title(scope.pacing) : undefined;
68
+ }
69
+ }
70
+ /**
71
+ * Every dimension of a scope with its value and how it is known, in a fixed order, including the ones nobody declared
72
+ * (`Not declared`, basis `undeclared`). A dimension is `verified` only when the scope's `verified` names it and it has
73
+ * a value; everything else a producer wrote is `declared`. With no scope every dimension is undeclared.
74
+ */
75
+ export function scopeFacts(scope) {
76
+ const verified = new Set(scope?.verified ?? []);
77
+ return SCOPE_DIMENSIONS.map(dimension => {
78
+ const value = scope ? valueOf(scope, dimension) : undefined;
79
+ return { dimension, label: LABELS[dimension], value: value ?? 'Not declared', basis: value === undefined ? 'undeclared' : verified.has(dimension) ? 'verified' : 'declared' };
80
+ });
81
+ }
82
+ /**
83
+ * The concise badges for a scope: one per declared dimension, in a fixed order (`Surface: Browser`, `Device: Simulator`, `Synthetic
84
+ * data`, `Scripted dependencies`, `Silent`, `Paced`). With no declared dimension the one badge `Scope unknown`, so
85
+ * a reviewer is told nothing was claimed rather than shown nothing.
86
+ */
87
+ export function scopeBadges(scope) {
88
+ const badges = [];
89
+ for (const fact of scopeFacts(scope)) {
90
+ if (fact.basis === 'undeclared')
91
+ continue;
92
+ const text = fact.dimension === 'surface' || fact.dimension === 'deviceKind' ? `${fact.label}: ${fact.value}`
93
+ : fact.dimension === 'syntheticData' ? `${fact.value} data`
94
+ : fact.dimension === 'dependencies' ? `${fact.value} dependencies`
95
+ : fact.dimension === 'audioCaptured' ? (fact.value === 'Silent' ? 'Silent' : 'Audio recorded')
96
+ : fact.value;
97
+ badges.push({ dimension: fact.dimension, text, basis: fact.basis });
98
+ }
99
+ return badges.length ? badges : [{ dimension: 'unknown', text: 'Scope unknown', basis: 'undeclared' }];
100
+ }
101
+ /**
102
+ * What a reviewer must not read into evidence: a sentence per thing that is not shown. It names the assertions the
103
+ * result stands on when there are some (so activity visible beyond them is not verified), and every kind of reality
104
+ * that was not declared. Empty only for a scope that declares and verifies everything and lists no limitation.
105
+ */
106
+ export function scopeCaveats(scope) {
107
+ const out = [];
108
+ if (!scope || scopeTokens(scope)[0] === UNKNOWN_SCOPE)
109
+ return ['Scope not declared: do not assume this ran live, on a physical device, with real audio or end to end.'];
110
+ if (scope.asserted?.length)
111
+ out.push(`Only these were asserted: ${scope.asserted.join('; ')}. Other activity visible in the recording is not verified.`);
112
+ else
113
+ out.push('No assertion boundary was declared: activity visible in the recording is not verified behavior.');
114
+ if (scope.syntheticData === true)
115
+ out.push('Uses synthetic data: not a statement about production data.');
116
+ if (scope.dependencies === undefined)
117
+ out.push('Whether dependencies were scripted or live is not declared.');
118
+ else if (word(scope.dependencies) !== 'live')
119
+ out.push(`Dependencies were ${word(scope.dependencies)}, not live.`);
120
+ if (scope.audioCaptured === undefined)
121
+ out.push('Whether audio was captured is not declared.');
122
+ else if (!scope.audioCaptured)
123
+ out.push('Silent: nothing audible was recorded.');
124
+ if (scope.deviceKind === undefined)
125
+ out.push('The kind of device is not declared.');
126
+ for (const limit of scope.limitations ?? [])
127
+ out.push(limit);
128
+ return out;
129
+ }
@@ -9,7 +9,7 @@
9
9
  * @see docs/report-contract.md
10
10
  */
11
11
  import type { EvidenceMeasurement, EvidenceStatus } from './explorer-types.js';
12
- import type { LayerRun } from '../../schema/src/layer.js';
12
+ import type { EvidenceScope, LayerRun } from '../../schema/src/layer.js';
13
13
  import type { LayerParameters } from './layer-types.js';
14
14
  /** Where a recorded test's timeline and captured files are read from. */
15
15
  export type RecordingAddress = {
@@ -51,10 +51,14 @@ export type VideoRef = {
51
51
  readonly attempt?: number;
52
52
  /** The name of the step that holds it, when `owner` is `step`. */
53
53
  readonly step?: string;
54
+ /** The normalized tags of this attachment itself (`demo`), so a query can ask for a tagged video rather than a result that has a video and some tagged file. Absent when it has none (and in documents of older servers). */
55
+ readonly tags?: readonly string[];
54
56
  /** What identifies the bytes (digest, bundle key or address); references with the same one are the same recording. Absent when `available` is false. */
55
57
  readonly recording?: string;
56
58
  /** False when the reference names no bytes at all (no bundle key, address or inline text), so it can never be played. */
57
59
  readonly available: boolean;
60
+ /** What this recording shows, as declared for it: its own `scope`, else its attempt's, check's, part's or layer's. Absent when none declared one: its scope is unknown, not live, physical or audible. */
61
+ readonly scope?: EvidenceScope;
58
62
  };
59
63
  /** Video availability as counts: references, distinct playable recordings (deduplicated), and references that cannot be played. */
60
64
  export type VideoCounts = {
@@ -36,7 +36,7 @@ export type EvidenceMeasurement = {
36
36
  readonly limit?: number;
37
37
  };
38
38
  /** What a check may carry beyond its outcome. All optional and open: see docs/extending.md. */
39
- export type EvidenceExtras = Partial<Pick<import('../../schema/src/layer.js').Check, 'tags' | 'labels' | 'links' | 'steps' | 'attachments' | 'annotations' | 'attempts' | 'extensions'>>;
39
+ export type EvidenceExtras = Partial<Pick<import('../../schema/src/layer.js').Check, 'tags' | 'labels' | 'links' | 'steps' | 'attachments' | 'annotations' | 'attempts' | 'extensions' | 'scope'>>;
40
40
  /** One check of a part, as recorded. */
41
41
  export type EvidenceCheck = EvidenceExtras & {
42
42
  /** Stable across runs; the newer result replaces the older one only if both used the same id. */
@@ -1,4 +1,5 @@
1
- import { summarizeEvidence, summarizeLayerEvidence } from './evidence-detail.js';
1
+ import { summarizeEvidenceIn, summarizeLayerEvidence } from './evidence-detail.js';
2
+ import { effectiveScope } from './evidence-scope.js';
2
3
  import { layerPlace } from './layer-axes.js';
3
4
  /**
4
5
  * A short, deterministic fingerprint of a spec's text, shaped `<length>:<hash>:<hash>` (the length in UTF-16 code
@@ -79,7 +80,7 @@ export function layerEvidence(summary, backend) {
79
80
  for (const part of summary.parts) {
80
81
  for (const check of part.checks) {
81
82
  const test = tests.get(check.id);
82
- const evidence = summarizeEvidence(check);
83
+ const evidence = summarizeEvidenceIn(effectiveScope(part.scope, summary.scope), check);
83
84
  const item = expectations?.get(check.id);
84
85
  rows.push({
85
86
  kind: part.kind,
@@ -15,7 +15,7 @@ import type { SourceRecord } from './protocol.js';
15
15
  /** String choices (such as `{ device: 'iphone-15' }`) that make a result its own slot. Compared as sorted key/value pairs. */
16
16
  export type LayerParameters = Readonly<Record<string, string>>;
17
17
  /** What a layer may carry as a whole. All optional and open: see docs/extending.md. */
18
- export type LayerExtras = Partial<Pick<import('../../schema/src/layer.js').LayerDocument, 'tags' | 'labels' | 'links' | 'attachments' | 'annotations' | 'extensions' | 'recordings'>>;
18
+ export type LayerExtras = Partial<Pick<import('../../schema/src/layer.js').LayerDocument, 'tags' | 'labels' | 'links' | 'attachments' | 'annotations' | 'extensions' | 'recordings' | 'scope'>>;
19
19
  /** A layer without its captured files: what composition and the viewer's lists read. */
20
20
  export type LayerSummary = LayerExtras & {
21
21
  /** `modified`: recorded from a checkout with uncommitted changes. `unknown`: origin not captured. Absent for a clean checkout. Advisory; never changes a status. */
@@ -123,6 +123,7 @@ export async function publishChecks(store, input, options = {}) {
123
123
  ...(args.links?.length ? { links: args.links } : {}),
124
124
  ...(layerEvidence.attachments?.length ? { attachments: layerEvidence.attachments } : {}),
125
125
  ...(args.annotations?.length ? { annotations: args.annotations } : {}),
126
+ ...(args.scope ? { scope: args.scope } : {}),
126
127
  ...(args.extensions && Object.keys(args.extensions).length ? { extensions: args.extensions } : {})
127
128
  };
128
129
  const bundle = entries.size ? await writeBundle(store, (async function* () { for (const entry of [...entries.values()].sort((a, b) => (a.key < b.key ? -1 : 1)))
@@ -13,7 +13,7 @@ import { type PublishChecksInput } from '../../schema/src/layer.js';
13
13
  import type { ObjectStore } from './object-store.js';
14
14
  type Check = PublishChecksInput['checks'][number];
15
15
  /** What every layer of one stream shares: who is reporting, from where. Whatever `publishChecks` takes, except the checks and the time. */
16
- export type ReporterIdentity = Pick<PublishChecksInput, 'producer' | 'kind' | 'title' | 'sha' | 'branch' | 'runUrl' | 'run' | 'environment' | 'parameters' | 'freshness' | 'specs' | 'tags' | 'labels' | 'links' | 'attachments' | 'annotations' | 'extensions'>;
16
+ export type ReporterIdentity = Pick<PublishChecksInput, 'producer' | 'kind' | 'title' | 'sha' | 'branch' | 'runUrl' | 'run' | 'environment' | 'parameters' | 'freshness' | 'specs' | 'tags' | 'labels' | 'links' | 'attachments' | 'annotations' | 'scope' | 'extensions'>;
17
17
  /** How a reporter sends: the `store` (a folder, S3, or `httpStore` for a server), how often (`flushMs`, `batchSize`), how hard it tries (`attempts`, `backoffMs`), what it tells (`onError`, `onPublished`), and the clock and sleep for tests. */
18
18
  export type ReporterOptions = {
19
19
  /** Where layers go: a directory, S3, or `httpStore` for an Atlas server. */
@@ -67,6 +67,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
67
67
  } | null;
68
68
  parts: readonly {
69
69
  tags?: string[] | undefined;
70
+ scope?: {
71
+ [x: string]: import("hono/utils/types").JSONValue;
72
+ surface?: string | undefined;
73
+ deviceKind?: string | undefined;
74
+ syntheticData?: boolean | undefined;
75
+ dependencies?: string | undefined;
76
+ audioCaptured?: boolean | undefined;
77
+ pacing?: string | undefined;
78
+ asserted?: string[] | undefined;
79
+ limitations?: string[] | undefined;
80
+ declaredBy?: string | undefined;
81
+ verified?: string[] | undefined;
82
+ verifiedBy?: string | undefined;
83
+ provenance?: {
84
+ [x: string]: import("hono/utils/types").JSONValue;
85
+ url: string;
86
+ name?: string | undefined;
87
+ rel?: string | undefined;
88
+ }[] | undefined;
89
+ extensions?: {
90
+ [x: string]: import("hono/utils/types").JSONValue;
91
+ } | undefined;
92
+ } | undefined;
70
93
  attachments?: {
71
94
  [x: string]: import("hono/utils/types").JSONValue;
72
95
  name: string;
@@ -83,6 +106,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
83
106
  durationMs?: number | undefined;
84
107
  description?: string | undefined;
85
108
  omitted?: string | undefined;
109
+ scope?: {
110
+ [x: string]: import("hono/utils/types").JSONValue;
111
+ surface?: string | undefined;
112
+ deviceKind?: string | undefined;
113
+ syntheticData?: boolean | undefined;
114
+ dependencies?: string | undefined;
115
+ audioCaptured?: boolean | undefined;
116
+ pacing?: string | undefined;
117
+ asserted?: string[] | undefined;
118
+ limitations?: string[] | undefined;
119
+ declaredBy?: string | undefined;
120
+ verified?: string[] | undefined;
121
+ verifiedBy?: string | undefined;
122
+ provenance?: {
123
+ [x: string]: import("hono/utils/types").JSONValue;
124
+ url: string;
125
+ name?: string | undefined;
126
+ rel?: string | undefined;
127
+ }[] | undefined;
128
+ extensions?: {
129
+ [x: string]: import("hono/utils/types").JSONValue;
130
+ } | undefined;
131
+ } | undefined;
86
132
  tags?: string[] | undefined;
87
133
  at?: {
88
134
  [x: string]: import("hono/utils/types").JSONValue;
@@ -102,6 +148,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
102
148
  durationMs?: number | undefined;
103
149
  startedAtMs?: number | undefined;
104
150
  detail?: string | undefined;
151
+ scope?: {
152
+ [x: string]: import("hono/utils/types").JSONValue;
153
+ surface?: string | undefined;
154
+ deviceKind?: string | undefined;
155
+ syntheticData?: boolean | undefined;
156
+ dependencies?: string | undefined;
157
+ audioCaptured?: boolean | undefined;
158
+ pacing?: string | undefined;
159
+ asserted?: string[] | undefined;
160
+ limitations?: string[] | undefined;
161
+ declaredBy?: string | undefined;
162
+ verified?: string[] | undefined;
163
+ verifiedBy?: string | undefined;
164
+ provenance?: {
165
+ [x: string]: import("hono/utils/types").JSONValue;
166
+ url: string;
167
+ name?: string | undefined;
168
+ rel?: string | undefined;
169
+ }[] | undefined;
170
+ extensions?: {
171
+ [x: string]: import("hono/utils/types").JSONValue;
172
+ } | undefined;
173
+ } | undefined;
105
174
  steps?: {
106
175
  [x: string]: import("hono/utils/types").JSONValue;
107
176
  name: string;
@@ -128,6 +197,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
128
197
  durationMs?: number | undefined;
129
198
  description?: string | undefined;
130
199
  omitted?: string | undefined;
200
+ scope?: {
201
+ [x: string]: import("hono/utils/types").JSONValue;
202
+ surface?: string | undefined;
203
+ deviceKind?: string | undefined;
204
+ syntheticData?: boolean | undefined;
205
+ dependencies?: string | undefined;
206
+ audioCaptured?: boolean | undefined;
207
+ pacing?: string | undefined;
208
+ asserted?: string[] | undefined;
209
+ limitations?: string[] | undefined;
210
+ declaredBy?: string | undefined;
211
+ verified?: string[] | undefined;
212
+ verifiedBy?: string | undefined;
213
+ provenance?: {
214
+ [x: string]: import("hono/utils/types").JSONValue;
215
+ url: string;
216
+ name?: string | undefined;
217
+ rel?: string | undefined;
218
+ }[] | undefined;
219
+ extensions?: {
220
+ [x: string]: import("hono/utils/types").JSONValue;
221
+ } | undefined;
222
+ } | undefined;
131
223
  tags?: string[] | undefined;
132
224
  at?: {
133
225
  [x: string]: import("hono/utils/types").JSONValue;
@@ -186,6 +278,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
186
278
  durationMs?: number | undefined;
187
279
  description?: string | undefined;
188
280
  omitted?: string | undefined;
281
+ scope?: {
282
+ [x: string]: import("hono/utils/types").JSONValue;
283
+ surface?: string | undefined;
284
+ deviceKind?: string | undefined;
285
+ syntheticData?: boolean | undefined;
286
+ dependencies?: string | undefined;
287
+ audioCaptured?: boolean | undefined;
288
+ pacing?: string | undefined;
289
+ asserted?: string[] | undefined;
290
+ limitations?: string[] | undefined;
291
+ declaredBy?: string | undefined;
292
+ verified?: string[] | undefined;
293
+ verifiedBy?: string | undefined;
294
+ provenance?: {
295
+ [x: string]: import("hono/utils/types").JSONValue;
296
+ url: string;
297
+ name?: string | undefined;
298
+ rel?: string | undefined;
299
+ }[] | undefined;
300
+ extensions?: {
301
+ [x: string]: import("hono/utils/types").JSONValue;
302
+ } | undefined;
303
+ } | undefined;
189
304
  tags?: string[] | undefined;
190
305
  at?: {
191
306
  [x: string]: import("hono/utils/types").JSONValue;
@@ -254,6 +369,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
254
369
  durationMs?: number | undefined;
255
370
  description?: string | undefined;
256
371
  omitted?: string | undefined;
372
+ scope?: {
373
+ [x: string]: import("hono/utils/types").JSONValue;
374
+ surface?: string | undefined;
375
+ deviceKind?: string | undefined;
376
+ syntheticData?: boolean | undefined;
377
+ dependencies?: string | undefined;
378
+ audioCaptured?: boolean | undefined;
379
+ pacing?: string | undefined;
380
+ asserted?: string[] | undefined;
381
+ limitations?: string[] | undefined;
382
+ declaredBy?: string | undefined;
383
+ verified?: string[] | undefined;
384
+ verifiedBy?: string | undefined;
385
+ provenance?: {
386
+ [x: string]: import("hono/utils/types").JSONValue;
387
+ url: string;
388
+ name?: string | undefined;
389
+ rel?: string | undefined;
390
+ }[] | undefined;
391
+ extensions?: {
392
+ [x: string]: import("hono/utils/types").JSONValue;
393
+ } | undefined;
394
+ } | undefined;
257
395
  tags?: string[] | undefined;
258
396
  at?: {
259
397
  [x: string]: import("hono/utils/types").JSONValue;
@@ -345,6 +483,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
345
483
  readonly recordedAtMs: number;
346
484
  readonly checks: readonly {
347
485
  tags?: string[] | undefined;
486
+ scope?: {
487
+ [x: string]: import("hono/utils/types").JSONValue;
488
+ surface?: string | undefined;
489
+ deviceKind?: string | undefined;
490
+ syntheticData?: boolean | undefined;
491
+ dependencies?: string | undefined;
492
+ audioCaptured?: boolean | undefined;
493
+ pacing?: string | undefined;
494
+ asserted?: string[] | undefined;
495
+ limitations?: string[] | undefined;
496
+ declaredBy?: string | undefined;
497
+ verified?: string[] | undefined;
498
+ verifiedBy?: string | undefined;
499
+ provenance?: {
500
+ [x: string]: import("hono/utils/types").JSONValue;
501
+ url: string;
502
+ name?: string | undefined;
503
+ rel?: string | undefined;
504
+ }[] | undefined;
505
+ extensions?: {
506
+ [x: string]: import("hono/utils/types").JSONValue;
507
+ } | undefined;
508
+ } | undefined;
348
509
  attachments?: {
349
510
  [x: string]: import("hono/utils/types").JSONValue;
350
511
  name: string;
@@ -361,6 +522,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
361
522
  durationMs?: number | undefined;
362
523
  description?: string | undefined;
363
524
  omitted?: string | undefined;
525
+ scope?: {
526
+ [x: string]: import("hono/utils/types").JSONValue;
527
+ surface?: string | undefined;
528
+ deviceKind?: string | undefined;
529
+ syntheticData?: boolean | undefined;
530
+ dependencies?: string | undefined;
531
+ audioCaptured?: boolean | undefined;
532
+ pacing?: string | undefined;
533
+ asserted?: string[] | undefined;
534
+ limitations?: string[] | undefined;
535
+ declaredBy?: string | undefined;
536
+ verified?: string[] | undefined;
537
+ verifiedBy?: string | undefined;
538
+ provenance?: {
539
+ [x: string]: import("hono/utils/types").JSONValue;
540
+ url: string;
541
+ name?: string | undefined;
542
+ rel?: string | undefined;
543
+ }[] | undefined;
544
+ extensions?: {
545
+ [x: string]: import("hono/utils/types").JSONValue;
546
+ } | undefined;
547
+ } | undefined;
364
548
  tags?: string[] | undefined;
365
549
  at?: {
366
550
  [x: string]: import("hono/utils/types").JSONValue;
@@ -380,6 +564,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
380
564
  durationMs?: number | undefined;
381
565
  startedAtMs?: number | undefined;
382
566
  detail?: string | undefined;
567
+ scope?: {
568
+ [x: string]: import("hono/utils/types").JSONValue;
569
+ surface?: string | undefined;
570
+ deviceKind?: string | undefined;
571
+ syntheticData?: boolean | undefined;
572
+ dependencies?: string | undefined;
573
+ audioCaptured?: boolean | undefined;
574
+ pacing?: string | undefined;
575
+ asserted?: string[] | undefined;
576
+ limitations?: string[] | undefined;
577
+ declaredBy?: string | undefined;
578
+ verified?: string[] | undefined;
579
+ verifiedBy?: string | undefined;
580
+ provenance?: {
581
+ [x: string]: import("hono/utils/types").JSONValue;
582
+ url: string;
583
+ name?: string | undefined;
584
+ rel?: string | undefined;
585
+ }[] | undefined;
586
+ extensions?: {
587
+ [x: string]: import("hono/utils/types").JSONValue;
588
+ } | undefined;
589
+ } | undefined;
383
590
  steps?: {
384
591
  [x: string]: import("hono/utils/types").JSONValue;
385
592
  name: string;
@@ -406,6 +613,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
406
613
  durationMs?: number | undefined;
407
614
  description?: string | undefined;
408
615
  omitted?: string | undefined;
616
+ scope?: {
617
+ [x: string]: import("hono/utils/types").JSONValue;
618
+ surface?: string | undefined;
619
+ deviceKind?: string | undefined;
620
+ syntheticData?: boolean | undefined;
621
+ dependencies?: string | undefined;
622
+ audioCaptured?: boolean | undefined;
623
+ pacing?: string | undefined;
624
+ asserted?: string[] | undefined;
625
+ limitations?: string[] | undefined;
626
+ declaredBy?: string | undefined;
627
+ verified?: string[] | undefined;
628
+ verifiedBy?: string | undefined;
629
+ provenance?: {
630
+ [x: string]: import("hono/utils/types").JSONValue;
631
+ url: string;
632
+ name?: string | undefined;
633
+ rel?: string | undefined;
634
+ }[] | undefined;
635
+ extensions?: {
636
+ [x: string]: import("hono/utils/types").JSONValue;
637
+ } | undefined;
638
+ } | undefined;
409
639
  tags?: string[] | undefined;
410
640
  at?: {
411
641
  [x: string]: import("hono/utils/types").JSONValue;
@@ -464,6 +694,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
464
694
  durationMs?: number | undefined;
465
695
  description?: string | undefined;
466
696
  omitted?: string | undefined;
697
+ scope?: {
698
+ [x: string]: import("hono/utils/types").JSONValue;
699
+ surface?: string | undefined;
700
+ deviceKind?: string | undefined;
701
+ syntheticData?: boolean | undefined;
702
+ dependencies?: string | undefined;
703
+ audioCaptured?: boolean | undefined;
704
+ pacing?: string | undefined;
705
+ asserted?: string[] | undefined;
706
+ limitations?: string[] | undefined;
707
+ declaredBy?: string | undefined;
708
+ verified?: string[] | undefined;
709
+ verifiedBy?: string | undefined;
710
+ provenance?: {
711
+ [x: string]: import("hono/utils/types").JSONValue;
712
+ url: string;
713
+ name?: string | undefined;
714
+ rel?: string | undefined;
715
+ }[] | undefined;
716
+ extensions?: {
717
+ [x: string]: import("hono/utils/types").JSONValue;
718
+ } | undefined;
719
+ } | undefined;
467
720
  tags?: string[] | undefined;
468
721
  at?: {
469
722
  [x: string]: import("hono/utils/types").JSONValue;
@@ -532,6 +785,29 @@ export declare function createReportsExplorerApp(deps: ReportsExplorerDeps): imp
532
785
  durationMs?: number | undefined;
533
786
  description?: string | undefined;
534
787
  omitted?: string | undefined;
788
+ scope?: {
789
+ [x: string]: import("hono/utils/types").JSONValue;
790
+ surface?: string | undefined;
791
+ deviceKind?: string | undefined;
792
+ syntheticData?: boolean | undefined;
793
+ dependencies?: string | undefined;
794
+ audioCaptured?: boolean | undefined;
795
+ pacing?: string | undefined;
796
+ asserted?: string[] | undefined;
797
+ limitations?: string[] | undefined;
798
+ declaredBy?: string | undefined;
799
+ verified?: string[] | undefined;
800
+ verifiedBy?: string | undefined;
801
+ provenance?: {
802
+ [x: string]: import("hono/utils/types").JSONValue;
803
+ url: string;
804
+ name?: string | undefined;
805
+ rel?: string | undefined;
806
+ }[] | undefined;
807
+ extensions?: {
808
+ [x: string]: import("hono/utils/types").JSONValue;
809
+ } | undefined;
810
+ } | undefined;
535
811
  tags?: string[] | undefined;
536
812
  at?: {
537
813
  [x: string]: import("hono/utils/types").JSONValue;