@studio-foundation/engine 0.3.0-beta.1 → 0.3.0-beta.5

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 (246) hide show
  1. package/dist/__tests__/engine.conditions-group.test.d.ts +2 -0
  2. package/dist/__tests__/engine.conditions-group.test.d.ts.map +1 -0
  3. package/dist/__tests__/engine.conditions-group.test.js +222 -0
  4. package/dist/__tests__/engine.conditions-group.test.js.map +1 -0
  5. package/dist/__tests__/engine.conditions.test.d.ts +2 -0
  6. package/dist/__tests__/engine.conditions.test.d.ts.map +1 -0
  7. package/dist/__tests__/engine.conditions.test.js +164 -0
  8. package/dist/__tests__/engine.conditions.test.js.map +1 -0
  9. package/dist/__tests__/engine.resume.test.d.ts +2 -0
  10. package/dist/__tests__/engine.resume.test.d.ts.map +1 -0
  11. package/dist/__tests__/engine.resume.test.js +93 -0
  12. package/dist/__tests__/engine.resume.test.js.map +1 -0
  13. package/dist/__tests__/engine.script-stage.test.d.ts +2 -0
  14. package/dist/__tests__/engine.script-stage.test.d.ts.map +1 -0
  15. package/dist/__tests__/engine.script-stage.test.js +108 -0
  16. package/dist/__tests__/engine.script-stage.test.js.map +1 -0
  17. package/dist/db/client.d.ts +3 -0
  18. package/dist/db/client.d.ts.map +1 -0
  19. package/{src/db/client.ts → dist/db/client.js} +1 -2
  20. package/dist/db/client.js.map +1 -0
  21. package/dist/engine.context-event.test.d.ts +2 -0
  22. package/dist/engine.context-event.test.d.ts.map +1 -0
  23. package/dist/engine.context-event.test.js +111 -0
  24. package/dist/engine.context-event.test.js.map +1 -0
  25. package/dist/engine.d.ts +52 -0
  26. package/dist/engine.d.ts.map +1 -0
  27. package/dist/engine.js +364 -0
  28. package/dist/engine.js.map +1 -0
  29. package/dist/events.d.ts +163 -0
  30. package/dist/events.d.ts.map +1 -0
  31. package/dist/events.js +12 -0
  32. package/dist/events.js.map +1 -0
  33. package/{src/index.ts → dist/index.d.ts} +3 -45
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +24 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/pipeline/agent-loader.d.ts +4 -0
  38. package/dist/pipeline/agent-loader.d.ts.map +1 -0
  39. package/dist/pipeline/agent-loader.js +28 -0
  40. package/dist/pipeline/agent-loader.js.map +1 -0
  41. package/dist/pipeline/agent-loader.test.d.ts +2 -0
  42. package/dist/pipeline/agent-loader.test.d.ts.map +1 -0
  43. package/dist/pipeline/agent-loader.test.js +134 -0
  44. package/dist/pipeline/agent-loader.test.js.map +1 -0
  45. package/dist/pipeline/condition-evaluator.d.ts +6 -0
  46. package/dist/pipeline/condition-evaluator.d.ts.map +1 -0
  47. package/dist/pipeline/condition-evaluator.js +105 -0
  48. package/dist/pipeline/condition-evaluator.js.map +1 -0
  49. package/dist/pipeline/condition-evaluator.test.d.ts +2 -0
  50. package/dist/pipeline/condition-evaluator.test.d.ts.map +1 -0
  51. package/dist/pipeline/condition-evaluator.test.js +103 -0
  52. package/dist/pipeline/condition-evaluator.test.js.map +1 -0
  53. package/dist/pipeline/context-pack-loader.d.ts +3 -0
  54. package/dist/pipeline/context-pack-loader.d.ts.map +1 -0
  55. package/dist/pipeline/context-pack-loader.js +51 -0
  56. package/dist/pipeline/context-pack-loader.js.map +1 -0
  57. package/dist/pipeline/context-propagation.d.ts +28 -0
  58. package/dist/pipeline/context-propagation.d.ts.map +1 -0
  59. package/dist/pipeline/context-propagation.js +161 -0
  60. package/dist/pipeline/context-propagation.js.map +1 -0
  61. package/dist/pipeline/context-propagation.test.d.ts +2 -0
  62. package/dist/pipeline/context-propagation.test.d.ts.map +1 -0
  63. package/dist/pipeline/context-propagation.test.js +194 -0
  64. package/dist/pipeline/context-propagation.test.js.map +1 -0
  65. package/dist/pipeline/contract-loader.d.ts +4 -0
  66. package/dist/pipeline/contract-loader.d.ts.map +1 -0
  67. package/dist/pipeline/contract-loader.js +30 -0
  68. package/dist/pipeline/contract-loader.js.map +1 -0
  69. package/dist/pipeline/group-orchestrator.d.ts +19 -0
  70. package/dist/pipeline/group-orchestrator.d.ts.map +1 -0
  71. package/dist/pipeline/group-orchestrator.js +376 -0
  72. package/dist/pipeline/group-orchestrator.js.map +1 -0
  73. package/dist/pipeline/hook-executor.d.ts +28 -0
  74. package/dist/pipeline/hook-executor.d.ts.map +1 -0
  75. package/dist/pipeline/hook-executor.js +63 -0
  76. package/dist/pipeline/hook-executor.js.map +1 -0
  77. package/dist/pipeline/hook-executor.test.d.ts +2 -0
  78. package/dist/pipeline/hook-executor.test.d.ts.map +1 -0
  79. package/dist/pipeline/hook-executor.test.js +71 -0
  80. package/dist/pipeline/hook-executor.test.js.map +1 -0
  81. package/dist/pipeline/invariants-loader.d.ts +6 -0
  82. package/dist/pipeline/invariants-loader.d.ts.map +1 -0
  83. package/dist/pipeline/invariants-loader.js +15 -0
  84. package/dist/pipeline/invariants-loader.js.map +1 -0
  85. package/dist/pipeline/invariants-loader.test.d.ts +2 -0
  86. package/dist/pipeline/invariants-loader.test.d.ts.map +1 -0
  87. package/dist/pipeline/invariants-loader.test.js +39 -0
  88. package/dist/pipeline/invariants-loader.test.js.map +1 -0
  89. package/dist/pipeline/loader.d.ts +5 -0
  90. package/dist/pipeline/loader.d.ts.map +1 -0
  91. package/dist/pipeline/loader.js +132 -0
  92. package/dist/pipeline/loader.js.map +1 -0
  93. package/dist/pipeline/loader.test.d.ts +2 -0
  94. package/dist/pipeline/loader.test.d.ts.map +1 -0
  95. package/dist/pipeline/loader.test.js +120 -0
  96. package/dist/pipeline/loader.test.js.map +1 -0
  97. package/dist/pipeline/output-validator.d.ts +10 -0
  98. package/dist/pipeline/output-validator.d.ts.map +1 -0
  99. package/dist/pipeline/output-validator.js +20 -0
  100. package/dist/pipeline/output-validator.js.map +1 -0
  101. package/dist/pipeline/output-validator.test.d.ts +2 -0
  102. package/dist/pipeline/output-validator.test.d.ts.map +1 -0
  103. package/dist/pipeline/output-validator.test.js +77 -0
  104. package/dist/pipeline/output-validator.test.js.map +1 -0
  105. package/dist/pipeline/post-validator.d.ts +8 -0
  106. package/dist/pipeline/post-validator.d.ts.map +1 -0
  107. package/dist/pipeline/post-validator.js +103 -0
  108. package/dist/pipeline/post-validator.js.map +1 -0
  109. package/dist/pipeline/skill-loader.d.ts +10 -0
  110. package/dist/pipeline/skill-loader.d.ts.map +1 -0
  111. package/dist/pipeline/skill-loader.js +26 -0
  112. package/dist/pipeline/skill-loader.js.map +1 -0
  113. package/dist/pipeline/skill-loader.test.d.ts +2 -0
  114. package/dist/pipeline/skill-loader.test.d.ts.map +1 -0
  115. package/dist/pipeline/skill-loader.test.js +38 -0
  116. package/dist/pipeline/skill-loader.test.js.map +1 -0
  117. package/dist/pipeline/stage-executor.d.ts +26 -0
  118. package/dist/pipeline/stage-executor.d.ts.map +1 -0
  119. package/dist/pipeline/stage-executor.js +540 -0
  120. package/dist/pipeline/stage-executor.js.map +1 -0
  121. package/dist/pipeline/stage-resolver.d.ts +3 -0
  122. package/dist/pipeline/stage-resolver.d.ts.map +1 -0
  123. package/dist/pipeline/stage-resolver.js +6 -0
  124. package/dist/pipeline/stage-resolver.js.map +1 -0
  125. package/dist/pipeline/startup-executor.d.ts +3 -0
  126. package/dist/pipeline/startup-executor.d.ts.map +1 -0
  127. package/dist/pipeline/startup-executor.js +23 -0
  128. package/dist/pipeline/startup-executor.js.map +1 -0
  129. package/dist/pipeline/startup-executor.test.d.ts +2 -0
  130. package/dist/pipeline/startup-executor.test.d.ts.map +1 -0
  131. package/dist/pipeline/startup-executor.test.js +34 -0
  132. package/dist/pipeline/startup-executor.test.js.map +1 -0
  133. package/dist/pipeline/types.d.ts +28 -0
  134. package/dist/pipeline/types.d.ts.map +1 -0
  135. package/dist/pipeline/types.js +12 -0
  136. package/dist/pipeline/types.js.map +1 -0
  137. package/dist/repo-resolver.d.ts +10 -0
  138. package/dist/repo-resolver.d.ts.map +1 -0
  139. package/dist/repo-resolver.js +39 -0
  140. package/dist/repo-resolver.js.map +1 -0
  141. package/dist/spawners/direct-engine-spawner.d.ts +8 -0
  142. package/dist/spawners/direct-engine-spawner.d.ts.map +1 -0
  143. package/dist/spawners/direct-engine-spawner.js +24 -0
  144. package/dist/spawners/direct-engine-spawner.js.map +1 -0
  145. package/dist/state/run-store.d.ts +74 -0
  146. package/dist/state/run-store.d.ts.map +1 -0
  147. package/dist/state/run-store.js +269 -0
  148. package/dist/state/run-store.js.map +1 -0
  149. package/dist/state/run-store.test.d.ts +2 -0
  150. package/dist/state/run-store.test.d.ts.map +1 -0
  151. package/dist/state/run-store.test.js +130 -0
  152. package/dist/state/run-store.test.js.map +1 -0
  153. package/dist/state/state-machine.d.ts +6 -0
  154. package/dist/state/state-machine.d.ts.map +1 -0
  155. package/dist/state/state-machine.js +27 -0
  156. package/dist/state/state-machine.js.map +1 -0
  157. package/dist/state/status-derivation.d.ts +12 -0
  158. package/dist/state/status-derivation.d.ts.map +1 -0
  159. package/dist/state/status-derivation.js +30 -0
  160. package/dist/state/status-derivation.js.map +1 -0
  161. package/package.json +7 -4
  162. package/ARCHITECTURE.md +0 -57
  163. package/src/__tests__/__fixtures__/script-stage/contracts/book-context.contract.yaml +0 -4
  164. package/src/__tests__/engine.conditions-group.test.ts +0 -244
  165. package/src/__tests__/engine.conditions.test.ts +0 -186
  166. package/src/__tests__/engine.resume.test.ts +0 -108
  167. package/src/__tests__/engine.script-stage.test.ts +0 -125
  168. package/src/engine.context-event.test.ts +0 -175
  169. package/src/engine.ts +0 -491
  170. package/src/events.ts +0 -167
  171. package/src/pipeline/agent-loader.test.ts +0 -151
  172. package/src/pipeline/agent-loader.ts +0 -39
  173. package/src/pipeline/condition-evaluator.test.ts +0 -129
  174. package/src/pipeline/condition-evaluator.ts +0 -121
  175. package/src/pipeline/context-pack-loader.ts +0 -63
  176. package/src/pipeline/context-propagation.test.ts +0 -237
  177. package/src/pipeline/context-propagation.ts +0 -235
  178. package/src/pipeline/contract-loader.ts +0 -41
  179. package/src/pipeline/group-orchestrator.ts +0 -483
  180. package/src/pipeline/hook-executor.test.ts +0 -121
  181. package/src/pipeline/hook-executor.ts +0 -87
  182. package/src/pipeline/invariants-loader.test.ts +0 -51
  183. package/src/pipeline/invariants-loader.ts +0 -14
  184. package/src/pipeline/loader.test.ts +0 -128
  185. package/src/pipeline/loader.ts +0 -149
  186. package/src/pipeline/output-validator.test.ts +0 -100
  187. package/src/pipeline/output-validator.ts +0 -40
  188. package/src/pipeline/post-validator.ts +0 -124
  189. package/src/pipeline/skill-loader.test.ts +0 -44
  190. package/src/pipeline/skill-loader.ts +0 -32
  191. package/src/pipeline/stage-executor.ts +0 -654
  192. package/src/pipeline/stage-resolver.ts +0 -8
  193. package/src/pipeline/startup-executor.test.ts +0 -37
  194. package/src/pipeline/startup-executor.ts +0 -32
  195. package/src/pipeline/types.ts +0 -42
  196. package/src/repo-resolver.ts +0 -61
  197. package/src/spawners/direct-engine-spawner.ts +0 -26
  198. package/src/state/run-store.test.ts +0 -157
  199. package/src/state/run-store.ts +0 -362
  200. package/src/state/state-machine.ts +0 -35
  201. package/src/state/status-derivation.ts +0 -36
  202. package/tests/context-pack-loader.test.ts +0 -113
  203. package/tests/context-propagation.test.ts +0 -267
  204. package/tests/direct-engine-spawner.test.ts +0 -102
  205. package/tests/e2e/feature-v5.test.ts +0 -57
  206. package/tests/events.test.ts +0 -56
  207. package/tests/fixtures/agents/test-agent.agent.yaml +0 -5
  208. package/tests/fixtures/contracts/code-gen.contract.yaml +0 -6
  209. package/tests/fixtures/contracts/qa-gate.contract.yaml +0 -15
  210. package/tests/fixtures/contracts/test-contract.contract.yaml +0 -6
  211. package/tests/fixtures/pipelines/group-test.pipeline.yaml +0 -40
  212. package/tests/fixtures/pipelines/simple.pipeline.yaml +0 -15
  213. package/tests/fixtures/pipelines/two-stage.pipeline.yaml +0 -24
  214. package/tests/fixtures/software/pipelines/feature-builder.pipeline.yaml +0 -75
  215. package/tests/fixtures/test-project/agents/test-agent.agent.yaml +0 -5
  216. package/tests/fixtures/test-project/contracts/basic-result.contract.yaml +0 -6
  217. package/tests/fixtures/test-project/contracts/code-gen.contract.yaml +0 -6
  218. package/tests/fixtures/test-project/contracts/maximum-only.contract.yaml +0 -8
  219. package/tests/fixtures/test-project/contracts/qa-gate.contract.yaml +0 -15
  220. package/tests/fixtures/test-project/contracts/strict-result.contract.yaml +0 -7
  221. package/tests/fixtures/test-project/contracts/test-contract.contract.yaml +0 -6
  222. package/tests/fixtures/test-project/pipelines/group-simple.pipeline.yaml +0 -26
  223. package/tests/fixtures/test-project/pipelines/group-test.pipeline.yaml +0 -40
  224. package/tests/fixtures/test-project/pipelines/hook-output-template.pipeline.yaml +0 -19
  225. package/tests/fixtures/test-project/pipelines/hook-reject-on-failure.pipeline.yaml +0 -19
  226. package/tests/fixtures/test-project/pipelines/maximum-only.pipeline.yaml +0 -15
  227. package/tests/fixtures/test-project/pipelines/parallel-collect-all-test.pipeline.yaml +0 -39
  228. package/tests/fixtures/test-project/pipelines/parallel-context-isolation-test.pipeline.yaml +0 -38
  229. package/tests/fixtures/test-project/pipelines/parallel-fail-test.pipeline.yaml +0 -39
  230. package/tests/fixtures/test-project/pipelines/parallel-test.pipeline.yaml +0 -39
  231. package/tests/fixtures/test-project/pipelines/parallel-then-sequential-test.pipeline.yaml +0 -38
  232. package/tests/fixtures/test-project/pipelines/simple.pipeline.yaml +0 -15
  233. package/tests/fixtures/test-project/pipelines/two-stage.pipeline.yaml +0 -24
  234. package/tests/fixtures/test-project/pipelines/with-startup.pipeline.yaml +0 -19
  235. package/tests/loader.test.ts +0 -385
  236. package/tests/post-validator.test.ts +0 -297
  237. package/tests/repo-resolver.test.ts +0 -102
  238. package/tests/run-store.test.ts +0 -143
  239. package/tests/state-machine.test.ts +0 -110
  240. package/tests/unit/context-propagation.test.ts +0 -45
  241. package/tests/unit/engine.test.ts +0 -770
  242. package/tests/unit/group-loop.test.ts +0 -389
  243. package/tests/unit/group-parallel.test.ts +0 -423
  244. package/tests/unit/state/status-derivation.test.ts +0 -88
  245. package/tsconfig.json +0 -24
  246. package/vitest.config.ts +0 -14
@@ -1,102 +0,0 @@
1
- import { describe, it, expect, vi, afterEach } from 'vitest';
2
- import { homedir } from 'node:os';
3
-
4
- vi.mock('node:child_process', () => ({
5
- execSync: vi.fn(),
6
- }));
7
-
8
- vi.mock('node:fs/promises', () => ({
9
- mkdir: vi.fn().mockResolvedValue(undefined),
10
- }));
11
-
12
- import { resolveRepoPath, cloneRepo } from '../src/repo-resolver.js';
13
- import { execSync } from 'node:child_process';
14
-
15
- const mockExecSync = vi.mocked(execSync);
16
-
17
- afterEach(() => {
18
- vi.unstubAllEnvs();
19
- mockExecSync.mockClear();
20
- });
21
-
22
- describe('resolveRepoPath', () => {
23
- it('returns resolved repoPathOverride when provided', async () => {
24
- const result = await resolveRepoPath({ repoPathOverride: 'my-project', pipelineName: 'p' });
25
- expect(result).toMatch(/my-project$/);
26
- });
27
-
28
- it('returns "." when no repoPathOverride and no repoUrl', async () => {
29
- const result = await resolveRepoPath({ pipelineName: 'feature-builder' });
30
- expect(result).toBe('.');
31
- });
32
-
33
- it('throws when repoUrl is set but no rawProjectsDir and no env var', async () => {
34
- vi.stubEnv('STUDIO_PROJECTS_DIR', '');
35
- await expect(
36
- resolveRepoPath({ repoUrl: 'https://github.com/user/repo', pipelineName: 'p' })
37
- ).rejects.toThrow('STUDIO_PROJECTS_DIR is not set');
38
- });
39
-
40
- it('clones when repoUrl is provided with rawProjectsDir', async () => {
41
- const result = await resolveRepoPath({
42
- repoUrl: 'https://github.com/user/repo',
43
- rawProjectsDir: '/tmp/projects',
44
- pipelineName: 'my-pipeline',
45
- });
46
- expect(mockExecSync).toHaveBeenCalledOnce();
47
- const cmd = mockExecSync.mock.calls[0]![0] as string;
48
- expect(cmd).toContain('git clone');
49
- expect(cmd).toContain('https://github.com/user/repo');
50
- expect(result).toContain('/tmp/projects/my-pipeline-');
51
- });
52
-
53
- it('clones when repoUrl is provided via STUDIO_PROJECTS_DIR env var', async () => {
54
- vi.stubEnv('STUDIO_PROJECTS_DIR', '/tmp/envprojects');
55
- const result = await resolveRepoPath({
56
- repoUrl: 'https://github.com/user/repo',
57
- pipelineName: 'test-pipe',
58
- });
59
- expect(mockExecSync).toHaveBeenCalledOnce();
60
- expect(result).toContain('/tmp/envprojects/test-pipe-');
61
- });
62
-
63
- it('expands ~ in rawProjectsDir', async () => {
64
- const result = await resolveRepoPath({
65
- repoUrl: 'https://github.com/user/repo',
66
- rawProjectsDir: '~/projects',
67
- pipelineName: 'p',
68
- });
69
- expect(result).toContain(homedir() + '/projects/p-');
70
- });
71
-
72
- it('repoPathOverride takes precedence over repoUrl', async () => {
73
- const result = await resolveRepoPath({
74
- repoPathOverride: '/explicit/path',
75
- repoUrl: 'https://github.com/user/repo',
76
- rawProjectsDir: '/tmp/p',
77
- pipelineName: 'p',
78
- });
79
- expect(result).toBe('/explicit/path');
80
- expect(mockExecSync).not.toHaveBeenCalled();
81
- });
82
-
83
- it('passes branch arg when branch is set', async () => {
84
- await resolveRepoPath({
85
- repoUrl: 'https://github.com/user/repo',
86
- rawProjectsDir: '/tmp/projects',
87
- pipelineName: 'p',
88
- branch: 'main',
89
- });
90
- const cmd = mockExecSync.mock.calls[0]![0] as string;
91
- expect(cmd).toContain('--branch main');
92
- });
93
- });
94
-
95
- describe('cloneRepo', () => {
96
- it('throws a descriptive error when git clone fails', async () => {
97
- mockExecSync.mockImplementationOnce(() => { throw new Error('repository not found'); });
98
- await expect(
99
- cloneRepo('https://github.com/bad/repo', '/tmp/p', 'pipeline')
100
- ).rejects.toThrow('Failed to clone https://github.com/bad/repo');
101
- });
102
- });
@@ -1,143 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { InMemoryRunStore } from '../src/state/run-store.js';
3
- import type { PipelineRun } from '@studio-foundation/contracts';
4
-
5
- function makePipelineRun(overrides: Partial<PipelineRun> = {}): PipelineRun {
6
- return {
7
- id: `run-${Math.random().toString(36).slice(2, 8)}`,
8
- pipeline_name: 'test-pipeline',
9
- status: 'success',
10
- started_at: new Date().toISOString(),
11
- completed_at: new Date().toISOString(),
12
- stages: [],
13
- ...overrides,
14
- } as PipelineRun;
15
- }
16
-
17
- describe('InMemoryRunStore', () => {
18
- let store: InMemoryRunStore;
19
-
20
- beforeEach(() => {
21
- store = new InMemoryRunStore();
22
- });
23
-
24
- it('saves and retrieves a pipeline run', () => {
25
- const run = makePipelineRun({ id: 'run-1' });
26
- store.savePipelineRun(run);
27
-
28
- const retrieved = store.getPipelineRun('run-1');
29
- expect(retrieved).not.toBeNull();
30
- expect(retrieved!.id).toBe('run-1');
31
- expect(retrieved!.pipeline_name).toBe('test-pipeline');
32
- });
33
-
34
- it('returns null for non-existent run', () => {
35
- expect(store.getPipelineRun('nonexistent')).toBeNull();
36
- });
37
-
38
- it('returns a clone (not the original reference)', () => {
39
- const run = makePipelineRun({ id: 'run-clone' });
40
- store.savePipelineRun(run);
41
-
42
- const retrieved = store.getPipelineRun('run-clone');
43
- expect(retrieved).not.toBe(run);
44
- });
45
-
46
- it('listPipelineRuns returns all runs', () => {
47
- store.savePipelineRun(makePipelineRun({ id: 'r1', started_at: '2024-01-01T00:00:00Z' }));
48
- store.savePipelineRun(makePipelineRun({ id: 'r2', started_at: '2024-01-02T00:00:00Z' }));
49
- store.savePipelineRun(makePipelineRun({ id: 'r3', started_at: '2024-01-03T00:00:00Z' }));
50
-
51
- const runs = store.listPipelineRuns();
52
- expect(runs).toHaveLength(3);
53
- });
54
-
55
- it('listPipelineRuns sorts by started_at descending', () => {
56
- store.savePipelineRun(makePipelineRun({ id: 'r1', started_at: '2024-01-01T00:00:00Z' }));
57
- store.savePipelineRun(makePipelineRun({ id: 'r3', started_at: '2024-01-03T00:00:00Z' }));
58
- store.savePipelineRun(makePipelineRun({ id: 'r2', started_at: '2024-01-02T00:00:00Z' }));
59
-
60
- const runs = store.listPipelineRuns();
61
- expect(runs[0].id).toBe('r3');
62
- expect(runs[1].id).toBe('r2');
63
- expect(runs[2].id).toBe('r1');
64
- });
65
-
66
- it('listPipelineRuns respects limit', () => {
67
- store.savePipelineRun(makePipelineRun({ id: 'r1', started_at: '2024-01-01T00:00:00Z' }));
68
- store.savePipelineRun(makePipelineRun({ id: 'r2', started_at: '2024-01-02T00:00:00Z' }));
69
- store.savePipelineRun(makePipelineRun({ id: 'r3', started_at: '2024-01-03T00:00:00Z' }));
70
-
71
- const runs = store.listPipelineRuns({ limit: 2 });
72
- expect(runs).toHaveLength(2);
73
- });
74
-
75
- it('listPipelineRuns filters by status', () => {
76
- store.savePipelineRun(makePipelineRun({ id: 'r1', status: 'success' }));
77
- store.savePipelineRun(makePipelineRun({ id: 'r2', status: 'failed' }));
78
- store.savePipelineRun(makePipelineRun({ id: 'r3', status: 'success' }));
79
-
80
- const runs = store.listPipelineRuns({ status: 'failed' });
81
- expect(runs).toHaveLength(1);
82
- expect(runs[0].id).toBe('r2');
83
- });
84
-
85
- it('getLatestRun returns the most recent run', () => {
86
- store.savePipelineRun(makePipelineRun({ id: 'r1', started_at: '2024-01-01T00:00:00Z' }));
87
- store.savePipelineRun(makePipelineRun({ id: 'r2', started_at: '2024-01-03T00:00:00Z' }));
88
- store.savePipelineRun(makePipelineRun({ id: 'r3', started_at: '2024-01-02T00:00:00Z' }));
89
-
90
- const latest = store.getLatestRun();
91
- expect(latest).not.toBeNull();
92
- expect(latest!.id).toBe('r2');
93
- });
94
-
95
- it('getLatestRun filters by pipeline name', () => {
96
- store.savePipelineRun(makePipelineRun({
97
- id: 'r1',
98
- pipeline_name: 'feature-builder',
99
- started_at: '2024-01-01T00:00:00Z',
100
- }));
101
- store.savePipelineRun(makePipelineRun({
102
- id: 'r2',
103
- pipeline_name: 'other-pipeline',
104
- started_at: '2024-01-03T00:00:00Z',
105
- }));
106
-
107
- const latest = store.getLatestRun('feature-builder');
108
- expect(latest).not.toBeNull();
109
- expect(latest!.id).toBe('r1');
110
- });
111
-
112
- it('getLatestRun returns null when empty', () => {
113
- expect(store.getLatestRun()).toBeNull();
114
- });
115
-
116
- it('overwrites existing run on save with same id', () => {
117
- const run = makePipelineRun({ id: 'r1', status: 'running' });
118
- store.savePipelineRun(run);
119
-
120
- const updated = { ...run, status: 'success' as const, completed_at: new Date().toISOString() };
121
- store.savePipelineRun(updated);
122
-
123
- const retrieved = store.getPipelineRun('r1');
124
- expect(retrieved!.status).toBe('success');
125
- });
126
-
127
- describe('log path', () => {
128
- it('returns null for unknown run', () => {
129
- expect(store.getLogPath('nonexistent')).toBeNull();
130
- });
131
-
132
- it('saves and retrieves a log path', () => {
133
- store.saveLogPath('run-1', '/tmp/.studio/runs/log.jsonl');
134
- expect(store.getLogPath('run-1')).toBe('/tmp/.studio/runs/log.jsonl');
135
- });
136
-
137
- it('overwrites existing log path', () => {
138
- store.saveLogPath('run-1', '/tmp/old.jsonl');
139
- store.saveLogPath('run-1', '/tmp/new.jsonl');
140
- expect(store.getLogPath('run-1')).toBe('/tmp/new.jsonl');
141
- });
142
- });
143
- });
@@ -1,110 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { isValidTransition, transition } from '../src/state/state-machine.js';
3
-
4
- describe('isValidTransition', () => {
5
- it('pending → running is valid', () => {
6
- expect(isValidTransition('pending', 'running')).toBe(true);
7
- });
8
-
9
- it('running → success is valid', () => {
10
- expect(isValidTransition('running', 'success')).toBe(true);
11
- });
12
-
13
- it('running → failed is valid', () => {
14
- expect(isValidTransition('running', 'failed')).toBe(true);
15
- });
16
-
17
- it('pending → skipped is valid', () => {
18
- expect(isValidTransition('pending', 'skipped')).toBe(true);
19
- });
20
-
21
- it('pending → success is INVALID', () => {
22
- expect(isValidTransition('pending', 'success')).toBe(false);
23
- });
24
-
25
- it('success → running is INVALID', () => {
26
- expect(isValidTransition('success', 'running')).toBe(false);
27
- });
28
-
29
- it('failed → running is INVALID', () => {
30
- expect(isValidTransition('failed', 'running')).toBe(false);
31
- });
32
-
33
- it('success → failed is INVALID', () => {
34
- expect(isValidTransition('success', 'failed')).toBe(false);
35
- });
36
-
37
- it('pending → failed is INVALID', () => {
38
- expect(isValidTransition('pending', 'failed')).toBe(false);
39
- });
40
-
41
- it('running → rejected is valid', () => {
42
- expect(isValidTransition('running', 'rejected')).toBe(true);
43
- });
44
-
45
- it('pending → rejected is INVALID', () => {
46
- expect(isValidTransition('pending', 'rejected')).toBe(false);
47
- });
48
-
49
- it('rejected → running is INVALID', () => {
50
- expect(isValidTransition('rejected', 'running')).toBe(false);
51
- });
52
-
53
- it('running → cancelled is valid', () => {
54
- expect(isValidTransition('running', 'cancelled')).toBe(true);
55
- });
56
-
57
- it('pending → cancelled is INVALID', () => {
58
- expect(isValidTransition('pending', 'cancelled')).toBe(false);
59
- });
60
-
61
- it('cancelled → running is INVALID', () => {
62
- expect(isValidTransition('cancelled', 'running')).toBe(false);
63
- });
64
- });
65
-
66
- describe('transition', () => {
67
- it('pending + start → running', () => {
68
- expect(transition('pending', 'start')).toBe('running');
69
- });
70
-
71
- it('running + succeed → success', () => {
72
- expect(transition('running', 'succeed')).toBe('success');
73
- });
74
-
75
- it('running + fail → failed', () => {
76
- expect(transition('running', 'fail')).toBe('failed');
77
- });
78
-
79
- it('pending + skip → skipped', () => {
80
- expect(transition('pending', 'skip')).toBe('skipped');
81
- });
82
-
83
- it('pending + succeed throws', () => {
84
- expect(() => transition('pending', 'succeed')).toThrow('Invalid state transition');
85
- });
86
-
87
- it('success + start throws', () => {
88
- expect(() => transition('success', 'start')).toThrow('Invalid state transition');
89
- });
90
-
91
- it('failed + succeed throws', () => {
92
- expect(() => transition('failed', 'succeed')).toThrow('Invalid state transition');
93
- });
94
-
95
- it('running + reject → rejected', () => {
96
- expect(transition('running', 'reject')).toBe('rejected');
97
- });
98
-
99
- it('pending + reject throws', () => {
100
- expect(() => transition('pending', 'reject')).toThrow('Invalid state transition');
101
- });
102
-
103
- it('running + cancel → cancelled', () => {
104
- expect(transition('running', 'cancel')).toBe('cancelled');
105
- });
106
-
107
- it('pending + cancel throws', () => {
108
- expect(() => transition('pending', 'cancel')).toThrow('Invalid state transition');
109
- });
110
- });
@@ -1,45 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- getContextForStage,
4
- createInitialContext,
5
- } from '../../src/pipeline/context-propagation.js';
6
- import type { StageDefinition } from '@studio-foundation/contracts';
7
-
8
- const makeStage = (name: string, includes: string[]): StageDefinition => ({
9
- name,
10
- kind: 'analysis',
11
- agent: 'test-agent',
12
- context: { include: includes },
13
- });
14
-
15
- describe('getContextForStage — stage_name', () => {
16
- it('injects stage_name when included', () => {
17
- const ctx = createInitialContext({ userId: 'u1' });
18
- const stage = makeStage('recipe-1', ['stage_name']);
19
-
20
- const agentCtx = getContextForStage(ctx, stage);
21
-
22
- expect(agentCtx.stage_name).toBe('recipe-1');
23
- });
24
-
25
- it('does not inject stage_name when not included', () => {
26
- const ctx = createInitialContext({ userId: 'u1' });
27
- const stage = makeStage('recipe-1', ['input']);
28
-
29
- const agentCtx = getContextForStage(ctx, stage);
30
-
31
- expect(agentCtx.stage_name).toBeUndefined();
32
- });
33
-
34
- it('injects correct name for each stage', () => {
35
- const ctx = createInitialContext('input data');
36
- const stages = ['recipe-1', 'recipe-2', 'recipe-5'].map(n =>
37
- makeStage(n, ['stage_name'])
38
- );
39
-
40
- for (const stage of stages) {
41
- const agentCtx = getContextForStage(ctx, stage);
42
- expect(agentCtx.stage_name).toBe(stage.name);
43
- }
44
- });
45
- });