@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,251 @@
1
+ import { assertFound, ConflictError, ValidationError } from '@cat-factory/kernel';
2
+ import { hasNotesToIncorporate } from '../requirements/requirements.logic.js';
3
+ /**
4
+ * Drives the two iterative review gates — `requirements-review` and `clarity-review` — which
5
+ * share the SAME control flow: an inline reviewer LLM raises findings, the run parks on a
6
+ * durable decision-wait, a human answers/dismisses through the dedicated window, an
7
+ * incorporation pass folds the answers into one standardized document, and the reviewer
8
+ * re-reviews it until it converges (or the iteration budget runs out). Only the subject and
9
+ * the persisted document differ; everything structural is shared, so each method below takes
10
+ * a {@link ReviewKind} and is written exactly once. Extracted out of `ExecutionService`; the
11
+ * shared step-graph primitives it calls (the parking gate, the resolved-gate advance, the
12
+ * iteration-cap dispatch, the block/instance writes) stay on the engine and are injected via
13
+ * {@link ReviewGateControllerDeps}.
14
+ */
15
+ export class ReviewGateController {
16
+ deps;
17
+ constructor(deps) {
18
+ this.deps = deps;
19
+ }
20
+ /**
21
+ * Run a review gate step. When the reviewer isn't wired the step passes through (pipelines
22
+ * run unchanged without the feature). Otherwise it runs the initial reviewer pass: an
23
+ * auto-pass (no findings, or all at/below the task's tolerated severity) advances
24
+ * immediately, recording the findings; anything else parks the run for the dedicated review
25
+ * window to drive the iterative loop. Re-entrant on incorporation: when the human answered
26
+ * the findings and asked to incorporate, the (slow) fold + re-review LLM work runs here in
27
+ * the durable driver instead of in the HTTP request the user is no longer waiting on.
28
+ */
29
+ async evaluate(kind, workspaceId, instance, step, block, isFinalStep) {
30
+ if (!kind.enabled()) {
31
+ return this.completeStep(workspaceId, instance, step, isFinalStep);
32
+ }
33
+ // Re-entry: the human answered the findings and asked to incorporate. Do the (slow)
34
+ // LLM work here in the durable driver — fold the answers into a document, then
35
+ // re-review it — instead of in the HTTP request that the user is no longer waiting on.
36
+ // `reReview` raises the re-summon notification itself when it finds findings.
37
+ const pending = step.pendingIncorporation;
38
+ if (pending) {
39
+ step.pendingIncorporation = null;
40
+ const review = await this.runIncorporationCycle(kind, workspaceId, block.id, pending.feedback);
41
+ if (review.status === 'incorporated') {
42
+ return this.completeStep(workspaceId, instance, step, isFinalStep);
43
+ }
44
+ // `ready`/`exceeded`: re-park (a fresh decision id) and wait for the human again.
45
+ // At the cap, raise a notification so the three-choice decision is discoverable.
46
+ if (review.status === 'exceeded')
47
+ await this.deps.raiseDecisionRequired(workspaceId, instance);
48
+ return this.deps.parkStepOnDecision(workspaceId, instance, step);
49
+ }
50
+ // Fresh entry: run the initial reviewer pass with the task's preset knobs (shared with
51
+ // the off-path inspector surface). Auto-pass (status `incorporated`) → advance; the
52
+ // findings stay recorded on the review for transparency. `ready`/`exceeded` → park for
53
+ // the dedicated window.
54
+ const review = await this.review(kind, workspaceId, block.id);
55
+ if (review.status === 'incorporated') {
56
+ return this.completeStep(workspaceId, instance, step, isFinalStep);
57
+ }
58
+ if (review.status === 'exceeded')
59
+ await this.deps.raiseDecisionRequired(workspaceId, instance);
60
+ return this.deps.parkStepOnDecision(workspaceId, instance, step);
61
+ }
62
+ /**
63
+ * Fold the human's settled answers into a standardized document and re-review it (one
64
+ * iteration of the loop), emitting the live review-changed event after each phase so an
65
+ * open window/inspector tracks progress. Runs inside the durable driver (see
66
+ * {@link evaluate} re-entry); shared by the no-run inline fallback in {@link incorporate}.
67
+ * Returns the re-reviewed review.
68
+ */
69
+ async runIncorporationCycle(kind, workspaceId, blockId, feedback) {
70
+ const review = await this.currentReview(kind, workspaceId, blockId);
71
+ // Nothing to fold in (every finding dismissed, no answered replies, no redo
72
+ // feedback) → the requirements stand as-is. Skip the rework + re-review LLM calls
73
+ // and settle the review directly. `markIncorporated` preserves any
74
+ // incorporated document from an earlier iteration, so downstream consumes that
75
+ // prior doc when one exists, else falls back to the original description (nothing
76
+ // was clarified). Mirrors a polling gate's precheck skip.
77
+ if (!hasNotesToIncorporate(review.items, feedback)) {
78
+ const settled = await kind.markIncorporated(workspaceId, review.id);
79
+ await kind.emit(workspaceId, settled);
80
+ return settled;
81
+ }
82
+ const block = assertFound(await this.deps.blockRepository.get(workspaceId, blockId), 'Block', blockId);
83
+ const preset = await this.deps.resolveMergePreset(workspaceId, block);
84
+ await kind.incorporate(workspaceId, blockId, review.id, feedback);
85
+ // The fold is done; flag the SECOND stage (`reviewing`) so the board/window can show
86
+ // "re-reviewing" distinctly from "incorporating" — either of the two LLM calls can be
87
+ // the slow one, so the human needs to know which is currently running.
88
+ const reReviewing = await kind.markReReviewing(workspaceId, review.id);
89
+ await kind.emit(workspaceId, reReviewing);
90
+ const reviewed = await kind.reReview(workspaceId, review.id, preset);
91
+ await kind.emit(workspaceId, reviewed);
92
+ return reviewed;
93
+ }
94
+ /** Finish a review gate step and advance to the next step (or finish the run). */
95
+ async completeStep(workspaceId, instance, step, isFinalStep) {
96
+ this.deps.finishStep(step);
97
+ step.progress = 1;
98
+ step.subtasks = undefined;
99
+ step.approval = null;
100
+ if (isFinalStep) {
101
+ instance.status = 'done';
102
+ await this.deps.finalizeBlock(workspaceId, instance, undefined);
103
+ await this.deps.persistInstance(workspaceId, instance);
104
+ await this.deps.emitInstance(workspaceId, instance);
105
+ await this.deps.stopRunContainer(workspaceId, instance);
106
+ return { kind: 'done' };
107
+ }
108
+ instance.currentStep += 1;
109
+ const next = instance.steps[instance.currentStep];
110
+ if (next)
111
+ this.deps.startStep(next);
112
+ await this.deps.updateBlockProgress(workspaceId, instance, 'in_progress');
113
+ await this.deps.persistInstance(workspaceId, instance);
114
+ await this.deps.emitInstance(workspaceId, instance);
115
+ return { kind: 'continue' };
116
+ }
117
+ /** Resolve a block's current review or throw. */
118
+ async currentReview(kind, workspaceId, blockId) {
119
+ return assertFound(await kind.getForBlock(workspaceId, blockId), kind.entityName, blockId);
120
+ }
121
+ /**
122
+ * Run a fresh reviewer pass over a block, snapshotting the task's merge-preset knobs
123
+ * (iteration budget + tolerated severity) onto the review. Shared by the pipeline gate
124
+ * ({@link evaluate}) and the off-path inspector "Run review" surface, so both honour the
125
+ * task's preset identically.
126
+ */
127
+ async review(kind, workspaceId, blockId) {
128
+ const block = assertFound(await this.deps.blockRepository.get(workspaceId, blockId), 'Block', blockId);
129
+ const preset = await this.deps.resolveMergePreset(workspaceId, block);
130
+ return kind.review(workspaceId, block, preset);
131
+ }
132
+ /**
133
+ * Incorporate the human's settled answers ASYNCHRONOUSLY. Validates that every finding is
134
+ * answered/dismissed (so the user gets immediate feedback if not), flags the review
135
+ * `incorporating`, records the intent on the parked gate step, and signals the durable
136
+ * driver to wake — which folds the answers and re-reviews in the background (see
137
+ * {@link evaluate} re-entry). Returns at once with the `incorporating` review so the SPA
138
+ * can return the user to the board; they are summoned again only if the re-review yields
139
+ * findings (`ready`) or hits the cap (`exceeded`).
140
+ *
141
+ * No parked run (an off-path inspector review with no active pipeline) → there is no driver
142
+ * to offload to, so the fold + re-review run inline here. That path never had the
143
+ * pipeline-gate freeze this method exists to remove.
144
+ */
145
+ async incorporate(kind, workspaceId, blockId, feedback) {
146
+ const review = await this.currentReview(kind, workspaceId, blockId);
147
+ const open = review.items.filter((i) => i.status === 'open');
148
+ if (open.length > 0) {
149
+ throw new ValidationError(`Answer or dismiss all ${open.length} remaining item(s) before incorporating`);
150
+ }
151
+ const parked = await this.findParkedStep(kind, workspaceId, blockId);
152
+ if (!parked) {
153
+ // Off-path: no pipeline parked on this review. Do the work inline (it cannot be
154
+ // offloaded to a driver that isn't running) and return the re-reviewed result.
155
+ return this.runIncorporationCycle(kind, workspaceId, blockId, feedback);
156
+ }
157
+ const { instance, step } = parked;
158
+ step.pendingIncorporation = feedback ? { feedback } : {};
159
+ // Re-arm the run BEFORE signalling the driver: the park left it `blocked`, but
160
+ // `advanceInstance` no-ops unless the run is `running`/`paused`, so a woken driver
161
+ // would otherwise return `noop` (and the workflow would end) WITHOUT running the
162
+ // re-entrant incorporate + re-review cycle — leaving the review stuck `incorporating`
163
+ // forever. Mirrors every other resume path (e.g. `advancePastResolvedGate`).
164
+ if (instance.status === 'blocked')
165
+ instance.status = 'running';
166
+ const updated = await kind.markIncorporating(workspaceId, review.id);
167
+ await this.deps.persistInstance(workspaceId, instance);
168
+ await this.deps.emitInstance(workspaceId, instance);
169
+ await kind.emit(workspaceId, updated);
170
+ await this.deps.workRunner.signalDecision(workspaceId, instance.id, step.approval.id, 'incorporate');
171
+ return updated;
172
+ }
173
+ /** Locate the run + gate step a block's review is parked on (or null). */
174
+ async findParkedStep(kind, workspaceId, blockId) {
175
+ const block = await this.deps.blockRepository.get(workspaceId, blockId);
176
+ if (!block?.executionId)
177
+ return null;
178
+ const instance = await this.deps.executionRepository.get(workspaceId, block.executionId);
179
+ if (!instance)
180
+ return null;
181
+ const step = instance.steps.find((s) => s.agentKind === kind.agentKind &&
182
+ s.state === 'waiting_decision' &&
183
+ s.approval?.status === 'pending');
184
+ return step ? { instance, step } : null;
185
+ }
186
+ /**
187
+ * Re-review the incorporated document (one more reviewer pass). On convergence
188
+ * (`incorporated`) the parked run advances; otherwise the window shows the next cycle
189
+ * (`ready`) or the iteration-cap choices (`exceeded`). Only valid once an incorporation
190
+ * has produced a document to re-review (status `merged`).
191
+ */
192
+ async reReview(kind, workspaceId, blockId) {
193
+ const review = await this.currentReview(kind, workspaceId, blockId);
194
+ if (review.status !== 'merged') {
195
+ throw new ConflictError('Incorporate the answers before re-reviewing');
196
+ }
197
+ const block = assertFound(await this.deps.blockRepository.get(workspaceId, blockId), 'Block', blockId);
198
+ const preset = await this.deps.resolveMergePreset(workspaceId, block);
199
+ const updated = await kind.reReview(workspaceId, review.id, preset);
200
+ if (updated.status === 'incorporated')
201
+ await this.resumeRun(kind, workspaceId, blockId);
202
+ return updated;
203
+ }
204
+ /**
205
+ * Proceed: settle the review (the last incorporated doc, if any, becomes what downstream
206
+ * agents consume) and advance the parked run. Used when every finding is dismissed, or the
207
+ * human proceeds past the iteration cap.
208
+ */
209
+ async proceed(kind, workspaceId, blockId) {
210
+ const review = await this.currentReview(kind, workspaceId, blockId);
211
+ const updated = await kind.markIncorporated(workspaceId, review.id);
212
+ await this.resumeRun(kind, workspaceId, blockId);
213
+ return updated;
214
+ }
215
+ /**
216
+ * Resolve a review that hit its iteration cap: grant one more round, proceed with the last
217
+ * incorporated doc, or stop the task and reset it to phase zero (the run is cancelled, the
218
+ * block returns to `planned` and is editable again; the review — with its last incorporated
219
+ * doc — survives as a base for the next attempt).
220
+ */
221
+ async resolveExceeded(kind, workspaceId, blockId, choice) {
222
+ const review = await this.currentReview(kind, workspaceId, blockId);
223
+ await this.deps.dispatchIterationCap(workspaceId, blockId, choice, {
224
+ extraRound: () => kind.grantExtraRound(workspaceId, review.id),
225
+ proceed: () => this.proceed(kind, workspaceId, blockId),
226
+ });
227
+ // Re-read so the caller sees the post-resolution state (the doc survives stop-reset).
228
+ return this.currentReview(kind, workspaceId, blockId);
229
+ }
230
+ /**
231
+ * Resume a run parked on its review gate: finish the gate step, advance to the next step
232
+ * and wake the durable driver. A no-op when the block has no run parked on a review gate of
233
+ * this kind (e.g. an off-path inspector review with no active pipeline).
234
+ */
235
+ async resumeRun(kind, workspaceId, blockId) {
236
+ const block = await this.deps.blockRepository.get(workspaceId, blockId);
237
+ if (!block?.executionId)
238
+ return;
239
+ const instance = await this.deps.executionRepository.get(workspaceId, block.executionId);
240
+ if (!instance)
241
+ return;
242
+ const idx = instance.steps.findIndex((s) => s.agentKind === kind.agentKind &&
243
+ s.state === 'waiting_decision' &&
244
+ s.approval?.status === 'pending');
245
+ if (idx === -1)
246
+ return;
247
+ instance.steps[idx].approval.status = 'approved';
248
+ await this.deps.advancePastResolvedGate(workspaceId, instance, idx);
249
+ }
250
+ }
251
+ //# sourceMappingURL=ReviewGateController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReviewGateController.js","sourceRoot":"","sources":["../../../src/modules/execution/ReviewGateController.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { AgentExecutor, AgentRunResult, Block, BlockRepository, ExecutionInstance, PipelineStep } from '@cat-factory/kernel';
2
+ import type { NotificationService } from '../notifications/NotificationService.js';
3
+ import type { AdvanceResult } from './advance.js';
4
+ import type { AgentContextBuilder } from './AgentContextBuilder.js';
5
+ /** The engine collaborators the Tester gate drives (kept on the engine, injected here). */
6
+ export interface TesterControllerDeps {
7
+ blockRepository: BlockRepository;
8
+ notificationService?: NotificationService;
9
+ agentExecutor: AgentExecutor;
10
+ contextBuilder: AgentContextBuilder;
11
+ /** The task's CI/test attempt budget (from the resolved merge preset). */
12
+ resolveMergePreset: (workspaceId: string, block: Block) => Promise<{
13
+ ciMaxAttempts: number;
14
+ }>;
15
+ /** Reclaim the run's finished container before dispatching the next job. */
16
+ stopRunContainer: (workspaceId: string, instance: ExecutionInstance) => Promise<void>;
17
+ persistInstance: (workspaceId: string, instance: ExecutionInstance) => Promise<void>;
18
+ emitInstance: (workspaceId: string, instance: ExecutionInstance) => Promise<void>;
19
+ }
20
+ /**
21
+ * Drives the Tester gate's fix loop: apply a Tester report to its step (greenlight →
22
+ * advance; withheld + budget left → dispatch the fixer and re-test on its completion;
23
+ * withheld + budget spent / unparseable → fail for a human), and re-dispatch the Tester
24
+ * after a fixer job finishes. Extracted out of `ExecutionService`; the shared engine writes
25
+ * (block reads, container reclaim, instance persistence/emit) stay on the engine and are
26
+ * injected via {@link TesterControllerDeps}.
27
+ */
28
+ export declare class TesterController {
29
+ private readonly deps;
30
+ constructor(deps: TesterControllerDeps);
31
+ /**
32
+ * Apply a Tester report to its step's gate. Records the report on the step, then:
33
+ * - greenlight → returns `null` so `recordStepResult` finishes + advances;
34
+ * - withheld + budget left → dispatches the `fixer` and parks (re-tested on its
35
+ * completion, see `pollAgentJob`);
36
+ * - withheld + budget spent (or unparseable) → fails the run for human attention.
37
+ */
38
+ resolveTesterResult(workspaceId: string, instance: ExecutionInstance, step: PipelineStep, result: AgentRunResult): Promise<AdvanceResult | null>;
39
+ /**
40
+ * Re-dispatch the Tester after a Fixer job finished, against the (now-fixed) PR
41
+ * branch. Parks on the fresh Tester job; its report then drives greenlight-or-loop.
42
+ */
43
+ dispatchTester(workspaceId: string, instance: ExecutionInstance, step: PipelineStep, block: Block): Promise<AdvanceResult>;
44
+ /**
45
+ * Give up on a Tester gate that can't be greenlit. Persists the step (left
46
+ * un-`done`, like the CI gate — never falsely completed), raises a human-actionable
47
+ * `test_failed` notification, and fails the run for human attention. Returns the
48
+ * `job_failed` result the driver propagates.
49
+ */
50
+ private failTester;
51
+ /** Raise a `test_failed` notification when the Tester gate gives up. */
52
+ private raiseTestFailed;
53
+ /**
54
+ * Dispatch a `fixer` container job for a Tester step that withheld its greenlight:
55
+ * build the agent context with the kind overridden to `fixer` and the Tester's
56
+ * report folded in as resolved context, park on the job, and flip the gate to
57
+ * `fixing`. On the fixer's completion `pollAgentJob` re-dispatches the Tester.
58
+ */
59
+ private dispatchFixer;
60
+ }
61
+ //# sourceMappingURL=TesterController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TesterController.d.ts","sourceRoot":"","sources":["../../../src/modules/execution/TesterController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAEb,cAAc,EACd,KAAK,EACL,eAAe,EACf,iBAAiB,EACjB,YAAY,EACb,MAAM,qBAAqB,CAAA;AAI5B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAA;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AA4CnE,2FAA2F;AAC3F,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,eAAe,CAAA;IAChC,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC,aAAa,EAAE,aAAa,CAAA;IAC5B,cAAc,EAAE,mBAAmB,CAAA;IACnC,0EAA0E;IAC1E,kBAAkB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;QAAE,aAAa,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7F,4EAA4E;IAC5E,gBAAgB,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrF,eAAe,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpF,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAClF;AAED;;;;;;;GAOG;AACH,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAjC,YAA6B,IAAI,EAAE,oBAAoB,EAAI;IAE3D;;;;;;OAMG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,YAAY,EAClB,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA+D/B;IAED;;;OAGG;IACG,cAAc,CAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,KAAK,GACX,OAAO,CAAC,aAAa,CAAC,CAsBxB;IAED;;;;;OAKG;YACW,UAAU;IAkBxB,wEAAwE;YAC1D,eAAe;IAuB7B;;;;;OAKG;YACW,aAAa;CA0D5B"}
@@ -0,0 +1,215 @@
1
+ import { DEFAULT_MERGE_PRESET, isAsyncAgentExecutor } from '@cat-factory/kernel';
2
+ import { parseTestReport } from '@cat-factory/contracts';
3
+ import { FIXER_AGENT_KIND, TESTER_AGENT_KIND } from './ci.logic.js';
4
+ /** Whether a Tester report raised any concern serious enough to block a release. */
5
+ function hasBlockingConcerns(report) {
6
+ return report.concerns.some((c) => c.severity === 'high' || c.severity === 'critical');
7
+ }
8
+ /**
9
+ * The engine's release verdict for a Tester report: greenlit only when the Tester
10
+ * said so AND it raised no blocking (high/critical) concern. Defensive against a
11
+ * harness that greenlights with open blockers — low/medium concerns are advisory
12
+ * and do not, on their own, withhold the greenlight or loop the fixer.
13
+ */
14
+ function isGreenlit(report) {
15
+ return report.greenlight === true && !hasBlockingConcerns(report);
16
+ }
17
+ /** One-line, human-readable summary of a Tester report's concerns, for failure messages. */
18
+ function describeTestConcerns(report) {
19
+ if (!report.concerns.length)
20
+ return report.summary || 'No greenlight given.';
21
+ const names = report.concerns
22
+ .map((c) => `${c.title} (${c.severity})`)
23
+ .slice(0, 5)
24
+ .join(', ');
25
+ return `Concerns: ${names}${report.concerns.length > 5 ? ', …' : ''}`;
26
+ }
27
+ /** Render a Tester report as the resolved-context block handed to the fixer. */
28
+ function renderReportForFixer(report) {
29
+ const lines = ['Tester report — fix the concerns below, then the tester will re-run.', ''];
30
+ if (report.summary)
31
+ lines.push(report.summary, '');
32
+ if (report.concerns.length) {
33
+ lines.push('Concerns to fix:');
34
+ for (const c of report.concerns)
35
+ lines.push(`- [${c.severity}] ${c.title}: ${c.detail}`);
36
+ lines.push('');
37
+ }
38
+ const failed = report.outcomes.filter((o) => o.status === 'failed');
39
+ if (failed.length) {
40
+ lines.push('Failed checks:');
41
+ for (const o of failed)
42
+ lines.push(`- ${o.name}${o.detail ? `: ${o.detail}` : ''}`);
43
+ }
44
+ return lines.join('\n').trim();
45
+ }
46
+ /**
47
+ * Drives the Tester gate's fix loop: apply a Tester report to its step (greenlight →
48
+ * advance; withheld + budget left → dispatch the fixer and re-test on its completion;
49
+ * withheld + budget spent / unparseable → fail for a human), and re-dispatch the Tester
50
+ * after a fixer job finishes. Extracted out of `ExecutionService`; the shared engine writes
51
+ * (block reads, container reclaim, instance persistence/emit) stay on the engine and are
52
+ * injected via {@link TesterControllerDeps}.
53
+ */
54
+ export class TesterController {
55
+ deps;
56
+ constructor(deps) {
57
+ this.deps = deps;
58
+ }
59
+ /**
60
+ * Apply a Tester report to its step's gate. Records the report on the step, then:
61
+ * - greenlight → returns `null` so `recordStepResult` finishes + advances;
62
+ * - withheld + budget left → dispatches the `fixer` and parks (re-tested on its
63
+ * completion, see `pollAgentJob`);
64
+ * - withheld + budget spent (or unparseable) → fails the run for human attention.
65
+ */
66
+ async resolveTesterResult(workspaceId, instance, step, result) {
67
+ const block = await this.deps.blockRepository.get(workspaceId, instance.blockId);
68
+ let report = null;
69
+ try {
70
+ report = parseTestReport(result.testReport);
71
+ }
72
+ catch {
73
+ report = null;
74
+ }
75
+ if (!step.test) {
76
+ const maxAttempts = block
77
+ ? (await this.deps.resolveMergePreset(workspaceId, block)).ciMaxAttempts
78
+ : DEFAULT_MERGE_PRESET.ciMaxAttempts;
79
+ step.test = { phase: 'testing', attempts: 0, maxAttempts, lastReport: null };
80
+ }
81
+ if (report)
82
+ step.test.lastReport = report;
83
+ step.test.phase = 'testing';
84
+ step.subtasks = undefined;
85
+ // An unparseable report can't gate a release — fail loudly rather than silently
86
+ // greenlighting or looping forever.
87
+ if (!report) {
88
+ return this.failTester(workspaceId, instance, step, block, result.output ?? 'Tester returned an unparseable report.', 'Tester returned an unparseable test report.', step.test.attempts);
89
+ }
90
+ // The FIRST testing round always loops the fixer when the report flags ANYTHING — any
91
+ // concern (regardless of severity) or a withheld greenlight — so the first batch of
92
+ // findings is always handed to the fixer. From the SECOND round onward the normal
93
+ // threshold applies (`isGreenlit`: a greenlight stands unless a high/critical concern
94
+ // is open; low/medium concerns are advisory). The defensive greenlight+no-blocker
95
+ // check still protects every round against a harness that greenlights with blockers.
96
+ const firstRound = step.test.attempts === 0;
97
+ const accepted = firstRound
98
+ ? report.greenlight === true && report.concerns.length === 0
99
+ : isGreenlit(report);
100
+ if (accepted)
101
+ return null;
102
+ // Withheld greenlight: loop the fixer if any budget remains, else give up.
103
+ const executor = this.deps.agentExecutor;
104
+ if (isAsyncAgentExecutor(executor) && block && step.test.attempts < step.test.maxAttempts) {
105
+ // Reclaim the finished Tester container before dispatching the Fixer so the
106
+ // next job boots fresh — the per-run container would otherwise re-attach to the
107
+ // completed Tester job (idempotent dispatch by run id), replaying its result.
108
+ await this.deps.stopRunContainer(workspaceId, instance);
109
+ return this.dispatchFixer(workspaceId, instance, step, block, report);
110
+ }
111
+ // Budget spent (or no async executor to fix with): give up for human attention.
112
+ return this.failTester(workspaceId, instance, step, block, report.summary || 'Tester withheld its greenlight.', `Tester withheld its greenlight after ${step.test.attempts} fix attempt(s). ${describeTestConcerns(report)}`.trim(), step.test.attempts);
113
+ }
114
+ /**
115
+ * Re-dispatch the Tester after a Fixer job finished, against the (now-fixed) PR
116
+ * branch. Parks on the fresh Tester job; its report then drives greenlight-or-loop.
117
+ */
118
+ async dispatchTester(workspaceId, instance, step, block) {
119
+ const executor = this.deps.agentExecutor;
120
+ if (!isAsyncAgentExecutor(executor)) {
121
+ return { kind: 'job_failed', error: 'No async executor available to run the tester.' };
122
+ }
123
+ const isFinalStep = instance.currentStep === instance.steps.length - 1;
124
+ const context = await this.deps.contextBuilder.buildContext(workspaceId, instance, step, isFinalStep, block);
125
+ const handle = await executor.startJob(context);
126
+ step.jobId = handle.jobId;
127
+ if (handle.model)
128
+ step.model = handle.model;
129
+ step.startingContainer = true;
130
+ step.subtasks = undefined;
131
+ if (step.test)
132
+ step.test.phase = 'testing';
133
+ await this.deps.persistInstance(workspaceId, instance);
134
+ await this.deps.emitInstance(workspaceId, instance);
135
+ return { kind: 'awaiting_job', jobId: step.jobId, stepIndex: instance.currentStep };
136
+ }
137
+ /**
138
+ * Give up on a Tester gate that can't be greenlit. Persists the step (left
139
+ * un-`done`, like the CI gate — never falsely completed), raises a human-actionable
140
+ * `test_failed` notification, and fails the run for human attention. Returns the
141
+ * `job_failed` result the driver propagates.
142
+ */
143
+ async failTester(workspaceId, instance, step, block, output, error, attempts) {
144
+ step.output = output;
145
+ await this.deps.persistInstance(workspaceId, instance);
146
+ await this.raiseTestFailed(workspaceId, instance, block, error, attempts);
147
+ // Carry the precise classification (`agent`, not the generic container `job_failed`)
148
+ // and the Tester's own summary to the driver's single `failRun` funnel; failing the
149
+ // run here too would let the driver's second `failRun` clobber it.
150
+ return { kind: 'job_failed', failureKind: 'agent', error, detail: output || undefined };
151
+ }
152
+ /** Raise a `test_failed` notification when the Tester gate gives up. */
153
+ async raiseTestFailed(workspaceId, instance, block, summary, attempts) {
154
+ if (!this.deps.notificationService || !block)
155
+ return;
156
+ await this.deps.notificationService.raise(workspaceId, {
157
+ type: 'test_failed',
158
+ blockId: block.id,
159
+ executionId: instance.id,
160
+ title: `Tests are still failing for "${block.title}"`,
161
+ body: `The Fixer agent tried ${attempts} time(s) but the Tester still won't greenlight. ${summary} ` +
162
+ `Take a look and retry the run once fixed.`,
163
+ payload: {
164
+ ...(block.pullRequest?.url ? { prUrl: block.pullRequest.url } : {}),
165
+ pipelineName: instance.pipelineName,
166
+ },
167
+ });
168
+ }
169
+ /**
170
+ * Dispatch a `fixer` container job for a Tester step that withheld its greenlight:
171
+ * build the agent context with the kind overridden to `fixer` and the Tester's
172
+ * report folded in as resolved context, park on the job, and flip the gate to
173
+ * `fixing`. On the fixer's completion `pollAgentJob` re-dispatches the Tester.
174
+ */
175
+ async dispatchFixer(workspaceId, instance, step, block, report) {
176
+ const executor = this.deps.agentExecutor;
177
+ if (!isAsyncAgentExecutor(executor)) {
178
+ return { kind: 'job_failed', error: 'No async executor available to fix test failures.' };
179
+ }
180
+ // The fixer pushes its commits onto the implementation PR branch, so it can only
181
+ // run once a coder/integrator step opened one. A Tester-only pipeline (or one whose
182
+ // earlier step never produced a PR) can't be auto-fixed — fail cleanly with the
183
+ // report instead of letting the job-body builder throw out of the advance.
184
+ if (!block.pullRequest?.branch) {
185
+ return this.failTester(workspaceId, instance, step, block, report.summary || 'Tester withheld its greenlight.', `Tester withheld its greenlight and there is no PR branch for the fixer to push to. ${describeTestConcerns(report)}`.trim(), step.test?.attempts ?? 0);
186
+ }
187
+ const isFinalStep = instance.currentStep === instance.steps.length - 1;
188
+ const base = await this.deps.contextBuilder.buildContext(workspaceId, instance, step, isFinalStep, block);
189
+ const context = {
190
+ ...base,
191
+ agentKind: FIXER_AGENT_KIND,
192
+ // Hand the fixer the Tester's report (what failed + the concerns) as context.
193
+ priorOutputs: [
194
+ ...base.priorOutputs,
195
+ { agentKind: TESTER_AGENT_KIND, output: renderReportForFixer(report) },
196
+ ],
197
+ };
198
+ const handle = await executor.startJob(context);
199
+ step.jobId = handle.jobId;
200
+ if (handle.model)
201
+ step.model = handle.model;
202
+ step.startingContainer = true;
203
+ step.subtasks = undefined;
204
+ step.test = {
205
+ phase: 'fixing',
206
+ attempts: (step.test?.attempts ?? 0) + 1,
207
+ maxAttempts: step.test?.maxAttempts ?? DEFAULT_MERGE_PRESET.ciMaxAttempts,
208
+ lastReport: report,
209
+ };
210
+ await this.deps.persistInstance(workspaceId, instance);
211
+ await this.deps.emitInstance(workspaceId, instance);
212
+ return { kind: 'awaiting_job', jobId: step.jobId, stepIndex: instance.currentStep };
213
+ }
214
+ }
215
+ //# sourceMappingURL=TesterController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TesterController.js","sourceRoot":"","sources":["../../../src/modules/execution/TesterController.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAChF,OAAO,EAAmB,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACzE,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAKnE,oFAAoF;AACpF,SAAS,mBAAmB,CAAC,MAAkB;IAC7C,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAA;AACxF,CAAC;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAAC,MAAkB;IACpC,OAAO,MAAM,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAA;AACnE,CAAC;AAED,4FAA4F;AAC5F,SAAS,oBAAoB,CAAC,MAAkB;IAC9C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC,OAAO,IAAI,sBAAsB,CAAA;IAC5E,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ;SAC1B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,QAAQ,GAAG,CAAC;SACxC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;SACX,IAAI,CAAC,IAAI,CAAC,CAAA;IACb,OAAO,aAAa,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;AACvE,CAAC;AAED,gFAAgF;AAChF,SAAS,oBAAoB,CAAC,MAAkB;IAC9C,MAAM,KAAK,GAAG,CAAC,sEAAsE,EAAE,EAAE,CAAC,CAAA;IAC1F,IAAI,MAAM,CAAC,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IAClD,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAA;QAC9B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;QACxF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAChB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAA;IACnE,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;QAC5B,KAAK,MAAM,CAAC,IAAI,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACrF,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAA;AAChC,CAAC;AAgBD;;;;;;;GAOG;AACH,MAAM,OAAO,gBAAgB;IACE,IAAI;IAAjC,YAA6B,IAA0B;oBAA1B,IAAI;IAAyB,CAAC;IAE3D;;;;;;OAMG;IACH,KAAK,CAAC,mBAAmB,CACvB,WAAmB,EACnB,QAA2B,EAC3B,IAAkB,EAClB,MAAsB;QAEtB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;QAChF,IAAI,MAAM,GAAsB,IAAI,CAAA;QACpC,IAAI,CAAC;YACH,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,IAAI,CAAA;QACf,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,MAAM,WAAW,GAAG,KAAK;gBACvB,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,aAAa;gBACxE,CAAC,CAAC,oBAAoB,CAAC,aAAa,CAAA;YACtC,IAAI,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;QAC9E,CAAC;QACD,IAAI,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAA;QACzC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QAC3B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAA;QAEzB,gFAAgF;QAChF,oCAAoC;QACpC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC,UAAU,CACpB,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,MAAM,CAAC,MAAM,IAAI,wCAAwC,EACzD,6CAA6C,EAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,CACnB,CAAA;QACH,CAAC;QAED,sFAAsF;QACtF,oFAAoF;QACpF,kFAAkF;QAClF,sFAAsF;QACtF,kFAAkF;QAClF,qFAAqF;QACrF,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAA;QAC3C,MAAM,QAAQ,GAAG,UAAU;YACzB,CAAC,CAAC,MAAM,CAAC,UAAU,KAAK,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAC5D,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;QACtB,IAAI,QAAQ;YAAE,OAAO,IAAI,CAAA;QAEzB,2EAA2E;QAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAA;QACxC,IAAI,oBAAoB,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YAC1F,4EAA4E;YAC5E,gFAAgF;YAChF,8EAA8E;YAC9E,MAAM,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;YACvD,OAAO,IAAI,CAAC,aAAa,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QACvE,CAAC;QACD,gFAAgF;QAChF,OAAO,IAAI,CAAC,UAAU,CACpB,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,MAAM,CAAC,OAAO,IAAI,iCAAiC,EACnD,wCAAwC,IAAI,CAAC,IAAI,CAAC,QAAQ,oBAAoB,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,EACnH,IAAI,CAAC,IAAI,CAAC,QAAQ,CACnB,CAAA;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc,CAClB,WAAmB,EACnB,QAA2B,EAC3B,IAAkB,EAClB,KAAY;QAEZ,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAA;QACxC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,gDAAgD,EAAE,CAAA;QACxF,CAAC;QACD,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,KAAK,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;QACtE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,CACzD,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,WAAW,EACX,KAAK,CACN,CAAA;QACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QAC/C,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;QACzB,IAAI,MAAM,CAAC,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;QAC3C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAA;QAC7B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAA;QACzB,IAAI,IAAI,CAAC,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QAC1C,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QACtD,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QACnD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAA;IACrF,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,UAAU,CACtB,WAAmB,EACnB,QAA2B,EAC3B,IAAkB,EAClB,KAAmB,EACnB,MAAc,EACd,KAAa,EACb,QAAgB;QAEhB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QACtD,MAAM,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAA;QACzE,qFAAqF;QACrF,oFAAoF;QACpF,mEAAmE;QACnE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,SAAS,EAAE,CAAA;IACzF,CAAC;IAED,wEAAwE;IAChE,KAAK,CAAC,eAAe,CAC3B,WAAmB,EACnB,QAA2B,EAC3B,KAAmB,EACnB,OAAe,EACf,QAAgB;QAEhB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,KAAK;YAAE,OAAM;QACpD,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,WAAW,EAAE;YACrD,IAAI,EAAE,aAAa;YACnB,OAAO,EAAE,KAAK,CAAC,EAAE;YACjB,WAAW,EAAE,QAAQ,CAAC,EAAE;YACxB,KAAK,EAAE,gCAAgC,KAAK,CAAC,KAAK,GAAG;YACrD,IAAI,EACF,yBAAyB,QAAQ,mDAAmD,OAAO,GAAG;gBAC9F,2CAA2C;YAC7C,OAAO,EAAE;gBACP,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnE,YAAY,EAAE,QAAQ,CAAC,YAAY;aACpC;SACF,CAAC,CAAA;IACJ,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,aAAa,CACzB,WAAmB,EACnB,QAA2B,EAC3B,IAAkB,EAClB,KAAY,EACZ,MAAkB;QAElB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAA;QACxC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,mDAAmD,EAAE,CAAA;QAC3F,CAAC;QACD,iFAAiF;QACjF,oFAAoF;QACpF,gFAAgF;QAChF,2EAA2E;QAC3E,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,UAAU,CACpB,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,MAAM,CAAC,OAAO,IAAI,iCAAiC,EACnD,sFAAsF,oBAAoB,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,EAC3H,IAAI,CAAC,IAAI,EAAE,QAAQ,IAAI,CAAC,CACzB,CAAA;QACH,CAAC;QACD,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,KAAK,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;QACtE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,CACtD,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,WAAW,EACX,KAAK,CACN,CAAA;QACD,MAAM,OAAO,GAAoB;YAC/B,GAAG,IAAI;YACP,SAAS,EAAE,gBAAgB;YAC3B,8EAA8E;YAC9E,YAAY,EAAE;gBACZ,GAAG,IAAI,CAAC,YAAY;gBACpB,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,EAAE,oBAAoB,CAAC,MAAM,CAAC,EAAE;aACvE;SACF,CAAA;QACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QAC/C,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;QACzB,IAAI,MAAM,CAAC,KAAK;YAAE,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;QAC3C,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAA;QAC7B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAA;QACzB,IAAI,CAAC,IAAI,GAAG;YACV,KAAK,EAAE,QAAQ;YACf,QAAQ,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;YACxC,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,IAAI,oBAAoB,CAAC,aAAa;YACzE,UAAU,EAAE,MAAM;SACnB,CAAA;QACD,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QACtD,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QACnD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAA;IACrF,CAAC;CACF"}