@crvouga/atlas 1.12.0 → 1.13.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 +7 -1
- package/dist/critics/src/contract.d.ts +113 -0
- package/dist/critics/src/contract.js +48 -0
- package/dist/critics/src/credentials.d.ts +72 -0
- package/dist/critics/src/credentials.js +136 -0
- package/dist/critics/src/critique.d.ts +128 -0
- package/dist/critics/src/critique.js +163 -0
- package/dist/critics/src/index.d.ts +21 -0
- package/dist/critics/src/index.js +21 -0
- package/dist/critics/src/judging.d.ts +153 -0
- package/dist/critics/src/judging.js +145 -0
- package/dist/critics/src/publish.d.ts +23 -0
- package/dist/critics/src/publish.js +21 -0
- package/dist/critics/src/runner.d.ts +104 -0
- package/dist/critics/src/runner.js +202 -0
- package/dist/critics/src/scope.d.ts +8 -0
- package/dist/critics/src/scope.js +26 -0
- package/dist/reports/src/compose.d.ts +2 -2
- package/dist/reports/src/compose.js +6 -5
- package/dist/reports/src/evidence-view.d.ts +2 -1
- package/dist/reports/src/evidence-view.js +5 -2
- package/dist/reports/src/layer-merge.d.ts +11 -3
- package/dist/reports/src/layer-merge.js +14 -3
- package/dist/reports/src/report-status.d.ts +1 -1
- package/dist/reports/src/report-status.js +6 -4
- package/dist/schema/src/api.d.ts +1 -0
- package/dist/schema/src/api.js +1 -1
- package/dist/schema/src/critique.d.ts +144 -0
- package/dist/schema/src/critique.js +96 -0
- package/dist/schema/src/index.d.ts +2 -1
- package/dist/schema/src/index.js +2 -1
- package/dist/schema/src/server-config.d.ts +7 -1
- package/dist/schema/src/server-config.js +6 -3
- package/dist/server/src/auth.d.ts +1 -1
- package/dist/server/src/auth.js +2 -2
- package/dist/server/src/config.js +2 -2
- package/dist/server/src/openapi.d.ts +3 -3
- package/dist/server/src/openapi.js +7 -4
- package/dist/server/src/store-api.js +39 -1
- package/docs/agents.md +1 -0
- package/docs/critics.md +190 -0
- package/docs/extending.md +1 -0
- package/docs/reference/critics.md +840 -0
- package/docs/reference/http-api.md +6 -2
- package/docs/reference/reports.md +20 -7
- package/docs/reference/schema.md +119 -2
- package/docs/reference/server.md +3 -3
- package/docs/report-contract.md +4 -0
- package/docs/server.md +6 -4
- package/llms-full.txt +211 -6
- package/llms.txt +2 -0
- package/package.json +7 -2
- package/packages/critics/README.md +18 -0
- package/packages/schema/schemas/critique.schema.json +254 -0
- package/packages/schema/schemas/server-config.schema.json +2 -1
- package/packages/schema/schemas/server-info.schema.json +1 -0
- package/packages/server/openapi.json +7 -6
|
@@ -414,7 +414,7 @@ Responses: `200` It exists.; `401` No token, or an unknown one. Send `Authorizat
|
|
|
414
414
|
|
|
415
415
|
### Upload an object
|
|
416
416
|
|
|
417
|
-
`PUT /api/store/{key}` · role:
|
|
417
|
+
`PUT /api/store/{key}` · role: critic
|
|
418
418
|
|
|
419
419
|
The write half of the `ObjectStore` interface. The server checks what it can from the bytes in hand and says no to what would corrupt the report for everyone:
|
|
420
420
|
|
|
@@ -426,6 +426,8 @@ The write half of the `ObjectStore` interface. The server checks what it can fro
|
|
|
426
426
|
|
|
427
427
|
Upload in dependency order: blobs, then the manifest, then the catalog entry. `atlas report publish`, `atlas report stream` and `httpStore` do. Needs a `Bearer` token of role `publish` or `admin`; a browser's sign-in box is refused.
|
|
428
428
|
|
|
429
|
+
A token of role `critic` may upload only what a critic publishes: blobs, and layers (and their catalog entries) whose `producer` is `critic.<name>` and whose parts are all of kind `critique`. Anything else is `403` with the rule it broke, and nothing is stored.
|
|
430
|
+
|
|
429
431
|
- `key` (path, required): An object key under `layers/`, such as `layers/manifests/<id>.json`. It contains slashes, which are not percent-encoded; every other character is.
|
|
430
432
|
- `content-type` (header): Stored with the object and returned when it is read.
|
|
431
433
|
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
@@ -434,7 +436,7 @@ Responses: `200` Already there with the same bytes.; `201` Stored.; `401` No tok
|
|
|
434
436
|
|
|
435
437
|
### Upload an object
|
|
436
438
|
|
|
437
|
-
`PUT /api/projects/{project}/store/{key}` · role:
|
|
439
|
+
`PUT /api/projects/{project}/store/{key}` · role: critic
|
|
438
440
|
|
|
439
441
|
The write half of the `ObjectStore` interface. The server checks what it can from the bytes in hand and says no to what would corrupt the report for everyone:
|
|
440
442
|
|
|
@@ -446,6 +448,8 @@ The write half of the `ObjectStore` interface. The server checks what it can fro
|
|
|
446
448
|
|
|
447
449
|
Upload in dependency order: blobs, then the manifest, then the catalog entry. `atlas report publish`, `atlas report stream` and `httpStore` do. Needs a `Bearer` token of role `publish` or `admin`; a browser's sign-in box is refused.
|
|
448
450
|
|
|
451
|
+
A token of role `critic` may upload only what a critic publishes: blobs, and layers (and their catalog entries) whose `producer` is `critic.<name>` and whose parts are all of kind `critique`. Anything else is `403` with the rule it broke, and nothing is stored.
|
|
452
|
+
|
|
449
453
|
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.
|
|
450
454
|
|
|
451
455
|
- `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.
|
|
@@ -1366,10 +1366,10 @@ A report: the results laid over the specs, with what was read and what could not
|
|
|
1366
1366
|
#### `countResults`
|
|
1367
1367
|
|
|
1368
1368
|
```ts
|
|
1369
|
-
export function countResults(
|
|
1369
|
+
export function countResults(all: readonly EvidenceResult[]): StatusCounts & { total: number }
|
|
1370
1370
|
```
|
|
1371
1371
|
|
|
1372
|
-
Counts results by status. Diagnostic results (parts of kind `performance`, which only qualify a check) are left out
|
|
1372
|
+
Counts results by status. Opinion results (parts of kind `critique`) are never counted. Diagnostic results (parts of kind `performance`, which only qualify a check) are left out
|
|
1373
1373
|
whenever any other result is present; if only diagnostics exist, they are counted so the report is not empty.
|
|
1374
1374
|
|
|
1375
1375
|
#### `summarize`
|
|
@@ -2371,7 +2371,8 @@ export function checkSlots(results: readonly EvidenceResult[]): CheckSlot[]
|
|
|
2371
2371
|
|
|
2372
2372
|
Groups the combined results (the winners of the slots) by check, in the order the checks were first seen.
|
|
2373
2373
|
A check's status is the worst of its non-diagnostic results; if it has only diagnostics (timings), those are its
|
|
2374
|
-
outcomes. The title and id shown are the newest result's.
|
|
2374
|
+
outcomes. The title and id shown are the newest result's. Opinions (parts of kind `critique`, see `isOpinionKind`) are
|
|
2375
|
+
not checks and are left out.
|
|
2375
2376
|
|
|
2376
2377
|
#### `SpecStatus`
|
|
2377
2378
|
|
|
@@ -4582,6 +4583,17 @@ export function isDiagnosticKind(kind: string): boolean
|
|
|
4582
4583
|
|
|
4583
4584
|
Whether a part `kind` only qualifies an outcome (today only `performance`) and so is not counted as a check when anything else is recorded.
|
|
4584
4585
|
|
|
4586
|
+
#### `isOpinionKind`
|
|
4587
|
+
|
|
4588
|
+
```ts
|
|
4589
|
+
export function isOpinionKind(kind: string): boolean
|
|
4590
|
+
```
|
|
4591
|
+
|
|
4592
|
+
Whether a part `kind` records an opinion rather than an outcome (today only `critique`, an automated reviewer's
|
|
4593
|
+
judgement of a captured UI). An opinion is never counted as a check, never gates, and is not folded into a spec's
|
|
4594
|
+
rollup, even when it is the only thing recorded: a report of opinions alone has no test results. The check's `status`
|
|
4595
|
+
of such a part says whether the reviewer ran, not whether anything passed.
|
|
4596
|
+
|
|
4585
4597
|
#### `emptyCounts`
|
|
4586
4598
|
|
|
4587
4599
|
```ts
|
|
@@ -4593,11 +4605,12 @@ A fresh status tally with every status at zero.
|
|
|
4593
4605
|
#### `outcomeCounts`
|
|
4594
4606
|
|
|
4595
4607
|
```ts
|
|
4596
|
-
export function outcomeCounts(
|
|
4608
|
+
export function outcomeCounts(allRows: readonly LayerRow[]): StatusCounts
|
|
4597
4609
|
```
|
|
4598
4610
|
|
|
4599
|
-
Counts rows by status, leaving out the
|
|
4600
|
-
When every row is diagnostic they are counted, so a
|
|
4611
|
+
Counts rows by status, leaving out the opinion rows (see `isOpinionKind`) always, and the diagnostic rows (see
|
|
4612
|
+
`isDiagnosticKind`) that only qualify an outcome. When every remaining row is diagnostic they are counted, so a
|
|
4613
|
+
layer of timings alone is not empty.
|
|
4601
4614
|
|
|
4602
4615
|
#### `layerEvidence`
|
|
4603
4616
|
|
|
@@ -6981,7 +6994,7 @@ Returns a `ReportStatus` (schemaVersion 1): `ok` is true exactly when `reasons`
|
|
|
6981
6994
|
checks (or none completed), failed checks, running checks, specs without results (only with `requireAllSpecs`),
|
|
6982
6995
|
each required environment with no checks (the name is normalised, so `iOS` means `ios`), then every notice of the
|
|
6983
6996
|
report (an unreadable layer or backend). `failing` lists each failed or running winner, in a fixed order that does
|
|
6984
|
-
not depend on read order; diagnostic `performance` checks are left out unless nothing else was recorded.
|
|
6997
|
+
not depend on read order; diagnostic `performance` checks are left out unless nothing else was recorded, and `critique` opinions are always left out.
|
|
6985
6998
|
`recordings` reports recording completeness of the same results apart from `ok`: an incomplete or unknown
|
|
6986
6999
|
completeness never changes the gate (use `atlas report publish --require-recordings` to fail on a missing video).
|
|
6987
7000
|
|
package/docs/reference/schema.md
CHANGED
|
@@ -775,12 +775,128 @@ export function transitionId(source: string, event: string)
|
|
|
775
775
|
Builds the id of a transition, the key of `transitions` in a manifest: the source state's name,
|
|
776
776
|
`TRANSITION_ID_SEPARATOR`, then the event name.
|
|
777
777
|
|
|
778
|
+
### The `atlas.critique` evidence schema
|
|
779
|
+
|
|
780
|
+
Source: `packages/schema/src/critique.ts`
|
|
781
|
+
|
|
782
|
+
What an automated reviewer (a critic) says about one captured UI. It is not a new layer format: a critique is a
|
|
783
|
+
check inside a part of kind `critique`, and the opinion rides in the check's `extensions["atlas.critique"]`, so by the
|
|
784
|
+
rules of docs/extending.md it can never decide a result. The check's `status` says only whether the critic ran
|
|
785
|
+
(`passed` a verdict was produced, `failed` the critic could not produce one, `running`, `not-run`).
|
|
786
|
+
|
|
787
|
+
Everything except `schema`, `subject` and `critic` is optional, so a single score (a model-based critic) and rich
|
|
788
|
+
findings (an LLM critic) are one shape. Every object is open, and `kind`, `category`, `severity`, `label` and `reason`
|
|
789
|
+
are open strings: a value nobody has heard of is kept and shown generically, never rejected. A reader that cannot read
|
|
790
|
+
the whole extension drops it and keeps the check and its status (`readCritique` in `@crvouga/atlas/critics`).
|
|
791
|
+
|
|
792
|
+
See docs/critics.md
|
|
793
|
+
See docs/extending.md
|
|
794
|
+
|
|
795
|
+
#### `CRITIQUE_EXTENSION`
|
|
796
|
+
|
|
797
|
+
```ts
|
|
798
|
+
export const CRITIQUE_EXTENSION = …
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
The key under which a critique is stored in a check's `extensions`.
|
|
802
|
+
|
|
803
|
+
#### `CRITIQUE_SCHEMA_VERSION`
|
|
804
|
+
|
|
805
|
+
```ts
|
|
806
|
+
export const CRITIQUE_SCHEMA_VERSION = …
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
The version of the `atlas.critique` extension a reader of this release understands. A critique of another version is unknown, not wrong.
|
|
810
|
+
|
|
811
|
+
#### `CRITIQUE_PART_KIND`
|
|
812
|
+
|
|
813
|
+
```ts
|
|
814
|
+
export const CRITIQUE_PART_KIND = …
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
The `kind` of the report part that holds critiques. Parts of this kind are opinions: never counted, never part of the gate.
|
|
818
|
+
|
|
819
|
+
#### `CRITIC_PRODUCER_PREFIX`
|
|
820
|
+
|
|
821
|
+
```ts
|
|
822
|
+
export const CRITIC_PRODUCER_PREFIX = …
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
The prefix of the `producer` of a layer of critiques: `critic.<critic name>`.
|
|
826
|
+
|
|
827
|
+
#### `CritiqueRegionSchema`
|
|
828
|
+
|
|
829
|
+
```ts
|
|
830
|
+
export const CritiqueRegionSchema = …
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
The region of the image a finding is about, in the stated unit (`px` of the stored image, or `normalized` 0..1).
|
|
834
|
+
|
|
835
|
+
#### `CritiqueFindingSchema`
|
|
836
|
+
|
|
837
|
+
```ts
|
|
838
|
+
export const CritiqueFindingSchema = …
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
One thing a critic found: where, what kind, how bad, why, and what to do. Nothing is required.
|
|
842
|
+
|
|
843
|
+
#### `CritiqueRelativeSchema`
|
|
844
|
+
|
|
845
|
+
```ts
|
|
846
|
+
export const CritiqueRelativeSchema = …
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
How a score reads in context. A raw score is not absolute: a loading overlay is compared with other loading overlays,
|
|
850
|
+
not with full screens, and a change against the same entry's previous image is more telling than the level.
|
|
851
|
+
|
|
852
|
+
#### `CritiqueVerdictSchema`
|
|
853
|
+
|
|
854
|
+
```ts
|
|
855
|
+
export const CritiqueVerdictSchema = …
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
The judgement: a score and how to read it, a label, a confidence. A critic that cannot score may give only a label.
|
|
859
|
+
|
|
860
|
+
#### `CritiqueFailureSchema`
|
|
861
|
+
|
|
862
|
+
```ts
|
|
863
|
+
export const CritiqueFailureSchema = …
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
Why a critic produced no verdict: a reason code in an open vocabulary (`error`, `timeout`, `rate-limited`, `invalid-output`) and a message that holds no credential.
|
|
867
|
+
|
|
868
|
+
#### `CritiqueSchema`
|
|
869
|
+
|
|
870
|
+
```ts
|
|
871
|
+
export const CritiqueSchema = …
|
|
872
|
+
```
|
|
873
|
+
|
|
874
|
+
The `atlas.critique` extension, version 1: the image judged (`subject`), who judged (`critic`, `model`,
|
|
875
|
+
`parameters`), the opinion (`verdict`, `findings`) or why there is none (`failure`), and what it cost (`usage`).
|
|
876
|
+
The prompt is recorded only as a hash (`parameters.rubric`). Emitted as `critique.schema.json`.
|
|
877
|
+
|
|
878
|
+
#### `Critique`
|
|
879
|
+
|
|
880
|
+
```ts
|
|
881
|
+
export type Critique = z.output<typeof CritiqueSchema>;
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
A critique as stored in a check's `extensions["atlas.critique"]`.
|
|
885
|
+
|
|
886
|
+
#### `CritiqueFinding`
|
|
887
|
+
|
|
888
|
+
```ts
|
|
889
|
+
export type CritiqueFinding = z.output<typeof CritiqueFindingSchema>;
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
One finding of a critique.
|
|
893
|
+
|
|
778
894
|
### Schema package entry point (`@crvouga/atlas/schema`)
|
|
779
895
|
|
|
780
896
|
Source: `packages/schema/src/index.ts`. Import: `@crvouga/atlas/schema`
|
|
781
897
|
|
|
782
898
|
Re-exports every Zod schema, constant and inferred type of the Atlas contract: the layer format and report status
|
|
783
|
-
(`layer`), the statechart run manifest (`manifest`, `common`), the spec file formats (`spec`), the run index and
|
|
899
|
+
(`layer`), the `atlas.critique` evidence of automated UI reviewers (`critique`), the statechart run manifest (`manifest`, `common`), the spec file formats (`spec`), the run index and
|
|
784
900
|
report sources the viewer reads (`runs-index`, `report-sources`) and the server configuration (`server-config`).
|
|
785
901
|
The JSON Schemas shipped in `schemas/` are generated from these by `bun run schemas`.
|
|
786
902
|
|
|
@@ -1869,7 +1985,8 @@ See docs/server.md
|
|
|
1869
1985
|
export const ServerRoleSchema = …
|
|
1870
1986
|
```
|
|
1871
1987
|
|
|
1872
|
-
What a bearer token may do: `read` (look), `
|
|
1988
|
+
What a bearer token may do: `read` (look), `critic` (also publish critiques, and nothing else), `publish` (also upload
|
|
1989
|
+
anything) or `admin` (also remove scratch objects).
|
|
1873
1990
|
|
|
1874
1991
|
#### `ServerRole`
|
|
1875
1992
|
|
package/docs/reference/server.md
CHANGED
|
@@ -93,7 +93,7 @@ What it knows lives in its backends (by default a folder on its own disk), so re
|
|
|
93
93
|
|
|
94
94
|
Source: `packages/server/src/auth.ts`
|
|
95
95
|
|
|
96
|
-
Who is asking, and may they. Tokens have a role (`read` < `publish` < `admin`) and are compared in constant time. Reading
|
|
96
|
+
Who is asking, and may they. Tokens have a role (`read` < `critic` < `publish` < `admin`) and are compared in constant time. Reading
|
|
97
97
|
accepts a Bearer token or the browser's sign-in box (any user name, the token as the password); changing anything needs
|
|
98
98
|
the Bearer header, because a browser sends the sign-in box's credentials to any page that asks, which would let another
|
|
99
99
|
site upload with them. A server with no tokens is open, and refuses to listen beyond this machine unless told it may.
|
|
@@ -530,10 +530,10 @@ The version of the API described, `major.minor.patch`: the minor grows when an o
|
|
|
530
530
|
#### `Role`
|
|
531
531
|
|
|
532
532
|
```ts
|
|
533
|
-
export type Role = 'none' | 'read' | 'publish' | 'admin';
|
|
533
|
+
export type Role = 'none' | 'read' | 'critic' | 'publish' | 'admin';
|
|
534
534
|
```
|
|
535
535
|
|
|
536
|
-
Who may call an operation: `none` (anyone), or the least role that may (`read` < `publish` < `admin`).
|
|
536
|
+
Who may call an operation: `none` (anyone), or the least role that may (`read` < `critic` < `publish` < `admin`).
|
|
537
537
|
|
|
538
538
|
#### `Operation`
|
|
539
539
|
|
package/docs/report-contract.md
CHANGED
|
@@ -230,6 +230,10 @@ An Atlas path that passed on retry (`flaky`) is `passed`, with the detail "Passe
|
|
|
230
230
|
Timings that only qualify a check (parts of kind `performance`) are not counted as checks when
|
|
231
231
|
anything else is recorded. A measurement over its allowance is reported and never fails a check.
|
|
232
232
|
|
|
233
|
+
Opinions (parts of kind `critique`, the verdicts of [automated UI reviewers](critics.md)) are never counted as checks,
|
|
234
|
+
even when they are all that was recorded, and are never part of the gate: a low score, or a critic that crashed, leaves
|
|
235
|
+
`atlas report status` as it was. The `status` of a critique check says only whether the critic ran.
|
|
236
|
+
|
|
233
237
|
**Freshness never changes a status.** A layer whose spec fingerprint no longer matches, whose
|
|
234
238
|
checkout was modified, or whose related code changed still shows its recorded outcome, with a
|
|
235
239
|
warning ("Potentially outdated: spec changed"). Atlas favours quick, possibly stale evidence over
|
package/docs/server.md
CHANGED
|
@@ -205,7 +205,7 @@ is said in words and the server does not start. The file has a published
|
|
|
205
205
|
| How often (seconds) the backends are looked at for layers written by something else | `refreshSeconds` | `--refresh <seconds>` | `ATLAS_REFRESH_SECONDS` | `10` |
|
|
206
206
|
| Let anyone look, not upload | `auth.anonymousRead` | `--anonymous-read` | `ATLAS_ANONYMOUS_READ` | no |
|
|
207
207
|
| Listen publicly with no tokens (something in front signs people in) | `auth.insecure` | `--insecure` | `ATLAS_INSECURE` | no |
|
|
208
|
-
| The admin token (also `ATLAS_PUBLISH_TOKEN` and `ATLAS_READ_TOKEN` for the other roles, or `auth.tokens` in the file) | `auth.tokens` | | `ATLAS_TOKEN` | none: the server is open and listens on this machine only |
|
|
208
|
+
| The admin token (also `ATLAS_PUBLISH_TOKEN`, `ATLAS_CRITIC_TOKEN` and `ATLAS_READ_TOKEN` for the other roles, or `auth.tokens` in the file) | `auth.tokens` | | `ATLAS_TOKEN` | none: the server is open and listens on this machine only |
|
|
209
209
|
| The configuration file | `(the file itself)` | `--config <file>` | `ATLAS_CONFIG` | `./atlas.server.json` when it exists |
|
|
210
210
|
| Largest uploaded object in MiB | `limits.maxObjectMiB` | | `ATLAS_MAX_OBJECT_MIB` | `256` |
|
|
211
211
|
| Largest JSON upload in MiB | `limits.maxJsonMiB` | | `ATLAS_MAX_JSON_MIB` | `32` |
|
|
@@ -238,16 +238,18 @@ backend in the client and `/readyz`, and recovers by itself.
|
|
|
238
238
|
| Role | May |
|
|
239
239
|
| --- | --- |
|
|
240
240
|
| `read` | look: the client, the report, `/api/status` |
|
|
241
|
+
| `critic` | also publish [critiques](critics.md#authentication-and-authorization) and nothing else: blobs and layers whose producer is `critic.<name>` and whose parts are all of kind `critique`. It cannot post test results, start live runs or change settings |
|
|
241
242
|
| `publish` | also upload layers, specs and runs, and remove the scratch objects `atlas report doctor` writes |
|
|
242
243
|
| `admin` | all server operations |
|
|
243
244
|
|
|
244
245
|
Tokens are at least 12 characters (`openssl rand -hex 24`) and different from one another. Give them as
|
|
245
|
-
`ATLAS_TOKEN` (admin), `ATLAS_PUBLISH_TOKEN`, `ATLAS_READ_TOKEN`, or under `auth.tokens` in the file, preferably
|
|
246
|
+
`ATLAS_TOKEN` (admin), `ATLAS_PUBLISH_TOKEN`, `ATLAS_CRITIC_TOKEN`, `ATLAS_READ_TOKEN`, or under `auth.tokens` in the file, preferably
|
|
246
247
|
with `tokenEnv` so the file holds no secret.
|
|
247
248
|
|
|
248
249
|
- **Sending a token:** `Authorization: Bearer <token>`. A browser's sign-in box also works for *looking* (any
|
|
249
250
|
user name, the token as the password). **Uploading needs the Bearer header, never the sign-in box**: a browser
|
|
250
251
|
sends those credentials by itself to any page that asks, which would let another site upload with them.
|
|
252
|
+
- **Run critics against a server with tokens.** With no tokens every caller is an administrator, a critic included.
|
|
251
253
|
- **No tokens** means open, and then the server refuses to listen beyond this machine unless told
|
|
252
254
|
`--insecure` (for when a proxy in front does the signing in).
|
|
253
255
|
- `/healthz` and `/readyz` never need a token, so a load balancer can use them.
|
|
@@ -490,8 +492,8 @@ curl "$ATLAS_SERVER_URL/api/projects/$ATLAS_PROJECT/reports/tests?format=markdow
|
|
|
490
492
|
| `GET /api/projects/{project}/store/{key}` | read | Read an object. |
|
|
491
493
|
| `HEAD /api/store/{key}` | read | Does an object exist. |
|
|
492
494
|
| `HEAD /api/projects/{project}/store/{key}` | read | Does an object exist. |
|
|
493
|
-
| `PUT /api/store/{key}` |
|
|
494
|
-
| `PUT /api/projects/{project}/store/{key}` |
|
|
495
|
+
| `PUT /api/store/{key}` | critic | Upload an object. |
|
|
496
|
+
| `PUT /api/projects/{project}/store/{key}` | critic | Upload an object. |
|
|
495
497
|
| `DELETE /api/store/{key}` | publish | Remove a scratch object. |
|
|
496
498
|
| `DELETE /api/projects/{project}/store/{key}` | publish | Remove a scratch object. |
|
|
497
499
|
| `GET /api/reports/tests` | read | Every test, with insights, facets and history statistics. |
|
package/llms-full.txt
CHANGED
|
@@ -112,6 +112,7 @@ The complete public export map is generated from the package manifest (wildcards
|
|
|
112
112
|
@crvouga/atlas/vitest
|
|
113
113
|
@crvouga/atlas/schema
|
|
114
114
|
@crvouga/atlas/server
|
|
115
|
+
@crvouga/atlas/critics
|
|
115
116
|
@crvouga/atlas/reports/*
|
|
116
117
|
@crvouga/atlas/schemas/*
|
|
117
118
|
@crvouga/atlas/openapi.json
|
|
@@ -1671,7 +1672,7 @@ is said in words and the server does not start. The file has a published
|
|
|
1671
1672
|
| How often (seconds) the backends are looked at for layers written by something else | `refreshSeconds` | `--refresh <seconds>` | `ATLAS_REFRESH_SECONDS` | `10` |
|
|
1672
1673
|
| Let anyone look, not upload | `auth.anonymousRead` | `--anonymous-read` | `ATLAS_ANONYMOUS_READ` | no |
|
|
1673
1674
|
| Listen publicly with no tokens (something in front signs people in) | `auth.insecure` | `--insecure` | `ATLAS_INSECURE` | no |
|
|
1674
|
-
| The admin token (also `ATLAS_PUBLISH_TOKEN` and `ATLAS_READ_TOKEN` for the other roles, or `auth.tokens` in the file) | `auth.tokens` | | `ATLAS_TOKEN` | none: the server is open and listens on this machine only |
|
|
1675
|
+
| The admin token (also `ATLAS_PUBLISH_TOKEN`, `ATLAS_CRITIC_TOKEN` and `ATLAS_READ_TOKEN` for the other roles, or `auth.tokens` in the file) | `auth.tokens` | | `ATLAS_TOKEN` | none: the server is open and listens on this machine only |
|
|
1675
1676
|
| The configuration file | `(the file itself)` | `--config <file>` | `ATLAS_CONFIG` | `./atlas.server.json` when it exists |
|
|
1676
1677
|
| Largest uploaded object in MiB | `limits.maxObjectMiB` | | `ATLAS_MAX_OBJECT_MIB` | `256` |
|
|
1677
1678
|
| Largest JSON upload in MiB | `limits.maxJsonMiB` | | `ATLAS_MAX_JSON_MIB` | `32` |
|
|
@@ -1704,16 +1705,18 @@ backend in the client and `/readyz`, and recovers by itself.
|
|
|
1704
1705
|
| Role | May |
|
|
1705
1706
|
| --- | --- |
|
|
1706
1707
|
| `read` | look: the client, the report, `/api/status` |
|
|
1708
|
+
| `critic` | also publish [critiques](critics.md#authentication-and-authorization) and nothing else: blobs and layers whose producer is `critic.<name>` and whose parts are all of kind `critique`. It cannot post test results, start live runs or change settings |
|
|
1707
1709
|
| `publish` | also upload layers, specs and runs, and remove the scratch objects `atlas report doctor` writes |
|
|
1708
1710
|
| `admin` | all server operations |
|
|
1709
1711
|
|
|
1710
1712
|
Tokens are at least 12 characters (`openssl rand -hex 24`) and different from one another. Give them as
|
|
1711
|
-
`ATLAS_TOKEN` (admin), `ATLAS_PUBLISH_TOKEN`, `ATLAS_READ_TOKEN`, or under `auth.tokens` in the file, preferably
|
|
1713
|
+
`ATLAS_TOKEN` (admin), `ATLAS_PUBLISH_TOKEN`, `ATLAS_CRITIC_TOKEN`, `ATLAS_READ_TOKEN`, or under `auth.tokens` in the file, preferably
|
|
1712
1714
|
with `tokenEnv` so the file holds no secret.
|
|
1713
1715
|
|
|
1714
1716
|
- **Sending a token:** `Authorization: Bearer <token>`. A browser's sign-in box also works for *looking* (any
|
|
1715
1717
|
user name, the token as the password). **Uploading needs the Bearer header, never the sign-in box**: a browser
|
|
1716
1718
|
sends those credentials by itself to any page that asks, which would let another site upload with them.
|
|
1719
|
+
- **Run critics against a server with tokens.** With no tokens every caller is an administrator, a critic included.
|
|
1717
1720
|
- **No tokens** means open, and then the server refuses to listen beyond this machine unless told
|
|
1718
1721
|
`--insecure` (for when a proxy in front does the signing in).
|
|
1719
1722
|
- `/healthz` and `/readyz` never need a token, so a load balancer can use them.
|
|
@@ -1956,8 +1959,8 @@ curl "$ATLAS_SERVER_URL/api/projects/$ATLAS_PROJECT/reports/tests?format=markdow
|
|
|
1956
1959
|
| `GET /api/projects/{project}/store/{key}` | read | Read an object. |
|
|
1957
1960
|
| `HEAD /api/store/{key}` | read | Does an object exist. |
|
|
1958
1961
|
| `HEAD /api/projects/{project}/store/{key}` | read | Does an object exist. |
|
|
1959
|
-
| `PUT /api/store/{key}` |
|
|
1960
|
-
| `PUT /api/projects/{project}/store/{key}` |
|
|
1962
|
+
| `PUT /api/store/{key}` | critic | Upload an object. |
|
|
1963
|
+
| `PUT /api/projects/{project}/store/{key}` | critic | Upload an object. |
|
|
1961
1964
|
| `DELETE /api/store/{key}` | publish | Remove a scratch object. |
|
|
1962
1965
|
| `DELETE /api/projects/{project}/store/{key}` | publish | Remove a scratch object. |
|
|
1963
1966
|
| `GET /api/reports/tests` | read | Every test, with insights, facets and history statistics. |
|
|
@@ -2798,6 +2801,10 @@ An Atlas path that passed on retry (`flaky`) is `passed`, with the detail "Passe
|
|
|
2798
2801
|
Timings that only qualify a check (parts of kind `performance`) are not counted as checks when
|
|
2799
2802
|
anything else is recorded. A measurement over its allowance is reported and never fails a check.
|
|
2800
2803
|
|
|
2804
|
+
Opinions (parts of kind `critique`, the verdicts of [automated UI reviewers](critics.md)) are never counted as checks,
|
|
2805
|
+
even when they are all that was recorded, and are never part of the gate: a low score, or a critic that crashed, leaves
|
|
2806
|
+
`atlas report status` as it was. The `status` of a critique check says only whether the critic ran.
|
|
2807
|
+
|
|
2801
2808
|
**Freshness never changes a status.** A layer whose spec fingerprint no longer matches, whose
|
|
2802
2809
|
checkout was modified, or whose related code changed still shows its recorded outcome, with a
|
|
2803
2810
|
warning ("Potentially outdated: spec changed"). Atlas favours quick, possibly stale evidence over
|
|
@@ -3224,6 +3231,7 @@ Annotation `type`: `note`, `warning`, `deprecation`, `issue`, `accessibility`, `
|
|
|
3224
3231
|
| A kind of file Atlas has never seen | Attach it with its media type and a `role`. It is stored and offered for download today. |
|
|
3225
3232
|
| Structured data of your own | Put it in `extensions` under a name you own. It is kept, and shown as raw data. |
|
|
3226
3233
|
| A new annotation type, role or step kind | Use it. Open vocabularies need no registration. |
|
|
3234
|
+
| An automated reviewer's opinion of a captured UI | A [critique](critics.md): a part of kind `critique` with the `atlas.critique` extension. It is never counted and never gates. |
|
|
3227
3235
|
| Something that should be shown well | Ask for a renderer (below). |
|
|
3228
3236
|
| A field every tool should have | Open an issue. It is added as an optional field, and the rules above guarantee nothing breaks. |
|
|
3229
3237
|
|
|
@@ -3245,6 +3253,199 @@ listed with its name, role, type and size, and offered for download, and everyth
|
|
|
3245
3253
|
unaffected.
|
|
3246
3254
|
</doc>
|
|
3247
3255
|
|
|
3256
|
+
<doc path="docs/critics.md">
|
|
3257
|
+
# Critics: automated reviewers that mark which UIs need polish
|
|
3258
|
+
|
|
3259
|
+
A **critic** looks at one captured UI (an image and what is known about it) and says how polished it is, and ideally
|
|
3260
|
+
where and why it is not. A critic may be a pretrained model that gives a score, a vision-capable language model that
|
|
3261
|
+
follows a rubric, or a classic measure such as a contrast ratio. All three are the same thing to Atlas, through one small
|
|
3262
|
+
contract, so people review the likeliest problems first.
|
|
3263
|
+
|
|
3264
|
+
**Critics are producers that publish layers.** Atlas supplies the contract, the runner, the evidence format and the
|
|
3265
|
+
credential. It never executes a model on the server and never holds a provider key. A critique is not a test result and
|
|
3266
|
+
is not human review: it never changes whether a report passes.
|
|
3267
|
+
|
|
3268
|
+
- [What is available](#what-is-available)
|
|
3269
|
+
- [The critic contract](#the-critic-contract)
|
|
3270
|
+
- [A critique is a layer](#a-critique-is-a-layer)
|
|
3271
|
+
- [Running critics](#running-critics)
|
|
3272
|
+
- [State-aware judging, calibration and modes](#state-aware-judging-calibration-and-modes)
|
|
3273
|
+
- [Authentication and authorization](#authentication-and-authorization)
|
|
3274
|
+
- [Credentials](#credentials)
|
|
3275
|
+
- [What is not here yet](#what-is-not-here-yet)
|
|
3276
|
+
|
|
3277
|
+
## What is available
|
|
3278
|
+
|
|
3279
|
+
Import `@crvouga/atlas/critics`. It needs no model, no provider SDK and no network; a critic brings its own.
|
|
3280
|
+
|
|
3281
|
+
| You want | Use |
|
|
3282
|
+
| --- | --- |
|
|
3283
|
+
| Define a critic | `defineCritic` (the contract below) |
|
|
3284
|
+
| Judge many images with caching, retries, rate limits, timeouts and budgets | `runCritic` |
|
|
3285
|
+
| Store the opinions | `critiqueLayer`, `publishCritiques` (a layer of parts of kind `critique`) |
|
|
3286
|
+
| Read them back | `readCritique`, `critiquesOfLayer`, `critiqueCurrency` |
|
|
3287
|
+
| Read a score in context, measure a threshold, decide an exit code | `relativeVerdict`, `calibrate`, `gateDecision` |
|
|
3288
|
+
| Keep keys out of configuration and logs | `parseCredentialRef`, `resolveCredential`, `assertNoLiteralCredentials`, `createRedactor` |
|
|
3289
|
+
| Let a token publish critiques and nothing else | the `critic` role (below) |
|
|
3290
|
+
|
|
3291
|
+
The JSON Schema of the evidence is [`critique.schema.json`](../packages/schema/schemas/critique.schema.json). The
|
|
3292
|
+
function-level reference is generated from the code: [critics](reference/critics.md).
|
|
3293
|
+
|
|
3294
|
+
## The critic contract
|
|
3295
|
+
|
|
3296
|
+
```ts
|
|
3297
|
+
import { defineCritic } from '@crvouga/atlas/critics';
|
|
3298
|
+
|
|
3299
|
+
export default defineCritic({
|
|
3300
|
+
name: 'contrast', version: '1.0.0', kind: 'heuristic', // kind is an open string: model, llm, ml, heuristic…
|
|
3301
|
+
describe: () => ({ sendsImagesTo: null, needsSetup: false }),
|
|
3302
|
+
evaluate: async ({ image, imageSha256, entry, signal }) => ({
|
|
3303
|
+
verdict: { score: measureContrast(image), scale: '0..1, higher is better' }
|
|
3304
|
+
})
|
|
3305
|
+
});
|
|
3306
|
+
```
|
|
3307
|
+
|
|
3308
|
+
A critic only judges one image. `describe()` says where images are sent (`sendsImagesTo`, `null` when they never leave
|
|
3309
|
+
the machine), the model, and the fixed parameters (temperature, the SHA-256 of the rubric): together with the critic's
|
|
3310
|
+
`name` and `version` and the image hash they are the identity of a judgement. `evaluate` returns a `verdict` (a score
|
|
3311
|
+
and how to read it), `findings`, or both, and may throw: throw a `RateLimitedError` (with the provider's `Retry-After` in
|
|
3312
|
+
milliseconds), an `InvalidOutputError`, or any `CriticError` with your own reason code. The runner records the failure
|
|
3313
|
+
and the next run retries it. A failure never becomes a score.
|
|
3314
|
+
|
|
3315
|
+
## A critique is a layer
|
|
3316
|
+
|
|
3317
|
+
A critique is a check inside a part of kind `critique`, in a layer whose `producer` is `critic.<name>`. The check's
|
|
3318
|
+
`status` says only whether the critic ran: `passed` a verdict was produced, `failed` the critic could not produce one.
|
|
3319
|
+
The opinion is evidence, by the rules of [extending.md](extending.md#the-rules-that-keep-it-compatible-forever), so it
|
|
3320
|
+
can never decide a result. It lives in `extensions["atlas.critique"]` and is repeated as standard evidence, so a viewer
|
|
3321
|
+
that has never heard of critics still shows it: the score is a measurement named `polish`, and each finding is an
|
|
3322
|
+
annotation of type `critique`.
|
|
3323
|
+
|
|
3324
|
+
```json
|
|
3325
|
+
{
|
|
3326
|
+
"producer": "critic.vision-llm",
|
|
3327
|
+
"kind": "critique",
|
|
3328
|
+
"checks": [{
|
|
3329
|
+
"id": "critique/checkout/payment-sheet",
|
|
3330
|
+
"status": "passed",
|
|
3331
|
+
"measurements": [{ "name": "polish", "value": 0.62, "unit": "score" }],
|
|
3332
|
+
"annotations": [{ "type": "critique", "level": "warning", "message": "spacing: Label touches both edges of the button. (suggestion: Increase horizontal padding.)" }],
|
|
3333
|
+
"extensions": { "atlas.critique": {
|
|
3334
|
+
"schema": 1,
|
|
3335
|
+
"subject": { "entryKey": "checkout/payment-sheet", "imageSha256": "…", "state": "overlay" },
|
|
3336
|
+
"critic": { "name": "vision-llm", "kind": "llm", "version": "1.0.0" },
|
|
3337
|
+
"model": { "id": "…", "provider": "…" },
|
|
3338
|
+
"parameters": { "temperature": 0, "rubric": "<sha256 of the rubric>" },
|
|
3339
|
+
"verdict": { "score": 0.62, "scale": "0..1, higher is better", "label": "needs-polish", "confidence": 0.7 },
|
|
3340
|
+
"findings": [{ "id": "f1", "category": "spacing", "severity": "minor", "region": { "unit": "px", "x": 24, "y": 612, "w": 342, "h": 48 }, "rationale": "Label touches both edges of the button.", "suggestion": "Increase horizontal padding." }]
|
|
3341
|
+
} }
|
|
3342
|
+
}]
|
|
3343
|
+
}
|
|
3344
|
+
```
|
|
3345
|
+
|
|
3346
|
+
- A single score (a model-based critic) is a valid critique: everything except `schema`, `subject` and `critic` is optional.
|
|
3347
|
+
- `kind`, `category`, `severity`, `label` and failure `reason` are open strings, and every object is open. A value or field
|
|
3348
|
+
nobody has heard of is kept and shown generically, never rejected.
|
|
3349
|
+
- A reader that cannot read the whole extension (a missing `subject`, a score that is not a number, a `schema` version it
|
|
3350
|
+
does not know) drops the extension and keeps the check and its status. An unreadable opinion is unknown.
|
|
3351
|
+
- **Anchored to pixels.** `subject.imageSha256` is the hash of the image that was judged. When an entry's image changes,
|
|
3352
|
+
`critiqueCurrency` says the old critique judged an earlier image: it is shown, never hidden, and the entry counts as not
|
|
3353
|
+
yet judged.
|
|
3354
|
+
- **Slots.** The producer is the critic's name; the critic and model versions are labels. A newer version of a critic
|
|
3355
|
+
replaces an older one's critique of the same entry. Put the version in `parameters` of `critiqueLayer` to compare
|
|
3356
|
+
versions side by side instead.
|
|
3357
|
+
- **The prompt is recorded as a hash** (`parameters.rubric`), not as text.
|
|
3358
|
+
- **Failures are data.** A crash, a timeout, a rate limit that never cleared and an unparseable answer are `failed`
|
|
3359
|
+
critiques with `failure.reason` (`error`, `timeout`, `rate-limited`, `invalid-output`, `image-mismatch`…) and a message.
|
|
3360
|
+
|
|
3361
|
+
**Critiques never change the gate.** Parts of kind `critique` are left out of every count and of `atlas report status`,
|
|
3362
|
+
even when they are the only thing recorded (a report of opinions alone has no test results, so the gate says so). A low
|
|
3363
|
+
score does not fail it, and neither does a critic that crashed. A project that wants a score to fail something opts in
|
|
3364
|
+
with `gateDecision` in `gate` mode, which is computed by the reader from critique data and never changes a check's
|
|
3365
|
+
status, a slot or a winner.
|
|
3366
|
+
|
|
3367
|
+
## Running critics
|
|
3368
|
+
|
|
3369
|
+
`runCritic` owns everything around the judgement; a critic only judges one image.
|
|
3370
|
+
|
|
3371
|
+
- **Caching.** An entry is skipped, without fetching its image, when a critique with a verdict already exists for the same
|
|
3372
|
+
critic, version, model, parameters and image hash (`existing`, read with `critiquesOfLayer`). Identical images across
|
|
3373
|
+
runs are judged once; only a changed image is judged again. A failed critique is not a cache hit. `force` judges again.
|
|
3374
|
+
Layers are content-addressed, so publishing the same outcomes twice is harmless.
|
|
3375
|
+
- **Concurrency, timeout, rate limits.** A bounded number of calls in flight. A call over `timeoutMs` is aborted and
|
|
3376
|
+
recorded as `timeout`; the run goes on. A rate-limit answer waits at least `Retry-After`, with exponential backoff and
|
|
3377
|
+
jitter, for a bounded number of attempts, and then is recorded as `rate-limited`.
|
|
3378
|
+
- **Budgets.** `budget: { maxImages, maxTokens, maxCostUsd }`. When one is reached the run stops cleanly and returns what
|
|
3379
|
+
it finished with `stoppedBy: 'budget'`. Cost and tokens come from the `usage` a critic reports.
|
|
3380
|
+
- **Dry run.** `dryRun` loads and sends nothing and returns what would be judged and skipped, and where images would go
|
|
3381
|
+
(`sendsImagesTo`).
|
|
3382
|
+
- **Reproducibility.** Results come back in the order of the entries, so the same inputs publish the same layer; a
|
|
3383
|
+
deterministic critic gives identical scores on every run. Atlas records what explains a result (critic, version,
|
|
3384
|
+
model, rubric hash, parameters) and does not promise that a language model answers the same twice. A re-run adds a
|
|
3385
|
+
newer layer instead of changing an old one.
|
|
3386
|
+
- **Images may leave the machine.** `describe().sendsImagesTo` is the thing to show a person before a run that sends
|
|
3387
|
+
images to a third party, and to require explicit confirmation for.
|
|
3388
|
+
|
|
3389
|
+
```ts
|
|
3390
|
+
import { critiquesOfLayer, publishCritiques, runCritic } from '@crvouga/atlas/critics';
|
|
3391
|
+
|
|
3392
|
+
const result = await runCritic({
|
|
3393
|
+
critic, entries, loadImage: (entry) => fetchImage(entry),
|
|
3394
|
+
existing: layers.flatMap((layer) => critiquesOfLayer(layer).map((found) => found.critique)),
|
|
3395
|
+
budget: { maxImages: 200 }, secrets: [providerKey]
|
|
3396
|
+
});
|
|
3397
|
+
await publishCritiques(store, { critic, outcomes: result.outcomes, recordedAtMs: Date.now() });
|
|
3398
|
+
```
|
|
3399
|
+
|
|
3400
|
+
## State-aware judging, calibration and modes
|
|
3401
|
+
|
|
3402
|
+
A raw score is not absolute: a loading overlay is not a full screen.
|
|
3403
|
+
|
|
3404
|
+
- `relativeVerdict` reads a score against the project's scores of the **same state kind** (a percentile within the kind)
|
|
3405
|
+
and against the same entry's previous image (`delta`), and goes into `verdict.relative`.
|
|
3406
|
+
- `calibrate` measures a critic against labelled examples **per state kind**: sample sizes, precision, recall, the
|
|
3407
|
+
threshold chosen, and `inSample`, which is true unless held-out examples were given. In-sample figures flatter a critic
|
|
3408
|
+
and are labelled as such.
|
|
3409
|
+
- `gateDecision` turns scores into an exit code. Modes: `report` (default: shown, always exit 0), `warn` (a warning for each
|
|
3410
|
+
score below its threshold, exit 0), `gate` (exit 1 when a threshold is crossed). `gate` is **refused** (exit 2, with the
|
|
3411
|
+
reasons) unless every state kind present has a calibration of this critic and version with a threshold, at least 10 bad
|
|
3412
|
+
and 10 good examples, and held-out figures. An explicit threshold never waives that evidence.
|
|
3413
|
+
|
|
3414
|
+
## Authentication and authorization
|
|
3415
|
+
|
|
3416
|
+
Run critics against a server that has tokens: a server with no tokens is open and every caller is an administrator, a
|
|
3417
|
+
critic included.
|
|
3418
|
+
|
|
3419
|
+
A token of role `critic` (`ATLAS_CRITIC_TOKEN`, or `role: "critic"` under `auth.tokens`) may do what `read` may, and
|
|
3420
|
+
publish critiques, and nothing else. Through the store protocol (`PUT /api/store/…`) it may upload blobs and layers, and
|
|
3421
|
+
the catalog entries that make them visible, whose `producer` is `critic.<name>` and whose parts are all of kind
|
|
3422
|
+
`critique`. Anything else is `403` naming the rule it broke, and the layer is not stored. It cannot post test results,
|
|
3423
|
+
start live runs, change settings or remove anything. `publish` and `admin` tokens may publish critiques too. As for every
|
|
3424
|
+
token: send it as `Authorization: Bearer`, never in a URL, never over plain `http` beyond this machine.
|
|
3425
|
+
|
|
3426
|
+
## Credentials
|
|
3427
|
+
|
|
3428
|
+
Developers bring their own models and AI accounts. Configuration names a credential by **reference**
|
|
3429
|
+
(`env:SOME_API_KEY`, `file:/run/secrets/key`) and never contains one, so it can be committed;
|
|
3430
|
+
`assertNoLiteralCredentials` refuses a configuration with a literal key and says where, never what. Credentials are read at
|
|
3431
|
+
run time (`resolveCredential`) and every message the runner records goes through a redactor (`secrets` in `runCritic`,
|
|
3432
|
+
`createRedactor`) that removes them, in their URL-encoded and base64 forms too, and anything shaped like a provider key,
|
|
3433
|
+
token or JWT. A credential never reaches a layer, a progress event or an error message.
|
|
3434
|
+
|
|
3435
|
+
## What is not here yet
|
|
3436
|
+
|
|
3437
|
+
This release delivers the contract, the evidence format, the runner, state-aware judging and calibration, credential
|
|
3438
|
+
handling and the scoped `critic` role. Still to come, tracked in [issue 50](https://github.com/crvouga/atlas/issues/50):
|
|
3439
|
+
|
|
3440
|
+
- the **catalog read surface** for critics (cursor pagination, `since`, realtime events, images by hash), which builds on
|
|
3441
|
+
the UI catalog of [issue 43](https://github.com/crvouga/atlas/issues/43);
|
|
3442
|
+
- the **`atlas critic` command** (`list`, `setup`, `doctor`, `run`, `watch`, `review`, `calibrate`), its interactive mode
|
|
3443
|
+
and its `--format json` documents;
|
|
3444
|
+
- the **built-in UIClip critic** and the **LLM critic** with provider adapters;
|
|
3445
|
+
- the **viewer**: a ranked "needs polish" list, finding overlays and agreement across critics. Until then critiques are
|
|
3446
|
+
stored, composed and kept out of test counts, and the viewer lists only test results.
|
|
3447
|
+
</doc>
|
|
3448
|
+
|
|
3248
3449
|
<doc path="docs/report-backends.md">
|
|
3249
3450
|
|
|
3250
3451
|
</doc>
|
|
@@ -5265,7 +5466,7 @@ Responses: `200` It exists.; `401` No token, or an unknown one. Send `Authorizat
|
|
|
5265
5466
|
|
|
5266
5467
|
### Upload an object
|
|
5267
5468
|
|
|
5268
|
-
`PUT /api/store/{key}` · role:
|
|
5469
|
+
`PUT /api/store/{key}` · role: critic
|
|
5269
5470
|
|
|
5270
5471
|
The write half of the `ObjectStore` interface. The server checks what it can from the bytes in hand and says no to what would corrupt the report for everyone:
|
|
5271
5472
|
|
|
@@ -5277,6 +5478,8 @@ The write half of the `ObjectStore` interface. The server checks what it can fro
|
|
|
5277
5478
|
|
|
5278
5479
|
Upload in dependency order: blobs, then the manifest, then the catalog entry. `atlas report publish`, `atlas report stream` and `httpStore` do. Needs a `Bearer` token of role `publish` or `admin`; a browser's sign-in box is refused.
|
|
5279
5480
|
|
|
5481
|
+
A token of role `critic` may upload only what a critic publishes: blobs, and layers (and their catalog entries) whose `producer` is `critic.<name>` and whose parts are all of kind `critique`. Anything else is `403` with the rule it broke, and nothing is stored.
|
|
5482
|
+
|
|
5280
5483
|
- `key` (path, required): An object key under `layers/`, such as `layers/manifests/<id>.json`. It contains slashes, which are not percent-encoded; every other character is.
|
|
5281
5484
|
- `content-type` (header): Stored with the object and returned when it is read.
|
|
5282
5485
|
- `project` (query): Project slug. Alternatively send X-Atlas-Project or use the project-prefixed route. Omit for the default project. Conflicting selectors are refused.
|
|
@@ -5285,7 +5488,7 @@ Responses: `200` Already there with the same bytes.; `201` Stored.; `401` No tok
|
|
|
5285
5488
|
|
|
5286
5489
|
### Upload an object
|
|
5287
5490
|
|
|
5288
|
-
`PUT /api/projects/{project}/store/{key}` · role:
|
|
5491
|
+
`PUT /api/projects/{project}/store/{key}` · role: critic
|
|
5289
5492
|
|
|
5290
5493
|
The write half of the `ObjectStore` interface. The server checks what it can from the bytes in hand and says no to what would corrupt the report for everyone:
|
|
5291
5494
|
|
|
@@ -5297,6 +5500,8 @@ The write half of the `ObjectStore` interface. The server checks what it can fro
|
|
|
5297
5500
|
|
|
5298
5501
|
Upload in dependency order: blobs, then the manifest, then the catalog entry. `atlas report publish`, `atlas report stream` and `httpStore` do. Needs a `Bearer` token of role `publish` or `admin`; a browser's sign-in box is refused.
|
|
5299
5502
|
|
|
5503
|
+
A token of role `critic` may upload only what a critic publishes: blobs, and layers (and their catalog entries) whose `producer` is `critic.<name>` and whose parts are all of kind `critique`. Anything else is `403` with the rule it broke, and nothing is stored.
|
|
5504
|
+
|
|
5300
5505
|
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.
|
|
5301
5506
|
|
|
5302
5507
|
- `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.
|
package/llms.txt
CHANGED
|
@@ -21,6 +21,7 @@ Given only the repository link, start at docs/agents.md: install `@crvouga/atlas
|
|
|
21
21
|
|
|
22
22
|
- [The report contract](https://raw.githubusercontent.com/crvouga/atlas/main/docs/report-contract.md): This is the normative description of what Atlas reads and writes when it reports on specs of any kind:
|
|
23
23
|
- [Evidence: attachments, timelines, snapshots and everything else](https://raw.githubusercontent.com/crvouga/atlas/main/docs/extending.md): Atlas is opinionated about specs and open about evidence.
|
|
24
|
+
- [Critics: automated reviewers that mark which UIs need polish](https://raw.githubusercontent.com/crvouga/atlas/main/docs/critics.md): A critic looks at one captured UI (an image and what is known about it) and says how polished it is, and ideally where and why it is not.
|
|
24
25
|
- [docs/report-backends.md](https://raw.githubusercontent.com/crvouga/atlas/main/docs/report-backends.md):
|
|
25
26
|
- [Executable statecharts](https://raw.githubusercontent.com/crvouga/atlas/main/docs/statecharts.md): Write down how a product behaves as statecharts in open formats, then let Atlas lint them, plan paths that cover every transition, run those paths against the real product through any driver (a browser, a phone, a pla…
|
|
26
27
|
|
|
@@ -46,6 +47,7 @@ Given only the repository link, start at docs/agents.md: install `@crvouga/atlas
|
|
|
46
47
|
- [TypeScript API: detox](https://raw.githubusercontent.com/crvouga/atlas/main/docs/reference/detox.md): The Detox driver for statechart runs of React Native apps.
|
|
47
48
|
- [TypeScript API: runners](https://raw.githubusercontent.com/crvouga/atlas/main/docs/reference/runners.md): Native reporters that upload to the project server.
|
|
48
49
|
- [TypeScript API: vitest](https://raw.githubusercontent.com/crvouga/atlas/main/docs/reference/vitest.md): Run a statechart plan as Vitest tests.
|
|
50
|
+
- [TypeScript API: critics](https://raw.githubusercontent.com/crvouga/atlas/main/docs/reference/critics.md): Automated reviewers of the UI catalog: the critic contract, the runner (caching, retries, rate limits, budgets), credential references and redaction, the `atlas.critique` evidence as layers, calibration and report, warn and gate modes.
|
|
49
51
|
- [TypeScript API: playwright-reporter](https://raw.githubusercontent.com/crvouga/atlas/main/docs/reference/playwright-reporter.md): A Playwright Test reporter that streams every test to Atlas with its duration, device labels, steps, retries, videos, screenshots and traces.
|
|
50
52
|
- [JSON Schemas](https://unpkg.com/@crvouga/atlas/packages/schema/schemas/): the layer format, checks file, report status, command results, server responses, server configuration.
|
|
51
53
|
|