@cat-factory/orchestration 0.6.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 (182) hide show
  1. package/LICENSE +21 -0
  2. package/dist/container.d.ts +460 -0
  3. package/dist/container.d.ts.map +1 -0
  4. package/dist/container.js +657 -0
  5. package/dist/container.js.map +1 -0
  6. package/dist/index.d.ts +29 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +31 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/modules/board/BoardService.d.ts +125 -0
  11. package/dist/modules/board/BoardService.d.ts.map +1 -0
  12. package/dist/modules/board/BoardService.js +496 -0
  13. package/dist/modules/board/BoardService.js.map +1 -0
  14. package/dist/modules/board/board.logic.d.ts +17 -0
  15. package/dist/modules/board/board.logic.d.ts.map +1 -0
  16. package/dist/modules/board/board.logic.js +51 -0
  17. package/dist/modules/board/board.logic.js.map +1 -0
  18. package/dist/modules/boardScan/BoardScanService.d.ts +35 -0
  19. package/dist/modules/boardScan/BoardScanService.d.ts.map +1 -0
  20. package/dist/modules/boardScan/BoardScanService.js +91 -0
  21. package/dist/modules/boardScan/BoardScanService.js.map +1 -0
  22. package/dist/modules/boardScan/board-scan.logic.d.ts +10 -0
  23. package/dist/modules/boardScan/board-scan.logic.d.ts.map +1 -0
  24. package/dist/modules/boardScan/board-scan.logic.js +26 -0
  25. package/dist/modules/boardScan/board-scan.logic.js.map +1 -0
  26. package/dist/modules/bootstrap/BootstrapService.d.ts +114 -0
  27. package/dist/modules/bootstrap/BootstrapService.d.ts.map +1 -0
  28. package/dist/modules/bootstrap/BootstrapService.js +516 -0
  29. package/dist/modules/bootstrap/BootstrapService.js.map +1 -0
  30. package/dist/modules/clarity/ClarityReviewService.d.ts +48 -0
  31. package/dist/modules/clarity/ClarityReviewService.d.ts.map +1 -0
  32. package/dist/modules/clarity/ClarityReviewService.js +63 -0
  33. package/dist/modules/clarity/ClarityReviewService.js.map +1 -0
  34. package/dist/modules/clarity/clarity.logic.d.ts +36 -0
  35. package/dist/modules/clarity/clarity.logic.d.ts.map +1 -0
  36. package/dist/modules/clarity/clarity.logic.js +98 -0
  37. package/dist/modules/clarity/clarity.logic.js.map +1 -0
  38. package/dist/modules/estimation/estimate.logic.d.ts +11 -0
  39. package/dist/modules/estimation/estimate.logic.d.ts.map +1 -0
  40. package/dist/modules/estimation/estimate.logic.js +37 -0
  41. package/dist/modules/estimation/estimate.logic.js.map +1 -0
  42. package/dist/modules/execution/AgentContextBuilder.d.ts +114 -0
  43. package/dist/modules/execution/AgentContextBuilder.d.ts.map +1 -0
  44. package/dist/modules/execution/AgentContextBuilder.js +316 -0
  45. package/dist/modules/execution/AgentContextBuilder.js.map +1 -0
  46. package/dist/modules/execution/CompanionController.d.ts +60 -0
  47. package/dist/modules/execution/CompanionController.d.ts.map +1 -0
  48. package/dist/modules/execution/CompanionController.js +216 -0
  49. package/dist/modules/execution/CompanionController.js.map +1 -0
  50. package/dist/modules/execution/ExecutionService.d.ts +874 -0
  51. package/dist/modules/execution/ExecutionService.d.ts.map +1 -0
  52. package/dist/modules/execution/ExecutionService.js +2921 -0
  53. package/dist/modules/execution/ExecutionService.js.map +1 -0
  54. package/dist/modules/execution/MergeResolver.d.ts +34 -0
  55. package/dist/modules/execution/MergeResolver.d.ts.map +1 -0
  56. package/dist/modules/execution/MergeResolver.js +81 -0
  57. package/dist/modules/execution/MergeResolver.js.map +1 -0
  58. package/dist/modules/execution/ReviewGateController.d.ts +163 -0
  59. package/dist/modules/execution/ReviewGateController.d.ts.map +1 -0
  60. package/dist/modules/execution/ReviewGateController.js +251 -0
  61. package/dist/modules/execution/ReviewGateController.js.map +1 -0
  62. package/dist/modules/execution/TesterController.d.ts +61 -0
  63. package/dist/modules/execution/TesterController.d.ts.map +1 -0
  64. package/dist/modules/execution/TesterController.js +215 -0
  65. package/dist/modules/execution/TesterController.js.map +1 -0
  66. package/dist/modules/execution/advance.d.ts +84 -0
  67. package/dist/modules/execution/advance.d.ts.map +1 -0
  68. package/dist/modules/execution/advance.js +2 -0
  69. package/dist/modules/execution/advance.js.map +1 -0
  70. package/dist/modules/execution/artifact-review.logic.d.ts +25 -0
  71. package/dist/modules/execution/artifact-review.logic.d.ts.map +1 -0
  72. package/dist/modules/execution/artifact-review.logic.js +39 -0
  73. package/dist/modules/execution/artifact-review.logic.js.map +1 -0
  74. package/dist/modules/execution/ci.logic.d.ts +101 -0
  75. package/dist/modules/execution/ci.logic.d.ts.map +1 -0
  76. package/dist/modules/execution/ci.logic.js +117 -0
  77. package/dist/modules/execution/ci.logic.js.map +1 -0
  78. package/dist/modules/execution/drive.d.ts +47 -0
  79. package/dist/modules/execution/drive.d.ts.map +1 -0
  80. package/dist/modules/execution/drive.js +112 -0
  81. package/dist/modules/execution/drive.js.map +1 -0
  82. package/dist/modules/execution/gates.d.ts +97 -0
  83. package/dist/modules/execution/gates.d.ts.map +1 -0
  84. package/dist/modules/execution/gates.js +2 -0
  85. package/dist/modules/execution/gates.js.map +1 -0
  86. package/dist/modules/execution/individualVendors.logic.d.ts +22 -0
  87. package/dist/modules/execution/individualVendors.logic.d.ts.map +1 -0
  88. package/dist/modules/execution/individualVendors.logic.js +33 -0
  89. package/dist/modules/execution/individualVendors.logic.js.map +1 -0
  90. package/dist/modules/execution/job.logic.d.ts +52 -0
  91. package/dist/modules/execution/job.logic.d.ts.map +1 -0
  92. package/dist/modules/execution/job.logic.js +56 -0
  93. package/dist/modules/execution/job.logic.js.map +1 -0
  94. package/dist/modules/execution/release.logic.d.ts +43 -0
  95. package/dist/modules/execution/release.logic.d.ts.map +1 -0
  96. package/dist/modules/execution/release.logic.js +49 -0
  97. package/dist/modules/execution/release.logic.js.map +1 -0
  98. package/dist/modules/execution/retry.logic.d.ts +40 -0
  99. package/dist/modules/execution/retry.logic.d.ts.map +1 -0
  100. package/dist/modules/execution/retry.logic.js +83 -0
  101. package/dist/modules/execution/retry.logic.js.map +1 -0
  102. package/dist/modules/execution/stepGating.logic.d.ts +15 -0
  103. package/dist/modules/execution/stepGating.logic.d.ts.map +1 -0
  104. package/dist/modules/execution/stepGating.logic.js +29 -0
  105. package/dist/modules/execution/stepGating.logic.js.map +1 -0
  106. package/dist/modules/execution/stepResolvers.d.ts +41 -0
  107. package/dist/modules/execution/stepResolvers.d.ts.map +1 -0
  108. package/dist/modules/execution/stepResolvers.js +2 -0
  109. package/dist/modules/execution/stepResolvers.js.map +1 -0
  110. package/dist/modules/execution/tester-infra.logic.d.ts +42 -0
  111. package/dist/modules/execution/tester-infra.logic.d.ts.map +1 -0
  112. package/dist/modules/execution/tester-infra.logic.js +46 -0
  113. package/dist/modules/execution/tester-infra.logic.js.map +1 -0
  114. package/dist/modules/merge/MergePresetService.d.ts +32 -0
  115. package/dist/modules/merge/MergePresetService.d.ts.map +1 -0
  116. package/dist/modules/merge/MergePresetService.js +109 -0
  117. package/dist/modules/merge/MergePresetService.js.map +1 -0
  118. package/dist/modules/modelDefaults/ModelDefaultsService.d.ts +22 -0
  119. package/dist/modules/modelDefaults/ModelDefaultsService.d.ts.map +1 -0
  120. package/dist/modules/modelDefaults/ModelDefaultsService.js +28 -0
  121. package/dist/modules/modelDefaults/ModelDefaultsService.js.map +1 -0
  122. package/dist/modules/notifications/NotificationService.d.ts +74 -0
  123. package/dist/modules/notifications/NotificationService.d.ts.map +1 -0
  124. package/dist/modules/notifications/NotificationService.js +131 -0
  125. package/dist/modules/notifications/NotificationService.js.map +1 -0
  126. package/dist/modules/observability/LlmObservabilityService.d.ts +121 -0
  127. package/dist/modules/observability/LlmObservabilityService.d.ts.map +1 -0
  128. package/dist/modules/observability/LlmObservabilityService.js +140 -0
  129. package/dist/modules/observability/LlmObservabilityService.js.map +1 -0
  130. package/dist/modules/observability/observability.logic.d.ts +57 -0
  131. package/dist/modules/observability/observability.logic.d.ts.map +1 -0
  132. package/dist/modules/observability/observability.logic.js +186 -0
  133. package/dist/modules/observability/observability.logic.js.map +1 -0
  134. package/dist/modules/pipelines/PipelineService.d.ts +54 -0
  135. package/dist/modules/pipelines/PipelineService.d.ts.map +1 -0
  136. package/dist/modules/pipelines/PipelineService.js +226 -0
  137. package/dist/modules/pipelines/PipelineService.js.map +1 -0
  138. package/dist/modules/pipelines/pipelineShape.d.ts +53 -0
  139. package/dist/modules/pipelines/pipelineShape.d.ts.map +1 -0
  140. package/dist/modules/pipelines/pipelineShape.js +74 -0
  141. package/dist/modules/pipelines/pipelineShape.js.map +1 -0
  142. package/dist/modules/recurring/RecurringPipelineService.d.ts +76 -0
  143. package/dist/modules/recurring/RecurringPipelineService.d.ts.map +1 -0
  144. package/dist/modules/recurring/RecurringPipelineService.js +295 -0
  145. package/dist/modules/recurring/RecurringPipelineService.js.map +1 -0
  146. package/dist/modules/recurring/TrackerSettingsService.d.ts +16 -0
  147. package/dist/modules/recurring/TrackerSettingsService.d.ts.map +1 -0
  148. package/dist/modules/recurring/TrackerSettingsService.js +30 -0
  149. package/dist/modules/recurring/TrackerSettingsService.js.map +1 -0
  150. package/dist/modules/recurring/schedule.logic.d.ts +14 -0
  151. package/dist/modules/recurring/schedule.logic.d.ts.map +1 -0
  152. package/dist/modules/recurring/schedule.logic.js +85 -0
  153. package/dist/modules/recurring/schedule.logic.js.map +1 -0
  154. package/dist/modules/releaseHealth/ReleaseHealthService.d.ts +38 -0
  155. package/dist/modules/releaseHealth/ReleaseHealthService.d.ts.map +1 -0
  156. package/dist/modules/releaseHealth/ReleaseHealthService.js +96 -0
  157. package/dist/modules/releaseHealth/ReleaseHealthService.js.map +1 -0
  158. package/dist/modules/requirements/RequirementReviewService.d.ts +48 -0
  159. package/dist/modules/requirements/RequirementReviewService.d.ts.map +1 -0
  160. package/dist/modules/requirements/RequirementReviewService.js +83 -0
  161. package/dist/modules/requirements/RequirementReviewService.js.map +1 -0
  162. package/dist/modules/requirements/requirements.logic.d.ts +93 -0
  163. package/dist/modules/requirements/requirements.logic.d.ts.map +1 -0
  164. package/dist/modules/requirements/requirements.logic.js +203 -0
  165. package/dist/modules/requirements/requirements.logic.js.map +1 -0
  166. package/dist/modules/review/IterativeReviewService.d.ts +175 -0
  167. package/dist/modules/review/IterativeReviewService.d.ts.map +1 -0
  168. package/dist/modules/review/IterativeReviewService.js +327 -0
  169. package/dist/modules/review/IterativeReviewService.js.map +1 -0
  170. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.d.ts +20 -0
  171. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.d.ts.map +1 -0
  172. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.js +26 -0
  173. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.js.map +1 -0
  174. package/dist/modules/services/ServiceMountService.d.ts +48 -0
  175. package/dist/modules/services/ServiceMountService.d.ts.map +1 -0
  176. package/dist/modules/services/ServiceMountService.js +90 -0
  177. package/dist/modules/services/ServiceMountService.js.map +1 -0
  178. package/dist/modules/settings/WorkspaceSettingsService.d.ts +22 -0
  179. package/dist/modules/settings/WorkspaceSettingsService.d.ts.map +1 -0
  180. package/dist/modules/settings/WorkspaceSettingsService.js +50 -0
  181. package/dist/modules/settings/WorkspaceSettingsService.js.map +1 -0
  182. package/package.json +41 -0
@@ -0,0 +1,226 @@
1
+ import { assertFound, ValidationError } from '@cat-factory/kernel';
2
+ import { requireWorkspace } from '@cat-factory/kernel';
3
+ import { validatePipelineShape } from './pipelineShape.js';
4
+ /**
5
+ * The post-release-health gate watches a released PR's observability signals, so it is
6
+ * meaningless (and rejected) on a workspace with no observability integration wired. It
7
+ * is NOT part of any default pipeline — a user adds it deliberately, and only then.
8
+ */
9
+ const OBSERVABILITY_GATED_KIND = 'post-release-health';
10
+ /** Saved, reusable pipelines (the pipeline palette). */
11
+ export class PipelineService {
12
+ workspaceRepository;
13
+ pipelineRepository;
14
+ idGenerator;
15
+ datadogConnectionRepository;
16
+ constructor({ workspaceRepository, pipelineRepository, idGenerator, datadogConnectionRepository, }) {
17
+ this.workspaceRepository = workspaceRepository;
18
+ this.pipelineRepository = pipelineRepository;
19
+ this.idGenerator = idGenerator;
20
+ this.datadogConnectionRepository = datadogConnectionRepository;
21
+ }
22
+ /**
23
+ * The post-release-health gate is only meaningful with an observability integration, so
24
+ * reject a chain that includes an ENABLED post-release-health step unless the workspace
25
+ * has one wired. Validated only when the chain/enable mask is being authored (create, or
26
+ * an update that changes them) so an unrelated edit to an existing pipeline never trips.
27
+ */
28
+ async assertObservabilityGatedStepAllowed(workspaceId, agentKinds, enabled) {
29
+ const present = agentKinds.some((kind, i) => kind === OBSERVABILITY_GATED_KIND && enabled?.[i] !== false);
30
+ if (!present)
31
+ return;
32
+ const connection = await this.datadogConnectionRepository?.get(workspaceId);
33
+ if (!connection) {
34
+ throw new ValidationError(`The '${OBSERVABILITY_GATED_KIND}' step needs an observability integration. Connect Datadog for this workspace first.`);
35
+ }
36
+ }
37
+ requireWorkspace(workspaceId) {
38
+ return requireWorkspace(this.workspaceRepository, workspaceId);
39
+ }
40
+ async list(workspaceId) {
41
+ await this.requireWorkspace(workspaceId);
42
+ return this.pipelineRepository.listByWorkspace(workspaceId);
43
+ }
44
+ async create(workspaceId, input) {
45
+ await this.requireWorkspace(workspaceId);
46
+ assertSomeEnabled(input.agentKinds, input.enabled);
47
+ validatePipelineShape({
48
+ agentKinds: input.agentKinds,
49
+ enabled: input.enabled,
50
+ gating: input.gating,
51
+ });
52
+ await this.assertObservabilityGatedStepAllowed(workspaceId, input.agentKinds, input.enabled);
53
+ const pipeline = {
54
+ id: this.idGenerator.next('pl'),
55
+ name: input.name.trim() || 'Untitled pipeline',
56
+ agentKinds: [...input.agentKinds],
57
+ ...alignedGates(input.agentKinds, input.gates),
58
+ ...alignedThresholds(input.agentKinds, input.thresholds),
59
+ ...alignedEnabled(input.agentKinds, input.enabled),
60
+ ...alignedConsensus(input.agentKinds, input.consensus),
61
+ ...alignedGating(input.agentKinds, input.gating),
62
+ ...normalizedLabels(input.labels),
63
+ };
64
+ await this.pipelineRepository.insert(workspaceId, pipeline);
65
+ return pipeline;
66
+ }
67
+ /**
68
+ * Clone any pipeline (built-in or custom) into a new, editable copy. The copy keeps
69
+ * the source's steps / gates / thresholds / enable flags but is never `builtin`, so
70
+ * it can be edited — this is how a built-in template is "made editable".
71
+ */
72
+ async clone(workspaceId, sourceId, input) {
73
+ await this.requireWorkspace(workspaceId);
74
+ const source = assertFound(await this.pipelineRepository.get(workspaceId, sourceId), 'Pipeline', sourceId);
75
+ // Validate the source's shape so a clone is rejected at clone time, not deferred to run
76
+ // start — the same guarantee `create`/`update` give (a built-in can't ship invalid, but
77
+ // a custom source mutated out of band could).
78
+ validatePipelineShape({
79
+ agentKinds: source.agentKinds,
80
+ enabled: source.enabled,
81
+ gating: source.gating,
82
+ });
83
+ const pipeline = {
84
+ id: this.idGenerator.next('pl'),
85
+ name: input.name?.trim() || `${source.name} (copy)`,
86
+ agentKinds: [...source.agentKinds],
87
+ ...(source.gates ? { gates: [...source.gates] } : {}),
88
+ ...(source.thresholds ? { thresholds: [...source.thresholds] } : {}),
89
+ ...(source.enabled ? { enabled: [...source.enabled] } : {}),
90
+ ...(source.consensus ? { consensus: [...source.consensus] } : {}),
91
+ ...(source.gating ? { gating: [...source.gating] } : {}),
92
+ ...(source.labels ? { labels: [...source.labels] } : {}),
93
+ // A clone is a fresh, active, editable copy — never `builtin`, never `archived`.
94
+ };
95
+ await this.pipelineRepository.insert(workspaceId, pipeline);
96
+ return pipeline;
97
+ }
98
+ /**
99
+ * Edit a custom pipeline in place. Only the supplied fields change; passing
100
+ * `agentKinds` replaces the whole chain and re-aligns the parallel arrays. Built-in
101
+ * catalog templates are read-only and reject this — clone them first.
102
+ */
103
+ async update(workspaceId, id, input) {
104
+ await this.requireWorkspace(workspaceId);
105
+ const existing = assertFound(await this.pipelineRepository.get(workspaceId, id), 'Pipeline', id);
106
+ if (existing.builtin) {
107
+ throw new ValidationError('Built-in pipelines are read-only. Clone it to make an editable copy.');
108
+ }
109
+ const agentKinds = input.agentKinds ?? existing.agentKinds;
110
+ const gates = input.gates ?? existing.gates;
111
+ const thresholds = input.thresholds ?? existing.thresholds;
112
+ const enabled = input.enabled ?? existing.enabled;
113
+ const consensus = input.consensus ?? existing.consensus;
114
+ const gating = input.gating ?? existing.gating;
115
+ const labels = input.labels ?? existing.labels;
116
+ assertSomeEnabled(agentKinds, enabled);
117
+ // Re-validate the shape against the EFFECTIVE (enabled) chain — disabling a producer
118
+ // while leaving its companion on would orphan the companion, and adding gating without
119
+ // an estimator is illegal — so validate whenever the chain, enable flags, OR gating
120
+ // change, not just on a chain replacement.
121
+ if (input.agentKinds || input.enabled || input.gating) {
122
+ validatePipelineShape({ agentKinds, enabled, gating });
123
+ await this.assertObservabilityGatedStepAllowed(workspaceId, agentKinds, enabled);
124
+ }
125
+ const pipeline = {
126
+ id: existing.id,
127
+ name: input.name?.trim() || existing.name,
128
+ agentKinds: [...agentKinds],
129
+ ...alignedGates(agentKinds, gates),
130
+ ...alignedThresholds(agentKinds, thresholds),
131
+ ...alignedEnabled(agentKinds, enabled),
132
+ ...alignedConsensus(agentKinds, consensus),
133
+ ...alignedGating(agentKinds, gating),
134
+ ...normalizedLabels(labels),
135
+ // `archived` is organization-only state, mutated via `organize` — preserved here.
136
+ ...(existing.archived ? { archived: true } : {}),
137
+ };
138
+ await this.pipelineRepository.update(workspaceId, pipeline);
139
+ return pipeline;
140
+ }
141
+ async remove(workspaceId, id) {
142
+ await this.requireWorkspace(workspaceId);
143
+ const existing = assertFound(await this.pipelineRepository.get(workspaceId, id), 'Pipeline', id);
144
+ // Built-in catalog templates are read-only — they can be cloned but never deleted
145
+ // (matching `update`), so the curated palette is always present. Clone to customise.
146
+ if (existing.builtin) {
147
+ throw new ValidationError('Built-in pipelines are read-only and cannot be deleted.');
148
+ }
149
+ await this.pipelineRepository.delete(workspaceId, id);
150
+ }
151
+ /**
152
+ * Set a pipeline's organizational metadata (labels and/or archive state). This is the
153
+ * ONLY mutation allowed on a BUILT-IN pipeline — it touches the library view, not the
154
+ * pipeline's structure, so a built-in can be tagged or archived while staying read-only
155
+ * for its steps. Only the supplied fields change.
156
+ */
157
+ async organize(workspaceId, id, input) {
158
+ await this.requireWorkspace(workspaceId);
159
+ const existing = assertFound(await this.pipelineRepository.get(workspaceId, id), 'Pipeline', id);
160
+ // Explicit-undefined check (not `??`): passing `labels: []` clears the labels, while
161
+ // omitting the field preserves the existing ones.
162
+ const labels = input.labels !== undefined ? cleanLabels(input.labels) : existing.labels;
163
+ const archived = input.archived !== undefined ? input.archived : existing.archived;
164
+ const pipeline = {
165
+ ...existing,
166
+ ...(labels && labels.length ? { labels } : { labels: undefined }),
167
+ ...(archived ? { archived: true } : { archived: undefined }),
168
+ };
169
+ await this.pipelineRepository.update(workspaceId, pipeline);
170
+ return pipeline;
171
+ }
172
+ }
173
+ // Keep gates aligned to agentKinds; only persist when at least one step is gated so an
174
+ // all-false / absent array stays absent (a straight-through run).
175
+ function alignedGates(agentKinds, gates) {
176
+ return gates?.some(Boolean) ? { gates: agentKinds.map((_, i) => gates[i] ?? false) } : {};
177
+ }
178
+ // Keep thresholds aligned to agentKinds; only persist when at least one step sets an
179
+ // explicit value (else companions fall back to their default bar).
180
+ function alignedThresholds(agentKinds, thresholds) {
181
+ return thresholds?.some((t) => t != null)
182
+ ? { thresholds: agentKinds.map((_, i) => thresholds[i] ?? null) }
183
+ : {};
184
+ }
185
+ // Keep enable flags aligned to agentKinds; only persist when at least one step is
186
+ // explicitly disabled (the default is "every step runs", i.e. no array at all).
187
+ function alignedEnabled(agentKinds, enabled) {
188
+ return enabled?.some((e) => e === false)
189
+ ? { enabled: agentKinds.map((_, i) => enabled[i] ?? true) }
190
+ : {};
191
+ }
192
+ // Keep consensus configs aligned to agentKinds; only persist when at least one step is
193
+ // consensus-enabled (the default is no array at all → every step is a standard agent).
194
+ function alignedConsensus(agentKinds, consensus) {
195
+ return consensus?.some((c) => c?.enabled)
196
+ ? { consensus: agentKinds.map((_, i) => consensus[i] ?? null) }
197
+ : {};
198
+ }
199
+ // Keep gating aligned to agentKinds; only persist when at least one step has gating enabled
200
+ // (the default is no array at all → every step always runs).
201
+ function alignedGating(agentKinds, gating) {
202
+ return gating?.some((g) => g?.enabled)
203
+ ? { gating: agentKinds.map((_, i) => gating[i] ?? null) }
204
+ : {};
205
+ }
206
+ // Trim, drop blanks, and dedupe labels; undefined when none remain.
207
+ function cleanLabels(labels) {
208
+ if (!labels)
209
+ return undefined;
210
+ const cleaned = [...new Set(labels.map((l) => l.trim()).filter(Boolean))];
211
+ return cleaned.length ? cleaned : undefined;
212
+ }
213
+ // Only persist labels when at least one survives cleaning.
214
+ function normalizedLabels(labels) {
215
+ const cleaned = cleanLabels(labels);
216
+ return cleaned ? { labels: cleaned } : {};
217
+ }
218
+ /** A pipeline with every step disabled would have nothing to run. */
219
+ function assertSomeEnabled(agentKinds, enabled) {
220
+ if (!enabled)
221
+ return;
222
+ if (!agentKinds.some((_, i) => enabled[i] ?? true)) {
223
+ throw new ValidationError('A pipeline must keep at least one step enabled.');
224
+ }
225
+ }
226
+ //# sourceMappingURL=PipelineService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PipelineService.js","sourceRoot":"","sources":["../../../src/modules/pipelines/PipelineService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { StepGating } from '@cat-factory/kernel';
2
+ /**
3
+ * Structural validation shared by the pipeline builder (save) and the execution engine
4
+ * (run start), so a pipeline that is invalid is rejected at BOTH boundaries.
5
+ *
6
+ * A run is built from the ENABLED steps alone, executed consecutively, so both checks
7
+ * reason over the enabled subset:
8
+ *
9
+ * - {@link assertValidCompanionPlacement}: a companion (reviewer / architect-companion /
10
+ * spec-companion) must run IMMEDIATELY after an ENABLED step it can review. Companions are
11
+ * dependent agents — they make no sense without their producer — so the builder surfaces
12
+ * them as toggles attached to the producer (inserting them immediately after), and the
13
+ * validation enforces exactly that adjacency: a companion's nearest preceding enabled step
14
+ * must be one of its targets. (The engine still reviews the nearest preceding target, but
15
+ * that target is now guaranteed to be the immediate predecessor.)
16
+ * - {@link assertValidGating}: a step gated on the task estimate must be a companion (the
17
+ * only kind it is safe to skip — skipping a producer would starve its downstream steps),
18
+ * must set at least one axis threshold (or it would always skip), and needs a
19
+ * `task-estimator` to have run before it (or the gate has nothing to consult).
20
+ */
21
+ export interface PipelineShape {
22
+ agentKinds: string[];
23
+ enabled?: boolean[];
24
+ gating?: (StepGating | null)[];
25
+ }
26
+ export declare function validatePipelineShape(pipeline: PipelineShape): void;
27
+ /**
28
+ * A companion step is only valid when the step IMMEDIATELY before it (over the enabled
29
+ * subset) produces output it is allowed to review (a step whose kind is in the companion's
30
+ * target allow-list). Validated over the enabled subset — that is exactly the chain the run
31
+ * executes — so it also rejects "disable the producer but leave its companion on" (which
32
+ * would leave the companion grading nothing at runtime) AND "slip another step between the
33
+ * producer and its companion". Companions are surfaced in the builder as toggles attached to
34
+ * their producer and run immediately after it, so adjacency is required.
35
+ */
36
+ export declare function assertValidCompanionPlacement(agentKinds: string[], enabled?: boolean[]): void;
37
+ /**
38
+ * Validate every ENABLED step that carries enabled estimate gating. A disabled gated step
39
+ * never runs, so it imposes no requirement; an enabled one must satisfy all three rules:
40
+ *
41
+ * 1. The gated step must be a COMPANION kind. Gating means "skip this step when the task is
42
+ * light", and skipping is only safe for a dependent companion — skipping a producer
43
+ * (coder / spec-writer / architect) would leave its downstream steps (tester, merger,
44
+ * …) running against output that was never produced. (The consensus-gating sibling can
45
+ * degrade to the standard agent; step-gating removes the step, so it is companion-only.)
46
+ * 2. It must set at least one axis threshold. With none, the axis loop in
47
+ * `shouldRunGatedStep` never matches, so a step with an estimate would ALWAYS skip — the
48
+ * opposite of the usual intent — making the toggle a silent footgun.
49
+ * 3. An enabled `task-estimator` must run earlier in the chain, or the gate has no estimate
50
+ * to consult.
51
+ */
52
+ export declare function assertValidGating(agentKinds: string[], enabled?: boolean[], gating?: (StepGating | null)[]): void;
53
+ //# sourceMappingURL=pipelineShape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipelineShape.d.ts","sourceRoot":"","sources":["../../../src/modules/pipelines/pipelineShape.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAGrD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,EAAE,CAAA;IACnB,MAAM,CAAC,EAAE,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,CAAA;CAC/B;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAGnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,CAoB7F;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAAE,EACpB,OAAO,CAAC,EAAE,OAAO,EAAE,EACnB,MAAM,CAAC,EAAE,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE,GAC7B,IAAI,CA0BN"}
@@ -0,0 +1,74 @@
1
+ import { ValidationError } from '@cat-factory/kernel';
2
+ import { companionTargets, isCompanionKind, TASK_ESTIMATOR_AGENT_KIND } from '@cat-factory/agents';
3
+ export function validatePipelineShape(pipeline) {
4
+ assertValidCompanionPlacement(pipeline.agentKinds, pipeline.enabled);
5
+ assertValidGating(pipeline.agentKinds, pipeline.enabled, pipeline.gating);
6
+ }
7
+ /**
8
+ * A companion step is only valid when the step IMMEDIATELY before it (over the enabled
9
+ * subset) produces output it is allowed to review (a step whose kind is in the companion's
10
+ * target allow-list). Validated over the enabled subset — that is exactly the chain the run
11
+ * executes — so it also rejects "disable the producer but leave its companion on" (which
12
+ * would leave the companion grading nothing at runtime) AND "slip another step between the
13
+ * producer and its companion". Companions are surfaced in the builder as toggles attached to
14
+ * their producer and run immediately after it, so adjacency is required.
15
+ */
16
+ export function assertValidCompanionPlacement(agentKinds, enabled) {
17
+ const isEnabled = (i) => enabled?.[i] !== false;
18
+ for (let i = 0; i < agentKinds.length; i++) {
19
+ const kind = agentKinds[i];
20
+ if (kind === undefined || !isCompanionKind(kind) || !isEnabled(i))
21
+ continue;
22
+ const targets = companionTargets(kind);
23
+ // The nearest preceding ENABLED step must be a producer this companion can review.
24
+ let predecessor;
25
+ for (let j = i - 1; j >= 0; j--) {
26
+ if (isEnabled(j)) {
27
+ predecessor = agentKinds[j];
28
+ break;
29
+ }
30
+ }
31
+ if (predecessor === undefined || !targets.includes(predecessor)) {
32
+ throw new ValidationError(`Companion '${kind}' must run immediately after an enabled step it can review (${targets.join(', ')}).`);
33
+ }
34
+ }
35
+ }
36
+ /**
37
+ * Validate every ENABLED step that carries enabled estimate gating. A disabled gated step
38
+ * never runs, so it imposes no requirement; an enabled one must satisfy all three rules:
39
+ *
40
+ * 1. The gated step must be a COMPANION kind. Gating means "skip this step when the task is
41
+ * light", and skipping is only safe for a dependent companion — skipping a producer
42
+ * (coder / spec-writer / architect) would leave its downstream steps (tester, merger,
43
+ * …) running against output that was never produced. (The consensus-gating sibling can
44
+ * degrade to the standard agent; step-gating removes the step, so it is companion-only.)
45
+ * 2. It must set at least one axis threshold. With none, the axis loop in
46
+ * `shouldRunGatedStep` never matches, so a step with an estimate would ALWAYS skip — the
47
+ * opposite of the usual intent — making the toggle a silent footgun.
48
+ * 3. An enabled `task-estimator` must run earlier in the chain, or the gate has no estimate
49
+ * to consult.
50
+ */
51
+ export function assertValidGating(agentKinds, enabled, gating) {
52
+ if (!gating)
53
+ return;
54
+ const isEnabled = (i) => enabled?.[i] !== false;
55
+ for (let i = 0; i < agentKinds.length; i++) {
56
+ const g = gating[i];
57
+ if (!g?.enabled || !isEnabled(i))
58
+ continue;
59
+ const kind = agentKinds[i];
60
+ if (kind === undefined || !isCompanionKind(kind)) {
61
+ throw new ValidationError(`Step '${kind}' cannot be estimate-gated — only companion steps (reviewer / architect-companion / spec-companion) may be skipped on the estimate.`);
62
+ }
63
+ if (g.minComplexity === undefined && g.minRisk === undefined && g.minImpact === undefined) {
64
+ throw new ValidationError(`Step '${kind}' is estimate-gated but sets no threshold — set at least one of complexity / risk / impact, or it would always be skipped.`);
65
+ }
66
+ const hasEstimator = agentKinds
67
+ .slice(0, i)
68
+ .some((k, j) => k === TASK_ESTIMATOR_AGENT_KIND && isEnabled(j));
69
+ if (!hasEstimator) {
70
+ throw new ValidationError(`Step '${kind}' is gated on the task estimate but no enabled '${TASK_ESTIMATOR_AGENT_KIND}' step runs before it. Add a task-estimator earlier in the pipeline.`);
71
+ }
72
+ }
73
+ }
74
+ //# sourceMappingURL=pipelineShape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipelineShape.js","sourceRoot":"","sources":["../../../src/modules/pipelines/pipelineShape.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import type { Clock, CreateScheduleInput, ExecutionRepository, IdGenerator, PipelineRepository, PipelineSchedule, PipelineScheduleRepository, ScheduleRun, ServiceRepository, UpdateScheduleInput, WorkspaceMountRepository, WorkspaceRepository } from '@cat-factory/kernel';
2
+ import type { BlockRepository } from '@cat-factory/kernel';
3
+ import type { ExecutionService } from '../execution/ExecutionService.js';
4
+ export interface RecurringPipelineServiceDependencies {
5
+ pipelineScheduleRepository: PipelineScheduleRepository;
6
+ workspaceRepository: WorkspaceRepository;
7
+ pipelineRepository: PipelineRepository;
8
+ blockRepository: BlockRepository;
9
+ executionRepository: ExecutionRepository;
10
+ executionService: ExecutionService;
11
+ idGenerator: IdGenerator;
12
+ clock: Clock;
13
+ /**
14
+ * In-org shared services. When wired, a new schedule (and its reused on-board block) is
15
+ * stamped with the frame's service, and {@link RecurringPipelineService.list} returns the
16
+ * schedules of every service the workspace mounts — so a shared service's recurring
17
+ * pipelines appear on every board that mounts it (and still fire once per org).
18
+ */
19
+ serviceRepository?: ServiceRepository;
20
+ workspaceMountRepository?: WorkspaceMountRepository;
21
+ }
22
+ /**
23
+ * Manages a workspace's recurring pipelines. Each schedule owns one reused
24
+ * on-board block (a task leaf inside the chosen service frame); the cron sweeper
25
+ * calls {@link runDue} to fire every due schedule by starting its pipeline against
26
+ * that block (skipping any whose block already has an active run), recording each
27
+ * fire in the run-history table the inspector reads.
28
+ */
29
+ export declare class RecurringPipelineService {
30
+ private readonly schedules;
31
+ private readonly workspaceRepository;
32
+ private readonly pipelineRepository;
33
+ private readonly blockRepository;
34
+ private readonly executionRepository;
35
+ private readonly executionService;
36
+ private readonly idGenerator;
37
+ private readonly clock;
38
+ private readonly serviceRepository?;
39
+ private readonly workspaceMountRepository?;
40
+ constructor(deps: RecurringPipelineServiceDependencies);
41
+ private requireWorkspace;
42
+ list(workspaceId: string): Promise<PipelineSchedule[]>;
43
+ /**
44
+ * Create a recurring pipeline on a service frame. Materialises the reused on-board
45
+ * block (a task leaf inside the frame), computes the first `nextRunAt`, and
46
+ * persists the schedule.
47
+ */
48
+ create(workspaceId: string, input: CreateScheduleInput): Promise<PipelineSchedule>;
49
+ update(workspaceId: string, id: string, patch: UpdateScheduleInput): Promise<PipelineSchedule>;
50
+ /** Remove a schedule, its reused block, and its run history. */
51
+ remove(workspaceId: string, id: string): Promise<void>;
52
+ /** A schedule's run history (most recent first), with live status overlaid. */
53
+ listRuns(workspaceId: string, id: string): Promise<ScheduleRun[]>;
54
+ /** Fire a schedule immediately (ignoring its cadence), if its block is free. */
55
+ runNow(workspaceId: string, id: string): Promise<PipelineSchedule>;
56
+ /**
57
+ * Fire every due schedule across all workspaces. The cron/interval sweepers call
58
+ * this; it skips any schedule whose block already has an active run. Returns the
59
+ * number of runs started (for logging).
60
+ */
61
+ runDue(now: number): Promise<{
62
+ fired: number;
63
+ skipped: number;
64
+ }>;
65
+ /**
66
+ * Start a schedule's pipeline against its reused block. Finalises the prior run's
67
+ * history row (its execution is about to be replaced), records a new running row,
68
+ * and advances `lastRunAt`/`nextRunAt`. Returns false (without starting) when the
69
+ * block already has an active run.
70
+ */
71
+ private fire;
72
+ private advanceCadence;
73
+ /** Map an execution's state to a history-row status + short outcome. */
74
+ private deriveRunOutcome;
75
+ }
76
+ //# sourceMappingURL=RecurringPipelineService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecurringPipelineService.d.ts","sourceRoot":"","sources":["../../../src/modules/recurring/RecurringPipelineService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,KAAK,EACL,mBAAmB,EAEnB,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,0BAA0B,EAC1B,WAAW,EAEX,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACpB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAE1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAA;AAGxE,MAAM,WAAW,oCAAoC;IACnD,0BAA0B,EAAE,0BAA0B,CAAA;IACtD,mBAAmB,EAAE,mBAAmB,CAAA;IACxC,kBAAkB,EAAE,kBAAkB,CAAA;IACtC,eAAe,EAAE,eAAe,CAAA;IAChC,mBAAmB,EAAE,mBAAmB,CAAA;IACxC,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,wBAAwB,CAAC,EAAE,wBAAwB,CAAA;CACpD;AAWD;;;;;;GAMG;AACH,qBAAa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IACvD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAkB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAO;IAC7B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAmB;IACtD,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAA0B;IAEpE,YAAY,IAAI,EAAE,oCAAoC,EAWrD;IAED,OAAO,CAAC,gBAAgB;IAIlB,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAsB3D;IAED;;;;OAIG;IACG,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA0DvF;IAEK,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CA2B3B;IAED,gEAAgE;IAC1D,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3D;IAED,+EAA+E;IACzE,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAatE;IAED,gFAAgF;IAC1E,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAKvE;IAED;;;;OAIG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAUrE;IAED;;;;;OAKG;YACW,IAAI;YAiGJ,cAAc;IAY5B,wEAAwE;IACxE,OAAO,CAAC,gBAAgB;CAezB"}