@ggui-ai/protocol-conformance 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +45 -25
  2. package/dist/cli.d.ts +4 -2
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +13 -8
  5. package/dist/dispatch-idempotency-conformance/index.d.ts +78 -0
  6. package/dist/dispatch-idempotency-conformance/index.d.ts.map +1 -0
  7. package/dist/dispatch-idempotency-conformance/index.js +173 -0
  8. package/dist/fixtures/index.d.ts +22 -4
  9. package/dist/fixtures/index.d.ts.map +1 -1
  10. package/dist/fixtures/index.js +23 -3
  11. package/dist/fixtures/reserved-channel-replay/fresh-subscribe-replays-reserved-preview.json +30 -0
  12. package/dist/fixtures/reserved-channel-replay/index.d.ts +4 -0
  13. package/dist/fixtures/reserved-channel-replay/index.d.ts.map +1 -0
  14. package/dist/fixtures/reserved-channel-replay/index.js +18 -0
  15. package/dist/host-helper-conformance/index.d.ts +144 -2
  16. package/dist/host-helper-conformance/index.d.ts.map +1 -1
  17. package/dist/host-helper-conformance/index.js +309 -1
  18. package/dist/index.d.ts +4 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +4 -2
  21. package/dist/match-behavior.d.ts.map +1 -1
  22. package/dist/match-behavior.js +47 -8
  23. package/dist/n1-compat-conformance/cases/forward-app-theme-carry-uninterpretable.json +21 -0
  24. package/dist/n1-compat-conformance/cases/forward-ops-list-blueprints-cloned.json +88 -0
  25. package/dist/n1-compat-conformance/cases/forward-ops-list-blueprints-stamped.json +77 -0
  26. package/dist/n1-compat-conformance/cases/forward-view-proof-later-version.json +14 -0
  27. package/dist/n1-compat-conformance/cases/forward-view-proof-root-later-claim.json +14 -0
  28. package/dist/n1-compat-conformance/cases/release-14-ops-list-blueprints.json +41 -0
  29. package/dist/n1-compat-conformance/index.d.ts +1 -1
  30. package/dist/n1-compat-conformance/index.d.ts.map +1 -1
  31. package/dist/n1-compat-conformance/index.js +128 -22
  32. package/dist/reporter.d.ts +5 -0
  33. package/dist/reporter.d.ts.map +1 -1
  34. package/dist/reporter.js +30 -4
  35. package/dist/run-conformance.d.ts +37 -1
  36. package/dist/run-conformance.d.ts.map +1 -1
  37. package/dist/run-conformance.js +32 -17
  38. package/dist/types.d.ts +16 -0
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +8 -3
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "forward-ops-list-blueprints-stamped",
3
+ "description": "FORWARD (ggui#1280 emit, VERSION-POLICY §3.6): the ggui_ops_list_blueprints result the NEXT release writes once generation mints are stamped — synthetic by construction, so it carries no landed sha. Two rows built on tag 14's served row (release-14-ops-list-blueprints): the first carries a valid `build` (the minting engine's version, mode and template digests), the second carries none (registered, installed and older rows). The reader MUST accept the result, keep the stamp VERBATIM on the first row and leave the second row unstamped. It is the half the backward case cannot test: a reader that dropped or rewrote the stamp — tag 14's reader declares it (`d9132ae6f`), and a later change to `opsListBlueprintsOutputSchema` must not lose it — fails here.",
4
+ "direction": "forward",
5
+ "release": {
6
+ "label": "a later ggui release (synthetic): tag 14's served row, once stamped and once not",
7
+ "sha": "synthetic"
8
+ },
9
+ "wire": "ops-list-blueprints",
10
+ "payload": {
11
+ "blueprints": [
12
+ {
13
+ "blueprintId": "bp_00000000-0000-4000-8000-000000001280",
14
+ "contractHash": "a8b8be414e9e53f7",
15
+ "appId": "IMud3W0m",
16
+ "codeS3Url": "s3://blueprint-code/code/be997120c9425551b4db6c041c32d632255ba166e5d3746fe3c4cbbbf3a2edd9.js",
17
+ "codeHash": "be997120c9425551b4db6c041c32d632255ba166e5d3746fe3c4cbbbf3a2edd9",
18
+ "source": {
19
+ "kind": "llm",
20
+ "generator": "ui-gen-default",
21
+ "model": "openai/gpt-6-luna"
22
+ },
23
+ "variance": {},
24
+ "createdAt": "2026-09-27T16:40:27.386Z",
25
+ "createdBy": "agent",
26
+ "contract": {
27
+ "propsSpec": {
28
+ "properties": {
29
+ "harbour": {
30
+ "schema": {
31
+ "type": "string"
32
+ },
33
+ "required": true
34
+ }
35
+ }
36
+ }
37
+ },
38
+ "build": {
39
+ "version": "0.25.0",
40
+ "mode": "constrained",
41
+ "digests": {
42
+ "promptTemplateSha256": "fdf5de0bdc977b698772e337d552dbe27cbc984382e6c0f68b0f5a7d8a953f00",
43
+ "boilerplateTemplateSha256": "9b4393cd686244d9e06d480f477dc85aa5b4469d45dfb103b76b873e22e65c24"
44
+ }
45
+ }
46
+ },
47
+ {
48
+ "blueprintId": "bp_00000000-0000-4000-8000-000000001281",
49
+ "contractHash": "a8b8be414e9e53f7",
50
+ "appId": "IMud3W0m",
51
+ "codeS3Url": "s3://blueprint-code/code/be997120c9425551b4db6c041c32d632255ba166e5d3746fe3c4cbbbf3a2edd9.js",
52
+ "codeHash": "be997120c9425551b4db6c041c32d632255ba166e5d3746fe3c4cbbbf3a2edd9",
53
+ "source": {
54
+ "kind": "llm",
55
+ "generator": "ui-gen-default",
56
+ "model": "openai/gpt-6-luna"
57
+ },
58
+ "variance": {},
59
+ "createdAt": "2026-09-27T16:40:27.386Z",
60
+ "createdBy": "agent",
61
+ "contract": {
62
+ "propsSpec": {
63
+ "properties": {
64
+ "harbour": {
65
+ "schema": {
66
+ "type": "string"
67
+ },
68
+ "required": true
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ ]
75
+ },
76
+ "expect": "accepted"
77
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "forward-view-proof-later-version",
3
+ "description": "FORWARD tolerance for the view-origin proof (ggui#1415, SPEC §4.7, VERSION-POLICY §3.6): a LATER release's runtime may send a proof of a later version (`v2.`, with more fields). SPEC §4.7 fixes the door shape for every version at 1 to 1024 characters of `[A-Za-z0-9._-]`, and a v1 proof is at most 901, so the room above 901 is the later versions'. This proof is longer than any v1 proof can be. It must still fit `VIEW_PROOF_RELAY_SHAPE`, so a relay that checks the shape keeps forwarding it (a relay capped at v1's maximum would fail this case), and today's parser must read it as `version_unknown`, never `malformed`: an unknown version is a runtime newer than the server, which is a different finding from a mangled proof, and at this release the call is accepted either way. Synthetic by construction: no landed release sends `v2`.",
4
+ "direction": "forward",
5
+ "release": {
6
+ "label": "a later ggui release (synthetic): a view proof of version 2",
7
+ "sha": "synthetic"
8
+ },
9
+ "wire": "view-proof",
10
+ "payload": {
11
+ "proof": "v2.eyJzZXNzaW9uSWQiOiJyZW5kZXJfbjEtdmlldy1wcm9vZiIsImFwcElkIjoiYXBwLTEiLCJraW5kIjoid3MiLCJpYXQiOjE3OTAwMDAwMDAsImV4cCI6MTc5MDAwMDE4MCwianRpIjoiai1uMS0xIiwia2lkIjoiYWJjZGVmZ2giLCJzcmMiOiJyZWFkIiwibGFuZSI6Mn0.Gxax31OLoS3D-X7buFyqcA.1790000000000.1.ypeBEsobvcr6wjGzmiPcTaeG7_gUfE5yuYB3ha_uSLs.Ln0sA6lQeuJl7PW1NWiFpTOTogKdJBOUmXJloaJa78Y.jPPrvazd_d1vE2c_lF9SPMmBPfL83ypCCiMNKDvfSno.JApCN0-oHSRQhjgoKkFsRrL_fR3FKYcecVHl7v_XexM.BbtfBFC9euhD7PCwpZaY_ZPYOjkwaG63z88z-7Ju3eQ.lqYQruNzWtZm16QKDwMEmLa4sm6jJApzCHI7Yad5Soc._jcik6xvyHZ9JIJ46c6su1OqV96NOzDvIIE5M5NdEzI.XkpFATZZBLlION0kvopTmR0WA6WUpj4WT8Dqs2HTJbU.OQV6gjuSVFmNqGnYyvwUltb3GXuhpgFqY390evoeb4k.rgEOb9-vL-iVxaOqeHwaQElHhJZDe1VWZ8QPISDTbHs.vbY9a3w12K0d4TugPWHEJlgTw7stAPR9rQXp0c4epHQ.p03as9nh6E5xhKe3G_i1kDlCEJMtJobtPXmTLy8kgJo.5XGKXuUJv0IF1IylJHO7pxtTM4xrnNFttSC71C71-dQ.KgBoV_T71GnQmivxHXAsRS30ih8q9bQ-XzytF87sQIg.0bt43PDDGf_ogPbxCfmYB_J9YxSp9aI63NLia26OY7I.M4QsAgRfWyfexpO2gj1vLHeC-aKxkMLa-4vhGDdKldM.E8RjxoojvGM2N7YT5j4AJeLUmM9S73aHeubtNHX0q6Y"
12
+ },
13
+ "expect": "accepted"
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "forward-view-proof-root-later-claim",
3
+ "description": "FORWARD tolerance for the view-origin proof's root (ggui#1415, SPEC §4.7, VERSION-POLICY §3.6): a LATER release's server may stamp its ws token's payload with a claim this release does not name (`lane`). SPEC §4.7: a reader decodes the root claims it names and ignores any others, so a v1 proof rooted in that token must still parse. A new value of the closed `src` set is deliberately NOT a forward case: §4.7 names `src` as the exception, and VERSION-POLICY §3.6 widens that set one release before any door stamps the value. Synthetic by construction.",
4
+ "direction": "forward",
5
+ "release": {
6
+ "label": "a later ggui release (synthetic): a v1 proof whose root carries an unnamed claim",
7
+ "sha": "synthetic"
8
+ },
9
+ "wire": "view-proof",
10
+ "payload": {
11
+ "proof": "v1.eyJzZXNzaW9uSWQiOiJyZW5kZXJfbjEtdmlldy1wcm9vZiIsImFwcElkIjoiYXBwLTEiLCJraW5kIjoid3MiLCJpYXQiOjE3OTAwMDAwMDAsImV4cCI6MTc5MDAwMDE4MCwianRpIjoiai1uMS0xIiwia2lkIjoiYWJjZGVmZ2giLCJzcmMiOiJyZWFkIiwibGFuZSI6Mn0.Gxax31OLoS3D-X7buFyqcA.1790000000000.1.ypeBEsobvcr6wjGzmiPcTaeG7_gUfE5yuYB3ha_uSLs.Ln0sA6lQeuJl7PW1NWiFpTOTogKdJBOUmXJloaJa78Y"
12
+ },
13
+ "expect": "accepted"
14
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "release-14-ops-list-blueprints",
3
+ "description": "A ggui_ops_list_blueprints result as ggui tag 14 (b68b964a7, serving prod and staging) emitted it for one generation-minted row, captured from staging serving that tag (`ggui-protocol-b68b964a7737`, 2026-09-27T16:40Z) on a fixture app after one cold render. Every byte is verbatim except `codeS3Url`: its bucket names the hosted deployment's store, so the value keeps the served shape (`s3://<bucket>/code/<codeHash>.js`) over a neutral bucket. The row carries NO `build`: tag 14 declares the stamp and no writer emits it. Today's opsListBlueprintsOutputSchema MUST accept the result, and the parsed row MUST still carry no `build` — an unstamped row reads as an unknown minting build, never an error, and a reader never invents one (ggui#1280, VERSION-POLICY §3.6).",
4
+ "release": {
5
+ "label": "ggui tag 14",
6
+ "sha": "b68b964a7"
7
+ },
8
+ "wire": "ops-list-blueprints",
9
+ "payload": {
10
+ "blueprints": [
11
+ {
12
+ "blueprintId": "bp_35eb73be-51be-479f-a0e3-dad47ea57739",
13
+ "contractHash": "a8b8be414e9e53f7",
14
+ "appId": "IMud3W0m",
15
+ "codeS3Url": "s3://blueprint-code/code/be997120c9425551b4db6c041c32d632255ba166e5d3746fe3c4cbbbf3a2edd9.js",
16
+ "codeHash": "be997120c9425551b4db6c041c32d632255ba166e5d3746fe3c4cbbbf3a2edd9",
17
+ "source": {
18
+ "kind": "llm",
19
+ "generator": "ui-gen-default",
20
+ "model": "openai/gpt-6-luna"
21
+ },
22
+ "variance": {},
23
+ "createdAt": "2026-09-27T16:40:27.386Z",
24
+ "createdBy": "agent",
25
+ "contract": {
26
+ "propsSpec": {
27
+ "properties": {
28
+ "harbour": {
29
+ "schema": {
30
+ "type": "string"
31
+ },
32
+ "required": true
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ]
39
+ },
40
+ "expect": "accepted"
41
+ }
@@ -1,5 +1,5 @@
1
1
  /** The protocol-owned wires the catalog can grade. */
2
- export declare const N1_COMPAT_WIRES: readonly ["app-theme", "app-theme-read", "app-theme-carry", "render-meta", "generation-profile", "ops-generate-blueprint", "handshake-suggestion", "render-result"];
2
+ export declare const N1_COMPAT_WIRES: readonly ["app-theme", "app-theme-read", "app-theme-carry", "render-meta", "generation-profile", "ops-generate-blueprint", "handshake-suggestion", "render-result", "ops-list-blueprints", "view-proof"];
3
3
  /** `backward`: the previous release's payload against today's parser. `forward`: a later release's payload against today's READ door. */
4
4
  export declare const N1_COMPAT_DIRECTIONS: readonly ["backward", "forward"];
5
5
  export type N1CompatDirection = (typeof N1_COMPAT_DIRECTIONS)[number];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/n1-compat-conformance/index.ts"],"names":[],"mappings":"AAgCA,sDAAsD;AACtD,eAAO,MAAM,eAAe,qKAAsK,CAAC;AAEnM,yIAAyI;AACzI,eAAO,MAAM,oBAAoB,kCAAmC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AACtE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iKAAiK;IACjK,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,4FAA4F;IAC5F,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,gGAAgG;IAChG,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAgDD,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAUhC,CAAC;AA6GpB,4DAA4D;AAC5D,wBAAgB,sBAAsB,IAAI,SAAS,cAAc,EAAE,CAoBlE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/n1-compat-conformance/index.ts"],"names":[],"mappings":"AA2CA,sDAAsD;AACtD,eAAO,MAAM,eAAe,0MAA2M,CAAC;AAExO,yIAAyI;AACzI,eAAO,MAAM,oBAAoB,kCAAmC,CAAC;AACrE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AACtE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iKAAiK;IACjK,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,4FAA4F;IAC5F,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,gGAAgG;IAChG,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAgDD,eAAO,MAAM,eAAe,EAAE,SAAS,YAAY,EAgBhC,CAAC;AAuNpB,4DAA4D;AAC5D,wBAAgB,sBAAsB,IAAI,SAAS,cAAc,EAAE,CAKlE"}
@@ -3,10 +3,12 @@
3
3
  *
4
4
  * Every BACKWARD case is the PREVIOUS release's payload for a protocol-owned
5
5
  * wire, tagged with that release's sha, and passes iff today's parser accepts
6
- * it. Every FORWARD case (ggui#1093 belt) is a LATER release's payload —
7
- * today's shape plus a top-level member today does not name, synthetic by
8
- * construction — and passes iff today's READ door keeps it, overlays intact,
9
- * stripping and naming the member. A case that starts failing is a receiver
6
+ * it. Every FORWARD case is a LATER release's payload, synthetic by
7
+ * construction, and passes iff today's READ side keeps it: a top-level
8
+ * member today does not name is stripped and named, overlays intact
9
+ * (ggui#1093 belt), and a view proof of a later version, or over a root
10
+ * claim today does not name, is recognised rather than refused as
11
+ * malformed (ggui#1415). A case that starts failing is a receiver
10
12
  * that broke N−1 across a rolling release — the fix is the receiver, never
11
13
  * the fixture; a fixture changes only when the pairing of record moves
12
14
  * (`docs/protocol/VERSION-POLICY.md` §3.6).
@@ -14,19 +16,25 @@
14
16
  * Pure-function catalog: no transport, no adopter input — graded on every
15
17
  * `runConformance()` and by the kit's own unit lane.
16
18
  */
17
- import { appGenerationProfileSchema, appThemeGetResponseSchema, appThemeSchema, handshakeSuggestionSchema, opsGenerateBlueprintInputSchema, parseAppThemeAtReadDoor, renderOutputSchema } from '@ggui-ai/protocol';
18
- import { MCP_APP_AI_GGUI_RENDER_META_KEY, parseMcpAppAiGguiRenderMeta, } from '@ggui-ai/protocol/integrations/mcp-apps';
19
+ import { appGenerationProfileSchema, appThemeGetResponseSchema, appThemeSchema, handshakeSuggestionSchema, opsGenerateBlueprintInputSchema, opsListBlueprintsOutputSchema, parseAppThemeAtReadDoor, renderOutputSchema } from '@ggui-ai/protocol';
20
+ import { MCP_APP_AI_GGUI_RENDER_META_KEY, parseMcpAppAiGguiRenderMeta, parseViewProof, VIEW_PROOF_RELAY_SHAPE, VIEW_PROOF_V1_MAX_CHARS, } from '@ggui-ai/protocol/integrations/mcp-apps';
19
21
  import release2AppThemeV2 from './cases/release-2-app-theme-v2.json' with { type: 'json' };
20
22
  import release2RenderMeta from './cases/release-2-render-meta.json' with { type: 'json' };
21
23
  import release2GenerationProfile from './cases/release-2-generation-profile.json' with { type: 'json' };
22
24
  import release2OpsGenerateBlueprint from './cases/release-2-ops-generate-blueprint.json' with { type: 'json' };
23
25
  import release91HandshakeSuggestion from './cases/release-9-1-handshake-suggestion.json' with { type: 'json' };
24
26
  import release13RenderResult from './cases/release-13-render-result.json' with { type: 'json' };
27
+ import release14OpsListBlueprints from './cases/release-14-ops-list-blueprints.json' with { type: 'json' };
25
28
  import forwardAppThemeUnknownMember from './cases/forward-app-theme-unknown-member.json' with { type: 'json' };
26
29
  import forwardAppThemeCarryUnknownMember from './cases/forward-app-theme-carry-unknown-member.json' with { type: 'json' };
30
+ import forwardAppThemeCarryUninterpretable from './cases/forward-app-theme-carry-uninterpretable.json' with { type: 'json' };
27
31
  import forwardRenderMetaUnknownMember from './cases/forward-render-meta-unknown-member.json' with { type: 'json' };
32
+ import forwardOpsListBlueprintsStamped from './cases/forward-ops-list-blueprints-stamped.json' with { type: 'json' };
33
+ import forwardOpsListBlueprintsCloned from './cases/forward-ops-list-blueprints-cloned.json' with { type: 'json' };
34
+ import forwardViewProofLaterVersion from './cases/forward-view-proof-later-version.json' with { type: 'json' };
35
+ import forwardViewProofRootLaterClaim from './cases/forward-view-proof-root-later-claim.json' with { type: 'json' };
28
36
  /** The protocol-owned wires the catalog can grade. */
29
- export const N1_COMPAT_WIRES = ['app-theme', 'app-theme-read', 'app-theme-carry', 'render-meta', 'generation-profile', 'ops-generate-blueprint', 'handshake-suggestion', 'render-result'];
37
+ export const N1_COMPAT_WIRES = ['app-theme', 'app-theme-read', 'app-theme-carry', 'render-meta', 'generation-profile', 'ops-generate-blueprint', 'handshake-suggestion', 'render-result', 'ops-list-blueprints', 'view-proof'];
30
38
  /** `backward`: the previous release's payload against today's parser. `forward`: a later release's payload against today's READ door. */
31
39
  export const N1_COMPAT_DIRECTIONS = ['backward', 'forward'];
32
40
  function isN1CompatWire(v) {
@@ -84,9 +92,15 @@ export const N1_COMPAT_CASES = [
84
92
  release2OpsGenerateBlueprint,
85
93
  release91HandshakeSuggestion,
86
94
  release13RenderResult,
95
+ release14OpsListBlueprints,
87
96
  forwardAppThemeUnknownMember,
88
97
  forwardAppThemeCarryUnknownMember,
98
+ forwardAppThemeCarryUninterpretable,
89
99
  forwardRenderMetaUnknownMember,
100
+ forwardOpsListBlueprintsStamped,
101
+ forwardOpsListBlueprintsCloned,
102
+ forwardViewProofLaterVersion,
103
+ forwardViewProofRootLaterClaim,
90
104
  ].map(n1CompatCase);
91
105
  function gradeAppTheme(payload) {
92
106
  const r = appThemeSchema.safeParse(payload);
@@ -116,6 +130,14 @@ function gradeAppThemeCarry(payload) {
116
130
  const r = appThemeGetResponseSchema.safeParse(payload);
117
131
  if (!r.success)
118
132
  return { pass: false, detail: `appThemeGetResponseSchema refused a later release's carry response: ${r.error.issues.map((i) => i.message).join('; ')}` };
133
+ // ggui#1175 — a stored document that is not a theme comes back `theme: null` WITH `uninterpretable`;
134
+ // the case grades that today's schema reads it as that, never as "no theme".
135
+ if (r.data.theme === null) {
136
+ const u = r.data.uninterpretable;
137
+ if (u === undefined)
138
+ return { pass: false, detail: 'a null-theme case must carry `uninterpretable`: a bare null is "no theme", not a forward case' };
139
+ return { pass: true, detail: `appThemeGetResponseSchema: theme null, uninterpretable (${u.issueCount} issue(s): ${u.issues.map((i) => i.code).join(', ')})` };
140
+ }
119
141
  const sentTheme = isRecord(payload) ? payload['theme'] : undefined;
120
142
  if (!isRecord(sentTheme))
121
143
  return { pass: false, detail: 'the case must carry a non-null theme' };
@@ -196,24 +218,108 @@ function gradeRenderResult(payload) {
196
218
  ? { pass: true, detail: 'renderOutputSchema: accepted' }
197
219
  : { pass: false, detail: `renderOutputSchema refused the previous release's render result: ${r.error.issues.map((i) => i.message).join('; ')}` };
198
220
  }
221
+ // ggui#1280 — the blueprint rows the served release lists must keep parsing once
222
+ // rows carry the minting build, and a row that carries none must not gain one:
223
+ // absence is the unknown-build category, never a value a reader fills in.
224
+ // ggui#1570 — the same for a copied row's `clonedFrom`: kept verbatim on the
225
+ // copy, and never filled in on a row that is not one.
226
+ function gradeOpsListBlueprints(payload) {
227
+ const r = opsListBlueprintsOutputSchema.safeParse(payload);
228
+ if (!r.success) {
229
+ return { pass: false, detail: `opsListBlueprintsOutputSchema refused the previous release's list: ${r.error.issues.map((i) => i.message).join('; ')}` };
230
+ }
231
+ const sent = isRecord(payload) && Array.isArray(payload['blueprints']) ? payload['blueprints'] : [];
232
+ for (const [i, row] of r.data.blueprints.entries()) {
233
+ const sentRow = sent[i];
234
+ const sentBuild = isRecord(sentRow) ? sentRow['build'] : undefined;
235
+ if (canonicalJson(row.build) !== canonicalJson(sentBuild)) {
236
+ return { pass: false, detail: `row ${i}'s build changed across the parse (sent ${canonicalJson(sentBuild)}, parsed ${canonicalJson(row.build)})` };
237
+ }
238
+ const sentClonedFrom = isRecord(sentRow) ? sentRow['clonedFrom'] : undefined;
239
+ if (canonicalJson(row.clonedFrom) !== canonicalJson(sentClonedFrom)) {
240
+ return { pass: false, detail: `row ${i}'s clonedFrom changed across the parse (sent ${canonicalJson(sentClonedFrom)}, parsed ${canonicalJson(row.clonedFrom)})` };
241
+ }
242
+ }
243
+ return { pass: true, detail: `opsListBlueprintsOutputSchema: accepted, ${r.data.blueprints.length} row(s), build and clonedFrom kept as sent` };
244
+ }
245
+ /** The keys of a root's JSON payload, decoded without trusting it; `undefined` when it is not a JSON object. */
246
+ function rootKeys(root) {
247
+ try {
248
+ const padded = root.replace(/-/g, '+').replace(/_/g, '/') + '==='.slice((root.length + 3) % 4);
249
+ const json = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(Uint8Array.from(atob(padded), (c) => c.charCodeAt(0))));
250
+ return isRecord(json) ? Object.keys(json) : undefined;
251
+ }
252
+ catch {
253
+ // Not base64url JSON: the case's own root is broken, reported by the caller.
254
+ return undefined;
255
+ }
256
+ }
257
+ // ggui#1415 — a later release's view proof (SPEC §4.7). The door shape is fixed
258
+ // for every version, so a relay that checks it must keep forwarding the proof,
259
+ // including one longer than any v1 proof. A later version reads
260
+ // `version_unknown`, never `malformed`; a v1 proof whose root carries a claim
261
+ // this release does not name still parses.
262
+ function gradeViewProof(payload) {
263
+ const proof = isRecord(payload) ? payload['proof'] : undefined;
264
+ if (typeof proof !== 'string')
265
+ return { pass: false, detail: 'the case must carry a proof string' };
266
+ if (!VIEW_PROOF_RELAY_SHAPE.test(proof)) {
267
+ return { pass: false, detail: "the later release's proof is off the relay door shape, so a relay that checks it would drop it" };
268
+ }
269
+ const parsed = parseViewProof(proof);
270
+ if (!proof.startsWith('v1.')) {
271
+ if (proof.length <= VIEW_PROOF_V1_MAX_CHARS) {
272
+ return { pass: false, detail: `the case must use the room the door keeps for later versions: its proof is ${proof.length} characters, within v1's ${VIEW_PROOF_V1_MAX_CHARS}` };
273
+ }
274
+ return !parsed.ok && parsed.reason === 'version_unknown'
275
+ ? { pass: true, detail: `parseViewProof: a later version of ${proof.length} characters reads version_unknown, and the relay shape holds` }
276
+ : { pass: false, detail: `parseViewProof read a later version as ${parsed.ok ? 'a v1 proof' : parsed.reason}, not version_unknown` };
277
+ }
278
+ if (!parsed.ok)
279
+ return { pass: false, detail: `parseViewProof refused a v1 proof over a later root claim: ${parsed.reason}` };
280
+ const named = new Set(Object.keys(parsed.proof.claims));
281
+ const unnamed = (rootKeys(parsed.proof.root) ?? []).filter((k) => !named.has(k));
282
+ return unnamed.length > 0
283
+ ? { pass: true, detail: `parseViewProof: a v1 proof whose root carries [${unnamed.join(', ')}] still parses` }
284
+ : { pass: false, detail: 'the case must root its proof in claims this release does not name' };
285
+ }
286
+ /**
287
+ * One grader per wire, exhaustively: a wire added to {@link N1_COMPAT_WIRES}
288
+ * without an arm here does not compile, rather than falling through to
289
+ * another wire's grader.
290
+ */
291
+ function gradeWire(wire, payload) {
292
+ switch (wire) {
293
+ case 'app-theme':
294
+ return gradeAppTheme(payload);
295
+ case 'app-theme-read':
296
+ return gradeAppThemeRead(payload);
297
+ case 'app-theme-carry':
298
+ return gradeAppThemeCarry(payload);
299
+ case 'render-meta':
300
+ return gradeRenderMeta(payload);
301
+ case 'generation-profile':
302
+ return gradeGenerationProfile(payload);
303
+ case 'ops-generate-blueprint':
304
+ return gradeOpsGenerateBlueprint(payload);
305
+ case 'handshake-suggestion':
306
+ return gradeHandshakeSuggestion(payload);
307
+ case 'render-result':
308
+ return gradeRenderResult(payload);
309
+ case 'ops-list-blueprints':
310
+ return gradeOpsListBlueprints(payload);
311
+ case 'view-proof':
312
+ return gradeViewProof(payload);
313
+ default: {
314
+ const unhandled = wire;
315
+ return { pass: false, detail: `no grader for wire ${String(unhandled)}` };
316
+ }
317
+ }
318
+ }
199
319
  /** Grade every N−1 case against the protocol as shipped. */
200
320
  export function runN1CompatConformance() {
201
321
  return N1_COMPAT_CASES.map((c) => {
202
- const graded = c.wire === 'app-theme'
203
- ? gradeAppTheme(c.payload)
204
- : c.wire === 'app-theme-read'
205
- ? gradeAppThemeRead(c.payload)
206
- : c.wire === 'app-theme-carry'
207
- ? gradeAppThemeCarry(c.payload)
208
- : c.wire === 'render-meta'
209
- ? gradeRenderMeta(c.payload)
210
- : c.wire === 'generation-profile'
211
- ? gradeGenerationProfile(c.payload)
212
- : c.wire === 'handshake-suggestion'
213
- ? gradeHandshakeSuggestion(c.payload)
214
- : c.wire === 'render-result'
215
- ? gradeRenderResult(c.payload)
216
- : gradeOpsGenerateBlueprint(c.payload);
322
+ const graded = gradeWire(c.wire, c.payload);
217
323
  return { name: c.name, direction: c.direction, pass: graded.pass, detail: `${c.release.label} (${c.release.sha}) → ${graded.detail}` };
218
324
  });
219
325
  }
@@ -24,6 +24,11 @@ export declare function formatSummary(result: ConformanceResult): string;
24
24
  * programmatic consumers that want a post-run diagnostic dump.
25
25
  */
26
26
  export declare function formatFailures(failures: readonly ConformanceFailure[]): string;
27
+ /**
28
+ * Emit the full set of warnings (ggui#1526): SHOULD fixtures whose
29
+ * expectation was not met, with the same evidence a failure carries.
30
+ */
31
+ export declare function formatWarnings(warnings: readonly ConformanceFailure[]): string;
27
32
  /**
28
33
  * Emit the full set of skip reasons — useful for `--verbose` mode to
29
34
  * see which directives an implementation hasn't wired yet.
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAM9B,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,sBAA2B,GACnC,mBAAmB,CA0CrB;AAMD,wBAAgB,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAejE;AAyBD,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAW/D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CAW9E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CAStE"}
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAM9B,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,sBAA2B,GACnC,mBAAmB,CA+CrB;AAMD,wBAAgB,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAejE;AA2BD,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAa/D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CAW9E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GAAG,MAAM,CAW9E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,CAStE"}
package/dist/reporter.js CHANGED
@@ -46,6 +46,12 @@ export function createDefaultReporter(options = {}) {
46
46
  write(` ${MARK_FAIL} ${padRight(failure.name, 48)} ${failure.criterion}`);
47
47
  write(` → ${failure.message}`);
48
48
  },
49
+ onFixtureWarn(warning) {
50
+ if (!streaming)
51
+ return;
52
+ write(` ${MARK_WARN} ${padRight(warning.name, 48)} ${warning.criterion}`);
53
+ write(` → ${warning.message}`);
54
+ },
49
55
  onFixtureSkip(name, reason) {
50
56
  if (!streaming)
51
57
  return;
@@ -92,23 +98,26 @@ export function formatScorecard(result) {
92
98
  function scorecardRow(slug, result, belongs) {
93
99
  const passedCount = result.passed.filter(belongs).length;
94
100
  const failedCount = result.failed.filter((f) => belongs(f.name)).length;
101
+ const warnedCount = result.warned.filter((w) => belongs(w.name)).length;
95
102
  const skippedCount = result.skipped.filter((s) => belongs(s.name)).length;
96
- const total = passedCount + failedCount + skippedCount;
103
+ const total = passedCount + failedCount + warnedCount + skippedCount;
97
104
  if (total === 0)
98
105
  return [];
99
- const tone = failedCount > 0 ? MARK_FAIL : passedCount > 0 ? MARK_PASS : MARK_SKIP;
106
+ const tone = failedCount > 0 ? MARK_FAIL : warnedCount > 0 ? MARK_WARN : passedCount > 0 ? MARK_PASS : MARK_SKIP;
100
107
  return [
101
- ` ${tone} ${padRight(slug, 38)} ${passedCount}/${total} pass${failedCount > 0 ? ` · ${failedCount} fail` : ''}${skippedCount > 0 ? ` · ${skippedCount} skip` : ''}`,
108
+ ` ${tone} ${padRight(slug, 38)} ${passedCount}/${total} pass${failedCount > 0 ? ` · ${failedCount} fail` : ''}${warnedCount > 0 ? ` · ${warnedCount} warn` : ''}${skippedCount > 0 ? ` · ${skippedCount} skip` : ''}`,
102
109
  ];
103
110
  }
104
111
  export function formatSummary(result) {
105
112
  const passed = result.passed.length;
106
113
  const failed = result.failed.length;
114
+ const warned = result.warned.length;
107
115
  const skipped = result.skipped.length;
108
- const total = passed + failed + skipped;
116
+ const total = passed + failed + warned + skipped;
109
117
  return [
110
118
  ` Passed: ${String(passed).padStart(3)}`,
111
119
  ` Failed: ${String(failed).padStart(3)}`,
120
+ ` Warned: ${String(warned).padStart(3)} (SHOULD, never fails a run)`,
112
121
  ` Skipped: ${String(skipped).padStart(3)}`,
113
122
  ` Total: ${String(total).padStart(3)} (${result.totalMs}ms)`,
114
123
  ].join('\n');
@@ -130,6 +139,23 @@ export function formatFailures(failures) {
130
139
  }
131
140
  return lines.join('\n');
132
141
  }
142
+ /**
143
+ * Emit the full set of warnings (ggui#1526): SHOULD fixtures whose
144
+ * expectation was not met, with the same evidence a failure carries.
145
+ */
146
+ export function formatWarnings(warnings) {
147
+ if (warnings.length === 0)
148
+ return '';
149
+ const lines = ['', 'Warnings (SHOULD):', ''];
150
+ for (const warning of warnings) {
151
+ lines.push(`${MARK_WARN} ${warning.name} (${warning.criterion})`);
152
+ lines.push(` ${warning.message}`);
153
+ lines.push(` expected: ${safeStringify(warning.expected)}`);
154
+ lines.push(` received: ${safeStringify(warning.received)}`);
155
+ lines.push('');
156
+ }
157
+ return lines.join('\n');
158
+ }
133
159
  /**
134
160
  * Emit the full set of skip reasons — useful for `--verbose` mode to
135
161
  * see which directives an implementation hasn't wired yet.
@@ -1,7 +1,7 @@
1
1
  import type { HostContextObservedPayload } from '@ggui-ai/protocol';
2
2
  import type { ConformanceHost, SetupStep as HostSetupStep } from './conformance-host.js';
3
3
  import { type MatchResult } from './match-behavior.js';
4
- import type { AuthConfig, SessionStateBehavior, StreamUpdateBehavior, VersionMismatchBehavior } from './types.js';
4
+ import type { AuthConfig, SessionStateBehavior, StreamUpdateBehavior, TestCase, VersionMismatchBehavior } from './types.js';
5
5
  import { type PreGenerationRefusalInput, type ProjectedRefusalResult } from './refusal-envelope-conformance/index.js';
6
6
  import { type RefusalRegistryView } from './registry-completeness/index.js';
7
7
  import { type ProjectedTransportRefusal, type TransportRefusalInput } from './transport-refusal-conformance/index.js';
@@ -79,6 +79,13 @@ export type PureFunctionCatalogSlug = (typeof PURE_FUNCTION_CATALOG_SLUGS)[numbe
79
79
  export interface ConformanceResult {
80
80
  readonly passed: readonly string[];
81
81
  readonly failed: readonly ConformanceFailure[];
82
+ /**
83
+ * SHOULD fixtures (`TestCase.level: 'should'`) whose expectation was
84
+ * not met, with the same evidence a failure carries. A warning never
85
+ * fails a run: a SHOULD is a recommendation the implementation may
86
+ * decline, and the kit reports it so the declining is visible.
87
+ */
88
+ readonly warned: readonly ConformanceFailure[];
82
89
  readonly skipped: readonly SkippedFixture[];
83
90
  readonly totalMs: number;
84
91
  }
@@ -97,6 +104,8 @@ export interface ConformanceReporter {
97
104
  onStart?(totalFixtures: number): void;
98
105
  onFixturePass?(name: string, elapsedMs: number): void;
99
106
  onFixtureFail?(failure: ConformanceFailure): void;
107
+ /** A SHOULD fixture's expectation was not met (see `ConformanceResult.warned`). */
108
+ onFixtureWarn?(warning: ConformanceFailure): void;
100
109
  onFixtureSkip?(name: string, reason: string): void;
101
110
  /**
102
111
  * Invoked when a teardown step throws. Non-fatal — the fixture's
@@ -107,6 +116,33 @@ export interface ConformanceReporter {
107
116
  onComplete?(result: ConformanceResult): void;
108
117
  }
109
118
  export declare function runConformance(config: RunConformanceConfig): Promise<ConformanceResult>;
119
+ /**
120
+ * One fixture's verdict. Exported for unit tests; not part of the
121
+ * package's public API.
122
+ */
123
+ export type FixtureOutcome = {
124
+ readonly kind: 'pass';
125
+ readonly elapsedMs: number;
126
+ } | {
127
+ readonly kind: 'fail';
128
+ readonly failure: ConformanceFailure;
129
+ } | {
130
+ readonly kind: 'warn';
131
+ readonly warning: ConformanceFailure;
132
+ } | {
133
+ readonly kind: 'skip';
134
+ readonly reason: string;
135
+ };
136
+ /**
137
+ * Turn an observed match into a fixture's verdict (ggui#1526). An unmet
138
+ * expectation is a failure for a MUST fixture and a warning for a
139
+ * SHOULD one (`TestCase.level: 'should'`), carrying the same evidence
140
+ * either way; what the transport cannot observe is a skip at either
141
+ * level.
142
+ *
143
+ * Exported for unit tests; not part of the package's public API.
144
+ */
145
+ export declare function gradeFixtureMatch(fixture: Pick<TestCase, 'name' | 'level'>, match: MatchResult, elapsedMs: number): FixtureOutcome;
110
146
  /**
111
147
  * Resolve the WS endpoint from `config.serverUrl`:
112
148
  * - `http://` / `https://` schemes become `ws://` / `wss://`; a
@@ -1 +1 @@
1
- {"version":3,"file":"run-conformance.d.ts","sourceRoot":"","sources":["../src/run-conformance.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-conformance.d.ts","sourceRoot":"","sources":["../src/run-conformance.ts"],"names":[],"mappings":"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"}
@@ -96,6 +96,7 @@ export async function runConformance(config) {
96
96
  reporter.onStart?.(fixtures.length);
97
97
  const passed = [];
98
98
  const failed = [];
99
+ const warned = [];
99
100
  const skipped = [];
100
101
  for (const fixture of fixtures) {
101
102
  if (config.signal?.aborted === true) {
@@ -116,6 +117,10 @@ export async function runConformance(config) {
116
117
  failed.push(outcome.failure);
117
118
  reporter.onFixtureFail?.(outcome.failure);
118
119
  break;
120
+ case 'warn':
121
+ warned.push(outcome.warning);
122
+ reporter.onFixtureWarn?.(outcome.warning);
123
+ break;
119
124
  case 'skip':
120
125
  skipped.push({ name: fixture.name, reason: outcome.reason });
121
126
  reporter.onFixtureSkip?.(fixture.name, outcome.reason);
@@ -126,6 +131,7 @@ export async function runConformance(config) {
126
131
  const result = {
127
132
  passed,
128
133
  failed,
134
+ warned,
129
135
  skipped,
130
136
  totalMs: Date.now() - started,
131
137
  };
@@ -299,6 +305,29 @@ async function runPureFunctionCatalogs(config, reporter, buckets) {
299
305
  }
300
306
  }
301
307
  }
308
+ /**
309
+ * Turn an observed match into a fixture's verdict (ggui#1526). An unmet
310
+ * expectation is a failure for a MUST fixture and a warning for a
311
+ * SHOULD one (`TestCase.level: 'should'`), carrying the same evidence
312
+ * either way; what the transport cannot observe is a skip at either
313
+ * level.
314
+ *
315
+ * Exported for unit tests; not part of the package's public API.
316
+ */
317
+ export function gradeFixtureMatch(fixture, match, elapsedMs) {
318
+ if (match.kind === 'pass')
319
+ return { kind: 'pass', elapsedMs };
320
+ if (match.kind === 'unmatchable-on-ws')
321
+ return { kind: 'skip', reason: match.reason };
322
+ const unmet = {
323
+ name: fixture.name,
324
+ criterion: criterionForFixture(fixture.name),
325
+ expected: match.expected,
326
+ received: match.received,
327
+ message: match.message,
328
+ };
329
+ return fixture.level === 'should' ? { kind: 'warn', warning: unmet } : { kind: 'fail', failure: unmet };
330
+ }
302
331
  async function runOneFixture(fixture, config, _reporter) {
303
332
  const fixtureStarted = Date.now();
304
333
  if (fixture.skipReason !== null) {
@@ -393,23 +422,7 @@ async function runOneFixture(fixture, config, _reporter) {
393
422
  }
394
423
  }
395
424
  }
396
- if (match.kind === 'pass') {
397
- return { kind: 'pass', elapsedMs: Date.now() - fixtureStarted };
398
- }
399
- if (match.kind === 'fail') {
400
- return {
401
- kind: 'fail',
402
- failure: {
403
- name: fixture.name,
404
- criterion: criterionForFixture(fixture.name),
405
- expected: match.expected,
406
- received: match.received,
407
- message: match.message,
408
- },
409
- };
410
- }
411
- // match.kind === 'unmatchable-on-ws'
412
- return { kind: 'skip', reason: match.reason };
425
+ return gradeFixtureMatch(fixture, match, Date.now() - fixtureStarted);
413
426
  }
414
427
  catch (err) {
415
428
  return {
@@ -758,6 +771,8 @@ function slugToCriterion(slug) {
758
771
  return 'Host-context persistence — host_context_observed MUST persist onto GguiSession.hostContext';
759
772
  case 'reserved-channel-authority':
760
773
  return 'SPEC §4.4 reserved-channel authority';
774
+ case 'reserved-channel-replay':
775
+ return 'SPEC §12.2.1 — a fresh subscribe SHOULD replay known-reserved channels after the ack (ggui#1521)';
761
776
  case 'schema-version-handshake':
762
777
  return 'Protocol #3 version negotiation';
763
778
  case 'subscribe-app-scope':