@company-semantics/contracts 0.97.0 → 0.98.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@company-semantics/contracts",
3
- "version": "0.97.0",
3
+ "version": "0.98.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,249 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import type {
3
+ RalphPRD,
4
+ RalphFeatureGroup,
5
+ RalphExecutionPolicy,
6
+ } from '../index.js'
7
+
8
+ describe('RalphFeatureGroup and RalphExecutionPolicy types', () => {
9
+ describe('RalphPRD with groups field', () => {
10
+ it('typechecks with groups containing RalphFeatureGroup entries', () => {
11
+ const prd = {
12
+ version: '2.1',
13
+ repo: 'company-semantics-backend',
14
+ features: [],
15
+ stopCondition: 'All features pass',
16
+ groups: [
17
+ {
18
+ id: 'auth-group',
19
+ description: 'Authentication features',
20
+ features: ['auth/login', 'auth/logout'],
21
+ },
22
+ ],
23
+ } satisfies RalphPRD
24
+
25
+ expect(prd.groups).toHaveLength(1)
26
+ expect(prd.groups![0].id).toBe('auth-group')
27
+ expect(prd.groups![0].features).toEqual(['auth/login', 'auth/logout'])
28
+ })
29
+
30
+ it('typechecks with multiple groups', () => {
31
+ const prd = {
32
+ version: '2.1',
33
+ repo: 'company-semantics-backend',
34
+ features: [],
35
+ stopCondition: 'All features pass',
36
+ groups: [
37
+ {
38
+ id: 'group-a',
39
+ description: 'First group',
40
+ features: ['feat-1'],
41
+ },
42
+ {
43
+ id: 'group-b',
44
+ description: 'Second group',
45
+ features: ['feat-2', 'feat-3'],
46
+ sharedContext: 'Shared context for group B',
47
+ },
48
+ ],
49
+ } satisfies RalphPRD
50
+
51
+ expect(prd.groups).toHaveLength(2)
52
+ })
53
+ })
54
+
55
+ describe('backward compatibility — PRD without groups field', () => {
56
+ it('typechecks without groups field (existing PRD shape)', () => {
57
+ const prd = {
58
+ version: '2.0',
59
+ repo: 'company-semantics-contracts',
60
+ features: [
61
+ {
62
+ id: 'test/feature',
63
+ category: 'functional' as const,
64
+ description: 'A test feature',
65
+ steps: ['Step 1'],
66
+ passes: false,
67
+ priority: 'high' as const,
68
+ },
69
+ ],
70
+ stopCondition: 'All features pass',
71
+ } satisfies RalphPRD
72
+
73
+ expect(prd.groups).toBeUndefined()
74
+ expect(prd.features).toHaveLength(1)
75
+ })
76
+
77
+ it('typechecks with all v2.0 optional fields but no groups', () => {
78
+ const prd = {
79
+ version: '2.0',
80
+ repo: 'company-semantics-backend',
81
+ features: [],
82
+ stopCondition: 'Done',
83
+ agentContract: {
84
+ mode: 'afk' as const,
85
+ allowedActions: ['Edit'],
86
+ forbiddenActions: [],
87
+ autonomyLevel: 'bounded' as const,
88
+ },
89
+ failurePolicy: {
90
+ maxRetries: 2,
91
+ onRepeatedFailure: 'halt_and_report' as const,
92
+ retryScope: 'current_feature_only' as const,
93
+ },
94
+ scope: {
95
+ allowedPaths: ['src/**'],
96
+ forbiddenPaths: ['node_modules/**'],
97
+ refactoringAllowed: true,
98
+ },
99
+ commitPolicy: {
100
+ strategy: 'per_feature' as const,
101
+ commitOn: 'acceptance_pass' as const,
102
+ rollbackOnFailure: 'uncommitted_only' as const,
103
+ onHalt: 'leave_committed' as const,
104
+ logRollback: true,
105
+ },
106
+ } satisfies RalphPRD
107
+
108
+ expect(prd.groups).toBeUndefined()
109
+ expect(prd.agentContract).toBeDefined()
110
+ })
111
+ })
112
+
113
+ describe('RalphExecutionPolicy defaults', () => {
114
+ it('accepts all required fields', () => {
115
+ const policy = {
116
+ parallelism: 1,
117
+ preferredExecutor: 'same',
118
+ serializeFiles: [],
119
+ } satisfies RalphExecutionPolicy
120
+
121
+ expect(policy.parallelism).toBe(1)
122
+ expect(policy.preferredExecutor).toBe('same')
123
+ expect(policy.serializeFiles).toEqual([])
124
+ })
125
+
126
+ it('accepts preferredExecutor: any', () => {
127
+ const policy = {
128
+ parallelism: 4,
129
+ preferredExecutor: 'any',
130
+ serializeFiles: ['src/config.ts'],
131
+ } satisfies RalphExecutionPolicy
132
+
133
+ expect(policy.preferredExecutor).toBe('any')
134
+ expect(policy.serializeFiles).toEqual(['src/config.ts'])
135
+ })
136
+
137
+ it('accepts high parallelism with multiple serialized files', () => {
138
+ const policy = {
139
+ parallelism: 10,
140
+ preferredExecutor: 'any',
141
+ serializeFiles: ['package.json', 'pnpm-lock.yaml', 'src/index.ts'],
142
+ } satisfies RalphExecutionPolicy
143
+
144
+ expect(policy.parallelism).toBe(10)
145
+ expect(policy.serializeFiles).toHaveLength(3)
146
+ })
147
+ })
148
+
149
+ describe('RalphFeatureGroup optional fields', () => {
150
+ it('typechecks with no optional fields', () => {
151
+ const group = {
152
+ id: 'minimal-group',
153
+ description: 'Minimal group with required fields only',
154
+ features: ['feat/one'],
155
+ } satisfies RalphFeatureGroup
156
+
157
+ expect(group.id).toBe('minimal-group')
158
+ expect(group.sharedContext).toBeUndefined()
159
+ expect(group.completionCriteria).toBeUndefined()
160
+ expect(group.executionPolicy).toBeUndefined()
161
+ })
162
+
163
+ it('typechecks with sharedContext only', () => {
164
+ const group = {
165
+ id: 'shared-ctx-group',
166
+ description: 'Group with shared context',
167
+ features: ['feat/a', 'feat/b'],
168
+ sharedContext: 'All features share the auth domain',
169
+ } satisfies RalphFeatureGroup
170
+
171
+ expect(group.sharedContext).toBe('All features share the auth domain')
172
+ })
173
+
174
+ it('typechecks with completionCriteria only', () => {
175
+ const group = {
176
+ id: 'completion-group',
177
+ description: 'Group with completion criteria',
178
+ features: ['feat/x'],
179
+ completionCriteria: 'Integration test suite passes end-to-end',
180
+ } satisfies RalphFeatureGroup
181
+
182
+ expect(group.completionCriteria).toBe(
183
+ 'Integration test suite passes end-to-end',
184
+ )
185
+ })
186
+
187
+ it('typechecks with executionPolicy only', () => {
188
+ const group = {
189
+ id: 'policy-group',
190
+ description: 'Group with execution policy',
191
+ features: ['feat/p', 'feat/q'],
192
+ executionPolicy: {
193
+ parallelism: 2,
194
+ preferredExecutor: 'any',
195
+ serializeFiles: ['shared.ts'],
196
+ },
197
+ } satisfies RalphFeatureGroup
198
+
199
+ expect(group.executionPolicy).toBeDefined()
200
+ expect(group.executionPolicy!.parallelism).toBe(2)
201
+ })
202
+
203
+ it('typechecks with all optional fields present', () => {
204
+ const group = {
205
+ id: 'full-group',
206
+ description: 'Group with all optional fields',
207
+ features: ['feat/1', 'feat/2', 'feat/3'],
208
+ sharedContext: 'All features modify the database schema',
209
+ completionCriteria: 'Migration runs cleanly and all tests pass',
210
+ executionPolicy: {
211
+ parallelism: 1,
212
+ preferredExecutor: 'same',
213
+ serializeFiles: ['drizzle/schema.ts'],
214
+ },
215
+ } satisfies RalphFeatureGroup
216
+
217
+ expect(group.sharedContext).toBeDefined()
218
+ expect(group.completionCriteria).toBeDefined()
219
+ expect(group.executionPolicy).toBeDefined()
220
+ expect(group.executionPolicy!.preferredExecutor).toBe('same')
221
+ })
222
+ })
223
+
224
+ describe('RalphPRD groups integration', () => {
225
+ it('groups field references RalphFeatureGroup type correctly', () => {
226
+ const group: RalphFeatureGroup = {
227
+ id: 'test-group',
228
+ description: 'Test group',
229
+ features: ['a', 'b'],
230
+ executionPolicy: {
231
+ parallelism: 3,
232
+ preferredExecutor: 'any',
233
+ serializeFiles: [],
234
+ },
235
+ }
236
+
237
+ const prd: RalphPRD = {
238
+ version: '2.1',
239
+ repo: 'company-semantics-app',
240
+ features: [],
241
+ stopCondition: 'Done',
242
+ groups: [group],
243
+ }
244
+
245
+ expect(prd.groups).toHaveLength(1)
246
+ expect(prd.groups![0]).toBe(group)
247
+ })
248
+ })
249
+ })
@@ -28,6 +28,9 @@ export type {
28
28
  RalphCapabilities,
29
29
  RalphSuccessCriteria,
30
30
  RalphCommitPolicy,
31
+ // PRD v2.1 schema extensions
32
+ RalphExecutionPolicy,
33
+ RalphFeatureGroup,
31
34
  } from './prd';
32
35
 
33
36
  // PRD constants (cross-repo support)
package/src/ralph/prd.ts CHANGED
@@ -288,6 +288,45 @@ export type RalphCommitPolicy = {
288
288
  logRollback: boolean;
289
289
  };
290
290
 
291
+ // =============================================================================
292
+ // PRD v2.1 Schema Extensions
293
+ // =============================================================================
294
+
295
+ /**
296
+ * Controls how features within a group are distributed and executed.
297
+ *
298
+ * @see company-semantics-control/scripts/ralph/src/group/types.ts for runtime equivalent
299
+ */
300
+ export type RalphExecutionPolicy = {
301
+ /** Max concurrent executors for this group (default 1) */
302
+ parallelism: number;
303
+ /** 'same' prefers all features on one executor, 'any' allows distribution */
304
+ preferredExecutor: 'same' | 'any';
305
+ /** Files that must not be modified concurrently (hard constraint) */
306
+ serializeFiles: string[];
307
+ };
308
+
309
+ /**
310
+ * A named group of related PRD features that share research context
311
+ * and have coordinated execution and completion semantics.
312
+ *
313
+ * @see company-semantics-control/scripts/ralph/src/group/types.ts for runtime equivalent (TaskGroup)
314
+ */
315
+ export type RalphFeatureGroup = {
316
+ /** Unique identifier for this group within the PRD */
317
+ id: string;
318
+ /** Human-readable description of the group's purpose */
319
+ description: string;
320
+ /** Feature IDs that belong to this group */
321
+ features: string[];
322
+ /** Optional shared context for all features in the group */
323
+ sharedContext?: string;
324
+ /** Optional criteria for group-level completion beyond individual feature completion */
325
+ completionCriteria?: string;
326
+ /** Optional execution policy controlling parallelism and serialization */
327
+ executionPolicy?: RalphExecutionPolicy;
328
+ };
329
+
291
330
  /**
292
331
  * A complete Ralph PRD document.
293
332
  *
@@ -321,4 +360,6 @@ export type RalphPRD = {
321
360
  nonGoals?: string[];
322
361
  /** Rollback and commit semantics */
323
362
  commitPolicy?: RalphCommitPolicy;
363
+ /** Task groups with shared research, execution policies, and completion tracking (v2.1) */
364
+ groups?: RalphFeatureGroup[];
324
365
  };