@critical-path/core 0.36.0 → 0.37.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.
Files changed (214) hide show
  1. package/README.md +1 -1
  2. package/dist/domain/authorization.d.ts.map +1 -1
  3. package/dist/domain/authorization.js +10 -7
  4. package/dist/domain/authorization.js.map +1 -1
  5. package/dist/engine/index.d.ts +6 -0
  6. package/dist/engine/index.d.ts.map +1 -1
  7. package/dist/engine/index.js +28 -2
  8. package/dist/engine/index.js.map +1 -1
  9. package/dist/schemas/index.d.ts +42 -12
  10. package/dist/schemas/index.d.ts.map +1 -1
  11. package/dist/schemas/index.js +4 -1
  12. package/dist/schemas/index.js.map +1 -1
  13. package/dist/store/firebase.d.ts +2 -2
  14. package/dist/store/firebase.d.ts.map +1 -1
  15. package/dist/store/firebase.js +4 -2
  16. package/dist/store/firebase.js.map +1 -1
  17. package/dist/store/index.d.ts +7 -2
  18. package/dist/store/index.d.ts.map +1 -1
  19. package/dist/store/index.js +3 -2
  20. package/dist/store/index.js.map +1 -1
  21. package/dist/store/sqlite.d.ts +2 -2
  22. package/dist/store/sqlite.d.ts.map +1 -1
  23. package/dist/store/sqlite.js +5 -3
  24. package/dist/store/sqlite.js.map +1 -1
  25. package/dist/testing/conformance.d.ts.map +1 -1
  26. package/dist/testing/conformance.js +9 -0
  27. package/dist/testing/conformance.js.map +1 -1
  28. package/dist/types/index.d.ts +13 -2
  29. package/dist/types/index.d.ts.map +1 -1
  30. package/package.json +5 -1
  31. package/CHANGELOG.md +0 -593
  32. package/dist/deliverables.test.d.ts +0 -2
  33. package/dist/deliverables.test.d.ts.map +0 -1
  34. package/dist/deliverables.test.js +0 -232
  35. package/dist/deliverables.test.js.map +0 -1
  36. package/dist/domain/calendar.test.d.ts +0 -2
  37. package/dist/domain/calendar.test.d.ts.map +0 -1
  38. package/dist/domain/calendar.test.js +0 -181
  39. package/dist/domain/calendar.test.js.map +0 -1
  40. package/dist/domain/cpm.test.d.ts +0 -2
  41. package/dist/domain/cpm.test.d.ts.map +0 -1
  42. package/dist/domain/cpm.test.js +0 -166
  43. package/dist/domain/cpm.test.js.map +0 -1
  44. package/dist/domain/ddd.test.d.ts +0 -2
  45. package/dist/domain/ddd.test.d.ts.map +0 -1
  46. package/dist/domain/ddd.test.js +0 -349
  47. package/dist/domain/ddd.test.js.map +0 -1
  48. package/dist/domain/ladder.test.d.ts +0 -2
  49. package/dist/domain/ladder.test.d.ts.map +0 -1
  50. package/dist/domain/ladder.test.js +0 -213
  51. package/dist/domain/ladder.test.js.map +0 -1
  52. package/dist/domain/metrics.test.d.ts +0 -2
  53. package/dist/domain/metrics.test.d.ts.map +0 -1
  54. package/dist/domain/metrics.test.js +0 -252
  55. package/dist/domain/metrics.test.js.map +0 -1
  56. package/dist/domain/task-duration.test.d.ts +0 -2
  57. package/dist/domain/task-duration.test.d.ts.map +0 -1
  58. package/dist/domain/task-duration.test.js +0 -307
  59. package/dist/domain/task-duration.test.js.map +0 -1
  60. package/dist/domain/workload.test.d.ts +0 -2
  61. package/dist/domain/workload.test.d.ts.map +0 -1
  62. package/dist/domain/workload.test.js +0 -261
  63. package/dist/domain/workload.test.js.map +0 -1
  64. package/dist/engine/actor.test.d.ts +0 -2
  65. package/dist/engine/actor.test.d.ts.map +0 -1
  66. package/dist/engine/actor.test.js +0 -54
  67. package/dist/engine/actor.test.js.map +0 -1
  68. package/dist/engine/authorization.test.d.ts +0 -2
  69. package/dist/engine/authorization.test.d.ts.map +0 -1
  70. package/dist/engine/authorization.test.js +0 -154
  71. package/dist/engine/authorization.test.js.map +0 -1
  72. package/dist/engine/cascade.test.d.ts +0 -2
  73. package/dist/engine/cascade.test.d.ts.map +0 -1
  74. package/dist/engine/cascade.test.js +0 -99
  75. package/dist/engine/cascade.test.js.map +0 -1
  76. package/dist/engine/config.test.d.ts +0 -2
  77. package/dist/engine/config.test.d.ts.map +0 -1
  78. package/dist/engine/config.test.js +0 -46
  79. package/dist/engine/config.test.js.map +0 -1
  80. package/dist/engine/entity-events.test.d.ts +0 -2
  81. package/dist/engine/entity-events.test.d.ts.map +0 -1
  82. package/dist/engine/entity-events.test.js +0 -61
  83. package/dist/engine/entity-events.test.js.map +0 -1
  84. package/dist/index.test.d.ts +0 -2
  85. package/dist/index.test.d.ts.map +0 -1
  86. package/dist/index.test.js +0 -429
  87. package/dist/index.test.js.map +0 -1
  88. package/dist/plugins/plugins.test.d.ts +0 -2
  89. package/dist/plugins/plugins.test.d.ts.map +0 -1
  90. package/dist/plugins/plugins.test.js +0 -137
  91. package/dist/plugins/plugins.test.js.map +0 -1
  92. package/dist/schemas/schemas.test.d.ts +0 -2
  93. package/dist/schemas/schemas.test.d.ts.map +0 -1
  94. package/dist/schemas/schemas.test.js +0 -58
  95. package/dist/schemas/schemas.test.js.map +0 -1
  96. package/dist/status.test.d.ts +0 -2
  97. package/dist/status.test.d.ts.map +0 -1
  98. package/dist/status.test.js +0 -295
  99. package/dist/status.test.js.map +0 -1
  100. package/dist/storage/storage.test.d.ts +0 -2
  101. package/dist/storage/storage.test.d.ts.map +0 -1
  102. package/dist/storage/storage.test.js +0 -236
  103. package/dist/storage/storage.test.js.map +0 -1
  104. package/dist/storage/upload-limits.test.d.ts +0 -2
  105. package/dist/storage/upload-limits.test.d.ts.map +0 -1
  106. package/dist/storage/upload-limits.test.js +0 -31
  107. package/dist/storage/upload-limits.test.js.map +0 -1
  108. package/dist/store/conformance.test.d.ts +0 -2
  109. package/dist/store/conformance.test.d.ts.map +0 -1
  110. package/dist/store/conformance.test.js +0 -15
  111. package/dist/store/conformance.test.js.map +0 -1
  112. package/dist/store/firebase.test.d.ts +0 -2
  113. package/dist/store/firebase.test.d.ts.map +0 -1
  114. package/dist/store/firebase.test.js +0 -103
  115. package/dist/store/firebase.test.js.map +0 -1
  116. package/dist/store/query.test.d.ts +0 -2
  117. package/dist/store/query.test.d.ts.map +0 -1
  118. package/dist/store/query.test.js +0 -89
  119. package/dist/store/query.test.js.map +0 -1
  120. package/dist/store/sqlite.test.d.ts +0 -2
  121. package/dist/store/sqlite.test.d.ts.map +0 -1
  122. package/dist/store/sqlite.test.js +0 -177
  123. package/dist/store/sqlite.test.js.map +0 -1
  124. package/dist/task-state.test.d.ts +0 -2
  125. package/dist/task-state.test.d.ts.map +0 -1
  126. package/dist/task-state.test.js +0 -423
  127. package/dist/task-state.test.js.map +0 -1
  128. package/dist/use-case-validation.test.d.ts +0 -2
  129. package/dist/use-case-validation.test.d.ts.map +0 -1
  130. package/dist/use-case-validation.test.js +0 -281
  131. package/dist/use-case-validation.test.js.map +0 -1
  132. package/dist/utils/fractional-index.test.d.ts +0 -2
  133. package/dist/utils/fractional-index.test.d.ts.map +0 -1
  134. package/dist/utils/fractional-index.test.js +0 -182
  135. package/dist/utils/fractional-index.test.js.map +0 -1
  136. package/dist/utils/key.test.d.ts +0 -2
  137. package/dist/utils/key.test.d.ts.map +0 -1
  138. package/dist/utils/key.test.js +0 -65
  139. package/dist/utils/key.test.js.map +0 -1
  140. package/dist/utils/mentions.test.d.ts +0 -2
  141. package/dist/utils/mentions.test.d.ts.map +0 -1
  142. package/dist/utils/mentions.test.js +0 -114
  143. package/dist/utils/mentions.test.js.map +0 -1
  144. package/dist/webhooks/webhooks.test.d.ts +0 -2
  145. package/dist/webhooks/webhooks.test.d.ts.map +0 -1
  146. package/dist/webhooks/webhooks.test.js +0 -154
  147. package/dist/webhooks/webhooks.test.js.map +0 -1
  148. package/src/deliverables.test.ts +0 -284
  149. package/src/domain/authorization.ts +0 -82
  150. package/src/domain/calendar.test.ts +0 -216
  151. package/src/domain/calendar.ts +0 -502
  152. package/src/domain/cpm.test.ts +0 -189
  153. package/src/domain/cpm.ts +0 -258
  154. package/src/domain/custom-fields.ts +0 -172
  155. package/src/domain/ddd.test.ts +0 -442
  156. package/src/domain/entities.ts +0 -798
  157. package/src/domain/errors.ts +0 -38
  158. package/src/domain/events.ts +0 -369
  159. package/src/domain/graph.ts +0 -70
  160. package/src/domain/index.ts +0 -12
  161. package/src/domain/ladder.test.ts +0 -251
  162. package/src/domain/ladder.ts +0 -474
  163. package/src/domain/metrics.test.ts +0 -288
  164. package/src/domain/metrics.ts +0 -497
  165. package/src/domain/task-duration.test.ts +0 -354
  166. package/src/domain/workload.test.ts +0 -311
  167. package/src/domain/workload.ts +0 -457
  168. package/src/engine/actor.test.ts +0 -63
  169. package/src/engine/authorization.test.ts +0 -199
  170. package/src/engine/cascade.test.ts +0 -121
  171. package/src/engine/config.test.ts +0 -51
  172. package/src/engine/entity-events.test.ts +0 -67
  173. package/src/engine/index.ts +0 -2495
  174. package/src/index.test.ts +0 -514
  175. package/src/index.ts +0 -9
  176. package/src/plugins/index.ts +0 -103
  177. package/src/plugins/plugins.test.ts +0 -161
  178. package/src/schemas/index.ts +0 -402
  179. package/src/schemas/schemas.test.ts +0 -116
  180. package/src/status.test.ts +0 -354
  181. package/src/storage/file-storage.ts +0 -200
  182. package/src/storage/firebase-storage.ts +0 -209
  183. package/src/storage/index.ts +0 -3
  184. package/src/storage/s3.ts +0 -151
  185. package/src/storage/storage.test.ts +0 -274
  186. package/src/storage/upload-limits.test.ts +0 -41
  187. package/src/store/conformance.test.ts +0 -15
  188. package/src/store/firebase.test.ts +0 -131
  189. package/src/store/firebase.ts +0 -658
  190. package/src/store/index.ts +0 -623
  191. package/src/store/query.test.ts +0 -110
  192. package/src/store/query.ts +0 -113
  193. package/src/store/sqlite.test.ts +0 -218
  194. package/src/store/sqlite.ts +0 -1480
  195. package/src/task-state.test.ts +0 -506
  196. package/src/testing/conformance.ts +0 -319
  197. package/src/testing/index.ts +0 -1
  198. package/src/types/index.ts +0 -871
  199. package/src/use-case-validation.test.ts +0 -324
  200. package/src/utils/fractional-index.test.ts +0 -228
  201. package/src/utils/fractional-index.ts +0 -254
  202. package/src/utils/index.ts +0 -7
  203. package/src/utils/key.test.ts +0 -76
  204. package/src/utils/key.ts +0 -62
  205. package/src/utils/mentions.test.ts +0 -132
  206. package/src/utils/mentions.ts +0 -131
  207. package/src/utils/status.ts +0 -419
  208. package/src/utils/workflow.ts +0 -306
  209. package/src/webhooks/dispatcher.ts +0 -190
  210. package/src/webhooks/index.ts +0 -2
  211. package/src/webhooks/signature.ts +0 -57
  212. package/src/webhooks/webhooks.test.ts +0 -201
  213. package/tsconfig.json +0 -8
  214. package/tsconfig.tsbuildinfo +0 -1
@@ -1,324 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { CriticalPathEngine, DEFAULT_SOFTWARE_WORKFLOW, DEFAULT_VFX_WORKFLOW } from './index.js';
3
-
4
- describe('Critical Path Use Case Validation Suite', () => {
5
- describe('Use Case 1: Software Development (Product Planning & Issue Tracking)', () => {
6
- it('supports full software development lifecycle with epics, sprints, dependencies, custom fields, and workflow transitions', async () => {
7
- const engine = new CriticalPathEngine();
8
-
9
- // 1. Create Workflow & Project
10
- const softwareProject = await engine.createProject({
11
- key: 'DEV',
12
- name: 'Mobile App Project',
13
- description: 'Software development project for mobile client',
14
- workflow: DEFAULT_SOFTWARE_WORKFLOW,
15
- customFieldDefinitions: [
16
- { id: 'cf_story_points', key: 'storyPoints', label: 'Story Points', type: 'number' },
17
- { id: 'cf_severity', key: 'severity', label: 'Severity', type: 'single_select', options: ['low', 'medium', 'high', 'critical'] }
18
- ]
19
- });
20
-
21
- expect(softwareProject.id).toBeDefined();
22
-
23
- // 2. Setup Team and Sprint Iteration
24
- const devTeam = await engine.createTeam({
25
- name: 'Frontend Core Team',
26
- memberIds: ['dev_alice', 'dev_bob', 'lead_charlie']
27
- });
28
-
29
- const sprint = await engine.createIteration({
30
- projectId: softwareProject.id,
31
- name: 'Sprint 24',
32
- type: 'sprint',
33
- startDate: '2026-09-01T00:00:00.000Z',
34
- endDate: '2026-09-14T00:00:00.000Z',
35
- status: 'active'
36
- });
37
-
38
- // 3. Epics / Task Containers
39
- const authEpic = await engine.createContainer({
40
- projectId: softwareProject.id,
41
- name: 'OAuth2 & Biometric Auth Epic',
42
- type: 'epic'
43
- });
44
-
45
- // 4. Tasks & Subtasks
46
- // Feature task under Auth Epic
47
- const featureTask = await engine.createTask({
48
- projectId: softwareProject.id,
49
- title: 'Implement FaceID & TouchID Login',
50
- description: 'Add biometric authentication flow to iOS and Android builds',
51
- status: 'backlog',
52
- priority: 'high',
53
- taskType: 'feature',
54
- reporterId: 'lead_charlie',
55
- reviewerId: 'lead_charlie',
56
- teamId: devTeam.id,
57
- containerId: authEpic.id,
58
- iterationId: sprint.id,
59
- estimatedHours: 16,
60
- customFields: {
61
- storyPoints: 5,
62
- severity: 'high'
63
- }
64
- });
65
-
66
- // Bug task blocking the feature release
67
- const bugTask = await engine.createTask({
68
- projectId: softwareProject.id,
69
- title: 'Fix OAuth Token Expiry Edge Case',
70
- description: 'Token refresh fails when app is backgrounded over 24 hours',
71
- status: 'todo',
72
- priority: 'urgent',
73
- taskType: 'bug',
74
- assigneeId: 'dev_alice',
75
- reporterId: 'dev_bob',
76
- reviewerId: 'lead_charlie',
77
- iterationId: sprint.id,
78
- estimatedHours: 4,
79
- customFields: {
80
- storyPoints: 2,
81
- severity: 'critical'
82
- }
83
- });
84
-
85
- // Subtask under Feature Task
86
- const subTask = await engine.createTask({
87
- projectId: softwareProject.id,
88
- title: 'iOS LocalAuthentication Bridge',
89
- status: 'todo',
90
- priority: 'high',
91
- taskType: 'subtask',
92
- assigneeId: 'dev_bob',
93
- parentId: featureTask.id,
94
- estimatedHours: 8
95
- });
96
-
97
- expect(subTask.parentId).toBe(featureTask.id);
98
-
99
- // 5. Blocker Dependency: FeatureTask depends on BugTask being resolved first
100
- await engine.store.addDependency({
101
- taskId: featureTask.id,
102
- dependsOnTaskId: bugTask.id,
103
- type: 'blocking'
104
- });
105
-
106
- const bugGraph = await engine.getTaskDependencyGraph(bugTask.id);
107
- expect(bugGraph.downstreamTasks.map((t) => t.id)).toContain(featureTask.id);
108
-
109
- // 6. Execution & Workflow Transitions
110
- // Move Feature from Backlog -> Todo -> In Progress
111
- await engine.updateTask(featureTask.id, { status: 'todo' });
112
- await engine.updateTask(featureTask.id, {
113
- status: 'in_progress',
114
- assigneeId: 'dev_alice',
115
- actualStartDate: '2026-09-02T09:00:00.000Z'
116
- });
117
-
118
- // Log progress and hours
119
- const updatedFeature = await engine.updateTask(featureTask.id, {
120
- loggedHours: 10,
121
- progress: 60
122
- });
123
- expect(updatedFeature?.loggedHours).toBe(10);
124
- expect(updatedFeature?.progress).toBe(60);
125
-
126
- // Transition In Progress -> In Review
127
- const reviewTask = await engine.updateTask(featureTask.id, { status: 'in_review' });
128
- expect(reviewTask?.status).toBe('in_review');
129
-
130
- // Invalid Transition Check: Try to transition back from Done to Backlog directly (not allowed by workflow)
131
- const doneTask = await engine.updateTask(featureTask.id, { status: 'done', actualEndDate: '2026-09-05T17:00:00.000Z' });
132
- expect(doneTask?.status).toBe('done');
133
-
134
- await expect(
135
- engine.updateTask(featureTask.id, { status: 'backlog' })
136
- ).rejects.toThrow();
137
-
138
- // Check Lifecycle State
139
- const lifecycle = await engine.getTaskLifecycleState(featureTask.id);
140
- expect(lifecycle?.isDone).toBe(true);
141
- expect(lifecycle?.semanticStatus).toBe('completed');
142
- });
143
- });
144
-
145
- describe('Use Case 2: Visual Effects (VFX) Production', () => {
146
- it('supports VFX shot deliverable hierarchies, drafting/bidding, artist/vendor assignment, and multi-stage review workflows', async () => {
147
- const engine = new CriticalPathEngine();
148
-
149
- // 1. Setup VFX Project with DEFAULT_VFX_WORKFLOW
150
- const vfxProject = await engine.createProject({
151
- key: 'FX_MOP',
152
- name: 'Feature Film - Movie Project',
153
- description: 'VFX shot production for sequence SEQ_101',
154
- workflow: DEFAULT_VFX_WORKFLOW,
155
- customFieldDefinitions: [
156
- { id: 'cf_bid_days', key: 'bidDays', label: 'Bid Days', type: 'number' },
157
- { id: 'cf_bid_amount', key: 'bidAmountUSD', label: 'Bid Amount ($)', type: 'number' },
158
- { id: 'cf_frame_count', key: 'frameCount', label: 'Frame Count', type: 'number' },
159
- { id: 'cf_resolution', key: 'resolution', label: 'Resolution', type: 'single_select', options: ['2K', '4K', '8K'] },
160
- { id: 'cf_vfx_version', key: 'vfxVersion', label: 'VFX Version', type: 'text' },
161
- { id: 'cf_review_url', key: 'reviewMediaUrl', label: 'Review Media URL', type: 'text' }
162
- ]
163
- });
164
-
165
- // 2. Outsource Vendor & Internal Department Teams
166
- const vendorTeam = await engine.createTeam({
167
- name: 'Outsource Vendor Studio X',
168
- description: '3D Simulation & FX Vendor Studio',
169
- memberIds: ['vendor_lead_mark', 'vendor_artist_joe']
170
- });
171
-
172
- const internalLeadTeam = await engine.createTeam({
173
- name: 'Internal VFX Supervisors',
174
- memberIds: ['vfx_sup_sarah', 'comp_lead_dave']
175
- });
176
-
177
- expect(vendorTeam.id).toBeDefined();
178
- expect(internalLeadTeam.id).toBeDefined();
179
-
180
- // 3. Hierarchy Deliverables: Sequence Container -> Shot Container
181
- const sequenceContainer = await engine.createContainer({
182
- projectId: vfxProject.id,
183
- name: 'SEQ_101 - Desert Canyon Chase',
184
- type: 'sequence'
185
- });
186
-
187
- const shotContainer = await engine.createContainer({
188
- projectId: vfxProject.id,
189
- name: 'SHOT_101_010 - Hero Vehicle Explosion',
190
- type: 'shot',
191
- parentId: sequenceContainer.id
192
- });
193
-
194
- expect(shotContainer.parentId).toBe(sequenceContainer.id);
195
-
196
- // 4. Draft & Bidding Phase
197
- // Create VFX Task for FX Simulation department under SHOT_101_010
198
- const fxBiddingTask = await engine.createTask({
199
- projectId: vfxProject.id,
200
- title: 'FX Simulation - Fire & Vehicle Debris',
201
- description: 'Heavy pyro explosion and fracturing geometry',
202
- status: 'bidding', // Starts in Draft & Bidding
203
- priority: 'high',
204
- taskType: 'vfx_task',
205
- reporterId: 'vfx_sup_sarah',
206
- containerId: shotContainer.id,
207
- teamId: vendorTeam.id,
208
- customFields: {
209
- bidDays: 12.5,
210
- bidAmountUSD: 15000,
211
- frameCount: 180,
212
- resolution: '4K',
213
- vfxVersion: 'v000'
214
- }
215
- });
216
-
217
- expect(fxBiddingTask.status).toBe('bidding');
218
- expect(fxBiddingTask.customFields?.bidAmountUSD).toBe(15000);
219
-
220
- // Element Subtask
221
- const dustElementTask = await engine.createTask({
222
- projectId: vfxProject.id,
223
- title: 'Secondary Dust Cloud Elements',
224
- status: 'bidding',
225
- priority: 'medium',
226
- taskType: 'subtask',
227
- parentId: fxBiddingTask.id,
228
- customFields: { bidDays: 3 }
229
- });
230
-
231
- expect(dustElementTask.parentId).toBe(fxBiddingTask.id);
232
-
233
- // 5. Award Bids & Transition to In Production
234
- const awardedTask = await engine.updateTask(fxBiddingTask.id, {
235
- status: 'awarded'
236
- });
237
- expect(awardedTask?.status).toBe('awarded');
238
-
239
- // Assign to Vendor Artist and start production
240
- const productionTask = await engine.updateTask(fxBiddingTask.id, {
241
- status: 'in_production',
242
- assigneeId: 'vendor_artist_joe',
243
- reviewerId: 'comp_lead_dave',
244
- customFields: {
245
- ...fxBiddingTask.customFields,
246
- vfxVersion: 'v001'
247
- }
248
- });
249
- expect(productionTask?.status).toBe('in_production');
250
- expect(productionTask?.assigneeId).toBe('vendor_artist_joe');
251
-
252
- // 6. Review & Approval Process
253
- // Artist finishes v001 and submits to Internal Review
254
- const internalReviewTask = await engine.updateTask(fxBiddingTask.id, {
255
- status: 'internal_review',
256
- customFields: {
257
- ...productionTask?.customFields,
258
- vfxVersion: 'v001',
259
- reviewMediaUrl: 'https://vfx-pipeline.studio.com/reviews/SHOT_101_010_FX_v001.mov'
260
- }
261
- });
262
- expect(internalReviewTask?.status).toBe('internal_review');
263
-
264
- // Internal Lead rejects v001 and requests revisions with notes
265
- await engine.store.addComment({
266
- taskId: fxBiddingTask.id,
267
- authorId: 'comp_lead_dave',
268
- content: 'Fire velocity looks great, but debris needs 20% more scale and air resistance.'
269
- });
270
-
271
- const revisionTask = await engine.updateTask(fxBiddingTask.id, {
272
- status: 'revision_requested'
273
- });
274
- expect(revisionTask?.status).toBe('revision_requested');
275
-
276
- // Artist addresses revisions -> In Production -> Resubmits v002 to Internal Review
277
- await engine.updateTask(fxBiddingTask.id, {
278
- status: 'in_production',
279
- customFields: {
280
- ...revisionTask?.customFields,
281
- vfxVersion: 'v002'
282
- }
283
- });
284
-
285
- const internalReviewPass = await engine.updateTask(fxBiddingTask.id, {
286
- status: 'internal_review',
287
- customFields: {
288
- ...revisionTask?.customFields,
289
- vfxVersion: 'v002',
290
- reviewMediaUrl: 'https://vfx-pipeline.studio.com/reviews/SHOT_101_010_FX_v002.mov'
291
- }
292
- });
293
- expect(internalReviewPass?.status).toBe('internal_review');
294
-
295
- // Internal Lead approves and passes shot to Client Review
296
- const clientReviewTask = await engine.updateTask(fxBiddingTask.id, {
297
- status: 'client_review'
298
- });
299
- expect(clientReviewTask?.status).toBe('client_review');
300
-
301
- // Director / Client Supervisor approves final shot
302
- await engine.store.addComment({
303
- taskId: fxBiddingTask.id,
304
- authorId: 'vfx_sup_sarah',
305
- content: 'Client Supervisor approved v002 for final comp assembly.'
306
- });
307
-
308
- const finalApprovedTask = await engine.updateTask(fxBiddingTask.id, {
309
- status: 'approved',
310
- progress: 100
311
- });
312
- expect(finalApprovedTask?.status).toBe('approved');
313
- expect(finalApprovedTask?.progress).toBe(100);
314
-
315
- // Verify task lifecycle status shows completed
316
- const lifecycle = await engine.getTaskLifecycleState(fxBiddingTask.id);
317
- expect(lifecycle?.isDone).toBe(true);
318
-
319
- // Verify audit log history recorded all approval steps
320
- const activities = await engine.store.getActivities({ taskId: fxBiddingTask.id });
321
- expect(activities.length).toBeGreaterThanOrEqual(6);
322
- });
323
- });
324
- });
@@ -1,228 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- encodeInteger,
4
- decodeInteger,
5
- getIntegerPart,
6
- getInitialKey,
7
- generateKeyBetween,
8
- generateNKeysBetween,
9
- compareOrderIndices
10
- } from './fractional-index.js';
11
-
12
- describe('fractional-index', () => {
13
- describe('encodeInteger and decodeInteger', () => {
14
- it('encodes and decodes non-negative integers', () => {
15
- const cases = [0, 1, 2, 9, 10, 61, 62, 100, 1000, 3843, 3844];
16
- for (const n of cases) {
17
- const encoded = encodeInteger(n);
18
- expect(decodeInteger(encoded)).toBe(n);
19
- }
20
- });
21
-
22
- it('encodes and decodes negative integers', () => {
23
- const cases = [-1, -2, -10, -62, -100, -1000];
24
- for (const n of cases) {
25
- const encoded = encodeInteger(n);
26
- expect(decodeInteger(encoded)).toBe(n);
27
- }
28
- });
29
-
30
- it('strictly maintains lexicographical sorting across integers', () => {
31
- const sequence = [-100, -62, -10, -2, -1, 0, 1, 2, 9, 10, 61, 62, 100, 3843, 3844];
32
- const encoded = sequence.map((n) => encodeInteger(n));
33
-
34
- for (let i = 0; i < encoded.length - 1; i++) {
35
- expect(encoded[i] < encoded[i + 1]).toBe(true);
36
- expect(compareOrderIndices(encoded[i], encoded[i + 1])).toBeLessThan(0);
37
- }
38
-
39
- // Reversing and sorting with compareOrderIndices or standard sort() restores exact order
40
- const reversed = [...encoded].reverse();
41
- expect(reversed.sort(compareOrderIndices)).toEqual(encoded);
42
- expect([...encoded].reverse().sort()).toEqual(encoded);
43
- });
44
- });
45
-
46
- describe('getInitialKey', () => {
47
- it('generates spaced initial keys', () => {
48
- const k0 = getInitialKey(0);
49
- const k1 = getInitialKey(1);
50
- const k2 = getInitialKey(2);
51
-
52
- expect(k0).toBe('a0');
53
- expect(k1).toBe('a2');
54
- expect(k2).toBe('a4');
55
-
56
- expect(k0 < k1).toBe(true);
57
- expect(k1 < k2).toBe(true);
58
- });
59
- });
60
-
61
- describe('generateKeyBetween', () => {
62
- it('returns default initial key when both bounds are null/undefined', () => {
63
- expect(generateKeyBetween(null, null)).toBe('a0');
64
- expect(generateKeyBetween(undefined, undefined)).toBe('a0');
65
- expect(generateKeyBetween('', '')).toBe('a0');
66
- });
67
-
68
- it('generates a key before an upper bound', () => {
69
- const b0 = 'a0';
70
- const beforeB0 = generateKeyBetween(null, b0);
71
- expect(beforeB0 < b0).toBe(true);
72
-
73
- const b1 = 'a1';
74
- const beforeB1 = generateKeyBetween(null, b1);
75
- expect(beforeB1 < b1).toBe(true);
76
-
77
- const bFractional = 'a0V';
78
- const beforeFractional = generateKeyBetween(null, bFractional);
79
- expect(beforeFractional < bFractional).toBe(true);
80
- });
81
-
82
- it('generates a key after a lower bound', () => {
83
- const a0 = 'a0';
84
- const afterA0 = generateKeyBetween(a0, null);
85
- expect(a0 < afterA0).toBe(true);
86
-
87
- const aFractional = 'a0V';
88
- const afterFractional = generateKeyBetween(aFractional, null);
89
- expect(aFractional < afterFractional).toBe(true);
90
- });
91
-
92
- it('generates key between spaced integers', () => {
93
- const a = 'a0';
94
- const b = 'a2';
95
- const mid = generateKeyBetween(a, b);
96
-
97
- expect(mid).toBe('a1');
98
- expect(a < mid).toBe(true);
99
- expect(mid < b).toBe(true);
100
- });
101
-
102
- it('generates fractional key between adjacent integers', () => {
103
- const a = 'a0';
104
- const b = 'a1';
105
- const mid = generateKeyBetween(a, b);
106
-
107
- expect(a < mid).toBe(true);
108
- expect(mid < b).toBe(true);
109
- });
110
-
111
- it('handles nested fractional insertions without degradation', () => {
112
- let current = 'a0';
113
- const target = 'a1';
114
-
115
- // Perform 50 sequential midpoint insertions
116
- for (let i = 0; i < 50; i++) {
117
- const next = generateKeyBetween(current, target);
118
- expect(current < next).toBe(true);
119
- expect(next < target).toBe(true);
120
- current = next;
121
- }
122
- });
123
-
124
- it('handles repeated insertions directly after the same lower bound', () => {
125
- const lower = 'a0';
126
- let upper = 'a1';
127
-
128
- // Repeatedly drop a card just below the first card
129
- for (let i = 0; i < 50; i++) {
130
- const next = generateKeyBetween(lower, upper);
131
- expect(lower < next).toBe(true);
132
- expect(next < upper).toBe(true);
133
- upper = next;
134
- }
135
- });
136
-
137
- it('keeps keys strictly ordered under random insertions', () => {
138
- const keys = ['a0', 'a1'];
139
- let seed = 42;
140
- const random = () => {
141
- seed = (seed * 1103515245 + 12345) % 2147483648;
142
- return seed / 2147483648;
143
- };
144
-
145
- for (let i = 0; i < 500; i++) {
146
- const idx = Math.floor(random() * (keys.length + 1));
147
- const next = generateKeyBetween(keys[idx - 1] ?? null, keys[idx] ?? null);
148
- keys.splice(idx, 0, next);
149
- }
150
-
151
- for (let i = 1; i < keys.length; i++) {
152
- expect(keys[i - 1] < keys[i]).toBe(true);
153
- }
154
- });
155
-
156
- it('throws when the lower bound does not sort before the upper bound', () => {
157
- expect(() => generateKeyBetween('a1', 'a0')).toThrow(RangeError);
158
- expect(() => generateKeyBetween('a0', 'a0')).toThrow(RangeError);
159
- });
160
- });
161
-
162
- describe('generateNKeysBetween', () => {
163
- it('returns empty array for count <= 0', () => {
164
- expect(generateNKeysBetween('a0', 'a2', 0)).toEqual([]);
165
- expect(generateNKeysBetween('a0', 'a2', -1)).toEqual([]);
166
- });
167
-
168
- it('generates 1 key matching generateKeyBetween', () => {
169
- const keys = generateNKeysBetween('a0', 'a2', 1);
170
- expect(keys.length).toBe(1);
171
- expect(keys[0]).toBe('a1');
172
- });
173
-
174
- it('generates N keys in strictly ascending order between a and b', () => {
175
- const count = 5;
176
- const a = 'a0';
177
- const b = 'a1';
178
- const keys = generateNKeysBetween(a, b, count);
179
-
180
- expect(keys.length).toBe(count);
181
-
182
- // Check strictly greater than a
183
- expect(a < keys[0]).toBe(true);
184
-
185
- // Check strictly increasing
186
- for (let i = 0; i < keys.length - 1; i++) {
187
- expect(keys[i] < keys[i + 1]).toBe(true);
188
- }
189
-
190
- // Check strictly less than b
191
- expect(keys[count - 1] < b).toBe(true);
192
- });
193
-
194
- it('generates N keys at swimlane start (a is null)', () => {
195
- const count = 3;
196
- const b = 'a0';
197
- const keys = generateNKeysBetween(null, b, count);
198
-
199
- expect(keys.length).toBe(count);
200
- for (let i = 0; i < keys.length - 1; i++) {
201
- expect(keys[i] < keys[i + 1]).toBe(true);
202
- }
203
- expect(keys[count - 1] < b).toBe(true);
204
- });
205
-
206
- it('generates N keys at swimlane end (b is null)', () => {
207
- const count = 4;
208
- const a = 'a5';
209
- const keys = generateNKeysBetween(a, null, count);
210
-
211
- expect(keys.length).toBe(count);
212
- expect(a < keys[0]).toBe(true);
213
- for (let i = 0; i < keys.length - 1; i++) {
214
- expect(keys[i] < keys[i + 1]).toBe(true);
215
- }
216
- });
217
-
218
- it('generates N keys in empty swimlane (both null)', () => {
219
- const count = 6;
220
- const keys = generateNKeysBetween(null, null, count);
221
-
222
- expect(keys.length).toBe(count);
223
- for (let i = 0; i < keys.length - 1; i++) {
224
- expect(keys[i] < keys[i + 1]).toBe(true);
225
- }
226
- });
227
- });
228
- });