@cleocode/contracts 2026.5.96 → 2026.5.98
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/dist/__tests__/enums.test.d.ts +14 -0
- package/dist/__tests__/enums.test.d.ts.map +1 -0
- package/dist/__tests__/enums.test.js +75 -0
- package/dist/__tests__/enums.test.js.map +1 -0
- package/dist/__tests__/jobs.test.d.ts +11 -0
- package/dist/__tests__/jobs.test.d.ts.map +1 -0
- package/dist/__tests__/jobs.test.js +48 -0
- package/dist/__tests__/jobs.test.js.map +1 -0
- package/dist/__tests__/memory-wire-shapes.test.d.ts +19 -0
- package/dist/__tests__/memory-wire-shapes.test.d.ts.map +1 -0
- package/dist/__tests__/memory-wire-shapes.test.js +119 -0
- package/dist/__tests__/memory-wire-shapes.test.js.map +1 -0
- package/dist/__tests__/operation-def.test.d.ts +20 -0
- package/dist/__tests__/operation-def.test.d.ts.map +1 -0
- package/dist/__tests__/operation-def.test.js +111 -0
- package/dist/__tests__/operation-def.test.js.map +1 -0
- package/dist/__tests__/provenance.test.d.ts +18 -0
- package/dist/__tests__/provenance.test.d.ts.map +1 -0
- package/dist/__tests__/provenance.test.js +142 -0
- package/dist/__tests__/provenance.test.js.map +1 -0
- package/dist/__tests__/scaffold-diagnostics.test.d.ts +19 -0
- package/dist/__tests__/scaffold-diagnostics.test.d.ts.map +1 -0
- package/dist/__tests__/scaffold-diagnostics.test.js +70 -0
- package/dist/__tests__/scaffold-diagnostics.test.js.map +1 -0
- package/dist/dispatch/identity.d.ts +72 -0
- package/dist/dispatch/identity.d.ts.map +1 -0
- package/dist/dispatch/identity.js +72 -0
- package/dist/dispatch/identity.js.map +1 -0
- package/dist/dispatch/operation-def.d.ts +92 -0
- package/dist/dispatch/operation-def.d.ts.map +1 -0
- package/dist/dispatch/operation-def.js +31 -0
- package/dist/dispatch/operation-def.js.map +1 -0
- package/dist/doctor.d.ts +43 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/enums.d.ts +123 -0
- package/dist/enums.d.ts.map +1 -0
- package/dist/enums.js +139 -0
- package/dist/enums.js.map +1 -0
- package/dist/index.d.ts +15 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +39 -0
- package/dist/jobs.d.ts.map +1 -0
- package/dist/jobs.js +19 -0
- package/dist/jobs.js.map +1 -0
- package/dist/memory/budgeted.d.ts +67 -0
- package/dist/memory/budgeted.d.ts.map +1 -0
- package/dist/memory/budgeted.js +17 -0
- package/dist/memory/budgeted.js.map +1 -0
- package/dist/memory/fetch.d.ts +58 -0
- package/dist/memory/fetch.d.ts.map +1 -0
- package/dist/memory/fetch.js +19 -0
- package/dist/memory/fetch.js.map +1 -0
- package/dist/memory/observe.d.ts +158 -0
- package/dist/memory/observe.d.ts.map +1 -0
- package/dist/memory/observe.js +46 -0
- package/dist/memory/observe.js.map +1 -0
- package/dist/memory/search.d.ts +137 -0
- package/dist/memory/search.d.ts.map +1 -0
- package/dist/memory/search.js +22 -0
- package/dist/memory/search.js.map +1 -0
- package/dist/memory/timeline.d.ts +89 -0
- package/dist/memory/timeline.d.ts.map +1 -0
- package/dist/memory/timeline.js +22 -0
- package/dist/memory/timeline.js.map +1 -0
- package/dist/operations/focus.d.ts +199 -0
- package/dist/operations/focus.d.ts.map +1 -0
- package/dist/operations/focus.js +15 -0
- package/dist/operations/focus.js.map +1 -0
- package/dist/operations/index.d.ts +1 -0
- package/dist/operations/index.d.ts.map +1 -1
- package/dist/operations/index.js +1 -0
- package/dist/operations/index.js.map +1 -1
- package/dist/operations/session.d.ts +65 -0
- package/dist/operations/session.d.ts.map +1 -1
- package/dist/operations/tasks.d.ts +38 -0
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/provenance.d.ts +257 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +42 -0
- package/dist/provenance.js.map +1 -0
- package/dist/release/plan.d.ts +7 -7
- package/dist/scaffold-diagnostics.d.ts +110 -0
- package/dist/scaffold-diagnostics.d.ts.map +1 -0
- package/dist/scaffold-diagnostics.js +28 -0
- package/dist/scaffold-diagnostics.js.map +1 -0
- package/dist/session.d.ts +37 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js.map +1 -1
- package/dist/tasks/archive.d.ts +3 -3
- package/package.json +42 -2
- package/src/__tests__/enums.test.ts +114 -0
- package/src/__tests__/jobs.test.ts +76 -0
- package/src/__tests__/memory-wire-shapes.test.ts +371 -0
- package/src/__tests__/operation-def.test.ts +185 -0
- package/src/__tests__/provenance.test.ts +259 -0
- package/src/__tests__/scaffold-diagnostics.test.ts +137 -0
- package/src/dispatch/identity.ts +109 -0
- package/src/dispatch/operation-def.ts +102 -0
- package/src/doctor.ts +44 -0
- package/src/enums.ts +144 -0
- package/src/index.ts +79 -1
- package/src/jobs.ts +45 -0
- package/src/memory/budgeted.ts +75 -0
- package/src/memory/fetch.ts +66 -0
- package/src/memory/observe.ts +176 -0
- package/src/memory/search.ts +145 -0
- package/src/memory/timeline.ts +100 -0
- package/src/operations/focus.ts +226 -0
- package/src/operations/index.ts +1 -0
- package/src/operations/session.ts +67 -0
- package/src/operations/tasks.ts +40 -0
- package/src/provenance.ts +335 -0
- package/src/scaffold-diagnostics.ts +119 -0
- package/src/session.ts +37 -0
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural-equivalence tests for the provenance graph contracts.
|
|
3
|
+
*
|
|
4
|
+
* These tests pin the literal shapes of the 16 provenance/release/BRAIN
|
|
5
|
+
* unions promoted in Phase 0c (T9955) so that accidental narrowing or
|
|
6
|
+
* widening produces a compile-time failure during `tsc -b` in the CI gate.
|
|
7
|
+
*
|
|
8
|
+
* The compile-time assertions use the conditional-equality trick
|
|
9
|
+
* (`Equals<A, B>`) so any structural drift produces a TS2322 or TS2344 at
|
|
10
|
+
* build time. The runtime `expect` shape sanity check below is a thin
|
|
11
|
+
* smoke verification that representative literals satisfy each union — it
|
|
12
|
+
* does NOT exercise behavior (these are pure type contracts with no
|
|
13
|
+
* runtime).
|
|
14
|
+
*
|
|
15
|
+
* @since SG-ARCH-SOLID Saga T9831 · E-CONTRACTS-FOUNDATION T9832 · T9955 (Phase 0c)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { describe, expect, it } from 'vitest';
|
|
19
|
+
import type {
|
|
20
|
+
BrainReleaseLinkType,
|
|
21
|
+
CommitConventionalType,
|
|
22
|
+
CommitFileChangeType,
|
|
23
|
+
CommitLinkKind,
|
|
24
|
+
CommitLinkSource,
|
|
25
|
+
PrLinkKind,
|
|
26
|
+
PrLinkSource,
|
|
27
|
+
PrState,
|
|
28
|
+
ReleaseArtifactType,
|
|
29
|
+
ReleaseChangeType,
|
|
30
|
+
ReleaseChannel,
|
|
31
|
+
ReleaseClassifiedBy,
|
|
32
|
+
ReleaseImpact,
|
|
33
|
+
ReleaseKind,
|
|
34
|
+
ReleaseScheme,
|
|
35
|
+
ReleaseStatus,
|
|
36
|
+
} from '../provenance.js';
|
|
37
|
+
|
|
38
|
+
// ─── Compile-time structural-equality helpers ───────────────────────
|
|
39
|
+
|
|
40
|
+
/** Resolve to `1` IFF `A` and `B` are mutually assignable; `2` otherwise. */
|
|
41
|
+
type Equals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? 1 : 2;
|
|
42
|
+
|
|
43
|
+
/** Compile-time assert that `T` resolves to `1`. */
|
|
44
|
+
type AssertEquals1<T extends 1> = T;
|
|
45
|
+
|
|
46
|
+
// ─── PrState pin ────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
type _PrStateShape = 'open' | 'closed' | 'merged';
|
|
49
|
+
type _AssertPrStatePinned = AssertEquals1<Equals<PrState, _PrStateShape>>;
|
|
50
|
+
|
|
51
|
+
// ─── CommitLinkKind pin ─────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
type _CommitLinkKindShape = 'implements' | 'fixes' | 'refactors' | 'tests' | 'docs' | 'reverts';
|
|
54
|
+
type _AssertCommitLinkKindPinned = AssertEquals1<Equals<CommitLinkKind, _CommitLinkKindShape>>;
|
|
55
|
+
|
|
56
|
+
// ─── ReleaseStatus pin (unified — admits both pipelines) ────────────
|
|
57
|
+
|
|
58
|
+
type _ReleaseStatusShape =
|
|
59
|
+
| 'planned'
|
|
60
|
+
| 'pr-opened'
|
|
61
|
+
| 'pr-merged'
|
|
62
|
+
| 'published'
|
|
63
|
+
| 'reconciled'
|
|
64
|
+
| 'prepared'
|
|
65
|
+
| 'committed'
|
|
66
|
+
| 'tagged'
|
|
67
|
+
| 'pushed'
|
|
68
|
+
| 'rolled_back'
|
|
69
|
+
| 'failed'
|
|
70
|
+
| 'cancelled';
|
|
71
|
+
type _AssertReleaseStatusPinned = AssertEquals1<Equals<ReleaseStatus, _ReleaseStatusShape>>;
|
|
72
|
+
|
|
73
|
+
// ─── ReleaseChangeType pin (12-value taxonomy) ──────────────────────
|
|
74
|
+
|
|
75
|
+
type _ReleaseChangeTypeShape =
|
|
76
|
+
| 'feature'
|
|
77
|
+
| 'enhancement'
|
|
78
|
+
| 'bug'
|
|
79
|
+
| 'hotfix'
|
|
80
|
+
| 'security'
|
|
81
|
+
| 'breaking'
|
|
82
|
+
| 'refactor'
|
|
83
|
+
| 'docs'
|
|
84
|
+
| 'chore'
|
|
85
|
+
| 'revert'
|
|
86
|
+
| 'deprecation'
|
|
87
|
+
| 'infrastructure';
|
|
88
|
+
type _AssertReleaseChangeTypePinned = AssertEquals1<
|
|
89
|
+
Equals<ReleaseChangeType, _ReleaseChangeTypeShape>
|
|
90
|
+
>;
|
|
91
|
+
|
|
92
|
+
// ─── ReleaseArtifactType pin ────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
type _ReleaseArtifactTypeShape =
|
|
95
|
+
| 'npm'
|
|
96
|
+
| 'cargo'
|
|
97
|
+
| 'docker'
|
|
98
|
+
| 'pypi'
|
|
99
|
+
| 'github-release'
|
|
100
|
+
| 'binary'
|
|
101
|
+
| 'github-tag';
|
|
102
|
+
type _AssertReleaseArtifactTypePinned = AssertEquals1<
|
|
103
|
+
Equals<ReleaseArtifactType, _ReleaseArtifactTypeShape>
|
|
104
|
+
>;
|
|
105
|
+
|
|
106
|
+
// ─── BrainReleaseLinkType pin ───────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
type _BrainReleaseLinkTypeShape = 'approved-by' | 'documented-in' | 'derived-from' | 'observed-in';
|
|
109
|
+
type _AssertBrainReleaseLinkTypePinned = AssertEquals1<
|
|
110
|
+
Equals<BrainReleaseLinkType, _BrainReleaseLinkTypeShape>
|
|
111
|
+
>;
|
|
112
|
+
|
|
113
|
+
// ─── Runtime constructibility smoke ─────────────────────────────────
|
|
114
|
+
|
|
115
|
+
describe('provenance contracts', () => {
|
|
116
|
+
it('PrState union covers exactly the 3 GitHub PR states', () => {
|
|
117
|
+
const all: PrState[] = ['open', 'closed', 'merged'];
|
|
118
|
+
expect(all).toHaveLength(3);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('PrLinkSource enumerates all 5 discovery sources', () => {
|
|
122
|
+
const all: PrLinkSource[] = ['pr-title', 'pr-body', 'branch-name', 'commit-trailer', 'manual'];
|
|
123
|
+
expect(all).toHaveLength(5);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('PrLinkKind extends CommitLinkKind with the "tracks" addition', () => {
|
|
127
|
+
const commit: CommitLinkKind = 'implements';
|
|
128
|
+
const pr: PrLinkKind = commit; // every commit-link-kind is a valid pr-link-kind
|
|
129
|
+
expect(pr).toBe('implements');
|
|
130
|
+
const tracks: PrLinkKind = 'tracks';
|
|
131
|
+
expect(tracks).toBe('tracks');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('CommitConventionalType enumerates the canonical CC prefixes plus "breaking"', () => {
|
|
135
|
+
const ccs: CommitConventionalType[] = [
|
|
136
|
+
'feat',
|
|
137
|
+
'fix',
|
|
138
|
+
'chore',
|
|
139
|
+
'docs',
|
|
140
|
+
'refactor',
|
|
141
|
+
'test',
|
|
142
|
+
'build',
|
|
143
|
+
'ci',
|
|
144
|
+
'perf',
|
|
145
|
+
'revert',
|
|
146
|
+
'breaking',
|
|
147
|
+
];
|
|
148
|
+
expect(ccs).toHaveLength(11);
|
|
149
|
+
expect(ccs).toContain('breaking');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('CommitLinkSource and CommitLinkKind are independent unions', () => {
|
|
153
|
+
const src: CommitLinkSource = 'commit-trailer';
|
|
154
|
+
const kind: CommitLinkKind = 'implements';
|
|
155
|
+
expect(src).toBe('commit-trailer');
|
|
156
|
+
expect(kind).toBe('implements');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('CommitFileChangeType matches the git status letter codes', () => {
|
|
160
|
+
const codes: CommitFileChangeType[] = ['A', 'M', 'D', 'R', 'C'];
|
|
161
|
+
expect(codes).toHaveLength(5);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('ReleaseScheme enumerates calver/semver/calver-suffix', () => {
|
|
165
|
+
const schemes: ReleaseScheme[] = ['calver', 'semver', 'calver-suffix'];
|
|
166
|
+
expect(schemes).toHaveLength(3);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('ReleaseChannel enumerates the provenance-layer channels', () => {
|
|
170
|
+
const channels: ReleaseChannel[] = ['latest', 'beta', 'dev', 'hotfix'];
|
|
171
|
+
expect(channels).toHaveLength(4);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('ReleaseKind enumerates regular/hotfix/prerelease', () => {
|
|
175
|
+
const kinds: ReleaseKind[] = ['regular', 'hotfix', 'prerelease'];
|
|
176
|
+
expect(kinds).toHaveLength(3);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('ReleaseStatus admits BOTH new-pipeline and legacy-pipeline statuses', () => {
|
|
180
|
+
const newPipeline: ReleaseStatus[] = [
|
|
181
|
+
'planned',
|
|
182
|
+
'pr-opened',
|
|
183
|
+
'pr-merged',
|
|
184
|
+
'published',
|
|
185
|
+
'reconciled',
|
|
186
|
+
];
|
|
187
|
+
const legacyPipeline: ReleaseStatus[] = ['prepared', 'committed', 'tagged', 'pushed'];
|
|
188
|
+
const terminals: ReleaseStatus[] = ['rolled_back', 'failed', 'cancelled'];
|
|
189
|
+
expect(newPipeline).toHaveLength(5);
|
|
190
|
+
expect(legacyPipeline).toHaveLength(4);
|
|
191
|
+
expect(terminals).toHaveLength(3);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('ReleaseChangeType enumerates the 12 CLEO change types', () => {
|
|
195
|
+
const types: ReleaseChangeType[] = [
|
|
196
|
+
'feature',
|
|
197
|
+
'enhancement',
|
|
198
|
+
'bug',
|
|
199
|
+
'hotfix',
|
|
200
|
+
'security',
|
|
201
|
+
'breaking',
|
|
202
|
+
'refactor',
|
|
203
|
+
'docs',
|
|
204
|
+
'chore',
|
|
205
|
+
'revert',
|
|
206
|
+
'deprecation',
|
|
207
|
+
'infrastructure',
|
|
208
|
+
];
|
|
209
|
+
expect(types).toHaveLength(12);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('ReleaseImpact enumerates the 4 semver-bump assessments', () => {
|
|
213
|
+
const impacts: ReleaseImpact[] = ['major', 'minor', 'patch', 'none'];
|
|
214
|
+
expect(impacts).toHaveLength(4);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('ReleaseClassifiedBy enumerates auto/manual/approved provenance', () => {
|
|
218
|
+
const provenance: ReleaseClassifiedBy[] = ['auto', 'manual', 'approved'];
|
|
219
|
+
expect(provenance).toHaveLength(3);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('ReleaseArtifactType enumerates the 7 supported artifact archetypes', () => {
|
|
223
|
+
const types: ReleaseArtifactType[] = [
|
|
224
|
+
'npm',
|
|
225
|
+
'cargo',
|
|
226
|
+
'docker',
|
|
227
|
+
'pypi',
|
|
228
|
+
'github-release',
|
|
229
|
+
'binary',
|
|
230
|
+
'github-tag',
|
|
231
|
+
];
|
|
232
|
+
expect(types).toHaveLength(7);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('BrainReleaseLinkType enumerates the 4 BRAIN↔release semantics', () => {
|
|
236
|
+
const links: BrainReleaseLinkType[] = [
|
|
237
|
+
'approved-by',
|
|
238
|
+
'documented-in',
|
|
239
|
+
'derived-from',
|
|
240
|
+
'observed-in',
|
|
241
|
+
];
|
|
242
|
+
expect(links).toHaveLength(4);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// The five `_Assert…Pinned` aliases above will fail compilation if any
|
|
246
|
+
// shape drifts. The following references prevent unused-locals
|
|
247
|
+
// diagnostics from removing them.
|
|
248
|
+
it('compile-time pins are wired (no-op at runtime)', () => {
|
|
249
|
+
const pinned: [
|
|
250
|
+
_AssertPrStatePinned,
|
|
251
|
+
_AssertCommitLinkKindPinned,
|
|
252
|
+
_AssertReleaseStatusPinned,
|
|
253
|
+
_AssertReleaseChangeTypePinned,
|
|
254
|
+
_AssertReleaseArtifactTypePinned,
|
|
255
|
+
_AssertBrainReleaseLinkTypePinned,
|
|
256
|
+
] = [1, 1, 1, 1, 1, 1];
|
|
257
|
+
expect(pinned).toEqual([1, 1, 1, 1, 1, 1]);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural-equivalence tests for the scaffold-diagnostics contracts.
|
|
3
|
+
*
|
|
4
|
+
* These tests pin the field shapes of {@link ScaffoldResult},
|
|
5
|
+
* {@link CheckStatus}, {@link CheckResult}, and {@link HookCheckResult}
|
|
6
|
+
* so accidental narrowing or widening triggers a compile-time failure
|
|
7
|
+
* 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` shape sanity check below is a
|
|
12
|
+
* thin smoke verification that constructible literals satisfy each
|
|
13
|
+
* interface — it does NOT exercise behavior (these are pure type
|
|
14
|
+
* contracts with no runtime).
|
|
15
|
+
*
|
|
16
|
+
* @since SG-ARCH-SOLID Saga T9831 · E-CONTRACTS-FOUNDATION T9832 (Phase 0a)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { describe, expect, it } from 'vitest';
|
|
20
|
+
import type {
|
|
21
|
+
CheckResult,
|
|
22
|
+
CheckStatus,
|
|
23
|
+
HookCheckResult,
|
|
24
|
+
ScaffoldResult,
|
|
25
|
+
} from '../scaffold-diagnostics.js';
|
|
26
|
+
|
|
27
|
+
// ─── Compile-time structural-equality helpers ───────────────────────
|
|
28
|
+
|
|
29
|
+
/** Resolve to `1` IFF `A` and `B` are mutually assignable; `2` otherwise. */
|
|
30
|
+
type Equals<A, B> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? 1 : 2;
|
|
31
|
+
|
|
32
|
+
/** Compile-time assert that `T` resolves to `1`. */
|
|
33
|
+
type AssertEquals1<T extends 1> = T;
|
|
34
|
+
|
|
35
|
+
// ─── ScaffoldResult shape pin ───────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
type _ScaffoldResultShape = {
|
|
38
|
+
action: 'created' | 'repaired' | 'skipped';
|
|
39
|
+
path: string;
|
|
40
|
+
details?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type _AssertScaffoldResultPinned = AssertEquals1<Equals<ScaffoldResult, _ScaffoldResultShape>>;
|
|
44
|
+
|
|
45
|
+
// ─── CheckStatus shape pin ──────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
type _CheckStatusShape = 'passed' | 'failed' | 'warning' | 'info';
|
|
48
|
+
|
|
49
|
+
type _AssertCheckStatusPinned = AssertEquals1<Equals<CheckStatus, _CheckStatusShape>>;
|
|
50
|
+
|
|
51
|
+
// ─── CheckResult shape pin ──────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
type _CheckResultShape = {
|
|
54
|
+
id: string;
|
|
55
|
+
category: string;
|
|
56
|
+
status: CheckStatus;
|
|
57
|
+
message: string;
|
|
58
|
+
details: Record<string, unknown>;
|
|
59
|
+
fix: string | null;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type _AssertCheckResultPinned = AssertEquals1<Equals<CheckResult, _CheckResultShape>>;
|
|
63
|
+
|
|
64
|
+
// ─── HookCheckResult shape pin ──────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
type _HookCheckResultShape = {
|
|
67
|
+
hook: string;
|
|
68
|
+
installed: boolean;
|
|
69
|
+
current: boolean;
|
|
70
|
+
sourcePath: string;
|
|
71
|
+
installedPath: string;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
type _AssertHookCheckResultPinned = AssertEquals1<Equals<HookCheckResult, _HookCheckResultShape>>;
|
|
75
|
+
|
|
76
|
+
// ─── Runtime constructibility smoke ─────────────────────────────────
|
|
77
|
+
|
|
78
|
+
describe('scaffold-diagnostics contracts', () => {
|
|
79
|
+
it('ScaffoldResult is constructible with the canonical shape', () => {
|
|
80
|
+
const r: ScaffoldResult = { action: 'created', path: '/tmp/x' };
|
|
81
|
+
expect(r.action).toBe('created');
|
|
82
|
+
expect(r.path).toBe('/tmp/x');
|
|
83
|
+
expect(r.details).toBeUndefined();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('ScaffoldResult accepts the optional details field', () => {
|
|
87
|
+
const r: ScaffoldResult = {
|
|
88
|
+
action: 'repaired',
|
|
89
|
+
path: '/tmp/y',
|
|
90
|
+
details: 'rewrote stale entry',
|
|
91
|
+
};
|
|
92
|
+
expect(r.details).toBe('rewrote stale entry');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('CheckResult is constructible with the canonical shape', () => {
|
|
96
|
+
const r: CheckResult = {
|
|
97
|
+
id: 'cleo_structure',
|
|
98
|
+
category: 'scaffold',
|
|
99
|
+
status: 'passed',
|
|
100
|
+
message: 'All subdirs present',
|
|
101
|
+
details: { missing: [] },
|
|
102
|
+
fix: null,
|
|
103
|
+
};
|
|
104
|
+
expect(r.status).toBe('passed');
|
|
105
|
+
expect(r.fix).toBeNull();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('CheckStatus union covers exactly the 4 documented values', () => {
|
|
109
|
+
const statuses: CheckStatus[] = ['passed', 'failed', 'warning', 'info'];
|
|
110
|
+
expect(statuses).toHaveLength(4);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('HookCheckResult is constructible with the canonical shape', () => {
|
|
114
|
+
const r: HookCheckResult = {
|
|
115
|
+
hook: 'pre-commit',
|
|
116
|
+
installed: true,
|
|
117
|
+
current: false,
|
|
118
|
+
sourcePath: '/pkg/templates/git-hooks/pre-commit',
|
|
119
|
+
installedPath: '/proj/.git/hooks/pre-commit',
|
|
120
|
+
};
|
|
121
|
+
expect(r.installed).toBe(true);
|
|
122
|
+
expect(r.current).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// The four `_Assert…Pinned` aliases above will fail compilation if
|
|
126
|
+
// any shape drifts. The following references prevent unused-locals
|
|
127
|
+
// diagnostics from removing them.
|
|
128
|
+
it('compile-time pins are wired (no-op at runtime)', () => {
|
|
129
|
+
const pinned: [
|
|
130
|
+
_AssertScaffoldResultPinned,
|
|
131
|
+
_AssertCheckStatusPinned,
|
|
132
|
+
_AssertCheckResultPinned,
|
|
133
|
+
_AssertHookCheckResultPinned,
|
|
134
|
+
] = [1, 1, 1, 1];
|
|
135
|
+
expect(pinned).toEqual([1, 1, 1, 1]);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dispatch identity contracts.
|
|
3
|
+
*
|
|
4
|
+
* Canonical home for the three primitive identity types every dispatch
|
|
5
|
+
* operation is keyed on:
|
|
6
|
+
*
|
|
7
|
+
* - {@link Gateway} — CQRS read vs write classification
|
|
8
|
+
* - {@link Tier} — progressive-disclosure tier (0/1/2)
|
|
9
|
+
* - {@link CanonicalDomain} — the closed set of dispatch domains
|
|
10
|
+
*
|
|
11
|
+
* Promoted to `@cleocode/contracts` in Phase 0b of the SG-ARCH-SOLID
|
|
12
|
+
* Saga (T9831 · E-CONTRACTS-FOUNDATION T9832 · T9954) alongside
|
|
13
|
+
* {@link OperationDef} / {@link Resolution}. Originally defined in
|
|
14
|
+
* `packages/cleo/src/dispatch/types.ts` (lines 16, 27, 58, 86). The
|
|
15
|
+
* `packages/cleo` definition is now a re-export shim — every consumer
|
|
16
|
+
* continues to compile unchanged.
|
|
17
|
+
*
|
|
18
|
+
* `CANONICAL_DOMAINS` remains the runtime SSoT — adding/removing a
|
|
19
|
+
* domain still requires editing the array here exactly as before.
|
|
20
|
+
*
|
|
21
|
+
* @since SG-ARCH-SOLID Saga T9831 · E-CONTRACTS-FOUNDATION T9832 · T9954 (Phase 0b)
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// ── Gateway ──────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* CQRS gateway: read-only queries vs state-modifying mutations.
|
|
28
|
+
*
|
|
29
|
+
* Originally defined in `packages/cleo/src/dispatch/types.ts:16`.
|
|
30
|
+
*
|
|
31
|
+
* @since SG-ARCH-SOLID Saga T9831 · E-CONTRACTS-FOUNDATION T9832 · T9954 (Phase 0b)
|
|
32
|
+
*/
|
|
33
|
+
export type Gateway = 'query' | 'mutate';
|
|
34
|
+
|
|
35
|
+
// ── Tier ─────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Progressive disclosure tier.
|
|
39
|
+
*
|
|
40
|
+
* - `0` — tasks + session (~80% of agents)
|
|
41
|
+
* - `1` — + memory + check (~15% of agents)
|
|
42
|
+
* - `2` — + pipeline + orchestrate + tools + admin + nexus (~5%)
|
|
43
|
+
*
|
|
44
|
+
* Originally defined in `packages/cleo/src/dispatch/types.ts:27`.
|
|
45
|
+
*
|
|
46
|
+
* @since SG-ARCH-SOLID Saga T9831 · E-CONTRACTS-FOUNDATION T9832 · T9954 (Phase 0b)
|
|
47
|
+
*/
|
|
48
|
+
export type Tier = 0 | 1 | 2;
|
|
49
|
+
|
|
50
|
+
// ── CanonicalDomain ──────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The closed set of dispatch canonical-domain names.
|
|
54
|
+
*
|
|
55
|
+
* T964: `conduit` promoted to first-class domain (supersedes ADR-042 Decision 1).
|
|
56
|
+
* CONDUIT is agent-to-agent messaging and is semantically disjoint from
|
|
57
|
+
* ORCHESTRATE (wave planning + spawn-prompt generation). The original
|
|
58
|
+
* "exactly 10 canonical domains" invariant that justified folding CONDUIT
|
|
59
|
+
* under ORCHESTRATE has been broken multiple times (intelligence, diagnostics,
|
|
60
|
+
* docs, playbook); promoting CONDUIT aligns registry with wire-format, CLI,
|
|
61
|
+
* and core module structure at zero behavior cost.
|
|
62
|
+
*
|
|
63
|
+
* T1726: `sentient` and `release` promoted to first-class domains. Both were
|
|
64
|
+
* reachable via the CLI and had registered DomainHandlers but were absent from
|
|
65
|
+
* CANONICAL_DOMAINS, making them invisible to SDK consumers via OPERATIONS.
|
|
66
|
+
*
|
|
67
|
+
* Originally defined in `packages/cleo/src/dispatch/types.ts:58`.
|
|
68
|
+
*
|
|
69
|
+
* @since SG-ARCH-SOLID Saga T9831 · E-CONTRACTS-FOUNDATION T9832 · T9954 (Phase 0b)
|
|
70
|
+
*/
|
|
71
|
+
export const CANONICAL_DOMAINS = [
|
|
72
|
+
'tasks',
|
|
73
|
+
'session',
|
|
74
|
+
'memory',
|
|
75
|
+
'check',
|
|
76
|
+
'pipeline',
|
|
77
|
+
'orchestrate',
|
|
78
|
+
'tools',
|
|
79
|
+
'admin',
|
|
80
|
+
'nexus',
|
|
81
|
+
'sticky',
|
|
82
|
+
'intelligence',
|
|
83
|
+
'diagnostics',
|
|
84
|
+
'docs',
|
|
85
|
+
'playbook',
|
|
86
|
+
'conduit',
|
|
87
|
+
'sentient',
|
|
88
|
+
'release',
|
|
89
|
+
'llm',
|
|
90
|
+
// T9528: provenance-graph maintenance verbs (backfill, verify, repair).
|
|
91
|
+
'provenance',
|
|
92
|
+
// T9536: `cleo upgrade workflows` — re-render release-pipeline workflow
|
|
93
|
+
// templates + 3-way merge with `.workflow-overrides.yml`.
|
|
94
|
+
'upgrade',
|
|
95
|
+
// T9546/T9547: 'cleo worktree list/prune/force-unlock' — worktree lifecycle.
|
|
96
|
+
'worktree',
|
|
97
|
+
// T9973: 'cleo focus <id>' — single-envelope task orientation (8 calls → 1).
|
|
98
|
+
'focus',
|
|
99
|
+
] as const;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* One of the canonical dispatch domain names.
|
|
103
|
+
*
|
|
104
|
+
* Derived as a string-literal union from {@link CANONICAL_DOMAINS} so
|
|
105
|
+
* adding/removing a domain in the array automatically updates the type.
|
|
106
|
+
*
|
|
107
|
+
* @since SG-ARCH-SOLID Saga T9831 · E-CONTRACTS-FOUNDATION T9832 · T9954 (Phase 0b)
|
|
108
|
+
*/
|
|
109
|
+
export type CanonicalDomain = (typeof CANONICAL_DOMAINS)[number];
|