@ggui-ai/protocol-conformance 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +54 -16
  2. package/dist/fixtures/consume-buffer/index.d.ts.map +1 -1
  3. package/dist/fixtures/consume-buffer/index.js +4 -1
  4. package/dist/fixtures/index.d.ts +42 -4
  5. package/dist/fixtures/index.d.ts.map +1 -1
  6. package/dist/fixtures/index.js +42 -4
  7. package/dist/fixtures/reserved-channel-authority/index.d.ts.map +1 -1
  8. package/dist/fixtures/reserved-channel-authority/index.js +4 -2
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +7 -0
  12. package/dist/match-behavior.d.ts.map +1 -1
  13. package/dist/match-behavior.js +4 -2
  14. package/dist/resource-read-conformance/cases/all-ephemeral-substrate-fuses-like-none.json +29 -0
  15. package/dist/resource-read-conformance/cases/evicted-row-remints-from-the-durable-record.json +26 -0
  16. package/dist/resource-read-conformance/cases/half-wired-blueprints-only-substrate-fuses-like-none.json +29 -0
  17. package/dist/resource-read-conformance/cases/half-wired-identity-only-substrate-fuses-like-none.json +29 -0
  18. package/dist/resource-read-conformance/cases/live-row-read-returns-mount-material.json +18 -0
  19. package/dist/resource-read-conformance/cases/malformed-locator-stays-outside-the-typed-set.json +19 -0
  20. package/dist/resource-read-conformance/cases/read-miss-answers-not-found.json +23 -0
  21. package/dist/resource-read-conformance/cases/read-on-a-substrate-less-server-answers-not-supported.json +22 -0
  22. package/dist/resource-read-conformance/cases/read-record-with-purged-blueprint-answers-blueprint-unresolvable.json +29 -0
  23. package/dist/resource-read-conformance/cases/read-refusal-is-indistinguishable-from-a-miss.json +47 -0
  24. package/dist/resource-read-conformance/cases/read-without-a-delivery-channel-answers-not-mountable.json +22 -0
  25. package/dist/resource-read-conformance/cases/registry-match-fuses-on-not-mountable.json +40 -0
  26. package/dist/resource-read-conformance/cases/substrate-less-fusion-of-refusal-and-miss.json +29 -0
  27. package/dist/resource-read-conformance/index.d.ts +371 -0
  28. package/dist/resource-read-conformance/index.d.ts.map +1 -0
  29. package/dist/resource-read-conformance/index.js +782 -0
  30. package/package.json +8 -3
package/README.md CHANGED
@@ -1,22 +1,25 @@
1
1
  # @ggui-ai/protocol-conformance
2
2
 
3
- Conformance test kit for the ggui protocol. Ships a JSON fixture catalog + a runner that drives each fixture against a live implementation over WebSocket and reports scorecard-style pass / fail / skip output.
3
+ Conformance test kit for the ggui protocol. Ships JSON case catalogs + runners that drive them against a live implementation — the behavioral fixtures over WebSocket, the `resources/read` cases over the MCP binding — and report scorecard-style pass / fail / skip output.
4
4
 
5
5
  This kit is the protocol's conformance surface: it lets a third-party MCP builder prove their implementation is protocol-conformant without reimplementing the test harness.
6
6
 
7
7
  ## Grading mechanisms
8
8
 
9
- The kit grades protocol obligations through **three mechanisms**, each matched to where the obligation is observable:
9
+ The kit grades protocol obligations through **four mechanisms**, each matched to where the obligation is observable:
10
10
 
11
11
  1. **Path-A WS fixtures** — wire-observable behaviors graded from frames the runner collects over the live channel (acks, error frames, canonical `data` deliveries).
12
12
  2. **Pure-function catalogs** — deterministic validation obligations (the SPEC §7.7.2 gadget obligations) graded against a caller-supplied function; no host, render, or transport.
13
13
  3. **Session-state host read-back** — stateful obligations with no wire response (e.g. `host_context_observed` persisting onto `GguiSession.hostContext`): the runner dispatches the C→S frame, waits out the observation window, then reads the GguiSession field back through `ConformanceHost.readSessionField()` and deep-equals it against the fixture's `expected`. No host (or a host without the method, or a read that throws) → the fixture SKIPS with the reason — a host that cannot read state cannot grade it, and the kit never converts that gap into a pass.
14
+ 4. **MCP-binding scenario driver** — request/response obligations on the MCP surface, where the deployment shape is part of what is under test. The `resources/read` catalog is the first: a case declares a server shape, a caller identity and a seed set, the adopter's driver brings a server up that way, and the kit reads locators against it and grades the raw JSON-RPC frames. A driver that cannot express a scenario throws, and the case SKIPS — never passes.
14
15
 
15
- A fourth surface — the **Path-B browser-host driver** for DOM-level claims (`bootstrap-failure`, `props-update`) — is honestly absent: it is not yet packaged, and those fixtures skip wherever the kit runs (see below).
16
+ A fifth surface — the **Path-B browser-host driver** for DOM-level claims (`bootstrap-failure`, `props-update`) — is honestly absent: it is not yet packaged, and those fixtures skip wherever the kit runs (see below).
16
17
 
17
18
  ## Transport
18
19
 
19
- **v1.0 is WebSocket-only.** The canonical ggui transport is WS (see SPEC §12 Transport Bindings). `TransportConfig` is shaped as an extensibly-closed union so later transports (stdio MCP, HTTP long-poll) can be added post-v1.1 without breaking the public API.
20
+ **The behavioral fixture catalog is WebSocket-only.** The canonical ggui live-channel transport is WS (see SPEC §12 Transport Bindings). `TransportConfig` is shaped as an extensibly-closed union so later live-channel transports (HTTP long-poll) can be added without breaking the public API.
21
+
22
+ The MCP surface is graded separately, by catalog rather than by transport config — see `resource-read-conformance` below. Its cases are deliberately NOT registered in `fixturesByContract`: everything in that map is driven over a WebSocket, so an MCP case there would be a permanent skip on every WS run, which is the false gate the kit's exact skip-set pinning exists to prevent. `resources/read` is the only MCP method bound today; `tools/call` has no driver yet.
20
23
 
21
24
  ## Path-A vs Path-B matchability
22
25
 
@@ -42,7 +45,7 @@ The Path-B driver is not yet packaged — no browser-host harness ships with the
42
45
 
43
46
  The partition is intentional: Path-A FAILs are vendor-neutrality bugs the server owns; Path-B SKIPs are not fails — they are claims a different driver is responsible for.
44
47
 
45
- One declared grading gap on the action loop: `action-ack` proves the append half of the consume-buffer contract; the retrieval half (the agent draining the buffer via `ggui_consume`) is an MCP tool call a WS-only runner cannot drive. Grading it needs an MCP-binding driver — a future kit surface, not a weaker WS assertion.
48
+ One declared grading gap on the action loop: `action-ack` proves the append half of the consume-buffer contract; the retrieval half (the agent draining the buffer via `ggui_consume`) is an MCP **tool call**, which a WS runner cannot drive. The resource-read catalog below does not close it — that driver binds `resources/read`, and `tools/call` is a different method with a different seam. Grading it needs a tool-call driver, not a weaker WS assertion.
46
49
 
47
50
  ## Pure-function conformance catalogs
48
51
 
@@ -74,27 +77,62 @@ if (schema.failed.length + gate.failed.length + urls.failed.length > 0) {
74
77
 
75
78
  The `schema-conformance` meta-test binds its catalog to the live `@ggui-ai/protocol` `clientCapabilitiesSpecSchema` — a drift-catch if the wire schema diverges from the §7.7.2 obligations the catalog freezes. The `registration-conformance` and `resolution-conformance` meta-tests verify catalog coherence against faithful in-test implementations (the kit stays vendor-neutral — it does not depend on a server implementation); grading the _shipping_ gate / resolver is an implementation-side test that drives the corresponding runner.
76
79
 
80
+ ## Resource-read conformance — the MCP binding
81
+
82
+ A read of a render locator (`ui://ggui/render/{sessionId}` or `ui://ggui/render/{sessionId}/{blueprintKey}`) has exactly **two exits**: a result whose contents declare a delivery channel — a live mount — or one typed JSON-RPC error. There is no third outcome, and in particular no successful result carrying a shell that can never paint anything.
83
+
84
+ That is what makes the read host-checkable. A host that gets `contents` back can mount them without inspecting anything; a host that gets an error routes on `error.data.code` — "come back never" versus "come back with a fresh render" — without parsing prose.
85
+
86
+ ```ts
87
+ import { runResourceReadConformance } from "@ggui-ai/protocol-conformance/resource-read-conformance";
88
+
89
+ const result = await runResourceReadConformance(async (scenario) => {
90
+ // Bring YOUR server up in `scenario.server`'s shape, as
91
+ // `scenario.caller`, with `scenario.seeds` applied.
92
+ const server = await bootMyServer(scenario);
93
+ return {
94
+ // Keys your registry assigned to `registered-blueprint` seeds.
95
+ registeredKeys: server.registeredKeys,
96
+ // MUST resolve. A protocol failure is `{kind: 'error'}` carrying
97
+ // the RAW frame — an MCP client's reconstructed error rewrites
98
+ // `message`, and the disclosure obligation is graded on bytes.
99
+ read: (uri) => server.readResource(uri),
100
+ dispose: () => server.close(),
101
+ };
102
+ });
103
+
104
+ if (result.failed.length > 0 || result.passed.length === 0) process.exit(1);
105
+ ```
106
+
107
+ Throwing from the driver means "I cannot express this scenario" and the case **skips** with that reason — never passes. Throwing from `read` is a **fail**, because the read is the thing under test.
108
+
109
+ The catalog grades all four failure classes (`NOT_FOUND` on `-32002`; `BLUEPRINT_UNRESOLVABLE` / `NOT_SUPPORTED` / `NOT_MOUNTABLE` on `-32006`), the mount half on both a live row and a re-minted one, and the disclosure obligation: a read refused for lack of entitlement and a read of a locator that never existed must be **byte-identical**, on a server that keeps durable records, on one that keeps none, on the two half-wired shapes in between, and on one whose blueprint registry matches the probed key.
110
+
111
+ It deliberately does **not** grade: the order in which a substrate-less server answers (`NOT_SUPPORTED` describes the deployment, so answering it immediately is correct); `detail` wording on any code; the `NOT_FOUND` message literal (its _constancy_ is what is normative); internal-error message text; the number a URI naming no locator receives (that belongs to the transport binding — only the negative is graded, that it must not be one of the four); and the shell's markup (success is graded on the projected render meta, never on DOM shape).
112
+
77
113
  ## Conformance status
78
114
 
79
115
  What is actually graded against shipping code today, and what is still awaiting a driver. This is an inventory, not a roadmap — a mechanism is listed as proven only if a test in the repo drives it against the real implementation on every push.
80
116
 
81
- Every test below is a plain `*.test.ts` inside a package's `src/`, so it runs in that package's ordinary vitest suite. The ggui repo's per-push CI runs `turbo run test` across all non-e2e packages, so **all five rows below are gated on every push** — the kit needs no dedicated workflow, and adopters wiring it into their own CI need nothing more than their existing unit-test task.
117
+ Every test below is a plain `*.test.ts` inside a package's `src/`, so it runs in that package's ordinary vitest suite. The ggui repo's per-push CI runs `turbo run test` across all non-e2e packages, so **all six rows below are gated on every push** — the kit needs no dedicated workflow, and adopters wiring it into their own CI need nothing more than their existing unit-test task.
82
118
 
83
- | Obligation | Graded against | Driver |
84
- | ----------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- |
85
- | Gadget wire schema (§7.7.2) | the shipping `@ggui-ai/protocol` `clientCapabilitiesSpecSchema` | this kit's own `src/schema-conformance/schema-conformance.test.ts` |
86
- | Registration gate (§7.7.3, §7.9) | the shipping gate trio in `@ggui-ai/mcp-server-handlers` | `mcp-server-handlers/src/renders/assert-gadgets.conformance.test.ts` |
87
- | Bundle-URL resolution (§7.7.2) | the shipping `resolveGadgetUrls` in `@ggui-ai/mcp-server-handlers` | `mcp-server-handlers/src/renders/resolve-gadget-urls.conformance.test.ts` |
88
- | Path-A WS fixtures — first-party | the real `@ggui-ai/mcp-server` GguiSession channel, booted in-process on an ephemeral port | `mcp-server/src/ggui-session-channel.conformance.test.ts` — **8 pass / 4 skip / 0 fail** |
89
- | Path-A WS fixtures — vendor-neutral | `@ggui-ai/protocol-reference-server` | `protocol-reference-server/src/conformance.test.ts` — **9 pass / 3 skip / 0 fail** |
119
+ | Obligation | Graded against | Driver |
120
+ | ------------------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
121
+ | Gadget wire schema (§7.7.2) | the shipping `@ggui-ai/protocol` `clientCapabilitiesSpecSchema` | this kit's own `src/schema-conformance/schema-conformance.test.ts` |
122
+ | Registration gate (§7.7.3, §7.9) | the shipping gate trio in `@ggui-ai/mcp-server-handlers` | `mcp-server-handlers/src/renders/assert-gadgets.conformance.test.ts` |
123
+ | Bundle-URL resolution (§7.7.2) | the shipping `resolveGadgetUrls` in `@ggui-ai/mcp-server-handlers` | `mcp-server-handlers/src/renders/resolve-gadget-urls.conformance.test.ts` |
124
+ | Path-A WS fixtures — first-party | the real `@ggui-ai/mcp-server` GguiSession channel, booted in-process on an ephemeral port | `mcp-server/src/ggui-session-channel.conformance.test.ts` — **8 pass / 4 skip / 0 fail** |
125
+ | Path-A WS fixtures — vendor-neutral | `@ggui-ai/protocol-reference-server` | `protocol-reference-server/src/conformance.test.ts` — **9 pass / 3 skip / 0 fail** |
126
+ | `resources/read` typed failures + mount invariant | the shipping `registerGguiRenderResourceTemplate` in `@ggui-ai/mcp-server` | `mcp-server/src/mcp-apps-outbound.resource-read.conformance.test.ts` — **12 pass / 0 skip / 0 fail** |
90
127
 
91
- Both WS drivers pin their pass set _and_ their skip set as exact sets. A fixture silently degrading to a skip fails the build, and so does a skipped fixture that quietly starts passing — a skip set that can grow unnoticed is a false gate, and re-pinning it is meant to be a deliberate act.
128
+ Both WS drivers pin their pass set _and_ their skip set as exact sets, and the resource-read driver pins its skip set as exactly empty. A fixture silently degrading to a skip fails the build, and so does a skipped fixture that quietly starts passing — a skip set that can grow unnoticed is a false gate, and re-pinning it is meant to be a deliberate act.
92
129
 
93
130
  **Awaiting a driver:**
94
131
 
95
- - **Path-B browser-host fixtures** — `bootstrap-bundle-fetch-failed`, `bootstrap-meta-missing` (both need MCP-Apps-host fault injection) and `props-update-roundtrip` (assertion is on rendered DOM). No browser-host adapter ships with the kit, so these skip on every driver above. Honestly absent, not silently passing.
132
+ - **Path-B browser-host fixtures** — `bootstrap-bundle-fetch-failed`, `bootstrap-meta-missing` (both need MCP-Apps-host fault injection) and `props-update-roundtrip` (assertion is on rendered DOM). No browser-host adapter ships with the kit, so these skip on every WS driver above. Honestly absent, not silently passing.
96
133
  - **`version-mismatch` on the first-party server** — needs a `server-version-override` seam the channel deliberately does not expose; adding a production seam with no production caller purely to drive a fixture was rejected. It passes on the reference server, which has a per-render override.
97
- - **The `ggui_consume` retrieval half of the action loop** — `action-ack-sequence` proves the append half; draining the buffer is an MCP tool call a WS-only runner cannot drive. Needs an MCP-binding driver, a future kit surface.
134
+ - **The `ggui_consume` retrieval half of the action loop, and `stream-delivery-roundtrip`** — both are `tools/call` obligations (`ggui_consume`, `ggui_emit`). The resource-read driver binds `resources/read` only, so neither is closed by it; both await a tool-call driver.
135
+ - **`resources/read` on a second implementation** — the catalog is vendor-neutral by construction (the kit imports no server; the adopter supplies the driver), but it is graded against one implementation today. A second binding is what would ground the vendor-neutrality claim empirically, the way `protocol-reference-server` does for the WS catalog.
98
136
 
99
137
  ## Public API
100
138
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fixtures/consume-buffer/index.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;gCAEgC;AAChC,eAAO,MAAM,qBAAqB,EAAE,SAAS,QAAQ,EAIpD,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fixtures/consume-buffer/index.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;gCAEgC;AAChC,eAAO,MAAM,qBAAqB,EAAE,SAAS,QAAQ,EAIpD,CAAC"}
@@ -20,7 +20,10 @@
20
20
  *
21
21
  * The retrieval half — the agent draining the buffer via
22
22
  * `ggui_consume` — is an MCP tool call a WS-only runner cannot drive;
23
- * that obligation belongs to an MCP-binding driver, not this catalog.
23
+ * that obligation belongs to a driver bound to `tools/call`, not this
24
+ * catalog. The kit's `resources/read` driver
25
+ * (`../../resource-read-conformance`) binds a different method and does
26
+ * not close it.
24
27
  * Same declared-gap posture for the schema-validity meta-check on the
25
28
  * DECLARED schema itself: a malformed authored schema is a fixture-
26
29
  * authoring error the runner throws on (validating parse), while a
@@ -20,7 +20,7 @@
20
20
  *
21
21
  * ## Current coverage
22
22
  *
23
- * Six sub-modules with content (12 fixtures). Additional sub-modules
23
+ * Six WebSocket sub-modules with content (12 fixtures). Additional sub-modules
24
24
  * (`data-contract`, `tool-name-uniqueness`)
25
25
  * are reserved for additive fixture expansion and not yet
26
26
  * materialized — adding fixtures there is a kit minor version.
@@ -36,6 +36,43 @@
36
36
  * frame to drive — so they do NOT belong in this behavioral fixture
37
37
  * catalog.
38
38
  *
39
+ * ## An MCP-binding driver EXISTS for `resources/read` — `../resource-read-conformance`
40
+ *
41
+ * This catalog used to declare the MCP-binding driver as missing. It is
42
+ * no longer missing. `../resource-read-conformance` ships a driver
43
+ * bound to the MCP **`resources/read`** method, with its own case
44
+ * shape, its own runner (`runResourceReadConformance`) and its own
45
+ * scenario-driver seam. It grades the typed-failure contract for a
46
+ * render locator (`ui://ggui/render/{sessionId}[/{blueprintKey}]`): the
47
+ * four failure classes on their canonical numbers, the constant
48
+ * `NOT_FOUND` body, the deny-equals-miss byte-identity obligation, and
49
+ * the invariant that any successful result declares a delivery channel.
50
+ *
51
+ * Its cases are NOT registered in the map below, and that is
52
+ * deliberate. Every entry here is driven by `runConformance()` over a
53
+ * live channel, so an MCP case landing in this catalog would be a
54
+ * permanent skip on every WebSocket run — a skip that can never become
55
+ * a pass on that transport, which is exactly the false gate this kit's
56
+ * exact skip-set pinning exists to prevent.
57
+ *
58
+ * ### Precisely what that driver does NOT close
59
+ *
60
+ * It binds ONE MCP method. `tools/call` has no driver, so both of these
61
+ * remain open and neither is addressed by it:
62
+ *
63
+ * - **`stream-delivery-roundtrip`** — see the next section. Needs
64
+ * `ggui_emit`.
65
+ * - **The `ggui_consume` retrieval half of the action loop** —
66
+ * `action-ack-sequence` proves the append half; draining the buffer
67
+ * is `ggui_consume`.
68
+ *
69
+ * `resources/read` and `tools/call` are different methods with
70
+ * different seams: the former is a stateless read of a URI the kit can
71
+ * construct, the latter is an invocation whose arguments and effects
72
+ * are the agent's vocabulary. A driver for one is not a driver for the
73
+ * other, and saying "the kit now has an MCP driver" without that
74
+ * qualifier would read as closing gaps it does not touch.
75
+ *
39
76
  * ## What is NOT here — declared Path-A sequencing gap
40
77
  *
41
78
  * A `stream-delivery-roundtrip` fixture (an `emit-envelope`'d payload
@@ -52,9 +89,10 @@
52
89
  * transport, while emission is server-side (the agent's `ggui_emit`
53
90
  * MCP tool) — routing a host directive through the wire-frame slot
54
91
  * would conflate the two vocabularies. Honest grading needs either a
55
- * post-subscribe host-directive phase (a runner-mechanism change) or
56
- * an MCP-binding driver. Declared here rather than papered over with
57
- * a fixture that fails conformant servers on sequencing alone.
92
+ * post-subscribe host-directive phase (a runner-mechanism change) or a
93
+ * driver bound to `tools/call`. The `resources/read` driver above is
94
+ * NOT that driver. Declared here rather than papered over with a
95
+ * fixture that fails conformant servers on sequencing alone.
58
96
  */
59
97
  export { bootstrapProtocolFixtures } from './bootstrap-protocol/index.js';
60
98
  export { consumeBufferFixtures } from './consume-buffer/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,gCAAgC,EAAE,MAAM,uCAAuC,CAAC;AACzF,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAExE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQ5C;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB,oBAAoB,GACpB,gBAAgB,GAChB,cAAc,GACd,4BAA4B,GAC5B,0BAA0B,GAC1B,mBAAmB,GACnB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,SAAS,QAAQ,EAAE,CAAC,CAOlF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,QAAQ,EAGwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fixtures/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+FG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,gCAAgC,EAAE,MAAM,uCAAuC,CAAC;AACzF,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAExE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAQ5C;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GACpB,oBAAoB,GACpB,gBAAgB,GAChB,cAAc,GACd,4BAA4B,GAC5B,0BAA0B,GAC1B,mBAAmB,GACnB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,SAAS,QAAQ,EAAE,CAAC,CAOlF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS,QAAQ,EAGwB,CAAC"}
@@ -20,7 +20,7 @@
20
20
  *
21
21
  * ## Current coverage
22
22
  *
23
- * Six sub-modules with content (12 fixtures). Additional sub-modules
23
+ * Six WebSocket sub-modules with content (12 fixtures). Additional sub-modules
24
24
  * (`data-contract`, `tool-name-uniqueness`)
25
25
  * are reserved for additive fixture expansion and not yet
26
26
  * materialized — adding fixtures there is a kit minor version.
@@ -36,6 +36,43 @@
36
36
  * frame to drive — so they do NOT belong in this behavioral fixture
37
37
  * catalog.
38
38
  *
39
+ * ## An MCP-binding driver EXISTS for `resources/read` — `../resource-read-conformance`
40
+ *
41
+ * This catalog used to declare the MCP-binding driver as missing. It is
42
+ * no longer missing. `../resource-read-conformance` ships a driver
43
+ * bound to the MCP **`resources/read`** method, with its own case
44
+ * shape, its own runner (`runResourceReadConformance`) and its own
45
+ * scenario-driver seam. It grades the typed-failure contract for a
46
+ * render locator (`ui://ggui/render/{sessionId}[/{blueprintKey}]`): the
47
+ * four failure classes on their canonical numbers, the constant
48
+ * `NOT_FOUND` body, the deny-equals-miss byte-identity obligation, and
49
+ * the invariant that any successful result declares a delivery channel.
50
+ *
51
+ * Its cases are NOT registered in the map below, and that is
52
+ * deliberate. Every entry here is driven by `runConformance()` over a
53
+ * live channel, so an MCP case landing in this catalog would be a
54
+ * permanent skip on every WebSocket run — a skip that can never become
55
+ * a pass on that transport, which is exactly the false gate this kit's
56
+ * exact skip-set pinning exists to prevent.
57
+ *
58
+ * ### Precisely what that driver does NOT close
59
+ *
60
+ * It binds ONE MCP method. `tools/call` has no driver, so both of these
61
+ * remain open and neither is addressed by it:
62
+ *
63
+ * - **`stream-delivery-roundtrip`** — see the next section. Needs
64
+ * `ggui_emit`.
65
+ * - **The `ggui_consume` retrieval half of the action loop** —
66
+ * `action-ack-sequence` proves the append half; draining the buffer
67
+ * is `ggui_consume`.
68
+ *
69
+ * `resources/read` and `tools/call` are different methods with
70
+ * different seams: the former is a stateless read of a URI the kit can
71
+ * construct, the latter is an invocation whose arguments and effects
72
+ * are the agent's vocabulary. A driver for one is not a driver for the
73
+ * other, and saying "the kit now has an MCP driver" without that
74
+ * qualifier would read as closing gaps it does not touch.
75
+ *
39
76
  * ## What is NOT here — declared Path-A sequencing gap
40
77
  *
41
78
  * A `stream-delivery-roundtrip` fixture (an `emit-envelope`'d payload
@@ -52,9 +89,10 @@
52
89
  * transport, while emission is server-side (the agent's `ggui_emit`
53
90
  * MCP tool) — routing a host directive through the wire-frame slot
54
91
  * would conflate the two vocabularies. Honest grading needs either a
55
- * post-subscribe host-directive phase (a runner-mechanism change) or
56
- * an MCP-binding driver. Declared here rather than papered over with
57
- * a fixture that fails conformant servers on sequencing alone.
92
+ * post-subscribe host-directive phase (a runner-mechanism change) or a
93
+ * driver bound to `tools/call`. The `resources/read` driver above is
94
+ * NOT that driver. Declared here rather than papered over with a
95
+ * fixture that fails conformant servers on sequencing alone.
58
96
  */
59
97
  export { bootstrapProtocolFixtures } from './bootstrap-protocol/index.js';
60
98
  export { consumeBufferFixtures } from './consume-buffer/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fixtures/reserved-channel-authority/index.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,qEAAqE;AACrE,eAAO,MAAM,gCAAgC,EAAE,SAAS,QAAQ,EAE/D,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/fixtures/reserved-channel-authority/index.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,qEAAqE;AACrE,eAAO,MAAM,gCAAgC,EAAE,SAAS,QAAQ,EAE/D,CAAC"}
@@ -21,8 +21,10 @@
21
21
  * are Server→Client only). Agent emission enters through the
22
22
  * `ggui_emit` MCP tool, so both the violation and the rejection that
23
23
  * grades it are observable only on the MCP binding. That fixture
24
- * belongs to a future MCP-binding driver — the gap is declared here
25
- * rather than faked with a frame the wire cannot carry.
24
+ * belongs to a future driver bound to `tools/call` — the kit's
25
+ * `resources/read` driver (`../../resource-read-conformance`) binds a
26
+ * different method and does not reach `ggui_emit`. The gap is declared
27
+ * here rather than faked with a frame the wire cannot carry.
26
28
  */
27
29
  import propsUpdateRoundtrip from './props-update-roundtrip.json' with { type: 'json' };
28
30
  /** All fixtures asserting reserved-channel authority (SPEC §4.4). */
package/dist/index.d.ts CHANGED
@@ -20,6 +20,8 @@ export { gadgetRegistrationCases, runRegistrationConformance, } from './registra
20
20
  export type { GadgetGateRejectCode, GateOutcome, RegistrationConformanceCase, RegistrationConformanceMismatch, RegistrationConformanceResult, } from './registration-conformance/index.js';
21
21
  export { gadgetResolutionCases, runResolutionConformance, } from './resolution-conformance/index.js';
22
22
  export type { GadgetUrlEntry, ResolvedGadgetUrls, ResolutionConformanceCase, ResolutionConformanceMismatch, ResolutionConformanceResult, } from './resolution-conformance/index.js';
23
+ export { declaresDeliveryChannel, renderLocatorUri, resourceReadCases, runResourceReadConformance, GGUI_RENDER_RESOURCE_URI, } from './resource-read-conformance/index.js';
24
+ export type { DurableSubstrateWiring, JsonRpcErrorFrame, PreparedResourceReadScenario, ResourceReadCaller, ResourceReadConformanceCase, ResourceReadConformanceFailure, ResourceReadConformanceResult, ResourceReadConformanceSkip, ResourceReadErrorCodeDecl, ResourceReadExpectation, ResourceReadKey, ResourceReadLocator, ResourceReadOutcome, ResourceReadProbe, ResourceReadRenderMeta, ResourceReadScenario, ResourceReadScenarioDriver, ResourceReadSeed, ResourceReadServerShape, RunResourceReadConformanceOptions, } from './resource-read-conformance/index.js';
23
25
  export { loadFixture, listFixtures, loadAllFixtures } from './loader.js';
24
26
  export { runConformance } from './run-conformance.js';
25
27
  export type { RunConformanceConfig, ConformanceResult, ConformanceFailure, SkippedFixture, ConformanceReporter, InputEnvelopeDispatch, HostContextObservedInputEnvelope, } from './run-conformance.js';
@@ -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;AAKvC,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;AAG3C,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"}
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;AAKvC,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;AAQ3C,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
@@ -30,6 +30,13 @@ export { gadgetRegistrationCases, runRegistrationConformance, } from './registra
30
30
  // compute for a gadget descriptor's transport fields (`bundleHost`
31
31
  // precedence, default host, loopback scheme).
32
32
  export { gadgetResolutionCases, runResolutionConformance, } from './resolution-conformance/index.js';
33
+ // Resource-read conformance — the kit's first MCP-binding driver, and
34
+ // it binds `resources/read` ONLY (`tools/call` has none). A read of a
35
+ // render locator has exactly two exits: a result whose contents
36
+ // declare a delivery channel, or one typed JSON-RPC error.
37
+ // Unlike the three catalogs above this one drives a live server, so
38
+ // the caller supplies a scenario driver rather than a pure function.
39
+ export { declaresDeliveryChannel, renderLocatorUri, resourceReadCases, runResourceReadConformance, GGUI_RENDER_RESOURCE_URI, } from './resource-read-conformance/index.js';
33
40
  // Runtime loader — look up a fixture by name from the inlined catalog.
34
41
  export { loadFixture, listFixtures, loadAllFixtures } from './loader.js';
35
42
  // Runner — drive the catalog against a live implementation.
@@ -1 +1 @@
1
- {"version":3,"file":"match-behavior.d.ts","sourceRoot":"","sources":["../src/match-behavior.ts"],"names":[],"mappings":"AA6FA,OAAO,KAAK,EAIV,gBAAgB,EAIjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,gBAAgB,CAAC,MAAM,CAAC,EAC3D,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,EAAE,CAAC,GACN,QAAQ,IAAI,OAAO,CAAC,gBAAgB,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,CAE7D;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,SAAS,aAAa,EAAE,GAC/B,WAAW,CAkDb;AAgRD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAgBzD"}
1
+ {"version":3,"file":"match-behavior.d.ts","sourceRoot":"","sources":["../src/match-behavior.ts"],"names":[],"mappings":"AA+FA,OAAO,KAAK,EAIV,gBAAgB,EAIjB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,gBAAgB,CAAC,MAAM,CAAC,EAC3D,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,EAAE,CAAC,GACN,QAAQ,IAAI,OAAO,CAAC,gBAAgB,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;CAAE,CAAC,CAE7D;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,SAAS,aAAa,EAAE,GAC/B,WAAW,CAkDb;AAgRD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAgBzD"}
@@ -84,8 +84,10 @@
84
84
  * `action-ack` proves the append half of the consume-buffer contract.
85
85
  * The retrieval half — the agent draining the buffer via
86
86
  * `ggui_consume({sessionId})` — is an MCP tool call a WS-only runner
87
- * cannot drive. Grading it needs an MCP-binding driver; that gap is
88
- * declared here rather than papered over with a weaker assertion.
87
+ * cannot drive. Grading it needs a driver bound to `tools/call`. The
88
+ * kit's `resources/read` driver (`../resource-read-conformance`) does
89
+ * NOT reach it: different method, different seam. That gap is declared
90
+ * here rather than papered over with a weaker assertion.
89
91
  */
90
92
  import { PROTOCOL_SCHEMA_VERSION } from '@ggui-ai/protocol';
91
93
  import { isRecord } from '@ggui-ai/protocol';
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "all-ephemeral-substrate-fuses-like-none",
3
+ "description": "A server binding the WHOLE substrate out of stores that declare ephemeral durability keeps no durable record — a restart erases everything — so it MUST fuse exactly like a substrate-less one (#457). Binding is not durability: deriving the NOT_SUPPORTED/NOT_FOUND split from mere binding lets an operator with three in-memory stores promise restorability the deployment cannot deliver, and the promise breaks silently, on the first restart, for every locator at once.",
4
+ "server": { "durableSubstrate": "all-ephemeral", "liveChannel": true },
5
+ "caller": "other",
6
+ "seeds": [{ "kind": "committed-render", "session": "6b2f8d13-4a5c-4e97-8f21-9d0c3b7a5e14" }],
7
+ "reads": [
8
+ {
9
+ "as": "refused",
10
+ "locator": { "kind": "render", "session": "6b2f8d13-4a5c-4e97-8f21-9d0c3b7a5e14" },
11
+ "expect": {
12
+ "kind": "typed-error",
13
+ "jsonRpcCode": -32006,
14
+ "dataCode": "NOT_SUPPORTED"
15
+ }
16
+ },
17
+ {
18
+ "as": "miss",
19
+ "locator": { "kind": "render", "session": "0c9e4f72-8b1d-4a63-95e8-2f6a7d3c1b58" },
20
+ "expect": {
21
+ "kind": "typed-error",
22
+ "jsonRpcCode": -32006,
23
+ "dataCode": "NOT_SUPPORTED"
24
+ }
25
+ }
26
+ ],
27
+ "indistinguishable": ["refused", "miss"],
28
+ "disclosesNothing": ["6b2f8d13-4a5c-4e97-8f21-9d0c3b7a5e14"]
29
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "evicted-row-remints-from-the-durable-record",
3
+ "description": "The success half on the path the durable substrate exists for. The render row is gone, but the record naming what it was survives, the blueprint it names is stored, and the component body behind that blueprint is still there — so the read restores it and hands back real mount material. Without this case the catalog would grade a server that answers every evicted locator with a typed error as fully conformant, which is the direction a broken re-mint fails in.",
4
+ "server": { "durableSubstrate": "all", "liveChannel": true },
5
+ "caller": "owner",
6
+ "seeds": [
7
+ {
8
+ "kind": "identity-record",
9
+ "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
10
+ "key": "fedcba9876543210",
11
+ "blueprint": "named"
12
+ },
13
+ { "kind": "durable-blueprint", "componentRef": "present", "body": "stored" }
14
+ ],
15
+ "reads": [
16
+ {
17
+ "as": "reminted",
18
+ "locator": {
19
+ "kind": "render",
20
+ "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
21
+ "key": { "kind": "literal", "value": "fedcba9876543210" }
22
+ },
23
+ "expect": { "kind": "live-mount" }
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "half-wired-blueprints-only-substrate-fuses-like-none",
3
+ "description": "The other half-wired shape: blueprint and component storage bound, no record of what any render was. Without the record there is nothing to look a blueprint up BY, so this server restores nothing either and owes the same fused answer as one wired for nothing at all.",
4
+ "server": { "durableSubstrate": "blueprints-only", "liveChannel": true },
5
+ "caller": "other",
6
+ "seeds": [{ "kind": "committed-render", "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70" }],
7
+ "reads": [
8
+ {
9
+ "as": "refused",
10
+ "locator": { "kind": "render", "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70" },
11
+ "expect": {
12
+ "kind": "typed-error",
13
+ "jsonRpcCode": -32006,
14
+ "dataCode": "NOT_SUPPORTED"
15
+ }
16
+ },
17
+ {
18
+ "as": "miss",
19
+ "locator": { "kind": "render", "session": "8d5b0e41-2c96-4a7f-b3d8-1e07f9a2c645" },
20
+ "expect": {
21
+ "kind": "typed-error",
22
+ "jsonRpcCode": -32006,
23
+ "dataCode": "NOT_SUPPORTED"
24
+ }
25
+ }
26
+ ],
27
+ "indistinguishable": ["refused", "miss"],
28
+ "disclosesNothing": ["3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70"]
29
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "half-wired-identity-only-substrate-fuses-like-none",
3
+ "description": "A server holding the record store and nothing else can restore no render, so it is as substrate-less as one holding nothing. This is where the fusion is easiest to get wrong: the re-mint is consulted only when the row is absent, so a verdict derived from a partial substrate check answers NOT_FOUND for a row that exists and NOT_SUPPORTED for one that never did — the existence oracle, rebuilt out of a provisioning mistake.",
4
+ "server": { "durableSubstrate": "identity-only", "liveChannel": true },
5
+ "caller": "other",
6
+ "seeds": [{ "kind": "committed-render", "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70" }],
7
+ "reads": [
8
+ {
9
+ "as": "refused",
10
+ "locator": { "kind": "render", "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70" },
11
+ "expect": {
12
+ "kind": "typed-error",
13
+ "jsonRpcCode": -32006,
14
+ "dataCode": "NOT_SUPPORTED"
15
+ }
16
+ },
17
+ {
18
+ "as": "miss",
19
+ "locator": { "kind": "render", "session": "8d5b0e41-2c96-4a7f-b3d8-1e07f9a2c645" },
20
+ "expect": {
21
+ "kind": "typed-error",
22
+ "jsonRpcCode": -32006,
23
+ "dataCode": "NOT_SUPPORTED"
24
+ }
25
+ }
26
+ ],
27
+ "indistinguishable": ["refused", "miss"],
28
+ "disclosesNothing": ["3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70"]
29
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "live-row-read-returns-mount-material",
3
+ "description": "The success half of the invariant, on the simplest path: the render is still live and the caller owns it. The result's contents must declare a delivery channel — a component URL, a live channel carrying BOTH an endpoint and a token, or a server-emitted system card. Graded on the projected render meta, never on markup: what the shell looks like is a server's own presentation concern. Note the blueprintKey segment names nothing this server has ever stored, and the read succeeds anyway — that is deliberate, and it makes a second obligation graded here: when the row is present and readable, the ROW is what gets mounted and the supplied key is not consulted. A server that validated the key against its own records and refused the mismatch would fail this case, correctly, because a resume locator's key exists to resolve a render that is GONE.",
4
+ "server": { "durableSubstrate": "all", "liveChannel": true },
5
+ "caller": "owner",
6
+ "seeds": [{ "kind": "committed-render", "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70" }],
7
+ "reads": [
8
+ {
9
+ "as": "live",
10
+ "locator": {
11
+ "kind": "render",
12
+ "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
13
+ "key": { "kind": "literal", "value": "fedcba9876543210" }
14
+ },
15
+ "expect": { "kind": "live-mount" }
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "malformed-locator-stays-outside-the-typed-set",
3
+ "description": "A URI with an empty session segment names no render locator, so it is not a read of one and the four classifications do not apply. The graded law is exactly one negative: the error carries NO resource-read code on error.data.code. The NUMBER is not graded, not even negatively — MCP assigns the resource-missing number to a resources/read of a URI the server does not serve, so a framework mapping every unrecognized URI onto it is correct and must not fail here. The classification alone keeps the two apart: a genuine miss carries NOT_FOUND and the constant message, a malformed URI carries no classification at all.",
4
+ "server": { "durableSubstrate": "all", "liveChannel": true },
5
+ "caller": "owner",
6
+ "seeds": [],
7
+ "reads": [
8
+ {
9
+ "as": "empty-session",
10
+ "locator": { "kind": "raw", "uri": "ui://ggui/render/" },
11
+ "expect": { "kind": "outside-typed-set" }
12
+ },
13
+ {
14
+ "as": "empty-session-with-key",
15
+ "locator": { "kind": "raw", "uri": "ui://ggui/render//fedcba9876543210" },
16
+ "expect": { "kind": "outside-typed-set" }
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "read-miss-answers-not-found",
3
+ "description": "A read of a locator that never existed, on a server that keeps durable records and could have restored one, answers NOT_FOUND on -32002 with a body carrying no detail. -32002 is the number a missing resource already rides on, and for a locator keyed by session id a missing session and a missing resource are one condition. The absent detail is what keeps this answer identical to a refusal.",
4
+ "server": { "durableSubstrate": "all", "liveChannel": true },
5
+ "caller": "owner",
6
+ "seeds": [],
7
+ "reads": [
8
+ {
9
+ "as": "miss",
10
+ "locator": {
11
+ "kind": "render",
12
+ "session": "8d5b0e41-2c96-4a7f-b3d8-1e07f9a2c645",
13
+ "key": { "kind": "literal", "value": "fedcba9876543210" }
14
+ },
15
+ "expect": {
16
+ "kind": "typed-error",
17
+ "jsonRpcCode": -32002,
18
+ "dataCode": "NOT_FOUND",
19
+ "detailAbsent": true
20
+ }
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "read-on-a-substrate-less-server-answers-not-supported",
3
+ "description": "A server that keeps no durable record cannot restore an evicted render for anybody, so a read it cannot serve answers NOT_SUPPORTED on -32006. This is a property of the deployment, not of the locator — which is why the catalog does not grade WHEN the answer is produced. A substrate-less server may answer immediately, before any per-locator work.",
4
+ "server": { "durableSubstrate": "none", "liveChannel": true },
5
+ "caller": "owner",
6
+ "seeds": [],
7
+ "reads": [
8
+ {
9
+ "as": "evicted",
10
+ "locator": {
11
+ "kind": "render",
12
+ "session": "8d5b0e41-2c96-4a7f-b3d8-1e07f9a2c645",
13
+ "key": { "kind": "literal", "value": "fedcba9876543210" }
14
+ },
15
+ "expect": {
16
+ "kind": "typed-error",
17
+ "jsonRpcCode": -32006,
18
+ "dataCode": "NOT_SUPPORTED"
19
+ }
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "read-record-with-purged-blueprint-answers-blueprint-unresolvable",
3
+ "description": "A durable record survives its render and names a blueprint, but the blueprint is gone. The read answers BLUEPRINT_UNRESOLVABLE on -32006 — not an internal error. Nothing malfunctioned: the record kept its promise and the thing it pointed at was reaped. A host reading -32006 knows a retry of this locator cannot succeed, which -32603 would have told it the opposite of.",
4
+ "server": { "durableSubstrate": "all", "liveChannel": true },
5
+ "caller": "owner",
6
+ "seeds": [
7
+ {
8
+ "kind": "identity-record",
9
+ "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
10
+ "key": "fedcba9876543210",
11
+ "blueprint": "named"
12
+ }
13
+ ],
14
+ "reads": [
15
+ {
16
+ "as": "evicted",
17
+ "locator": {
18
+ "kind": "render",
19
+ "session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
20
+ "key": { "kind": "literal", "value": "fedcba9876543210" }
21
+ },
22
+ "expect": {
23
+ "kind": "typed-error",
24
+ "jsonRpcCode": -32006,
25
+ "dataCode": "BLUEPRINT_UNRESOLVABLE"
26
+ }
27
+ }
28
+ ]
29
+ }