@volter/twin-standard 1.0.30 → 1.0.32

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 (149) hide show
  1. package/dist/src/archive-source.d.ts +13 -0
  2. package/dist/src/archive-source.js +176 -0
  3. package/dist/src/check-sources.d.ts +19 -2
  4. package/dist/src/check-sources.js +95 -37
  5. package/dist/src/cli.js +52 -9
  6. package/dist/src/clients/ai-gateway.d.ts +1 -0
  7. package/dist/src/clients/ai-gateway.js +3 -0
  8. package/dist/src/clients/anthropic-messages.d.ts +10 -0
  9. package/dist/src/clients/anthropic-messages.js +52 -0
  10. package/dist/src/clients/anthropic.d.ts +1 -0
  11. package/dist/src/clients/anthropic.js +3 -0
  12. package/dist/src/clients/aws.d.ts +2 -0
  13. package/dist/src/clients/aws.js +137 -0
  14. package/dist/src/clients/cerebras.d.ts +1 -0
  15. package/dist/src/clients/cerebras.js +3 -0
  16. package/dist/src/clients/clerk.d.ts +2 -0
  17. package/dist/src/clients/clerk.js +99 -0
  18. package/dist/src/clients/cloudflare.d.ts +2 -0
  19. package/dist/src/clients/cloudflare.js +86 -0
  20. package/dist/src/clients/elevenlabs.d.ts +2 -0
  21. package/dist/src/clients/elevenlabs.js +39 -0
  22. package/dist/src/clients/gemini.d.ts +2 -0
  23. package/dist/src/clients/gemini.js +92 -0
  24. package/dist/src/clients/github.d.ts +2 -0
  25. package/dist/src/clients/github.js +71 -0
  26. package/dist/src/clients/groq.d.ts +1 -0
  27. package/dist/src/clients/groq.js +3 -0
  28. package/dist/src/clients/harness.d.ts +36 -0
  29. package/dist/src/clients/harness.js +74 -0
  30. package/dist/src/clients/merge.d.ts +1 -0
  31. package/dist/src/clients/merge.js +8 -0
  32. package/dist/src/clients/openai-compatible.d.ts +9 -0
  33. package/dist/src/clients/openai-compatible.js +51 -0
  34. package/dist/src/clients/openai.d.ts +2 -0
  35. package/dist/src/clients/openai.js +65 -0
  36. package/dist/src/clients/openrouter.d.ts +1 -0
  37. package/dist/src/clients/openrouter.js +3 -0
  38. package/dist/src/clients/perplexity.d.ts +1 -0
  39. package/dist/src/clients/perplexity.js +3 -0
  40. package/dist/src/clients/planetscale.d.ts +2 -0
  41. package/dist/src/clients/planetscale.js +109 -0
  42. package/dist/src/clients/posthog.d.ts +2 -0
  43. package/dist/src/clients/posthog.js +145 -0
  44. package/dist/src/clients/run.d.ts +1 -0
  45. package/dist/src/clients/run.js +25 -0
  46. package/dist/src/clients/sentry.d.ts +2 -0
  47. package/dist/src/clients/sentry.js +167 -0
  48. package/dist/src/clients/slack.d.ts +2 -0
  49. package/dist/src/clients/slack.js +107 -0
  50. package/dist/src/clients/stripe.d.ts +2 -0
  51. package/dist/src/clients/stripe.js +161 -0
  52. package/dist/src/clients/tinybird.d.ts +2 -0
  53. package/dist/src/clients/tinybird.js +71 -0
  54. package/dist/src/clients/tunnel.d.ts +2 -0
  55. package/dist/src/clients/tunnel.js +99 -0
  56. package/dist/src/clients/upstash.d.ts +2 -0
  57. package/dist/src/clients/upstash.js +143 -0
  58. package/dist/src/clients/volteridentity.d.ts +2 -0
  59. package/dist/src/clients/volteridentity.js +128 -0
  60. package/dist/src/clients/xai.d.ts +2 -0
  61. package/dist/src/clients/xai.js +23 -0
  62. package/dist/src/conformance.d.ts +33 -0
  63. package/dist/src/conformance.js +520 -0
  64. package/dist/src/derive.js +113 -22
  65. package/dist/src/gate.js +22 -16
  66. package/dist/src/index.d.ts +5 -3
  67. package/dist/src/index.js +5 -3
  68. package/dist/src/lanes.d.ts +2 -0
  69. package/dist/src/lanes.js +7 -0
  70. package/dist/src/p3-rules.js +0 -2
  71. package/dist/src/protocol-3.d.ts +1 -0
  72. package/dist/src/protocol-3.js +76 -30
  73. package/dist/src/published.d.ts +6 -0
  74. package/dist/src/published.js +65 -11
  75. package/dist/src/spec-documents.js +2 -1
  76. package/dist/src/spec-ir-client.d.ts +5 -1
  77. package/dist/src/spec-ir-client.js +12 -2
  78. package/dist/src/spec-ir.d.ts +7 -2
  79. package/dist/src/spec-ir.js +42 -10
  80. package/dist/src/verdict.d.ts +14 -0
  81. package/dist/src/verdict.js +30 -0
  82. package/dist/src/walk/behavior-journey.d.ts +47 -0
  83. package/dist/src/walk/behavior-journey.js +1138 -0
  84. package/dist/src/walk/git-client.d.ts +26 -0
  85. package/dist/src/walk/git-client.js +139 -0
  86. package/dist/src/walk/journey-kit.d.ts +445 -0
  87. package/dist/src/walk/journey-kit.js +743 -0
  88. package/dist/src/walk/shape.d.ts +54 -0
  89. package/dist/src/walk/shape.js +210 -0
  90. package/dist/src/walk/sse-example.d.ts +2 -0
  91. package/dist/src/walk/sse-example.js +32 -0
  92. package/dist/src/walk/vendor-fetch.d.ts +20 -0
  93. package/dist/src/walk/vendor-fetch.js +92 -0
  94. package/dist/src/walk/walk-database.d.ts +12 -0
  95. package/dist/src/walk/walk-database.js +96 -0
  96. package/dist/src/walk/xml-view.d.ts +2 -0
  97. package/dist/src/walk/xml-view.js +55 -0
  98. package/package.json +39 -3
  99. package/src/archive-source.ts +141 -0
  100. package/src/check-sources.ts +83 -33
  101. package/src/cli.ts +40 -8
  102. package/src/clients/ai-gateway.ts +4 -0
  103. package/src/clients/anthropic-messages.ts +60 -0
  104. package/src/clients/anthropic.ts +4 -0
  105. package/src/clients/aws.ts +119 -0
  106. package/src/clients/cerebras.ts +4 -0
  107. package/src/clients/clerk.ts +97 -0
  108. package/src/clients/cloudflare.ts +80 -0
  109. package/src/clients/elevenlabs.ts +38 -0
  110. package/src/clients/gemini.ts +89 -0
  111. package/src/clients/github.ts +67 -0
  112. package/src/clients/groq.ts +4 -0
  113. package/src/clients/harness.ts +101 -0
  114. package/src/clients/merge.ts +9 -0
  115. package/src/clients/openai-compatible.ts +59 -0
  116. package/src/clients/openai.ts +66 -0
  117. package/src/clients/openrouter.ts +4 -0
  118. package/src/clients/perplexity.ts +4 -0
  119. package/src/clients/planetscale.ts +98 -0
  120. package/src/clients/posthog.ts +132 -0
  121. package/src/clients/run.ts +18 -0
  122. package/src/clients/sentry.ts +157 -0
  123. package/src/clients/slack.ts +98 -0
  124. package/src/clients/stripe.ts +129 -0
  125. package/src/clients/tinybird.ts +69 -0
  126. package/src/clients/tunnel.ts +79 -0
  127. package/src/clients/upstash.ts +137 -0
  128. package/src/clients/volteridentity.ts +108 -0
  129. package/src/clients/xai.ts +25 -0
  130. package/src/conformance.ts +446 -0
  131. package/src/derive.ts +87 -16
  132. package/src/gate.ts +20 -14
  133. package/src/index.ts +5 -3
  134. package/src/lanes.ts +7 -0
  135. package/src/p3-rules.ts +0 -2
  136. package/src/protocol-3.ts +71 -27
  137. package/src/published.ts +59 -11
  138. package/src/spec-documents.ts +2 -1
  139. package/src/spec-ir-client.ts +15 -4
  140. package/src/spec-ir.ts +43 -5
  141. package/src/verdict.ts +28 -0
  142. package/src/walk/behavior-journey.ts +806 -0
  143. package/src/walk/git-client.ts +138 -0
  144. package/src/walk/journey-kit.ts +858 -0
  145. package/src/walk/shape.ts +208 -0
  146. package/src/walk/sse-example.ts +25 -0
  147. package/src/walk/vendor-fetch.ts +80 -0
  148. package/src/walk/walk-database.ts +97 -0
  149. package/src/walk/xml-view.ts +44 -0
@@ -0,0 +1,25 @@
1
+ // xAI through openai-node (its OpenAI-compatible API): the shared chat cases, and a reasoning turn streamed through the
2
+ // SDK's Responses stream, whose accumulator takes each reasoning summary part the stream adds.
3
+ import type { ClientCase } from './harness.ts';
4
+ import { chatCases, openaiClient } from './openai-compatible.ts';
5
+
6
+ const XAI = { key: 'XAI_API_KEY', base: (host: string) => `https://${host}/v1`, model: 'grok-4.7' };
7
+
8
+ export const cases: ClientCase[] = [
9
+ ...chatCases(XAI),
10
+ {
11
+ id: 'responses-reasoning-stream',
12
+ asks: "a reasoning turn streamed through the SDK's Responses stream completes with its reasoning item and its message",
13
+ source: 'https://developers.openai.com/api/reference/resources/responses/streaming-events "response.reasoning_summary_part.added"',
14
+ async run(c) {
15
+ c.scenario({ handlers: [{ id: 'reasons', on: { userTextIncludes: 'hello' }, respond: { text: 'scripted words', reasoning: 'thinking it through' } }] });
16
+ const client = await openaiClient(c, XAI);
17
+ try {
18
+ const stream = client.responses.stream({ model: XAI.model, input: 'hello', reasoning: { summary: 'auto' } } as never);
19
+ for await (const _event of stream) { /* the accumulator reads each */ }
20
+ const types = (await stream.finalResponse()).output.map((o) => o.type);
21
+ return types.includes('reasoning') && types.includes('message') ? [] : [`the streamed response's output is ${JSON.stringify(types)}`];
22
+ } catch (e) { return [`the SDK's Responses stream failed: ${(e as Error).message}`]; }
23
+ },
24
+ },
25
+ ];
@@ -0,0 +1,446 @@
1
+ import { hasOwnSurface } from './lanes.ts';
2
+ // CONFORMANCE — the deterministic half of the Protocol 3 standard that executes the pack: the contract it serves, run the
3
+ // way an application calls it, and every rule a pack's author follows that a machine can decide (docs/contributing/
4
+ // architecture.md, "Conformance"). One command, the same for a contributor, the catalog's admission and its regular
5
+ // re-assessment:
6
+ //
7
+ // twin-standard conform <pack dir>
8
+ //
9
+ // It imports the pack (its manifest, fetch, index and server.ts) and the kernel the pack itself resolves, serves it on
10
+ // its own from a scratch directory, and reads every unit (the pack and its lanes). The form is the gate's; what a model
11
+ // judges (the life's plausibility) is the proof section's. Each check names the defect it was written against: one
12
+ // that escaped review once fails here when reintroduced.
13
+ import { spawn, spawnSync } from 'node:child_process';
14
+ import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
15
+ import { createRequire } from 'node:module';
16
+ import { connect } from 'node:net';
17
+ import { tmpdir } from 'node:os';
18
+ import { basename, dirname, join, matchesGlob, relative, resolve } from 'node:path';
19
+ import type { TwinStream } from '@volter/world-core';
20
+ import { citations, sourceHeld, type SourceRecord } from './check-sources.ts';
21
+
22
+ export type ConformanceResult = { id: string; asks: string; failures: string[]; note?: string };
23
+
24
+ type Manifest = {
25
+ vendor?: string;
26
+ resources?: Record<string, { refresh?: { list?: string; detail?: string; get?: string; none?: string } }>;
27
+ sockets?: Array<{ id: string; path: string; host?: string }>;
28
+ rateBudget?: { windowMs: number; ceiling: number; burstCeiling?: number; defaultWeight: number; rules?: Array<{ weight: number }>; reason?: string; allowance?: { perMinute: number; source: string } };
29
+ vendorBacked?: { none?: string };
30
+ events?: unknown;
31
+ ingest?: unknown;
32
+ };
33
+
34
+ const pascal = (s: string): string => s.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((w) => w[0]!.toUpperCase() + w.slice(1)).join('');
35
+ const readJson = (p: string): unknown => (existsSync(p) ? JSON.parse(readFileSync(p, 'utf8')) : undefined);
36
+
37
+ /** The pack's units: the pack and each lane in it (a directory with its own src/manifest.ts and no package.json). */
38
+ function unitsIn(pack: string): string[] {
39
+ return [pack, ...readdirSync(pack).sort().map((n) => join(pack, n)).filter((d) => !existsSync(join(d, 'package.json')) && existsSync(join(d, 'src', 'manifest.ts')))];
40
+ }
41
+ const nameOf = (pack: string, unit: string): string => relative(resolve(pack, '..'), unit);
42
+
43
+ /** The files conformance reads from a unit: its manifest, its spec's record, its vendor's life and its decision table
44
+ * (a lane's journeys are its own `journeys/` or else its vendor's). A release publishes each (published()), and a
45
+ * pack's standing counts it in the form only when every unit holds them (twin-world's scripts/pack-standing.ts, by
46
+ * unitFilesMissing): the one list of both. */
47
+ export const UNIT_FILES = ['src/manifest.ts', 'spec/SOURCE.md', 'journeys/customer-life.json', 'journeys/decisions.json'] as const;
48
+
49
+ /** The published package must carry every conformance input its units hold, including lanes. */
50
+ export function unpublishedUnitFiles(unit: string): string[] {
51
+ const vendor = existsSync(join(unit, 'package.json')) ? unit : dirname(unit);
52
+ const pkg = readJson(join(vendor, 'package.json')) as { files?: string[] } | undefined;
53
+ if (pkg?.files === undefined) return []; // npm's default includes the tree
54
+ const patterns = pkg.files.map((entry) => ({ exclude: entry.startsWith('!'), pattern: entry.replace(/^!/, '').replace(/^\.\//, '').replace(/\/+$/, '') }));
55
+ return UNIT_FILES.filter((f) => existsSync(join(unit, f))).map((f) => relative(vendor, join(unit, f)).replaceAll('\\', '/')).filter((f) => {
56
+ const matches = (pattern: string): boolean => matchesGlob(f, pattern) || matchesGlob(f, `${pattern}/**`);
57
+ return !patterns.some((p) => !p.exclude && matches(p.pattern)) || patterns.some((p) => p.exclude && matches(p.pattern));
58
+ }).map((f) => `${f} is absent from package.json files (conformance reads it)`);
59
+ }
60
+
61
+ /** Each of UNIT_FILES a unit lacks where conformance reads it: the manifest of every unit, the life at its vendor's root
62
+ * (lifeServed), the spec's record and the decision table of a unit with a spec (decided()). */
63
+ export function unitFilesMissing(unit: string): string[] {
64
+ const vendor = existsSync(join(unit, 'package.json')) ? unit : dirname(unit);
65
+ const spec = hasOwnSurface(unit);
66
+ const at: Record<(typeof UNIT_FILES)[number], string | undefined> = {
67
+ 'src/manifest.ts': unit,
68
+ 'spec/SOURCE.md': spec ? unit : undefined,
69
+ 'journeys/customer-life.json': vendor,
70
+ 'journeys/decisions.json': spec ? (existsSync(join(unit, 'journeys')) ? unit : vendor) : undefined,
71
+ };
72
+ return UNIT_FILES.filter((f) => at[f] !== undefined && !existsSync(join(at[f]!, f))).map((f) => `${relative(unit, join(at[f]!, f))} is missing (conformance reads it)`);
73
+ }
74
+ /** A unit's fetch export: a pack's `create<Vendor>Fetch`, a lane's `create<Lane>LaneFetch`. */
75
+ const fetchName = (dir: string): string => (existsSync(join(dir, 'package.json')) ? `create${pascal(basename(dir))}Fetch` : `create${pascal(basename(dir))}LaneFetch`);
76
+ const manifestOf = async (unit: string): Promise<Manifest> => ((await import(join(unit, 'src', 'manifest.ts'))) as { manifest: Manifest }).manifest;
77
+
78
+ type Owners = Record<string, string>;
79
+ type Decision = { operation: string; mechanism: string; demand?: string; lane?: string };
80
+
81
+ /** The decision table read against one unit of a vendor (its own API, `''`, or a lane by name), each unit against its
82
+ * own owners (a fetch's `ownersByUnit()`): a row decides the operation of the lane it names; a row naming none decides
83
+ * the vendor's own API's operation of that id, or, when its own API has none, the one lane's that has it. So an id two
84
+ * units share is two decisions, and a lane's operation its root serves stays the lane's gap (Clerk's Backend and
85
+ * Frontend APIs both name `ListOrganizationMemberships`). A demanded operation (journeys/demand.json, the vendor's
86
+ * backend calls) is the unit's a row naming no lane would decide. */
87
+ export function decisionTableProblems(unit: string, byUnit: Record<string, Owners>, decisions: Decision[], demanded: string[] = [], doors: string[] = []): string[] {
88
+ const owners = { ...(byUnit[unit] ?? {}), ...Object.fromEntries(doors.map((id) => [`door:${id}`, 'door'])) };
89
+ const holders = (id: string): string[] => Object.keys(byUnit).filter((u) => id in byUnit[u]!);
90
+ const unnamedUnit = (id: string): string | undefined => (id in (byUnit[''] ?? {}) ? '' : holders(id).length === 1 ? holders(id)[0] : undefined);
91
+ const decides = (d: Decision): string | undefined => (d.lane !== undefined ? d.lane : d.operation.startsWith('door:') && doors.includes(d.operation.slice(5)) ? unit : unnamedUnit(d.operation));
92
+ const mine = decisions.filter((d) => decides(d) === unit);
93
+ const decision = new Map(mine.map((d) => [d.operation, d]));
94
+ const out: string[] = [];
95
+ for (const [id, o] of Object.entries(owners).filter(([, o]) => o !== 'gap')) {
96
+ const d = decision.get(id);
97
+ if (!d) out.push(`${id} is served (${o}) and not in the decision table${holders(id).length > 1 ? ` (an id ${holders(id).map((u) => u || 'the root').join(' and ')} share: its row names "lane": "${unit}")` : ''}`);
98
+ else if (d.mechanism !== o) out.push(`${id} is decided ${d.mechanism} and served by ${o}`);
99
+ else if (!d.demand) out.push(`${id}'s decision names no demand`);
100
+ }
101
+ for (const d of mine) {
102
+ if (!(d.operation in owners)) out.push(`${d.operation} is decided for ${unit || 'the root'}, which has no such operation`);
103
+ else if (owners[d.operation] === 'gap' && d.mechanism !== 'unmodeled') out.push(`${d.operation} is decided ${d.mechanism} and served by nothing`);
104
+ }
105
+ for (const op of demanded) if (op && unnamedUnit(op) === unit && owners[op] === 'gap') out.push(`${op} is demanded (journeys/demand.json) and served by nothing`);
106
+ return out;
107
+ }
108
+
109
+ /** Every operation a unit serves is decided, every operation demanded or refreshed is served (create-pack --index's
110
+ * table, as the catalog reads it), each unit against its own operations. Escaped once: refresh scopes naming
111
+ * operations the pack answered with the gap (discord, polar, github, elevenlabs, cloudflare), so a World rooted at a
112
+ * second World's twin read nothing; and a root's handler counted as its lane's, hiding four Clerk Frontend API gaps. */
113
+ async function decided(pack: string): Promise<string[]> {
114
+ const out: string[] = [];
115
+ const makeOf = async (unit: string) => ((await import(join(unit, 'src', 'fetch.ts'))) as Record<string, unknown>)[fetchName(unit)] as (() => { owners?: () => Owners; ownersByUnit?: () => Record<string, Owners> }) | undefined;
116
+ const root = (await makeOf(pack))?.();
117
+ // a kernel with no ownersByUnit names each unit's owners from the unit's own fetch, each unit read on its own
118
+ const byUnit: Record<string, Owners> | undefined = root?.ownersByUnit?.();
119
+ for (const unit of unitsIn(pack).filter(hasOwnSurface)) {
120
+ const name = nameOf(pack, unit);
121
+ const key = unit === pack ? '' : basename(unit);
122
+ const journeys = existsSync(join(unit, 'journeys')) ? join(unit, 'journeys') : join(unit, '..', 'journeys');
123
+ const owners = byUnit ? byUnit[key] ?? {} : (await makeOf(unit))?.().owners?.();
124
+ if (!owners) { out.push(`${name}: its fetch (${fetchName(unit)}) names no owners() to check the decision table against`); continue; }
125
+ const table = readJson(join(journeys, 'decisions.json')) as { decisions?: Decision[] } | undefined;
126
+ if (!table) { out.push(`${name}: journeys/decisions.json is missing`); continue; }
127
+ const demand = readJson(join(journeys, 'demand.json')) as { applications?: Array<{ backend?: Array<{ operation?: string }> }> } | undefined;
128
+ const demanded = (demand?.applications ?? []).flatMap((a) => a.backend ?? []).flatMap((b) => String(b.operation ?? '').split(/;\s*/));
129
+ out.push(...decisionTableProblems(key, byUnit ?? { [key]: owners }, table.decisions ?? [], demanded, ((await manifestOf(unit)) as { doors?: Array<{ id: string }> }).doors?.map((d) => d.id) ?? []).map((p) => `${name}: ${p}`));
130
+ for (const [resource, decl] of Object.entries((await manifestOf(unit)).resources ?? {})) {
131
+ for (const id of [decl.refresh?.list ?? decl.refresh?.get, decl.refresh?.detail]) {
132
+ // a refresh reads one of its own unit's operations: one the unit lacks (a typo, another lane's) is read nowhere
133
+ if (id && owners[id] === undefined) out.push(`${name}: ${resource}'s refresh reads ${id}, which is no operation of ${name}`);
134
+ else if (id && owners[id] === 'gap') out.push(`${name}: ${resource}'s refresh reads ${id}, which nothing serves`);
135
+ }
136
+ }
137
+ }
138
+ return out;
139
+ }
140
+
141
+ /** Every vendor-backed unit, the pack or a lane, reaches the registered pack's state system: its perform, asked to send
142
+ * one of the unit's operations (to an executor that stops it before anything is sent), finds the unit; and a unit
143
+ * that declares a refresh has one. Escaped once: the kernel's root packOf dropped the lanes beside a pack's own API, so
144
+ * PlanetScale's and Clerk's lanes derived no perform and no refresh. */
145
+ async function registered(pack: string, runtime: Runtime): Promise<string[]> {
146
+ const backed: Array<{ unit: string; lane?: string; op?: string; refreshed: boolean }> = [];
147
+ for (const unit of unitsIn(pack)) {
148
+ const m = await manifestOf(unit);
149
+ const resources = Object.values(m.resources ?? {});
150
+ if (m.vendorBacked?.none || !resources.length) continue;
151
+ const op = (readJson(join(unit, 'src', 'generated', 'surface.gen.json')) as { operations?: Array<{ id: string }> } | undefined)?.operations?.[0]?.id;
152
+ backed.push({ unit: nameOf(pack, unit), ...(unit === pack ? {} : { lane: basename(unit) }), ...(op ? { op } : {}), refreshed: resources.some((r) => r.refresh?.list || r.refresh?.get) });
153
+ }
154
+ if (!backed.length) return [];
155
+ await import(join(pack, 'src', 'index.ts'));
156
+ const vendor = (await manifestOf(pack)).vendor ?? basename(pack);
157
+ const system = runtime.getPack(vendor)?.stateSystem as { perform?: (execute: unknown, group: unknown[], ctx: unknown) => Promise<unknown>; refresh?: unknown } | undefined;
158
+ if (!system?.perform) return [`${backed.map((b) => b.unit).join(', ')} ${backed.length === 1 ? 'is' : 'are'} vendor-backed, and the registered ${vendor} pack has no ${system ? 'perform' : 'state system'} (its index passes every lane to packOf)`];
159
+ const out: string[] = [];
160
+ if (backed.some((b) => b.refreshed) && !system.refresh) out.push(`the registered ${vendor} pack has no refresh, and ${backed.filter((b) => b.refreshed).map((b) => b.unit).join(', ')} declare one`);
161
+ const REACHED = 'conformance: the unit was reached';
162
+ for (const b of backed.filter((x) => x.op)) {
163
+ // a lane is asked by its name alone, with an operation no unit has: only the lane itself can be found (a dropped lane
164
+ // would otherwise be found through another unit holding the same operation id)
165
+ const input = b.lane ? { operationId: '__conformance_probe__', lane: b.lane, path: {}, query: {} } : { operationId: b.op, path: {}, query: {} };
166
+ const why = await system.perform(() => { throw new Error(REACHED); }, [{ id: 'conformance', input }], { resolve: (_t: string, v: string) => v }).then(() => REACHED, (e: Error) => e.message);
167
+ if (/records no operation of the vendor's surface/.test(why)) out.push(`${b.unit}: the registered ${vendor} pack's perform does not know it (${b.op}): its index passes every lane to packOf`);
168
+ }
169
+ return out;
170
+ }
171
+
172
+ /** What a release publishes holds every unit as the checkout does: each unit's manifest, its vendored spec and its
173
+ * journeys, read from the file list `npm pack` makes. Escaped once: seven packs published their lanes' src without
174
+ * their spec, so the catalog's conformance of the tarball never read those lanes. */
175
+ function published(pack: string): string[] {
176
+ const run = spawnSync('npm', ['pack', '--dry-run', '--json', '--ignore-scripts'], { cwd: pack, encoding: 'utf8' });
177
+ let files: Set<string>;
178
+ try { files = new Set(((JSON.parse(run.stdout) as Array<{ files: Array<{ path: string }> }>)[0]?.files ?? []).map((f) => f.path)); } catch { return [`npm pack lists no files (${String(run.stderr).trim().split('\n').at(-1) ?? ''})`]; }
179
+ const out: string[] = [];
180
+ for (const unit of unitsIn(pack)) {
181
+ const at = relative(pack, unit);
182
+ const of = (p: string): string => (at ? `${at}/${p}` : p);
183
+ for (const need of UNIT_FILES) {
184
+ if (existsSync(join(unit, need)) && !files.has(of(need))) out.push(`${nameOf(pack, unit)}: ${of(need)} is not published (package.json's files)`);
185
+ }
186
+ }
187
+ return out;
188
+ }
189
+
190
+ /** Every vendor fact cited (a handler's or a manifest's `// source:`, a patch's source) is recorded in spec/sources.json
191
+ * as a page that answered, with each quoted line found on it: check-sources' record, read, never fetched again. */
192
+ async function cited(pack: string): Promise<string[]> {
193
+ const out: string[] = [];
194
+ for (const unit of unitsIn(pack).filter(hasOwnSurface)) {
195
+ const record = (readJson(join(unit, 'spec', 'sources.json')) ?? {}) as Record<string, SourceRecord>;
196
+ for (const [url, quotes] of await citations(unit)) {
197
+ const r = record[url];
198
+ if (!r) { out.push(`${nameOf(pack, unit)}: ${url} is cited and not recorded (bun <twin-world>/scripts/check-sources.ts ${nameOf(pack, unit)})`); continue; }
199
+ if (!sourceHeld(url, r)) out.push(`${nameOf(pack, unit)}: ${url} did not answer with verified evidence when checked`);
200
+ for (const q of quotes) if (r.quotes?.[q] !== true) out.push(`${nameOf(pack, unit)}: "${q.slice(0, 80)}" is not on ${url}`);
201
+ }
202
+ }
203
+ return out;
204
+ }
205
+
206
+ /** Every stored resource of a vendor-backed unit says how a World rooted at the vendor reads it back: a list, a get, or
207
+ * none with why (architecture, "The real-system adapters"). A lane's events are ingested by its own declaration or by
208
+ * the one its vendor declares for all its lanes ("What a pack declares": `ingest` on the VendorManifest). */
209
+ async function refreshDeclared(pack: string): Promise<string[]> {
210
+ const out: string[] = [];
211
+ const vendorIngest = (await manifestOf(pack)).ingest;
212
+ for (const unit of unitsIn(pack)) {
213
+ const m = await manifestOf(unit);
214
+ if (m.vendorBacked?.none) continue;
215
+ // a `_` resource is the pack's own bookkeeping, never the vendor's
216
+ for (const [name, r] of Object.entries(m.resources ?? {})) if (!name.startsWith('_') && !r.refresh) out.push(`${nameOf(pack, unit)}: ${name} declares no refresh (a list, a get, or none with why)`);
217
+ if (m.events && !m.ingest && !vendorIngest) out.push(`${nameOf(pack, unit)}: it sends events and declares no ingest of the vendor's`);
218
+ }
219
+ return out;
220
+ }
221
+
222
+ /** A budget more permissive than the fallback rests on the vendor's documented allowance, cited (rate-budget-check). */
223
+ async function allowance(pack: string, runtime: Runtime): Promise<string[]> {
224
+ const out: string[] = [];
225
+ const perMinute = (d: NonNullable<Manifest['rateBudget']>): number => (d.windowMs <= 60_000 ? d.ceiling * (60_000 / d.windowMs) : (d.burstCeiling ?? d.ceiling)) / Math.min(d.defaultWeight, ...(d.rules ?? []).map((r) => r.weight));
226
+ const fallback = perMinute(runtime.DEFAULT_RATE_BUDGET);
227
+ for (const unit of unitsIn(pack)) {
228
+ const d = (await manifestOf(unit)).rateBudget;
229
+ if (!d) continue;
230
+ const name = nameOf(pack, unit);
231
+ if (d.windowMs < runtime.DEFAULT_RATE_BUDGET.windowMs) out.push(`${name}: its window (${d.windowMs} ms) is shorter than the fallback's`);
232
+ const admits = perMinute(d);
233
+ if (admits <= fallback) continue;
234
+ if (!d.allowance) { out.push(`${name}: admits ${admits} calls a minute, more than the fallback's ${fallback}, and declares no documented allowance`); continue; }
235
+ if (admits > d.allowance.perMinute) out.push(`${name}: admits ${admits} calls a minute, more than the documented ${d.allowance.perMinute}`);
236
+ // the page's own words state the number: a quote with a figure in it (`cited` finds it on the page)
237
+ const quotes = [...readFileSync(join(unit, 'src', 'manifest.ts'), 'utf8').matchAll(/\/\/ source: (\S+) "([^"]*)"/g)].filter((m) => m[1] === d.allowance!.source).map((m) => m[2]!);
238
+ if (!quotes.length) out.push(`${name}: its allowance's page (${d.allowance.source}) is cited on no // source: line of its manifest with the words it says`);
239
+ else if (!quotes.some((q) => /\d/.test(q))) out.push(`${name}: no quote of its allowance's page (${d.allowance.source}) states a number`);
240
+ }
241
+ return out;
242
+ }
243
+
244
+ type Runtime = {
245
+ getPack: (vendor: string) => { stateSystem?: { perform?: unknown; refresh?: unknown } } | undefined;
246
+ DEFAULT_RATE_BUDGET: NonNullable<Manifest['rateBudget']>;
247
+ };
248
+ type Served = { port: number; inspectPort?: number; stop: () => void };
249
+
250
+ /** The pack served on its own, as a World's process or an application's test serves it: its server.ts, from a scratch
251
+ * working directory (a server keeps its World under it), with the options given. */
252
+ async function serve(pack: string, options: Record<string, unknown>): Promise<Served> {
253
+ const mod = (await import(join(pack, 'src', 'server.ts'))) as Record<string, unknown>;
254
+ const create = Object.entries(mod).find(([k, v]) => /^create\w+TwinServer$/.test(k) && typeof v === 'function')?.[1] as ((o: Record<string, unknown>) => Promise<Served>) | undefined;
255
+ if (!create) throw new Error('src/server.ts exports no create<Vendor>TwinServer');
256
+ return create({ port: 0, ...options });
257
+ }
258
+ const within = <T>(ms: number, p: Promise<T>): Promise<T | 'timeout'> => Promise.race([p, new Promise<'timeout'>((r) => setTimeout(() => r('timeout'), ms))]);
259
+
260
+ /** Served by its server.ts as a World runs it (its boot id set), the pack answers HTTP and the boot probe, and every socket
261
+ * its manifests declare takes an upgrade. Escaped twice: serveHttp never took the upgrade a pack's fetch carries, then
262
+ * read it off the boot probe's wrapper of the fetch, so OpenAI Realtime's /v1/realtime answered 404, first everywhere
263
+ * and then only in a World, while every walk (which drives the fetch directly) passed. */
264
+ async function standalone(pack: string): Promise<string[]> {
265
+ const out: string[] = [];
266
+ const sockets: Array<{ unit: string; path: string; host?: string }> = [];
267
+ for (const unit of unitsIn(pack)) for (const s of (await manifestOf(unit)).sockets ?? []) sockets.push({ unit: nameOf(pack, unit), path: s.path, ...(s.host ? { host: s.host } : {}) });
268
+ // served the way a World runs it: the boot id set, so the seam wraps the fetch with the World's boot probe
269
+ const env = (globalThis as { process: { env: Record<string, string | undefined> } }).process.env;
270
+ const before = env.VOLTER_WORLD_BOOT_ID;
271
+ env.VOLTER_WORLD_BOOT_ID = 'conformance';
272
+ let server: Served;
273
+ try { server = await serve(pack, {}); } finally { if (before === undefined) delete env.VOLTER_WORLD_BOOT_ID; else env.VOLTER_WORLD_BOOT_ID = before; }
274
+ try {
275
+ const http = await within(5000, fetch(`http://127.0.0.1:${(server.inspectPort ?? server.port)}/`).then((r) => r.status, (e: Error) => e.message));
276
+ if (http === 'timeout' || typeof http === 'string') out.push(`served as a World runs it, it answers no HTTP request (${http})`);
277
+ const boot = await within(5000, fetch(`http://127.0.0.1:${(server.inspectPort ?? server.port)}/__volter/world-boot`).then((r) => r.json() as Promise<{ boot?: string }>, (): { boot?: string } => ({})));
278
+ if (boot === 'timeout' || boot.boot !== 'conformance') out.push("served as a World runs it, it does not answer the World's boot probe (/__volter/world-boot)");
279
+ for (const s of sockets) {
280
+ const upgraded = await new Promise<string | undefined>((done) => {
281
+ const ws = new WebSocket(`ws://127.0.0.1:${server.port}${s.path}`, { headers: s.host ? { host: s.host } : {} } as unknown as string[]);
282
+ const timer = setTimeout(() => { ws.close(); done('no upgrade in 5 s'); }, 5000);
283
+ ws.onopen = () => { clearTimeout(timer); ws.close(); done(undefined); };
284
+ ws.onerror = () => { clearTimeout(timer); done('the upgrade was refused'); };
285
+ });
286
+ if (upgraded) out.push(`${s.unit}: the socket at ${s.host ?? ''}${s.path}: ${upgraded}`);
287
+ }
288
+ } finally { server.stop(); }
289
+ return out;
290
+ }
291
+
292
+ /** A line protocol's connection over TCP, in the walk's stream shape. A line drew its replies when bytes arrived and then
293
+ * 50 ms passed with no more (a multi-line reply arrives in pieces), or when 500 ms passed with none (a line that draws no
294
+ * reply: a message's body before its end); 10 s bounds a server that keeps writing. The windows are timing, not protocol:
295
+ * a reply slower than 500 ms is read with the next line's. */
296
+ function lineConnection(port: number): TwinStream {
297
+ return (sink) => {
298
+ const socket = connect(port, '127.0.0.1');
299
+ let heard = 0;
300
+ socket.on('data', (bytes: Uint8Array) => { heard = Date.now(); sink.write(bytes); });
301
+ socket.on('close', () => sink.end());
302
+ socket.on('error', () => socket.destroy());
303
+ const opened = new Promise<void>((resolveOpen) => { socket.once('connect', () => resolveOpen()); socket.once('error', () => resolveOpen()); });
304
+ return {
305
+ data: (chunk) => { void opened.then(() => socket.write(chunk)); },
306
+ settled: async () => {
307
+ await opened;
308
+ const start = Date.now(); const since = heard;
309
+ while (Date.now() - start < 10_000) {
310
+ await new Promise((r) => setTimeout(r, 10));
311
+ if (socket.destroyed) return;
312
+ if (heard > since ? Date.now() - heard >= 50 : Date.now() - start >= 500) return;
313
+ }
314
+ },
315
+ close: () => socket.destroy(),
316
+ };
317
+ };
318
+ }
319
+
320
+ /** The life walked as an application calls the pack: its server.ts served as a World runs it (boot id set, the World's
321
+ * root and the life's scenario its options), every step over HTTP with the vendor's host, every check of the walk held
322
+ * (status, expectations, captures, SHAPE, LEAK over answers, events and frames). Slack's events once carried the
323
+ * kernel's updatedAt; a request the served pack does not answer in 10 s fails its step. */
324
+ async function lifeServed(pack: string): Promise<string[]> {
325
+ const lifePath = join(pack, 'journeys', 'customer-life.json');
326
+ if (!existsSync(lifePath)) return ['journeys/customer-life.json is missing'];
327
+ const { inMemoryWorld, usePacks, walk } = await import('./walk/behavior-journey.ts');
328
+ usePacks(dirname(pack));
329
+ const env = (globalThis as { process: { env: Record<string, string | undefined> } }).process.env;
330
+ const served = async (root: string, options: { scenarioPath?: string }) => {
331
+ const before = env.VOLTER_WORLD_BOOT_ID;
332
+ env.VOLTER_WORLD_BOOT_ID = 'conformance';
333
+ let server: Served;
334
+ try { server = await serve(pack, { root, ...options }); } finally { if (before === undefined) delete env.VOLTER_WORLD_BOOT_ID; else env.VOLTER_WORLD_BOOT_ID = before; }
335
+ const port = server.inspectPort ?? server.port;
336
+ return {
337
+ // the vendor's URL as the application called it, sent to the served pack: its host carried in the Host header
338
+ fetch: async (request: Request): Promise<Response> => {
339
+ const url = new URL(request.url);
340
+ const headers = new Headers(request.headers);
341
+ headers.set('host', url.host);
342
+ const body = request.method === 'GET' || request.method === 'HEAD' ? undefined : await request.arrayBuffer();
343
+ const answer = await within(10_000, fetch(`http://127.0.0.1:${port}${url.pathname}${url.search}`, { method: request.method, headers, redirect: 'manual', ...(body ? { body } : {}) }));
344
+ return answer === 'timeout' ? new Response(`no answer in 10 s from the served pack (${request.method} ${url.pathname})`, { status: 599 }) : answer;
345
+ },
346
+ // a socket opened as an application opens it: a WebSocket to the served port, the vendor's host in its Host header
347
+ socket: (url: string, headers: Record<string, string>) => new WebSocket(`ws://127.0.0.1:${port}${url}`, { headers } as unknown as string[]),
348
+ // a line connection opened as an application's client opens it: TCP to the protocol listener (the primary port)
349
+ stream: lineConnection(server.port),
350
+ stop: () => server.stop(),
351
+ };
352
+ };
353
+ const world = inMemoryWorld(`conform-${basename(pack)}`);
354
+ const failures: Array<{ step: string; beat: string; detail: string }> = [];
355
+ try {
356
+ await walk(JSON.parse(readFileSync(lifePath, 'utf8')), world.root(), failures, { beats: true, served });
357
+ } finally { world.restore(); }
358
+ return failures.slice(0, 30).map((f) => `${f.beat} ${f.step}: ${f.detail}`.slice(0, 300)).concat(failures.length > 30 ? [`… ${failures.length - 30} more`] : []);
359
+ }
360
+
361
+ /** The pack driven by the vendor's own client (clients/<vendor>.ts): served as a World runs it, its credentials issued
362
+ * by its credential door, the cases run in Node as an application runs the vendor's SDK (clients/run.ts), each the
363
+ * vendor's documented behaviour. A vendor whose descriptor names npm clients and has no cases fails it, unless the
364
+ * descriptor's clientCasesNone says why none is owed. */
365
+ async function clientCases(pack: string, scratch: string): Promise<{ failures: string[]; note?: string }> {
366
+ const vendor = (await manifestOf(pack)).vendor ?? basename(pack);
367
+ if (!existsSync(join(import.meta.dir, 'clients', `${vendor}.ts`))) {
368
+ // a vendor whose descriptor names npm clients (adoption.sdks) is held to them: no case is no proof the SDK works,
369
+ // unless the descriptor says why none is owed (clientCasesNone: no official SDK, or none the demand's applications
370
+ // use); one an application calls with no SDK of the vendor's (a raw HTTP catalog) has no client to drive
371
+ const descriptor = ((await manifestOf(pack)) as { descriptor?: { adoption?: { sdks?: string[] }; clientCasesNone?: string } }).descriptor;
372
+ const sdks = descriptor?.adoption?.sdks ?? [];
373
+ if (descriptor?.clientCasesNone) return { failures: [], note: `${vendor} owes no client case: ${descriptor.clientCasesNone}` };
374
+ return sdks.length
375
+ ? { failures: [`${vendor} names npm clients (${sdks.join(', ')}) and has no client cases: add packages/twin-standard/src/clients/${vendor}.ts, the SDK driving the pack as an application does, or the descriptor's clientCasesNone saying why none is owed`] }
376
+ : { failures: [], note: `${vendor} declares no SDK of its own` };
377
+ }
378
+ const descriptor = ((await manifestOf(pack)) as { descriptor?: { hosts?: Array<{ host?: string; suffix?: string }>; credentialDoor?: { path: string; body: Record<string, unknown>; fill: Record<string, string> } } }).descriptor;
379
+ // the first host that names one: a descriptor may lead with a suffix (a region's hosts under one domain), which no
380
+ // request addresses; the case's client reaches whatever host its issued credentials name
381
+ const host = descriptor?.hosts?.find((h) => h.host)?.host;
382
+ if (!host) return { failures: ['the descriptor names no host for its client to address'] };
383
+ const scenarioPath = join(scratch, 'client-scenario.json');
384
+ writeFileSync(scenarioPath, '{"handlers":[]}');
385
+ const env = (globalThis as { process: { env: Record<string, string | undefined> } }).process.env;
386
+ const before = env.VOLTER_WORLD_BOOT_ID;
387
+ env.VOLTER_WORLD_BOOT_ID = 'conformance';
388
+ let server: Served;
389
+ try { server = await serve(pack, { root: join(scratch, 'client-world'), scenarioPath }); } finally { if (before === undefined) delete env.VOLTER_WORLD_BOOT_ID; else env.VOLTER_WORLD_BOOT_ID = before; }
390
+ try {
391
+ // the World's credentials: what the pack's credential door issues, by the variable a World sets each in
392
+ const issued: Record<string, string> = {};
393
+ const door = descriptor?.credentialDoor;
394
+ if (door) {
395
+ const answer = (await (await fetch(`http://127.0.0.1:${server.port}${door.path}`, { method: 'POST', headers: { host, 'content-type': 'application/json' }, body: JSON.stringify(door.body) })).json().catch(() => ({}))) as Record<string, unknown>;
396
+ for (const [name, field] of Object.entries(door.fill)) if (answer[field] !== undefined) issued[name] = String(answer[field]);
397
+ }
398
+ const job = join(scratch, 'client-job.json');
399
+ writeFileSync(job, JSON.stringify({ vendor, port: server.port, host, env: issued, scenarioPath }));
400
+ // Node, as an application runs its SDK (asynchronous: this process serves the pack meanwhile)
401
+ const out = await new Promise<string>((resolve) => {
402
+ const child = spawn('node', ['--experimental-strip-types', '--no-warnings', join(import.meta.dir, 'clients', 'run.ts'), job], { stdio: ['ignore', 'pipe', 'pipe'] });
403
+ let text = '';
404
+ let errors = '';
405
+ child.stdout.on('data', (d: Buffer) => { text += d; });
406
+ child.stderr.on('data', (d: Buffer) => { errors += d; });
407
+ const timer = setTimeout(() => child.kill(), 300_000);
408
+ child.on('close', (code) => { clearTimeout(timer); resolve(code === 0 ? text : `${text}\n${JSON.stringify({ id: 'runner', asks: 'the cases run', failures: [`node exited ${code}: ${errors.trim().split('\n').slice(-2).join(' ')}`] })}`); });
409
+ });
410
+ const results = out.split('\n').filter((l) => l.startsWith('{')).map((l) => JSON.parse(l) as { id: string; asks: string; failures: string[] });
411
+ return { failures: results.flatMap((r) => r.failures.map((f) => `${r.id} (${r.asks}): ${f}`)), note: `${results.length} case(s) through the vendor's client` };
412
+ } finally { server.stop(); }
413
+ }
414
+
415
+ /** Every check over the pack at `dir`, in order. */
416
+ export async function conformance(dir: string): Promise<ConformanceResult[]> {
417
+ const pack = resolve(dir);
418
+ if (!existsSync(join(pack, 'package.json'))) throw new Error(`${dir} is no pack (no package.json): conformance takes a pack's directory`);
419
+ // the kernel the pack itself resolves: one registry, one serve seam, the pack's own
420
+ const runtime = (await import(createRequire(join(pack, 'package.json')).resolve('@volter/world-core/runtime'))) as Runtime;
421
+ const scratch = mkdtempSync(join(tmpdir(), 'conformance-'));
422
+ const here = process.cwd();
423
+ process.chdir(scratch);
424
+ const run = async (id: string, asks: string, check: () => Promise<string[]> | string[]): Promise<ConformanceResult> => {
425
+ try { return { id, asks, failures: await check() }; } catch (error) { return { id, asks, failures: [`the check could not run: ${(error as Error).message.split('\n')[0]}`] }; }
426
+ };
427
+ try {
428
+ return [
429
+ await run('decided', 'every served operation is decided with its demand; every demanded and every refreshed operation is served', () => decided(pack)),
430
+ await run('published', 'what a release publishes holds every unit: its manifest, its spec and its journeys', () => published(pack)),
431
+ await run('cited', 'every vendor fact cited is recorded as a page that answered, each quote found on it', () => cited(pack)),
432
+ await run('refresh', 'every stored resource of a vendor-backed unit declares how it is read back, and a unit that sends events ingests the vendor\'s', () => refreshDeclared(pack)),
433
+ await run('registered', 'a vendor-backed unit, lane or not, reaches the registered pack\'s state system', () => registered(pack, runtime)),
434
+ await run('allowance', 'a rate budget above the fallback rests on the vendor\'s documented allowance, cited in its manifest', () => allowance(pack, runtime)),
435
+ await (async (): Promise<ConformanceResult> => {
436
+ const asks = "driven by the vendor's own client (its official SDK), served as a World runs it, the vendor's documented behaviour holds";
437
+ try { const r = await clientCases(pack, scratch); return { id: 'client', asks, failures: r.failures, ...(r.note ? { note: r.note } : {}) }; } catch (error) { return { id: 'client', asks, failures: [`the check could not run: ${(error as Error).message.split('\n')[0]}`] }; }
438
+ })(),
439
+ await run('life', 'the life walked over HTTP against the pack served as a World runs it, with its scenario: every check held', () => lifeServed(pack)),
440
+ await run('standalone', 'served by its server.ts as a World runs it, the pack answers HTTP and the boot probe, and each declared socket upgrades', () => standalone(pack)),
441
+ ];
442
+ } finally {
443
+ process.chdir(here);
444
+ rmSync(scratch, { recursive: true, force: true });
445
+ }
446
+ }