@ggui-ai/protocol-conformance 0.9.0 → 0.10.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 +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/props-schema-conformance/cases/empty-closed-wrapper.json +17 -0
- package/dist/props-schema-conformance/cases/format-in-core-assertive.json +28 -0
- package/dist/props-schema-conformance/cases/format-out-of-list-full.json +21 -0
- package/dist/props-schema-conformance/cases/nullable-normalization.json +23 -0
- package/dist/props-schema-conformance/cases/nullable-object-closed.json +50 -0
- package/dist/props-schema-conformance/cases/out-of-core-pattern-full.json +21 -0
- package/dist/props-schema-conformance/cases/scheduler-enum-authority.json +36 -0
- package/dist/props-schema-conformance/index.d.ts +57 -0
- package/dist/props-schema-conformance/index.d.ts.map +1 -0
- package/dist/props-schema-conformance/index.js +95 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -51,12 +51,13 @@ One declared grading gap on the action loop: `action-ack` proves the append half
|
|
|
51
51
|
|
|
52
52
|
The behavioral fixture catalog above asserts what an implementation _does_ over the wire. Some protocol obligations aren't transport-observable at all — they are deterministic _validation functions_: given an input, accept or reject it. SPEC §7.7.2's gadget obligations are exactly this. Modeling them as WebSocket fixtures would mean faking wire frames the protocol never emits; instead the kit ships **pure-function catalogs** — accept/reject cases graded against a caller-supplied function, with no host, render, or transport.
|
|
53
53
|
|
|
54
|
-
Each catalog ships its cases as raw JSON so a non-TypeScript implementer can grade their own implementation, and each runner takes the implementation as a callback — the kit never hard-binds a concrete one.
|
|
54
|
+
Each catalog ships its cases as raw JSON so a non-TypeScript implementer can grade their own implementation, and each runner takes the implementation as a callback — the kit never hard-binds a concrete one. Five catalogs ship today:
|
|
55
55
|
|
|
56
56
|
- **`@ggui-ai/protocol-conformance/schema-conformance`** — which `DataContract.clientCapabilities` payloads a conformant parser MUST accept / reject (the gadget wire shape).
|
|
57
57
|
- **`@ggui-ai/protocol-conformance/registration-conformance`** — which `(contract, appGadgets, appPublicEnv)` triples the push-time gadget gate stack MUST accept / reject, and with which precise SPEC §7.9 reject code (`gadget_not_registered` / `gadget_package_mismatch` / `gadget_public_env_missing` / `duplicate_gadget_hook`).
|
|
58
58
|
- **`@ggui-ai/protocol-conformance/resolution-conformance`** — which bundle + style URLs the server MUST compute for a gadget descriptor's transport fields (`bundleHost` precedence, default host, loopback `http` scheme).
|
|
59
59
|
- **`@ggui-ai/protocol-conformance/binding-conformance`** — which MCP tool-binding set a manifest resolves to (a declared `mcpTools` list wins entirely; blueprints without it derive bare tool-name entries from contract tool names) and which artifacts the registry `tool=` / `server=` search filters MUST match (SPEC §7.7.4.1).
|
|
60
|
+
- **`@ggui-ai/protocol-conformance/props-schema-conformance`** — the schema-precise render arbiter (SPEC §2.3.2): the enforced-props-schema builder (drift), its RFC 8785 sha256 hash, the grammar-safe profile verdict, and returned-schema AUTHORITY (sample validity under the case's own `propsSchema`, including the 2026-08-19 out-of-vocabulary enum incident pinned as a permanent sample).
|
|
60
61
|
|
|
61
62
|
```ts
|
|
62
63
|
import { runSchemaConformance } from "@ggui-ai/protocol-conformance/schema-conformance";
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export { allFixtures, fixturesByContract, bootstrapProtocolFixtures, consumeBuff
|
|
|
16
16
|
export type { ContractSlug } from './fixtures/index.js';
|
|
17
17
|
export { gadgetWireSchemaCases, runSchemaConformance, } from './schema-conformance/index.js';
|
|
18
18
|
export type { SchemaConformanceCase, SchemaConformanceMismatch, SchemaConformanceResult, } from './schema-conformance/index.js';
|
|
19
|
+
export { propsSchemaConformanceCases, runPropsSchemaConformance, } from './props-schema-conformance/index.js';
|
|
20
|
+
export type { PropsSchemaConformanceCase, PropsSchemaConformanceFailure, PropsSchemaConformanceReport, PropsSchemaConformanceSample, PropsSchemaImplementation, } from './props-schema-conformance/index.js';
|
|
19
21
|
export { gadgetRegistrationCases, runRegistrationConformance, } from './registration-conformance/index.js';
|
|
20
22
|
export type { GadgetGateRejectCode, GateOutcome, RegistrationConformanceCase, RegistrationConformanceMismatch, RegistrationConformanceResult, } from './registration-conformance/index.js';
|
|
21
23
|
export { gadgetResolutionCases, runResolutionConformance, } from './resolution-conformance/index.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,YAAY,EAEV,eAAe,EACf,wBAAwB,EACxB,sBAAsB,EACtB,UAAU,EAEV,QAAQ,EACR,qBAAqB,EAErB,SAAS,EACT,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,uBAAuB,EACvB,yBAAyB,EACzB,gCAAgC,EAChC,gBAAgB,EAEhB,YAAY,EAEZ,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,eAAe,EAEf,aAAa,EACb,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAMpB,YAAY,EACV,eAAe,EAEf,SAAS,IAAI,aAAa,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,iCAAiC,EACjC,iBAAiB,EAEjB,YAAY,IAAI,gBAAgB,GACjC,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,yBAAyB,EACzB,qBAAqB,EACrB,mBAAmB,EACnB,gCAAgC,EAChC,8BAA8B,EAC9B,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AASxD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,YAAY,EAEV,eAAe,EACf,wBAAwB,EACxB,sBAAsB,EACtB,UAAU,EAEV,QAAQ,EACR,qBAAqB,EAErB,SAAS,EACT,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,uBAAuB,EACvB,yBAAyB,EACzB,gCAAgC,EAChC,gBAAgB,EAEhB,YAAY,EAEZ,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,eAAe,EAEf,aAAa,EACb,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAMpB,YAAY,EACV,eAAe,EAEf,SAAS,IAAI,aAAa,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,iCAAiC,EACjC,iBAAiB,EAEjB,YAAY,IAAI,gBAAgB,GACjC,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,yBAAyB,EACzB,qBAAqB,EACrB,mBAAmB,EACnB,gCAAgC,EAChC,8BAA8B,EAC9B,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AASxD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,qBAAqB,EACrB,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,+BAA+B,CAAC;AAOvC,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EACV,0BAA0B,EAC1B,6BAA6B,EAC7B,4BAA4B,EAC5B,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,qCAAqC,CAAC;AAK7C,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,2BAA2B,EAC3B,+BAA+B,EAC/B,6BAA6B,GAC9B,MAAM,qCAAqC,CAAC;AAK7C,OAAO,EACL,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,yBAAyB,EACzB,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,mCAAmC,CAAC;AAM3C,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AAQxC,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACV,sBAAsB,EACtB,iBAAiB,EACjB,4BAA4B,EAC5B,kBAAkB,EAClB,2BAA2B,EAC3B,8BAA8B,EAC9B,6BAA6B,EAC7B,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,0BAA0B,EAC1B,gBAAgB,EAChB,uBAAuB,EACvB,iCAAiC,GAClC,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EAInB,qBAAqB,EACrB,gCAAgC,GACjC,MAAM,sBAAsB,CAAC;AAK9B,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,cAAc,EACd,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,12 @@ export { allFixtures, fixturesByContract, bootstrapProtocolFixtures, consumeBuff
|
|
|
22
22
|
// Schema-conformance — which `DataContract.clientCapabilities`
|
|
23
23
|
// payloads a conformant parser MUST accept / reject.
|
|
24
24
|
export { gadgetWireSchemaCases, runSchemaConformance, } from './schema-conformance/index.js';
|
|
25
|
+
// Props-schema conformance — the schema-precise render arbiter
|
|
26
|
+
// (frozen shape 2026-08-19): the enforced-schema builder, its RFC 8785
|
|
27
|
+
// hash, the grammar-safe profile, and returned-schema AUTHORITY, each
|
|
28
|
+
// graded against caller-supplied callbacks over a polyglot JSON
|
|
29
|
+
// catalog. Includes the live-incident enum sample.
|
|
30
|
+
export { propsSchemaConformanceCases, runPropsSchemaConformance, } from './props-schema-conformance/index.js';
|
|
25
31
|
// Registration-conformance — which `(contract, appGadgets, appPublicEnv)`
|
|
26
32
|
// triples the push-time gadget gate stack MUST accept / reject, with
|
|
27
33
|
// which precise SPEC §7.9 reject code.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "empty-closed-wrapper",
|
|
3
|
+
"description": "A propsSpec-less / empty contract emits the empty closed wrapper — never omitted for a non-declined handshake. Non-empty props are invalid under it, making the props-without-propsSpec rejection schema-derivable.",
|
|
4
|
+
"propsSpec": { "properties": {} },
|
|
5
|
+
"propsSchema": {
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"properties": {},
|
|
8
|
+
"required": [],
|
|
9
|
+
"type": "object"
|
|
10
|
+
},
|
|
11
|
+
"propsSchemaHash": "d746974fa9afd5e951f76f9af38954b0ad7f436f2120dc974da65e5ee39f856f",
|
|
12
|
+
"propsSchemaProfile": "grammar-safe",
|
|
13
|
+
"samples": [
|
|
14
|
+
{ "props": {}, "valid": true },
|
|
15
|
+
{ "props": { "x": 1 }, "valid": false }
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "format-in-core-assertive",
|
|
3
|
+
"description": "The dialect pin: format is ASSERTIVE (not annotation-only), and the ten-format list is in the grammar-safe core. A malformed date-time MUST reject.",
|
|
4
|
+
"propsSpec": {
|
|
5
|
+
"properties": {
|
|
6
|
+
"when": {
|
|
7
|
+
"schema": { "type": "string", "format": "date-time" },
|
|
8
|
+
"required": true
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"propsSchema": {
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"properties": { "when": { "format": "date-time", "type": "string" } },
|
|
15
|
+
"required": ["when"],
|
|
16
|
+
"type": "object"
|
|
17
|
+
},
|
|
18
|
+
"propsSchemaHash": "4a9fa57c154a96bdb5b4ac2f3efc878eb07a41cb64e10ac5701a87bee75ad885",
|
|
19
|
+
"propsSchemaProfile": "grammar-safe",
|
|
20
|
+
"samples": [
|
|
21
|
+
{ "props": { "when": "2026-08-19T12:00:00Z" }, "valid": true },
|
|
22
|
+
{
|
|
23
|
+
"props": { "when": "yesterday" },
|
|
24
|
+
"valid": false,
|
|
25
|
+
"note": "format-assertive semantics — annotation-only readers would wrongly accept this"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "format-out-of-list-full",
|
|
3
|
+
"description": "A validator-known format OUTSIDE the enumerated ten (url) classifies 'full'. Server enforcement is unchanged; only grammar compilation is off the table.",
|
|
4
|
+
"propsSpec": {
|
|
5
|
+
"properties": {
|
|
6
|
+
"link": { "schema": { "type": "string", "format": "url" } }
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"propsSchema": {
|
|
10
|
+
"additionalProperties": false,
|
|
11
|
+
"properties": { "link": { "format": "url", "type": "string" } },
|
|
12
|
+
"required": [],
|
|
13
|
+
"type": "object"
|
|
14
|
+
},
|
|
15
|
+
"propsSchemaHash": "fa2b9ed841ac15c8a5ef525c61dc007532b7a6e6eea37ce7c6177ea5abe15213",
|
|
16
|
+
"propsSchemaProfile": "full",
|
|
17
|
+
"samples": [
|
|
18
|
+
{ "props": { "link": "https://example.com/a" }, "valid": true },
|
|
19
|
+
{ "props": { "link": "not a url" }, "valid": false }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "nullable-normalization",
|
|
3
|
+
"description": "OpenAPI nullable:true normalizes to the canonical type union in the emitted bytes; null acceptance is preserved (the validator's nullable widening, made explicit).",
|
|
4
|
+
"propsSpec": {
|
|
5
|
+
"properties": {
|
|
6
|
+
"note": { "schema": { "type": "string", "nullable": true } }
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"propsSchema": {
|
|
10
|
+
"additionalProperties": false,
|
|
11
|
+
"properties": { "note": { "type": ["string", "null"] } },
|
|
12
|
+
"required": [],
|
|
13
|
+
"type": "object"
|
|
14
|
+
},
|
|
15
|
+
"propsSchemaHash": "e45869c6cabb21553f1ff784c8dfdbbb1cd9992aa516ae98d995b1385a23bee8",
|
|
16
|
+
"propsSchemaProfile": "grammar-safe",
|
|
17
|
+
"samples": [
|
|
18
|
+
{ "props": { "note": null }, "valid": true },
|
|
19
|
+
{ "props": { "note": "x" }, "valid": true },
|
|
20
|
+
{ "props": { "note": 1 }, "valid": false },
|
|
21
|
+
{ "props": {}, "valid": true, "note": "note is not required" }
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "nullable-object-closed",
|
|
3
|
+
"description": "Closed shape survives nullability: a nullable free-form object node closes (additionalProperties:false), and the items of a nullable array are descended. Pins the round-2 injectClosedShape residual (2026-08-19) — single-string type comparisons silently left these subtrees open.",
|
|
4
|
+
"propsSpec": {
|
|
5
|
+
"properties": {
|
|
6
|
+
"meta": { "schema": { "type": ["object", "null"] } },
|
|
7
|
+
"rows": {
|
|
8
|
+
"schema": {
|
|
9
|
+
"type": ["array", "null"],
|
|
10
|
+
"items": { "type": "object", "properties": { "id": { "type": "string" } } }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"propsSchema": {
|
|
16
|
+
"additionalProperties": false,
|
|
17
|
+
"properties": {
|
|
18
|
+
"meta": {
|
|
19
|
+
"additionalProperties": false,
|
|
20
|
+
"type": ["object", "null"]
|
|
21
|
+
},
|
|
22
|
+
"rows": {
|
|
23
|
+
"items": {
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"properties": { "id": { "type": "string" } },
|
|
26
|
+
"type": "object"
|
|
27
|
+
},
|
|
28
|
+
"type": ["array", "null"]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"required": [],
|
|
32
|
+
"type": "object"
|
|
33
|
+
},
|
|
34
|
+
"propsSchemaHash": "8fcc242455134dd13ef9a6dbea78ff2b8f623798383e0fa27eacc107856dbc5c",
|
|
35
|
+
"propsSchemaProfile": "grammar-safe",
|
|
36
|
+
"samples": [
|
|
37
|
+
{ "props": { "meta": null, "rows": null }, "valid": true },
|
|
38
|
+
{ "props": { "meta": {}, "rows": [{ "id": "a" }] }, "valid": true },
|
|
39
|
+
{
|
|
40
|
+
"props": { "meta": { "x": 1 } },
|
|
41
|
+
"valid": false,
|
|
42
|
+
"note": "the nullable free-form object is CLOSED — undeclared keys reject"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"props": { "rows": [{ "id": "a", "extra": 1 }] },
|
|
46
|
+
"valid": false,
|
|
47
|
+
"note": "items of a nullable array are closed too"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "out-of-core-pattern-full",
|
|
3
|
+
"description": "An out-of-core keyword (pattern) classifies 'full' — the server still enforces it (AUTHORITY covers the whole schema); 'full' only means a grammar-compiling runtime falls back to schema-as-context.",
|
|
4
|
+
"propsSpec": {
|
|
5
|
+
"properties": {
|
|
6
|
+
"code": { "schema": { "type": "string", "pattern": "^[A-Z]+$" } }
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"propsSchema": {
|
|
10
|
+
"additionalProperties": false,
|
|
11
|
+
"properties": { "code": { "pattern": "^[A-Z]+$", "type": "string" } },
|
|
12
|
+
"required": [],
|
|
13
|
+
"type": "object"
|
|
14
|
+
},
|
|
15
|
+
"propsSchemaHash": "9e9fe92d5aa4dbbda98ef26874acbd732318344126776dc159c3fe9d8fd25b8d",
|
|
16
|
+
"propsSchemaProfile": "full",
|
|
17
|
+
"samples": [
|
|
18
|
+
{ "props": { "code": "ABC" }, "valid": true },
|
|
19
|
+
{ "props": { "code": "abc" }, "valid": false }
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "scheduler-enum-authority",
|
|
3
|
+
"description": "The incident shape: a closed enum vocabulary. The returned schema IS the enforced schema — props valid under it MUST validate; the out-of-vocabulary value ('booked') MUST reject with the enum keyword.",
|
|
4
|
+
"propsSpec": {
|
|
5
|
+
"properties": {
|
|
6
|
+
"status": {
|
|
7
|
+
"schema": { "type": "string", "enum": ["open", "busy", "tentative"] },
|
|
8
|
+
"required": true
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"propsSchema": {
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"properties": {
|
|
15
|
+
"status": { "enum": ["open", "busy", "tentative"], "type": "string" }
|
|
16
|
+
},
|
|
17
|
+
"required": ["status"],
|
|
18
|
+
"type": "object"
|
|
19
|
+
},
|
|
20
|
+
"propsSchemaHash": "1479a6193c963a0d501f0ef80fbba1d2d12087f2da9fcf86b2bd65b54c7e16ee",
|
|
21
|
+
"propsSchemaProfile": "grammar-safe",
|
|
22
|
+
"samples": [
|
|
23
|
+
{ "props": { "status": "open" }, "valid": true },
|
|
24
|
+
{
|
|
25
|
+
"props": { "status": "booked" },
|
|
26
|
+
"valid": false,
|
|
27
|
+
"note": "the live-incident value — out-of-vocabulary enum member"
|
|
28
|
+
},
|
|
29
|
+
{ "props": {}, "valid": false, "note": "required member missing" },
|
|
30
|
+
{
|
|
31
|
+
"props": { "status": "open", "extra": 1 },
|
|
32
|
+
"valid": false,
|
|
33
|
+
"note": "closed shape — undeclared key"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authored vocabulary — pinned here, deliberately decoupled from the
|
|
3
|
+
* live `@ggui-ai/protocol` source tree (the same drift discipline the
|
|
4
|
+
* behavioral fixture surface applies). JSON-representable only.
|
|
5
|
+
*/
|
|
6
|
+
export interface PropsSchemaConformanceSample {
|
|
7
|
+
readonly props: Record<string, unknown>;
|
|
8
|
+
readonly valid: boolean;
|
|
9
|
+
readonly note?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PropsSchemaConformanceCase {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly description: string;
|
|
14
|
+
/** The authored PropsSpec (JSON form). */
|
|
15
|
+
readonly propsSpec: Record<string, unknown>;
|
|
16
|
+
/** The expected enforced-schema artifact, in canonical form. */
|
|
17
|
+
readonly propsSchema: Record<string, unknown>;
|
|
18
|
+
/** sha256 (lowercase hex) over the RFC 8785 bytes of `propsSchema`. */
|
|
19
|
+
readonly propsSchemaHash: string;
|
|
20
|
+
/** Reference profile verdict. */
|
|
21
|
+
readonly propsSchemaProfile: 'grammar-safe' | 'full';
|
|
22
|
+
readonly samples: readonly PropsSchemaConformanceSample[];
|
|
23
|
+
}
|
|
24
|
+
/** The published catalog, id-sorted for deterministic reporting. */
|
|
25
|
+
export declare const propsSchemaConformanceCases: readonly PropsSchemaConformanceCase[];
|
|
26
|
+
/** The implementation under test, as callbacks. */
|
|
27
|
+
export interface PropsSchemaImplementation {
|
|
28
|
+
/** The enforced-schema builder (reference: `buildEnforcedPropsSchema`). */
|
|
29
|
+
readonly build: (propsSpec: Record<string, unknown>) => Record<string, unknown>;
|
|
30
|
+
/** The canonical hash (reference: `computePropsSchemaHash`). */
|
|
31
|
+
readonly hash: (schema: Record<string, unknown>) => string;
|
|
32
|
+
/** The profile classifier (reference: `classifyPropsSchemaProfile`). */
|
|
33
|
+
readonly classify: (schema: Record<string, unknown>) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Validity of `props` under a validator compiled from `schema`,
|
|
36
|
+
* read per the pinned dialect (draft-07, format-assertive, closed
|
|
37
|
+
* shape already in the bytes). Reference:
|
|
38
|
+
* `validatePropsDataWithSchema(props, schema).valid`.
|
|
39
|
+
*/
|
|
40
|
+
readonly validate: (props: Record<string, unknown>, schema: Record<string, unknown>) => boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface PropsSchemaConformanceFailure {
|
|
43
|
+
readonly caseId: string;
|
|
44
|
+
readonly obligation: 'drift' | 'hash' | 'profile' | 'authority';
|
|
45
|
+
readonly detail: string;
|
|
46
|
+
}
|
|
47
|
+
export interface PropsSchemaConformanceReport {
|
|
48
|
+
readonly total: number;
|
|
49
|
+
readonly failures: readonly PropsSchemaConformanceFailure[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Grade an implementation against the catalog. Deterministic; every
|
|
53
|
+
* failure names the case, the obligation, and the observed value —
|
|
54
|
+
* the report is the observable-violation surface of this contract.
|
|
55
|
+
*/
|
|
56
|
+
export declare function runPropsSchemaConformance(impl: PropsSchemaImplementation): PropsSchemaConformanceReport;
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/props-schema-conformance/index.ts"],"names":[],"mappings":"AAyCA;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5C,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,uEAAuE;IACvE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,iCAAiC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,cAAc,GAAG,MAAM,CAAC;IACrD,QAAQ,CAAC,OAAO,EAAE,SAAS,4BAA4B,EAAE,CAAC;CAC3D;AAED,oEAAoE;AACpE,eAAO,MAAM,2BAA2B,EAAE,SAAS,0BAA0B,EAS1C,CAAC;AAEpC,mDAAmD;AACnD,MAAM,WAAW,yBAAyB;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,CACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC/B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IAC3D,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;IAC/D;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,CACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC;CACd;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,SAAS,6BAA6B,EAAE,CAAC;CAC7D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,yBAAyB,GAC9B,4BAA4B,CAuC9B"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Props-schema conformance catalog — the arbiter fixtures for the
|
|
3
|
+
* schema-precise render contract (frozen shape 2026-08-19; the six
|
|
4
|
+
* pins in `docs/plans/2026-08-19-schema-precise-render.md` §2).
|
|
5
|
+
*
|
|
6
|
+
* Four obligations, one catalog:
|
|
7
|
+
*
|
|
8
|
+
* - **DRIFT** — `buildPropsSchema(propsSpec)` MUST produce the
|
|
9
|
+
* case's `propsSchema` exactly (canonical construction: sorted
|
|
10
|
+
* keys at every schema node, closed shape materialized,
|
|
11
|
+
* `nullable` rewritten, metadata keywords stripped).
|
|
12
|
+
* - **HASH** — sha256 over the RFC 8785 canonical bytes of that
|
|
13
|
+
* schema MUST equal the case's `propsSchemaHash` (lowercase hex,
|
|
14
|
+
* 64 chars). Portable: any JCS library + sha256 reproduces it.
|
|
15
|
+
* - **PROFILE** — the grammar-safe classification MUST equal the
|
|
16
|
+
* case's `propsSchemaProfile` (purely syntactic keyword
|
|
17
|
+
* membership; unknown wire values degrade to `'full'` on the
|
|
18
|
+
* consumer side, but a producer MUST emit the reference verdict).
|
|
19
|
+
* - **AUTHORITY** — every sample's validity under a validator
|
|
20
|
+
* compiled FROM THE CASE'S `propsSchema` (format-assertive
|
|
21
|
+
* dialect) MUST equal `valid`. The returned schema IS the
|
|
22
|
+
* enforced schema; the samples include the live-incident
|
|
23
|
+
* out-of-vocabulary enum value.
|
|
24
|
+
*
|
|
25
|
+
* Same modeling as `../schema-conformance`: structural obligations,
|
|
26
|
+
* no transport, no host. Cases ship as raw JSON under `./cases/` so a
|
|
27
|
+
* non-TypeScript implementer can grade their own builder/validator;
|
|
28
|
+
* {@link runPropsSchemaConformance} takes the implementation as
|
|
29
|
+
* callbacks — the kit never imports a concrete implementation here.
|
|
30
|
+
* The reference binding (the kit's own meta-test) wires
|
|
31
|
+
* `@ggui-ai/protocol`'s exports; see
|
|
32
|
+
* `./props-schema-conformance.test.ts`.
|
|
33
|
+
*/
|
|
34
|
+
import emptyClosedWrapper from './cases/empty-closed-wrapper.json' with { type: 'json' };
|
|
35
|
+
import formatInCoreAssertive from './cases/format-in-core-assertive.json' with { type: 'json' };
|
|
36
|
+
import formatOutOfListFull from './cases/format-out-of-list-full.json' with { type: 'json' };
|
|
37
|
+
import nullableNormalization from './cases/nullable-normalization.json' with { type: 'json' };
|
|
38
|
+
import nullableObjectClosed from './cases/nullable-object-closed.json' with { type: 'json' };
|
|
39
|
+
import outOfCorePatternFull from './cases/out-of-core-pattern-full.json' with { type: 'json' };
|
|
40
|
+
import schedulerEnumAuthority from './cases/scheduler-enum-authority.json' with { type: 'json' };
|
|
41
|
+
/** The published catalog, id-sorted for deterministic reporting. */
|
|
42
|
+
export const propsSchemaConformanceCases = [
|
|
43
|
+
emptyClosedWrapper,
|
|
44
|
+
formatInCoreAssertive,
|
|
45
|
+
formatOutOfListFull,
|
|
46
|
+
nullableNormalization,
|
|
47
|
+
nullableObjectClosed,
|
|
48
|
+
outOfCorePatternFull,
|
|
49
|
+
schedulerEnumAuthority,
|
|
50
|
+
];
|
|
51
|
+
/**
|
|
52
|
+
* Grade an implementation against the catalog. Deterministic; every
|
|
53
|
+
* failure names the case, the obligation, and the observed value —
|
|
54
|
+
* the report is the observable-violation surface of this contract.
|
|
55
|
+
*/
|
|
56
|
+
export function runPropsSchemaConformance(impl) {
|
|
57
|
+
const failures = [];
|
|
58
|
+
for (const testCase of propsSchemaConformanceCases) {
|
|
59
|
+
const built = impl.build(testCase.propsSpec);
|
|
60
|
+
if (JSON.stringify(built) !== JSON.stringify(testCase.propsSchema)) {
|
|
61
|
+
failures.push({
|
|
62
|
+
caseId: testCase.id,
|
|
63
|
+
obligation: 'drift',
|
|
64
|
+
detail: `built schema differs from the pinned artifact: ${JSON.stringify(built)}`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const hash = impl.hash(testCase.propsSchema);
|
|
68
|
+
if (hash !== testCase.propsSchemaHash) {
|
|
69
|
+
failures.push({
|
|
70
|
+
caseId: testCase.id,
|
|
71
|
+
obligation: 'hash',
|
|
72
|
+
detail: `expected ${testCase.propsSchemaHash}, got ${hash}`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const profile = impl.classify(testCase.propsSchema);
|
|
76
|
+
if (profile !== testCase.propsSchemaProfile) {
|
|
77
|
+
failures.push({
|
|
78
|
+
caseId: testCase.id,
|
|
79
|
+
obligation: 'profile',
|
|
80
|
+
detail: `expected '${testCase.propsSchemaProfile}', got '${profile}'`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
for (const sample of testCase.samples) {
|
|
84
|
+
const valid = impl.validate(sample.props, testCase.propsSchema);
|
|
85
|
+
if (valid !== sample.valid) {
|
|
86
|
+
failures.push({
|
|
87
|
+
caseId: testCase.id,
|
|
88
|
+
obligation: 'authority',
|
|
89
|
+
detail: `props ${JSON.stringify(sample.props)} expected valid=${sample.valid}, got ${valid}${sample.note ? ` (${sample.note})` : ''}`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { total: propsSchemaConformanceCases.length, failures };
|
|
95
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggui-ai/protocol-conformance",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Conformance test kit for the ggui protocol. Drives a behavioral fixture catalog against a live implementation over WebSocket, a resources/read catalog against a live MCP server, and four pure-function catalogs (schema, registration, resolution, binding) for the gadget and tool-binding obligations, reporting a scorecard-style pass / fail / skip. MCP builders adopt this kit to prove their implementation is protocol-conformant.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -55,11 +55,16 @@
|
|
|
55
55
|
"types": "./dist/binding-conformance/index.d.ts",
|
|
56
56
|
"import": "./dist/binding-conformance/index.js",
|
|
57
57
|
"default": "./dist/binding-conformance/index.js"
|
|
58
|
+
},
|
|
59
|
+
"./props-schema-conformance": {
|
|
60
|
+
"types": "./dist/props-schema-conformance/index.d.ts",
|
|
61
|
+
"import": "./dist/props-schema-conformance/index.js",
|
|
62
|
+
"default": "./dist/props-schema-conformance/index.js"
|
|
58
63
|
}
|
|
59
64
|
},
|
|
60
65
|
"dependencies": {
|
|
61
66
|
"ws": "^8.20.1",
|
|
62
|
-
"@ggui-ai/protocol": "0.
|
|
67
|
+
"@ggui-ai/protocol": "0.10.0"
|
|
63
68
|
},
|
|
64
69
|
"devDependencies": {
|
|
65
70
|
"@types/node": "^22.0.0",
|