@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.
- package/README.md +45 -25
- package/dist/cli.d.ts +4 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -8
- package/dist/dispatch-idempotency-conformance/index.d.ts +78 -0
- package/dist/dispatch-idempotency-conformance/index.d.ts.map +1 -0
- package/dist/dispatch-idempotency-conformance/index.js +173 -0
- package/dist/fixtures/index.d.ts +22 -4
- package/dist/fixtures/index.d.ts.map +1 -1
- package/dist/fixtures/index.js +23 -3
- package/dist/fixtures/reserved-channel-replay/fresh-subscribe-replays-reserved-preview.json +30 -0
- package/dist/fixtures/reserved-channel-replay/index.d.ts +4 -0
- package/dist/fixtures/reserved-channel-replay/index.d.ts.map +1 -0
- package/dist/fixtures/reserved-channel-replay/index.js +18 -0
- package/dist/host-helper-conformance/index.d.ts +144 -2
- package/dist/host-helper-conformance/index.d.ts.map +1 -1
- package/dist/host-helper-conformance/index.js +309 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/match-behavior.d.ts.map +1 -1
- package/dist/match-behavior.js +47 -8
- package/dist/n1-compat-conformance/cases/forward-app-theme-carry-uninterpretable.json +21 -0
- package/dist/n1-compat-conformance/cases/forward-ops-list-blueprints-cloned.json +88 -0
- package/dist/n1-compat-conformance/cases/forward-ops-list-blueprints-stamped.json +77 -0
- package/dist/n1-compat-conformance/cases/forward-view-proof-later-version.json +14 -0
- package/dist/n1-compat-conformance/cases/forward-view-proof-root-later-claim.json +14 -0
- package/dist/n1-compat-conformance/cases/release-14-ops-list-blueprints.json +41 -0
- package/dist/n1-compat-conformance/index.d.ts +1 -1
- package/dist/n1-compat-conformance/index.d.ts.map +1 -1
- package/dist/n1-compat-conformance/index.js +128 -22
- package/dist/reporter.d.ts +5 -0
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +30 -4
- package/dist/run-conformance.d.ts +37 -1
- package/dist/run-conformance.d.ts.map +1 -1
- package/dist/run-conformance.js +32 -17
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- 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":"
|
|
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
|
|
7
|
-
* today's
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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
|
|
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
|
}
|
package/dist/reporter.d.ts
CHANGED
|
@@ -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.
|
package/dist/reporter.d.ts.map
CHANGED
|
@@ -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,
|
|
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":"AA8DA,OAAO,KAAK,EACV,0BAA0B,EAG3B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,KAAK,EACV,eAAe,EACf,SAAS,IAAI,aAAa,EAE3B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAC7F,OAAO,KAAK,EAEV,UAAU,EAEV,oBAAoB,EACpB,oBAAoB,
|
|
1
|
+
{"version":3,"file":"run-conformance.d.ts","sourceRoot":"","sources":["../src/run-conformance.ts"],"names":[],"mappings":"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"}
|
package/dist/run-conformance.js
CHANGED
|
@@ -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
|
-
|
|
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':
|