@cat-factory/orchestration 0.129.2 → 0.129.4

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.
@@ -0,0 +1,451 @@
1
+ import { INITIATIVE_ANALYST_AGENT_KIND, INITIATIVE_COMMITTER_AGENT_KIND, INITIATIVE_PLANNER_AGENT_KIND, } from '@cat-factory/kernel';
2
+ import { FORK_PROPOSER_KIND, PR_REVIEWER_KIND, hasTrait, isCompanionKind, isContainerBackedCompanion, INTERVIEW_GATE_TRAIT, TASK_ESTIMATOR_AGENT_KIND, } from '@cat-factory/agents';
3
+ import { DEPLOYER_AGENT_KIND, isDeployStep } from '@cat-factory/integrations';
4
+ import { BUG_INTAKE_AGENT_KIND } from '../pipelines/pipelineShape.js';
5
+ import { coerceTaskEstimate, summarizeEstimate } from '../estimation/estimate.logic.js';
6
+ import { renderInvestigationDigest } from './bugInvestigation.logic.js';
7
+ import { renderReproDigest } from './reproTest.logic.js';
8
+ import { ARCHITECTURE_BRAINSTORM_AGENT_KIND, BLUEPRINTS_AGENT_KIND, BUG_INVESTIGATOR_AGENT_KIND, CLARITY_REVIEW_AGENT_KIND, HUMAN_TEST_AGENT_KIND, isTesterKind, MERGER_AGENT_KIND, REPRO_TEST_AGENT_KIND, REQUIREMENTS_BRAINSTORM_AGENT_KIND, REQUIREMENTS_REVIEW_AGENT_KIND, SPEC_WRITER_AGENT_KIND, TRACKER_AGENT_KIND, VISUAL_CONFIRM_AGENT_KIND, } from './ci.logic.js';
9
+ import { PrReviewController, PR_REVIEW_STEP_KIND } from './PrReviewController.js';
10
+ import { forkPhasePending, resolveForkTriState } from './forkDecision.logic.js';
11
+ import { isRalphKind } from './ralph.logic.js';
12
+ import { FALLTHROUGH_STEP_HANDLER_ORDER, } from './step-handler-registry.js';
13
+ /**
14
+ * The inline review/brainstorm gate kinds, all driven through the {@link ReviewGateController}
15
+ * by the dispatcher's `review-gate` StepHandler. Kept in sync with the handler's `switch`.
16
+ */
17
+ const REVIEW_GATE_AGENT_KINDS = new Set([
18
+ REQUIREMENTS_REVIEW_AGENT_KIND,
19
+ CLARITY_REVIEW_AGENT_KIND,
20
+ REQUIREMENTS_BRAINSTORM_AGENT_KIND,
21
+ ARCHITECTURE_BRAINSTORM_AGENT_KIND,
22
+ ]);
23
+ /**
24
+ * Build the order-sorted per-step-kind handler list (built-ins constructed inline, closing over
25
+ * the injected {@link DispatcherRegistryDeps}). Engine-internal: there is no public
26
+ * `registerStepHandler` seam. Phase 0 registers only the generic fallthrough; later phases
27
+ * prepend more-specific handlers with lower `order`.
28
+ */
29
+ export function buildStepHandlerRegistry(d) {
30
+ const handlers = [
31
+ // A `deployer` step provisions an ephemeral environment deterministically via the
32
+ // provider — no LLM, no token usage — when the integration is wired. Unwired, its
33
+ // `canHandle` is false so the step falls through to the generic agent path.
34
+ {
35
+ kind: DEPLOYER_AGENT_KIND,
36
+ order: 100,
37
+ canHandle: ({ step }) => !!d.environmentProvisioning && isDeployStep(step.agentKind),
38
+ handle: ({ workspaceId, instance, step, block, isFinalStep }) => d.deployer.runDeployerStep(workspaceId, instance, step, block, isFinalStep),
39
+ },
40
+ // A `tracker` step files a GitHub issue / Jira ticket from the preceding `analysis`
41
+ // output (the tech-debt pipeline) — no LLM of its own. It is a pass-through when no
42
+ // tracker provider is wired or none is configured for the workspace (handled inside
43
+ // {@link runTracker}, which still records a result), so it always claims the step.
44
+ {
45
+ kind: TRACKER_AGENT_KIND,
46
+ order: 110,
47
+ canHandle: ({ step }) => step.agentKind === TRACKER_AGENT_KIND,
48
+ handle: async ({ workspaceId, instance, step, block, isFinalStep }) => {
49
+ const result = await d.runTracker(workspaceId, instance, block);
50
+ return d.recordStepResult(workspaceId, instance, step, isFinalStep, result);
51
+ },
52
+ },
53
+ // A `bug-intake` step (the recurring bug-triage pipeline) pulls ONE matching open issue from
54
+ // the schedule's tracker board, claims it, and seeds the reused block from it — no LLM of its
55
+ // own, the inbound dual of `tracker`. On no match (or no task source wired) it completes the
56
+ // run successfully, skipping every remaining step. Handled entirely inside
57
+ // {@link runBugIntake}, so it always claims the step.
58
+ {
59
+ kind: BUG_INTAKE_AGENT_KIND,
60
+ order: 111,
61
+ canHandle: ({ step }) => step.agentKind === BUG_INTAKE_AGENT_KIND,
62
+ handle: ({ workspaceId, instance, step, block, isFinalStep }) => d.runBugIntake(workspaceId, instance, step, block, isFinalStep),
63
+ },
64
+ // The interactive-INTERVIEWER gates (`initiative-interviewer`, `doc-interviewer`, …): an
65
+ // inline LLM gate that PARKS the run on a decision-wait while the human answers the
66
+ // interviewer's questions through a dedicated window, then synthesizes a brief and advances
67
+ // (see {@link InterviewGateController}). Routed by the `interview-gate` TRAIT — the same
68
+ // marker the re-park + approval guards key off — and dispatched to the controller registered
69
+ // for the step's `agentKind`, so a new interviewer just carries the trait + wires its
70
+ // controller (no new branch here). Pass-through when the interviewer isn't wired (no model /
71
+ // no store) so pipelines + conformance run unchanged; a wired-but-unmatched trait kind is a
72
+ // pipeline authoring error, but we still advance rather than wedge the run.
73
+ {
74
+ kind: 'interview-gate',
75
+ order: 113,
76
+ canHandle: ({ step }) => hasTrait(step.agentKind, INTERVIEW_GATE_TRAIT, d.agentKindRegistry),
77
+ handle: ({ workspaceId, instance, step, block, isFinalStep }) => {
78
+ const controller = d.interviewControllers.get(step.agentKind);
79
+ return controller
80
+ ? controller.evaluate(workspaceId, instance, step, block, isFinalStep)
81
+ : d.recordStepResult(workspaceId, instance, step, isFinalStep, { output: '' });
82
+ },
83
+ },
84
+ // The `initiative-committer` step persists an APPROVED initiative plan: it runs
85
+ // strictly after the planner's human gate, flips the entity to `executing`, and
86
+ // mirrors the tracker into the repo (`docs/initiatives/<slug>/`) when GitHub is
87
+ // wired — no LLM of its own (the `tracker`-style deterministic one-shot).
88
+ {
89
+ kind: INITIATIVE_COMMITTER_AGENT_KIND,
90
+ order: 115,
91
+ canHandle: ({ step }) => step.agentKind === INITIATIVE_COMMITTER_AGENT_KIND,
92
+ handle: async ({ workspaceId, instance, step, block, isFinalStep }) => {
93
+ const outcome = await d.runInitiativeCommitter(workspaceId, block);
94
+ if (outcome.kind === 'failed') {
95
+ return { kind: 'job_failed', error: outcome.error, failureKind: 'agent' };
96
+ }
97
+ return d.recordStepResult(workspaceId, instance, step, isFinalStep, outcome.result);
98
+ },
99
+ },
100
+ // The `requirements-review` / `clarity-review` / `requirements-brainstorm` /
101
+ // `architecture-brainstorm` steps are inline reviewers that park for their dedicated
102
+ // window and drive the iterative answer → incorporate → re-review loop — NOT
103
+ // container/prose agents. All four run through the {@link ReviewGateController},
104
+ // parameterised by their {@link ReviewKind} (the per-case `switch` binds each kind's
105
+ // review type so `evaluate`'s generic infers). Pass-through when the service isn't wired.
106
+ {
107
+ kind: 'review-gate',
108
+ order: 120,
109
+ canHandle: ({ step }) => REVIEW_GATE_AGENT_KINDS.has(step.agentKind),
110
+ handle: ({ workspaceId, instance, step, block, isFinalStep }) => {
111
+ switch (step.agentKind) {
112
+ case REQUIREMENTS_REVIEW_AGENT_KIND:
113
+ return d.reviewGate.evaluate(d.requirementsKind, workspaceId, instance, step, block, isFinalStep);
114
+ case CLARITY_REVIEW_AGENT_KIND:
115
+ return d.reviewGate.evaluate(d.clarityKind, workspaceId, instance, step, block, isFinalStep);
116
+ case REQUIREMENTS_BRAINSTORM_AGENT_KIND:
117
+ return d.reviewGate.evaluate(d.requirementsBrainstormKind, workspaceId, instance, step, block, isFinalStep);
118
+ case ARCHITECTURE_BRAINSTORM_AGENT_KIND:
119
+ return d.reviewGate.evaluate(d.architectureBrainstormKind, workspaceId, instance, step, block, isFinalStep);
120
+ // `canHandle` admits only the kinds in REVIEW_GATE_AGENT_KINDS, so every member
121
+ // must have an explicit case above. Throw loudly if the two ever drift (a new
122
+ // review kind added to the Set without a case here) rather than silently routing
123
+ // it to the wrong reviewer.
124
+ default:
125
+ throw new Error(`Unhandled review-gate agentKind "${step.agentKind}"`);
126
+ }
127
+ },
128
+ },
129
+ // A `human-test` gate spins up an ephemeral environment and PARKS for a human to
130
+ // validate the change in a live URL — NOT a container/prose agent and NOT a
131
+ // programmatic polling gate (the human is the verdict). Degrades to a manual (no-env)
132
+ // mode when no ephemeral-environment provider is wired. See {@link HumanTestController}.
133
+ {
134
+ kind: HUMAN_TEST_AGENT_KIND,
135
+ order: 130,
136
+ canHandle: ({ step }) => step.agentKind === HUMAN_TEST_AGENT_KIND,
137
+ handle: ({ workspaceId, instance, step, block, isFinalStep }) => d.humanTestController.evaluate(workspaceId, instance, step, block, isFinalStep),
138
+ },
139
+ // A `visual-confirmation` gate gathers the UI tester's screenshots + uploaded reference
140
+ // designs and PARKS for a human to review actual-vs-reference, then on demand dispatches
141
+ // the Tester's `fixer`. Passes through when no binary-artifact store is wired.
142
+ // See {@link VisualConfirmationController}.
143
+ {
144
+ kind: VISUAL_CONFIRM_AGENT_KIND,
145
+ order: 140,
146
+ canHandle: ({ step }) => step.agentKind === VISUAL_CONFIRM_AGENT_KIND,
147
+ handle: ({ workspaceId, instance, step, block, isFinalStep }) => d.visualConfirmationController.evaluate(workspaceId, instance, step, block, isFinalStep),
148
+ },
149
+ // A polling gate step (`ci` / `conflicts` / `post-release-health` / `human-review`) runs
150
+ // a programmatic precheck and only escalates to a helper container agent on a negative
151
+ // verdict — no LLM of its own. Pass-through when the gate's provider is not wired. One
152
+ // generic machine drives every gate; see {@link evaluateGate}. `canHandle` is the gate
153
+ // registry lookup, so this claims exactly the registered gate kinds.
154
+ {
155
+ kind: 'polling-gate',
156
+ order: 150,
157
+ canHandle: ({ step }) => d.gateFor(step.agentKind) !== undefined,
158
+ handle: ({ workspaceId, instance, step, block, isFinalStep }) => d.evaluateGate(workspaceId, instance, step, block, isFinalStep, d.gateFor(step.agentKind)),
159
+ },
160
+ // An INLINE companion (architect-companion / spec-companion) grades the nearest
161
+ // preceding producer right here and loops it back for automatic rework below the
162
+ // threshold before any human gate. CONTAINER-backed companions (reviewer / doc-reviewer)
163
+ // do NOT match — they fall through to the generic async container dispatch and have their
164
+ // verdict resolved by the completion interceptor instead. See {@link CompanionController}.
165
+ {
166
+ kind: 'inline-companion',
167
+ order: 160,
168
+ canHandle: ({ step }) => isCompanionKind(step.agentKind) && !isContainerBackedCompanion(step.agentKind),
169
+ handle: ({ workspaceId, instance, step, block, isFinalStep, options }) => d.companionController.evaluate(workspaceId, instance, step, block, isFinalStep, options),
170
+ },
171
+ // The optional implementation-fork decision phase on a Coder step (Phase A): when the
172
+ // per-task tri-state + the risk-policy gate call for it, dispatch the read-only
173
+ // `fork-proposer` explore job as a helper off THIS coder step. Claims the step only
174
+ // while the phase is pending (tri-state not `off`, not yet resolved); once resolved
175
+ // (`chosen` / `single_path` / `skipped`) it falls through so the Coder dispatches
176
+ // normally (Phase B). A parked `awaiting_choice` step is short-circuited by the
177
+ // run-lifecycle re-entry guard before this handler runs. See {@link handleForkDecisionPhase}.
178
+ {
179
+ kind: 'fork-decision',
180
+ order: 170,
181
+ canHandle: ({ step, block }) => forkPhasePending(step, resolveForkTriState(block.agentConfig)),
182
+ handle: (ctx) => d.handleForkDecisionPhase(ctx),
183
+ },
184
+ // The PR deep-review RESOLUTION phase (PR 3): after the human resolved a parked review with
185
+ // `fix` / `post`, `PrReviewController.resolve` re-armed this `pr-reviewer` step and woke the
186
+ // driver. Claim it by the re-armed status so it re-dispatches as the Fixer (`fixing`) or
187
+ // posts the selected findings as inline PR comments (`posting`) — never the generic
188
+ // pr-reviewer clone the fallthrough would run. A `reviewing`/`awaiting_selection`/resolved
189
+ // step falls through (this handler doesn't claim it). See {@link handlePrReviewResolution}.
190
+ {
191
+ kind: 'pr-review-resolution',
192
+ order: 175,
193
+ canHandle: ({ step }) => step.agentKind === PR_REVIEW_STEP_KIND &&
194
+ (step.prReview?.status === 'fixing' || step.prReview?.status === 'posting'),
195
+ handle: (ctx) => d.handlePrReviewResolution(ctx),
196
+ },
197
+ // The generic container/inline-agent step — claims every step no more-specific handler
198
+ // did. Highest order so it always runs last. See {@link handleAgentStep}.
199
+ {
200
+ kind: 'agent',
201
+ order: FALLTHROUGH_STEP_HANDLER_ORDER,
202
+ canHandle: () => true,
203
+ handle: (ctx) => d.handleAgentStep(ctx),
204
+ },
205
+ ];
206
+ return handlers.sort((a, b) => a.order - b.order);
207
+ }
208
+ /**
209
+ * Build the order-sorted completion-path interceptors (companion / tester verdict
210
+ * short-circuits), mirroring {@link buildStepHandlerRegistry}.
211
+ */
212
+ export function buildStepCompletionInterceptors(d) {
213
+ const interceptors = [
214
+ // A container-backed companion (reviewer / doc-reviewer) just finished reviewing the
215
+ // real repository on the producer's PR branch and returned its verdict as
216
+ // `result.custom`. Hand it to the companion loop, which parses the verdict and applies
217
+ // the SAME threshold / rework / human-gate handling an inline companion gets. Routed
218
+ // here (not the normal step completion) so the verdict drives the loop instead of being
219
+ // recorded as plain output. Falls through (returns null) when the block can't be loaded.
220
+ {
221
+ kind: 'companion-verdict',
222
+ order: 100,
223
+ canIntercept: ({ step }) => isCompanionKind(step.agentKind) && isContainerBackedCompanion(step.agentKind),
224
+ intercept: async ({ workspaceId, instance, step, isFinalStep, result }) => {
225
+ const companionBlock = await d.blockRepository.get(workspaceId, instance.blockId);
226
+ if (!companionBlock)
227
+ return null;
228
+ return d.companionController.resolveContainerVerdict(workspaceId, instance, step, companionBlock, isFinalStep, result);
229
+ },
230
+ },
231
+ // The `fork-proposer` explore job (Phase A of the fork decision) just finished on a
232
+ // coder step. Its structured `result.custom` is the proposal; record it onto the step's
233
+ // `forkDecision` and either park for the human to choose (≥2 usable forks) or auto-advance
234
+ // a single path — never the normal output/PR/follow-up/approval completion (none applies
235
+ // to the proposer). Keyed on the coder step carrying `forkDecision.status === 'proposing'`.
236
+ {
237
+ kind: 'fork-proposal',
238
+ order: 105,
239
+ canIntercept: ({ step }) => step.agentKind === 'coder' && step.forkDecision?.status === 'proposing',
240
+ intercept: ({ workspaceId, instance, step, result }) => {
241
+ const proposal = d.agentKindRegistry
242
+ .structuredOutput(FORK_PROPOSER_KIND)
243
+ ?.safeParse(result.custom);
244
+ return d.forkDecisionController.recordProposal(workspaceId, instance, step, proposal, step.model);
245
+ },
246
+ },
247
+ // The read-only `pr-reviewer` deep-review job just finished on a review task's step. Its
248
+ // structured `result.custom` is the sliced, prioritized findings; record them onto the
249
+ // step's `prReview` and PARK for the human to select which findings matter (≥1 finding),
250
+ // rather than the normal completion. A clean PR (no findings) returns null and falls
251
+ // through to the normal finish. Keyed on the `pr-reviewer` step kind.
252
+ {
253
+ kind: 'pr-review',
254
+ order: 106,
255
+ canIntercept: ({ step }) => step.agentKind === PR_REVIEW_STEP_KIND,
256
+ intercept: async ({ workspaceId, instance, step, result }) => {
257
+ const output = d.agentKindRegistry
258
+ .structuredOutput(PR_REVIEWER_KIND)
259
+ ?.safeParse(result.custom);
260
+ const block = await d.blockRepository.get(workspaceId, instance.blockId);
261
+ return d.prReviewController.recordFindings(workspaceId, instance, step, output, result.model ?? step.model, block);
262
+ },
263
+ },
264
+ // A `tester` step returned a structured report. On a withheld greenlight we do NOT
265
+ // finish the step: loop the `fixer` (within the attempt budget) and re-test, mirroring
266
+ // the CI gate. A greenlight (or no provider) returns null and falls through to the
267
+ // normal finish/advance below. Records the report on the step either way.
268
+ {
269
+ kind: 'tester-verdict',
270
+ order: 110,
271
+ canIntercept: ({ step, result }) => isTesterKind(step.agentKind) && result.testReport !== undefined,
272
+ intercept: ({ workspaceId, instance, step, result }) => d.testerController.resolveTesterResult(workspaceId, instance, step, result),
273
+ },
274
+ // A `ralph` iteration finished and the harness attached its programmatic validation
275
+ // verdict. Hand it to the ralph loop: a passing command finishes + advances (returns
276
+ // null → normal completion), a failing one re-dispatches a fresh iteration within the
277
+ // budget or gives up for a human — never the normal single-shot completion. Keyed on the
278
+ // `ralph` kind carrying a `ralphVerdict`; a ralph run that errored before validation (no
279
+ // verdict) falls through to the normal failure path.
280
+ {
281
+ kind: 'ralph-verdict',
282
+ order: 115,
283
+ canIntercept: ({ step, result }) => isRalphKind(step.agentKind) && result.ralphVerdict !== undefined,
284
+ intercept: ({ workspaceId, instance, step, result }) => d.ralphController.resolveRalphResult(workspaceId, instance, step, result),
285
+ },
286
+ ];
287
+ return interceptors.sort((a, b) => a.order - b.order);
288
+ }
289
+ /**
290
+ * Build the post-completion / terminal step-resolver registry, keyed by `agentKind`. The
291
+ * built-in `merger` resolver (which owns terminal status) plus the post-completion result
292
+ * reshapers are constructed inline; deployment-registered resolvers are merged last (last
293
+ * registration wins).
294
+ */
295
+ export function buildStepResolverRegistry(d) {
296
+ const resolvers = [
297
+ // The `merger` agent OWNS the merge decision, but the merge itself is mechanical
298
+ // and uses backend-held GitHub credentials the sandboxed agent never sees — so the
299
+ // engine performs it deterministically from the agent's assessment here, the moment
300
+ // the merger step finishes (NOT only when it is the pipeline's last step, which is
301
+ // why a trailing `post-release-health` step no longer disables auto-merge).
302
+ {
303
+ kind: MERGER_AGENT_KIND,
304
+ applies: (result) => result.mergeAssessment !== undefined,
305
+ resolve: async ({ workspaceId, instance, step, result }) => {
306
+ // The real merge runs the engine GitHub client under the run initiator's
307
+ // ambient context, so a per-user PAT (when set) authors the merge.
308
+ const decision = await d.runInitiatorScope(instance.initiatedBy, () => d.mergeResolver.resolveMergerStep(workspaceId, instance, result.mergeAssessment));
309
+ // Record the structured verdict on the step so the SPA's dedicated merger result
310
+ // view renders the assessment + explains the auto-merge / awaiting-review decision,
311
+ // instead of showing the agent's raw JSON.
312
+ if (decision) {
313
+ step.custom = decision;
314
+ // Drop the raw JSON only when we captured a structured assessment (the view
315
+ // renders it from `step.custom`). When the merger produced NO parseable
316
+ // assessment, keep the raw reply so an operator can still diagnose what it sent.
317
+ if (decision.assessment)
318
+ step.output = '';
319
+ }
320
+ return { ownsTerminalStatus: true };
321
+ },
322
+ },
323
+ // POST-COMPLETION resolvers — run at the early slot (after output is recorded, before
324
+ // the follow-up/approval gates), reshaping the agent's structured result into domain
325
+ // state. Lifted verbatim from the old inline `recordStepResult` branches.
326
+ //
327
+ // A Blueprinter step produced a fresh service decomposition. Validate it with the
328
+ // authoritative schema (a bad payload must never touch the board), then reconcile it
329
+ // in place onto the run's service frame.
330
+ {
331
+ kind: BLUEPRINTS_AGENT_KIND,
332
+ phase: 'post-completion',
333
+ resolve: async ({ workspaceId, instance, result }) => {
334
+ if (result.blueprintService !== undefined) {
335
+ await d.ingestBlueprint(workspaceId, instance.blockId, result.blueprintService);
336
+ }
337
+ },
338
+ },
339
+ // An initiative-analyst step produced a prose codebase analysis. Fold it onto the
340
+ // block's `initiatives` entity (`analysisSummary`) so the planner's prompt is grounded
341
+ // in it. Best-effort: an empty analysis or an unwired store simply leaves the summary
342
+ // unchanged (the planner still runs); never fail the run on a missing analysis.
343
+ {
344
+ kind: INITIATIVE_ANALYST_AGENT_KIND,
345
+ phase: 'post-completion',
346
+ resolve: async ({ workspaceId, instance, result }) => {
347
+ const summary = result.output?.trim();
348
+ if (!d.initiativeService || !summary)
349
+ return;
350
+ await d.initiativeService.recordAnalysis(workspaceId, instance.blockId, summary);
351
+ },
352
+ },
353
+ // An initiative-planner step produced a plan draft. Ingest it into the block's
354
+ // `initiatives` entity (strict-parse at the trust boundary; replay-idempotent —
355
+ // the same draft re-applies to identical content). The run then parks at the
356
+ // planner's human gate; the committer step later mirrors the APPROVED plan into
357
+ // the repo. A malformed draft fails the step loudly — completing the run without
358
+ // an ingested plan would strand the initiative in `planning` with a green run.
359
+ {
360
+ kind: INITIATIVE_PLANNER_AGENT_KIND,
361
+ phase: 'post-completion',
362
+ resolve: async ({ workspaceId, instance, result }) => {
363
+ if (!d.initiativeService)
364
+ return;
365
+ if (result.initiativePlan === undefined) {
366
+ throw new Error('The initiative planner returned no usable plan (malformed or empty JSON)');
367
+ }
368
+ const ingested = await d.initiativeService.ingestPlan(workspaceId, instance.blockId, result.initiativePlan);
369
+ if (!ingested) {
370
+ throw new Error('No initiative entity found for this block — cannot ingest the plan');
371
+ }
372
+ },
373
+ },
374
+ // A spec-writer step produced the service's unified specification (`spec.json`) and
375
+ // committed it to the implementation branch — strict-validate it then nudge clients —
376
+ // and reports its BUSINESS-vs-TECHNICAL determination. "No business specs" (a purely
377
+ // technical task) is a valid outcome the spec-companion's convergence later combines
378
+ // with its `technicalCorroborated` verdict; recorded even when false so a re-run
379
+ // reflects the latest.
380
+ {
381
+ kind: SPEC_WRITER_AGENT_KIND,
382
+ phase: 'post-completion',
383
+ resolve: async ({ workspaceId, step, result }) => {
384
+ if (result.spec !== undefined)
385
+ await d.ingestSpec(workspaceId, result.spec);
386
+ step.noBusinessSpecs = result.noBusinessSpecs === true;
387
+ },
388
+ },
389
+ // A `task-estimator` step emits a JSON triage (complexity/risk/impact). Parse it
390
+ // tolerantly, persist it on the block (used to gate consensus steps + surfaced in the
391
+ // UI), and replace the raw JSON output with a readable summary. An unparseable estimate
392
+ // leaves the block untouched and keeps the raw output (no run failure). Works the same
393
+ // whether the single-actor estimator or the consensus ranked-scoring variant produced
394
+ // the JSON. Running at the post-completion slot keeps the summary in `step.output`
395
+ // before the approval gate reads it as the proposal.
396
+ // A `bug-investigator` step returns its STRUCTURED triage as `result.custom` (kept on
397
+ // `step.custom` for the generic-structured view + the clarity gate's structured read).
398
+ // Render a prose digest into `step.output` at the post-completion slot so downstream
399
+ // steps (estimator / repro-test / coder) read the investigation via `priorOutputs`
400
+ // (which carries only `step.output`), and the clarity gate's investigation-prose context
401
+ // sees it too. An unparseable result leaves the agent's raw reply on `step.output`.
402
+ {
403
+ kind: BUG_INVESTIGATOR_AGENT_KIND,
404
+ phase: 'post-completion',
405
+ applies: (result) => result.custom !== undefined,
406
+ resolve: async ({ result }) => {
407
+ const digest = renderInvestigationDigest(result.custom);
408
+ if (digest)
409
+ return { output: digest };
410
+ },
411
+ },
412
+ // A `repro-test` step returns its STRUCTURED outcome as `result.custom` (kept on
413
+ // `step.custom` for the generic-structured view). Render a short prose digest into
414
+ // `step.output` at the post-completion slot so the coder reads the reproduction result
415
+ // (reproduced / conceded + why) via `priorOutputs` (which carries only `step.output`).
416
+ // Conceding never fails the run — the container tolerates a no-op (`noChangesTolerated`),
417
+ // so this only reshapes the output. An unparseable result leaves the raw reply on
418
+ // `step.output`.
419
+ {
420
+ kind: REPRO_TEST_AGENT_KIND,
421
+ phase: 'post-completion',
422
+ applies: (result) => result.custom !== undefined,
423
+ resolve: async ({ result }) => {
424
+ const digest = renderReproDigest(result.custom);
425
+ if (digest)
426
+ return { output: digest };
427
+ },
428
+ },
429
+ {
430
+ kind: TASK_ESTIMATOR_AGENT_KIND,
431
+ phase: 'post-completion',
432
+ resolve: async ({ workspaceId, instance, step, result }) => {
433
+ const estimate = coerceTaskEstimate(step.output ?? '', result.model ?? step.model ?? null, d.clock.now());
434
+ if (estimate) {
435
+ await d.blockRepository.update(workspaceId, instance.blockId, { estimate });
436
+ return { output: summarizeEstimate(estimate) };
437
+ }
438
+ },
439
+ },
440
+ ];
441
+ const map = new Map(resolvers.map((r) => [r.kind, r]));
442
+ // Merge deployment-registered resolvers, mirroring the gate registry below. A
443
+ // registered resolver of the same kind replaces the built-in (last registration wins).
444
+ // The registry is the app-owned instance injected through `CoreDependencies`, not a
445
+ // module global.
446
+ const ctx = { runInitiatorScope: d.runInitiatorScope };
447
+ for (const { kind, factory } of d.stepResolverRegistry.factories())
448
+ map.set(kind, factory(ctx));
449
+ return map;
450
+ }
451
+ //# sourceMappingURL=dispatcher-registries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher-registries.js","sourceRoot":"","sources":["../../../src/modules/execution/dispatcher-registries.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,6BAA6B,EAC7B,+BAA+B,EAC/B,6BAA6B,GAC9B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,0BAA0B,EAC1B,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAE7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AACrE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACvF,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EACL,kCAAkC,EAClC,qBAAqB,EACrB,2BAA2B,EAC3B,yBAAyB,EACzB,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,qBAAqB,EACrB,kCAAkC,EAClC,8BAA8B,EAC9B,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,eAAe,CAAA;AAOtB,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AACjF,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAI/E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EACL,8BAA8B,GAI/B,MAAM,4BAA4B,CAAA;AAInC;;;GAGG;AACH,MAAM,uBAAuB,GAAwB,IAAI,GAAG,CAAC;IAC3D,8BAA8B;IAC9B,yBAAyB;IACzB,kCAAkC;IAClC,kCAAkC;CACnC,CAAC,CAAA;AAyEF;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,CAAyB;IAChE,MAAM,QAAQ,GAAkB;QAC9B,kFAAkF;QAClF,kFAAkF;QAClF,4EAA4E;QAC5E;YACE,IAAI,EAAE,mBAAmB;YACzB,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,uBAAuB,IAAI,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC;YACpF,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,CAC9D,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC;SAC9E;QACD,oFAAoF;QACpF,oFAAoF;QACpF,oFAAoF;QACpF,mFAAmF;QACnF;YACE,IAAI,EAAE,kBAAkB;YACxB,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,kBAAkB;YAC9D,MAAM,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE;gBACpE,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAA;gBAC/D,OAAO,CAAC,CAAC,gBAAgB,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAAA;YAC7E,CAAC;SACF;QACD,6FAA6F;QAC7F,8FAA8F;QAC9F,6FAA6F;QAC7F,2EAA2E;QAC3E,sDAAsD;QACtD;YACE,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,qBAAqB;YACjE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,CAC9D,CAAC,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC;SAClE;QACD,yFAAyF;QACzF,oFAAoF;QACpF,4FAA4F;QAC5F,yFAAyF;QACzF,6FAA6F;QAC7F,sFAAsF;QACtF,6FAA6F;QAC7F,4FAA4F;QAC5F,4EAA4E;QAC5E;YACE,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,EAAE,CAAC,CAAC,iBAAiB,CAAC;YAC5F,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE;gBAC9D,MAAM,UAAU,GAAG,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;gBAC7D,OAAO,UAAU;oBACf,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC;oBACtE,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAA;YAClF,CAAC;SACF;QACD,gFAAgF;QAChF,gFAAgF;QAChF,gFAAgF;QAChF,0EAA0E;QAC1E;YACE,IAAI,EAAE,+BAA+B;YACrC,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,+BAA+B;YAC3E,MAAM,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE;gBACpE,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,sBAAsB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAA;gBAClE,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAC9B,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAA;gBAC3E,CAAC;gBACD,OAAO,CAAC,CAAC,gBAAgB,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;YACrF,CAAC;SACF;QACD,6EAA6E;QAC7E,qFAAqF;QACrF,6EAA6E;QAC7E,iFAAiF;QACjF,qFAAqF;QACrF,0FAA0F;QAC1F;YACE,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,uBAAuB,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;YACpE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE;gBAC9D,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;oBACvB,KAAK,8BAA8B;wBACjC,OAAO,CAAC,CAAC,UAAU,CAAC,QAAQ,CAC1B,CAAC,CAAC,gBAAgB,EAClB,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,WAAW,CACZ,CAAA;oBACH,KAAK,yBAAyB;wBAC5B,OAAO,CAAC,CAAC,UAAU,CAAC,QAAQ,CAC1B,CAAC,CAAC,WAAW,EACb,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,WAAW,CACZ,CAAA;oBACH,KAAK,kCAAkC;wBACrC,OAAO,CAAC,CAAC,UAAU,CAAC,QAAQ,CAC1B,CAAC,CAAC,0BAA0B,EAC5B,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,WAAW,CACZ,CAAA;oBACH,KAAK,kCAAkC;wBACrC,OAAO,CAAC,CAAC,UAAU,CAAC,QAAQ,CAC1B,CAAC,CAAC,0BAA0B,EAC5B,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,WAAW,CACZ,CAAA;oBACH,gFAAgF;oBAChF,8EAA8E;oBAC9E,iFAAiF;oBACjF,4BAA4B;oBAC5B;wBACE,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAA;gBAC1E,CAAC;YACH,CAAC;SACF;QACD,iFAAiF;QACjF,4EAA4E;QAC5E,sFAAsF;QACtF,yFAAyF;QACzF;YACE,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,qBAAqB;YACjE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,CAC9D,CAAC,CAAC,mBAAmB,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC;SAClF;QACD,wFAAwF;QACxF,yFAAyF;QACzF,+EAA+E;QAC/E,4CAA4C;QAC5C;YACE,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,yBAAyB;YACrE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,CAC9D,CAAC,CAAC,4BAA4B,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC;SAC3F;QACD,yFAAyF;QACzF,uFAAuF;QACvF,uFAAuF;QACvF,uFAAuF;QACvF,qEAAqE;QACrE;YACE,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,SAAS;YAChE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,CAC9D,CAAC,CAAC,YAAY,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAE,CAAC;SAC9F;QACD,gFAAgF;QAChF,iFAAiF;QACjF,yFAAyF;QACzF,0FAA0F;QAC1F,2FAA2F;QAC3F;YACE,IAAI,EAAE,kBAAkB;YACxB,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CACtB,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,SAAS,CAAC;YAChF,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE,CACvE,CAAC,CAAC,mBAAmB,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC;SAC3F;QACD,sFAAsF;QACtF,gFAAgF;QAChF,oFAAoF;QACpF,oFAAoF;QACpF,kFAAkF;QAClF,gFAAgF;QAChF,8FAA8F;QAC9F;YACE,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAC7B,gBAAgB,CAAC,IAAI,EAAE,mBAAmB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAChE,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,uBAAuB,CAAC,GAAG,CAAC;SAChD;QACD,4FAA4F;QAC5F,6FAA6F;QAC7F,yFAAyF;QACzF,oFAAoF;QACpF,2FAA2F;QAC3F,4FAA4F;QAC5F;YACE,IAAI,EAAE,sBAAsB;YAC5B,KAAK,EAAE,GAAG;YACV,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CACtB,IAAI,CAAC,SAAS,KAAK,mBAAmB;gBACtC,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,MAAM,KAAK,SAAS,CAAC;YAC7E,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,wBAAwB,CAAC,GAAG,CAAC;SACjD;QACD,uFAAuF;QACvF,0EAA0E;QAC1E;YACE,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,8BAA8B;YACrC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI;YACrB,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC;SACxC;KACF,CAAA;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAA;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,+BAA+B,CAC7C,CAAyB;IAEzB,MAAM,YAAY,GAAgC;QAChD,qFAAqF;QACrF,0EAA0E;QAC1E,uFAAuF;QACvF,qFAAqF;QACrF,wFAAwF;QACxF,yFAAyF;QACzF;YACE,IAAI,EAAE,mBAAmB;YACzB,KAAK,EAAE,GAAG;YACV,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CACzB,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,0BAA0B,CAAC,IAAI,CAAC,SAAS,CAAC;YAC/E,SAAS,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE;gBACxE,MAAM,cAAc,GAAG,MAAM,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;gBACjF,IAAI,CAAC,cAAc;oBAAE,OAAO,IAAI,CAAA;gBAChC,OAAO,CAAC,CAAC,mBAAmB,CAAC,uBAAuB,CAClD,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,cAAc,EACd,WAAW,EACX,MAAM,CACP,CAAA;YACH,CAAC;SACF;QACD,oFAAoF;QACpF,wFAAwF;QACxF,2FAA2F;QAC3F,yFAAyF;QACzF,4FAA4F;QAC5F;YACE,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,GAAG;YACV,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CACzB,IAAI,CAAC,SAAS,KAAK,OAAO,IAAI,IAAI,CAAC,YAAY,EAAE,MAAM,KAAK,WAAW;YACzE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;gBACrD,MAAM,QAAQ,GAAG,CAAC,CAAC,iBAAiB;qBACjC,gBAAgB,CAAC,kBAAkB,CAAC;oBACrC,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,CAA6B,CAAA;gBACxD,OAAO,CAAC,CAAC,sBAAsB,CAAC,cAAc,CAC5C,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,IAAI,CAAC,KAAK,CACX,CAAA;YACH,CAAC;SACF;QACD,yFAAyF;QACzF,uFAAuF;QACvF,yFAAyF;QACzF,qFAAqF;QACrF,sEAAsE;QACtE;YACE,IAAI,EAAE,WAAW;YACjB,KAAK,EAAE,GAAG;YACV,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,mBAAmB;YAClE,SAAS,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;gBAC3D,MAAM,MAAM,GAAG,CAAC,CAAC,iBAAiB;qBAC/B,gBAAgB,CAAC,gBAAgB,CAAC;oBACnC,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,CAAoC,CAAA;gBAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;gBACxE,OAAO,CAAC,CAAC,kBAAkB,CAAC,cAAc,CACxC,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAC1B,KAAK,CACN,CAAA;YACH,CAAC;SACF;QACD,mFAAmF;QACnF,uFAAuF;QACvF,mFAAmF;QACnF,0EAA0E;QAC1E;YACE,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,GAAG;YACV,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CACjC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS;YACjE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CACrD,CAAC,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC;SAC9E;QACD,oFAAoF;QACpF,qFAAqF;QACrF,sFAAsF;QACtF,yFAAyF;QACzF,yFAAyF;QACzF,qDAAqD;QACrD;YACE,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,GAAG;YACV,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CACjC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS;YAClE,SAAS,EAAE,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CACrD,CAAC,CAAC,eAAe,CAAC,kBAAkB,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC;SAC5E;KACF,CAAA;IACD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAA;AACvD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CACvC,CAAyB;IAEzB,MAAM,SAAS,GAA6B;QAC1C,iFAAiF;QACjF,mFAAmF;QACnF,oFAAoF;QACpF,mFAAmF;QACnF,4EAA4E;QAC5E;YACE,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,eAAe,KAAK,SAAS;YACzD,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;gBACzD,yEAAyE;gBACzE,mEAAmE;gBACnE,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,iBAAiB,CAAC,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE,CACpE,CAAC,CAAC,aAAa,CAAC,iBAAiB,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC,eAAe,CAAC,CACjF,CAAA;gBACD,iFAAiF;gBACjF,oFAAoF;gBACpF,2CAA2C;gBAC3C,IAAI,QAAQ,EAAE,CAAC;oBACb,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAA;oBACtB,4EAA4E;oBAC5E,wEAAwE;oBACxE,iFAAiF;oBACjF,IAAI,QAAQ,CAAC,UAAU;wBAAE,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;gBAC3C,CAAC;gBACD,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAA;YACrC,CAAC;SACF;QACD,sFAAsF;QACtF,qFAAqF;QACrF,0EAA0E;QAC1E,EAAE;QACF,kFAAkF;QAClF,qFAAqF;QACrF,yCAAyC;QACzC;YACE,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;gBACnD,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;oBAC1C,MAAM,CAAC,CAAC,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAA;gBACjF,CAAC;YACH,CAAC;SACF;QACD,kFAAkF;QAClF,uFAAuF;QACvF,sFAAsF;QACtF,gFAAgF;QAChF;YACE,IAAI,EAAE,6BAA6B;YACnC,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;gBACnD,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAA;gBACrC,IAAI,CAAC,CAAC,CAAC,iBAAiB,IAAI,CAAC,OAAO;oBAAE,OAAM;gBAC5C,MAAM,CAAC,CAAC,iBAAiB,CAAC,cAAc,CAAC,WAAW,EAAE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAClF,CAAC;SACF;QACD,+EAA+E;QAC/E,gFAAgF;QAChF,6EAA6E;QAC7E,gFAAgF;QAChF,iFAAiF;QACjF,+EAA+E;QAC/E;YACE,IAAI,EAAE,6BAA6B;YACnC,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;gBACnD,IAAI,CAAC,CAAC,CAAC,iBAAiB;oBAAE,OAAM;gBAChC,IAAI,MAAM,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;oBACxC,MAAM,IAAI,KAAK,CACb,0EAA0E,CAC3E,CAAA;gBACH,CAAC;gBACD,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,iBAAiB,CAAC,UAAU,CACnD,WAAW,EACX,QAAQ,CAAC,OAAO,EAChB,MAAM,CAAC,cAAc,CACtB,CAAA;gBACD,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAA;gBACvF,CAAC;YACH,CAAC;SACF;QACD,oFAAoF;QACpF,sFAAsF;QACtF,qFAAqF;QACrF,qFAAqF;QACrF,iFAAiF;QACjF,uBAAuB;QACvB;YACE,IAAI,EAAE,sBAAsB;YAC5B,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;gBAC/C,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;oBAAE,MAAM,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;gBAC3E,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC,eAAe,KAAK,IAAI,CAAA;YACxD,CAAC;SACF;QACD,iFAAiF;QACjF,sFAAsF;QACtF,wFAAwF;QACxF,uFAAuF;QACvF,sFAAsF;QACtF,mFAAmF;QACnF,qDAAqD;QACrD,sFAAsF;QACtF,uFAAuF;QACvF,qFAAqF;QACrF,mFAAmF;QACnF,yFAAyF;QACzF,oFAAoF;QACpF;YACE,IAAI,EAAE,2BAA2B;YACjC,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS;YAChD,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;gBAC5B,MAAM,MAAM,GAAG,yBAAyB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;gBACvD,IAAI,MAAM;oBAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;YACvC,CAAC;SACF;QACD,iFAAiF;QACjF,mFAAmF;QACnF,uFAAuF;QACvF,uFAAuF;QACvF,0FAA0F;QAC1F,kFAAkF;QAClF,iBAAiB;QACjB;YACE,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS;YAChD,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;gBAC5B,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;gBAC/C,IAAI,MAAM;oBAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;YACvC,CAAC;SACF;QACD;YACE,IAAI,EAAE,yBAAyB;YAC/B,KAAK,EAAE,iBAAiB;YACxB,OAAO,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;gBACzD,MAAM,QAAQ,GAAG,kBAAkB,CACjC,IAAI,CAAC,MAAM,IAAI,EAAE,EACjB,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,EAClC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CACd,CAAA;gBACD,IAAI,QAAQ,EAAE,CAAC;oBACb,MAAM,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAA;oBAC3E,OAAO,EAAE,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAA;gBAChD,CAAC;YACH,CAAC;SACF;KACF,CAAA;IACD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACtD,8EAA8E;IAC9E,uFAAuF;IACvF,oFAAoF;IACpF,iBAAiB;IACjB,MAAM,GAAG,GAAoB,EAAE,iBAAiB,EAAE,CAAC,CAAC,iBAAiB,EAAE,CAAA;IACvE,KAAK,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,oBAAoB,CAAC,SAAS,EAAE;QAAE,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IAC/F,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -123,6 +123,13 @@ export declare class RequirementReviewService extends IterativeReviewService<Req
123
123
  }): Promise<{
124
124
  produced: number;
125
125
  }>;
126
+ /**
127
+ * Apply one Writer suggestion to its live placeholder against a fresh read, mutating `review` in
128
+ * place. Extracted from the chunk-application loop so that loop stays within the max-depth ceiling
129
+ * (the auto-accept/answered branch nested three levels below the loop). Returns whether the target
130
+ * produced a recommendation and whether it became a `ready` card the human must review.
131
+ */
132
+ private applyRecommendationToTarget;
126
133
  /**
127
134
  * Drop every `pending` placeholder on a review and reopen its source finding (the same cleanup
128
135
  * the per-finding failure path does, applied to the whole batch). Used when the Writer can't run
@@ -1 +1 @@
1
- {"version":3,"file":"RequirementReviewService.d.ts","sourceRoot":"","sources":["../../../src/modules/requirements/RequirementReviewService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RequirementReviewService.d.ts","sourceRoot":"","sources":["../../../src/modules/requirements/RequirementReviewService.ts"],"names":[],"mappings":"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"}
@@ -237,46 +237,12 @@ export class RequirementReviewService extends IterativeReviewService {
237
237
  // Re-read fresh before applying: the human may have acted during the Writer call.
238
238
  const review = assertFound(await this.repository.get(workspaceId, reviewId), this.entityName, reviewId);
239
239
  const now = this.deps.clock.now();
240
- for (const { ph, finding } of targets) {
241
- const rec = review.recommendations.find((r) => r.id === ph.id);
242
- if (!rec || rec.status !== 'pending')
243
- continue;
244
- const suggestion = finding ? suggestions.get(finding.id) : undefined;
245
- if (suggestion) {
246
- const standard = suggestion.fromStandard
247
- ? fragmentById.get(suggestion.fromStandard)
248
- : undefined;
249
- rec.recommendedText = suggestion.recommendation;
250
- rec.groundedInFragment = standard ? { id: standard.id, title: standard.title } : null;
251
- rec.updatedAt = now;
252
- if (rec.auto) {
253
- // Auto-recommendation: accept it immediately as the finding's default answer, so
254
- // the human sees it pre-filled (editable / dismissable) rather than a card to act
255
- // on. Mirrors `acceptRecommendation`, matching the finding against the fresh read.
256
- rec.status = 'accepted';
257
- const item = findSourceItem(review.items, ph.sourceFinding);
258
- if (item) {
259
- item.reply = suggestion.recommendation;
260
- item.status = 'answered';
261
- item.updatedAt = now;
262
- }
263
- }
264
- else {
265
- rec.status = 'ready';
266
- readyForReview += 1;
267
- }
240
+ for (const target of targets) {
241
+ const outcome = this.applyRecommendationToTarget(target, review, suggestions, fragmentById, now);
242
+ if (outcome.produced)
268
243
  produced += 1;
269
- }
270
- else {
271
- // The Writer failed for (or no longer matches) this finding: drop the dead placeholder
272
- // and reopen its finding so the human can answer it by hand.
273
- review.recommendations = review.recommendations.filter((r) => r.id !== ph.id);
274
- const item = findSourceItem(review.items, ph.sourceFinding);
275
- if (item && item.status === 'recommend_requested') {
276
- item.status = 'open';
277
- item.updatedAt = now;
278
- }
279
- }
244
+ if (outcome.readyForReview)
245
+ readyForReview += 1;
280
246
  }
281
247
  review.updatedAt = now;
282
248
  await this.repository.upsert(workspaceId, review);
@@ -287,6 +253,49 @@ export class RequirementReviewService extends IterativeReviewService {
287
253
  await this.notifyRecommendationsReady(workspaceId, block, readyForReview);
288
254
  return { produced };
289
255
  }
256
+ /**
257
+ * Apply one Writer suggestion to its live placeholder against a fresh read, mutating `review` in
258
+ * place. Extracted from the chunk-application loop so that loop stays within the max-depth ceiling
259
+ * (the auto-accept/answered branch nested three levels below the loop). Returns whether the target
260
+ * produced a recommendation and whether it became a `ready` card the human must review.
261
+ */
262
+ applyRecommendationToTarget(target, review, suggestions, fragmentById, now) {
263
+ const { ph, finding } = target;
264
+ const rec = review.recommendations.find((r) => r.id === ph.id);
265
+ if (!rec || rec.status !== 'pending')
266
+ return { produced: false, readyForReview: false };
267
+ const suggestion = finding ? suggestions.get(finding.id) : undefined;
268
+ if (!suggestion) {
269
+ // The Writer failed for (or no longer matches) this finding: drop the dead placeholder
270
+ // and reopen its finding so the human can answer it by hand.
271
+ review.recommendations = review.recommendations.filter((r) => r.id !== ph.id);
272
+ const item = findSourceItem(review.items, ph.sourceFinding);
273
+ if (item && item.status === 'recommend_requested') {
274
+ item.status = 'open';
275
+ item.updatedAt = now;
276
+ }
277
+ return { produced: false, readyForReview: false };
278
+ }
279
+ const standard = suggestion.fromStandard ? fragmentById.get(suggestion.fromStandard) : undefined;
280
+ rec.recommendedText = suggestion.recommendation;
281
+ rec.groundedInFragment = standard ? { id: standard.id, title: standard.title } : null;
282
+ rec.updatedAt = now;
283
+ if (!rec.auto) {
284
+ rec.status = 'ready';
285
+ return { produced: true, readyForReview: true };
286
+ }
287
+ // Auto-recommendation: accept it immediately as the finding's default answer, so the human
288
+ // sees it pre-filled (editable / dismissable) rather than a card to act on. Mirrors
289
+ // `acceptRecommendation`, matching the finding against the fresh read.
290
+ rec.status = 'accepted';
291
+ const item = findSourceItem(review.items, ph.sourceFinding);
292
+ if (item) {
293
+ item.reply = suggestion.recommendation;
294
+ item.status = 'answered';
295
+ item.updatedAt = now;
296
+ }
297
+ return { produced: true, readyForReview: false };
298
+ }
290
299
  /**
291
300
  * Drop every `pending` placeholder on a review and reopen its source finding (the same cleanup
292
301
  * the per-finding failure path does, applied to the whole batch). Used when the Writer can't run