@crvouga/atlas 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -1
- package/dist/reports/src/evidence-detail.js +6 -1
- package/dist/reports/src/evidence-types.d.ts +2 -0
- package/dist/reports/src/explorer-types.d.ts +19 -0
- package/dist/reports/src/latency.d.ts +204 -0
- package/dist/reports/src/latency.js +219 -0
- package/dist/reports/src/reliability.d.ts +181 -0
- package/dist/reports/src/reliability.js +294 -0
- package/dist/reports/src/report-index.js +1 -1
- package/dist/reports/src/reports-explorer-route.d.ts +19 -6
- package/dist/reports/src/reports-route.d.ts +19 -6
- package/dist/reports/src/test-catalog.d.ts +11 -3
- package/dist/reports/src/test-catalog.js +12 -5
- package/dist/schema/src/api.d.ts +377 -0
- package/dist/schema/src/api.js +55 -3
- package/dist/schema/src/layer.d.ts +90 -0
- package/dist/schema/src/layer.js +20 -1
- package/dist/server/src/app.js +15 -0
- package/dist/server/src/llms.js +1 -0
- package/dist/server/src/openapi.js +25 -2
- package/docs/extending.md +1 -1
- package/docs/reference/http-api.md +48 -0
- package/docs/reference/reports.md +619 -1
- package/docs/reference/schema.md +39 -0
- package/docs/rich-evidence.md +43 -0
- package/docs/server.md +18 -0
- package/docs/ui.md +20 -1
- package/llms-full.txt +130 -2
- package/package.json +1 -1
- package/packages/schema/schemas/check-detail.schema.json +56 -1
- package/packages/schema/schemas/effective-delta.schema.json +86 -2
- package/packages/schema/schemas/effective-evidence.schema.json +86 -2
- package/packages/schema/schemas/evidence-page.schema.json +13 -0
- package/packages/schema/schemas/layer-rows.schema.json +73 -2
- package/packages/schema/schemas/layer.schema.json +112 -2
- package/packages/schema/schemas/publish-checks.schema.json +56 -1
- package/packages/schema/schemas/test-catalog.schema.json +102 -0
- package/packages/schema/schemas/test-history.schema.json +204 -0
- package/packages/schema/schemas/test-reliability.schema.json +629 -0
- package/packages/server/openapi.json +2354 -661
- package/packages/visualizer/src/evidence/check-detail.tsx +9 -0
- package/packages/visualizer/src/explorer/ProductLatency.tsx +214 -0
- package/packages/visualizer/src/explorer/Reliability.tsx +337 -0
- package/packages/visualizer/src/explorer/TestDetail.tsx +12 -2
- package/packages/visualizer/src/explorer/catalog.tsx +1 -1
- package/packages/visualizer/src/explorer/search.ts +36 -0
|
@@ -2145,6 +2145,8 @@ export type EvidenceSummary = {
|
|
|
2145
2145
|
readonly annotations: number;
|
|
2146
2146
|
/** How many tries were recorded when the check was retried. */
|
|
2147
2147
|
readonly attempts: number;
|
|
2148
|
+
/** The status of each recorded try, in the order they were made (its length is `attempts`). Absent when no try was recorded, and in reports written before this was recorded: a first try is then unknown, never assumed to have passed. */
|
|
2149
|
+
readonly attemptStatuses?: readonly EvidenceStatus[];
|
|
2148
2150
|
readonly links: number;
|
|
2149
2151
|
readonly tags: readonly string[];
|
|
2150
2152
|
/** The normalized tags of the attachments themselves (`#demo` is `demo`), sorted and distinct. Absent when no attachment is tagged. Unlike `tags` they are never inherited from a check or run. */
|
|
@@ -2608,6 +2610,22 @@ export type EvidenceMeasurement = {
|
|
|
2608
2610
|
readonly unit: string;
|
|
2609
2611
|
/** The allowance for `value`, in the same unit, when there is one. */
|
|
2610
2612
|
readonly limit?: number;
|
|
2613
|
+
/** What the number is, in an open vocabulary. `product-latency` is a user-visible response time between `from` and `to`; absent is just a number. */
|
|
2614
|
+
readonly kind?: string;
|
|
2615
|
+
/** The event the measured interval starts at. */
|
|
2616
|
+
readonly from?: string;
|
|
2617
|
+
/** The event the measured interval ends at. */
|
|
2618
|
+
readonly to?: string;
|
|
2619
|
+
/** The interval the name claims, when the measured one is only part of it. */
|
|
2620
|
+
readonly intended?: { readonly from?: string; readonly to?: string };
|
|
2621
|
+
/** How it was measured. */
|
|
2622
|
+
readonly source?: string;
|
|
2623
|
+
/** `observed` (default), `timeout`, `failed`; otherwise `value` is how long the observation waited, at least. */
|
|
2624
|
+
readonly outcome?: string;
|
|
2625
|
+
/** The version or method of the measurement. */
|
|
2626
|
+
readonly instrumentation?: string;
|
|
2627
|
+
/** What the observation is comparable with: `device`, `build`. */
|
|
2628
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
2611
2629
|
};
|
|
2612
2630
|
```
|
|
2613
2631
|
|
|
@@ -4073,6 +4091,335 @@ export function splitViewerTestId(
|
|
|
4073
4091
|
|
|
4074
4092
|
The inverse of {@link viewerTestId}: splits at the first `/`; null when either side is not a safe key segment.
|
|
4075
4093
|
|
|
4094
|
+
### Product latency
|
|
4095
|
+
|
|
4096
|
+
Source: `packages/reports/src/latency.ts`. Import: `@crvouga/atlas/reports/latency`
|
|
4097
|
+
|
|
4098
|
+
Tells how quickly a product responded apart from how long a test took. A test's `durationMs` includes fixture setup,
|
|
4099
|
+
deliberate waits, demo pacing, retries and cleanup, so it says nothing about a user's wait. Product latency is only
|
|
4100
|
+
what a producer measured and named as such: a measurement of `kind` `product-latency` between two named events
|
|
4101
|
+
(`from`, `to`), optionally with a budget (`limit`), an outcome (`observed`, `timeout`, `failed`), the instrumentation,
|
|
4102
|
+
and labels (`device`, `build`) in its `labels`. Pure functions, safe in a browser.
|
|
4103
|
+
|
|
4104
|
+
The rules, each of which keeps a number from looking healthier or more certain than the evidence is:
|
|
4105
|
+
|
|
4106
|
+
- **Only named measurements are latency.** A check's duration, a video's length, a recorded step and a number with no
|
|
4107
|
+
`kind` are never product latency. No latency recorded is unknown, not fast.
|
|
4108
|
+
- **Boundaries are part of the claim.** Without both `from` and `to`, nothing is claimed from the numbers. When
|
|
4109
|
+
`intended` names a longer interval than the one measured (speech end to TTS dispatch, claimed as microphone to
|
|
4110
|
+
audible response) the measurement is partial and says so.
|
|
4111
|
+
- **Percentiles need samples.** p50 needs {@link MIN_P50_SAMPLES} observations and p95 {@link MIN_P95_SAMPLES}; below
|
|
4112
|
+
that they are withheld with the reason, never shown as if supported.
|
|
4113
|
+
- **A timeout or failed observation is a sample.** Its value is how long it waited, so a lower bound: it counts
|
|
4114
|
+
in the sample, in the percentiles and as a budget breach, and the summary says the figures are lower bounds.
|
|
4115
|
+
- **Comparisons are of the same metric.** Different boundaries are not compared; a change of instrumentation,
|
|
4116
|
+
environment, budget or other condition between the two sides is disclosed beside the change.
|
|
4117
|
+
|
|
4118
|
+
It decides nothing: a latency, a budget breach and a comparison never change a status, a slot or a winner.
|
|
4119
|
+
|
|
4120
|
+
See docs/rich-evidence.md#product-latency
|
|
4121
|
+
|
|
4122
|
+
#### `PRODUCT_LATENCY`
|
|
4123
|
+
|
|
4124
|
+
```ts
|
|
4125
|
+
export const PRODUCT_LATENCY = …
|
|
4126
|
+
```
|
|
4127
|
+
|
|
4128
|
+
The `kind` of a measurement that is a user-visible product latency.
|
|
4129
|
+
|
|
4130
|
+
#### `MIN_P50_SAMPLES`
|
|
4131
|
+
|
|
4132
|
+
```ts
|
|
4133
|
+
export const MIN_P50_SAMPLES = …
|
|
4134
|
+
```
|
|
4135
|
+
|
|
4136
|
+
Observations needed before a p50 is shown.
|
|
4137
|
+
|
|
4138
|
+
#### `MIN_P95_SAMPLES`
|
|
4139
|
+
|
|
4140
|
+
```ts
|
|
4141
|
+
export const MIN_P95_SAMPLES = …
|
|
4142
|
+
```
|
|
4143
|
+
|
|
4144
|
+
Observations needed before a p95 is shown.
|
|
4145
|
+
|
|
4146
|
+
#### `isProductLatency`
|
|
4147
|
+
|
|
4148
|
+
```ts
|
|
4149
|
+
export function isProductLatency(measurement: Pick<EvidenceMeasurement, 'kind'>): boolean
|
|
4150
|
+
```
|
|
4151
|
+
|
|
4152
|
+
Whether a measurement is a product latency: only the producer's `kind` makes it one. A name, a unit or a value never does.
|
|
4153
|
+
|
|
4154
|
+
#### `productLatency`
|
|
4155
|
+
|
|
4156
|
+
```ts
|
|
4157
|
+
export function productLatency(measurements: readonly EvidenceMeasurement[] | undefined): readonly EvidenceMeasurement[] | undefined
|
|
4158
|
+
```
|
|
4159
|
+
|
|
4160
|
+
The product-latency measurements of a check, or undefined when it has none (so a result with no latency carries
|
|
4161
|
+
nothing). Measurements that are not well formed (no finite value, no name) are left out.
|
|
4162
|
+
|
|
4163
|
+
#### `BoundaryState`
|
|
4164
|
+
|
|
4165
|
+
```ts
|
|
4166
|
+
export type BoundaryState = 'declared' | 'partial' | 'undeclared';
|
|
4167
|
+
```
|
|
4168
|
+
|
|
4169
|
+
How well the ends of a measurement are known: `declared` (both named), `partial` (it stops short of, or starts after, what it claims) or `undeclared`.
|
|
4170
|
+
|
|
4171
|
+
#### `Boundary`
|
|
4172
|
+
|
|
4173
|
+
```ts
|
|
4174
|
+
export type Boundary = {
|
|
4175
|
+
readonly state: BoundaryState;
|
|
4176
|
+
readonly from?: string;
|
|
4177
|
+
readonly to?: string;
|
|
4178
|
+
/** The interval the measurement's name claims, when it is longer than what was measured. */
|
|
4179
|
+
readonly intended?: string;
|
|
4180
|
+
/** `submit pressed to confirmation visible`, `to confirmation visible (start not declared)`. */
|
|
4181
|
+
readonly label: string;
|
|
4182
|
+
};
|
|
4183
|
+
```
|
|
4184
|
+
|
|
4185
|
+
The ends of a measurement, in words, and whether they support a claim about the user's wait.
|
|
4186
|
+
|
|
4187
|
+
#### `boundaryOf`
|
|
4188
|
+
|
|
4189
|
+
```ts
|
|
4190
|
+
export function boundaryOf(measurement: Pick<EvidenceMeasurement, 'from' | 'to' | 'intended'>): Boundary
|
|
4191
|
+
```
|
|
4192
|
+
|
|
4193
|
+
What a measurement spans. `undeclared` when either end is missing: the numbers are shown and no product claim is made.
|
|
4194
|
+
`partial` when `intended` names a different end than the measured one: the interval is the measured one, never the intended.
|
|
4195
|
+
|
|
4196
|
+
#### `ObservationOutcome`
|
|
4197
|
+
|
|
4198
|
+
```ts
|
|
4199
|
+
export type ObservationOutcome = 'observed' | 'timeout' | 'failed' | 'unclear';
|
|
4200
|
+
```
|
|
4201
|
+
|
|
4202
|
+
What happened to one observation: `observed`, or it `timeout`ed or `failed`, or the producer said something Atlas cannot read (`unclear`).
|
|
4203
|
+
|
|
4204
|
+
#### `LatencyEntry`
|
|
4205
|
+
|
|
4206
|
+
```ts
|
|
4207
|
+
export type LatencyEntry = {
|
|
4208
|
+
/** When it was recorded, epoch milliseconds. */
|
|
4209
|
+
readonly at: number;
|
|
4210
|
+
readonly environment: string | null;
|
|
4211
|
+
readonly parameters: Readonly<Record<string, string>>;
|
|
4212
|
+
readonly sha: string | null;
|
|
4213
|
+
readonly layer: string;
|
|
4214
|
+
readonly run?: string;
|
|
4215
|
+
readonly latency?: readonly EvidenceMeasurement[];
|
|
4216
|
+
readonly evidence?: { readonly labels: Readonly<Record<string, string>> };
|
|
4217
|
+
};
|
|
4218
|
+
```
|
|
4219
|
+
|
|
4220
|
+
One recorded result, as much of it as this module reads. A `TestResultEntry` is one.
|
|
4221
|
+
|
|
4222
|
+
#### `LatencyObservation`
|
|
4223
|
+
|
|
4224
|
+
```ts
|
|
4225
|
+
export type LatencyObservation = {
|
|
4226
|
+
readonly name: string;
|
|
4227
|
+
readonly unit: string;
|
|
4228
|
+
readonly value: number;
|
|
4229
|
+
readonly limit?: number;
|
|
4230
|
+
readonly outcome: ObservationOutcome;
|
|
4231
|
+
readonly boundary: Boundary;
|
|
4232
|
+
readonly source?: string;
|
|
4233
|
+
readonly instrumentation?: string;
|
|
4234
|
+
readonly cohort: Readonly<Record<string, string>>;
|
|
4235
|
+
readonly at: number;
|
|
4236
|
+
readonly layer: string;
|
|
4237
|
+
readonly environment: string | null;
|
|
4238
|
+
readonly parameters: Readonly<Record<string, string>>;
|
|
4239
|
+
readonly labels: Readonly<Record<string, string>>;
|
|
4240
|
+
readonly sha: string | null;
|
|
4241
|
+
};
|
|
4242
|
+
```
|
|
4243
|
+
|
|
4244
|
+
One observation of a latency. Time units are converted to milliseconds.
|
|
4245
|
+
|
|
4246
|
+
#### `latencyObservations`
|
|
4247
|
+
|
|
4248
|
+
```ts
|
|
4249
|
+
export function latencyObservations(entries: readonly LatencyEntry[]): LatencyObservation[]
|
|
4250
|
+
```
|
|
4251
|
+
|
|
4252
|
+
The observations in recorded results, in the order given. Entries with no latency contribute none.
|
|
4253
|
+
|
|
4254
|
+
#### `LatencyGrouping`
|
|
4255
|
+
|
|
4256
|
+
```ts
|
|
4257
|
+
export type LatencyGrouping = 'none' | 'device' | 'build' | 'window';
|
|
4258
|
+
```
|
|
4259
|
+
|
|
4260
|
+
How observations are grouped: not at all, by `device`, by `build`, or by the UTC `window` (day) they were recorded in.
|
|
4261
|
+
|
|
4262
|
+
#### `LATENCY_GROUPINGS`
|
|
4263
|
+
|
|
4264
|
+
```ts
|
|
4265
|
+
export const LATENCY_GROUPINGS: readonly LatencyGrouping[]
|
|
4266
|
+
```
|
|
4267
|
+
|
|
4268
|
+
The groupings, in the order a control offers them.
|
|
4269
|
+
|
|
4270
|
+
#### `groupOf`
|
|
4271
|
+
|
|
4272
|
+
```ts
|
|
4273
|
+
export function groupOf(observation: LatencyObservation, by: LatencyGrouping): string
|
|
4274
|
+
```
|
|
4275
|
+
|
|
4276
|
+
The group an observation falls in. `device` is the measurement's cohort, else the result's label, parameter or
|
|
4277
|
+
environment; `build` is the cohort or label, else the commit. One that names nothing is `unknown device` or
|
|
4278
|
+
`unknown build`: it is never merged into a named group.
|
|
4279
|
+
|
|
4280
|
+
#### `LatencyMetric`
|
|
4281
|
+
|
|
4282
|
+
```ts
|
|
4283
|
+
export type LatencyMetric = {
|
|
4284
|
+
/** Equal for observations of the same name, unit and boundaries. */
|
|
4285
|
+
readonly key: string;
|
|
4286
|
+
readonly name: string;
|
|
4287
|
+
readonly unit: string;
|
|
4288
|
+
readonly boundary: Boundary;
|
|
4289
|
+
};
|
|
4290
|
+
```
|
|
4291
|
+
|
|
4292
|
+
A measured interval: the name, the unit and the ends. Observations of one metric are comparable; of two metrics they are not.
|
|
4293
|
+
|
|
4294
|
+
#### `LatencySummary`
|
|
4295
|
+
|
|
4296
|
+
```ts
|
|
4297
|
+
export type LatencySummary = {
|
|
4298
|
+
readonly metric: LatencyMetric;
|
|
4299
|
+
/** The group (a device, a build, a day, or all). */
|
|
4300
|
+
readonly group: string;
|
|
4301
|
+
/** Every observation, including those that timed out or failed. */
|
|
4302
|
+
readonly samples: number;
|
|
4303
|
+
/** Observations that completed. */
|
|
4304
|
+
readonly observed: number;
|
|
4305
|
+
readonly timeouts: number;
|
|
4306
|
+
readonly failures: number;
|
|
4307
|
+
/** Observations over their budget, plus every timeout or failure when a budget is set. */
|
|
4308
|
+
readonly breaches: number;
|
|
4309
|
+
/** The budget, from the newest observation that has one. */
|
|
4310
|
+
readonly budget?: number;
|
|
4311
|
+
/** The median, or undefined with the reason in `withheld`. */
|
|
4312
|
+
readonly p50?: number;
|
|
4313
|
+
readonly p95?: number;
|
|
4314
|
+
/** Why a figure is not shown, in words. */
|
|
4315
|
+
readonly withheld: readonly string[];
|
|
4316
|
+
/** True when a timeout or failure's waiting time is in the figures: they are lower bounds. */
|
|
4317
|
+
readonly lowerBound: boolean;
|
|
4318
|
+
readonly max: number;
|
|
4319
|
+
/** The newest observation's value. */
|
|
4320
|
+
readonly last: number;
|
|
4321
|
+
readonly lastAt: number;
|
|
4322
|
+
/** Whether the numbers support a statement about the user's wait: false when the boundaries are not declared. */
|
|
4323
|
+
readonly claim: boolean;
|
|
4324
|
+
/** What the reader must not assume, in words: partial interval, missing boundaries, a single observation. */
|
|
4325
|
+
readonly gaps: readonly string[];
|
|
4326
|
+
readonly instrumentation: readonly string[];
|
|
4327
|
+
readonly environments: readonly string[];
|
|
4328
|
+
/** Other facts the observations differ by: cohort entries and parameters, as `key=value`. */
|
|
4329
|
+
readonly conditions: readonly string[];
|
|
4330
|
+
readonly budgets: readonly number[];
|
|
4331
|
+
};
|
|
4332
|
+
```
|
|
4333
|
+
|
|
4334
|
+
What the observations of one metric in one group show, and what they cannot.
|
|
4335
|
+
|
|
4336
|
+
#### `LatencyReview`
|
|
4337
|
+
|
|
4338
|
+
```ts
|
|
4339
|
+
export type LatencyReview = {
|
|
4340
|
+
readonly by: LatencyGrouping;
|
|
4341
|
+
readonly metrics: readonly { readonly metric: LatencyMetric; readonly groups: readonly LatencySummary[] }[];
|
|
4342
|
+
/** Results that recorded no product latency. They are unknown, not fast. */
|
|
4343
|
+
readonly unmeasured: number;
|
|
4344
|
+
/** Results that did. */
|
|
4345
|
+
readonly measured: number;
|
|
4346
|
+
};
|
|
4347
|
+
```
|
|
4348
|
+
|
|
4349
|
+
The metrics of a result set, each with its summary in every group, groups newest first.
|
|
4350
|
+
|
|
4351
|
+
#### `latencyReview`
|
|
4352
|
+
|
|
4353
|
+
```ts
|
|
4354
|
+
export function latencyReview(entries: readonly LatencyEntry[], by: LatencyGrouping = 'none'): LatencyReview
|
|
4355
|
+
```
|
|
4356
|
+
|
|
4357
|
+
Reads recorded results (any order) into a review: for every metric, a summary in every group. A result with no
|
|
4358
|
+
product latency counts only in `unmeasured`. Metrics are listed by name; groups newest first.
|
|
4359
|
+
|
|
4360
|
+
- `by`: How observations are grouped. Default `none`.
|
|
4361
|
+
|
|
4362
|
+
**Example**
|
|
4363
|
+
|
|
4364
|
+
```ts
|
|
4365
|
+
const review = latencyReview(history.history, 'build');
|
|
4366
|
+
review.metrics[0]?.groups[0]?.p95; // undefined until 20 observations of that build
|
|
4367
|
+
```
|
|
4368
|
+
|
|
4369
|
+
#### `LatencyDelta`
|
|
4370
|
+
|
|
4371
|
+
```ts
|
|
4372
|
+
export type LatencyDelta = {
|
|
4373
|
+
readonly baseline: number;
|
|
4374
|
+
readonly candidate: number;
|
|
4375
|
+
/** Candidate minus baseline, in the metric's unit. Positive is slower. */
|
|
4376
|
+
readonly absolute: number;
|
|
4377
|
+
/** The absolute change over the baseline, or null when the baseline is zero. */
|
|
4378
|
+
readonly relative: number | null;
|
|
4379
|
+
};
|
|
4380
|
+
```
|
|
4381
|
+
|
|
4382
|
+
A change of one figure from the baseline to the candidate.
|
|
4383
|
+
|
|
4384
|
+
#### `LatencyChange`
|
|
4385
|
+
|
|
4386
|
+
```ts
|
|
4387
|
+
export type LatencyChange = {
|
|
4388
|
+
/** Whether a change is stated at all. */
|
|
4389
|
+
readonly comparable: boolean;
|
|
4390
|
+
/** `p50` or `p95` when both sides support it; `last` compares the newest observation of each, which is not a percentile. */
|
|
4391
|
+
readonly basis?: 'p50' | 'p95' | 'last';
|
|
4392
|
+
readonly p50?: LatencyDelta;
|
|
4393
|
+
readonly p95?: LatencyDelta;
|
|
4394
|
+
readonly last?: LatencyDelta;
|
|
4395
|
+
/** Why no change is stated. */
|
|
4396
|
+
readonly reasons: readonly string[];
|
|
4397
|
+
/** Differences in how the two sides were measured or run. A change should be read with them. */
|
|
4398
|
+
readonly disclosures: readonly string[];
|
|
4399
|
+
};
|
|
4400
|
+
```
|
|
4401
|
+
|
|
4402
|
+
What changed between two summaries, or why that cannot be said.
|
|
4403
|
+
|
|
4404
|
+
#### `compareLatency`
|
|
4405
|
+
|
|
4406
|
+
```ts
|
|
4407
|
+
export function compareLatency(baseline: LatencySummary, candidate: LatencySummary): LatencyChange
|
|
4408
|
+
```
|
|
4409
|
+
|
|
4410
|
+
The change from a baseline to a candidate of the same metric. Different metrics (names, units or boundaries) are not
|
|
4411
|
+
compared and the reason is given. When both sides support p50 (and p95) the change is of those; otherwise only the
|
|
4412
|
+
newest observation of each is compared, labelled as such. Changes of instrumentation, environments, other
|
|
4413
|
+
conditions, budget and partial or lower-bound figures are listed in `disclosures`, never hidden.
|
|
4414
|
+
|
|
4415
|
+
#### `formatLatency`
|
|
4416
|
+
|
|
4417
|
+
```ts
|
|
4418
|
+
export function formatLatency(value: number | undefined, unit: string): string
|
|
4419
|
+
```
|
|
4420
|
+
|
|
4421
|
+
`500 ms`, `1.2 s`: a latency in the largest unit that keeps it readable. Non-time units print as given.
|
|
4422
|
+
|
|
4076
4423
|
### Environment axis of a layer
|
|
4077
4424
|
|
|
4078
4425
|
Source: `packages/reports/src/layer-axes.ts`. Import: `@crvouga/atlas/reports/layer-axes`
|
|
@@ -5992,6 +6339,273 @@ skipped or unresolved; otherwise `unknown` when a contributing layer has no rece
|
|
|
5992
6339
|
report with no results at all is unknown too); otherwise `complete` when something was expected and `none-expected` when
|
|
5993
6340
|
nothing was. Gaps are sorted so the output never depends on read order.
|
|
5994
6341
|
|
|
6342
|
+
### Reliability of a test
|
|
6343
|
+
|
|
6344
|
+
Source: `packages/reports/src/reliability.ts`. Import: `@crvouga/atlas/reports/reliability`
|
|
6345
|
+
|
|
6346
|
+
How often a test passed, counted so the number can be trusted or doubted. A pass rate over raw history is easy to
|
|
6347
|
+
overinterpret: one execution is published as several snapshots, a retry is not a second trial, desktop and phone are
|
|
6348
|
+
different questions, and an earlier version of a behavior says nothing about the current one. This module answers
|
|
6349
|
+
from the recorded results of one test and a {@link ReliabilityScope}, and states what it counted:
|
|
6350
|
+
|
|
6351
|
+
- **Independent executions.** The snapshots one run recorded for one producer, environment, parameter set and kind
|
|
6352
|
+
(running, finished, enriched) are one execution; its last word counts once, with the tries it recorded. Raw
|
|
6353
|
+
snapshots stay inspectable (`snapshots`) and are never weighted as new trials.
|
|
6354
|
+
- **First try and eventual.** Where tries were recorded, `firstAttempt` counts executions whose first try passed and
|
|
6355
|
+
`rate` counts those that passed in the end. An execution that recorded no tries is not assumed to have passed first
|
|
6356
|
+
time: it is left out of `firstAttempt` and counted in `unrecordedAttempts`.
|
|
6357
|
+
- **Scope.** Environment, producer, parameters, branch, commit, spec revision and a time window select the executions;
|
|
6358
|
+
the rest are shown as `outsideScope`, never mixed in. By default only the newest recorded revision of the spec
|
|
6359
|
+
counts, and earlier ones are shown as `historical` context.
|
|
6360
|
+
- **What is not a trial.** Only `passed` and `failed` executions are in the denominator. `not-run` and unfinished
|
|
6361
|
+
executions are excluded and counted by name; nothing missing becomes a pass.
|
|
6362
|
+
- **Limits, in words.** `notes` say when the sample is too small to establish anything, when environments, producers
|
|
6363
|
+
or branches are pooled, when failed cases were published again, and when history is bounded. A rate is never a
|
|
6364
|
+
production failure probability: the checks are a selection.
|
|
6365
|
+
|
|
6366
|
+
It only reads results; it never changes a status, a slot or a winner.
|
|
6367
|
+
|
|
6368
|
+
See docs/server.md#reading-the-report-as-an-agent
|
|
6369
|
+
|
|
6370
|
+
#### `RELIABILITY_VERSION`
|
|
6371
|
+
|
|
6372
|
+
```ts
|
|
6373
|
+
export const RELIABILITY_VERSION = …
|
|
6374
|
+
```
|
|
6375
|
+
|
|
6376
|
+
Version of the reliability document.
|
|
6377
|
+
|
|
6378
|
+
#### `MIN_EXECUTIONS`
|
|
6379
|
+
|
|
6380
|
+
```ts
|
|
6381
|
+
export const MIN_EXECUTIONS = …
|
|
6382
|
+
```
|
|
6383
|
+
|
|
6384
|
+
Completed executions below which a rate is shown but not treated as established.
|
|
6385
|
+
|
|
6386
|
+
#### `ReliabilityScope`
|
|
6387
|
+
|
|
6388
|
+
```ts
|
|
6389
|
+
export type ReliabilityScope = {
|
|
6390
|
+
/** Environments or devices (`phone`, `desktop-web`). Executions in others never count towards these. */
|
|
6391
|
+
readonly environment?: readonly string[];
|
|
6392
|
+
readonly producer?: readonly string[];
|
|
6393
|
+
/** `key=value` parameter choices (`device=small`). */
|
|
6394
|
+
readonly parameter?: readonly string[];
|
|
6395
|
+
readonly branch?: readonly string[];
|
|
6396
|
+
/** Commits: a full id, or a prefix of at least seven characters. */
|
|
6397
|
+
readonly sha?: readonly string[];
|
|
6398
|
+
/** Only executions recorded against these spec fingerprints. Overrides `revision`. */
|
|
6399
|
+
readonly specRevision?: readonly string[];
|
|
6400
|
+
/** `current` (the default): only the newest spec revision recorded in the rest of the scope, the older ones shown as context. `any`: every revision, pooled. */
|
|
6401
|
+
readonly revision?: 'current' | 'any';
|
|
6402
|
+
/** Only executions at or after this time, epoch ms. */
|
|
6403
|
+
readonly since?: number;
|
|
6404
|
+
/** Only executions at or before this time, epoch ms. */
|
|
6405
|
+
readonly until?: number;
|
|
6406
|
+
};
|
|
6407
|
+
```
|
|
6408
|
+
|
|
6409
|
+
What a reliability figure is about. Every field is optional; nothing asked is everything the history holds (of the current spec revision).
|
|
6410
|
+
|
|
6411
|
+
#### `ReliabilityExclusion`
|
|
6412
|
+
|
|
6413
|
+
```ts
|
|
6414
|
+
export type ReliabilityExclusion = 'not-run' | 'unfinished' | 'unknown-status' | 'historical-revision' | 'revision-not-recorded';
|
|
6415
|
+
```
|
|
6416
|
+
|
|
6417
|
+
Why an execution is not in the denominator.
|
|
6418
|
+
|
|
6419
|
+
#### `ReliabilityExecution`
|
|
6420
|
+
|
|
6421
|
+
```ts
|
|
6422
|
+
export type ReliabilityExecution = {
|
|
6423
|
+
/** The key of the run it belongs to (see `runKey`). */
|
|
6424
|
+
readonly run: string;
|
|
6425
|
+
/** When its last word was recorded, epoch ms. */
|
|
6426
|
+
readonly at: number;
|
|
6427
|
+
/** Its last word. */
|
|
6428
|
+
readonly status: string;
|
|
6429
|
+
/** True when it is in the denominator. */
|
|
6430
|
+
readonly counted: boolean;
|
|
6431
|
+
/** Why it is not, when it is not. */
|
|
6432
|
+
readonly excluded?: ReliabilityExclusion;
|
|
6433
|
+
readonly environment: string | null;
|
|
6434
|
+
readonly producer: string;
|
|
6435
|
+
readonly kind: string;
|
|
6436
|
+
readonly parameters: Readonly<Record<string, string>>;
|
|
6437
|
+
readonly branch: string | null;
|
|
6438
|
+
readonly sha: string | null;
|
|
6439
|
+
readonly runUrl: string | null;
|
|
6440
|
+
/** The layer holding its last word (open it with `/api/reports/evidence/detail/{backend}/{layer}?check=`). */
|
|
6441
|
+
readonly layer: string;
|
|
6442
|
+
readonly backend: string;
|
|
6443
|
+
readonly durationMs: number | null;
|
|
6444
|
+
readonly specRevision?: string;
|
|
6445
|
+
/** How many raw snapshots (layers) recorded it. They are one execution, not that many trials. */
|
|
6446
|
+
readonly snapshots: number;
|
|
6447
|
+
/** The status of each recorded try, in order. Absent when no try was recorded. */
|
|
6448
|
+
readonly attempts?: readonly string[];
|
|
6449
|
+
/** The first try's status, when tries were recorded. */
|
|
6450
|
+
readonly firstAttempt?: string;
|
|
6451
|
+
/** True when a failed snapshot of this execution was followed by a later one: it was published again after a failure. */
|
|
6452
|
+
readonly republished: boolean;
|
|
6453
|
+
};
|
|
6454
|
+
```
|
|
6455
|
+
|
|
6456
|
+
One independent execution: the last word of one run on one producer, environment, parameter set and kind.
|
|
6457
|
+
|
|
6458
|
+
#### `ReliabilityRate`
|
|
6459
|
+
|
|
6460
|
+
```ts
|
|
6461
|
+
export type ReliabilityRate = {
|
|
6462
|
+
/** Independent executions that passed or failed. */
|
|
6463
|
+
readonly executions: number;
|
|
6464
|
+
readonly passed: number;
|
|
6465
|
+
readonly failed: number;
|
|
6466
|
+
/** passed / executions, or null with none. This is the eventual success rate. */
|
|
6467
|
+
readonly rate: number | null;
|
|
6468
|
+
};
|
|
6469
|
+
```
|
|
6470
|
+
|
|
6471
|
+
Counts and rates over a set of executions.
|
|
6472
|
+
|
|
6473
|
+
#### `ReliabilityInterval`
|
|
6474
|
+
|
|
6475
|
+
```ts
|
|
6476
|
+
export type ReliabilityInterval = {
|
|
6477
|
+
readonly method: 'wilson';
|
|
6478
|
+
readonly level: number;
|
|
6479
|
+
readonly lower: number;
|
|
6480
|
+
readonly upper: number;
|
|
6481
|
+
/** What must hold for the interval to mean anything. */
|
|
6482
|
+
readonly assumptions: string;
|
|
6483
|
+
};
|
|
6484
|
+
```
|
|
6485
|
+
|
|
6486
|
+
A confidence interval and what it assumes.
|
|
6487
|
+
|
|
6488
|
+
#### `ReliabilityNote`
|
|
6489
|
+
|
|
6490
|
+
```ts
|
|
6491
|
+
export type ReliabilityNote = {
|
|
6492
|
+
readonly code: string;
|
|
6493
|
+
/** `warning`: the figure should not be read as representative without this in mind. */
|
|
6494
|
+
readonly severity: 'warning' | 'info';
|
|
6495
|
+
readonly message: string;
|
|
6496
|
+
};
|
|
6497
|
+
```
|
|
6498
|
+
|
|
6499
|
+
A reason to doubt or qualify the figure. An open set: treat a code you do not know as informational.
|
|
6500
|
+
|
|
6501
|
+
#### `ReliabilityCohort`
|
|
6502
|
+
|
|
6503
|
+
```ts
|
|
6504
|
+
export type ReliabilityCohort = ReliabilityRate & {
|
|
6505
|
+
readonly environment: string | null;
|
|
6506
|
+
readonly producer: string;
|
|
6507
|
+
readonly parameters: Readonly<Record<string, string>>;
|
|
6508
|
+
};
|
|
6509
|
+
```
|
|
6510
|
+
|
|
6511
|
+
One environment, producer and parameter set among the counted executions, with its own rate.
|
|
6512
|
+
|
|
6513
|
+
#### `ReliabilityDocument`
|
|
6514
|
+
|
|
6515
|
+
```ts
|
|
6516
|
+
export type ReliabilityDocument = {
|
|
6517
|
+
readonly schemaVersion: typeof RELIABILITY_VERSION;
|
|
6518
|
+
readonly key: string;
|
|
6519
|
+
readonly scope: ReliabilityScope;
|
|
6520
|
+
/** Completed executions below which the sample is `insufficient`. */
|
|
6521
|
+
readonly minimumExecutions: number;
|
|
6522
|
+
/** Count only: `none` without an independent execution, `insufficient` below the minimum, `sufficient` otherwise. Says nothing about representativeness; read `notes`. */
|
|
6523
|
+
readonly sample: 'none' | 'insufficient' | 'sufficient';
|
|
6524
|
+
/** Eventual success over the counted executions. */
|
|
6525
|
+
readonly eventual: ReliabilityRate;
|
|
6526
|
+
readonly interval: ReliabilityInterval | null;
|
|
6527
|
+
/** Executions whose first try is known (tries were recorded), and how many of those passed first time. */
|
|
6528
|
+
readonly firstAttempt: ReliabilityRate;
|
|
6529
|
+
/** Counted executions that recorded no tries: unknown first try, left out of `firstAttempt`. */
|
|
6530
|
+
readonly unrecordedAttempts: number;
|
|
6531
|
+
readonly attempts: {
|
|
6532
|
+
/** Tries recorded across the counted executions. */
|
|
6533
|
+
readonly total: number;
|
|
6534
|
+
/** Counted executions that needed more than one try. */
|
|
6535
|
+
readonly retried: number;
|
|
6536
|
+
/** Counted executions that failed first and passed in the end. */
|
|
6537
|
+
readonly recovered: number;
|
|
6538
|
+
};
|
|
6539
|
+
/** The revision `revision: 'current'` counted, or null when none is recorded. */
|
|
6540
|
+
readonly currentRevision: string | null;
|
|
6541
|
+
/** Earlier spec revisions, shown as context and counted nowhere above. */
|
|
6542
|
+
readonly historical: { readonly executions: number; readonly passed: number; readonly failed: number; readonly revisions: readonly { readonly specRevision: string; readonly executions: number }[] };
|
|
6543
|
+
/** What was left out, by name. */
|
|
6544
|
+
readonly excluded: Readonly<Record<ReliabilityExclusion, number>>;
|
|
6545
|
+
/** Executions the scope did not select. */
|
|
6546
|
+
readonly outsideScope: number;
|
|
6547
|
+
/** Raw recorded snapshots behind the counted executions. */
|
|
6548
|
+
readonly snapshots: number;
|
|
6549
|
+
/** Counted executions per environment, producer and parameter set. */
|
|
6550
|
+
readonly cohorts: readonly ReliabilityCohort[];
|
|
6551
|
+
/** What the scope could choose among: the values the history holds, with their independent executions. */
|
|
6552
|
+
readonly available: {
|
|
6553
|
+
readonly environments: readonly { readonly value: string; readonly count: number }[];
|
|
6554
|
+
readonly producers: readonly { readonly value: string; readonly count: number }[];
|
|
6555
|
+
readonly branches: readonly { readonly value: string; readonly count: number }[];
|
|
6556
|
+
…
|
|
6557
|
+
```
|
|
6558
|
+
|
|
6559
|
+
`GET /api/reports/tests/reliability`: how reliable a test is within a scope, and how that figure was counted.
|
|
6560
|
+
|
|
6561
|
+
#### `reliability`
|
|
6562
|
+
|
|
6563
|
+
```ts
|
|
6564
|
+
export function reliability(key: string, history: readonly TestResultEntry[], scope: ReliabilityScope = {}): ReliabilityDocument
|
|
6565
|
+
```
|
|
6566
|
+
|
|
6567
|
+
How reliable one test is inside a scope, from the recorded results of that test (`TestHistoryDocument.history`, any
|
|
6568
|
+
order). See the module comment for what is counted and what is left out.
|
|
6569
|
+
|
|
6570
|
+
- `key`: The test's key, echoed in the document.
|
|
6571
|
+
|
|
6572
|
+
- `history`: Every recorded result of the test the caller holds. When it holds as many as `HISTORY_PER_TEST` the
|
|
6573
|
+
document says the history is bounded.
|
|
6574
|
+
|
|
6575
|
+
- `scope`: What to count; see {@link ReliabilityScope}.
|
|
6576
|
+
|
|
6577
|
+
**Example**
|
|
6578
|
+
|
|
6579
|
+
```ts
|
|
6580
|
+
const document = reliability(test.key, history.history, { environment: ['phone'], since: Date.now() - 30 * 86_400_000 });
|
|
6581
|
+
if (document.sample !== 'sufficient') console.log(`only ${document.eventual.executions} executions`);
|
|
6582
|
+
```
|
|
6583
|
+
|
|
6584
|
+
#### `scopeText`
|
|
6585
|
+
|
|
6586
|
+
```ts
|
|
6587
|
+
export function scopeText(scope: ReliabilityScope): string
|
|
6588
|
+
```
|
|
6589
|
+
|
|
6590
|
+
The scope as a short phrase, or `all recorded executions`.
|
|
6591
|
+
|
|
6592
|
+
#### `reliabilityMarkdown`
|
|
6593
|
+
|
|
6594
|
+
```ts
|
|
6595
|
+
export function reliabilityMarkdown(document: ReliabilityDocument): string
|
|
6596
|
+
```
|
|
6597
|
+
|
|
6598
|
+
The reliability document as Markdown, for an agent or a terminal.
|
|
6599
|
+
|
|
6600
|
+
#### `parseReliabilityScope`
|
|
6601
|
+
|
|
6602
|
+
```ts
|
|
6603
|
+
export function parseReliabilityScope(params: URLSearchParams): ReliabilityScope
|
|
6604
|
+
```
|
|
6605
|
+
|
|
6606
|
+
The scope an address asks for: `environment`, `producer`, `parameter`, `branch`, `sha` and `specRevision` (comma-separated
|
|
6607
|
+
lists), `revision` (`any`, else current) and `since` and `until` (epoch milliseconds, or a date).
|
|
6608
|
+
|
|
5995
6609
|
### Remote commit report
|
|
5996
6610
|
|
|
5997
6611
|
Source: `packages/reports/src/remote-report.ts`. Import: `@crvouga/atlas/reports/remote-report`
|
|
@@ -8316,8 +8930,12 @@ export type TestResultEntry = {
|
|
|
8316
8930
|
readonly backend: string;
|
|
8317
8931
|
/** Why it has its status: the first 500 characters of the check's detail. */
|
|
8318
8932
|
readonly detail: string;
|
|
8933
|
+
/** 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". */
|
|
8934
|
+
readonly specRevision?: string;
|
|
8319
8935
|
/** What it carried beyond its outcome (attachments, steps, retries, tags, labels), when anything. */
|
|
8320
8936
|
readonly evidence?: EvidenceSummary;
|
|
8937
|
+
/** 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). */
|
|
8938
|
+
readonly latency?: readonly EvidenceMeasurement[];
|
|
8321
8939
|
};
|
|
8322
8940
|
```
|
|
8323
8941
|
|
|
@@ -8832,7 +9450,7 @@ const catalog = testCatalog(catalogModel(layers, { history }));
|
|
|
8832
9450
|
- `get specs(): RecordedSpecs { return this.#specs; }` — The specs the added layers recorded.
|
|
8833
9451
|
- `tests(): readonly { readonly key: string; readonly id: string; readonly specPath: string | null }[]` — Every test the history holds a result for (keyed as `TestSummary.key`), including tests the report no longer has a current result for.
|
|
8834
9452
|
- `get layers(): number { return this.#seen.size; }` — How many layers were added.
|
|
8835
|
-
- `add(layer: LayerHeader, rows: readonly LayerRow[], specs: readonly SpecEntry[] = [], backend = layer.backend): void` — Takes in one layer: its header, its rows, the specs it recorded and the backend it was read from. A layer id already added is ignored.
|
|
9453
|
+
- `add(layer: LayerHeader, rows: readonly LayerRow[], specs: readonly SpecEntry[] = [], backend = layer.backend, fingerprints: Readonly<Record<string, string>> = {}): void` — Takes in one layer: its header, its rows, the specs it recorded and the backend it was read from. A layer id already added is ignored. `fingerprints` (spec path to the fingerprint the layer recorded, as `LayerRows.fingerprints`) gives each result its `specRevision`; without it no result has one.
|
|
8836
9454
|
- `of(key: string): readonly TestResultEntry[]` — The recorded results of one test (by `TestSummary.key`), newest first. Empty when none.
|
|
8837
9455
|
- `get runCount(): number { return this.#runs.size; }` — How many runs it knows of.
|
|
8838
9456
|
- `runs(): RunSummary[]` — Every run, newest first.
|