@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.
- package/CHANGELOG.md +13 -1
- package/dist/reports/src/artifact-query.d.ts +14 -2
- package/dist/reports/src/artifact-query.js +13 -4
- package/dist/reports/src/evidence-detail.js +7 -4
- package/dist/reports/src/evidence-types.d.ts +4 -0
- package/dist/reports/src/recording-receipt.d.ts +23 -0
- package/dist/reports/src/recording-receipt.js +16 -0
- package/dist/reports/src/recording-verify.d.ts +74 -0
- package/dist/reports/src/recording-verify.js +142 -0
- package/dist/reports/src/report-cli.d.ts +2 -2
- package/dist/reports/src/report-cli.js +53 -3
- package/dist/reports/src/test-catalog.d.ts +20 -1
- package/dist/reports/src/test-catalog.js +31 -9
- package/dist/schema/src/api.d.ts +113 -0
- package/dist/schema/src/api.js +17 -3
- package/dist/schema/src/cli-results.d.ts +51 -1
- package/dist/schema/src/cli-results.js +25 -1
- package/dist/server/src/openapi.js +1 -0
- package/docs/agents.md +1 -0
- package/docs/extending.md +1 -1
- package/docs/reference/cli.md +11 -2
- package/docs/reference/http-api.md +2 -0
- package/docs/reference/reports.md +202 -9
- package/docs/reference/schema.md +19 -1
- package/docs/rich-evidence.md +32 -5
- package/docs/server.md +1 -1
- package/docs/ui.md +8 -0
- package/llms-full.txt +56 -9
- package/package.json +1 -1
- package/packages/schema/schemas/effective-delta.schema.json +8 -0
- package/packages/schema/schemas/effective-evidence.schema.json +8 -0
- package/packages/schema/schemas/evidence-page.schema.json +4 -0
- package/packages/schema/schemas/layer-rows.schema.json +4 -0
- package/packages/schema/schemas/report-run.schema.json +74 -0
- package/packages/schema/schemas/report-runs.schema.json +86 -0
- package/packages/schema/schemas/review-collection.schema.json +4 -0
- package/packages/schema/schemas/review-manifest.schema.json +4 -0
- package/packages/schema/schemas/test-catalog.schema.json +30 -0
- package/packages/schema/schemas/test-history.schema.json +48 -0
- package/packages/schema/schemas/verify-recordings-result.schema.json +97 -0
- package/packages/server/openapi.json +318 -0
- package/packages/visualizer/src/evidence/applicability-view.tsx +46 -20
- package/packages/visualizer/src/evidence/applicability.test.ts +68 -1
- package/packages/visualizer/src/evidence/applicability.ts +121 -26
- package/packages/visualizer/src/explorer/ArtifactsView.tsx +3 -2
- package/packages/visualizer/src/explorer/Explorer.tsx +96 -8
- package/packages/visualizer/src/explorer/RunsView.tsx +28 -3
- package/packages/visualizer/src/explorer/TestDetail.tsx +14 -1
- package/packages/visualizer/src/explorer/applicability.ts +43 -0
- package/packages/visualizer/src/explorer/filters.tsx +11 -3
- package/packages/visualizer/src/explorer/search.ts +20 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module Command results (`--format json`)
|
|
3
3
|
*
|
|
4
|
-
* What `atlas report publish`, `stream`, `merge` and `
|
|
4
|
+
* What `atlas report publish`, `stream`, `merge`, `doctor` and `verify-recordings` print on standard output with `--format json` (alias
|
|
5
5
|
* `--json`), and what any `atlas` command prints on standard error for a command that was wrong: one JSON document,
|
|
6
6
|
* so an agent or a script never has to parse prose. (`status` and `policy` already print documents:
|
|
7
7
|
* `ReportStatusSchema`, and an IAM policy.) Each is also published as a JSON Schema.
|
|
@@ -152,6 +152,36 @@ export declare const DoctorResultSchema: z.ZodObject<{
|
|
|
152
152
|
}, z.core.$loose>>;
|
|
153
153
|
}, z.core.$loose>>;
|
|
154
154
|
}, z.core.$loose>;
|
|
155
|
+
/** A published video that could not be read back from the store as published (see `atlas report verify-recordings`). */
|
|
156
|
+
export declare const RecordingGapSchema: z.ZodObject<{
|
|
157
|
+
layer: z.ZodString;
|
|
158
|
+
check: z.ZodNullable<z.ZodString>;
|
|
159
|
+
name: z.ZodString;
|
|
160
|
+
bundleKey: z.ZodString;
|
|
161
|
+
problem: z.ZodString;
|
|
162
|
+
reason: z.ZodString;
|
|
163
|
+
}, z.core.$loose>;
|
|
164
|
+
/**
|
|
165
|
+
* What `atlas report verify-recordings --format json` prints: whether the videos the layers published can still be read
|
|
166
|
+
* back from the store as they were published. It describes stored bytes, never playback in a browser, and it never changes
|
|
167
|
+
* a check\'s result.
|
|
168
|
+
*/
|
|
169
|
+
export declare const VerifyRecordingsResultSchema: z.ZodObject<{
|
|
170
|
+
ok: z.ZodBoolean;
|
|
171
|
+
layers: z.ZodNumber;
|
|
172
|
+
checked: z.ZodNumber;
|
|
173
|
+
verified: z.ZodNumber;
|
|
174
|
+
referenced: z.ZodNumber;
|
|
175
|
+
gaps: z.ZodArray<z.ZodObject<{
|
|
176
|
+
layer: z.ZodString;
|
|
177
|
+
check: z.ZodNullable<z.ZodString>;
|
|
178
|
+
name: z.ZodString;
|
|
179
|
+
bundleKey: z.ZodString;
|
|
180
|
+
problem: z.ZodString;
|
|
181
|
+
reason: z.ZodString;
|
|
182
|
+
}, z.core.$loose>>;
|
|
183
|
+
notices: z.ZodArray<z.ZodString>;
|
|
184
|
+
}, z.core.$loose>;
|
|
155
185
|
/** What any `atlas` command prints on standard error, with `--format json`, when the command itself was wrong (exit code 2). */
|
|
156
186
|
export declare const CommandErrorSchema: z.ZodObject<{
|
|
157
187
|
ok: z.ZodLiteral<false>;
|
|
@@ -532,6 +562,26 @@ export declare const CLI_RESULTS: {
|
|
|
532
562
|
}, z.core.$loose>>;
|
|
533
563
|
}, z.core.$loose>;
|
|
534
564
|
};
|
|
565
|
+
readonly verifyRecordings: {
|
|
566
|
+
readonly command: "atlas report verify-recordings";
|
|
567
|
+
readonly schemaName: "verify-recordings-result";
|
|
568
|
+
readonly schema: z.ZodObject<{
|
|
569
|
+
ok: z.ZodBoolean;
|
|
570
|
+
layers: z.ZodNumber;
|
|
571
|
+
checked: z.ZodNumber;
|
|
572
|
+
verified: z.ZodNumber;
|
|
573
|
+
referenced: z.ZodNumber;
|
|
574
|
+
gaps: z.ZodArray<z.ZodObject<{
|
|
575
|
+
layer: z.ZodString;
|
|
576
|
+
check: z.ZodNullable<z.ZodString>;
|
|
577
|
+
name: z.ZodString;
|
|
578
|
+
bundleKey: z.ZodString;
|
|
579
|
+
problem: z.ZodString;
|
|
580
|
+
reason: z.ZodString;
|
|
581
|
+
}, z.core.$loose>>;
|
|
582
|
+
notices: z.ZodArray<z.ZodString>;
|
|
583
|
+
}, z.core.$loose>;
|
|
584
|
+
};
|
|
535
585
|
readonly error: {
|
|
536
586
|
readonly command: "a command that was wrong (standard error)";
|
|
537
587
|
readonly schemaName: "command-error";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module Command results (`--format json`)
|
|
3
3
|
*
|
|
4
|
-
* What `atlas report publish`, `stream`, `merge` and `
|
|
4
|
+
* What `atlas report publish`, `stream`, `merge`, `doctor` and `verify-recordings` print on standard output with `--format json` (alias
|
|
5
5
|
* `--json`), and what any `atlas` command prints on standard error for a command that was wrong: one JSON document,
|
|
6
6
|
* so an agent or a script never has to parse prose. (`status` and `policy` already print documents:
|
|
7
7
|
* `ReportStatusSchema`, and an IAM policy.) Each is also published as a JSON Schema.
|
|
@@ -75,6 +75,29 @@ export const DoctorResultSchema = z.looseObject({
|
|
|
75
75
|
ok: z.boolean().describe('True when every check of every store passed (otherwise the exit code is 1)'),
|
|
76
76
|
stores: z.array(z.looseObject({ id: z.string(), checks: z.array(DoctorCheckSchema) }))
|
|
77
77
|
});
|
|
78
|
+
/** A published video that could not be read back from the store as published (see `atlas report verify-recordings`). */
|
|
79
|
+
export const RecordingGapSchema = z.looseObject({
|
|
80
|
+
layer: z.string().describe('The layer that holds the video'),
|
|
81
|
+
check: z.string().nullable().describe('The check that holds it, or null for a video attached to the layer or a part'),
|
|
82
|
+
name: z.string().describe('The attachment\'s name'),
|
|
83
|
+
bundleKey: z.string().describe('Its key in the layer\'s bundle (`attachments/<sha256>`)'),
|
|
84
|
+
problem: z.string().describe('`not-in-bundle` (the layer names a file its bundle does not list), `blob-missing` (a blob holding it is gone: a 404 when played), `unreadable` (the store could not be read: not proof it is gone), `size-mismatch`, `checksum-mismatch` or `undecodable` (empty, or not the container its media type names). An open vocabulary: treat an unknown problem as a gap'),
|
|
85
|
+
reason: z.string().describe('A short explanation without tokens, paths or payload')
|
|
86
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* What `atlas report verify-recordings --format json` prints: whether the videos the layers published can still be read
|
|
89
|
+
* back from the store as they were published. It describes stored bytes, never playback in a browser, and it never changes
|
|
90
|
+
* a check\'s result.
|
|
91
|
+
*/
|
|
92
|
+
export const VerifyRecordingsResultSchema = z.looseObject({
|
|
93
|
+
ok: z.boolean().describe('True when no layer has a gap and every layer could be read (otherwise the exit code is 1). Test results are unaffected either way'),
|
|
94
|
+
layers: z.number().int().nonnegative().describe('Layers examined'),
|
|
95
|
+
checked: z.number().int().nonnegative().describe('Videos read back from the store (those that name a bundle key)'),
|
|
96
|
+
verified: z.number().int().nonnegative().describe('Of those, the ones that passed every check'),
|
|
97
|
+
referenced: z.number().int().nonnegative().describe('Videos given only by address, which Atlas holds no bytes for and does not check'),
|
|
98
|
+
gaps: z.array(RecordingGapSchema).describe('Every video that is missing, unreadable, corrupt or not the container it claims. Re-publish the layer\'s recordings to repair them'),
|
|
99
|
+
notices: z.array(z.string()).describe('Layers that could not be read at all: their recordings are unknown, never verified')
|
|
100
|
+
});
|
|
78
101
|
/** What any `atlas` command prints on standard error, with `--format json`, when the command itself was wrong (exit code 2). */
|
|
79
102
|
export const CommandErrorSchema = z.looseObject({
|
|
80
103
|
ok: z.literal(false),
|
|
@@ -142,5 +165,6 @@ export const CLI_RESULTS = {
|
|
|
142
165
|
merge: { command: 'atlas report merge', schemaName: 'merge-result', schema: MergeResultSchema },
|
|
143
166
|
doctor: { command: 'atlas report doctor', schemaName: 'doctor-result', schema: DoctorResultSchema },
|
|
144
167
|
status: { command: 'atlas report status', schemaName: 'report-status', schema: ReportStatusSchema },
|
|
168
|
+
verifyRecordings: { command: 'atlas report verify-recordings', schemaName: 'verify-recordings-result', schema: VerifyRecordingsResultSchema },
|
|
145
169
|
error: { command: 'a command that was wrong (standard error)', schemaName: 'command-error', schema: CommandErrorSchema }
|
|
146
170
|
};
|
|
@@ -427,6 +427,7 @@ const BASE_OPERATIONS = [
|
|
|
427
427
|
{ name: 'branch', in: 'query', description: 'Branches.', schema: str },
|
|
428
428
|
{ name: 'sha', in: 'query', description: 'Revisions: a full commit id or a prefix of at least seven characters.', schema: str },
|
|
429
429
|
{ name: 'outcome', in: 'query', description: 'Statuses: `passed`, `failed`, `running`, `not-run`. A run\'s outcome is `failed` when any result failed, else `running`, else `passed`, else `not-run`.', schema: str },
|
|
430
|
+
{ name: 'recordings', in: 'query', description: 'Recording completeness of the run, apart from its outcome: `complete`, `incomplete`, `none-expected` or `unknown` (a run whose layers declared no recording expectations is `unknown`, never `complete`). Repeatable or comma-separated. Answered from the layers\' receipts; no recording is read. A server that does not know it ignores it and does not name it in `filters`.', schema: str },
|
|
430
431
|
{ name: 'q', in: 'query', description: 'Words that must all appear in the title, id, spec, branch, producer, environment, tags or labels.', schema: str },
|
|
431
432
|
{ name: 'since', in: 'query', description: 'Only records at or after this time: epoch milliseconds or an ISO date (a date alone is UTC). A result\'s recording time; a run\'s end.', schema: str },
|
|
432
433
|
{ name: 'until', in: 'query', description: 'Only records at or before this time (same forms as `since`).', schema: str },
|
package/docs/agents.md
CHANGED
|
@@ -163,6 +163,7 @@ JSON Schema (`@crvouga/atlas/schemas/<name>.schema.json`, draft 2020-12):
|
|
|
163
163
|
| `atlas report merge` | `merge-result` |
|
|
164
164
|
| `atlas report doctor` | `doctor-result` |
|
|
165
165
|
| `atlas report status` | `report-status` |
|
|
166
|
+
| `atlas report verify-recordings` | `verify-recordings-result` |
|
|
166
167
|
| `a command that was wrong (standard error)` | `command-error` |
|
|
167
168
|
<!-- /generated:json-command-schemas -->
|
|
168
169
|
|
package/docs/extending.md
CHANGED
|
@@ -30,7 +30,7 @@ carry any of these. Every one is optional.
|
|
|
30
30
|
| `annotations[]` | Notes about the result: a warning, a deprecation, an accessibility finding, a coverage figure. |
|
|
31
31
|
| `attempts[]` | Every try, when the check was retried. |
|
|
32
32
|
| `tags[]` | Free labels to filter by: `smoke`, `payments`. |
|
|
33
|
-
| `labels{}` | Facts about the result that are not its identity: `owner`, `severity`, `browser`, `build`. The viewer reads `build`, `platform`, `applies-to` and `applies-to-by` to tell whether evidence applies to a release (see [the viewer conventions](ui.md#behaviors-read-watch-inspect)). `behavior-id` and `example-id` declare a stable id for the behavior and for one example of an outline, and `outline` names the outline; the [catalog health](server.md#reading-the-report-as-an-agent) report counts them and names an id shared by several checks. They never change which check a result belongs to. |
|
|
33
|
+
| `labels{}` | Facts about the result that are not its identity: `owner`, `severity`, `browser`, `build`. The viewer reads `build`, `platform`, `applies-to` and `applies-to-by` to tell whether evidence applies to a release, and `published-at` and `enriched-at` (epoch milliseconds or an ISO date) to show when it was published or enriched apart from when the checks ran (see [the viewer conventions](ui.md#behaviors-read-watch-inspect)). `behavior-id` and `example-id` declare a stable id for the behavior and for one example of an outline, and `outline` names the outline; the [catalog health](server.md#reading-the-report-as-an-agent) report counts them and names an id shared by several checks. They never change which check a result belongs to. |
|
|
34
34
|
| `scope{}` | What the evidence does and does not show: surface, device kind, synthetic data, scripted dependencies, audio, pacing, what was asserted and its limits. Unknown when absent. See [Evidence scope](rich-evidence.md#evidence-scope). |
|
|
35
35
|
| `links[]` | Where to read more: the CI run, the issue, the trace client. |
|
|
36
36
|
| `extensions{}` | Structured data of your own or a tool's, under a name you own. |
|
package/docs/reference/cli.md
CHANGED
|
@@ -105,6 +105,8 @@ atlas report — layered evidence for any kind of spec
|
|
|
105
105
|
[--specs <dir>] [--format text|json|markdown]
|
|
106
106
|
[--require-environments a,b] [--require-all-specs]
|
|
107
107
|
[--branch <name>]... [--ignore-modified]
|
|
108
|
+
atlas report verify-recordings [--store <dir|s3://…|id=url>]... [--backends <module>] [--layer <id>]...
|
|
109
|
+
[--limit <n>] [--branch <name>]... [--ignore-modified] [--format text|json]
|
|
108
110
|
atlas report merge [--store …]... [--backends <module>] --into <dir|s3://…>
|
|
109
111
|
atlas report doctor [--store …]... [--role publisher|reader|both]
|
|
110
112
|
atlas report policy --store s3://bucket/prefix [--role publisher|reader|both]
|
|
@@ -148,6 +150,13 @@ atlas report — layered evidence for any kind of spec
|
|
|
148
150
|
--require-all-specs fail while any spec has no results
|
|
149
151
|
--branch <name> only layers recorded on this branch (repeatable)
|
|
150
152
|
--ignore-modified leave out layers from modified checkouts and unknown origins
|
|
153
|
+
verify-recordings Read back the videos the layers published and check they are still there as published: the
|
|
154
|
+
layer still lists the file, its blob is still stored, its size and SHA-256 are the recorded ones, and it
|
|
155
|
+
begins like the container its media type names. Exit 1 when any video is missing, unreadable, corrupt or
|
|
156
|
+
not that container, naming the layer and check; test results are never changed. This checks stored bytes,
|
|
157
|
+
not playback in a browser. Videos given only by address are counted, not checked.
|
|
158
|
+
--layer <id> only the layer whose id starts with this (repeatable; at least 7 digits)
|
|
159
|
+
--limit <n> only the newest n layers
|
|
151
160
|
merge Copy the layers of several stores into one.
|
|
152
161
|
doctor Check that a store works: credentials, listing, writing, and what it holds.
|
|
153
162
|
policy Print the least-privilege IAM policy for publishers, readers, or both.
|
|
@@ -156,8 +165,8 @@ atlas report — layered evidence for any kind of spec
|
|
|
156
165
|
{ id, label?, store } (or a function returning it): a device lab, anything
|
|
157
166
|
that implements ObjectStore.
|
|
158
167
|
|
|
159
|
-
--format json (alias --json) publish, stream, merge, doctor and
|
|
160
|
-
(schemas: publish-result, stream-result, merge-result, doctor-result, report-status). A command
|
|
168
|
+
--format json (alias --json) publish, stream, merge, doctor, status and verify-recordings print one JSON document instead of prose
|
|
169
|
+
(schemas: publish-result, stream-result, merge-result, doctor-result, report-status, verify-recordings-result). A command
|
|
161
170
|
that is wrong is {"ok":false,"error":{"message":…,"code":"usage"}} on standard error.
|
|
162
171
|
A store may be an Atlas server (https://…): ATLAS_STORE=https://atlas.example.com, with ATLAS_TOKEN.
|
|
163
172
|
|
|
@@ -833,6 +833,7 @@ Every run, newest first: layers grouped by their CI run URL (else branch and com
|
|
|
833
833
|
- `branch` (query): Branches.
|
|
834
834
|
- `sha` (query): Revisions: a full commit id or a prefix of at least seven characters.
|
|
835
835
|
- `outcome` (query): Statuses: `passed`, `failed`, `running`, `not-run`. A run's outcome is `failed` when any result failed, else `running`, else `passed`, else `not-run`.
|
|
836
|
+
- `recordings` (query): Recording completeness of the run, apart from its outcome: `complete`, `incomplete`, `none-expected` or `unknown` (a run whose layers declared no recording expectations is `unknown`, never `complete`). Repeatable or comma-separated. Answered from the layers' receipts; no recording is read. A server that does not know it ignores it and does not name it in `filters`.
|
|
836
837
|
- `q` (query): Words that must all appear in the title, id, spec, branch, producer, environment, tags or labels.
|
|
837
838
|
- `since` (query): Only records at or after this time: epoch milliseconds or an ISO date (a date alone is UTC). A result's recording time; a run's end.
|
|
838
839
|
- `until` (query): Only records at or before this time (same forms as `since`).
|
|
@@ -861,6 +862,7 @@ Scoped to one project. The same project may be selected on the unprefixed API us
|
|
|
861
862
|
- `branch` (query): Branches.
|
|
862
863
|
- `sha` (query): Revisions: a full commit id or a prefix of at least seven characters.
|
|
863
864
|
- `outcome` (query): Statuses: `passed`, `failed`, `running`, `not-run`. A run's outcome is `failed` when any result failed, else `running`, else `passed`, else `not-run`.
|
|
865
|
+
- `recordings` (query): Recording completeness of the run, apart from its outcome: `complete`, `incomplete`, `none-expected` or `unknown` (a run whose layers declared no recording expectations is `unknown`, never `complete`). Repeatable or comma-separated. Answered from the layers' receipts; no recording is read. A server that does not know it ignores it and does not name it in `filters`.
|
|
864
866
|
- `q` (query): Words that must all appear in the title, id, spec, branch, producer, environment, tags or labels.
|
|
865
867
|
- `since` (query): Only records at or after this time: epoch milliseconds or an ISO date (a date alone is UTC). A result's recording time; a run's end.
|
|
866
868
|
- `until` (query): Only records at or before this time (same forms as `since`).
|
|
@@ -10,7 +10,7 @@ Source: `packages/reports/src/artifact-query.ts`. Import: `@crvouga/atlas/report
|
|
|
10
10
|
|
|
11
11
|
One shareable query for the runs of a project and for the recorded results that carry evidence (recordings,
|
|
12
12
|
screenshots, traces): by artifact tag, label, media type, feature (domain, spec), environment or device, producer,
|
|
13
|
-
branch, revision, outcome and
|
|
13
|
+
branch, revision, outcome, time and (runs only) recording completeness. The same rules answer `GET /api/reports/runs`, `GET /api/reports/artifacts`
|
|
14
14
|
and the viewer's Runs and Artifacts views, so a link means the same thing everywhere.
|
|
15
15
|
|
|
16
16
|
The semantics are fixed, not inferred:
|
|
@@ -81,6 +81,12 @@ export type ArtifactFilter = {
|
|
|
81
81
|
readonly domain?: readonly string[];
|
|
82
82
|
/** Spec paths. Not applicable to runs. */
|
|
83
83
|
readonly spec?: readonly string[];
|
|
84
|
+
/**
|
|
85
|
+
* Recording completeness of a run: `complete`, `incomplete`, `none-expected` or `unknown` (a run whose layers declared no
|
|
86
|
+
* recording expectations is `unknown`, never `complete`). Answered from the layers' receipts alone, without reading any
|
|
87
|
+
* recording. Applies to runs only; it is independent of `outcome`.
|
|
88
|
+
*/
|
|
89
|
+
readonly recordings?: readonly string[];
|
|
84
90
|
/** Only records at or after this time (epoch ms): a result's recording time, a run's end. */
|
|
85
91
|
readonly since?: number;
|
|
86
92
|
/** Only records at or before this time (epoch ms). */
|
|
@@ -124,6 +130,8 @@ export type FilterFields = {
|
|
|
124
130
|
/** Absent when the dimension does not apply (runs have no domain). */
|
|
125
131
|
readonly domains?: readonly string[];
|
|
126
132
|
readonly specs?: readonly string[];
|
|
133
|
+
/** The recording completeness of a run (one value). Absent when the dimension does not apply (results). */
|
|
134
|
+
readonly recordings?: readonly string[];
|
|
127
135
|
readonly at: number;
|
|
128
136
|
};
|
|
129
137
|
```
|
|
@@ -143,6 +151,8 @@ export type QueryFacets = {
|
|
|
143
151
|
readonly branches: readonly QueryFacetValue[];
|
|
144
152
|
readonly outcomes: readonly QueryFacetValue[];
|
|
145
153
|
readonly domains?: readonly QueryFacetValue[];
|
|
154
|
+
/** Runs only: `complete`, `incomplete`, `none-expected` and `unknown`, with how many runs have each. */
|
|
155
|
+
readonly recordings?: readonly QueryFacetValue[];
|
|
146
156
|
};
|
|
147
157
|
```
|
|
148
158
|
|
|
@@ -204,6 +214,14 @@ export function usedFilters(filter: ArtifactFilter): FilterName[]
|
|
|
204
214
|
|
|
205
215
|
Which filters a query uses, by the names an address gives them.
|
|
206
216
|
|
|
217
|
+
#### `forArtifacts`
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
export function forArtifacts(filter: ArtifactFilter): ArtifactFilter
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Every filter in `filter` that may be applied to a recorded result, as the artifacts query applies them (no recording completeness, which is a property of a run).
|
|
224
|
+
|
|
207
225
|
#### `isFiltered`
|
|
208
226
|
|
|
209
227
|
```ts
|
|
@@ -218,7 +236,7 @@ Whether a query narrows anything.
|
|
|
218
236
|
export function forRuns(filter: ArtifactFilter): ArtifactFilter
|
|
219
237
|
```
|
|
220
238
|
|
|
221
|
-
Every filter in `filter` that may be applied to a
|
|
239
|
+
Every filter in `filter` that may be applied to a run, as the runs query applies them (no domain or spec).
|
|
222
240
|
|
|
223
241
|
#### `matchesFilter`
|
|
224
242
|
|
|
@@ -2161,6 +2179,8 @@ export type VideoRef = {
|
|
|
2161
2179
|
readonly recording?: string;
|
|
2162
2180
|
/** False when the reference names no bytes at all (no bundle key, address or inline text), or a bundle key that the layer's own bundle does not hold, so it can never be played. */
|
|
2163
2181
|
readonly available: boolean;
|
|
2182
|
+
/** Why it is unavailable, when the report can tell: `not-in-bundle` names a file the layer should hold (its bundle key) but does not, a recording that was acknowledged and is no longer there. Absent otherwise, and for an available video. An open vocabulary: treat an unknown reason as plain unavailable. */
|
|
2183
|
+
readonly reason?: 'not-in-bundle';
|
|
2164
2184
|
/** 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. */
|
|
2165
2185
|
readonly scope?: EvidenceScope;
|
|
2166
2186
|
};
|
|
@@ -2180,6 +2200,8 @@ export type VideoCounts = {
|
|
|
2180
2200
|
readonly recordings: number;
|
|
2181
2201
|
/** References that name no bytes, or bytes the layer does not hold. A report that predates availability metadata has none of these and none of the others. */
|
|
2182
2202
|
readonly unavailable: number;
|
|
2203
|
+
/** Of `unavailable`, those that name a file the layer's bundle does not hold (stored, then gone). Absent when there are none. */
|
|
2204
|
+
readonly missing?: number;
|
|
2183
2205
|
};
|
|
2184
2206
|
```
|
|
2185
2207
|
|
|
@@ -6408,6 +6430,160 @@ skipped or unresolved; otherwise `unknown` when a contributing layer has no rece
|
|
|
6408
6430
|
report with no results at all is unknown too); otherwise `complete` when something was expected and `none-expected` when
|
|
6409
6431
|
nothing was. Gaps are sorted so the output never depends on read order.
|
|
6410
6432
|
|
|
6433
|
+
#### `RunRecordings`
|
|
6434
|
+
|
|
6435
|
+
```ts
|
|
6436
|
+
export type RunRecordings = {
|
|
6437
|
+
/** `incomplete`: a recording is skipped or unresolved. `unknown`: no gap is known, but a layer declared no expectations. `complete`: everything expected was published. `none-expected`: every layer declared expectations and none required a recording. */
|
|
6438
|
+
readonly state: 'complete' | 'incomplete' | 'none-expected' | 'unknown';
|
|
6439
|
+
/** Recordings the layers expected. */
|
|
6440
|
+
readonly expected: number;
|
|
6441
|
+
/** Of those, uploaded or referenced by address. */
|
|
6442
|
+
readonly published: number;
|
|
6443
|
+
readonly skipped: number;
|
|
6444
|
+
readonly unresolved: number;
|
|
6445
|
+
/** Intentionally unrecorded checks: neither expected nor missing. */
|
|
6446
|
+
readonly notApplicable: number;
|
|
6447
|
+
/** Layers of the run that declared no expectations: nothing is claimed for them. */
|
|
6448
|
+
readonly layersWithoutReceipt: number;
|
|
6449
|
+
};
|
|
6450
|
+
```
|
|
6451
|
+
|
|
6452
|
+
A run's recording completeness, from the receipts of its layers (see {@link runRecordings}).
|
|
6453
|
+
|
|
6454
|
+
#### `runRecordings`
|
|
6455
|
+
|
|
6456
|
+
```ts
|
|
6457
|
+
export function runRecordings(layers: readonly (RecordingCounts | undefined)[]): RunRecordings | undefined
|
|
6458
|
+
```
|
|
6459
|
+
|
|
6460
|
+
The recording completeness of a run, from the `recordings` counts of each of its layers (`undefined` for a layer that
|
|
6461
|
+
has no receipt). `undefined` when no layer has a receipt: the run claims nothing, which a reader treats as `unknown`.
|
|
6462
|
+
A gap always wins (`incomplete`); otherwise a layer without a receipt keeps the run `unknown`, never `complete`. Reads
|
|
6463
|
+
only the counts, never a recording, and never changes a status.
|
|
6464
|
+
|
|
6465
|
+
### Recording verification
|
|
6466
|
+
|
|
6467
|
+
Source: `packages/reports/src/recording-verify.ts`. Import: `@crvouga/atlas/reports/recording-verify`
|
|
6468
|
+
|
|
6469
|
+
Answers "is the recording we published still there, and does it still look like the video we published?" apart from
|
|
6470
|
+
"did the tests pass?" and apart from "was it uploaded?" (the receipt, see `recording-receipt.ts`). `verifyLayerRecordings`
|
|
6471
|
+
reads each video a layer keeps in its bundle back from the store and checks, in order, that the layer's bundle still
|
|
6472
|
+
lists its file, that the blob it lives in is still stored, that its size and SHA-256 are the ones recorded at upload,
|
|
6473
|
+
and that its first bytes are the container header its media type promises. A problem is a gap in the evidence, named
|
|
6474
|
+
with the check that holds the video; it never changes a status, a slot or a winner.
|
|
6475
|
+
|
|
6476
|
+
Trap: this is a stored-bytes check, not a playback test. A file with a valid header can still fail to play in a given
|
|
6477
|
+
browser (codec, truncation), and acknowledging an upload is not playback either. Videos given only by `uri` are counted
|
|
6478
|
+
as `referenced`, not checked: Atlas holds no bytes to read back.
|
|
6479
|
+
|
|
6480
|
+
See docs/rich-evidence.md#recording-completeness
|
|
6481
|
+
|
|
6482
|
+
#### `RecordingProblem`
|
|
6483
|
+
|
|
6484
|
+
```ts
|
|
6485
|
+
export type RecordingProblem =
|
|
6486
|
+
/** The layer names the file but its bundle does not list it: stored, then gone (or never arrived). */
|
|
6487
|
+
| 'not-in-bundle'
|
|
6488
|
+
/** The bundle lists the file but a blob holding its bytes is no longer in the store (a 404 when played). */
|
|
6489
|
+
| 'blob-missing'
|
|
6490
|
+
/** The store could not be read just now. Not proof the bytes are gone, and not a pass. */
|
|
6491
|
+
| 'unreadable'
|
|
6492
|
+
/** The stored bytes are not as many as were recorded. */
|
|
6493
|
+
| 'size-mismatch'
|
|
6494
|
+
/** The stored bytes do not hash to the SHA-256 recorded at upload. */
|
|
6495
|
+
| 'checksum-mismatch'
|
|
6496
|
+
/** The stored bytes do not begin like the container their media type names, or are empty: incompatible or corrupt. */
|
|
6497
|
+
| 'undecodable';
|
|
6498
|
+
```
|
|
6499
|
+
|
|
6500
|
+
What is wrong with a stored recording. An open vocabulary: a reader treats an unknown problem as a gap.
|
|
6501
|
+
|
|
6502
|
+
#### `RecordingGap`
|
|
6503
|
+
|
|
6504
|
+
```ts
|
|
6505
|
+
export type RecordingGap = {
|
|
6506
|
+
/** The check that holds the video, or `null` for a video attached to the layer or a part. */
|
|
6507
|
+
readonly check: string | null;
|
|
6508
|
+
/** The attachment's name. */
|
|
6509
|
+
readonly name: string;
|
|
6510
|
+
readonly bundleKey: string;
|
|
6511
|
+
readonly problem: RecordingProblem;
|
|
6512
|
+
/** A short, non-secret explanation: sizes or the container found. */
|
|
6513
|
+
readonly reason: string;
|
|
6514
|
+
};
|
|
6515
|
+
```
|
|
6516
|
+
|
|
6517
|
+
One video of a layer that failed verification.
|
|
6518
|
+
|
|
6519
|
+
#### `LayerRecordingVerification`
|
|
6520
|
+
|
|
6521
|
+
```ts
|
|
6522
|
+
export type LayerRecordingVerification = {
|
|
6523
|
+
readonly layer: string;
|
|
6524
|
+
/** Videos read back from the store: those that name a bundle key. */
|
|
6525
|
+
readonly checked: number;
|
|
6526
|
+
/** Of those, the ones that passed every check. */
|
|
6527
|
+
readonly ok: number;
|
|
6528
|
+
/** Videos given only by `uri`, which Atlas cannot read back. */
|
|
6529
|
+
readonly referenced: number;
|
|
6530
|
+
readonly gaps: readonly RecordingGap[];
|
|
6531
|
+
};
|
|
6532
|
+
```
|
|
6533
|
+
|
|
6534
|
+
What verifying one layer found.
|
|
6535
|
+
|
|
6536
|
+
#### `containerProblem`
|
|
6537
|
+
|
|
6538
|
+
```ts
|
|
6539
|
+
export function containerProblem(bytes: Uint8Array, contentType: string): string | null
|
|
6540
|
+
```
|
|
6541
|
+
|
|
6542
|
+
Whether the first bytes of a stored video are the container its media type names, as a reason it is not, or `null` when
|
|
6543
|
+
it looks right or the type is one this check has no signature for (then nothing is claimed). Recognizes WebM and
|
|
6544
|
+
Matroska (EBML), MP4, QuickTime and 3GPP (an ISO box), Ogg and AVI. An empty file is never a video.
|
|
6545
|
+
|
|
6546
|
+
#### `verifyLayerRecordings`
|
|
6547
|
+
|
|
6548
|
+
```ts
|
|
6549
|
+
export async function verifyLayerRecordings(store: ObjectStore, layer: ReportLayer): Promise<LayerRecordingVerification>
|
|
6550
|
+
```
|
|
6551
|
+
|
|
6552
|
+
Reads back every video of `layer` that is kept in its bundle and reports the ones that are no longer retrievable or no
|
|
6553
|
+
longer look like what was published (see {@link RecordingProblem}). Each blob is read once however many checks
|
|
6554
|
+
reference it. A store that fails to answer is `unreadable`, never a pass. It only reads: nothing is written, and no
|
|
6555
|
+
status changes.
|
|
6556
|
+
|
|
6557
|
+
**Example**
|
|
6558
|
+
|
|
6559
|
+
```ts
|
|
6560
|
+
const layer = await readLayer(store, id);
|
|
6561
|
+
const { gaps } = await verifyLayerRecordings(store, layer!);
|
|
6562
|
+
gaps.map(gap => `${gap.check}: ${gap.problem}`);
|
|
6563
|
+
```
|
|
6564
|
+
|
|
6565
|
+
#### `StoreRecordingVerification`
|
|
6566
|
+
|
|
6567
|
+
```ts
|
|
6568
|
+
export type StoreRecordingVerification = {
|
|
6569
|
+
readonly layers: readonly LayerRecordingVerification[];
|
|
6570
|
+
/** Layers that could not be read at all (their manifest is unreadable or gone): their recordings are unknown, not verified. */
|
|
6571
|
+
readonly notices: readonly string[];
|
|
6572
|
+
};
|
|
6573
|
+
```
|
|
6574
|
+
|
|
6575
|
+
What verifying the layers of one store found.
|
|
6576
|
+
|
|
6577
|
+
#### `verifyStoreRecordings`
|
|
6578
|
+
|
|
6579
|
+
```ts
|
|
6580
|
+
export async function verifyStoreRecordings(store: ObjectStore, options: { readonly ids?: readonly string[]; readonly limit?: number; readonly include?: (layer: ReportLayer) => boolean } = {}): Promise<StoreRecordingVerification>
|
|
6581
|
+
```
|
|
6582
|
+
|
|
6583
|
+
Verifies the recordings of the layers a store lists, newest first. `ids` narrows to layers whose id starts with one of
|
|
6584
|
+
them (at least 7 hex digits); `limit` keeps only that many of the newest. A layer that cannot be read is a notice, never
|
|
6585
|
+
skipped silently and never counted as verified. A store that cannot be listed rejects.
|
|
6586
|
+
|
|
6411
6587
|
### Reliability of a test
|
|
6412
6588
|
|
|
6413
6589
|
Source: `packages/reports/src/reliability.ts`. Import: `@crvouga/atlas/reports/reliability`
|
|
@@ -6767,7 +6943,7 @@ Call this inside a Solid root: it creates signals and a store.
|
|
|
6767
6943
|
|
|
6768
6944
|
Source: `packages/reports/src/report-cli.ts`. Import: `@crvouga/atlas/reports/report-cli`
|
|
6769
6945
|
|
|
6770
|
-
`reportMain` is `atlas report publish | stream | status | merge | doctor | policy`, written so it can be called from code and
|
|
6946
|
+
`reportMain` is `atlas report publish | stream | status | verify-recordings | merge | doctor | policy`, written so it can be called from code and
|
|
6771
6947
|
tests as well as a terminal: it takes its arguments, environment, working directory, clock, git and standard input as
|
|
6772
6948
|
parameters and returns the exit code. Every command can print a JSON document (`--format json`) instead of prose, and a
|
|
6773
6949
|
command that is wrong throws, which `runReportCommand` turns into a message (or a JSON error) and exit code 2.
|
|
@@ -9578,6 +9754,17 @@ export const HISTORY_PER_TEST = …
|
|
|
9578
9754
|
|
|
9579
9755
|
Recorded results kept per test, newest first. Older ones are counted in no statistic.
|
|
9580
9756
|
|
|
9757
|
+
#### `APPLICABILITY_LABELS`
|
|
9758
|
+
|
|
9759
|
+
```ts
|
|
9760
|
+
export const APPLICABILITY_LABELS = …
|
|
9761
|
+
```
|
|
9762
|
+
|
|
9763
|
+
The open `labels` (see docs/extending.md) that say what a result's evidence ran against and who vouches for it: `build`,
|
|
9764
|
+
`platform`, `applies-to` (builds or commits it is asserted to still hold for), `applies-to-by` (the policy or person that
|
|
9765
|
+
asserts it), `published-at` and `enriched-at` (when the evidence was published or enriched, epoch milliseconds or an ISO
|
|
9766
|
+
date: never when the checks ran). A test's current results carry them as `provenance`.
|
|
9767
|
+
|
|
9581
9768
|
#### `TestResultEntry`
|
|
9582
9769
|
|
|
9583
9770
|
```ts
|
|
@@ -9606,6 +9793,10 @@ export type TestResultEntry = {
|
|
|
9606
9793
|
readonly detail: string;
|
|
9607
9794
|
/** 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". */
|
|
9608
9795
|
readonly specRevision?: string;
|
|
9796
|
+
/** `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. */
|
|
9797
|
+
readonly freshness?: 'unknown' | 'modified';
|
|
9798
|
+
/** 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. */
|
|
9799
|
+
readonly provenance?: Readonly<Record<string, string>>;
|
|
9609
9800
|
/** What it carried beyond its outcome (attachments, steps, retries, tags, labels), when anything. */
|
|
9610
9801
|
readonly evidence?: EvidenceSummary;
|
|
9611
9802
|
/** 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). */
|
|
@@ -9680,6 +9871,8 @@ export type CatalogSpec = {
|
|
|
9680
9871
|
/** The group it belongs to: the spec's own domain, else the folder under `specs/`. */
|
|
9681
9872
|
readonly domain: string;
|
|
9682
9873
|
readonly kind: string;
|
|
9874
|
+
/** 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". */
|
|
9875
|
+
readonly fingerprint?: string;
|
|
9683
9876
|
};
|
|
9684
9877
|
```
|
|
9685
9878
|
|
|
@@ -9925,14 +10118,14 @@ export type RunSummary = {
|
|
|
9925
10118
|
* and references that cannot be played. Absent when it has none, and in a report that carries no video information.
|
|
9926
10119
|
*/
|
|
9927
10120
|
readonly videos?: VideoCounts;
|
|
10121
|
+
/**
|
|
10122
|
+
* Whether the recordings its layers expected were published, apart from its outcomes (see `runRecordings`). Absent when
|
|
10123
|
+
* none of its layers declared a recording expectation: its completeness is unknown, never complete. Never changes a status.
|
|
10124
|
+
*/
|
|
10125
|
+
readonly recordings?: RunRecordings;
|
|
9928
10126
|
/**
|
|
9929
10127
|
* Files attached to the run itself, in its layers (and their parts), not to any one check: review movies, archives,
|
|
9930
10128
|
* summaries. All kinds, videos included. Absent when none, and in a report that carries no such information.
|
|
9931
|
-
*/
|
|
9932
|
-
readonly attachments?: number;
|
|
9933
|
-
/** The normalized tags of the attachments of its results and layers, sorted and distinct. Absent when none is tagged. */
|
|
9934
|
-
readonly artifactTags?: readonly string[];
|
|
9935
|
-
/** The tags of its checks and layers, which its attachments inherit (what `inherited=1` also matches), sorted and distinct. Absent when none. */
|
|
9936
10129
|
…
|
|
9937
10130
|
```
|
|
9938
10131
|
|
|
@@ -10264,7 +10457,7 @@ What a query looks at in a run: see {@link ArtifactFilter}. A run's outcome is `
|
|
|
10264
10457
|
#### `artifactList`
|
|
10265
10458
|
|
|
10266
10459
|
```ts
|
|
10267
|
-
export function artifactList(history: TestHistory, version: string,
|
|
10460
|
+
export function artifactList(history: TestHistory, version: string, asked: ArtifactFilter = {}, page: { readonly limit?: number; readonly offset?: number; readonly domainOf?: (specPath: string) => string | undefined } = {}): ArtifactList
|
|
10268
10461
|
```
|
|
10269
10462
|
|
|
10270
10463
|
The recorded results that carry evidence and match a query, newest first, across the whole history (`limit` default
|
package/docs/reference/schema.md
CHANGED
|
@@ -535,7 +535,7 @@ of every layer. States are in id order and cells in profile order, so the same l
|
|
|
535
535
|
|
|
536
536
|
Source: `packages/schema/src/cli-results.ts`
|
|
537
537
|
|
|
538
|
-
What `atlas report publish`, `stream`, `merge` and `
|
|
538
|
+
What `atlas report publish`, `stream`, `merge`, `doctor` and `verify-recordings` print on standard output with `--format json` (alias
|
|
539
539
|
`--json`), and what any `atlas` command prints on standard error for a command that was wrong: one JSON document,
|
|
540
540
|
so an agent or a script never has to parse prose. (`status` and `policy` already print documents:
|
|
541
541
|
`ReportStatusSchema`, and an IAM policy.) Each is also published as a JSON Schema.
|
|
@@ -594,6 +594,24 @@ export const DoctorResultSchema = …
|
|
|
594
594
|
|
|
595
595
|
What `atlas report doctor --format json` prints.
|
|
596
596
|
|
|
597
|
+
#### `RecordingGapSchema`
|
|
598
|
+
|
|
599
|
+
```ts
|
|
600
|
+
export const RecordingGapSchema = …
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
A published video that could not be read back from the store as published (see `atlas report verify-recordings`).
|
|
604
|
+
|
|
605
|
+
#### `VerifyRecordingsResultSchema`
|
|
606
|
+
|
|
607
|
+
```ts
|
|
608
|
+
export const VerifyRecordingsResultSchema = …
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
What `atlas report verify-recordings --format json` prints: whether the videos the layers published can still be read
|
|
612
|
+
back from the store as they were published. It describes stored bytes, never playback in a browser, and it never changes
|
|
613
|
+
a check\'s result.
|
|
614
|
+
|
|
597
615
|
#### `CommandErrorSchema`
|
|
598
616
|
|
|
599
617
|
```ts
|
package/docs/rich-evidence.md
CHANGED
|
@@ -208,7 +208,8 @@ curl "$ATLAS_SERVER_URL/api/reports/artifacts?artifactTag=demo&type=video&domain
|
|
|
208
208
|
- **Dimensions.** `artifactTag`, `type` (the media type as a role: `video`, `image`, `audio`, `snapshot`, `text`,
|
|
209
209
|
`file`; `video` means a playable recording), `label` (`key=value`, or `key`), `domain` and `spec` (artifacts only),
|
|
210
210
|
`environment` (the device or client), `producer`, `branch`, `sha` (seven or more characters), `outcome` (`passed`,
|
|
211
|
-
`failed`, `running`, `not-run`), `
|
|
211
|
+
`failed`, `running`, `not-run`), `recordings` (runs only: `complete`, `incomplete`, `none-expected` or `unknown`, see
|
|
212
|
+
[recording completeness](#recording-completeness)), `since` and `until` (epoch milliseconds or an ISO date), and `q` (words).
|
|
212
213
|
- **Semantics.** Filters combine with AND across dimensions and OR within one (`environment=ios,android`). With both
|
|
213
214
|
`artifactTag` and `type`, one attachment must be both where the report says which video carries which tag: a tagged
|
|
214
215
|
screenshot beside an untagged video is not a tagged video. `inherited=1` also counts the tags an attachment inherits
|
|
@@ -475,7 +476,33 @@ publish; it never contains a path or a token.
|
|
|
475
476
|
retry (publishing again reuses the stored files).
|
|
476
477
|
- `atlas report status --format json` has `recordings`: `state` is `complete`, `incomplete`, `none-expected` or
|
|
477
478
|
`unknown`, with counts and each gap. It never changes `ok`. A layer without a receipt is `unknown`, never `complete`.
|
|
478
|
-
- The viewer shows `Recordings 2 of 3: incomplete` beside the run's origin
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
479
|
+
- The viewer shows `Recordings 2 of 3: incomplete` beside the run's origin and in each row of the Runs tab, apart from
|
|
480
|
+
its green or red outcomes.
|
|
481
|
+
- **Find runs by completeness.** `GET /api/reports/runs?recordings=incomplete` (and the Runs tab's *Recordings* filter,
|
|
482
|
+
`?tab=runs&recordings=incomplete`) keep the runs whose layers have a skipped or unresolved expected recording. The
|
|
483
|
+
values are `complete`, `incomplete`, `none-expected` and `unknown`, comma-separated for OR, and they combine with the
|
|
484
|
+
other filters by AND. A run whose layers declared no expectations is `unknown`, never `complete`, and a run with any
|
|
485
|
+
layer without a receipt cannot be `complete`. It is answered from the layers' receipts, before the page limit, and
|
|
486
|
+
never reads a recording; the facet counts say how many runs have each state. It is a property of a run, so the
|
|
487
|
+
artifacts list does not take it. A server that predates it ignores the parameter and does not name it in `filters`, and
|
|
488
|
+
the viewer then says the list is not narrowed by it.
|
|
489
|
+
|
|
490
|
+
### Is the recording still there?
|
|
491
|
+
|
|
492
|
+
A receipt says a file was published. Whether it is still retrievable later is a separate question, and Atlas answers it
|
|
493
|
+
two ways without touching a check's result:
|
|
494
|
+
|
|
495
|
+
- **Layer bundle.** A video that names a `bundleKey` the layer's bundle does not list (the file was acknowledged and is
|
|
496
|
+
gone, or never arrived) is not offered to play. The report marks it `available: false` with `reason: "not-in-bundle"`,
|
|
497
|
+
the run counts it in `videos.unavailable` and `videos.missing`, and the Runs tab explains that the run stored the file
|
|
498
|
+
but no longer holds it, which is different from a reference that never named any bytes.
|
|
499
|
+
- **`atlas report verify-recordings`** reads the videos of the stored layers back and checks, for each, that the bundle
|
|
500
|
+
lists it, that its blob is still in the store, that its size and SHA-256 are the recorded ones, and that it begins like
|
|
501
|
+
the container its media type names (WebM, MP4, QuickTime, Ogg, AVI). It exits 1 and names the layer and check for
|
|
502
|
+
every gap (`not-in-bundle`, `blob-missing`, `unreadable`, `size-mismatch`, `checksum-mismatch`, `undecodable`);
|
|
503
|
+
`--format json` prints the `verify-recordings-result` document. `--layer <id>` and `--limit <n>` narrow the work, since
|
|
504
|
+
it reads blobs. A store that cannot answer is `unreadable`, never verified. Test results are unchanged either way.
|
|
505
|
+
|
|
506
|
+
Both are statements about stored bytes. Atlas does not check that a stored video plays in a given browser (a valid header
|
|
507
|
+
can still have an unsupported codec or be truncated), and acknowledging an upload is not playback. Videos given only by
|
|
508
|
+
`uri` are counted as referenced and not checked.
|
package/docs/server.md
CHANGED
|
@@ -410,7 +410,7 @@ with `format=markdown` (or `Accept: text/markdown`), and takes the project prefi
|
|
|
410
410
|
| One test: every recorded result (status, duration, environment, branch, commit, CI run), newest first | `GET /api/reports/tests/history?key=<key>` |
|
|
411
411
|
| How reliable one test is in a scope (environment, producer, parameters, branch, commit, spec revision, time window): independent executions, first-try and eventual success, what was left out and why | `GET /api/reports/tests/reliability?key=<key>&environment=phone` |
|
|
412
412
|
| Why Specs, domains and counts disagree: ambiguous spec paths, shared domains, gaps, a reconciliation of the totals | `GET /api/reports/tests/health` |
|
|
413
|
-
| The runs that recorded the report (by CI run URL, else branch and commit); `?video=true` keeps those with a playable video; `artifactTag`, `type`, `label`, `environment`, `branch`, `outcome`, `since`, `until` narrow it ([filtering](rich-evidence.md#finding-runs-and-artifacts)) | `GET /api/reports/runs` |
|
|
413
|
+
| The runs that recorded the report (by CI run URL, else branch and commit); `?video=true` keeps those with a playable video; `artifactTag`, `type`, `label`, `environment`, `branch`, `outcome`, `recordings` (completeness of the recordings the run's layers expected), `since`, `until` narrow it ([filtering](rich-evidence.md#finding-runs-and-artifacts)) | `GET /api/reports/runs` |
|
|
414
414
|
| Recorded results that carry evidence, across the whole history, found by tag, media type, label, feature, environment, branch, revision, outcome and time | `GET /api/reports/artifacts?artifactTag=demo&type=video` |
|
|
415
415
|
| Every test one run recorded | `GET /api/reports/runs/detail?key=<run key>` |
|
|
416
416
|
|
package/docs/ui.md
CHANGED
|
@@ -91,6 +91,14 @@ when Watch is on screen.
|
|
|
91
91
|
what they know with labels: `build`, `platform`, and `applies-to` with `applies-to-by` (the policy that asserts the
|
|
92
92
|
evidence still holds for those builds; without it the assertion is refused). The "Stale or unverified" filter
|
|
93
93
|
(`evidence=stale`) lists every behavior whose evidence does not apply, including those with none.
|
|
94
|
+
- The test explorer makes the same reading of every test from the catalog (`explorer/applicability.ts`): the catalog
|
|
95
|
+
carries the spec's fingerprint now (`spec.fingerprint`) and, on each current result, `specRevision`, `freshness` and
|
|
96
|
+
`provenance` (the labels above), all optional. The same address names (`release`, `releaseSha`, `releasePlatform`,
|
|
97
|
+
`evidence=stale`) give the "Stale or unverified" button, the "Stale evidence first" sort, a verdict badge on rows
|
|
98
|
+
(stale always; unknown or none when a release is named or evidence is filtered or sorted), and the summary in test
|
|
99
|
+
detail. Layers carry no publication time, because a layer's id is the digest of its content and a time of upload in
|
|
100
|
+
it would make a retried publish a different layer: a producer that knows states `published-at` and `enriched-at`
|
|
101
|
+
labels, shown apart from "Checks ran", which alone is the age.
|
|
94
102
|
- A recording is always labelled illustrative: it shows one run and does not approve a behavior for production.
|
|
95
103
|
`demo`/`representative` tags only add a "Representative demo" label.
|
|
96
104
|
- The spec index searches titles, paths and declared domains, and can group by the domain from spec metadata (specs with
|