@ggui-ai/protocol-conformance 0.24.0 → 0.26.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 +195 -1
- package/dist/host-helper-conformance/index.d.ts.map +1 -1
- package/dist/host-helper-conformance/index.js +383 -2
- 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-13-render-result.json +25 -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 +138 -20
- 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,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "forward-ops-list-blueprints-cloned",
|
|
3
|
+
"description": "FORWARD (ggui#1570 emit, VERSION-POLICY §3.6): the ggui_ops_list_blueprints result the release after the declare step writes once a copied row marks itself — synthetic by construction, so it carries no landed sha. Two rows built on the stamped forward case's first row: the original, with no `clonedFrom`, and its copy, which carries the SAME bytes, `source` and `build` (true about the bytes) under its own id and variance, and `clonedFrom` naming the original. The reader MUST accept the result, keep `clonedFrom` VERBATIM on the copy and leave the original without one. A reader that dropped or rewrote the mark — the declare step's reader declares it, and a later change to `opsListBlueprintsOutputSchema` must not lose it — fails here, and so would a copy that reads as a second production of its generation.",
|
|
4
|
+
"direction": "forward",
|
|
5
|
+
"release": {
|
|
6
|
+
"label": "a later ggui release (synthetic): the stamped served row, and its copy",
|
|
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-000000001570",
|
|
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
|
+
"persona": "control"
|
|
60
|
+
},
|
|
61
|
+
"createdAt": "2026-09-30T09:00:00.000Z",
|
|
62
|
+
"createdBy": "operator",
|
|
63
|
+
"contract": {
|
|
64
|
+
"propsSpec": {
|
|
65
|
+
"properties": {
|
|
66
|
+
"harbour": {
|
|
67
|
+
"schema": {
|
|
68
|
+
"type": "string"
|
|
69
|
+
},
|
|
70
|
+
"required": true
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"build": {
|
|
76
|
+
"version": "0.25.0",
|
|
77
|
+
"mode": "constrained",
|
|
78
|
+
"digests": {
|
|
79
|
+
"promptTemplateSha256": "fdf5de0bdc977b698772e337d552dbe27cbc984382e6c0f68b0f5a7d8a953f00",
|
|
80
|
+
"boilerplateTemplateSha256": "9b4393cd686244d9e06d480f477dc85aa5b4469d45dfb103b76b873e22e65c24"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"clonedFrom": "bp_00000000-0000-4000-8000-000000001280"
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
"expect": "accepted"
|
|
88
|
+
}
|
|
@@ -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,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "release-13-render-result",
|
|
3
|
+
"description": "A ggui_render result as ggui tag 13 (5903985f2, serving prod) emitted it on a cold generation — captured verbatim from dev serving that tag (`ggui-protocol-5903985f2f46`, 2026-09-27T04:36Z) on a fixture app: `outcome: rendered`, the six identity fields, and the cache marker, WITHOUT `effort`. Today's renderOutputSchema MUST accept it: `effort` (#1459) is optional, so a consumer holding this release's result keeps parsing (VERSION-POLICY §3.6).",
|
|
4
|
+
"release": {
|
|
5
|
+
"label": "ggui tag 13",
|
|
6
|
+
"sha": "5903985f2"
|
|
7
|
+
},
|
|
8
|
+
"wire": "render-result",
|
|
9
|
+
"payload": {
|
|
10
|
+
"outcome": "rendered",
|
|
11
|
+
"sessionId": "render_63607d1b-afbf-405b-aa0d-43e1c27fc474",
|
|
12
|
+
"resourceUri": "ui://ggui/render/render_63607d1b-afbf-405b-aa0d-43e1c27fc474/9a679f0634bac356",
|
|
13
|
+
"action": "create",
|
|
14
|
+
"contractHash": "9a679f0634bac356",
|
|
15
|
+
"blueprintId": "bp_62c8adae-6405-4093-8a35-15f827a6f703",
|
|
16
|
+
"variantKey": "44136fa355b3678a",
|
|
17
|
+
"cache": {
|
|
18
|
+
"hit": false,
|
|
19
|
+
"llmCallsAvoided": 0,
|
|
20
|
+
"kind": "cold",
|
|
21
|
+
"reason": "cold: generated fresh — no saved interface fit this render"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"expect": "accepted"
|
|
25
|
+
}
|
|
@@ -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"];
|
|
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,18 +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 } 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' };
|
|
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' };
|
|
24
28
|
import forwardAppThemeUnknownMember from './cases/forward-app-theme-unknown-member.json' with { type: 'json' };
|
|
25
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' };
|
|
26
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' };
|
|
27
36
|
/** The protocol-owned wires the catalog can grade. */
|
|
28
|
-
export const N1_COMPAT_WIRES = ['app-theme', 'app-theme-read', 'app-theme-carry', 'render-meta', 'generation-profile', 'ops-generate-blueprint', 'handshake-suggestion'];
|
|
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'];
|
|
29
38
|
/** `backward`: the previous release's payload against today's parser. `forward`: a later release's payload against today's READ door. */
|
|
30
39
|
export const N1_COMPAT_DIRECTIONS = ['backward', 'forward'];
|
|
31
40
|
function isN1CompatWire(v) {
|
|
@@ -82,9 +91,16 @@ export const N1_COMPAT_CASES = [
|
|
|
82
91
|
release2GenerationProfile,
|
|
83
92
|
release2OpsGenerateBlueprint,
|
|
84
93
|
release91HandshakeSuggestion,
|
|
94
|
+
release13RenderResult,
|
|
95
|
+
release14OpsListBlueprints,
|
|
85
96
|
forwardAppThemeUnknownMember,
|
|
86
97
|
forwardAppThemeCarryUnknownMember,
|
|
98
|
+
forwardAppThemeCarryUninterpretable,
|
|
87
99
|
forwardRenderMetaUnknownMember,
|
|
100
|
+
forwardOpsListBlueprintsStamped,
|
|
101
|
+
forwardOpsListBlueprintsCloned,
|
|
102
|
+
forwardViewProofLaterVersion,
|
|
103
|
+
forwardViewProofRootLaterClaim,
|
|
88
104
|
].map(n1CompatCase);
|
|
89
105
|
function gradeAppTheme(payload) {
|
|
90
106
|
const r = appThemeSchema.safeParse(payload);
|
|
@@ -114,6 +130,14 @@ function gradeAppThemeCarry(payload) {
|
|
|
114
130
|
const r = appThemeGetResponseSchema.safeParse(payload);
|
|
115
131
|
if (!r.success)
|
|
116
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
|
+
}
|
|
117
141
|
const sentTheme = isRecord(payload) ? payload['theme'] : undefined;
|
|
118
142
|
if (!isRecord(sentTheme))
|
|
119
143
|
return { pass: false, detail: 'the case must carry a non-null theme' };
|
|
@@ -186,22 +210,116 @@ function gradeHandshakeSuggestion(payload) {
|
|
|
186
210
|
? { pass: true, detail: 'handshakeSuggestionSchema: accepted' }
|
|
187
211
|
: { pass: false, detail: `handshakeSuggestionSchema refused the previous release's suggestion: ${r.error.issues.map((i) => i.message).join('; ')}` };
|
|
188
212
|
}
|
|
213
|
+
// ggui#1459 — the render result the served release emits must keep parsing when
|
|
214
|
+
// the result gains an optional member (`effort`).
|
|
215
|
+
function gradeRenderResult(payload) {
|
|
216
|
+
const r = renderOutputSchema.safeParse(payload);
|
|
217
|
+
return r.success
|
|
218
|
+
? { pass: true, detail: 'renderOutputSchema: accepted' }
|
|
219
|
+
: { pass: false, detail: `renderOutputSchema refused the previous release's render result: ${r.error.issues.map((i) => i.message).join('; ')}` };
|
|
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
|
+
}
|
|
189
319
|
/** Grade every N−1 case against the protocol as shipped. */
|
|
190
320
|
export function runN1CompatConformance() {
|
|
191
321
|
return N1_COMPAT_CASES.map((c) => {
|
|
192
|
-
const graded = c.wire
|
|
193
|
-
? gradeAppTheme(c.payload)
|
|
194
|
-
: c.wire === 'app-theme-read'
|
|
195
|
-
? gradeAppThemeRead(c.payload)
|
|
196
|
-
: c.wire === 'app-theme-carry'
|
|
197
|
-
? gradeAppThemeCarry(c.payload)
|
|
198
|
-
: c.wire === 'render-meta'
|
|
199
|
-
? gradeRenderMeta(c.payload)
|
|
200
|
-
: c.wire === 'generation-profile'
|
|
201
|
-
? gradeGenerationProfile(c.payload)
|
|
202
|
-
: c.wire === 'handshake-suggestion'
|
|
203
|
-
? gradeHandshakeSuggestion(c.payload)
|
|
204
|
-
: gradeOpsGenerateBlueprint(c.payload);
|
|
322
|
+
const graded = gradeWire(c.wire, c.payload);
|
|
205
323
|
return { name: c.name, direction: c.direction, pass: graded.pass, detail: `${c.release.label} (${c.release.sha}) → ${graded.detail}` };
|
|
206
324
|
});
|
|
207
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.
|