@cat-factory/orchestration 0.177.0 → 0.178.1

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 (61) hide show
  1. package/dist/container/dependencies.d.ts +9 -1
  2. package/dist/container/dependencies.d.ts.map +1 -1
  3. package/dist/container.d.ts.map +1 -1
  4. package/dist/container.js +1 -0
  5. package/dist/container.js.map +1 -1
  6. package/dist/modules/execution/AgentContextBuilder.d.ts +5 -24
  7. package/dist/modules/execution/AgentContextBuilder.d.ts.map +1 -1
  8. package/dist/modules/execution/AgentContextBuilder.js +10 -42
  9. package/dist/modules/execution/AgentContextBuilder.js.map +1 -1
  10. package/dist/modules/execution/ExecutionService.d.ts +14 -72
  11. package/dist/modules/execution/ExecutionService.d.ts.map +1 -1
  12. package/dist/modules/execution/ExecutionService.js +70 -353
  13. package/dist/modules/execution/ExecutionService.js.map +1 -1
  14. package/dist/modules/execution/ExecutionServiceDependencies.d.ts +7 -1
  15. package/dist/modules/execution/ExecutionServiceDependencies.d.ts.map +1 -1
  16. package/dist/modules/execution/OneShotStepController.d.ts +92 -0
  17. package/dist/modules/execution/OneShotStepController.d.ts.map +1 -0
  18. package/dist/modules/execution/OneShotStepController.js +202 -0
  19. package/dist/modules/execution/OneShotStepController.js.map +1 -0
  20. package/dist/modules/execution/PollRunningController.d.ts +113 -0
  21. package/dist/modules/execution/PollRunningController.d.ts.map +1 -0
  22. package/dist/modules/execution/PollRunningController.js +298 -0
  23. package/dist/modules/execution/PollRunningController.js.map +1 -0
  24. package/dist/modules/execution/RunAdmission.d.ts.map +1 -1
  25. package/dist/modules/execution/RunAdmission.js +4 -2
  26. package/dist/modules/execution/RunAdmission.js.map +1 -1
  27. package/dist/modules/execution/RunDispatcher.d.ts +4 -108
  28. package/dist/modules/execution/RunDispatcher.d.ts.map +1 -1
  29. package/dist/modules/execution/RunDispatcher.js +51 -468
  30. package/dist/modules/execution/RunDispatcher.js.map +1 -1
  31. package/dist/modules/execution/RunStateMachine.d.ts +44 -1
  32. package/dist/modules/execution/RunStateMachine.d.ts.map +1 -1
  33. package/dist/modules/execution/RunStateMachine.js +92 -1
  34. package/dist/modules/execution/RunStateMachine.js.map +1 -1
  35. package/dist/modules/execution/StepDecisionController.d.ts +120 -0
  36. package/dist/modules/execution/StepDecisionController.d.ts.map +1 -0
  37. package/dist/modules/execution/StepDecisionController.js +347 -0
  38. package/dist/modules/execution/StepDecisionController.js.map +1 -0
  39. package/dist/modules/execution/dispatchPromptSettings.d.ts +43 -0
  40. package/dist/modules/execution/dispatchPromptSettings.d.ts.map +1 -0
  41. package/dist/modules/execution/dispatchPromptSettings.js +76 -0
  42. package/dist/modules/execution/dispatchPromptSettings.js.map +1 -0
  43. package/dist/modules/execution/runStart.d.ts +50 -0
  44. package/dist/modules/execution/runStart.d.ts.map +1 -0
  45. package/dist/modules/execution/runStart.js +53 -0
  46. package/dist/modules/execution/runStart.js.map +1 -0
  47. package/dist/modules/kaizen/KaizenService.d.ts.map +1 -1
  48. package/dist/modules/kaizen/KaizenService.js +7 -5
  49. package/dist/modules/kaizen/KaizenService.js.map +1 -1
  50. package/dist/modules/kaizen/kaizen.logic.d.ts +24 -9
  51. package/dist/modules/kaizen/kaizen.logic.d.ts.map +1 -1
  52. package/dist/modules/kaizen/kaizen.logic.js +25 -10
  53. package/dist/modules/kaizen/kaizen.logic.js.map +1 -1
  54. package/dist/modules/pipelines/pipelineShape.d.ts +27 -0
  55. package/dist/modules/pipelines/pipelineShape.d.ts.map +1 -1
  56. package/dist/modules/pipelines/pipelineShape.js +44 -1
  57. package/dist/modules/pipelines/pipelineShape.js.map +1 -1
  58. package/dist/validation/validateRegistrations.d.ts.map +1 -1
  59. package/dist/validation/validateRegistrations.js +92 -0
  60. package/dist/validation/validateRegistrations.js.map +1 -1
  61. package/package.json +11 -11
@@ -0,0 +1,120 @@
1
+ import { type AgentExecutor, type AgentFailureKind, type Block, type BlockRepository, type Clock, type ExecutionInstance, type ExecutionRepository, type StepReviewComment, type WorkRunner } from '@cat-factory/kernel';
2
+ import { type AgentKindRegistry } from '@cat-factory/agents';
3
+ import type { ReviewEffort } from '@cat-factory/contracts';
4
+ import type { RunStateMachine } from './RunStateMachine.js';
5
+ import type { StepGraph } from './StepGraph.js';
6
+ import type { RunMergePolicy } from './RunMergePolicy.js';
7
+ import type { FinalizeMergeResult } from './MergeResolver.js';
8
+ import type { RunDispatcher } from './RunDispatcher.js';
9
+ /**
10
+ * Collaborators + bound engine call-backs the {@link StepDecisionController} needs. The five
11
+ * call-backs (`requireWorkspace` / `requireBlock` / `failRun` / `finalizeMerge`) are bound
12
+ * {@link ExecutionService} methods, so a decision still runs against the SAME engine state the
13
+ * inline code did.
14
+ */
15
+ export interface StepDecisionControllerDeps {
16
+ agentExecutor: AgentExecutor;
17
+ agentKindRegistry: AgentKindRegistry;
18
+ blockRepository: BlockRepository;
19
+ clock: Clock;
20
+ executionRepository: ExecutionRepository;
21
+ mergePolicy: RunMergePolicy;
22
+ runDispatcher: RunDispatcher;
23
+ runStateMachine: RunStateMachine;
24
+ stepGraph: StepGraph;
25
+ workRunner: WorkRunner;
26
+ requireWorkspace: (workspaceId: string) => Promise<unknown>;
27
+ requireBlock: (workspaceId: string, id: string) => Promise<Block>;
28
+ failRun: (workspaceId: string, executionId: string, message: string, kind?: AgentFailureKind, detail?: string | null, reason?: string | null) => Promise<void>;
29
+ finalizeMerge: (workspaceId: string, blockId: string) => Promise<FinalizeMergeResult>;
30
+ }
31
+ /**
32
+ * The HUMAN decision surface on a parked run: resolve a decision, approve / request changes on /
33
+ * reject a gated proposal, ask the human-review gate for a fix, and merge (or decline to merge)
34
+ * the PR a finished run left behind. Every one of these is reached from the SPA or the public
35
+ * API when a person acts on a run the engine deliberately stopped — as opposed to the engine's
36
+ * own advance path, which is the dispatcher's.
37
+ *
38
+ * Extracted from {@link ExecutionService} as a cohesive collaborator (the `RunDispatcher`
39
+ * controller pattern: a deps object of bound engine call-backs), so the engine service keeps the
40
+ * run state machine and this file keeps the decisions people make about it. The engine exposes
41
+ * thin delegates, so no HTTP call site changed.
42
+ */
43
+ export declare class StepDecisionController {
44
+ private readonly deps;
45
+ constructor(deps: StepDecisionControllerDeps);
46
+ /**
47
+ * Several gates park on a `step.approval` but are NOT generic prose approvals — they are
48
+ * driven by their own dedicated surface, never the generic
49
+ * approve/request-changes/reject resolvers (which would advance the run bypassing the
50
+ * loop). Guard those resolvers so a stray approve can't short-circuit any of them:
51
+ * the requirements/clarity review gates, the brainstorms, the human-testing and
52
+ * visual-confirmation gates, an interview-trait gate, a companion at its rework cap
53
+ * (`companion.exceeded`), the follow-up companion with undecided items, and a coder
54
+ * parked on the implementation-fork decision (whose approve would skip the build
55
+ * dispatch entirely).
56
+ */
57
+ private assertNotIterativeGate;
58
+ /**
59
+ * Dispatch the `fixer` against the human-review gate's PR branch from a human's freeform
60
+ * instructions — bypassing the precheck + grace window. Parks a `pendingFix` on the gate step,
61
+ * consumed on the gate's next poll (see {@link evaluateGate}) which dispatches the fixer with
62
+ * the instructions folded in. A second request before the first is consumed simply replaces the
63
+ * pending instructions. Throws when no human-review gate is currently parked.
64
+ *
65
+ * The run is re-driven via `workRunner.startRun` so the pending fix is picked up promptly even
66
+ * when the driver had died (e.g. its durable advance job expired/was evicted before the stale-
67
+ * run sweeper re-drove it) — `startRun` is idempotent for a live run (the exclusive advance
68
+ * queue no-ops a duplicate send), so this only has an effect when no driver is currently
69
+ * polling. A spend-paused run is left paused (it resumes through its own path).
70
+ */
71
+ requestHumanReviewFix(workspaceId: string, blockId: string, instructions: string): Promise<ExecutionInstance>; /** Resolve a pending decision; the run's next step lets the agent finish it. */
72
+ resolveDecision(workspaceId: string, executionId: string, decisionId: string, choice: string): Promise<ExecutionInstance>;
73
+ /**
74
+ * Approve a step's gated proposal: the run advances to the next step, carrying
75
+ * the (optionally human-edited) proposal forward as context. Mirrors
76
+ * {@link resolveDecision}'s durable-wake but *advances* the pipeline instead of
77
+ * re-running the step (the step is already done). Idempotent — re-approving an
78
+ * already-approved gate is a no-op.
79
+ */
80
+ approveStep(workspaceId: string, executionId: string, approvalId: string, opts?: {
81
+ proposal?: string;
82
+ }): Promise<ExecutionInstance>;
83
+ /**
84
+ * Request changes on a step's gated proposal: the same step re-runs with the
85
+ * human's freeform feedback and/or per-block comments (and its prior proposal)
86
+ * folded into the agent's context (see {@link AgentContextBuilder}). The run is left
87
+ * `running` on the same step; on the re-run's completion the gate is raised
88
+ * afresh. At least one of `feedback`/`comments` is expected (the controller
89
+ * validates this), but an empty review is harmless — the agent simply re-runs.
90
+ */
91
+ requestStepChanges(workspaceId: string, executionId: string, approvalId: string, review: {
92
+ feedback?: string;
93
+ comments?: StepReviewComment[];
94
+ }): Promise<ExecutionInstance>;
95
+ /**
96
+ * Reject a step's gated proposal: the run stops entirely. The gate is marked
97
+ * `rejected` and the run is failed with a dedicated `rejected` failure kind, so
98
+ * the board surfaces it via the shared failure banner (block → `blocked`) with a
99
+ * Retry affordance. The parked durable run is woken so it observes the now-terminal
100
+ * status and stops (the workflow's advance loop no-ops on a non-running run).
101
+ * Idempotent — rejecting an already-terminal gate is a no-op.
102
+ */
103
+ rejectStep(workspaceId: string, executionId: string, approvalId: string, reason?: string): Promise<ExecutionInstance>;
104
+ /**
105
+ * Merge an open PR: a block moves from `pr_ready` to `done`. This is the HUMAN merge path —
106
+ * a `merge_review` / `pipeline_complete` notification act, or the inspector's merge control.
107
+ *
108
+ * `reviewEffort` records, in the same call, how much review the PR actually needed. It is
109
+ * always optional: an untagged merge settles the track record with a null tag and nothing
110
+ * downstream breaks. The record settle runs AFTER the merge and is best-effort inside the
111
+ * service, so no part of this feature can fail or block a merge.
112
+ */
113
+ mergePr(workspaceId: string, blockId: string, reviewEffort?: ReviewEffort | null): Promise<Block>;
114
+ /**
115
+ * Record that a human DECLINED to merge — they dismissed the review card rather than acting on
116
+ * it, so the class's rollup counts a rejection instead of a forever-`pending_review` row.
117
+ */
118
+ recordMergeRejection(workspaceId: string, executionId: string): Promise<void>;
119
+ }
120
+ //# sourceMappingURL=StepDecisionController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StepDecisionController.d.ts","sourceRoot":"","sources":["../../../src/modules/execution/StepDecisionController.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,347 @@
1
+ import { ConflictError, NotFoundError, ValidationError, assertFound, } from '@cat-factory/kernel';
2
+ import { companionTargets, hasTrait, INTERVIEW_GATE_TRAIT, isCompanionKind, } from '@cat-factory/agents';
3
+ import { isAsyncAgentExecutor } from '@cat-factory/kernel';
4
+ import { ARCHITECTURE_BRAINSTORM_AGENT_KIND, CLARITY_REVIEW_AGENT_KIND, HUMAN_REVIEW_AGENT_KIND, HUMAN_TEST_AGENT_KIND, REQUIREMENTS_BRAINSTORM_AGENT_KIND, REQUIREMENTS_REVIEW_AGENT_KIND, VISUAL_CONFIRM_AGENT_KIND, } from './ci.logic.js';
5
+ /**
6
+ * The HUMAN decision surface on a parked run: resolve a decision, approve / request changes on /
7
+ * reject a gated proposal, ask the human-review gate for a fix, and merge (or decline to merge)
8
+ * the PR a finished run left behind. Every one of these is reached from the SPA or the public
9
+ * API when a person acts on a run the engine deliberately stopped — as opposed to the engine's
10
+ * own advance path, which is the dispatcher's.
11
+ *
12
+ * Extracted from {@link ExecutionService} as a cohesive collaborator (the `RunDispatcher`
13
+ * controller pattern: a deps object of bound engine call-backs), so the engine service keeps the
14
+ * run state machine and this file keeps the decisions people make about it. The engine exposes
15
+ * thin delegates, so no HTTP call site changed.
16
+ */
17
+ export class StepDecisionController {
18
+ deps;
19
+ constructor(deps) {
20
+ this.deps = deps;
21
+ }
22
+ /**
23
+ * Several gates park on a `step.approval` but are NOT generic prose approvals — they are
24
+ * driven by their own dedicated surface, never the generic
25
+ * approve/request-changes/reject resolvers (which would advance the run bypassing the
26
+ * loop). Guard those resolvers so a stray approve can't short-circuit any of them:
27
+ * the requirements/clarity review gates, the brainstorms, the human-testing and
28
+ * visual-confirmation gates, an interview-trait gate, a companion at its rework cap
29
+ * (`companion.exceeded`), the follow-up companion with undecided items, and a coder
30
+ * parked on the implementation-fork decision (whose approve would skip the build
31
+ * dispatch entirely).
32
+ */
33
+ assertNotIterativeGate(step) {
34
+ if (step.agentKind === REQUIREMENTS_REVIEW_AGENT_KIND) {
35
+ throw new ConflictError('Resolve the requirements review through its review window, not the approval gate');
36
+ }
37
+ if (step.agentKind === CLARITY_REVIEW_AGENT_KIND) {
38
+ throw new ConflictError('Resolve the clarity review through its review window, not the approval gate');
39
+ }
40
+ if (step.agentKind === REQUIREMENTS_BRAINSTORM_AGENT_KIND ||
41
+ step.agentKind === ARCHITECTURE_BRAINSTORM_AGENT_KIND) {
42
+ throw new ConflictError('Resolve the brainstorm through its brainstorm window, not the approval gate');
43
+ }
44
+ if (step.agentKind === HUMAN_TEST_AGENT_KIND) {
45
+ throw new ConflictError('Resolve the human-testing gate through its window (confirm / request a fix), not the approval gate');
46
+ }
47
+ if (step.agentKind === VISUAL_CONFIRM_AGENT_KIND) {
48
+ throw new ConflictError('Resolve the visual-confirmation gate through its window (approve / request a fix), not the approval gate');
49
+ }
50
+ if (hasTrait(step.agentKind, INTERVIEW_GATE_TRAIT, this.deps.agentKindRegistry)) {
51
+ throw new ConflictError('Resolve the interview through its interview window, not the approval gate');
52
+ }
53
+ if (step.companion?.exceeded) {
54
+ throw new ConflictError('Resolve this companion review through its iteration-cap prompt, not the approval gate');
55
+ }
56
+ if (step.followUps?.enabled && step.followUps.items.some((i) => i.status === 'pending')) {
57
+ throw new ConflictError('Resolve the follow-up companion through its window (file / send back / answer / dismiss), not the approval gate');
58
+ }
59
+ if (step.forkDecision?.status === 'awaiting_choice' ||
60
+ step.forkDecision?.status === 'answering') {
61
+ // Approving here would advance the run PAST the coder step with the build never run
62
+ // (the park sits between the proposer and the Coder dispatch).
63
+ throw new ConflictError('Resolve the implementation-fork decision through its fork window (choose an approach), not the approval gate');
64
+ }
65
+ }
66
+ /**
67
+ * Dispatch the `fixer` against the human-review gate's PR branch from a human's freeform
68
+ * instructions — bypassing the precheck + grace window. Parks a `pendingFix` on the gate step,
69
+ * consumed on the gate's next poll (see {@link evaluateGate}) which dispatches the fixer with
70
+ * the instructions folded in. A second request before the first is consumed simply replaces the
71
+ * pending instructions. Throws when no human-review gate is currently parked.
72
+ *
73
+ * The run is re-driven via `workRunner.startRun` so the pending fix is picked up promptly even
74
+ * when the driver had died (e.g. its durable advance job expired/was evicted before the stale-
75
+ * run sweeper re-drove it) — `startRun` is idempotent for a live run (the exclusive advance
76
+ * queue no-ops a duplicate send), so this only has an effect when no driver is currently
77
+ * polling. A spend-paused run is left paused (it resumes through its own path).
78
+ */
79
+ async requestHumanReviewFix(workspaceId, blockId, instructions) {
80
+ const block = await this.deps.blockRepository.get(workspaceId, blockId);
81
+ if (!block?.executionId) {
82
+ throw new ConflictError('No human-review gate is currently awaiting input');
83
+ }
84
+ // Optimistic-concurrency write: parking the `pendingFix` can race the gate's own poll
85
+ // (the durable driver advancing the run), so re-read + re-apply on fresh state instead
86
+ // of clobbering — the lost-update fix, same path as resolveDecision. The validation runs
87
+ // inside the mutation so it sees the run as it stands at write time.
88
+ const instance = await this.deps.runStateMachine.mutateInstance(workspaceId, block.executionId, (inst) => {
89
+ const step = inst.steps[inst.currentStep];
90
+ if (!step || step.agentKind !== HUMAN_REVIEW_AGENT_KIND || !step.gate) {
91
+ throw new ConflictError('No human-review gate is currently awaiting input');
92
+ }
93
+ // The fix is consumed by evaluateGate's pendingFix branch, which dispatches the fixer
94
+ // ONLY when the gate's provider is wired AND there is an async executor to escalate to.
95
+ // Reject up front when neither holds, instead of silently parking a pendingFix the gate
96
+ // would discard on its pass-through (an unwired gate advances) — the caller must see the
97
+ // failure, not a 200.
98
+ const gate = this.deps.runDispatcher.gateFor(step.agentKind);
99
+ if (!gate?.wired() || !isAsyncAgentExecutor(this.deps.agentExecutor)) {
100
+ throw new ConflictError('The human-review gate cannot dispatch a fix on this deployment (no review provider or async executor configured)');
101
+ }
102
+ step.gate.pendingFix = { instructions, at: this.deps.clock.now() };
103
+ // Re-arm a decision-parked run so the re-driven loop polls instead of no-oping; a spend-
104
+ // paused run stays paused.
105
+ if (inst.status === 'blocked')
106
+ inst.status = 'running';
107
+ });
108
+ await this.deps.runStateMachine.emitInstance(workspaceId, instance);
109
+ // Ensure a driver is active to consume the pending fix (idempotent for a live run).
110
+ if (instance.status === 'running') {
111
+ await this.deps.workRunner.startRun(workspaceId, instance.id);
112
+ }
113
+ return instance;
114
+ } /** Resolve a pending decision; the run's next step lets the agent finish it. */
115
+ async resolveDecision(workspaceId, executionId, decisionId, choice) {
116
+ await this.deps.requireWorkspace(workspaceId);
117
+ // Optimistic-concurrency write: a second resolve (double-click) or a racing driver
118
+ // poll can't clobber the chosen decision — the loser re-reads and re-applies.
119
+ const instance = await this.deps.runStateMachine.mutateInstance(workspaceId, executionId, async (inst) => {
120
+ const step = inst.steps.find((s) => s.decision?.id === decisionId);
121
+ if (!step || !step.decision)
122
+ throw new NotFoundError('Decision', decisionId);
123
+ step.decision.chosen = choice;
124
+ this.deps.stepGraph.startStep(step);
125
+ if (inst.status === 'blocked')
126
+ inst.status = 'running';
127
+ await this.deps.runStateMachine.updateBlockProgress(workspaceId, inst, 'in_progress');
128
+ });
129
+ // Wake the parked durable run, if any. The DB write above remains the source
130
+ // of truth (so the backstop sweeper can still re-drive it); the signal is an
131
+ // optimisation that lets the workflow continue immediately.
132
+ await this.deps.workRunner.signalDecision(workspaceId, instance.id, decisionId, choice);
133
+ await this.deps.runStateMachine.emitInstance(workspaceId, instance);
134
+ return instance;
135
+ }
136
+ /**
137
+ * Approve a step's gated proposal: the run advances to the next step, carrying
138
+ * the (optionally human-edited) proposal forward as context. Mirrors
139
+ * {@link resolveDecision}'s durable-wake but *advances* the pipeline instead of
140
+ * re-running the step (the step is already done). Idempotent — re-approving an
141
+ * already-approved gate is a no-op.
142
+ */
143
+ async approveStep(workspaceId, executionId, approvalId, opts = {}) {
144
+ await this.deps.requireWorkspace(workspaceId);
145
+ // Optimistic-concurrency write like resolveDecision/requestStepChanges: an approve
146
+ // holding a stale snapshot (a racing reject, a driver poll, a terminal transition)
147
+ // must re-read and re-validate rather than blind-write — otherwise it can resurrect
148
+ // a run another writer already failed. The advance's in-memory half runs inside the
149
+ // CAS; the non-idempotent side effects (block writes, driver signal, emit) run once
150
+ // after, on the winning state.
151
+ let stepIndex = -1;
152
+ let alreadyApproved = false;
153
+ const instance = await this.deps.runStateMachine.mutateInstance(workspaceId, executionId, (inst) => {
154
+ alreadyApproved = false;
155
+ stepIndex = inst.steps.findIndex((s) => s.approval?.id === approvalId);
156
+ const step = inst.steps[stepIndex];
157
+ if (!step || !step.approval)
158
+ throw new NotFoundError('Approval', approvalId);
159
+ this.assertNotIterativeGate(step);
160
+ if (step.approval.status === 'approved') {
161
+ alreadyApproved = true;
162
+ return;
163
+ }
164
+ if (step.approval.status === 'rejected') {
165
+ throw new ConflictError(`Approval '${approvalId}' was rejected`);
166
+ }
167
+ if (inst.status === 'failed' || inst.status === 'done') {
168
+ throw new ConflictError(`Execution '${executionId}' is already ${inst.status}`);
169
+ }
170
+ // A human edit to the proposal replaces the agent's text, so the revised
171
+ // proposal is what downstream steps read (via priorOutputs). That only holds when the
172
+ // output IS the agent's work product: a step whose output is a RENDERING of an
173
+ // already-ingested artifact (the spec doc, the blueprint tree, the initiative plan —
174
+ // see `reviewableArtifactOutput`) would take the edit into `step.output` while the
175
+ // committed artifact stayed the ingested one, so the correction silently reaches
176
+ // nothing. Refuse rather than accept-and-drop; the reviewer's route is "request
177
+ // changes", which re-runs the producer with the correction as feedback.
178
+ if (opts.proposal !== undefined) {
179
+ if (step.outputIsRendered) {
180
+ throw new ValidationError("This step's output is a rendering of the artifact it already produced, so edits " +
181
+ 'to it cannot change that artifact. Request changes instead — the step re-runs ' +
182
+ 'with your feedback.', { reason: 'proposal_not_editable' });
183
+ }
184
+ step.output = opts.proposal;
185
+ step.approval.proposal = opts.proposal;
186
+ }
187
+ step.approval.status = 'approved';
188
+ // A gate is never raised on the final step, but the shared advance stays defensive.
189
+ this.deps.runStateMachine.advanceRunPastGate(inst, stepIndex);
190
+ });
191
+ if (alreadyApproved)
192
+ return instance;
193
+ await this.deps.runStateMachine.settleAdvancedGate(workspaceId, instance, stepIndex);
194
+ return instance;
195
+ }
196
+ /**
197
+ * Request changes on a step's gated proposal: the same step re-runs with the
198
+ * human's freeform feedback and/or per-block comments (and its prior proposal)
199
+ * folded into the agent's context (see {@link AgentContextBuilder}). The run is left
200
+ * `running` on the same step; on the re-run's completion the gate is raised
201
+ * afresh. At least one of `feedback`/`comments` is expected (the controller
202
+ * validates this), but an empty review is harmless — the agent simply re-runs.
203
+ */
204
+ async requestStepChanges(workspaceId, executionId, approvalId, review) {
205
+ await this.deps.requireWorkspace(workspaceId);
206
+ // Optimistic-concurrency write: two concurrent change-requests on the same gate
207
+ // (the documented double-submit) can't both dispatch a re-run — the loser re-reads,
208
+ // sees `changes_requested`, and is rejected below instead of clobbering.
209
+ const instance = await this.deps.runStateMachine.mutateInstance(workspaceId, executionId, (inst) => {
210
+ const step = inst.steps.find((s) => s.approval?.id === approvalId);
211
+ if (!step || !step.approval)
212
+ throw new NotFoundError('Approval', approvalId);
213
+ this.assertNotIterativeGate(step);
214
+ if (step.approval.status === 'approved') {
215
+ throw new ConflictError(`Approval '${approvalId}' is already approved`);
216
+ }
217
+ if (step.approval.status === 'rejected') {
218
+ throw new ConflictError(`Approval '${approvalId}' was rejected`);
219
+ }
220
+ // A re-run is already in flight (and will raise a fresh gate on completion);
221
+ // acting on this now-stale gate id would dispatch duplicate work.
222
+ if (step.approval.status === 'changes_requested') {
223
+ throw new ConflictError(`Approval '${approvalId}' is already being re-run`);
224
+ }
225
+ const stepIndex = inst.steps.findIndex((s) => s.approval?.id === approvalId);
226
+ step.approval.status = 'changes_requested';
227
+ step.approval.feedback = review.feedback;
228
+ step.approval.comments = review.comments?.length ? review.comments : undefined;
229
+ // A companion's gate reviews the PRODUCER's output, not the companion's own work:
230
+ // requesting changes here must re-run the producer (with the human's feedback
231
+ // folded in) and re-grade, NOT re-run the companion. Redirect the rework to the
232
+ // nearest preceding step of one of the companion's target kinds.
233
+ if (isCompanionKind(step.agentKind)) {
234
+ const targets = companionTargets(step.agentKind);
235
+ let producerIndex = -1;
236
+ for (let i = stepIndex - 1; i >= 0; i--) {
237
+ if (targets.includes(inst.steps[i].agentKind)) {
238
+ producerIndex = i;
239
+ break;
240
+ }
241
+ }
242
+ const producer = producerIndex >= 0 ? inst.steps[producerIndex] : undefined;
243
+ if (producer) {
244
+ // Re-run the producer (with the human's feedback) and every step up to and
245
+ // including the companion, then the companion re-grades. Does NOT touch the
246
+ // companion's automatic-rework budget — a human-driven iteration is unbounded.
247
+ const previousProposal = producer.output ?? step.approval.proposal;
248
+ this.deps.stepGraph.rerunProducerThrough(inst, producerIndex, stepIndex, {
249
+ previousProposal,
250
+ feedback: review.feedback ?? '',
251
+ ...(review.comments?.length ? { comments: review.comments } : {}),
252
+ });
253
+ if (inst.status === 'blocked')
254
+ inst.status = 'running';
255
+ return;
256
+ }
257
+ }
258
+ // Drop the live job handle so the re-run dispatches fresh work rather than
259
+ // re-attaching to the finished job (async steps); inline steps ignore this.
260
+ step.jobId = undefined;
261
+ // A requested re-run is a fresh execution: clear the prior timing so the next
262
+ // start/finish times this attempt rather than spanning the human gate wait.
263
+ step.startedAt = null;
264
+ step.finishedAt = null;
265
+ this.deps.stepGraph.startStep(step);
266
+ if (inst.status === 'blocked')
267
+ inst.status = 'running';
268
+ });
269
+ await this.deps.workRunner.signalDecision(workspaceId, instance.id, approvalId, 'changes_requested');
270
+ await this.deps.runStateMachine.emitInstance(workspaceId, instance);
271
+ return instance;
272
+ }
273
+ /**
274
+ * Reject a step's gated proposal: the run stops entirely. The gate is marked
275
+ * `rejected` and the run is failed with a dedicated `rejected` failure kind, so
276
+ * the board surfaces it via the shared failure banner (block → `blocked`) with a
277
+ * Retry affordance. The parked durable run is woken so it observes the now-terminal
278
+ * status and stops (the workflow's advance loop no-ops on a non-running run).
279
+ * Idempotent — rejecting an already-terminal gate is a no-op.
280
+ */
281
+ async rejectStep(workspaceId, executionId, approvalId, reason) {
282
+ await this.deps.requireWorkspace(workspaceId);
283
+ // Optimistic-concurrency write: a reject racing the durable driver (or a concurrent
284
+ // resolve/request-changes on the same gate) re-reads and re-applies instead of
285
+ // clobbering the other writer — the lost-update fix, same as resolveDecision.
286
+ let alreadyRejected = false;
287
+ const instance = await this.deps.runStateMachine.mutateInstance(workspaceId, executionId, (inst) => {
288
+ const step = inst.steps.find((s) => s.approval?.id === approvalId);
289
+ if (!step || !step.approval)
290
+ throw new NotFoundError('Approval', approvalId);
291
+ this.assertNotIterativeGate(step);
292
+ if (step.approval.status === 'approved') {
293
+ throw new ConflictError(`Approval '${approvalId}' is already approved`);
294
+ }
295
+ // A re-run is in flight; this gate id is stale (a fresh one is raised on its
296
+ // completion). Reject the current gate via that fresh id, not this one.
297
+ if (step.approval.status === 'changes_requested') {
298
+ throw new ConflictError(`Approval '${approvalId}' is being re-run`);
299
+ }
300
+ // Already rejected (and the run already failed): leave it as-is and skip failRun below.
301
+ if (step.approval.status === 'rejected') {
302
+ alreadyRejected = true;
303
+ return;
304
+ }
305
+ step.approval.status = 'rejected';
306
+ if (reason)
307
+ step.approval.feedback = reason;
308
+ });
309
+ if (alreadyRejected)
310
+ return instance;
311
+ const message = reason
312
+ ? `A reviewer rejected the proposal: ${reason}`
313
+ : 'A reviewer rejected the proposal, stopping the run.';
314
+ // failRun persists the terminal failure + flips the block to `blocked` and emits.
315
+ await this.deps.failRun(workspaceId, executionId, message, 'rejected');
316
+ // Wake the parked durable run; it re-reads the now-terminal status and stops.
317
+ await this.deps.workRunner.signalDecision(workspaceId, instance.id, approvalId, 'rejected');
318
+ return assertFound(await this.deps.executionRepository.get(workspaceId, executionId), 'Execution', executionId);
319
+ }
320
+ /**
321
+ * Merge an open PR: a block moves from `pr_ready` to `done`. This is the HUMAN merge path —
322
+ * a `merge_review` / `pipeline_complete` notification act, or the inspector's merge control.
323
+ *
324
+ * `reviewEffort` records, in the same call, how much review the PR actually needed. It is
325
+ * always optional: an untagged merge settles the track record with a null tag and nothing
326
+ * downstream breaks. The record settle runs AFTER the merge and is best-effort inside the
327
+ * service, so no part of this feature can fail or block a merge.
328
+ */
329
+ async mergePr(workspaceId, blockId, reviewEffort) {
330
+ await this.deps.requireWorkspace(workspaceId);
331
+ const block = await this.deps.requireBlock(workspaceId, blockId);
332
+ if (block.status !== 'pr_ready') {
333
+ throw new ConflictError(`Block '${blockId}' has no PR awaiting merge`, 'no_pr_to_merge');
334
+ }
335
+ await this.deps.finalizeMerge(workspaceId, blockId);
336
+ await this.deps.mergePolicy.recordHumanMerge(workspaceId, blockId, reviewEffort);
337
+ return this.deps.requireBlock(workspaceId, blockId);
338
+ }
339
+ /**
340
+ * Record that a human DECLINED to merge — they dismissed the review card rather than acting on
341
+ * it, so the class's rollup counts a rejection instead of a forever-`pending_review` row.
342
+ */
343
+ recordMergeRejection(workspaceId, executionId) {
344
+ return this.deps.mergePolicy.recordRejection(workspaceId, executionId);
345
+ }
346
+ }
347
+ //# sourceMappingURL=StepDecisionController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StepDecisionController.js","sourceRoot":"","sources":["../../../src/modules/execution/StepDecisionController.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { AgentPromptRepository, Logger, PipelineStep, WorkspaceAgentSettingsRepository } from '@cat-factory/kernel';
2
+ import type { AgentKindRegistry } from '@cat-factory/agents';
3
+ /** The narrow slice of the builder's dependencies these two resolvers need. */
4
+ export interface DispatchPromptSettingsDeps {
5
+ agentKindRegistry: AgentKindRegistry;
6
+ /** The append-only per-workspace prompt log. Absent ⇒ the override feature is not wired. */
7
+ agentPrompts?: AgentPromptRepository;
8
+ /** The per-workspace, per-kind generation settings. Absent ⇒ the feature is not wired. */
9
+ agentSettings?: WorkspaceAgentSettingsRepository;
10
+ logger?: Logger;
11
+ }
12
+ /**
13
+ * The base system prompt this dispatch replaces the shipped one with, or undefined to run the
14
+ * shipped one. Two tiers converge here — the deployment's registered agent-kind VARIANT for this
15
+ * step and the WORKSPACE's edited prompt for the kind — because they are the same unit of text.
16
+ *
17
+ * The head revision's `text` is `null` when the workspace deliberately went BACK to the built-in —
18
+ * a real state, distinct from never having edited the kind, which is why it is recorded rather than
19
+ * deleted. Both resolve to "no override" here, so a revert keeps tracking the shipped prompt as the
20
+ * product bumps it instead of pinning a stale copy.
21
+ *
22
+ * MUTATES the step to pin what it resolved (`promptRevision`, `promptVariant`), because neither can
23
+ * be re-derived afterwards: the prompt log is append-only, so re-reading it at any later point — a
24
+ * Kaizen grading, a debug read, a post-mortem — would answer about whatever landed since. The
25
+ * VARIANT is pinned for a second reason: `stepOptions.agentVariantId` is what the pipeline ASKED
26
+ * for, and a workspace override or a withdrawal can mean the variant's text never reached the
27
+ * prompt, so only a pin taken HERE can answer what the step actually ran under.
28
+ */
29
+ export declare function resolveDispatchSystemPrompt(deps: DispatchPromptSettingsDeps, workspaceId: string, agentKind: string, step: PipelineStep): Promise<{
30
+ systemPromptOverride?: string;
31
+ }>;
32
+ /**
33
+ * The output-token ceiling this dispatch runs under, or undefined to run the deployment routing
34
+ * default.
35
+ *
36
+ * NARROWEST TIER WINS: the step's own `stepOptions.maxOutputTokens` beats the workspace's per-kind
37
+ * setting, which beats the deployment routing ceiling. The step value is read WITHOUT touching the
38
+ * repository, so a pipeline that pins its own budget costs no query.
39
+ */
40
+ export declare function resolveDispatchMaxOutputTokens(deps: DispatchPromptSettingsDeps, workspaceId: string, agentKind: string, step: PipelineStep): Promise<{
41
+ maxOutputTokens?: number;
42
+ }>;
43
+ //# sourceMappingURL=dispatchPromptSettings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatchPromptSettings.d.ts","sourceRoot":"","sources":["../../../src/modules/execution/dispatchPromptSettings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,MAAM,EACN,YAAY,EACZ,gCAAgC,EACjC,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAsB5D,+EAA+E;AAC/E,MAAM,WAAW,0BAA0B;IACzC,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,4FAA4F;IAC5F,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,0FAA0F;IAC1F,aAAa,CAAC,EAAE,gCAAgC,CAAA;IAChD,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,0BAA0B,EAChC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA+C5C;AAED;;;;;;;GAOG;AACH,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,0BAA0B,EAChC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC;IAAE,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAKvC"}
@@ -0,0 +1,76 @@
1
+ import { applyAgentVariant, shippedBasePromptFor } from '@cat-factory/agents';
2
+ /**
3
+ * The base system prompt this dispatch replaces the shipped one with, or undefined to run the
4
+ * shipped one. Two tiers converge here — the deployment's registered agent-kind VARIANT for this
5
+ * step and the WORKSPACE's edited prompt for the kind — because they are the same unit of text.
6
+ *
7
+ * The head revision's `text` is `null` when the workspace deliberately went BACK to the built-in —
8
+ * a real state, distinct from never having edited the kind, which is why it is recorded rather than
9
+ * deleted. Both resolve to "no override" here, so a revert keeps tracking the shipped prompt as the
10
+ * product bumps it instead of pinning a stale copy.
11
+ *
12
+ * MUTATES the step to pin what it resolved (`promptRevision`, `promptVariant`), because neither can
13
+ * be re-derived afterwards: the prompt log is append-only, so re-reading it at any later point — a
14
+ * Kaizen grading, a debug read, a post-mortem — would answer about whatever landed since. The
15
+ * VARIANT is pinned for a second reason: `stepOptions.agentVariantId` is what the pipeline ASKED
16
+ * for, and a workspace override or a withdrawal can mean the variant's text never reached the
17
+ * prompt, so only a pin taken HERE can answer what the step actually ran under.
18
+ */
19
+ export async function resolveDispatchSystemPrompt(deps, workspaceId, agentKind, step) {
20
+ const head = await deps.agentPrompts?.head(workspaceId, agentKind);
21
+ // Cleared, not left stale: a re-dispatch after the workspace reverted must not keep reporting
22
+ // the revision the previous attempt ran under.
23
+ step.promptRevision = head?.text ? head.revision : undefined;
24
+ // The variant applies only when the kind being dispatched IS the step's own — a helper dispatched
25
+ // off this step (a gate's fixer, the fork proposer) is a different agent and must not inherit the
26
+ // step's alternate prompt. Same rule as `followUpCompanion`.
27
+ const variantId = agentKind === step.agentKind ? step.stepOptions?.agentVariantId : undefined;
28
+ const variant = variantId ? deps.agentKindRegistry.variant(variantId) : undefined;
29
+ // A variant the deployment has since withdrawn: the step runs the SHIPPED prompt, which is the
30
+ // safe disposition, but it is not what the pipeline asked for — so say so rather than let a step
31
+ // quietly stop being the variation someone configured.
32
+ if (variantId && !variant) {
33
+ deps.logger?.warn('Agent kind variant is not registered; running the shipped prompt', {
34
+ agentKind,
35
+ variantId,
36
+ });
37
+ }
38
+ const { prompt, applied, fingerprint } = applyAgentVariant(
39
+ // Only paid for when a variant is actually selected; the shipped prompt is what its addition
40
+ // folds onto and what its replacement is measured against.
41
+ variant ? shippedBasePromptFor(agentKind, deps.agentKindRegistry) : '', variant, head?.text ?? undefined);
42
+ // The workspace out-ranks the deployment on the same unit of text, so its edit DISPLACES a
43
+ // variant's replacement. That is the intended precedence, but it is a silent loss of something a
44
+ // deployment configured in code — the one disposition here with no other symptom — so it is
45
+ // reported at the fold, exactly as a withdrawal is above.
46
+ if (applied === 'superseded' || applied === 'addition-only') {
47
+ deps.logger?.warn("Workspace prompt override displaced the agent variant's own prompt for this dispatch", { agentKind, variantId, promptRevision: step.promptRevision, applied });
48
+ }
49
+ // What the dispatch DID with the step's selected variant, so a later reader is never told a step
50
+ // ran a variation whose text never reached it. Cleared, not left stale, for the same reason
51
+ // `promptRevision` is: a re-dispatch resolves it afresh.
52
+ step.promptVariant = variantId
53
+ ? {
54
+ id: variantId,
55
+ applied: variant ? applied : 'withdrawn',
56
+ ...(fingerprint ? { fingerprint } : {}),
57
+ }
58
+ : undefined;
59
+ return prompt ? { systemPromptOverride: prompt } : {};
60
+ }
61
+ /**
62
+ * The output-token ceiling this dispatch runs under, or undefined to run the deployment routing
63
+ * default.
64
+ *
65
+ * NARROWEST TIER WINS: the step's own `stepOptions.maxOutputTokens` beats the workspace's per-kind
66
+ * setting, which beats the deployment routing ceiling. The step value is read WITHOUT touching the
67
+ * repository, so a pipeline that pins its own budget costs no query.
68
+ */
69
+ export async function resolveDispatchMaxOutputTokens(deps, workspaceId, agentKind, step) {
70
+ const fromStep = step.stepOptions?.maxOutputTokens;
71
+ if (fromStep != null)
72
+ return { maxOutputTokens: fromStep };
73
+ const configured = await deps.agentSettings?.get(workspaceId, agentKind);
74
+ return configured?.maxOutputTokens != null ? { maxOutputTokens: configured.maxOutputTokens } : {};
75
+ }
76
+ //# sourceMappingURL=dispatchPromptSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatchPromptSettings.js","sourceRoot":"","sources":["../../../src/modules/execution/dispatchPromptSettings.ts"],"names":[],"mappings":"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"}