@ggui-ai/protocol-conformance 0.8.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.
@@ -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
+ }
@@ -98,6 +98,19 @@ export type ResourceReadSeed = {
98
98
  */
99
99
  readonly kind: 'registered-blueprint';
100
100
  readonly as: string;
101
+ } | {
102
+ /**
103
+ * A session with epoch HISTORY (#483): `records[0]` is the mint
104
+ * (epoch 0), each later entry one `ggui_update` (epoch N). The
105
+ * driver seeds the ledger so every superseded record is
106
+ * reconstructable (an in-reign props event per record) and the
107
+ * row carries the head epoch.
108
+ */
109
+ readonly kind: 'epoch-history';
110
+ readonly session: string;
111
+ readonly records: ReadonlyArray<{
112
+ readonly props: Readonly<Record<string, unknown>>;
113
+ }>;
101
114
  };
102
115
  /**
103
116
  * The blueprint-key segment of a locator.
@@ -127,6 +140,14 @@ export type ResourceReadLocator = {
127
140
  readonly kind: 'render';
128
141
  readonly session: string;
129
142
  readonly key?: ResourceReadKey;
143
+ /**
144
+ * Epoch pin (#483, SPEC §7.1.2.2): present ⇒ the kit reads the
145
+ * PINNED history record (`…#N`); absent ⇒ the live head. The
146
+ * fragment encoding is pinned HERE deliberately — the kit is
147
+ * the arbiter of the wire grammar, so an encoding change is a
148
+ * conscious, visible kit change.
149
+ */
150
+ readonly epoch?: number;
130
151
  } | {
131
152
  readonly kind: 'raw';
132
153
  readonly uri: string;
@@ -169,6 +190,15 @@ export type ResourceReadExpectation = {
169
190
  } | {
170
191
  /** A result whose contents declare a delivery channel. */
171
192
  readonly kind: 'live-mount';
193
+ } | {
194
+ /**
195
+ * A mount serving a HISTORY record (#483): still a real mount
196
+ * (delivery channel required), graded additionally on its
197
+ * props — every `propsInclude` entry must appear in the parsed
198
+ * `propsJson` with the authored value.
199
+ */
200
+ readonly kind: 'pinned-mount';
201
+ readonly propsInclude?: Readonly<Record<string, unknown>>;
172
202
  };
173
203
  /** One read within a case. */
174
204
  export interface ResourceReadProbe {
@@ -209,6 +239,14 @@ export interface ResourceReadConformanceCase {
209
239
  * not excused here.
210
240
  */
211
241
  readonly indistinguishable?: readonly string[];
242
+ /**
243
+ * Probe names whose MOUNT outcomes must carry byte-identical
244
+ * `propsJson` (#483 — pinned-record immutability). Deliberately
245
+ * compares ONLY the props: live-channel tokens and expiries on the
246
+ * same meta legitimately differ per read; the RECORD is what may
247
+ * not.
248
+ */
249
+ readonly identicalMounts?: readonly string[];
212
250
  /**
213
251
  * Literal substrings that MUST NOT appear anywhere in any error frame
214
252
  * this case produces. The values a refusal would leak if a diagnostic
@@ -244,6 +282,12 @@ export interface ResourceReadRenderMeta {
244
282
  readonly wsToken?: string;
245
283
  /** A server-emitted system card, which carries its own content. */
246
284
  readonly kind?: string;
285
+ /**
286
+ * The mount's props as a JSON string (#483). Graded by the
287
+ * `pinned-mount` expectation and the `identicalMounts` group;
288
+ * ungraded elsewhere.
289
+ */
290
+ readonly propsJson?: string;
247
291
  }
248
292
  /** The raw JSON-RPC error body a server put on the wire. */
249
293
  export interface JsonRpcErrorFrame {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resource-read-conformance/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resource-read-conformance/index.ts"],"names":[],"mappings":"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"}
@@ -111,6 +111,8 @@ import malformedLocator from './cases/malformed-locator-stays-outside-the-typed-
111
111
  import liveRowMount from './cases/live-row-read-returns-mount-material.json' with { type: 'json' };
112
112
  import remintMount from './cases/evicted-row-remints-from-the-durable-record.json' with { type: 'json' };
113
113
  import inlineMount from './cases/channel-less-read-mounts-inline.json' with { type: 'json' };
114
+ import pinnedImmutable from './cases/pinned-superseded-record-serves-its-reign-immutably.json' with { type: 'json' };
115
+ import pinPastHead from './cases/pin-past-the-head-answers-not-found.json' with { type: 'json' };
114
116
  // =============================================================================
115
117
  // Catalog
116
118
  // =============================================================================
@@ -134,6 +136,8 @@ export const resourceReadCases = [
134
136
  liveRowMount,
135
137
  remintMount,
136
138
  inlineMount,
139
+ pinnedImmutable,
140
+ pinPastHead,
137
141
  ];
138
142
  // =============================================================================
139
143
  // Locator grammar + the mount predicate
@@ -148,17 +152,18 @@ export const GGUI_RENDER_RESOURCE_URI = 'ui://ggui/render';
148
152
  export function renderLocatorUri(locator, registeredKeys = {}) {
149
153
  if (locator.kind === 'raw')
150
154
  return locator.uri;
155
+ const pin = locator.epoch === undefined ? '' : `#${locator.epoch}`;
151
156
  const base = `${GGUI_RENDER_RESOURCE_URI}/${locator.session}`;
152
157
  const keyRef = locator.key;
153
158
  if (keyRef === undefined)
154
- return base;
159
+ return `${base}${pin}`;
155
160
  if (keyRef.kind === 'literal')
156
- return `${base}/${keyRef.value}`;
161
+ return `${base}/${keyRef.value}${pin}`;
157
162
  const key = registeredKeys[keyRef.seed];
158
163
  if (key === undefined) {
159
164
  throw new Error(`no key was reported for registry seed '${keyRef.seed}'`);
160
165
  }
161
- return `${base}/${key}`;
166
+ return `${base}/${key}${pin}`;
162
167
  }
163
168
  /**
164
169
  * Does this render meta carry enough to paint something? One of the
@@ -289,6 +294,9 @@ async function gradeCase(testCase, prepared) {
289
294
  const fusion = gradeIndistinguishable(testCase, outcomes);
290
295
  if (fusion !== null)
291
296
  return { kind: 'fail', failure: fusion };
297
+ const pinned = gradeIdenticalMounts(testCase, outcomes);
298
+ if (pinned !== null)
299
+ return { kind: 'fail', failure: pinned };
292
300
  return { kind: 'pass' };
293
301
  }
294
302
  function gradeProbe(testCase, probe, uri, outcome) {
@@ -309,6 +317,38 @@ function gradeProbe(testCase, probe, uri, outcome) {
309
317
  }
310
318
  return null;
311
319
  }
320
+ if (probe.expect.kind === 'pinned-mount') {
321
+ if (outcome.kind !== 'mount') {
322
+ return fail('a pinned history record within the servable window mounts (SPEC §7.1.2.2)', 'a result whose contents declare a delivery channel and carry the record props', `reading '${uri}' failed, but this pinned record resolves on this server.`);
323
+ }
324
+ if (!declaresDeliveryChannel(outcome.renderMeta)) {
325
+ return fail('a pinned mount is still a mount', 'render meta declaring a delivery channel', `reading '${uri}' succeeded with contents that declare no delivery channel.`);
326
+ }
327
+ const include = probe.expect.propsInclude;
328
+ if (include !== undefined) {
329
+ const raw = outcome.renderMeta.propsJson;
330
+ let parsed = null;
331
+ if (typeof raw === 'string') {
332
+ try {
333
+ const candidate = JSON.parse(raw);
334
+ if (isRecord(candidate))
335
+ parsed = candidate;
336
+ }
337
+ catch {
338
+ parsed = null;
339
+ }
340
+ }
341
+ if (parsed === null) {
342
+ return fail('a pinned mount carries its record props (SPEC §7.1.2.2)', 'parseable propsJson on the render meta', `reading '${uri}' mounted, but its render meta carries no parseable propsJson to grade the record against.`);
343
+ }
344
+ for (const [key, expected] of Object.entries(include)) {
345
+ if (JSON.stringify(parsed[key]) !== JSON.stringify(expected)) {
346
+ return fail('a pinned record serves the props of ITS epoch, never a neighbor\'s', `propsJson entry '${key}' = ${JSON.stringify(expected)}`, `reading '${uri}' served '${key}' = ${JSON.stringify(parsed[key])}.`);
347
+ }
348
+ }
349
+ }
350
+ return null;
351
+ }
312
352
  if (outcome.kind !== 'error') {
313
353
  return fail('a read that cannot be mounted answers with a typed error, never a success-shaped result', probe.expect, `reading '${uri}' returned a result, but this locator cannot be mounted on this server.`);
314
354
  }
@@ -376,6 +416,40 @@ function gradeDisclosure(testCase, outcomes) {
376
416
  }
377
417
  return null;
378
418
  }
419
+ function gradeIdenticalMounts(testCase, outcomes) {
420
+ const group = testCase.identicalMounts ?? [];
421
+ if (group.length < 2)
422
+ return null;
423
+ let reference = null;
424
+ for (const probeName of group) {
425
+ const outcome = outcomes.get(probeName);
426
+ if (outcome === undefined || outcome.kind !== 'mount' || outcome.renderMeta.propsJson === undefined) {
427
+ return {
428
+ name: testCase.name,
429
+ probe: probeName,
430
+ obligation: 'a pinned history record serves identical props on every read (SPEC §7.1.2.2)',
431
+ expected: 'a mount outcome carrying propsJson to compare',
432
+ actual: outcome ?? '(no outcome recorded)',
433
+ message: `probe '${probeName}' is named in this case's identicalMounts group but did not produce a props-carrying mount, so immutability cannot even be compared.`,
434
+ };
435
+ }
436
+ if (reference === null) {
437
+ reference = { probe: probeName, propsJson: outcome.renderMeta.propsJson };
438
+ continue;
439
+ }
440
+ if (outcome.renderMeta.propsJson !== reference.propsJson) {
441
+ return {
442
+ name: testCase.name,
443
+ probe: probeName,
444
+ obligation: 'a pinned history record serves identical props on every read (SPEC §7.1.2.2)',
445
+ expected: reference.propsJson,
446
+ actual: outcome.renderMeta.propsJson,
447
+ message: `probes '${reference.probe}' and '${probeName}' read the same pinned record and got different props — the record mutated between reads.`,
448
+ };
449
+ }
450
+ }
451
+ return null;
452
+ }
379
453
  function gradeIndistinguishable(testCase, outcomes) {
380
454
  const group = testCase.indistinguishable ?? [];
381
455
  if (group.length < 2)
@@ -531,6 +605,14 @@ export function parseCase(input) {
531
605
  probeNames.add(parsed.as);
532
606
  return parsed;
533
607
  });
608
+ const mountGroup = input['identicalMounts'];
609
+ if (mountGroup !== undefined) {
610
+ if (!Array.isArray(mountGroup))
611
+ throw bad("'identicalMounts' must be an array of probe names");
612
+ if (mountGroup.length < 2) {
613
+ throw bad("'identicalMounts' names a single probe — a group of one asserts nothing, which reads as an obligation being graded when it is not");
614
+ }
615
+ }
534
616
  const group = input['indistinguishable'];
535
617
  if (group !== undefined) {
536
618
  if (!Array.isArray(group))
@@ -565,6 +647,7 @@ export function parseCase(input) {
565
647
  seeds,
566
648
  reads,
567
649
  ...(group !== undefined ? { indistinguishable: group.map(String) } : {}),
650
+ ...(mountGroup !== undefined ? { identicalMounts: mountGroup.map(String) } : {}),
568
651
  ...(secrets !== undefined ? { disclosesNothing: secrets.map(String) } : {}),
569
652
  };
570
653
  }
@@ -593,6 +676,7 @@ const CASE_KEYS = [
593
676
  'seeds',
594
677
  'reads',
595
678
  'indistinguishable',
679
+ 'identicalMounts',
596
680
  'disclosesNothing',
597
681
  ];
598
682
  const SERVER_KEYS = [
@@ -606,11 +690,12 @@ const SEED_KEYS = {
606
690
  'identity-record': ['kind', 'session', 'key', 'blueprint'],
607
691
  'durable-blueprint': ['kind', 'componentRef', 'body'],
608
692
  'committed-render': ['kind', 'session', 'size'],
693
+ 'epoch-history': ['kind', 'session', 'records'],
609
694
  'uncommitted-render': ['kind', 'session'],
610
695
  'registered-blueprint': ['kind', 'as'],
611
696
  };
612
697
  const LOCATOR_KEYS = {
613
- render: ['kind', 'session', 'key'],
698
+ render: ['kind', 'session', 'key', 'epoch'],
614
699
  raw: ['kind', 'uri'],
615
700
  };
616
701
  const KEY_KEYS = {
@@ -621,6 +706,7 @@ const EXPECTATION_KEYS = {
621
706
  'typed-error': ['kind', 'jsonRpcCode', 'dataCode', 'detailAbsent'],
622
707
  'outside-typed-set': ['kind'],
623
708
  'live-mount': ['kind'],
709
+ 'pinned-mount': ['kind', 'propsInclude'],
624
710
  };
625
711
  function parseSubstrate(bad, value) {
626
712
  switch (value) {
@@ -686,6 +772,21 @@ function parseSeed(bad, seed) {
686
772
  kind: 'uncommitted-render',
687
773
  session: requireText(bad, seed['session'], "an uncommitted-render seed's 'session'"),
688
774
  };
775
+ case 'epoch-history': {
776
+ const session = requireText(bad, seed['session'], "epoch-history seed 'session'");
777
+ const rawRecords = seed['records'];
778
+ if (!Array.isArray(rawRecords) || rawRecords.length < 2) {
779
+ throw bad("epoch-history seed 'records' must be an array of at least TWO entries — one mint plus one update; a single-record history has nothing superseded to pin");
780
+ }
781
+ const records = rawRecords.map((entry, i) => {
782
+ if (!isRecord(entry) || !isRecord(entry['props'])) {
783
+ throw bad(`epoch-history seed record ${i} authors no 'props' object`);
784
+ }
785
+ rejectUnknownKeys(bad, entry, ['props'], `epoch-history seed record ${i}`);
786
+ return { props: entry['props'] };
787
+ });
788
+ return { kind: 'epoch-history', session, records };
789
+ }
689
790
  case 'registered-blueprint':
690
791
  return {
691
792
  kind: 'registered-blueprint',
@@ -716,10 +817,19 @@ function parseLocator(bad, as, locator) {
716
817
  switch (locator['kind']) {
717
818
  case 'render': {
718
819
  const session = requireText(bad, locator['session'], `probe '${as}' locator 'session'`);
820
+ const rawEpoch = locator['epoch'];
821
+ let epoch;
822
+ if (rawEpoch !== undefined) {
823
+ if (typeof rawEpoch !== 'number' || !Number.isInteger(rawEpoch) || rawEpoch < 0) {
824
+ throw bad(`probe '${as}' locator 'epoch' must be a non-negative integer`);
825
+ }
826
+ epoch = rawEpoch;
827
+ }
719
828
  const key = locator['key'];
829
+ const base = { kind: 'render', session, ...(epoch !== undefined ? { epoch } : {}) };
720
830
  if (key === undefined)
721
- return { kind: 'render', session };
722
- return { kind: 'render', session, key: parseKey(bad, as, key) };
831
+ return base;
832
+ return { ...base, key: parseKey(bad, as, key) };
723
833
  }
724
834
  case 'raw':
725
835
  return { kind: 'raw', uri: requireText(bad, locator['uri'], `probe '${as}' locator 'uri'`) };
@@ -755,6 +865,18 @@ function parseExpectation(bad, as, expect) {
755
865
  switch (expect['kind']) {
756
866
  case 'outside-typed-set':
757
867
  return { kind: 'outside-typed-set' };
868
+ case 'pinned-mount': {
869
+ const rawInclude = expect['propsInclude'];
870
+ if (rawInclude !== undefined && !isRecord(rawInclude)) {
871
+ throw bad(`probe '${as}' 'propsInclude' must be an object of expected prop entries`);
872
+ }
873
+ return {
874
+ kind: 'pinned-mount',
875
+ ...(rawInclude !== undefined
876
+ ? { propsInclude: rawInclude }
877
+ : {}),
878
+ };
879
+ }
758
880
  case 'live-mount':
759
881
  return { kind: 'live-mount' };
760
882
  case 'typed-error': {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggui-ai/protocol-conformance",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Conformance test kit for the ggui protocol. Drives a behavioral fixture catalog against a live implementation over WebSocket, a resources/read catalog against a live MCP server, and four pure-function catalogs (schema, registration, resolution, binding) for the gadget and tool-binding obligations, reporting a scorecard-style pass / fail / skip. MCP builders adopt this kit to prove their implementation is protocol-conformant.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -59,7 +59,7 @@
59
59
  },
60
60
  "dependencies": {
61
61
  "ws": "^8.20.1",
62
- "@ggui-ai/protocol": "0.8.0"
62
+ "@ggui-ai/protocol": "0.9.0"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/node": "^22.0.0",