@cleocode/contracts 2026.5.96 → 2026.5.97

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 (113) hide show
  1. package/dist/__tests__/enums.test.d.ts +14 -0
  2. package/dist/__tests__/enums.test.d.ts.map +1 -0
  3. package/dist/__tests__/enums.test.js +75 -0
  4. package/dist/__tests__/enums.test.js.map +1 -0
  5. package/dist/__tests__/jobs.test.d.ts +11 -0
  6. package/dist/__tests__/jobs.test.d.ts.map +1 -0
  7. package/dist/__tests__/jobs.test.js +48 -0
  8. package/dist/__tests__/jobs.test.js.map +1 -0
  9. package/dist/__tests__/memory-wire-shapes.test.d.ts +19 -0
  10. package/dist/__tests__/memory-wire-shapes.test.d.ts.map +1 -0
  11. package/dist/__tests__/memory-wire-shapes.test.js +119 -0
  12. package/dist/__tests__/memory-wire-shapes.test.js.map +1 -0
  13. package/dist/__tests__/operation-def.test.d.ts +20 -0
  14. package/dist/__tests__/operation-def.test.d.ts.map +1 -0
  15. package/dist/__tests__/operation-def.test.js +111 -0
  16. package/dist/__tests__/operation-def.test.js.map +1 -0
  17. package/dist/__tests__/provenance.test.d.ts +18 -0
  18. package/dist/__tests__/provenance.test.d.ts.map +1 -0
  19. package/dist/__tests__/provenance.test.js +142 -0
  20. package/dist/__tests__/provenance.test.js.map +1 -0
  21. package/dist/__tests__/scaffold-diagnostics.test.d.ts +19 -0
  22. package/dist/__tests__/scaffold-diagnostics.test.d.ts.map +1 -0
  23. package/dist/__tests__/scaffold-diagnostics.test.js +70 -0
  24. package/dist/__tests__/scaffold-diagnostics.test.js.map +1 -0
  25. package/dist/dispatch/identity.d.ts +72 -0
  26. package/dist/dispatch/identity.d.ts.map +1 -0
  27. package/dist/dispatch/identity.js +72 -0
  28. package/dist/dispatch/identity.js.map +1 -0
  29. package/dist/dispatch/operation-def.d.ts +92 -0
  30. package/dist/dispatch/operation-def.d.ts.map +1 -0
  31. package/dist/dispatch/operation-def.js +31 -0
  32. package/dist/dispatch/operation-def.js.map +1 -0
  33. package/dist/enums.d.ts +123 -0
  34. package/dist/enums.d.ts.map +1 -0
  35. package/dist/enums.js +139 -0
  36. package/dist/enums.js.map +1 -0
  37. package/dist/index.d.ts +14 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +4 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/jobs.d.ts +39 -0
  42. package/dist/jobs.d.ts.map +1 -0
  43. package/dist/jobs.js +19 -0
  44. package/dist/jobs.js.map +1 -0
  45. package/dist/memory/budgeted.d.ts +67 -0
  46. package/dist/memory/budgeted.d.ts.map +1 -0
  47. package/dist/memory/budgeted.js +17 -0
  48. package/dist/memory/budgeted.js.map +1 -0
  49. package/dist/memory/fetch.d.ts +58 -0
  50. package/dist/memory/fetch.d.ts.map +1 -0
  51. package/dist/memory/fetch.js +19 -0
  52. package/dist/memory/fetch.js.map +1 -0
  53. package/dist/memory/observe.d.ts +158 -0
  54. package/dist/memory/observe.d.ts.map +1 -0
  55. package/dist/memory/observe.js +46 -0
  56. package/dist/memory/observe.js.map +1 -0
  57. package/dist/memory/search.d.ts +137 -0
  58. package/dist/memory/search.d.ts.map +1 -0
  59. package/dist/memory/search.js +22 -0
  60. package/dist/memory/search.js.map +1 -0
  61. package/dist/memory/timeline.d.ts +89 -0
  62. package/dist/memory/timeline.d.ts.map +1 -0
  63. package/dist/memory/timeline.js +22 -0
  64. package/dist/memory/timeline.js.map +1 -0
  65. package/dist/operations/focus.d.ts +199 -0
  66. package/dist/operations/focus.d.ts.map +1 -0
  67. package/dist/operations/focus.js +15 -0
  68. package/dist/operations/focus.js.map +1 -0
  69. package/dist/operations/index.d.ts +1 -0
  70. package/dist/operations/index.d.ts.map +1 -1
  71. package/dist/operations/index.js +1 -0
  72. package/dist/operations/index.js.map +1 -1
  73. package/dist/operations/session.d.ts +54 -0
  74. package/dist/operations/session.d.ts.map +1 -1
  75. package/dist/operations/tasks.d.ts +38 -0
  76. package/dist/operations/tasks.d.ts.map +1 -1
  77. package/dist/provenance.d.ts +257 -0
  78. package/dist/provenance.d.ts.map +1 -0
  79. package/dist/provenance.js +42 -0
  80. package/dist/provenance.js.map +1 -0
  81. package/dist/release/plan.d.ts +7 -7
  82. package/dist/scaffold-diagnostics.d.ts +110 -0
  83. package/dist/scaffold-diagnostics.d.ts.map +1 -0
  84. package/dist/scaffold-diagnostics.js +28 -0
  85. package/dist/scaffold-diagnostics.js.map +1 -0
  86. package/dist/session.d.ts +37 -0
  87. package/dist/session.d.ts.map +1 -1
  88. package/dist/session.js.map +1 -1
  89. package/dist/tasks/archive.d.ts +3 -3
  90. package/package.json +42 -2
  91. package/src/__tests__/enums.test.ts +114 -0
  92. package/src/__tests__/jobs.test.ts +76 -0
  93. package/src/__tests__/memory-wire-shapes.test.ts +371 -0
  94. package/src/__tests__/operation-def.test.ts +185 -0
  95. package/src/__tests__/provenance.test.ts +259 -0
  96. package/src/__tests__/scaffold-diagnostics.test.ts +137 -0
  97. package/src/dispatch/identity.ts +109 -0
  98. package/src/dispatch/operation-def.ts +102 -0
  99. package/src/enums.ts +144 -0
  100. package/src/index.ts +77 -0
  101. package/src/jobs.ts +45 -0
  102. package/src/memory/budgeted.ts +75 -0
  103. package/src/memory/fetch.ts +66 -0
  104. package/src/memory/observe.ts +176 -0
  105. package/src/memory/search.ts +145 -0
  106. package/src/memory/timeline.ts +100 -0
  107. package/src/operations/focus.ts +226 -0
  108. package/src/operations/index.ts +1 -0
  109. package/src/operations/session.ts +56 -0
  110. package/src/operations/tasks.ts +40 -0
  111. package/src/provenance.ts +335 -0
  112. package/src/scaffold-diagnostics.ts +119 -0
  113. package/src/session.ts +37 -0
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Structural-equivalence tests for the BRAIN memory wire-shape contracts.
3
+ *
4
+ * These tests pin the field shapes of the 15 types promoted from
5
+ * `packages/core/src/memory/brain-retrieval.ts` in Phase 0e of
6
+ * SG-ARCH-SOLID (E-CONTRACTS-FOUNDATION). Accidental narrowing or
7
+ * widening triggers a compile-time failure during `tsc -b` in the CI gate.
8
+ *
9
+ * The compile-time assertions use the conditional-equality trick
10
+ * (`Equals<A, B>`) so any structural drift produces a TS2322 or TS2344
11
+ * at build time. The runtime `expect` smoke verifies that constructible
12
+ * literals satisfy each interface — these are pure type contracts with
13
+ * no runtime, so this is a thin satisfies check rather than a behavior
14
+ * test.
15
+ *
16
+ * @since SG-ARCH-SOLID Saga T9831 · E-CONTRACTS-FOUNDATION T9832 · T9956 (Phase 0e)
17
+ */
18
+
19
+ import { describe, expect, it } from 'vitest';
20
+ import type { BrainSourceConfidence } from '../brain.js';
21
+ import type { BrainObservationType } from '../facade.js';
22
+ import type {
23
+ BudgetedEntry,
24
+ BudgetedResult,
25
+ BudgetedRetrievalOptions,
26
+ } from '../memory/budgeted.js';
27
+ import type {
28
+ FetchBrainEntriesParams,
29
+ FetchBrainEntriesResult,
30
+ FetchedBrainEntry,
31
+ } from '../memory/fetch.js';
32
+ import {
33
+ BRAIN_OBSERVATION_SOURCE_TYPES,
34
+ type BrainObservationSourceType,
35
+ type ObserveBrainParams,
36
+ type ObserveBrainResult,
37
+ } from '../memory/observe.js';
38
+ import type {
39
+ BrainCompactHit,
40
+ SearchBrainCompactParams,
41
+ SearchBrainCompactResult,
42
+ } from '../memory/search.js';
43
+ import type {
44
+ BrainAnchor,
45
+ TimelineBrainParams,
46
+ TimelineBrainResult,
47
+ TimelineNeighbor,
48
+ } from '../memory/timeline.js';
49
+
50
+ // ─── Compile-time structural-equality helpers ───────────────────────
51
+
52
+ /** Resolve to `1` IFF `A` and `B` are mutually assignable; `2` otherwise. */
53
+ type Equals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? 1 : 2;
54
+
55
+ /** Compile-time assert that `T` resolves to `1`. */
56
+ type AssertEquals1<T extends 1> = T;
57
+
58
+ // ─── Search: BrainCompactHit shape pin ──────────────────────────────
59
+
60
+ type _BrainCompactHitShape = {
61
+ id: string;
62
+ type: 'decision' | 'pattern' | 'learning' | 'observation';
63
+ title: string;
64
+ date: string;
65
+ relevance?: number;
66
+ rrfScore?: number;
67
+ bm25Score?: number;
68
+ _next?: Record<string, string>;
69
+ };
70
+
71
+ type _AssertBrainCompactHitPinned = AssertEquals1<Equals<BrainCompactHit, _BrainCompactHitShape>>;
72
+
73
+ // ─── Search: SearchBrainCompactParams shape pin ─────────────────────
74
+
75
+ type _SearchBrainCompactParamsShape = {
76
+ query: string;
77
+ limit?: number;
78
+ tables?: Array<'decisions' | 'patterns' | 'learnings' | 'observations'>;
79
+ dateStart?: string;
80
+ dateEnd?: string;
81
+ agent?: string;
82
+ useRRF?: boolean;
83
+ peerId?: string;
84
+ includeGlobal?: boolean;
85
+ mode?: 'recency' | 'lexical' | 'hybrid';
86
+ since?: string;
87
+ };
88
+
89
+ type _AssertSearchParamsPinned = AssertEquals1<
90
+ Equals<SearchBrainCompactParams, _SearchBrainCompactParamsShape>
91
+ >;
92
+
93
+ // ─── Search: SearchBrainCompactResult shape pin ─────────────────────
94
+
95
+ type _SearchBrainCompactResultShape = {
96
+ results: BrainCompactHit[];
97
+ total: number;
98
+ tokensEstimated: number;
99
+ };
100
+
101
+ type _AssertSearchResultPinned = AssertEquals1<
102
+ Equals<SearchBrainCompactResult, _SearchBrainCompactResultShape>
103
+ >;
104
+
105
+ // ─── Timeline: BrainAnchor shape pin ────────────────────────────────
106
+
107
+ type _BrainAnchorShape = {
108
+ id: string;
109
+ type: string;
110
+ data: unknown;
111
+ };
112
+
113
+ type _AssertBrainAnchorPinned = AssertEquals1<Equals<BrainAnchor, _BrainAnchorShape>>;
114
+
115
+ // ─── Timeline: TimelineBrainParams shape pin ────────────────────────
116
+
117
+ type _TimelineBrainParamsShape = {
118
+ anchor: string;
119
+ depthBefore?: number;
120
+ depthAfter?: number;
121
+ };
122
+
123
+ type _AssertTimelineParamsPinned = AssertEquals1<
124
+ Equals<TimelineBrainParams, _TimelineBrainParamsShape>
125
+ >;
126
+
127
+ // ─── Timeline: TimelineNeighbor shape pin ───────────────────────────
128
+
129
+ type _TimelineNeighborShape = {
130
+ id: string;
131
+ type: string;
132
+ date: string;
133
+ };
134
+
135
+ type _AssertTimelineNeighborPinned = AssertEquals1<
136
+ Equals<TimelineNeighbor, _TimelineNeighborShape>
137
+ >;
138
+
139
+ // ─── Timeline: TimelineBrainResult shape pin ────────────────────────
140
+
141
+ type _TimelineBrainResultShape = {
142
+ anchor: BrainAnchor | null;
143
+ before: TimelineNeighbor[];
144
+ after: TimelineNeighbor[];
145
+ };
146
+
147
+ type _AssertTimelineResultPinned = AssertEquals1<
148
+ Equals<TimelineBrainResult, _TimelineBrainResultShape>
149
+ >;
150
+
151
+ // ─── Fetch: FetchBrainEntriesParams shape pin ───────────────────────
152
+
153
+ type _FetchBrainEntriesParamsShape = {
154
+ ids: string[];
155
+ };
156
+
157
+ type _AssertFetchParamsPinned = AssertEquals1<
158
+ Equals<FetchBrainEntriesParams, _FetchBrainEntriesParamsShape>
159
+ >;
160
+
161
+ // ─── Fetch: FetchedBrainEntry shape pin ─────────────────────────────
162
+
163
+ type _FetchedBrainEntryShape = {
164
+ id: string;
165
+ type: string;
166
+ data: unknown;
167
+ };
168
+
169
+ type _AssertFetchedEntryPinned = AssertEquals1<Equals<FetchedBrainEntry, _FetchedBrainEntryShape>>;
170
+
171
+ // ─── Fetch: FetchBrainEntriesResult shape pin ───────────────────────
172
+
173
+ type _FetchBrainEntriesResultShape = {
174
+ results: FetchedBrainEntry[];
175
+ notFound: string[];
176
+ tokensEstimated: number;
177
+ };
178
+
179
+ type _AssertFetchResultPinned = AssertEquals1<
180
+ Equals<FetchBrainEntriesResult, _FetchBrainEntriesResultShape>
181
+ >;
182
+
183
+ // ─── Observe: BrainObservationSourceType shape pin ──────────────────
184
+
185
+ type _BrainObservationSourceTypeShape = 'agent' | 'session-debrief' | 'claude-mem' | 'manual';
186
+
187
+ type _AssertObservationSourceTypePinned = AssertEquals1<
188
+ Equals<BrainObservationSourceType, _BrainObservationSourceTypeShape>
189
+ >;
190
+
191
+ // ─── Observe: ObserveBrainParams shape pin ──────────────────────────
192
+
193
+ type _ObserveBrainParamsShape = {
194
+ text: string;
195
+ title?: string;
196
+ type?: BrainObservationType;
197
+ project?: string;
198
+ sourceSessionId?: string;
199
+ sourceType?: BrainObservationSourceType;
200
+ agent?: string;
201
+ sourceConfidence?: BrainSourceConfidence;
202
+ crossRef?: string[];
203
+ attachmentRefs?: string[];
204
+ origin?: string | null;
205
+ provenanceChain?: string[] | null;
206
+ _skipGate?: boolean;
207
+ };
208
+
209
+ type _AssertObserveParamsPinned = AssertEquals1<
210
+ Equals<ObserveBrainParams, _ObserveBrainParamsShape>
211
+ >;
212
+
213
+ // ─── Observe: ObserveBrainResult shape pin ──────────────────────────
214
+
215
+ type _ObserveBrainResultShape = {
216
+ id: string;
217
+ type: string;
218
+ createdAt: string;
219
+ };
220
+
221
+ type _AssertObserveResultPinned = AssertEquals1<
222
+ Equals<ObserveBrainResult, _ObserveBrainResultShape>
223
+ >;
224
+
225
+ // ─── Budgeted: BudgetedRetrievalOptions shape pin ───────────────────
226
+
227
+ type _BudgetedRetrievalOptionsShape = {
228
+ types?: Array<'semantic' | 'episodic' | 'procedural'>;
229
+ tiers?: Array<'short' | 'medium' | 'long'>;
230
+ verified?: boolean;
231
+ };
232
+
233
+ type _AssertBudgetedOptionsPinned = AssertEquals1<
234
+ Equals<BudgetedRetrievalOptions, _BudgetedRetrievalOptionsShape>
235
+ >;
236
+
237
+ // ─── Budgeted: BudgetedEntry shape pin ──────────────────────────────
238
+
239
+ type _BudgetedEntryShape = {
240
+ id: string;
241
+ type: string;
242
+ title: string;
243
+ text: string;
244
+ score: number;
245
+ tokensEstimated: number;
246
+ memoryTier?: string;
247
+ memoryType?: string;
248
+ };
249
+
250
+ type _AssertBudgetedEntryPinned = AssertEquals1<Equals<BudgetedEntry, _BudgetedEntryShape>>;
251
+
252
+ // ─── Budgeted: BudgetedResult shape pin ─────────────────────────────
253
+
254
+ type _BudgetedResultShape = {
255
+ entries: BudgetedEntry[];
256
+ tokensUsed: number;
257
+ tokensRemaining: number;
258
+ excluded: number;
259
+ };
260
+
261
+ type _AssertBudgetedResultPinned = AssertEquals1<Equals<BudgetedResult, _BudgetedResultShape>>;
262
+
263
+ // ─── Runtime constructibility smoke ─────────────────────────────────
264
+
265
+ describe('memory wire-shape contracts (T9956)', () => {
266
+ it('BrainCompactHit is constructible with the canonical shape', () => {
267
+ const hit: BrainCompactHit = {
268
+ id: 'O-abc123',
269
+ type: 'observation',
270
+ title: 'sample',
271
+ date: '2026-05-21T00:00:00Z',
272
+ };
273
+ expect(hit.id).toBe('O-abc123');
274
+ expect(hit.type).toBe('observation');
275
+ });
276
+
277
+ it('SearchBrainCompactResult composes BrainCompactHit and token accounting', () => {
278
+ const result: SearchBrainCompactResult = {
279
+ results: [{ id: 'O-1', type: 'observation', title: 't', date: '2026-05-21' }],
280
+ total: 1,
281
+ tokensEstimated: 50,
282
+ };
283
+ expect(result.results).toHaveLength(1);
284
+ expect(result.tokensEstimated).toBe(50);
285
+ });
286
+
287
+ it('BrainAnchor and TimelineBrainResult compose correctly', () => {
288
+ const anchor: BrainAnchor = { id: 'O-anchor', type: 'observation', data: { x: 1 } };
289
+ const result: TimelineBrainResult = {
290
+ anchor,
291
+ before: [{ id: 'O-prev', type: 'observation', date: '2026-05-20' }],
292
+ after: [{ id: 'O-next', type: 'observation', date: '2026-05-22' }],
293
+ };
294
+ expect(result.anchor?.id).toBe('O-anchor');
295
+ expect(result.before).toHaveLength(1);
296
+ expect(result.after).toHaveLength(1);
297
+ });
298
+
299
+ it('TimelineBrainResult accepts null anchor for unresolved IDs', () => {
300
+ const result: TimelineBrainResult = { anchor: null, before: [], after: [] };
301
+ expect(result.anchor).toBeNull();
302
+ });
303
+
304
+ it('FetchBrainEntriesResult tracks notFound IDs alongside resolved entries', () => {
305
+ const result: FetchBrainEntriesResult = {
306
+ results: [{ id: 'O-1', type: 'observation', data: null }],
307
+ notFound: ['O-missing'],
308
+ tokensEstimated: 100,
309
+ };
310
+ expect(result.results).toHaveLength(1);
311
+ expect(result.notFound).toContain('O-missing');
312
+ });
313
+
314
+ it('BRAIN_OBSERVATION_SOURCE_TYPES is the runtime tuple backing BrainObservationSourceType', () => {
315
+ expect(BRAIN_OBSERVATION_SOURCE_TYPES).toEqual([
316
+ 'agent',
317
+ 'session-debrief',
318
+ 'claude-mem',
319
+ 'manual',
320
+ ]);
321
+ // The derived type must include every const member.
322
+ const sample: BrainObservationSourceType[] = [...BRAIN_OBSERVATION_SOURCE_TYPES];
323
+ expect(sample).toHaveLength(4);
324
+ });
325
+
326
+ it('ObserveBrainParams accepts the minimal required shape (text only)', () => {
327
+ const params: ObserveBrainParams = { text: 'hello world' };
328
+ expect(params.text).toBe('hello world');
329
+ });
330
+
331
+ it('ObserveBrainResult mirrors the persisted row identity tuple', () => {
332
+ const result: ObserveBrainResult = {
333
+ id: 'O-new',
334
+ type: 'observation',
335
+ createdAt: '2026-05-21T12:00:00Z',
336
+ };
337
+ expect(result.id).toBe('O-new');
338
+ expect(result.type).toBe('observation');
339
+ });
340
+
341
+ it('BudgetedRetrievalOptions composes all three optional filter axes', () => {
342
+ const opts: BudgetedRetrievalOptions = {
343
+ types: ['semantic', 'procedural'],
344
+ tiers: ['medium', 'long'],
345
+ verified: true,
346
+ };
347
+ expect(opts.types).toHaveLength(2);
348
+ expect(opts.tiers).toHaveLength(2);
349
+ expect(opts.verified).toBe(true);
350
+ });
351
+
352
+ it('BudgetedResult tracks token usage, remaining budget, and exclusion count', () => {
353
+ const result: BudgetedResult = {
354
+ entries: [
355
+ {
356
+ id: 'L-1',
357
+ type: 'learning',
358
+ title: 'insight',
359
+ text: 'body',
360
+ score: 0.42,
361
+ tokensEstimated: 32,
362
+ },
363
+ ],
364
+ tokensUsed: 32,
365
+ tokensRemaining: 468,
366
+ excluded: 0,
367
+ };
368
+ expect(result.entries).toHaveLength(1);
369
+ expect(result.tokensUsed + result.tokensRemaining).toBe(500);
370
+ });
371
+ });
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Structural-equivalence tests for the dispatch operation-def contracts.
3
+ *
4
+ * These tests pin the field shapes of {@link OperationDef} and
5
+ * {@link Resolution} (and the underlying identity types {@link Gateway},
6
+ * {@link Tier}, {@link CanonicalDomain}) so accidental narrowing or
7
+ * widening triggers a compile-time failure during `tsc -b` in the CI
8
+ * gate.
9
+ *
10
+ * The compile-time assertions use the conditional-equality trick
11
+ * (`Equals<A, B>`) so any structural drift produces a TS2322 or TS2344
12
+ * at build time. The runtime `expect` shape sanity check below is a
13
+ * thin smoke verification that constructible literals satisfy each
14
+ * interface — it does NOT exercise behavior (these are pure type
15
+ * contracts with no runtime).
16
+ *
17
+ * @since SG-ARCH-SOLID Saga T9831 · E-CONTRACTS-FOUNDATION T9832 · T9954 (Phase 0b)
18
+ */
19
+
20
+ import { describe, expect, it } from 'vitest';
21
+ import type { CanonicalDomain, Gateway, Tier } from '../dispatch/identity.js';
22
+ import { CANONICAL_DOMAINS } from '../dispatch/identity.js';
23
+ import type { OperationDef, Resolution } from '../dispatch/operation-def.js';
24
+ import type { ParamDef } from '../operations/params.js';
25
+
26
+ // ─── Compile-time structural-equality helpers ───────────────────────
27
+
28
+ /** Resolve to `1` IFF `A` and `B` are mutually assignable; `2` otherwise. */
29
+ type Equals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? 1 : 2;
30
+
31
+ /** Compile-time assert that `T` resolves to `1`. */
32
+ type AssertEquals1<T extends 1> = T;
33
+
34
+ // ─── Gateway shape pin ──────────────────────────────────────────────
35
+
36
+ type _GatewayShape = 'query' | 'mutate';
37
+
38
+ type _AssertGatewayPinned = AssertEquals1<Equals<Gateway, _GatewayShape>>;
39
+
40
+ // ─── Tier shape pin ─────────────────────────────────────────────────
41
+
42
+ type _TierShape = 0 | 1 | 2;
43
+
44
+ type _AssertTierPinned = AssertEquals1<Equals<Tier, _TierShape>>;
45
+
46
+ // ─── CanonicalDomain shape pin ──────────────────────────────────────
47
+ // CanonicalDomain is derived from the runtime tuple CANONICAL_DOMAINS;
48
+ // pinning the tuple element type guards against silent drift if someone
49
+ // reorders the array but accidentally introduces a `string`-widened
50
+ // element (e.g. `... as string[]`).
51
+
52
+ type _CanonicalDomainShape = (typeof CANONICAL_DOMAINS)[number];
53
+
54
+ type _AssertCanonicalDomainPinned = AssertEquals1<Equals<CanonicalDomain, _CanonicalDomainShape>>;
55
+
56
+ // ─── OperationDef shape pin ─────────────────────────────────────────
57
+
58
+ type _OperationDefShape = {
59
+ gateway: Gateway;
60
+ domain: CanonicalDomain;
61
+ operation: string;
62
+ description: string;
63
+ tier: Tier;
64
+ idempotent: boolean;
65
+ sessionRequired: boolean;
66
+ requiredParams: string[];
67
+ params?: ParamDef[];
68
+ };
69
+
70
+ type _AssertOperationDefPinned = AssertEquals1<Equals<OperationDef, _OperationDefShape>>;
71
+
72
+ // ─── Resolution shape pin ───────────────────────────────────────────
73
+
74
+ type _ResolutionShape = {
75
+ domain: CanonicalDomain;
76
+ operation: string;
77
+ def: OperationDef;
78
+ };
79
+
80
+ type _AssertResolutionPinned = AssertEquals1<Equals<Resolution, _ResolutionShape>>;
81
+
82
+ // ─── Runtime constructibility smoke ─────────────────────────────────
83
+
84
+ describe('dispatch/operation-def contracts', () => {
85
+ it('Gateway union covers exactly the 2 documented values', () => {
86
+ const values: Gateway[] = ['query', 'mutate'];
87
+ expect(values).toHaveLength(2);
88
+ });
89
+
90
+ it('Tier union covers exactly the 3 documented values', () => {
91
+ const tiers: Tier[] = [0, 1, 2];
92
+ expect(tiers).toHaveLength(3);
93
+ });
94
+
95
+ it('CANONICAL_DOMAINS is a non-empty readonly tuple', () => {
96
+ expect(CANONICAL_DOMAINS.length).toBeGreaterThan(0);
97
+ // T9954 — snapshot the current count so accidental additions/removals
98
+ // trip this test and force an explicit human review.
99
+ expect(CANONICAL_DOMAINS.length).toBe(22);
100
+ });
101
+
102
+ it('CANONICAL_DOMAINS contains the four sentinel newly-promoted domains', () => {
103
+ // Spot-check the canonical surface: T964 conduit, T1726 sentient/release,
104
+ // T9528 provenance, T9536 upgrade. Catches the most common drift where
105
+ // someone deletes one and breaks SDK consumers downstream.
106
+ expect(CANONICAL_DOMAINS).toContain('conduit');
107
+ expect(CANONICAL_DOMAINS).toContain('sentient');
108
+ expect(CANONICAL_DOMAINS).toContain('release');
109
+ expect(CANONICAL_DOMAINS).toContain('provenance');
110
+ expect(CANONICAL_DOMAINS).toContain('upgrade');
111
+ });
112
+
113
+ it('OperationDef is constructible with the canonical shape (no params field)', () => {
114
+ const def: OperationDef = {
115
+ gateway: 'query',
116
+ domain: 'tasks',
117
+ operation: 'show',
118
+ description: 'tasks.show (query)',
119
+ tier: 0,
120
+ idempotent: true,
121
+ sessionRequired: false,
122
+ requiredParams: ['taskId'],
123
+ };
124
+ expect(def.gateway).toBe('query');
125
+ expect(def.domain).toBe('tasks');
126
+ expect(def.params).toBeUndefined();
127
+ });
128
+
129
+ it('OperationDef is constructible with the optional params field populated', () => {
130
+ const def: OperationDef = {
131
+ gateway: 'mutate',
132
+ domain: 'tasks',
133
+ operation: 'add',
134
+ description: 'tasks.add (mutate)',
135
+ tier: 0,
136
+ idempotent: false,
137
+ sessionRequired: false,
138
+ requiredParams: ['title'],
139
+ params: [
140
+ {
141
+ name: 'title',
142
+ type: 'string',
143
+ required: true,
144
+ description: 'Title of the task',
145
+ },
146
+ ],
147
+ };
148
+ expect(def.params).toHaveLength(1);
149
+ expect(def.params?.[0].name).toBe('title');
150
+ });
151
+
152
+ it('Resolution wraps a domain + operation + def triple', () => {
153
+ const def: OperationDef = {
154
+ gateway: 'query',
155
+ domain: 'session',
156
+ operation: 'status',
157
+ description: 'session.status (query)',
158
+ tier: 0,
159
+ idempotent: true,
160
+ sessionRequired: false,
161
+ requiredParams: [],
162
+ };
163
+ const r: Resolution = {
164
+ domain: 'session',
165
+ operation: 'status',
166
+ def,
167
+ };
168
+ expect(r.domain).toBe('session');
169
+ expect(r.def).toBe(def);
170
+ });
171
+
172
+ // The five `_Assert…Pinned` aliases above will fail compilation if
173
+ // any shape drifts. The following references prevent unused-locals
174
+ // diagnostics from removing them.
175
+ it('compile-time pins are wired (no-op at runtime)', () => {
176
+ const pinned: [
177
+ _AssertGatewayPinned,
178
+ _AssertTierPinned,
179
+ _AssertCanonicalDomainPinned,
180
+ _AssertOperationDefPinned,
181
+ _AssertResolutionPinned,
182
+ ] = [1, 1, 1, 1, 1];
183
+ expect(pinned).toEqual([1, 1, 1, 1, 1]);
184
+ });
185
+ });