@crvouga/atlas 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/reports/src/attachment-files.d.ts +3 -1
- package/dist/reports/src/attachment-files.js +14 -4
- package/dist/reports/src/evidence-detail.d.ts +19 -2
- package/dist/reports/src/evidence-detail.js +63 -14
- package/dist/reports/src/evidence-types.d.ts +53 -0
- package/dist/reports/src/layer-merge.js +7 -2
- package/dist/reports/src/layer-types.d.ts +1 -1
- package/dist/reports/src/publish-checks.js +11 -3
- package/dist/reports/src/recording-receipt.d.ts +48 -0
- package/dist/reports/src/recording-receipt.js +103 -0
- package/dist/reports/src/report-cli.d.ts +1 -1
- package/dist/reports/src/report-cli.js +20 -2
- package/dist/reports/src/report-status.d.ts +2 -0
- package/dist/reports/src/report-status.js +17 -1
- package/dist/reports/src/reports-explorer-route.d.ts +8 -0
- package/dist/reports/src/reports-route.d.ts +8 -0
- package/dist/reports/src/test-catalog.d.ts +24 -3
- package/dist/reports/src/test-catalog.js +37 -11
- package/dist/schema/src/api.d.ts +232 -0
- package/dist/schema/src/api.js +35 -6
- package/dist/schema/src/cli-results.d.ts +107 -2
- package/dist/schema/src/cli-results.js +11 -3
- package/dist/schema/src/layer.d.ts +165 -0
- package/dist/schema/src/layer.js +72 -1
- package/dist/server/src/app.js +3 -2
- package/dist/server/src/llms.js +2 -1
- package/dist/server/src/openapi.js +2 -1
- package/docs/reference/cli.md +5 -0
- package/docs/reference/http-api.md +4 -2
- package/docs/reference/reports.md +204 -5
- package/docs/reference/schema.md +75 -0
- package/docs/rich-evidence.md +29 -0
- package/docs/server.md +1 -1
- package/llms-full.txt +39 -3
- package/package.json +1 -1
- package/packages/schema/schemas/check-detail.schema.json +20 -0
- package/packages/schema/schemas/effective-delta.schema.json +143 -0
- package/packages/schema/schemas/effective-evidence.schema.json +143 -0
- package/packages/schema/schemas/evidence-page.schema.json +86 -0
- package/packages/schema/schemas/layer-rows.schema.json +57 -0
- package/packages/schema/schemas/layer.schema.json +153 -0
- package/packages/schema/schemas/publish-checks.schema.json +24 -0
- package/packages/schema/schemas/publish-result.schema.json +161 -1
- package/packages/schema/schemas/report-run.schema.json +71 -0
- package/packages/schema/schemas/report-runs.schema.json +38 -1
- package/packages/schema/schemas/report-status.schema.json +104 -0
- package/packages/schema/schemas/test-catalog.schema.json +41 -0
- package/packages/schema/schemas/test-history.schema.json +82 -0
- package/packages/server/openapi.json +830 -4
- package/packages/visualizer/src/evidence/marks.tsx +16 -0
- package/packages/visualizer/src/explorer/RunsView.tsx +128 -10
- package/packages/visualizer/src/explorer/catalog.tsx +16 -6
- package/packages/visualizer/src/explorer/search.ts +4 -1
package/docs/reference/cli.md
CHANGED
|
@@ -91,6 +91,7 @@ atlas report — layered evidence for any kind of spec
|
|
|
91
91
|
[--param key=value]... [--kind <k>] [--title <t>] [--recorded-at <iso|ms>]
|
|
92
92
|
[--attach [role=]<file>]... [--label key=value]... [--link [name=]<url>]...
|
|
93
93
|
[--attach-root <dir>]... [--max-attachment-mb <n>] [--max-total-attachment-mb <n>]
|
|
94
|
+
[--require-recordings]
|
|
94
95
|
atlas report stream [--store <dir|s3://…|https://atlas.example.com>] --producer <name> [--environment <name>]
|
|
95
96
|
[--branch <b>] [--sha <s>] [--run-url <u>] [--kind <k>] [--param key=value]...
|
|
96
97
|
[--run-id <id>] [--run-title <t>]
|
|
@@ -127,6 +128,10 @@ atlas report — layered evidence for any kind of spec
|
|
|
127
128
|
Files a report names (CTRF attachments, JUnit [[ATTACHMENT|path]] lines) come from the report's own
|
|
128
129
|
folder and each --attach-root, and never from a hidden file or a link out: a report is data from a
|
|
129
130
|
test run, not instructions. One that is missing or not allowed is skipped with a warning.
|
|
131
|
+
--require-recordings exit 1 when a check that declared recordingExpectation "required" has no
|
|
132
|
+
uploaded video (absent, left out as too large or not allowed, or refused). The layer is
|
|
133
|
+
still written and the checks keep their results; only this command fails. The receipt of
|
|
134
|
+
what was uploaded (counts, sizes, checksums) is in the JSON result under layer.recordings.
|
|
130
135
|
From a checkout with uncommitted changes the layer is marked "modified".
|
|
131
136
|
stream Send checks as they finish: one JSON object per line on standard input (the fields of one
|
|
132
137
|
check: id, title, status, and any evidence). Each second's worth becomes one layer, so a server
|
|
@@ -616,9 +616,10 @@ Responses: `201` The recorded entry.; `401` No token, or an unknown one. Send `A
|
|
|
616
616
|
|
|
617
617
|
`GET /api/reports/runs` · role: read
|
|
618
618
|
|
|
619
|
-
Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned
|
|
619
|
+
Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned, up to twenty failures and their video counts (references, distinct playable recordings, unavailable). Send `format=markdown` for Markdown.
|
|
620
620
|
|
|
621
621
|
- `limit` (query): At most this many runs (default 100).
|
|
622
|
+
- `video` (query): `true` to list only runs that have a playable video recording. Applied to every run before `limit`; `total` counts the matches. A server that does not know it answers with every run.
|
|
622
623
|
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
623
624
|
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
624
625
|
|
|
@@ -628,12 +629,13 @@ Responses: `200` The runs.; `304` Unchanged since the `If-None-Match` ETag.; `40
|
|
|
628
629
|
|
|
629
630
|
`GET /api/projects/{project}/reports/runs` · role: read
|
|
630
631
|
|
|
631
|
-
Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned
|
|
632
|
+
Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned, up to twenty failures and their video counts (references, distinct playable recordings, unavailable). Send `format=markdown` for Markdown.
|
|
632
633
|
|
|
633
634
|
Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
|
|
634
635
|
|
|
635
636
|
- `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
636
637
|
- `limit` (query): At most this many runs (default 100).
|
|
638
|
+
- `video` (query): `true` to list only runs that have a playable video recording. Applied to every run before `limit`; `total` counts the matches. A server that does not know it answers with every run.
|
|
637
639
|
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
638
640
|
|
|
639
641
|
Responses: `200` The runs.; `304` Unchanged since the `If-None-Match` ETag.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
|
|
@@ -276,7 +276,9 @@ recursively), and hands their bytes over as `data` with a `contentType` guessed
|
|
|
276
276
|
attachment gave none.
|
|
277
277
|
|
|
278
278
|
A file that is missing, not allowed or too large is left out with a warning: evidence that could not be
|
|
279
|
-
found never stops the results from being published.
|
|
279
|
+
found never stops the results from being published. The exception in kind is a video of a check that declared
|
|
280
|
+
`recordingExpectation: "required"`: it stays as an attachment without bytes whose `omitted` says why (`not-found`,
|
|
281
|
+
`not-allowed`, `too-large`, `over-budget`), so the recording receipt can name the gap. Attachments without a `path` are kept untouched.
|
|
280
282
|
|
|
281
283
|
- `holder`: A check, step, attempt or any object that may have `attachments`, `steps` and `attempts`.
|
|
282
284
|
|
|
@@ -1426,7 +1428,36 @@ read when someone asks for it. Undefined when there is none.
|
|
|
1426
1428
|
Several holders (for example a check and its layer) are summed: `attachments` also counts those on steps and
|
|
1427
1429
|
attempts, `steps` counts only each holder's own top-level steps, tags are de-duplicated, and for a repeated
|
|
1428
1430
|
label the later holder wins. `artifactTags` are the attachments' own normalized tags and are never inherited. `roles` are the attachments' roles (or, without one, what their media type or file
|
|
1429
|
-
name implies), sorted and distinct.
|
|
1431
|
+
name implies), sorted and distinct. `videos` lists every attachment whose effective media type is video, with the
|
|
1432
|
+
check, retry attempt or step that holds it (see {@link isVideoAttachment}).
|
|
1433
|
+
|
|
1434
|
+
#### `summarizeLayerEvidence`
|
|
1435
|
+
|
|
1436
|
+
```ts
|
|
1437
|
+
export function summarizeLayerEvidence(...holders: readonly EvidenceHolder[]): EvidenceSummary | undefined
|
|
1438
|
+
```
|
|
1439
|
+
|
|
1440
|
+
Like {@link summarizeEvidence} for a layer and its parts: the same summary, but a video attached to the layer or a
|
|
1441
|
+
part is owned by the `layer`, not by a check.
|
|
1442
|
+
|
|
1443
|
+
#### `isVideoAttachment`
|
|
1444
|
+
|
|
1445
|
+
```ts
|
|
1446
|
+
export function isVideoAttachment(attachment: Pick<Attachment, 'name' | 'role' | 'contentType'>): boolean
|
|
1447
|
+
```
|
|
1448
|
+
|
|
1449
|
+
Whether an attachment is a video, by its effective media type: the producer's `contentType`, else the one its file
|
|
1450
|
+
name implies. A role never decides this on its own (`demo` on a PNG is not a video), except that a `video` role
|
|
1451
|
+
settles a file whose type nothing says (`application/octet-stream`).
|
|
1452
|
+
|
|
1453
|
+
#### `videoCounts`
|
|
1454
|
+
|
|
1455
|
+
```ts
|
|
1456
|
+
export function videoCounts(videos: readonly VideoRef[] | undefined): VideoCounts | undefined
|
|
1457
|
+
```
|
|
1458
|
+
|
|
1459
|
+
Counts references, distinct playable recordings and unplayable references. Undefined when there is no reference, so
|
|
1460
|
+
"no video" and "nothing known about video" look the same on the wire.
|
|
1430
1461
|
|
|
1431
1462
|
#### `roleOfType`
|
|
1432
1463
|
|
|
@@ -1609,6 +1640,51 @@ export type StatusCounts = Readonly<Record<EvidenceStatus, number>>;
|
|
|
1609
1640
|
|
|
1610
1641
|
How many results have each status (`passed`, `failed`, `running`, `not-run`).
|
|
1611
1642
|
|
|
1643
|
+
#### `VideoOwner`
|
|
1644
|
+
|
|
1645
|
+
```ts
|
|
1646
|
+
export type VideoOwner = 'layer' | 'check' | 'attempt' | 'step';
|
|
1647
|
+
```
|
|
1648
|
+
|
|
1649
|
+
Who holds a recording: the whole layer (or one of its parts), one check, one retry attempt of a check, or one step.
|
|
1650
|
+
|
|
1651
|
+
#### `VideoRef`
|
|
1652
|
+
|
|
1653
|
+
```ts
|
|
1654
|
+
export type VideoRef = {
|
|
1655
|
+
/** The attachment's name, for a person. */
|
|
1656
|
+
readonly name: string;
|
|
1657
|
+
readonly owner: VideoOwner;
|
|
1658
|
+
/** The try that holds it, when `owner` is `attempt` or the step is inside one. */
|
|
1659
|
+
readonly attempt?: number;
|
|
1660
|
+
/** The name of the step that holds it, when `owner` is `step`. */
|
|
1661
|
+
readonly step?: string;
|
|
1662
|
+
/** What identifies the bytes (digest, bundle key or address); references with the same one are the same recording. Absent when `available` is false. */
|
|
1663
|
+
readonly recording?: string;
|
|
1664
|
+
/** False when the reference names no bytes at all (no bundle key, address or inline text), so it can never be played. */
|
|
1665
|
+
readonly available: boolean;
|
|
1666
|
+
};
|
|
1667
|
+
```
|
|
1668
|
+
|
|
1669
|
+
One reference to a video, found by its effective media type (the producer's `contentType`, else the one the file
|
|
1670
|
+
name implies; a `video` role alone counts only when nothing else says what the file is). A reference is not a
|
|
1671
|
+
recording: the same bytes referenced from three checks are three references and one recording.
|
|
1672
|
+
|
|
1673
|
+
#### `VideoCounts`
|
|
1674
|
+
|
|
1675
|
+
```ts
|
|
1676
|
+
export type VideoCounts = {
|
|
1677
|
+
/** Every reference to a video, playable or not. */
|
|
1678
|
+
readonly references: number;
|
|
1679
|
+
/** Distinct playable recordings: identical bytes referenced twice are one. */
|
|
1680
|
+
readonly recordings: number;
|
|
1681
|
+
/** References that name no bytes. A report that predates availability metadata has none of these and none of the others. */
|
|
1682
|
+
readonly unavailable: number;
|
|
1683
|
+
};
|
|
1684
|
+
```
|
|
1685
|
+
|
|
1686
|
+
Video availability as counts: references, distinct playable recordings (deduplicated), and references that cannot be played.
|
|
1687
|
+
|
|
1612
1688
|
#### `EvidenceSummary`
|
|
1613
1689
|
|
|
1614
1690
|
```ts
|
|
@@ -1626,6 +1702,8 @@ export type EvidenceSummary = {
|
|
|
1626
1702
|
readonly labels: Readonly<Record<string, string>>;
|
|
1627
1703
|
/** The distinct roles of its attachments (or what their media types say), to show what kind of evidence there is. */
|
|
1628
1704
|
readonly roles: readonly string[];
|
|
1705
|
+
/** Its references to videos, wherever they are held (layer, check, attempt, step). Only there when it has any. */
|
|
1706
|
+
readonly videos?: readonly VideoRef[];
|
|
1629
1707
|
};
|
|
1630
1708
|
```
|
|
1631
1709
|
|
|
@@ -1671,11 +1749,34 @@ export type LayerHeader = {
|
|
|
1671
1749
|
readonly execution: string;
|
|
1672
1750
|
/** What the run as a whole carries: its logs, links and labels. */
|
|
1673
1751
|
readonly evidence?: EvidenceSummary;
|
|
1752
|
+
/** Counts from the layer's recording receipt: how many expected recordings were published. Absent when the layer declared no expectations: its completeness is unknown, not complete. Never changes a status. */
|
|
1753
|
+
readonly recordings?: RecordingCounts;
|
|
1674
1754
|
};
|
|
1675
1755
|
```
|
|
1676
1756
|
|
|
1677
1757
|
A report layer's origin and totals. Its checks are read separately, once.
|
|
1678
1758
|
|
|
1759
|
+
#### `RecordingCounts`
|
|
1760
|
+
|
|
1761
|
+
```ts
|
|
1762
|
+
export type RecordingCounts = {
|
|
1763
|
+
/** Checks that required a recording: `uploaded + referenced + skipped + unresolved`. */
|
|
1764
|
+
readonly expected: number;
|
|
1765
|
+
/** Stored in the layer, with a checksum. */
|
|
1766
|
+
readonly uploaded: number;
|
|
1767
|
+
/** Given only by `uri`: Atlas cannot vouch for it. */
|
|
1768
|
+
readonly referenced: number;
|
|
1769
|
+
/** Captured but left out, with a reason (too large, not allowed…). */
|
|
1770
|
+
readonly skipped: number;
|
|
1771
|
+
/** No video was attached at all. */
|
|
1772
|
+
readonly unresolved: number;
|
|
1773
|
+
/** Intentionally unrecorded checks: neither expected nor missing. */
|
|
1774
|
+
readonly notApplicable: number;
|
|
1775
|
+
};
|
|
1776
|
+
```
|
|
1777
|
+
|
|
1778
|
+
How many recordings a layer expected and what became of them (see `RecordingReceipt`).
|
|
1779
|
+
|
|
1679
1780
|
#### `LayerRow`
|
|
1680
1781
|
|
|
1681
1782
|
```ts
|
|
@@ -1699,6 +1800,8 @@ export type LayerRow = {
|
|
|
1699
1800
|
readonly durationMs: number | null;
|
|
1700
1801
|
/** What this result carries beyond its outcome. Absent when nothing. */
|
|
1701
1802
|
readonly evidence?: EvidenceSummary;
|
|
1803
|
+
/** What became of this check's expected recording: `uploaded`, `referenced`, `skipped`, `unresolved` or `not-applicable`, with a reason when it is not uploaded. Absent when the check declared no expectation. Independent of `status`. */
|
|
1804
|
+
readonly recording?: { readonly state: string; readonly reason?: string };
|
|
1702
1805
|
};
|
|
1703
1806
|
```
|
|
1704
1807
|
|
|
@@ -3907,7 +4010,7 @@ String choices (such as `{ device: 'iphone-15' }`) that make a result its own sl
|
|
|
3907
4010
|
#### `LayerExtras`
|
|
3908
4011
|
|
|
3909
4012
|
```ts
|
|
3910
|
-
export type LayerExtras = Partial<Pick<import('../../schema/src/layer').LayerDocument, 'tags' | 'labels' | 'links' | 'attachments' | 'annotations' | 'extensions'>>;
|
|
4013
|
+
export type LayerExtras = Partial<Pick<import('../../schema/src/layer').LayerDocument, 'tags' | 'labels' | 'links' | 'attachments' | 'annotations' | 'extensions' | 'recordings'>>;
|
|
3911
4014
|
```
|
|
3912
4015
|
|
|
3913
4016
|
What a layer may carry as a whole. All optional and open: see docs/extending.md.
|
|
@@ -5305,6 +5408,69 @@ export function contentTypeForMedia(media: string): string
|
|
|
5305
5408
|
|
|
5306
5409
|
The media type for a snapshot media kind (`html`, `png`, `json`, `text`, `bytes`, `video`); an unknown kind is `application/octet-stream`.
|
|
5307
5410
|
|
|
5411
|
+
### Recording receipts
|
|
5412
|
+
|
|
5413
|
+
Source: `packages/reports/src/recording-receipt.ts`. Import: `@crvouga/atlas/reports/recording-receipt`
|
|
5414
|
+
|
|
5415
|
+
Answers "did the recordings we expected get published?" apart from "did the tests pass?". `recordingReceipt` reads the
|
|
5416
|
+
checks a producer is about to publish (after their files were stored) and describes, per check that declared a
|
|
5417
|
+
`recordingExpectation`, whether its video was uploaded, only referenced, left out (with a reason) or never attached.
|
|
5418
|
+
`recordingCompleteness` reduces the winning results of a report to the figures `atlas report status` prints. Neither
|
|
5419
|
+
reads a clock or the environment, and neither changes a status, a slot or a winner: a receipt describes publication, it
|
|
5420
|
+
does not decide a result. A layer with no receipt is Unknown, never Complete.
|
|
5421
|
+
|
|
5422
|
+
See docs/rich-evidence.md#recording-completeness
|
|
5423
|
+
|
|
5424
|
+
#### `isVideoAttachment`
|
|
5425
|
+
|
|
5426
|
+
```ts
|
|
5427
|
+
export function isVideoAttachment(attachment: Loose): boolean
|
|
5428
|
+
```
|
|
5429
|
+
|
|
5430
|
+
Is this attachment a screen recording: a role of `video`, or a `video/*` media type?
|
|
5431
|
+
|
|
5432
|
+
#### `recordingReceipt`
|
|
5433
|
+
|
|
5434
|
+
```ts
|
|
5435
|
+
export function recordingReceipt(checks: readonly Loose[]): RecordingReceipt | undefined
|
|
5436
|
+
```
|
|
5437
|
+
|
|
5438
|
+
The receipt for the checks of one layer, or `undefined` when no check declares a `recordingExpectation` (the layer then
|
|
5439
|
+
claims nothing). Call it with checks whose attachment bytes are already stored (`bundleKey`, `bytes` and `sha256` set).
|
|
5440
|
+
|
|
5441
|
+
A `required` check is `uploaded` when any video of it has stored bytes, `referenced` when one has only a `uri`,
|
|
5442
|
+
`skipped` when its videos were left out (the attachment's `omitted` is the reason) and `unresolved` when it has no
|
|
5443
|
+
video at all. A `not-applicable` check is listed but is neither expected nor missing. Items are sorted by check id so the
|
|
5444
|
+
same input is always the same receipt.
|
|
5445
|
+
|
|
5446
|
+
#### `RecordingResult`
|
|
5447
|
+
|
|
5448
|
+
```ts
|
|
5449
|
+
export type RecordingResult = {
|
|
5450
|
+
readonly spec: string | null;
|
|
5451
|
+
readonly check: string;
|
|
5452
|
+
readonly environment: string | null;
|
|
5453
|
+
readonly layer: string;
|
|
5454
|
+
/** Whether the layer the result came from carried a receipt. */
|
|
5455
|
+
readonly hasReceipt: boolean;
|
|
5456
|
+
/** The result's item in that receipt, when its check declared an expectation. */
|
|
5457
|
+
readonly recording?: { readonly state: string; readonly reason?: string };
|
|
5458
|
+
};
|
|
5459
|
+
```
|
|
5460
|
+
|
|
5461
|
+
One result as {@link recordingCompleteness} reads it.
|
|
5462
|
+
|
|
5463
|
+
#### `recordingCompleteness`
|
|
5464
|
+
|
|
5465
|
+
```ts
|
|
5466
|
+
export function recordingCompleteness(results: readonly RecordingResult[]): RecordingCompleteness
|
|
5467
|
+
```
|
|
5468
|
+
|
|
5469
|
+
Reduces the winning results of a report to its recording completeness. `incomplete` when any expected recording is
|
|
5470
|
+
skipped or unresolved; otherwise `unknown` when a contributing layer has no receipt (nothing is claimed for it, and a
|
|
5471
|
+
report with no results at all is unknown too); otherwise `complete` when something was expected and `none-expected` when
|
|
5472
|
+
nothing was. Gaps are sorted so the output never depends on read order.
|
|
5473
|
+
|
|
5308
5474
|
### Remote commit report
|
|
5309
5475
|
|
|
5310
5476
|
Source: `packages/reports/src/remote-report.ts`. Import: `@crvouga/atlas/reports/remote-report`
|
|
@@ -5680,6 +5846,8 @@ checks (or none completed), failed checks, running checks, specs without results
|
|
|
5680
5846
|
each required environment with no checks (the name is normalised, so `iOS` means `ios`), then every notice of the
|
|
5681
5847
|
report (an unreadable layer or backend). `failing` lists each failed or running winner, in a fixed order that does
|
|
5682
5848
|
not depend on read order; diagnostic `performance` checks are left out unless nothing else was recorded.
|
|
5849
|
+
`recordings` reports recording completeness of the same results apart from `ok`: an incomplete or unknown
|
|
5850
|
+
completeness never changes the gate (use `atlas report publish --require-recordings` to fail on a missing video).
|
|
5683
5851
|
|
|
5684
5852
|
**Example**
|
|
5685
5853
|
|
|
@@ -7930,20 +8098,39 @@ export type RunSummary = {
|
|
|
7930
8098
|
readonly totalDurationMs: number | null;
|
|
7931
8099
|
/** Up to twenty tests that failed in it. */
|
|
7932
8100
|
readonly failing: readonly { readonly key: string; readonly title: string; readonly environment: string | null; readonly detail: string }[];
|
|
8101
|
+
/**
|
|
8102
|
+
* Its videos, from the run's layers and from the last result of each test: references, distinct playable recordings
|
|
8103
|
+
* and references that cannot be played. Absent when it has none, and in a report that carries no video information.
|
|
8104
|
+
*/
|
|
8105
|
+
readonly videos?: VideoCounts;
|
|
7933
8106
|
};
|
|
7934
8107
|
```
|
|
7935
8108
|
|
|
7936
8109
|
A run: the layers that name the same execution (`run.id`), else share a CI run URL, else a branch and commit. When,
|
|
7937
8110
|
where, and how its tests went.
|
|
7938
8111
|
|
|
8112
|
+
#### `RunQuery`
|
|
8113
|
+
|
|
8114
|
+
```ts
|
|
8115
|
+
export type RunQuery = {
|
|
8116
|
+
/** Only runs that have at least one playable video recording. `total` then counts only those. */
|
|
8117
|
+
readonly video?: boolean;
|
|
8118
|
+
};
|
|
8119
|
+
```
|
|
8120
|
+
|
|
8121
|
+
What `runList` can be asked.
|
|
8122
|
+
|
|
7939
8123
|
#### `RunList`
|
|
7940
8124
|
|
|
7941
8125
|
```ts
|
|
7942
8126
|
export type RunList = {
|
|
7943
8127
|
readonly schemaVersion: typeof TEST_CATALOG_VERSION;
|
|
7944
8128
|
readonly version: string;
|
|
8129
|
+
/** Runs that matched, before `limit`. */
|
|
7945
8130
|
readonly total: number;
|
|
7946
8131
|
readonly runs: readonly RunSummary[];
|
|
8132
|
+
/** How many of all runs (whatever was asked, before `limit`) have a playable video recording. */
|
|
8133
|
+
readonly withVideo?: number;
|
|
7947
8134
|
};
|
|
7948
8135
|
```
|
|
7949
8136
|
|
|
@@ -7964,6 +8151,8 @@ export type RunResult = {
|
|
|
7964
8151
|
readonly at: number;
|
|
7965
8152
|
readonly layer: string;
|
|
7966
8153
|
readonly detail: string;
|
|
8154
|
+
/** Every video its evidence references, with who holds it (check, retry attempt or step). Absent when there is none. */
|
|
8155
|
+
readonly videos?: readonly VideoRef[];
|
|
7967
8156
|
};
|
|
7968
8157
|
```
|
|
7969
8158
|
|
|
@@ -8114,13 +8303,23 @@ export function testHistory(model: CatalogModel, key: string): TestHistoryDocume
|
|
|
8114
8303
|
|
|
8115
8304
|
One test and its recorded results, or null when the report has no test with that key.
|
|
8116
8305
|
|
|
8306
|
+
#### `hasVideo`
|
|
8307
|
+
|
|
8308
|
+
```ts
|
|
8309
|
+
export function hasVideo(run: Pick<RunSummary, 'videos'>): boolean
|
|
8310
|
+
```
|
|
8311
|
+
|
|
8312
|
+
Whether a run has at least one playable video recording.
|
|
8313
|
+
|
|
8117
8314
|
#### `runList`
|
|
8118
8315
|
|
|
8119
8316
|
```ts
|
|
8120
|
-
export function runList(history: TestHistory, version: string, limit = 100): RunList
|
|
8317
|
+
export function runList(history: TestHistory, version: string, limit = 100, query: RunQuery = {}): RunList
|
|
8121
8318
|
```
|
|
8122
8319
|
|
|
8123
|
-
Every run the history knows of, newest first, `limit` at most (default 100).
|
|
8320
|
+
Every run the history knows of, newest first, `limit` at most (default 100). With `query.video` only the runs that
|
|
8321
|
+
have a playable video recording: the filter and `total` apply to every run before `limit`, and nothing is read from
|
|
8322
|
+
any recording's bytes. `withVideo` always counts every run that has one.
|
|
8124
8323
|
|
|
8125
8324
|
#### `runDetail`
|
|
8126
8325
|
|
package/docs/reference/schema.md
CHANGED
|
@@ -69,6 +69,22 @@ export const BackendInfoSchema = …
|
|
|
69
69
|
|
|
70
70
|
A backend as the report shows it: where results came from, and whether it could be read.
|
|
71
71
|
|
|
72
|
+
#### `VideoRefSchema`
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
export const VideoRefSchema = …
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
One reference to a video held by a layer, check, retry attempt or step, found by the attachment's effective media type.
|
|
79
|
+
|
|
80
|
+
#### `VideoCountsSchema`
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
export const VideoCountsSchema = …
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Video references as counts: how many are referenced, how many distinct recordings can be played, how many cannot.
|
|
87
|
+
|
|
72
88
|
#### `EvidenceSummarySchema`
|
|
73
89
|
|
|
74
90
|
```ts
|
|
@@ -748,6 +764,47 @@ export type Attachment = z.output<typeof AttachmentSchema>;
|
|
|
748
764
|
|
|
749
765
|
An attachment as stored in a layer, parsed from `AttachmentSchema`.
|
|
750
766
|
|
|
767
|
+
#### `RECORDING_EXPECTATIONS`
|
|
768
|
+
|
|
769
|
+
```ts
|
|
770
|
+
export const RECORDING_EXPECTATIONS = …
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
Whether a check is expected to have a screen recording (a video). `required`: the producer expects one, and the layer's
|
|
774
|
+
recording receipt says whether it arrived. `not-applicable`: the check is intentionally unrecorded (an API-only check),
|
|
775
|
+
so it counts as neither a recorded pass nor a missing video. An open string: a reader treats any other value, and its
|
|
776
|
+
absence, as no expectation.
|
|
777
|
+
|
|
778
|
+
#### `RecordingItemSchema`
|
|
779
|
+
|
|
780
|
+
```ts
|
|
781
|
+
export const RecordingItemSchema = …
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
The state of one check's recording in a receipt, in an open vocabulary. Conventions: `uploaded` (the video's bytes are in
|
|
785
|
+
the layer, with a checksum), `referenced` (only a `uri` was given: Atlas cannot vouch for it), `skipped` (the video was
|
|
786
|
+
captured but its bytes were left out, with a reason), `unresolved` (no video was attached at all), `not-applicable`
|
|
787
|
+
(intentionally unrecorded). A reader shows a state it does not know as it is and never counts it as uploaded.
|
|
788
|
+
|
|
789
|
+
#### `RecordingReceiptSchema`
|
|
790
|
+
|
|
791
|
+
```ts
|
|
792
|
+
export const RecordingReceiptSchema = …
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
What a layer's producer published about the recordings it expected: counts and one item per check that expected a
|
|
796
|
+
recording (or declared itself not applicable). Written by `publishChecks` when any check declares a
|
|
797
|
+
`recordingExpectation`; a layer without it says nothing, which a reader reports as Unknown, never Complete. It only
|
|
798
|
+
describes publication: it never changes a check's status, its slot or which result wins.
|
|
799
|
+
|
|
800
|
+
#### `RecordingReceipt`
|
|
801
|
+
|
|
802
|
+
```ts
|
|
803
|
+
export type RecordingReceipt = z.output<typeof RecordingReceiptSchema>;
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
A recording receipt as stored in a layer, parsed from `RecordingReceiptSchema`.
|
|
807
|
+
|
|
751
808
|
#### `AnnotationSchema`
|
|
752
809
|
|
|
753
810
|
```ts
|
|
@@ -946,6 +1003,24 @@ export type PublishChecksInput = z.input<typeof PublishChecksSchema>;
|
|
|
946
1003
|
|
|
947
1004
|
What a caller passes to `publishChecks`: the input type of `PublishChecksSchema`, before defaults.
|
|
948
1005
|
|
|
1006
|
+
#### `RecordingCompletenessSchema`
|
|
1007
|
+
|
|
1008
|
+
```ts
|
|
1009
|
+
export const RecordingCompletenessSchema = …
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
Recording completeness across the winning results of a report: `complete` (every expected recording was published),
|
|
1013
|
+
`incomplete` (at least one is skipped or unresolved), `none-expected` (every layer declared its expectations, and none
|
|
1014
|
+
required a recording) or `unknown` (a layer has no receipt, so nothing is claimed). Independent of the checks' outcomes.
|
|
1015
|
+
|
|
1016
|
+
#### `RecordingCompleteness`
|
|
1017
|
+
|
|
1018
|
+
```ts
|
|
1019
|
+
export type RecordingCompleteness = z.output<typeof RecordingCompletenessSchema>;
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
A parsed `RecordingCompletenessSchema` value.
|
|
1023
|
+
|
|
949
1024
|
#### `REPORT_STATUS_SCHEMA_VERSION`
|
|
950
1025
|
|
|
951
1026
|
```ts
|
package/docs/rich-evidence.md
CHANGED
|
@@ -224,3 +224,32 @@ or the command line, an attachment may name a `path` instead; `atlas report publ
|
|
|
224
224
|
([attach evidence](extending.md#attach-evidence)). A CTRF report already carries most of this, and
|
|
225
225
|
`atlas report publish --ctrf` keeps it ([native runner reporters](runners.md) cover CodeceptJS, Mocha, Jest and Node's
|
|
226
226
|
test runner).
|
|
227
|
+
|
|
228
|
+
## Recording completeness
|
|
229
|
+
|
|
230
|
+
A green run does not say whether the videos you expected were kept and published. Atlas answers that separately from
|
|
231
|
+
the checks' outcomes. Mark a check with `recordingExpectation`: `"required"` (expect a video of it) or
|
|
232
|
+
`"not-applicable"` (an API-only or intentionally unrecorded check). A check with neither is not part of the answer.
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{ "id": "checkout#pays", "title": "Pays", "status": "passed", "recordingExpectation": "required",
|
|
236
|
+
"attachments": [{ "name": "pays.webm", "role": "video", "path": "test-results/pays.webm" }] }
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
When any check carries an expectation, `publishChecks` and `atlas report publish` write a receipt into the layer
|
|
240
|
+
(`recordings`): counts of expected, uploaded, referenced (only a `uri`, which Atlas cannot vouch for), skipped and
|
|
241
|
+
unresolved recordings, and for each expected check its state, a reason when it is not uploaded, and the size and SHA-256
|
|
242
|
+
of every stored video. A video over the size limit, or a file that is missing or not allowed, is kept as an attachment
|
|
243
|
+
without bytes whose `omitted` says why (`too-large`, `not-found`, `not-allowed`, `over-budget`) instead of failing the
|
|
244
|
+
publish; it never contains a path or a token.
|
|
245
|
+
|
|
246
|
+
- `atlas report publish --format json` prints the receipt under `layer.recordings` and a summary under `recordings`.
|
|
247
|
+
Add `--require-recordings` to exit 1 when an expected recording is skipped or unresolved. The layer and its test
|
|
248
|
+
results are still written and are not changed; only the command fails, so a CI step can report the evidence gap and
|
|
249
|
+
retry (publishing again reuses the stored files).
|
|
250
|
+
- `atlas report status --format json` has `recordings`: `state` is `complete`, `incomplete`, `none-expected` or
|
|
251
|
+
`unknown`, with counts and each gap. It never changes `ok`. A layer without a receipt is `unknown`, never `complete`.
|
|
252
|
+
- The viewer shows `Recordings 2 of 3: incomplete` beside the run's origin, apart from its green or red outcomes.
|
|
253
|
+
|
|
254
|
+
This is a statement about publication. Atlas does not check that a stored video plays in a browser, and does not yet
|
|
255
|
+
re-check later that a stored file is still readable.
|
package/docs/server.md
CHANGED
|
@@ -276,7 +276,7 @@ with `format=markdown` (or `Accept: text/markdown`), and takes the project prefi
|
|
|
276
276
|
| Only some: `q` (all words), `status` (or `flaky`), `environment`, `producer`, `domain`, `spec`, `tag`, `run`; `sort`, `offset`, `limit` | `GET /api/reports/tests?status=failed&environment=ios` |
|
|
277
277
|
| One test: every recorded result (status, duration, environment, branch, commit, CI run), newest first | `GET /api/reports/tests/history?key=<key>` |
|
|
278
278
|
| Why Specs, domains and counts disagree: ambiguous spec paths, shared domains, gaps, a reconciliation of the totals | `GET /api/reports/tests/health` |
|
|
279
|
-
| The runs that recorded the report (by CI run URL, else branch and commit) | `GET /api/reports/runs` |
|
|
279
|
+
| The runs that recorded the report (by CI run URL, else branch and commit); `?video=true` keeps those with a playable video | `GET /api/reports/runs` |
|
|
280
280
|
| Every test one run recorded | `GET /api/reports/runs/detail?key=<run key>` |
|
|
281
281
|
|
|
282
282
|
```sh
|
package/llms-full.txt
CHANGED
|
@@ -1211,6 +1211,35 @@ or the command line, an attachment may name a `path` instead; `atlas report publ
|
|
|
1211
1211
|
([attach evidence](extending.md#attach-evidence)). A CTRF report already carries most of this, and
|
|
1212
1212
|
`atlas report publish --ctrf` keeps it ([native runner reporters](runners.md) cover CodeceptJS, Mocha, Jest and Node's
|
|
1213
1213
|
test runner).
|
|
1214
|
+
|
|
1215
|
+
## Recording completeness
|
|
1216
|
+
|
|
1217
|
+
A green run does not say whether the videos you expected were kept and published. Atlas answers that separately from
|
|
1218
|
+
the checks' outcomes. Mark a check with `recordingExpectation`: `"required"` (expect a video of it) or
|
|
1219
|
+
`"not-applicable"` (an API-only or intentionally unrecorded check). A check with neither is not part of the answer.
|
|
1220
|
+
|
|
1221
|
+
```json
|
|
1222
|
+
{ "id": "checkout#pays", "title": "Pays", "status": "passed", "recordingExpectation": "required",
|
|
1223
|
+
"attachments": [{ "name": "pays.webm", "role": "video", "path": "test-results/pays.webm" }] }
|
|
1224
|
+
```
|
|
1225
|
+
|
|
1226
|
+
When any check carries an expectation, `publishChecks` and `atlas report publish` write a receipt into the layer
|
|
1227
|
+
(`recordings`): counts of expected, uploaded, referenced (only a `uri`, which Atlas cannot vouch for), skipped and
|
|
1228
|
+
unresolved recordings, and for each expected check its state, a reason when it is not uploaded, and the size and SHA-256
|
|
1229
|
+
of every stored video. A video over the size limit, or a file that is missing or not allowed, is kept as an attachment
|
|
1230
|
+
without bytes whose `omitted` says why (`too-large`, `not-found`, `not-allowed`, `over-budget`) instead of failing the
|
|
1231
|
+
publish; it never contains a path or a token.
|
|
1232
|
+
|
|
1233
|
+
- `atlas report publish --format json` prints the receipt under `layer.recordings` and a summary under `recordings`.
|
|
1234
|
+
Add `--require-recordings` to exit 1 when an expected recording is skipped or unresolved. The layer and its test
|
|
1235
|
+
results are still written and are not changed; only the command fails, so a CI step can report the evidence gap and
|
|
1236
|
+
retry (publishing again reuses the stored files).
|
|
1237
|
+
- `atlas report status --format json` has `recordings`: `state` is `complete`, `incomplete`, `none-expected` or
|
|
1238
|
+
`unknown`, with counts and each gap. It never changes `ok`. A layer without a receipt is `unknown`, never `complete`.
|
|
1239
|
+
- The viewer shows `Recordings 2 of 3: incomplete` beside the run's origin, apart from its green or red outcomes.
|
|
1240
|
+
|
|
1241
|
+
This is a statement about publication. Atlas does not check that a stored video plays in a browser, and does not yet
|
|
1242
|
+
re-check later that a stored file is still readable.
|
|
1214
1243
|
</doc>
|
|
1215
1244
|
|
|
1216
1245
|
<doc path="docs/server.md">
|
|
@@ -1492,7 +1521,7 @@ with `format=markdown` (or `Accept: text/markdown`), and takes the project prefi
|
|
|
1492
1521
|
| Only some: `q` (all words), `status` (or `flaky`), `environment`, `producer`, `domain`, `spec`, `tag`, `run`; `sort`, `offset`, `limit` | `GET /api/reports/tests?status=failed&environment=ios` |
|
|
1493
1522
|
| One test: every recorded result (status, duration, environment, branch, commit, CI run), newest first | `GET /api/reports/tests/history?key=<key>` |
|
|
1494
1523
|
| Why Specs, domains and counts disagree: ambiguous spec paths, shared domains, gaps, a reconciliation of the totals | `GET /api/reports/tests/health` |
|
|
1495
|
-
| The runs that recorded the report (by CI run URL, else branch and commit) | `GET /api/reports/runs` |
|
|
1524
|
+
| The runs that recorded the report (by CI run URL, else branch and commit); `?video=true` keeps those with a playable video | `GET /api/reports/runs` |
|
|
1496
1525
|
| Every test one run recorded | `GET /api/reports/runs/detail?key=<run key>` |
|
|
1497
1526
|
|
|
1498
1527
|
```sh
|
|
@@ -4315,6 +4344,7 @@ atlas report — layered evidence for any kind of spec
|
|
|
4315
4344
|
[--param key=value]... [--kind <k>] [--title <t>] [--recorded-at <iso|ms>]
|
|
4316
4345
|
[--attach [role=]<file>]... [--label key=value]... [--link [name=]<url>]...
|
|
4317
4346
|
[--attach-root <dir>]... [--max-attachment-mb <n>] [--max-total-attachment-mb <n>]
|
|
4347
|
+
[--require-recordings]
|
|
4318
4348
|
atlas report stream [--store <dir|s3://…|https://atlas.example.com>] --producer <name> [--environment <name>]
|
|
4319
4349
|
[--branch <b>] [--sha <s>] [--run-url <u>] [--kind <k>] [--param key=value]...
|
|
4320
4350
|
[--run-id <id>] [--run-title <t>]
|
|
@@ -4351,6 +4381,10 @@ atlas report — layered evidence for any kind of spec
|
|
|
4351
4381
|
Files a report names (CTRF attachments, JUnit [[ATTACHMENT|path]] lines) come from the report's own
|
|
4352
4382
|
folder and each --attach-root, and never from a hidden file or a link out: a report is data from a
|
|
4353
4383
|
test run, not instructions. One that is missing or not allowed is skipped with a warning.
|
|
4384
|
+
--require-recordings exit 1 when a check that declared recordingExpectation "required" has no
|
|
4385
|
+
uploaded video (absent, left out as too large or not allowed, or refused). The layer is
|
|
4386
|
+
still written and the checks keep their results; only this command fails. The receipt of
|
|
4387
|
+
what was uploaded (counts, sizes, checksums) is in the JSON result under layer.recordings.
|
|
4354
4388
|
From a checkout with uncommitted changes the layer is marked "modified".
|
|
4355
4389
|
stream Send checks as they finish: one JSON object per line on standard input (the fields of one
|
|
4356
4390
|
check: id, title, status, and any evidence). Each second's worth becomes one layer, so a server
|
|
@@ -4998,9 +5032,10 @@ Responses: `201` The recorded entry.; `401` No token, or an unknown one. Send `A
|
|
|
4998
5032
|
|
|
4999
5033
|
`GET /api/reports/runs` · role: read
|
|
5000
5034
|
|
|
5001
|
-
Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned
|
|
5035
|
+
Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned, up to twenty failures and their video counts (references, distinct playable recordings, unavailable). Send `format=markdown` for Markdown.
|
|
5002
5036
|
|
|
5003
5037
|
- `limit` (query): At most this many runs (default 100).
|
|
5038
|
+
- `video` (query): `true` to list only runs that have a playable video recording. Applied to every run before `limit`; `total` counts the matches. A server that does not know it answers with every run.
|
|
5004
5039
|
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
5005
5040
|
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
5006
5041
|
|
|
@@ -5010,12 +5045,13 @@ Responses: `200` The runs.; `304` Unchanged since the `If-None-Match` ETag.; `40
|
|
|
5010
5045
|
|
|
5011
5046
|
`GET /api/projects/{project}/reports/runs` · role: read
|
|
5012
5047
|
|
|
5013
|
-
Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned
|
|
5048
|
+
Every run, newest first: layers grouped by their CI run URL (else branch and commit), with their environments, producers, how many tests they recorded, their results by status, the time they spanned, up to twenty failures and their video counts (references, distinct playable recordings, unavailable). Send `format=markdown` for Markdown.
|
|
5014
5049
|
|
|
5015
5050
|
Scoped to one project. The same project may be selected on the unprefixed API using ?project= or X-Atlas-Project. Unknown projects return 404 on reads and are created by an authenticated upload.
|
|
5016
5051
|
|
|
5017
5052
|
- `project` (path, required): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
5018
5053
|
- `limit` (query): At most this many runs (default 100).
|
|
5054
|
+
- `video` (query): `true` to list only runs that have a playable video recording. Applied to every run before `limit`; `total` counts the matches. A server that does not know it answers with every run.
|
|
5019
5055
|
- `format` (query): `markdown` for Markdown instead of JSON.
|
|
5020
5056
|
|
|
5021
5057
|
Responses: `200` The runs.; `304` Unchanged since the `If-None-Match` ETag.; `401` No token, or an unknown one. Send `Authorization: Bearer <token>`.; `503` The server is still reading its backends. Retry after the number of seconds in `Retry-After`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crvouga/atlas",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Atlas: specs of any kind, reported as layered evidence. Publish checks from any runner (JUnit, CTRF, a script), combine them across nodes, branches, environments and backends, gate CI on the result, and run statecharts against the real app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -73,6 +73,10 @@
|
|
|
73
73
|
"type": "number",
|
|
74
74
|
"minimum": 0
|
|
75
75
|
},
|
|
76
|
+
"recordingExpectation": {
|
|
77
|
+
"description": "`required`: expect a video of this check, and report whether it was published. `not-applicable`: intentionally unrecorded. Absent: no expectation. Never changes the check's status",
|
|
78
|
+
"type": "string"
|
|
79
|
+
},
|
|
76
80
|
"tags": {
|
|
77
81
|
"description": "Free labels to filter and group by: smoke, slow, flaky…",
|
|
78
82
|
"type": "array",
|
|
@@ -169,6 +173,10 @@
|
|
|
169
173
|
"description": {
|
|
170
174
|
"type": "string"
|
|
171
175
|
},
|
|
176
|
+
"omitted": {
|
|
177
|
+
"description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
|
|
178
|
+
"type": "string"
|
|
179
|
+
},
|
|
172
180
|
"tags": {
|
|
173
181
|
"description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
|
|
174
182
|
"type": "array",
|
|
@@ -404,6 +412,10 @@
|
|
|
404
412
|
"description": {
|
|
405
413
|
"type": "string"
|
|
406
414
|
},
|
|
415
|
+
"omitted": {
|
|
416
|
+
"description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
|
|
417
|
+
"type": "string"
|
|
418
|
+
},
|
|
407
419
|
"tags": {
|
|
408
420
|
"description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
|
|
409
421
|
"type": "array",
|
|
@@ -676,6 +688,10 @@
|
|
|
676
688
|
"description": {
|
|
677
689
|
"type": "string"
|
|
678
690
|
},
|
|
691
|
+
"omitted": {
|
|
692
|
+
"description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
|
|
693
|
+
"type": "string"
|
|
694
|
+
},
|
|
679
695
|
"tags": {
|
|
680
696
|
"description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
|
|
681
697
|
"type": "array",
|
|
@@ -883,6 +899,10 @@
|
|
|
883
899
|
"description": {
|
|
884
900
|
"type": "string"
|
|
885
901
|
},
|
|
902
|
+
"omitted": {
|
|
903
|
+
"description": "Why the producer did not include its bytes, in an open vocabulary: too-large, not-found, not-allowed, over-budget, policy… Present on a metadata-only attachment, so a reader can tell \"left out\" from \"never recorded\"",
|
|
904
|
+
"type": "string"
|
|
905
|
+
},
|
|
886
906
|
"tags": {
|
|
887
907
|
"description": "Free labels of this one attachment, such as `demo`: shown as `#demo`, searchable, and normalized by `normalizeTag` (a leading `#` and letter case never matter). Unlike the tags of a check they say nothing about its result",
|
|
888
908
|
"type": "array",
|