@ggui-ai/protocol-conformance 0.1.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +89 -0
  3. package/dist/cli.d.ts +15 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +193 -0
  6. package/dist/conformance-host.d.ts +155 -0
  7. package/dist/conformance-host.d.ts.map +1 -0
  8. package/dist/conformance-host.js +45 -0
  9. package/dist/fixtures/bootstrap-protocol/bootstrap-bundle-fetch-failed.json +26 -0
  10. package/dist/fixtures/bootstrap-protocol/bootstrap-meta-missing.json +32 -0
  11. package/dist/fixtures/bootstrap-protocol/bootstrap-success.json +17 -0
  12. package/dist/fixtures/bootstrap-protocol/index.d.ts +5 -0
  13. package/dist/fixtures/bootstrap-protocol/index.d.ts.map +1 -0
  14. package/dist/fixtures/bootstrap-protocol/index.js +24 -0
  15. package/dist/fixtures/canvas-mode-wire-shapes/canvas-bootstrap-mutual-exclusion.json +25 -0
  16. package/dist/fixtures/canvas-mode-wire-shapes/canvas-lifecycle-channel-emits-handshake-started.json +30 -0
  17. package/dist/fixtures/canvas-mode-wire-shapes/canvas-navigated-updates-active-stack-item.json +30 -0
  18. package/dist/fixtures/canvas-mode-wire-shapes/host-context-observed-persists.json +22 -0
  19. package/dist/fixtures/canvas-mode-wire-shapes/index.d.ts +5 -0
  20. package/dist/fixtures/canvas-mode-wire-shapes/index.d.ts.map +1 -0
  21. package/dist/fixtures/canvas-mode-wire-shapes/index.js +47 -0
  22. package/dist/fixtures/index.d.ts +78 -0
  23. package/dist/fixtures/index.d.ts.map +1 -0
  24. package/dist/fixtures/index.js +87 -0
  25. package/dist/fixtures/observability-events/index.d.ts +5 -0
  26. package/dist/fixtures/observability-events/index.d.ts.map +1 -0
  27. package/dist/fixtures/observability-events/index.js +22 -0
  28. package/dist/fixtures/observability-events/observability-contract-error-emitted.json +29 -0
  29. package/dist/fixtures/observability-events/observability-wired-tool-invoked.json +28 -0
  30. package/dist/fixtures/refresh-semantics/index.d.ts +5 -0
  31. package/dist/fixtures/refresh-semantics/index.d.ts.map +1 -0
  32. package/dist/fixtures/refresh-semantics/index.js +19 -0
  33. package/dist/fixtures/refresh-semantics/stream-refresh-success.json +40 -0
  34. package/dist/fixtures/reserved-channel-authority/index.d.ts +4 -0
  35. package/dist/fixtures/reserved-channel-authority/index.d.ts.map +1 -0
  36. package/dist/fixtures/reserved-channel-authority/index.js +21 -0
  37. package/dist/fixtures/reserved-channel-authority/props-update-roundtrip.json +34 -0
  38. package/dist/fixtures/reserved-channel-authority/stream-schema-violation.json +45 -0
  39. package/dist/fixtures/schema-version-handshake/index.d.ts +4 -0
  40. package/dist/fixtures/schema-version-handshake/index.d.ts.map +1 -0
  41. package/dist/fixtures/schema-version-handshake/index.js +20 -0
  42. package/dist/fixtures/schema-version-handshake/version-match.json +16 -0
  43. package/dist/fixtures/schema-version-handshake/version-mismatch.json +26 -0
  44. package/dist/fixtures/wired-action-dispatch/index.d.ts +5 -0
  45. package/dist/fixtures/wired-action-dispatch/index.d.ts.map +1 -0
  46. package/dist/fixtures/wired-action-dispatch/index.js +29 -0
  47. package/dist/fixtures/wired-action-dispatch/wired-action-success.json +36 -0
  48. package/dist/fixtures/wired-action-dispatch/wired-action-tool-not-found.json +31 -0
  49. package/dist/fixtures/wired-action-dispatch/wired-action-tool-threw.json +32 -0
  50. package/dist/fixtures/wired-action-dispatch/wired-action-tool-timeout.json +32 -0
  51. package/dist/index.d.ts +28 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +40 -0
  54. package/dist/loader.d.ts +19 -0
  55. package/dist/loader.d.ts.map +1 -0
  56. package/dist/loader.js +46 -0
  57. package/dist/match-behavior.d.ts +121 -0
  58. package/dist/match-behavior.d.ts.map +1 -0
  59. package/dist/match-behavior.js +385 -0
  60. package/dist/registration-conformance/cases/gate-export-not-registered.json +19 -0
  61. package/dist/registration-conformance/cases/gate-no-gadgets-accept.json +13 -0
  62. package/dist/registration-conformance/cases/gate-package-mismatch.json +19 -0
  63. package/dist/registration-conformance/cases/gate-priority-not-registered-outranks-mismatch.json +20 -0
  64. package/dist/registration-conformance/cases/gate-registered-resolves-accept.json +25 -0
  65. package/dist/registration-conformance/cases/gate-stdlib-hook-accept.json +27 -0
  66. package/dist/registration-conformance/cases/gate-typo-classifies-as-not-registered.json +27 -0
  67. package/dist/registration-conformance/index.d.ts +114 -0
  68. package/dist/registration-conformance/index.d.ts.map +1 -0
  69. package/dist/registration-conformance/index.js +63 -0
  70. package/dist/reporter.d.ts +32 -0
  71. package/dist/reporter.d.ts.map +1 -0
  72. package/dist/reporter.js +151 -0
  73. package/dist/resolution-conformance/cases/resolve-bundle-host-computed.json +13 -0
  74. package/dist/resolution-conformance/cases/resolve-default-host.json +12 -0
  75. package/dist/resolution-conformance/cases/resolve-explicit-bundle-and-style-url.json +14 -0
  76. package/dist/resolution-conformance/cases/resolve-explicit-bundle-url.json +12 -0
  77. package/dist/resolution-conformance/cases/resolve-loopback-host-http-scheme.json +13 -0
  78. package/dist/resolution-conformance/cases/resolve-package-only-no-version.json +8 -0
  79. package/dist/resolution-conformance/cases/resolve-stdlib-no-urls.json +10 -0
  80. package/dist/resolution-conformance/index.d.ts +76 -0
  81. package/dist/resolution-conformance/index.d.ts.map +1 -0
  82. package/dist/resolution-conformance/index.js +81 -0
  83. package/dist/run-conformance.d.ts +58 -0
  84. package/dist/run-conformance.d.ts.map +1 -0
  85. package/dist/run-conformance.js +286 -0
  86. package/dist/schema-conformance/cases/wire-bad-export-name-reject.json +11 -0
  87. package/dist/schema-conformance/cases/wire-bad-package-name-reject.json +11 -0
  88. package/dist/schema-conformance/cases/wire-component-export-accept.json +10 -0
  89. package/dist/schema-conformance/cases/wire-empty-package-reject.json +11 -0
  90. package/dist/schema-conformance/cases/wire-minimal-accept.json +10 -0
  91. package/dist/schema-conformance/cases/wire-multi-package-accept.json +14 -0
  92. package/dist/schema-conformance/cases/wire-prose-accept.json +15 -0
  93. package/dist/schema-conformance/cases/wire-registry-metadata-reject.json +13 -0
  94. package/dist/schema-conformance/cases/wire-stale-libraries-reject.json +14 -0
  95. package/dist/schema-conformance/cases/wire-stdlib-accept.json +10 -0
  96. package/dist/schema-conformance/cases/wire-transport-field-reject.json +15 -0
  97. package/dist/schema-conformance/cases/wire-version-field-reject.json +13 -0
  98. package/dist/schema-conformance/index.d.ts +71 -0
  99. package/dist/schema-conformance/index.d.ts.map +1 -0
  100. package/dist/schema-conformance/index.js +103 -0
  101. package/dist/types.d.ts +434 -0
  102. package/dist/types.d.ts.map +1 -0
  103. package/dist/types.js +62 -0
  104. package/dist/ws-transport.d.ts +42 -0
  105. package/dist/ws-transport.d.ts.map +1 -0
  106. package/dist/ws-transport.js +135 -0
  107. package/package.json +83 -0
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `ConformanceHost` — the seam between the kit's authored fixtures
3
+ * and the implementation under test.
4
+ *
5
+ * Most fixtures declare `setup` / `teardown` directives (see
6
+ * {@link SetupStep} / {@link TeardownStep} in `./types`) that the host
7
+ * dispatches against its live server. v1.0 of the kit does NOT
8
+ * require implementations to ship a host — fixtures that need setup
9
+ * directives skip cleanly when `runConformance()` is called without
10
+ * a `host`.
11
+ *
12
+ * When a host IS provided, the runner calls `dispatchSetup()` for
13
+ * every step before driving the fixture's `inputEnvelope`, then
14
+ * `dispatchTeardown()` for every step after the assertion. Unknown
15
+ * `kind` values MUST throw so unimplemented directives surface as a
16
+ * test SKIP with a clear "host does not implement X" reason — never
17
+ * a silent pass.
18
+ *
19
+ * ## Extensibly-closed
20
+ *
21
+ * The concrete step vocabulary below mirrors `./types`' vocabulary
22
+ * but is re-exported here as a host-facing narrower form:
23
+ * - Every arm carries the full semantic payload (not the JSON-
24
+ * loader's `unknown`-fields view).
25
+ * - The tail `(Record<string, unknown> & { readonly kind: string & {} })`
26
+ * catches fixture-JSON directives the host hasn't wired — the host
27
+ * returns by throwing, the runner maps it to a skip.
28
+ *
29
+ * Third-party hosts MAY extend either union with additional arms by
30
+ * augmenting the declaration in their own module. v1.0 of the kit
31
+ * does not require exhaustive coverage — it requires *honest*
32
+ * coverage: either implement the directive OR throw, never silently
33
+ * succeed on an unknown kind.
34
+ *
35
+ * ## Relation to `./types`
36
+ *
37
+ * `./types` defines the JSON-authoring surface: every directive has
38
+ * optional-by-default fields so JSON authors don't have to think
39
+ * about shape. This module defines the *runtime* surface: once the
40
+ * loader has validated a directive, the host receives a narrowed
41
+ * form. The two are intentionally parallel but not identical — JSON
42
+ * loaders accept what the author wrote; hosts receive what the
43
+ * runner confirmed is shape-valid.
44
+ */
45
+ export {};
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "bootstrap-bundle-fetch-failed",
3
+ "description": "Thin shell reads a valid `_meta.ggui.bootstrap.runtimeUrl` but the `<script src>` errors (network failure / 404 / CSP reject). Shell emits `postMessage({type:'ggui:bootstrap-failed', reason:'BUNDLE_FETCH_FAILED', message})` to the parent. No WS opens.",
4
+ "skipReason": null,
5
+ "setup": [
6
+ { "type": "create-session", "sessionId": "ck-bundle-fetch-failed-1" },
7
+ {
8
+ "type": "renderer-url-override",
9
+ "sessionId": "ck-bundle-fetch-failed-1",
10
+ "url": "http://127.0.0.1:1/does-not-exist.js"
11
+ }
12
+ ],
13
+ "inputEnvelope": {
14
+ "type": "render",
15
+ "sessionId": "ck-bundle-fetch-failed-1",
16
+ "resource": {
17
+ "uri": "ui://ggui/session"
18
+ }
19
+ },
20
+ "expectedBehavior": {
21
+ "kind": "bootstrap-failure",
22
+ "reason": "BUNDLE_FETCH_FAILED",
23
+ "messageContains": "Runtime bundle failed to load"
24
+ },
25
+ "teardown": [{ "type": "close-session", "sessionId": "ck-bundle-fetch-failed-1" }]
26
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "bootstrap-meta-missing",
3
+ "description": "Host's `ui/initialize` response returns a tool-output without `_meta.ggui.bootstrap` (or missing the `runtimeUrl` field). Thin shell emits `postMessage({type:'ggui:bootstrap-failed', reason:'BOOTSTRAP_META_MISSING', message})` to the parent. No WS connection opens — live-channel envelopes are out of scope pre-handshake.",
4
+ "skipReason": null,
5
+ "setup": [
6
+ { "type": "create-session", "sessionId": "ck-bootstrap-meta-missing-1" },
7
+ {
8
+ "type": "ui-initialize-response-override",
9
+ "sessionId": "ck-bootstrap-meta-missing-1",
10
+ "override": {
11
+ "toolOutput": {
12
+ "_meta": {
13
+ "ggui": {}
14
+ }
15
+ }
16
+ }
17
+ }
18
+ ],
19
+ "inputEnvelope": {
20
+ "type": "render",
21
+ "sessionId": "ck-bootstrap-meta-missing-1",
22
+ "resource": {
23
+ "uri": "ui://ggui/session"
24
+ }
25
+ },
26
+ "expectedBehavior": {
27
+ "kind": "bootstrap-failure",
28
+ "reason": "BOOTSTRAP_META_MISSING",
29
+ "messageContains": "_meta.ggui.bootstrap"
30
+ },
31
+ "teardown": [{ "type": "close-session", "sessionId": "ck-bootstrap-meta-missing-1" }]
32
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "bootstrap-success",
3
+ "description": "Happy-path boot: host reads `_meta.ui.resourceUri`, fetches `ui://ggui/session`, the thin shell loads the renderer bundle, `ui/initialize` returns valid `_meta.ggui.bootstrap.{wsUrl, token, sessionId, appId, runtimeUrl}`, WS handshake succeeds, first stack item renders with `data-ggui-code-ready=\"true\"`. No bootstrap-failed postMessage, no contract-error, no upgrade-required.",
4
+ "skipReason": null,
5
+ "setup": [{ "type": "create-session", "sessionId": "ck-bootstrap-success-1" }],
6
+ "inputEnvelope": {
7
+ "type": "render",
8
+ "sessionId": "ck-bootstrap-success-1",
9
+ "resource": {
10
+ "uri": "ui://ggui/session"
11
+ }
12
+ },
13
+ "expectedBehavior": {
14
+ "kind": "bootstrap-success"
15
+ },
16
+ "teardown": [{ "type": "close-session", "sessionId": "ck-bootstrap-success-1" }]
17
+ }
@@ -0,0 +1,5 @@
1
+ import type { TestCase } from '../../types.js';
2
+ /** All fixtures asserting the bootstrap contract (Protocol #5 — named
3
+ * failure modes + SPEC §8 `GguiBootstrapMeta`). */
4
+ export declare const bootstrapProtocolFixtures: readonly TestCase[];
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fixtures/bootstrap-protocol/index.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;oDACoD;AACpD,eAAO,MAAM,yBAAyB,EAAE,SAAS,QAAQ,EAIxD,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `bootstrap-protocol` fixture sub-module.
3
+ *
4
+ * Exercises the MCP Apps bootstrap contract (SPEC §8):
5
+ * - `ui/initialize` tool-result must stamp `_meta.ggui.bootstrap`
6
+ * with a well-formed envelope.
7
+ * - Renderer-bundle fetch must succeed (or surface a typed
8
+ * `BOOTSTRAP_FAILURE` protocol error).
9
+ * - Happy-path boot reaches `data-ggui-code-ready="true"` with no
10
+ * error frames.
11
+ *
12
+ * Three fixtures today; `bootstrap-success` drives against the
13
+ * current harness, the other two are `ConformanceHost`-gated.
14
+ */
15
+ import bootstrapBundleFetchFailed from './bootstrap-bundle-fetch-failed.json' with { type: 'json' };
16
+ import bootstrapMetaMissing from './bootstrap-meta-missing.json' with { type: 'json' };
17
+ import bootstrapSuccess from './bootstrap-success.json' with { type: 'json' };
18
+ /** All fixtures asserting the bootstrap contract (Protocol #5 — named
19
+ * failure modes + SPEC §8 `GguiBootstrapMeta`). */
20
+ export const bootstrapProtocolFixtures = [
21
+ bootstrapBundleFetchFailed,
22
+ bootstrapMetaMissing,
23
+ bootstrapSuccess,
24
+ ];
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "canvas-bootstrap-mutual-exclusion",
3
+ "description": "Bootstrap with both `canvasMode: true` AND `stackItemId` is malformed — canvas iframes are session-scoped (no pinned stack item). Defensive parsers SHOULD reject; the protocol does not require them to. Reference parser (`@ggui-ai/iframe-runtime` `validateBootstrapMeta`) currently accepts the combination but the field-pair has no defined consumer semantics. This fixture pins the contract intent so future parsers can opt into strict rejection without surprise.",
4
+ "skipReason": null,
5
+ "setup": [
6
+ { "type": "create-session", "sessionId": "ck-canvas-bootstrap-exclusion-1" },
7
+ {
8
+ "type": "set-app-mode",
9
+ "appId": "ck-canvas-bootstrap-exclusion-app",
10
+ "defaultMcpAppsMode": "canvas"
11
+ }
12
+ ],
13
+ "inputEnvelope": {
14
+ "type": "render",
15
+ "sessionId": "ck-canvas-bootstrap-exclusion-1",
16
+ "resource": {
17
+ "uri": "ui://ggui/session/ck-canvas-bootstrap-exclusion-1"
18
+ }
19
+ },
20
+ "expectedBehavior": {
21
+ "kind": "bootstrap-success",
22
+ "observabilitySequence": []
23
+ },
24
+ "teardown": [{ "type": "close-session", "sessionId": "ck-canvas-bootstrap-exclusion-1" }]
25
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "canvas-lifecycle-channel-emits-handshake-started",
3
+ "description": "When a canvas-mode session (`mcpAppsMode === 'canvas'`) enters `ggui_handshake`, the server publishes a `_ggui:lifecycle` envelope with `CanvasLifecyclePayload {kind: 'handshake_started', handshakeId, intent}` BEFORE the handshake completes. Animator pill state machine advances on receipt. Discriminator-narrowing: payload's `kind` field is the union tag; unknown `kind` values drop with a no-op on the iframe side. Fire-and-forget on the server: failure to publish degrades the animator (no pill state change) but MUST NOT abort the handshake's primary result.",
4
+ "skipReason": null,
5
+ "setup": [
6
+ { "type": "create-session", "sessionId": "ck-canvas-lifecycle-handshake-1" },
7
+ {
8
+ "type": "set-app-mode",
9
+ "appId": "ck-canvas-lifecycle-handshake-app",
10
+ "defaultMcpAppsMode": "canvas"
11
+ }
12
+ ],
13
+ "inputEnvelope": {
14
+ "type": "action",
15
+ "channel": 0,
16
+ "sessionId": "ck-canvas-lifecycle-handshake-1",
17
+ "action": {
18
+ "name": "ggui_handshake",
19
+ "data": { "intent": "test" }
20
+ }
21
+ },
22
+ "expectedBehavior": {
23
+ "kind": "stream-update",
24
+ "channel": "_ggui:lifecycle",
25
+ "value": {
26
+ "kind": "handshake_started"
27
+ }
28
+ },
29
+ "teardown": [{ "type": "close-session", "sessionId": "ck-canvas-lifecycle-handshake-1" }]
30
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "canvas-navigated-updates-active-stack-item",
3
+ "description": "Client emits a `canvas_navigated` WS message (Client→Server) carrying `{sessionId, previousActiveItemId, activeItemId}`. Server updates `Session.activeStackItemId` to `activeItemId` (or null when the user popped to empty). Subsequent `ggui_consume` calls observe the new active pipe. Payload omits `appId` — the subscriber binding is the authoritative scope; an inbound message scoped to a wrong session is rejected with `SESSION_MISMATCH` rather than implicit reroute.",
4
+ "skipReason": null,
5
+ "setup": [
6
+ { "type": "create-session", "sessionId": "ck-canvas-navigated-1" },
7
+ {
8
+ "type": "seed-channel",
9
+ "sessionId": "ck-canvas-navigated-1",
10
+ "channel": "_internal:stack",
11
+ "value": [
12
+ { "id": "item-a", "type": "component" },
13
+ { "id": "item-b", "type": "component" }
14
+ ]
15
+ }
16
+ ],
17
+ "inputEnvelope": {
18
+ "type": "canvas_navigated",
19
+ "payload": {
20
+ "sessionId": "ck-canvas-navigated-1",
21
+ "previousActiveItemId": "item-b",
22
+ "activeItemId": "item-a"
23
+ }
24
+ },
25
+ "expectedBehavior": {
26
+ "kind": "no-op",
27
+ "reason": "canvas_navigated has no synchronous response envelope — the side effect is a Session field mutation observed via subsequent ggui_consume / handshake output."
28
+ },
29
+ "teardown": [{ "type": "close-session", "sessionId": "ck-canvas-navigated-1" }]
30
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "host-context-observed-persists",
3
+ "description": "Client iframe echoes the `McpUiHostContext` it received from `ui/initialize` via `host_context_observed` WS message (Client→Server). Server projects to `HostContextProjection` and persists on `Session.hostContext`. Persisted projection is observable via subsequent `ggui_handshake` and `ggui_consume` output. Field shape: `availableDisplayModes`, `displayMode`, `theme` — sourced verbatim from the spec-compliant host's `_meta.ui.host` projection. Stale-snapshot guard: a later `host_context_observed` overwrites earlier values.",
4
+ "skipReason": null,
5
+ "setup": [{ "type": "create-session", "sessionId": "ck-host-context-observed-1" }],
6
+ "inputEnvelope": {
7
+ "type": "host_context_observed",
8
+ "payload": {
9
+ "sessionId": "ck-host-context-observed-1",
10
+ "hostContext": {
11
+ "availableDisplayModes": ["inline", "pip", "fullscreen"],
12
+ "displayMode": "inline",
13
+ "theme": "light"
14
+ }
15
+ }
16
+ },
17
+ "expectedBehavior": {
18
+ "kind": "no-op",
19
+ "reason": "host_context_observed has no synchronous response — the side effect is `Session.hostContext` mutation observable via subsequent ggui_consume / handshake output."
20
+ },
21
+ "teardown": [{ "type": "close-session", "sessionId": "ck-host-context-observed-1" }]
22
+ }
@@ -0,0 +1,5 @@
1
+ import type { TestCase } from '../../types.js';
2
+ /** All fixtures asserting canvas-mode wire shapes (protocol
3
+ * draft-2026-05-17). */
4
+ export declare const canvasModeWireShapesFixtures: readonly TestCase[];
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fixtures/canvas-mode-wire-shapes/index.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;yBACyB;AACzB,eAAO,MAAM,4BAA4B,EAAE,SAAS,QAAQ,EAK3D,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * `canvas-mode-wire-shapes` fixture sub-module.
3
+ *
4
+ * Exercises the canvas-mode wire additions (protocol draft-2026-05-17):
5
+ *
6
+ * - `GguiBootstrapMeta.canvasMode` — discriminator + mutual-exclusion
7
+ * with `stackItemId`. Canvas iframes are session-scoped; no pinned
8
+ * item.
9
+ * - `_ggui:lifecycle` reserved channel + `CanvasLifecyclePayload`
10
+ * discriminated union (`handshake_started` / `handshake_completed` /
11
+ * `push_started` / `consume_polling`). Server fires fire-and-forget;
12
+ * iframe animator advances state machine on each kind.
13
+ * - `canvas_navigated` C→S WS message — user back-navigated; server
14
+ * updates `Session.activeStackItemId`. Payload omits `appId` —
15
+ * subscriber binding is authoritative scope.
16
+ * - `host_context_observed` C→S WS message — iframe echoes
17
+ * `McpUiHostContext` so server persists `HostContextProjection` on
18
+ * the session.
19
+ *
20
+ * All four fixtures ship with `skipReason: null`: the kit does not
21
+ * JSON-gate skip behavior. The runner dispatches every fixture and
22
+ * per-fixture skips emerge at RUNTIME from the host adapter throwing
23
+ * on an unimplemented setup directive (`set-app-mode`,
24
+ * `assert-session-field`, `assert-channel-envelope`) or from
25
+ * `match-behavior.ts` returning `unmatchable-on-ws`. Runner
26
+ * activation against the reference `ConformanceHost` is a follow-up
27
+ * kit minor once those directives land.
28
+ *
29
+ * Rationale: per the kit's drift discipline (see `../../types.ts`
30
+ * "Authored protocol vocabulary"), fixtures freeze the contract intent
31
+ * at the time the wire shape lands — not when the runner catches up.
32
+ * Third-party host implementers can read these JSONs directly to
33
+ * understand canvas-mode obligations without waiting for the reference
34
+ * host's directive coverage.
35
+ */
36
+ import canvasBootstrapMutualExclusion from './canvas-bootstrap-mutual-exclusion.json' with { type: 'json' };
37
+ import canvasLifecycleChannelEmitsHandshakeStarted from './canvas-lifecycle-channel-emits-handshake-started.json' with { type: 'json' };
38
+ import canvasNavigatedUpdatesActiveStackItem from './canvas-navigated-updates-active-stack-item.json' with { type: 'json' };
39
+ import hostContextObservedPersists from './host-context-observed-persists.json' with { type: 'json' };
40
+ /** All fixtures asserting canvas-mode wire shapes (protocol
41
+ * draft-2026-05-17). */
42
+ export const canvasModeWireShapesFixtures = [
43
+ canvasBootstrapMutualExclusion,
44
+ canvasLifecycleChannelEmitsHandshakeStarted,
45
+ canvasNavigatedUpdatesActiveStackItem,
46
+ hostContextObservedPersists,
47
+ ];
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Fixture catalog — every conformance fixture, classified by the
3
+ * contract or protocol criterion it exercises.
4
+ *
5
+ * The sub-module layout (one directory per contract) is load-bearing:
6
+ * it is how the reporter groups results on the bar-scorecard output,
7
+ * and how third-party implementers understand which criterion each
8
+ * fixture asserts. Renaming a sub-module directory breaks the
9
+ * reporter's output shape — treat the classification as the public
10
+ * API.
11
+ *
12
+ * ## Why static imports
13
+ *
14
+ * The fixture JSON files are statically imported (`import … from
15
+ * './foo.json' with { type: 'json' }`) so tsc inlines the fixture
16
+ * data into `dist/fixtures/`. Third-party TS consumers get typed
17
+ * arrays without a runtime file-read. Non-TS consumers (Python, Go,
18
+ * Rust) read the raw `.json` files directly — they ship in `dist/`
19
+ * via the package's `files` field.
20
+ *
21
+ * ## Current coverage
22
+ *
23
+ * Seven sub-modules with content (18 fixtures). Three additional
24
+ * sub-modules (`data-contract`, `tool-name-uniqueness`,
25
+ * `contract-error-payload`) are reserved for additive fixture
26
+ * expansion and not yet materialized — adding fixtures there is a
27
+ * kit minor version.
28
+ *
29
+ * `canvas-mode-wire-shapes` (4 fixtures) ships the canvas-mode wire
30
+ * contract intent: `canvasMode` bootstrap discriminator,
31
+ * `_ggui:lifecycle` channel + payload, `canvas_navigated` +
32
+ * `host_context_observed` C→S WS messages. Runner activation is a
33
+ * follow-up kit minor once the reference `ConformanceHost` exposes
34
+ * `set-app-mode` + `assert-session-field` + `assert-channel-envelope`
35
+ * directives.
36
+ *
37
+ * ## What is NOT here — the gadget pure-function obligations
38
+ *
39
+ * SPEC §7.7.2's gadget obligations are pure-function obligations, not
40
+ * transport-observable behaviors: which `clientCapabilities` payloads
41
+ * a parser accepts (`../schema-conformance`) and which `(contract,
42
+ * appGadgets)` pairs the push-time registry gate accepts / rejects
43
+ * (`../registration-conformance`). They are graded against a
44
+ * caller-supplied function — there is no session, transport, or wire
45
+ * frame to drive — so they do NOT belong in this behavioral fixture
46
+ * catalog.
47
+ */
48
+ export { bootstrapProtocolFixtures } from './bootstrap-protocol/index.js';
49
+ export { canvasModeWireShapesFixtures } from './canvas-mode-wire-shapes/index.js';
50
+ export { observabilityEventsFixtures } from './observability-events/index.js';
51
+ export { refreshSemanticsFixtures } from './refresh-semantics/index.js';
52
+ export { reservedChannelAuthorityFixtures } from './reserved-channel-authority/index.js';
53
+ export { schemaVersionHandshakeFixtures } from './schema-version-handshake/index.js';
54
+ export { wiredActionDispatchFixtures } from './wired-action-dispatch/index.js';
55
+ import type { TestCase } from '../types.js';
56
+ /**
57
+ * Contract slugs — match the sub-module directory names. The
58
+ * reporter uses these verbatim as group headers on the scorecard.
59
+ * Extensibly-closed via `(string & {})` so additive sub-modules
60
+ * register without an API break.
61
+ */
62
+ export type ContractSlug = 'bootstrap-protocol' | 'canvas-mode-wire-shapes' | 'observability-events' | 'refresh-semantics' | 'reserved-channel-authority' | 'schema-version-handshake' | 'wired-action-dispatch' | (string & {});
63
+ /**
64
+ * Every fixture the kit ships, grouped by the contract it exercises.
65
+ * Order within each group matches the sub-module's `export` order.
66
+ *
67
+ * Map shape is load-bearing — the runner iterates
68
+ * `Object.entries(fixturesByContract)` to produce the bar scorecard,
69
+ * and consumers iterate it for custom reporters.
70
+ */
71
+ export declare const fixturesByContract: Readonly<Record<ContractSlug, readonly TestCase[]>>;
72
+ /**
73
+ * Every fixture flat, in deterministic lexicographic order by
74
+ * `name`. Runners that don't care about classification iterate this
75
+ * list directly.
76
+ */
77
+ export declare const allFixtures: readonly TestCase[];
78
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AAC9E,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,gCAAgC,EAAE,MAAM,uCAAuC,CAAC;AACzF,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAE/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAS5C;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB,oBAAoB,GACpB,yBAAyB,GACzB,sBAAsB,GACtB,mBAAmB,GACnB,4BAA4B,GAC5B,0BAA0B,GAC1B,uBAAuB,GACvB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,SAAS,QAAQ,EAAE,CAAC,CAQlF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,QAAQ,EAGwB,CAAC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Fixture catalog — every conformance fixture, classified by the
3
+ * contract or protocol criterion it exercises.
4
+ *
5
+ * The sub-module layout (one directory per contract) is load-bearing:
6
+ * it is how the reporter groups results on the bar-scorecard output,
7
+ * and how third-party implementers understand which criterion each
8
+ * fixture asserts. Renaming a sub-module directory breaks the
9
+ * reporter's output shape — treat the classification as the public
10
+ * API.
11
+ *
12
+ * ## Why static imports
13
+ *
14
+ * The fixture JSON files are statically imported (`import … from
15
+ * './foo.json' with { type: 'json' }`) so tsc inlines the fixture
16
+ * data into `dist/fixtures/`. Third-party TS consumers get typed
17
+ * arrays without a runtime file-read. Non-TS consumers (Python, Go,
18
+ * Rust) read the raw `.json` files directly — they ship in `dist/`
19
+ * via the package's `files` field.
20
+ *
21
+ * ## Current coverage
22
+ *
23
+ * Seven sub-modules with content (18 fixtures). Three additional
24
+ * sub-modules (`data-contract`, `tool-name-uniqueness`,
25
+ * `contract-error-payload`) are reserved for additive fixture
26
+ * expansion and not yet materialized — adding fixtures there is a
27
+ * kit minor version.
28
+ *
29
+ * `canvas-mode-wire-shapes` (4 fixtures) ships the canvas-mode wire
30
+ * contract intent: `canvasMode` bootstrap discriminator,
31
+ * `_ggui:lifecycle` channel + payload, `canvas_navigated` +
32
+ * `host_context_observed` C→S WS messages. Runner activation is a
33
+ * follow-up kit minor once the reference `ConformanceHost` exposes
34
+ * `set-app-mode` + `assert-session-field` + `assert-channel-envelope`
35
+ * directives.
36
+ *
37
+ * ## What is NOT here — the gadget pure-function obligations
38
+ *
39
+ * SPEC §7.7.2's gadget obligations are pure-function obligations, not
40
+ * transport-observable behaviors: which `clientCapabilities` payloads
41
+ * a parser accepts (`../schema-conformance`) and which `(contract,
42
+ * appGadgets)` pairs the push-time registry gate accepts / rejects
43
+ * (`../registration-conformance`). They are graded against a
44
+ * caller-supplied function — there is no session, transport, or wire
45
+ * frame to drive — so they do NOT belong in this behavioral fixture
46
+ * catalog.
47
+ */
48
+ export { bootstrapProtocolFixtures } from './bootstrap-protocol/index.js';
49
+ export { canvasModeWireShapesFixtures } from './canvas-mode-wire-shapes/index.js';
50
+ export { observabilityEventsFixtures } from './observability-events/index.js';
51
+ export { refreshSemanticsFixtures } from './refresh-semantics/index.js';
52
+ export { reservedChannelAuthorityFixtures } from './reserved-channel-authority/index.js';
53
+ export { schemaVersionHandshakeFixtures } from './schema-version-handshake/index.js';
54
+ export { wiredActionDispatchFixtures } from './wired-action-dispatch/index.js';
55
+ import { bootstrapProtocolFixtures } from './bootstrap-protocol/index.js';
56
+ import { canvasModeWireShapesFixtures } from './canvas-mode-wire-shapes/index.js';
57
+ import { observabilityEventsFixtures } from './observability-events/index.js';
58
+ import { refreshSemanticsFixtures } from './refresh-semantics/index.js';
59
+ import { reservedChannelAuthorityFixtures } from './reserved-channel-authority/index.js';
60
+ import { schemaVersionHandshakeFixtures } from './schema-version-handshake/index.js';
61
+ import { wiredActionDispatchFixtures } from './wired-action-dispatch/index.js';
62
+ /**
63
+ * Every fixture the kit ships, grouped by the contract it exercises.
64
+ * Order within each group matches the sub-module's `export` order.
65
+ *
66
+ * Map shape is load-bearing — the runner iterates
67
+ * `Object.entries(fixturesByContract)` to produce the bar scorecard,
68
+ * and consumers iterate it for custom reporters.
69
+ */
70
+ export const fixturesByContract = {
71
+ 'bootstrap-protocol': bootstrapProtocolFixtures,
72
+ 'canvas-mode-wire-shapes': canvasModeWireShapesFixtures,
73
+ 'observability-events': observabilityEventsFixtures,
74
+ 'refresh-semantics': refreshSemanticsFixtures,
75
+ 'reserved-channel-authority': reservedChannelAuthorityFixtures,
76
+ 'schema-version-handshake': schemaVersionHandshakeFixtures,
77
+ 'wired-action-dispatch': wiredActionDispatchFixtures,
78
+ };
79
+ /**
80
+ * Every fixture flat, in deterministic lexicographic order by
81
+ * `name`. Runners that don't care about classification iterate this
82
+ * list directly.
83
+ */
84
+ export const allFixtures = Object.values(fixturesByContract)
85
+ .flat()
86
+ .slice()
87
+ .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
@@ -0,0 +1,5 @@
1
+ import type { TestCase } from '../../types.js';
2
+ /** All fixtures asserting the observability-event contract
3
+ * (SPEC §"Runtime Consequence Chain" Tier 3). */
4
+ export declare const observabilityEventsFixtures: readonly TestCase[];
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fixtures/observability-events/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;kDACkD;AAClD,eAAO,MAAM,2BAA2B,EAAE,SAAS,QAAQ,EAG1D,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `observability-events` fixture sub-module.
3
+ *
4
+ * Exercises the renderer's observability-event emission contract:
5
+ * every wired-action dispatch emits a `wired-tool-invoked` event;
6
+ * every `_ggui:contract-error` envelope emits a matching
7
+ * `contract-error-emitted` event.
8
+ *
9
+ * Both fixtures are `ConformanceHost`-gated — the direct
10
+ * postMessage-frame assertion requires a `page.exposeBinding` harness
11
+ * hook that the runner supplies. The effects are asserted implicitly
12
+ * via DOM state on the `wired-action-success` + contract-error
13
+ * fixtures in `wired-action-dispatch/`.
14
+ */
15
+ import observabilityContractErrorEmitted from './observability-contract-error-emitted.json' with { type: 'json' };
16
+ import observabilityWiredToolInvoked from './observability-wired-tool-invoked.json' with { type: 'json' };
17
+ /** All fixtures asserting the observability-event contract
18
+ * (SPEC §"Runtime Consequence Chain" Tier 3). */
19
+ export const observabilityEventsFixtures = [
20
+ observabilityContractErrorEmitted,
21
+ observabilityWiredToolInvoked,
22
+ ];
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "observability-contract-error-emitted",
3
+ "description": "Standalone `contract-error-emitted` observability row. Every `_ggui:contract-error` envelope the server emits on the live channel MUST be mirrored as a `contract-error-emitted` observability event for host-side inspection without requiring the host to parse live-channel frames. Asserts the mirror-emission contract directly on the observability envelope.",
4
+ "skipReason": null,
5
+ "setup": [
6
+ { "type": "create-session", "sessionId": "ck-obs-ce-1" },
7
+ { "type": "register-tool", "toolName": "broken_tool", "handler": "throw" },
8
+ { "type": "register-actionspec", "name": "triggerBroken", "tool": "broken_tool" }
9
+ ],
10
+ "inputEnvelope": {
11
+ "type": "action",
12
+ "channel": 0,
13
+ "sessionId": "ck-obs-ce-1",
14
+ "action": {
15
+ "name": "triggerBroken",
16
+ "data": {}
17
+ }
18
+ },
19
+ "expectedBehavior": {
20
+ "kind": "observability-event",
21
+ "event": {
22
+ "kind": "contract-error-emitted",
23
+ "code": "TOOL_THREW",
24
+ "toolName": "broken_tool",
25
+ "actionName": "triggerBroken"
26
+ }
27
+ },
28
+ "teardown": [{ "type": "close-session", "sessionId": "ck-obs-ce-1" }]
29
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "observability-wired-tool-invoked",
3
+ "description": "Standalone `wired-tool-invoked` observability row. Distinct from `wired-action-success`'s DOM-level assertion — asserts directly on the postMessage observability envelope shape so Phase 3.1 can assert wire-byte equality without DOM observation.",
4
+ "skipReason": null,
5
+ "setup": [
6
+ { "type": "create-session", "sessionId": "ck-obs-wired-1" },
7
+ { "type": "register-tool", "toolName": "tasks_complete", "handler": "echo" },
8
+ { "type": "register-actionspec", "name": "toggleTask", "tool": "tasks_complete" }
9
+ ],
10
+ "inputEnvelope": {
11
+ "type": "action",
12
+ "channel": 0,
13
+ "sessionId": "ck-obs-wired-1",
14
+ "action": {
15
+ "name": "toggleTask",
16
+ "data": { "id": "seed-1" }
17
+ }
18
+ },
19
+ "expectedBehavior": {
20
+ "kind": "observability-event",
21
+ "event": {
22
+ "kind": "wired-tool-invoked",
23
+ "toolName": "tasks_complete",
24
+ "actionName": "toggleTask"
25
+ }
26
+ },
27
+ "teardown": [{ "type": "close-session", "sessionId": "ck-obs-wired-1" }]
28
+ }
@@ -0,0 +1,5 @@
1
+ import type { TestCase } from '../../types.js';
2
+ /** All fixtures asserting refresh semantics (SPEC §2.3 StreamSpec +
3
+ * §4.4 Envelope Specification refresh triggers). */
4
+ export declare const refreshSemanticsFixtures: readonly TestCase[];
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fixtures/refresh-semantics/index.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;qDACqD;AACrD,eAAO,MAAM,wBAAwB,EAAE,SAAS,QAAQ,EAEvD,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `refresh-semantics` fixture sub-module.
3
+ *
4
+ * Exercises stream-refresh contract:
5
+ * - Successful action → declared `streamSpec[channel].triggers` list
6
+ * triggers a refresh → new stream-update observed on the channel.
7
+ * - Failed action → refresh does NOT fire; stream state preserved.
8
+ *
9
+ * One fixture today (`stream-refresh-success`) driving through the
10
+ * `todo-list` blueprint's DOM state. The direct stream-update channel
11
+ * assertion is `ConformanceHost`-gated pending a channel-subscription
12
+ * transport seam.
13
+ */
14
+ import streamRefreshSuccess from './stream-refresh-success.json' with { type: 'json' };
15
+ /** All fixtures asserting refresh semantics (SPEC §2.3 StreamSpec +
16
+ * §4.4 Envelope Specification refresh triggers). */
17
+ export const refreshSemanticsFixtures = [
18
+ streamRefreshSuccess,
19
+ ];