@ggui-ai/protocol-conformance 0.7.0 → 0.9.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 +15 -12
- package/dist/binding-conformance/cases/bind-blueprint-no-contract-none.json +6 -0
- package/dist/binding-conformance/cases/bind-contract-without-tool-names-none.json +18 -0
- package/dist/binding-conformance/cases/bind-declared-gadget.json +13 -0
- package/dist/binding-conformance/cases/bind-declared-wins-over-contract.json +23 -0
- package/dist/binding-conformance/cases/bind-derived-caps-at-sixteen.json +54 -0
- package/dist/binding-conformance/cases/bind-derived-props-and-stream.json +36 -0
- package/dist/binding-conformance/cases/bind-derived-skips-invalid-names.json +40 -0
- package/dist/binding-conformance/cases/bind-derived-union-dedupes.json +38 -0
- package/dist/binding-conformance/cases/bind-gadget-undeclared-none.json +6 -0
- package/dist/binding-conformance/cases/bind-reject-charset.json +9 -0
- package/dist/binding-conformance/cases/bind-reject-duplicate-pair.json +12 -0
- package/dist/binding-conformance/cases/bind-reject-empty-list.json +9 -0
- package/dist/binding-conformance/cases/bind-reject-overlong-name.json +13 -0
- package/dist/binding-conformance/cases/bind-reject-server-charset.json +9 -0
- package/dist/binding-conformance/cases/bind-reject-too-many-entries.json +27 -0
- package/dist/binding-conformance/cases/filter-case-sensitive-server.json +7 -0
- package/dist/binding-conformance/cases/filter-case-sensitive-tool.json +7 -0
- package/dist/binding-conformance/cases/filter-none-passes-bound.json +7 -0
- package/dist/binding-conformance/cases/filter-none-passes-unbound.json +6 -0
- package/dist/binding-conformance/cases/filter-pair-bare-never-matches.json +7 -0
- package/dist/binding-conformance/cases/filter-pair-exact.json +10 -0
- package/dist/binding-conformance/cases/filter-pair-not-cross-entry.json +10 -0
- package/dist/binding-conformance/cases/filter-server-excludes-bare.json +7 -0
- package/dist/binding-conformance/cases/filter-server-match.json +7 -0
- package/dist/binding-conformance/cases/filter-tool-cross-server.json +7 -0
- package/dist/binding-conformance/cases/filter-tool-excludes-unbound.json +6 -0
- package/dist/binding-conformance/cases/filter-tool-matches-bare-entry.json +7 -0
- package/dist/binding-conformance/cases/filter-tool-no-match.json +7 -0
- package/dist/binding-conformance/index.d.ts +214 -0
- package/dist/binding-conformance/index.d.ts.map +1 -0
- package/dist/binding-conformance/index.js +153 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/resolution-conformance/cases/resolve-bundle-host-computed.json +3 -3
- package/dist/resolution-conformance/cases/resolve-default-host.json +2 -2
- package/dist/resolution-conformance/cases/resolve-loopback-host-http-scheme.json +2 -2
- package/dist/resource-read-conformance/cases/channel-less-read-mounts-inline.json +24 -0
- package/dist/resource-read-conformance/cases/pin-past-the-head-answers-not-found.json +29 -0
- package/dist/resource-read-conformance/cases/pinned-superseded-record-serves-its-reign-immutably.json +42 -0
- package/dist/resource-read-conformance/cases/read-without-a-delivery-channel-answers-not-mountable.json +15 -4
- package/dist/resource-read-conformance/index.d.ts +60 -4
- package/dist/resource-read-conformance/index.d.ts.map +1 -1
- package/dist/resource-read-conformance/index.js +144 -11
- package/package.json +8 -3
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binding-conformance catalog — MCP tool-binding resolution + search
|
|
3
|
+
* filter semantics (SPEC §7.7.4 — tool bindings & discovery).
|
|
4
|
+
*
|
|
5
|
+
* ## Why this is a pure-function catalog
|
|
6
|
+
*
|
|
7
|
+
* Two pure obligations connect a registry artifact to the MCP tools
|
|
8
|
+
* it renders:
|
|
9
|
+
*
|
|
10
|
+
* 1. RESOLUTION — given a manifest, which binding set is
|
|
11
|
+
* effective? A declared `mcpTools` list wins entirely; a
|
|
12
|
+
* blueprint without one derives bare tool names from its
|
|
13
|
+
* contract's lineage fields
|
|
14
|
+
* (`propsSpec.properties[*].sourceTool`,
|
|
15
|
+
* `streamSpec[*].source.tool`); a schema-invalid list rejects
|
|
16
|
+
* with the manifest-validation code. Bindings are search
|
|
17
|
+
* metadata only — they never enter contract canonicalization
|
|
18
|
+
* or any cache identity.
|
|
19
|
+
* 2. FILTERING — given an artifact's stored binding set and a
|
|
20
|
+
* search query's `tool` / `server` params, does the artifact
|
|
21
|
+
* match? Both dimensions are case-sensitive exact and must land
|
|
22
|
+
* on a single entry; a bare entry never satisfies a server
|
|
23
|
+
* dimension; no dimensions given passes everything.
|
|
24
|
+
*
|
|
25
|
+
* Both are deterministic functions — no render, transport, or wire
|
|
26
|
+
* frame — so they get the same treatment as the registry gate
|
|
27
|
+
* (`../registration-conformance`) and bundle-URL resolution
|
|
28
|
+
* (`../resolution-conformance`): catalogs of JSON cases graded
|
|
29
|
+
* against caller-supplied functions.
|
|
30
|
+
*
|
|
31
|
+
* ## Polyglot, decoupled
|
|
32
|
+
*
|
|
33
|
+
* Each case ships as raw JSON under `./cases/`.
|
|
34
|
+
* {@link runBindingResolutionCases} / {@link runBindingFilterCases}
|
|
35
|
+
* take the implementation as a callback — the kit never imports a
|
|
36
|
+
* concrete resolver or search filter, so adopters drive their OWN.
|
|
37
|
+
* The reference binding (`./binding-conformance.test.ts`) restates
|
|
38
|
+
* faithful minimal implementations in-test.
|
|
39
|
+
*/
|
|
40
|
+
import type { DataContract } from "@ggui-ai/protocol";
|
|
41
|
+
/**
|
|
42
|
+
* One MCP tool binding as authored on a manifest / stored on search
|
|
43
|
+
* metadata. `server` carries `serverInfo.name` semantics; a bare
|
|
44
|
+
* entry (no `server`) is server-agnostic.
|
|
45
|
+
*/
|
|
46
|
+
export interface McpToolBindingDecl {
|
|
47
|
+
readonly server?: string;
|
|
48
|
+
readonly tool: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The binding-relevant subset of an artifact manifest the resolver
|
|
52
|
+
* reads. Authored kit-local — decoupled from any shipping manifest
|
|
53
|
+
* schema on purpose; `contract` reuses the protocol's
|
|
54
|
+
* {@link DataContract} type the way `../registration-conformance`
|
|
55
|
+
* does.
|
|
56
|
+
*/
|
|
57
|
+
export interface BindingManifestEntry {
|
|
58
|
+
/** Which manifest kind the entry models. Only blueprints derive. */
|
|
59
|
+
readonly kind: "gadget" | "blueprint";
|
|
60
|
+
/** The `mcpTools` list as authored — possibly invalid. */
|
|
61
|
+
readonly mcpTools?: readonly McpToolBindingDecl[];
|
|
62
|
+
/** The blueprint's contract, when it has one. */
|
|
63
|
+
readonly contract?: DataContract;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The code a schema-invalid `mcpTools` list classifies into.
|
|
67
|
+
* Extensibly-closed `(string & {})` tail so a later protocol
|
|
68
|
+
* revision's code rides through without a kit bump — same
|
|
69
|
+
* discipline as `GadgetGateRejectCode` in
|
|
70
|
+
* `../registration-conformance`.
|
|
71
|
+
*/
|
|
72
|
+
export type BindingRejectCode = "manifest_invalid" | (string & {});
|
|
73
|
+
/**
|
|
74
|
+
* What resolving one manifest's bindings produces: accept with a
|
|
75
|
+
* `source` + `bindings` pair (declared or derived — present together
|
|
76
|
+
* or absent together), accept with NEITHER (the artifact has no
|
|
77
|
+
* bindings and is not findable by tool), or reject with the
|
|
78
|
+
* manifest-validation code.
|
|
79
|
+
*/
|
|
80
|
+
export type BindingResolutionOutcome = {
|
|
81
|
+
readonly outcome: "accept";
|
|
82
|
+
readonly source?: "declared" | "derived";
|
|
83
|
+
readonly bindings?: readonly McpToolBindingDecl[];
|
|
84
|
+
} | {
|
|
85
|
+
readonly outcome: "reject";
|
|
86
|
+
readonly code: BindingRejectCode;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* A search query's tool/server params as one filter input. Both
|
|
90
|
+
* optional — an absent dimension does not constrain. Matching is
|
|
91
|
+
* case-sensitive exact on both dimensions.
|
|
92
|
+
*/
|
|
93
|
+
export interface McpToolFilterDecl {
|
|
94
|
+
readonly tool?: string;
|
|
95
|
+
readonly server?: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* One binding-resolution case. Authored as JSON under `./cases/`,
|
|
99
|
+
* consumed via {@link bindingResolutionCases}, graded by
|
|
100
|
+
* {@link runBindingResolutionCases}.
|
|
101
|
+
*
|
|
102
|
+
* The shape IS the public API — additive changes only.
|
|
103
|
+
*/
|
|
104
|
+
export interface BindingResolutionCase {
|
|
105
|
+
/** Unique case name. MUST match the JSON filename without `.json`. */
|
|
106
|
+
readonly name: string;
|
|
107
|
+
/** Which binding obligation this case proves. */
|
|
108
|
+
readonly description: string;
|
|
109
|
+
/** The manifest subset fed to the resolver. */
|
|
110
|
+
readonly entry: BindingManifestEntry;
|
|
111
|
+
/**
|
|
112
|
+
* The outcome a spec-conformant resolver MUST produce. Binding
|
|
113
|
+
* lists are compared order-sensitively: declared bindings carry
|
|
114
|
+
* authored order; derived bindings carry first-appearance order
|
|
115
|
+
* (props in declaration order, then stream channels), duplicates
|
|
116
|
+
* collapsed to the first occurrence.
|
|
117
|
+
*/
|
|
118
|
+
readonly expect: BindingResolutionOutcome;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* One filter-semantics case. Authored as JSON under `./cases/`,
|
|
122
|
+
* consumed via {@link bindingFilterCases}, graded by
|
|
123
|
+
* {@link runBindingFilterCases}.
|
|
124
|
+
*
|
|
125
|
+
* The shape IS the public API — additive changes only.
|
|
126
|
+
*/
|
|
127
|
+
export interface BindingFilterCase {
|
|
128
|
+
/** Unique case name. MUST match the JSON filename without `.json`. */
|
|
129
|
+
readonly name: string;
|
|
130
|
+
/** Which filter obligation this case proves. */
|
|
131
|
+
readonly description: string;
|
|
132
|
+
/** The artifact's stored binding set — absent models an unbound artifact. */
|
|
133
|
+
readonly bindings?: readonly McpToolBindingDecl[];
|
|
134
|
+
/** The query's tool/server params — `{}` models neither given. */
|
|
135
|
+
readonly filters: McpToolFilterDecl;
|
|
136
|
+
/** Whether a spec-conformant filter matches the artifact. */
|
|
137
|
+
readonly expect: boolean;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Every binding-resolution case the kit ships, in deterministic
|
|
141
|
+
* order — declared first, then derived, then the no-binding edges,
|
|
142
|
+
* then the schema rejects.
|
|
143
|
+
*/
|
|
144
|
+
export declare const bindingResolutionCases: readonly BindingResolutionCase[];
|
|
145
|
+
/**
|
|
146
|
+
* Every filter-semantics case the kit ships, in deterministic
|
|
147
|
+
* order — tool-dimension, server-dimension, pair, no-filter
|
|
148
|
+
* passthrough, then case-sensitivity.
|
|
149
|
+
*/
|
|
150
|
+
export declare const bindingFilterCases: readonly BindingFilterCase[];
|
|
151
|
+
/** One resolution case the resolver under test graded wrong. */
|
|
152
|
+
export interface BindingResolutionMismatch {
|
|
153
|
+
readonly name: string;
|
|
154
|
+
/** The outcome the catalog says a conformant resolver MUST produce. */
|
|
155
|
+
readonly expected: BindingResolutionOutcome;
|
|
156
|
+
/** The outcome the resolver under test actually produced. */
|
|
157
|
+
readonly actual: BindingResolutionOutcome;
|
|
158
|
+
}
|
|
159
|
+
/** Outcome of grading a resolver against the resolution catalog. */
|
|
160
|
+
export interface BindingResolutionResult {
|
|
161
|
+
/** Names of cases the resolver graded correctly. */
|
|
162
|
+
readonly passed: readonly string[];
|
|
163
|
+
/** Cases the resolver graded wrong — empty iff fully conformant. */
|
|
164
|
+
readonly failed: readonly BindingResolutionMismatch[];
|
|
165
|
+
}
|
|
166
|
+
/** One filter case the predicate under test graded wrong. */
|
|
167
|
+
export interface BindingFilterMismatch {
|
|
168
|
+
readonly name: string;
|
|
169
|
+
/** Whether the catalog says a conformant filter matches. */
|
|
170
|
+
readonly expected: boolean;
|
|
171
|
+
/** What the predicate under test actually returned. */
|
|
172
|
+
readonly actual: boolean;
|
|
173
|
+
}
|
|
174
|
+
/** Outcome of grading a filter predicate against the filter catalog. */
|
|
175
|
+
export interface BindingFilterResult {
|
|
176
|
+
/** Names of cases the predicate graded correctly. */
|
|
177
|
+
readonly passed: readonly string[];
|
|
178
|
+
/** Cases the predicate graded wrong — empty iff fully conformant. */
|
|
179
|
+
readonly failed: readonly BindingFilterMismatch[];
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Grade a binding resolver against the resolution catalog.
|
|
183
|
+
*
|
|
184
|
+
* `resolve` MUST be a pure computation of `(entry) →
|
|
185
|
+
* BindingResolutionOutcome` — schema validation of a declared list
|
|
186
|
+
* plus declared-wins/derivation precedence, composed however the
|
|
187
|
+
* implementation likes. The kit deliberately does NOT import a
|
|
188
|
+
* concrete resolver — adopters pass their own (the reference
|
|
189
|
+
* binding uses a faithful in-test implementation; see
|
|
190
|
+
* `./binding-conformance.test.ts`). A conformant resolver produces
|
|
191
|
+
* an empty `failed` array.
|
|
192
|
+
*
|
|
193
|
+
* `resolve` is invoked exactly once per case, in
|
|
194
|
+
* {@link bindingResolutionCases} order; it MUST be pure and MUST
|
|
195
|
+
* NOT throw (a throwing resolver is itself non-conformant — wrap it
|
|
196
|
+
* so a classified rejection becomes a returned outcome).
|
|
197
|
+
*/
|
|
198
|
+
export declare function runBindingResolutionCases(resolve: (entry: BindingManifestEntry) => BindingResolutionOutcome): BindingResolutionResult;
|
|
199
|
+
/**
|
|
200
|
+
* Grade a search filter predicate against the filter catalog.
|
|
201
|
+
*
|
|
202
|
+
* `matches` MUST be a pure computation of `(bindings, filters) →
|
|
203
|
+
* boolean` — the same predicate every storage implementation shares
|
|
204
|
+
* so `tool=` / `server=` behave identically everywhere. The kit
|
|
205
|
+
* deliberately does NOT import a concrete predicate — adopters pass
|
|
206
|
+
* their own. A conformant predicate produces an empty `failed`
|
|
207
|
+
* array.
|
|
208
|
+
*
|
|
209
|
+
* `matches` is invoked exactly once per case, in
|
|
210
|
+
* {@link bindingFilterCases} order; it MUST be pure and MUST NOT
|
|
211
|
+
* throw.
|
|
212
|
+
*/
|
|
213
|
+
export declare function runBindingFilterCases(matches: (bindings: readonly McpToolBindingDecl[] | undefined, filters: McpToolFilterDecl) => boolean): BindingFilterResult;
|
|
214
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/binding-conformance/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import bindBlueprintNoContractNone from "./cases/bind-blueprint-no-contract-none.json" with { type: "json" };
|
|
2
|
+
import bindContractWithoutToolNamesNone from "./cases/bind-contract-without-tool-names-none.json" with { type: "json" };
|
|
3
|
+
import bindDeclaredGadget from "./cases/bind-declared-gadget.json" with { type: "json" };
|
|
4
|
+
import bindDeclaredWinsOverContract from "./cases/bind-declared-wins-over-contract.json" with { type: "json" };
|
|
5
|
+
import bindDerivedCapsAtSixteen from "./cases/bind-derived-caps-at-sixteen.json" with { type: "json" };
|
|
6
|
+
import bindDerivedPropsAndStream from "./cases/bind-derived-props-and-stream.json" with { type: "json" };
|
|
7
|
+
import bindDerivedSkipsInvalidNames from "./cases/bind-derived-skips-invalid-names.json" with { type: "json" };
|
|
8
|
+
import bindDerivedUnionDedupes from "./cases/bind-derived-union-dedupes.json" with { type: "json" };
|
|
9
|
+
import bindGadgetUndeclaredNone from "./cases/bind-gadget-undeclared-none.json" with { type: "json" };
|
|
10
|
+
import bindRejectCharset from "./cases/bind-reject-charset.json" with { type: "json" };
|
|
11
|
+
import bindRejectDuplicatePair from "./cases/bind-reject-duplicate-pair.json" with { type: "json" };
|
|
12
|
+
import bindRejectEmptyList from "./cases/bind-reject-empty-list.json" with { type: "json" };
|
|
13
|
+
import bindRejectOverlongName from "./cases/bind-reject-overlong-name.json" with { type: "json" };
|
|
14
|
+
import bindRejectServerCharset from "./cases/bind-reject-server-charset.json" with { type: "json" };
|
|
15
|
+
import bindRejectTooManyEntries from "./cases/bind-reject-too-many-entries.json" with { type: "json" };
|
|
16
|
+
import filterCaseSensitiveServer from "./cases/filter-case-sensitive-server.json" with { type: "json" };
|
|
17
|
+
import filterCaseSensitiveTool from "./cases/filter-case-sensitive-tool.json" with { type: "json" };
|
|
18
|
+
import filterNonePassesBound from "./cases/filter-none-passes-bound.json" with { type: "json" };
|
|
19
|
+
import filterNonePassesUnbound from "./cases/filter-none-passes-unbound.json" with { type: "json" };
|
|
20
|
+
import filterPairBareNeverMatches from "./cases/filter-pair-bare-never-matches.json" with { type: "json" };
|
|
21
|
+
import filterPairExact from "./cases/filter-pair-exact.json" with { type: "json" };
|
|
22
|
+
import filterPairNotCrossEntry from "./cases/filter-pair-not-cross-entry.json" with { type: "json" };
|
|
23
|
+
import filterServerExcludesBare from "./cases/filter-server-excludes-bare.json" with { type: "json" };
|
|
24
|
+
import filterServerMatch from "./cases/filter-server-match.json" with { type: "json" };
|
|
25
|
+
import filterToolCrossServer from "./cases/filter-tool-cross-server.json" with { type: "json" };
|
|
26
|
+
import filterToolExcludesUnbound from "./cases/filter-tool-excludes-unbound.json" with { type: "json" };
|
|
27
|
+
import filterToolMatchesBareEntry from "./cases/filter-tool-matches-bare-entry.json" with { type: "json" };
|
|
28
|
+
import filterToolNoMatch from "./cases/filter-tool-no-match.json" with { type: "json" };
|
|
29
|
+
/**
|
|
30
|
+
* Every binding-resolution case the kit ships, in deterministic
|
|
31
|
+
* order — declared first, then derived, then the no-binding edges,
|
|
32
|
+
* then the schema rejects.
|
|
33
|
+
*/
|
|
34
|
+
export const bindingResolutionCases = [
|
|
35
|
+
bindDeclaredGadget,
|
|
36
|
+
bindDeclaredWinsOverContract,
|
|
37
|
+
bindDerivedPropsAndStream,
|
|
38
|
+
bindDerivedUnionDedupes,
|
|
39
|
+
bindDerivedSkipsInvalidNames,
|
|
40
|
+
bindDerivedCapsAtSixteen,
|
|
41
|
+
bindBlueprintNoContractNone,
|
|
42
|
+
bindContractWithoutToolNamesNone,
|
|
43
|
+
bindGadgetUndeclaredNone,
|
|
44
|
+
bindRejectCharset,
|
|
45
|
+
bindRejectServerCharset,
|
|
46
|
+
bindRejectDuplicatePair,
|
|
47
|
+
bindRejectOverlongName,
|
|
48
|
+
bindRejectTooManyEntries,
|
|
49
|
+
bindRejectEmptyList,
|
|
50
|
+
];
|
|
51
|
+
/**
|
|
52
|
+
* Every filter-semantics case the kit ships, in deterministic
|
|
53
|
+
* order — tool-dimension, server-dimension, pair, no-filter
|
|
54
|
+
* passthrough, then case-sensitivity.
|
|
55
|
+
*/
|
|
56
|
+
export const bindingFilterCases = [
|
|
57
|
+
filterToolCrossServer,
|
|
58
|
+
filterToolMatchesBareEntry,
|
|
59
|
+
filterToolNoMatch,
|
|
60
|
+
filterServerMatch,
|
|
61
|
+
filterServerExcludesBare,
|
|
62
|
+
filterPairExact,
|
|
63
|
+
filterPairNotCrossEntry,
|
|
64
|
+
filterPairBareNeverMatches,
|
|
65
|
+
filterNonePassesBound,
|
|
66
|
+
filterNonePassesUnbound,
|
|
67
|
+
filterToolExcludesUnbound,
|
|
68
|
+
filterCaseSensitiveTool,
|
|
69
|
+
filterCaseSensitiveServer,
|
|
70
|
+
];
|
|
71
|
+
function bindingListsEqual(a, b) {
|
|
72
|
+
if (a === undefined || b === undefined)
|
|
73
|
+
return a === b;
|
|
74
|
+
if (a.length !== b.length)
|
|
75
|
+
return false;
|
|
76
|
+
return a.every((entry, i) => {
|
|
77
|
+
const other = b[i];
|
|
78
|
+
return other !== undefined && entry.tool === other.tool && entry.server === other.server;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function resolutionOutcomeEquals(a, b) {
|
|
82
|
+
if (a.outcome === "reject" || b.outcome === "reject") {
|
|
83
|
+
return a.outcome === "reject" && b.outcome === "reject" && a.code === b.code;
|
|
84
|
+
}
|
|
85
|
+
return a.source === b.source && bindingListsEqual(a.bindings, b.bindings);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Grade a binding resolver against the resolution catalog.
|
|
89
|
+
*
|
|
90
|
+
* `resolve` MUST be a pure computation of `(entry) →
|
|
91
|
+
* BindingResolutionOutcome` — schema validation of a declared list
|
|
92
|
+
* plus declared-wins/derivation precedence, composed however the
|
|
93
|
+
* implementation likes. The kit deliberately does NOT import a
|
|
94
|
+
* concrete resolver — adopters pass their own (the reference
|
|
95
|
+
* binding uses a faithful in-test implementation; see
|
|
96
|
+
* `./binding-conformance.test.ts`). A conformant resolver produces
|
|
97
|
+
* an empty `failed` array.
|
|
98
|
+
*
|
|
99
|
+
* `resolve` is invoked exactly once per case, in
|
|
100
|
+
* {@link bindingResolutionCases} order; it MUST be pure and MUST
|
|
101
|
+
* NOT throw (a throwing resolver is itself non-conformant — wrap it
|
|
102
|
+
* so a classified rejection becomes a returned outcome).
|
|
103
|
+
*/
|
|
104
|
+
export function runBindingResolutionCases(resolve) {
|
|
105
|
+
const passed = [];
|
|
106
|
+
const failed = [];
|
|
107
|
+
for (const testCase of bindingResolutionCases) {
|
|
108
|
+
const actual = resolve(testCase.entry);
|
|
109
|
+
if (resolutionOutcomeEquals(actual, testCase.expect)) {
|
|
110
|
+
passed.push(testCase.name);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
failed.push({
|
|
114
|
+
name: testCase.name,
|
|
115
|
+
expected: testCase.expect,
|
|
116
|
+
actual,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { passed, failed };
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Grade a search filter predicate against the filter catalog.
|
|
124
|
+
*
|
|
125
|
+
* `matches` MUST be a pure computation of `(bindings, filters) →
|
|
126
|
+
* boolean` — the same predicate every storage implementation shares
|
|
127
|
+
* so `tool=` / `server=` behave identically everywhere. The kit
|
|
128
|
+
* deliberately does NOT import a concrete predicate — adopters pass
|
|
129
|
+
* their own. A conformant predicate produces an empty `failed`
|
|
130
|
+
* array.
|
|
131
|
+
*
|
|
132
|
+
* `matches` is invoked exactly once per case, in
|
|
133
|
+
* {@link bindingFilterCases} order; it MUST be pure and MUST NOT
|
|
134
|
+
* throw.
|
|
135
|
+
*/
|
|
136
|
+
export function runBindingFilterCases(matches) {
|
|
137
|
+
const passed = [];
|
|
138
|
+
const failed = [];
|
|
139
|
+
for (const testCase of bindingFilterCases) {
|
|
140
|
+
const actual = matches(testCase.bindings, testCase.filters);
|
|
141
|
+
if (actual === testCase.expect) {
|
|
142
|
+
passed.push(testCase.name);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
failed.push({
|
|
146
|
+
name: testCase.name,
|
|
147
|
+
expected: testCase.expect,
|
|
148
|
+
actual,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return { passed, failed };
|
|
153
|
+
}
|
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 { bindingFilterCases, bindingResolutionCases, runBindingFilterCases, runBindingResolutionCases, } from './binding-conformance/index.js';
|
|
24
|
+
export type { BindingFilterCase, BindingFilterMismatch, BindingFilterResult, BindingManifestEntry, BindingRejectCode, BindingResolutionCase, BindingResolutionMismatch, BindingResolutionOutcome, BindingResolutionResult, McpToolBindingDecl, McpToolFilterDecl, } from './binding-conformance/index.js';
|
|
23
25
|
export { declaresDeliveryChannel, renderLocatorUri, resourceReadCases, runResourceReadConformance, GGUI_RENDER_RESOURCE_URI, } from './resource-read-conformance/index.js';
|
|
24
26
|
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';
|
|
25
27
|
export { loadFixture, listFixtures, loadAllFixtures } from './loader.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;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;
|
|
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;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
|
@@ -30,6 +30,11 @@ 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
|
+
// Binding-conformance — MCP tool-binding resolution (declared wins,
|
|
34
|
+
// contract-lineage derivation, schema-invalid rejects) + the search
|
|
35
|
+
// filter semantics (`tool=` / `server=` case-sensitive exact,
|
|
36
|
+
// AND-composed per entry).
|
|
37
|
+
export { bindingFilterCases, bindingResolutionCases, runBindingFilterCases, runBindingResolutionCases, } from './binding-conformance/index.js';
|
|
33
38
|
// Resource-read conformance — the kit's first MCP-binding driver, and
|
|
34
39
|
// it binds `resources/read` ONLY (`tools/call` has none). A read of a
|
|
35
40
|
// render locator has exactly two exits: a result whose contents
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "resolve-bundle-host-computed",
|
|
3
|
-
"description": "A descriptor with a hostname-only `bundleHost` (no scheme, no path) and no explicit `bundleUrl`. The resolver MUST compute both the bundle and style URLs as `https://<bundleHost>/bundles/<package>/<version>/<file>` — the
|
|
3
|
+
"description": "A descriptor with a hostname-only `bundleHost` (no scheme, no path) and no explicit `bundleUrl`. The resolver MUST compute both the bundle and style URLs as `https://<bundleHost>/bundles/public/<package>/<version>/<file>` — the PUBLIC prefix of `@ggui-ai/registry-core`'s visibility-split `BundleStorage` layout (H1). bundleHost-tier composition is anonymous render-time fetching, which only the public prefix serves; private artifacts reach the iframe via an explicit presigned `bundleUrl`, never via host composition. Keeping the shapes aligned means a bundle published to one registry resolves cleanly from the other.",
|
|
4
4
|
"entry": {
|
|
5
5
|
"package": "@acme/leaflet-wrapper",
|
|
6
6
|
"version": "2.3.1",
|
|
7
7
|
"bundleHost": "cdn.example.com"
|
|
8
8
|
},
|
|
9
9
|
"expect": {
|
|
10
|
-
"bundleUrl": "https://cdn.example.com/bundles/@acme/leaflet-wrapper/2.3.1/bundle.js",
|
|
11
|
-
"styleUrl": "https://cdn.example.com/bundles/@acme/leaflet-wrapper/2.3.1/style.css"
|
|
10
|
+
"bundleUrl": "https://cdn.example.com/bundles/public/@acme/leaflet-wrapper/2.3.1/bundle.js",
|
|
11
|
+
"styleUrl": "https://cdn.example.com/bundles/public/@acme/leaflet-wrapper/2.3.1/style.css"
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"version": "2.3.1"
|
|
7
7
|
},
|
|
8
8
|
"expect": {
|
|
9
|
-
"bundleUrl": "https://registry.ggui.ai/bundles/@acme/leaflet-wrapper/2.3.1/bundle.js",
|
|
10
|
-
"styleUrl": "https://registry.ggui.ai/bundles/@acme/leaflet-wrapper/2.3.1/style.css"
|
|
9
|
+
"bundleUrl": "https://registry.ggui.ai/bundles/public/@acme/leaflet-wrapper/2.3.1/bundle.js",
|
|
10
|
+
"styleUrl": "https://registry.ggui.ai/bundles/public/@acme/leaflet-wrapper/2.3.1/style.css"
|
|
11
11
|
}
|
|
12
12
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"bundleHost": "localhost:4873"
|
|
8
8
|
},
|
|
9
9
|
"expect": {
|
|
10
|
-
"bundleUrl": "http://localhost:4873/bundles/@acme/leaflet-wrapper/2.3.1/bundle.js",
|
|
11
|
-
"styleUrl": "http://localhost:4873/bundles/@acme/leaflet-wrapper/2.3.1/style.css"
|
|
10
|
+
"bundleUrl": "http://localhost:4873/bundles/public/@acme/leaflet-wrapper/2.3.1/bundle.js",
|
|
11
|
+
"styleUrl": "http://localhost:4873/bundles/public/@acme/leaflet-wrapper/2.3.1/style.css"
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "channel-less-read-mounts-inline",
|
|
3
|
+
"description": "The inline static channel makes a channel-less server mountable: no static component URL is wired, no live channel is wired, but the committed component fits the inline codeB64 cap — so the read returns a shell whose projected render meta declares the inline channel, not a typed failure. This is the success half of the size split whose failure half is read-without-a-delivery-channel-answers-not-mountable: the SAME server wiring answers by component size class, which is what makes the inline channel a real delivery channel rather than a serving detail.",
|
|
4
|
+
"server": { "durableSubstrate": "all" },
|
|
5
|
+
"caller": "owner",
|
|
6
|
+
"seeds": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "committed-render",
|
|
9
|
+
"session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
|
|
10
|
+
"size": "under-inline-cap"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"reads": [
|
|
14
|
+
{
|
|
15
|
+
"as": "inline",
|
|
16
|
+
"locator": {
|
|
17
|
+
"kind": "render",
|
|
18
|
+
"session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
|
|
19
|
+
"key": { "kind": "literal", "value": "fedcba9876543210" }
|
|
20
|
+
},
|
|
21
|
+
"expect": { "kind": "live-mount" }
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pin-past-the-head-answers-not-found",
|
|
3
|
+
"description": "Epoch history (#483, SPEC §7.1.2.2): a pinned locator whose epoch exceeds the session's head names a record that does not exist — the same NOT_FOUND on -32002, detail-free, that a missing session answers. Nothing about the head's value may leak through the error shape: a caller must not be able to binary-search the history depth off error variance.",
|
|
4
|
+
"server": { "durableSubstrate": "all", "liveChannel": true },
|
|
5
|
+
"caller": "owner",
|
|
6
|
+
"seeds": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "epoch-history",
|
|
9
|
+
"session": "7a4d9c15-8e2f-4b06-a3c9-5d1f8e0b2c47",
|
|
10
|
+
"records": [{ "props": { "count": 1 } }, { "props": { "count": 5 } }]
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"reads": [
|
|
14
|
+
{
|
|
15
|
+
"as": "past-head",
|
|
16
|
+
"locator": {
|
|
17
|
+
"kind": "render",
|
|
18
|
+
"session": "7a4d9c15-8e2f-4b06-a3c9-5d1f8e0b2c47",
|
|
19
|
+
"epoch": 7
|
|
20
|
+
},
|
|
21
|
+
"expect": {
|
|
22
|
+
"kind": "typed-error",
|
|
23
|
+
"jsonRpcCode": -32002,
|
|
24
|
+
"dataCode": "NOT_FOUND",
|
|
25
|
+
"detailAbsent": true
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pinned-superseded-record-serves-its-reign-immutably",
|
|
3
|
+
"description": "Epoch history (#483, SPEC §7.1.2.2): a session whose head advanced to epoch 1 still serves its superseded epoch-0 record on the pinned locator — with the props of ITS reign (the amended mint state), never the successor's — and serves them byte-identically on a second read. The bare locator meanwhile tracks the live head. Immutability is graded on propsJson only: live-channel tokens on the same meta legitimately differ per read; the RECORD is what may not.",
|
|
4
|
+
"server": { "durableSubstrate": "all", "liveChannel": true },
|
|
5
|
+
"caller": "owner",
|
|
6
|
+
"seeds": [
|
|
7
|
+
{
|
|
8
|
+
"kind": "epoch-history",
|
|
9
|
+
"session": "5e2c7a90-4f1b-4c3d-8a67-9b0e2d4f6a81",
|
|
10
|
+
"records": [{ "props": { "count": 1 } }, { "props": { "count": 5 } }]
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"reads": [
|
|
14
|
+
{
|
|
15
|
+
"as": "pin0-first",
|
|
16
|
+
"locator": {
|
|
17
|
+
"kind": "render",
|
|
18
|
+
"session": "5e2c7a90-4f1b-4c3d-8a67-9b0e2d4f6a81",
|
|
19
|
+
"epoch": 0
|
|
20
|
+
},
|
|
21
|
+
"expect": { "kind": "pinned-mount", "propsInclude": { "count": 1 } }
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"as": "pin0-second",
|
|
25
|
+
"locator": {
|
|
26
|
+
"kind": "render",
|
|
27
|
+
"session": "5e2c7a90-4f1b-4c3d-8a67-9b0e2d4f6a81",
|
|
28
|
+
"epoch": 0
|
|
29
|
+
},
|
|
30
|
+
"expect": { "kind": "pinned-mount", "propsInclude": { "count": 1 } }
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"as": "head",
|
|
34
|
+
"locator": {
|
|
35
|
+
"kind": "render",
|
|
36
|
+
"session": "5e2c7a90-4f1b-4c3d-8a67-9b0e2d4f6a81"
|
|
37
|
+
},
|
|
38
|
+
"expect": { "kind": "pinned-mount", "propsInclude": { "count": 5 } }
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"identicalMounts": ["pin0-first", "pin0-second"]
|
|
42
|
+
}
|
|
@@ -1,16 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "read-without-a-delivery-channel-answers-not-mountable",
|
|
3
|
-
"description": "The locator resolves and the caller owns it, but the server wires no way to deliver
|
|
4
|
-
"server": {
|
|
3
|
+
"description": "The locator resolves and the caller owns it, but the server wires no way to deliver the component \u2014 neither a static component URL nor a live channel, and the committed component is too large for the inline codeB64 channel (an under-cap component would mount inline; that arm has its own case). The read answers NOT_MOUNTABLE on -32006 rather than handing back a shell with nothing to mount. This is the branch that proves the invariant is about the RESULT, not the lookup: the lookup succeeded.",
|
|
4
|
+
"server": {
|
|
5
|
+
"durableSubstrate": "all"
|
|
6
|
+
},
|
|
5
7
|
"caller": "owner",
|
|
6
|
-
"seeds": [
|
|
8
|
+
"seeds": [
|
|
9
|
+
{
|
|
10
|
+
"kind": "committed-render",
|
|
11
|
+
"session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
|
|
12
|
+
"size": "over-inline-cap"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
7
15
|
"reads": [
|
|
8
16
|
{
|
|
9
17
|
"as": "resolvable",
|
|
10
18
|
"locator": {
|
|
11
19
|
"kind": "render",
|
|
12
20
|
"session": "3f1a9c22-7b4e-4d18-9a05-6c2e8b1d4f70",
|
|
13
|
-
"key": {
|
|
21
|
+
"key": {
|
|
22
|
+
"kind": "literal",
|
|
23
|
+
"value": "fedcba9876543210"
|
|
24
|
+
}
|
|
14
25
|
},
|
|
15
26
|
"expect": {
|
|
16
27
|
"kind": "typed-error",
|