stageflow 0.9.0 → 0.13.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 (167) hide show
  1. package/README.md +6 -1
  2. package/dist/agent/activity.d.ts +9 -0
  3. package/dist/agent/agentBackend.d.ts +32 -0
  4. package/dist/agent/agentBackend.js +38 -0
  5. package/dist/agent/claudeActivity.d.ts +15 -0
  6. package/dist/agent/claudeActivity.js +58 -0
  7. package/dist/agent/claudeAdapter.d.ts +5 -0
  8. package/dist/agent/claudeAdapter.js +331 -0
  9. package/dist/agent/claudeSession.d.ts +18 -0
  10. package/dist/agent/claudeSession.js +54 -0
  11. package/dist/agent/claudeTools.d.ts +89 -0
  12. package/dist/agent/claudeTools.js +135 -0
  13. package/dist/agent/fakeAgent.js +24 -4
  14. package/dist/agent/piAdapter.d.ts +12 -7
  15. package/dist/agent/piAdapter.js +194 -44
  16. package/dist/agent/piIsolatedMcp.d.ts +23 -0
  17. package/dist/agent/piIsolatedMcp.js +223 -0
  18. package/dist/agent/piIsolatedMcpProbe.d.ts +15 -0
  19. package/dist/agent/piIsolatedMcpProbe.js +292 -0
  20. package/dist/agent/port.d.ts +52 -1
  21. package/dist/agent/port.js +1 -1
  22. package/dist/agent/resolveAgentPort.d.ts +26 -0
  23. package/dist/agent/resolveAgentPort.js +39 -0
  24. package/dist/cli/runCommand.js +2 -2
  25. package/dist/cli/runsCommand.d.ts +1 -1
  26. package/dist/cli/runsCommand.js +184 -2
  27. package/dist/cli.d.ts +1 -1
  28. package/dist/cli.js +11 -5
  29. package/dist/config/browseCatalog.js +1 -1
  30. package/dist/config/createPipeline.d.ts +2 -1
  31. package/dist/config/createPipeline.js +34 -9
  32. package/dist/config/createStage.js +14 -9
  33. package/dist/config/loadPipeline.js +11 -1
  34. package/dist/config/loadStage.d.ts +1 -0
  35. package/dist/config/loadStage.js +207 -2
  36. package/dist/config/loadStageflowManifest.js +14 -1
  37. package/dist/config/mergePipelineIncludes.d.ts +1 -0
  38. package/dist/config/mergePipelineIncludes.js +14 -1
  39. package/dist/config/normalizePipelineStageEntry.d.ts +7 -2
  40. package/dist/config/normalizePipelineStageEntry.js +56 -7
  41. package/dist/config/parseCompletionContract.d.ts +8 -0
  42. package/dist/config/parseCompletionContract.js +218 -0
  43. package/dist/config/parsePreEmitChecks.d.ts +3 -0
  44. package/dist/config/parsePreEmitChecks.js +65 -0
  45. package/dist/config/pipelineNeeds.d.ts +19 -0
  46. package/dist/config/pipelineNeeds.js +151 -0
  47. package/dist/config/pipelineStageKeys.js +17 -1
  48. package/dist/config/resolveForkEmitContext.d.ts +6 -2
  49. package/dist/config/resolveForkEmitContext.js +9 -2
  50. package/dist/config/resolvePipelineDag.d.ts +2 -1
  51. package/dist/config/resolvePipelineDag.js +182 -39
  52. package/dist/config/resolveStageMcpServers.d.ts +35 -0
  53. package/dist/config/resolveStageMcpServers.js +207 -0
  54. package/dist/config/validateCatalog.d.ts +1 -1
  55. package/dist/config/validateCatalog.js +51 -0
  56. package/dist/config/validateCompletionContract.d.ts +4 -0
  57. package/dist/config/validateCompletionContract.js +52 -0
  58. package/dist/envelope/check.d.ts +1 -1
  59. package/dist/envelope/check.js +86 -13
  60. package/dist/envelope/cloneForks.d.ts +1 -1
  61. package/dist/envelope/cloneForks.js +24 -9
  62. package/dist/envelope/feedbackLoop.d.ts +4 -0
  63. package/dist/envelope/feedbackLoop.js +31 -0
  64. package/dist/envelope/payloadSchema.d.ts +7 -1
  65. package/dist/envelope/payloadSchema.js +170 -13
  66. package/dist/envelope/preEmitChecks.d.ts +29 -0
  67. package/dist/envelope/preEmitChecks.js +57 -0
  68. package/dist/hitl/qaTrail.d.ts +25 -0
  69. package/dist/hitl/qaTrail.js +51 -0
  70. package/dist/mcp/catalogTools.js +4 -2
  71. package/dist/mcp/controlTools.js +72 -0
  72. package/dist/mcp/readArtifact.d.ts +2 -0
  73. package/dist/mcp/readArtifact.js +25 -1
  74. package/dist/mcp/waitingGates.d.ts +2 -1
  75. package/dist/mcp/waitingGates.js +17 -1
  76. package/dist/package-meta.d.ts +1 -1
  77. package/dist/package-meta.js +1 -1
  78. package/dist/projection/projectRun.d.ts +4 -1
  79. package/dist/projection/projectRun.js +7 -0
  80. package/dist/prompt/feedbackLoopContext.d.ts +2 -0
  81. package/dist/prompt/feedbackLoopContext.js +17 -0
  82. package/dist/prompt/priorEnvelope.d.ts +2 -2
  83. package/dist/prompt/priorEnvelope.js +4 -1
  84. package/dist/runstore/pipelineDagSnapshot.d.ts +1 -0
  85. package/dist/runstore/pipelineDagSnapshot.js +51 -11
  86. package/dist/runstore/port.d.ts +185 -3
  87. package/dist/runstore/port.js +42 -3
  88. package/dist/runstore/runProjection.d.ts +7 -3
  89. package/dist/runstore/runProjection.js +30 -15
  90. package/dist/runstore/sqlite/SqliteRunStore.d.ts +25 -1
  91. package/dist/runstore/sqlite/SqliteRunStore.js +626 -8
  92. package/dist/runstore/sqlite/schema.d.ts +1 -1
  93. package/dist/runstore/sqlite/schema.js +99 -0
  94. package/dist/runstore/stageInstanceId.d.ts +1 -1
  95. package/dist/runstore/stageInstanceId.js +3 -2
  96. package/dist/runstore/trackProjection.js +59 -27
  97. package/dist/runstore/verificationHistory.d.ts +19 -0
  98. package/dist/runstore/verificationHistory.js +30 -0
  99. package/dist/runtime/cloneSchedule.d.ts +40 -4
  100. package/dist/runtime/cloneSchedule.js +212 -28
  101. package/dist/runtime/completionCheckRunner.d.ts +137 -0
  102. package/dist/runtime/completionCheckRunner.js +365 -0
  103. package/dist/runtime/completionCheckoutBaseline.d.ts +3 -0
  104. package/dist/runtime/completionCheckoutBaseline.js +44 -0
  105. package/dist/runtime/envelopeRouting.d.ts +10 -3
  106. package/dist/runtime/envelopeRouting.js +146 -5
  107. package/dist/runtime/feedbackLoopCoordinator.d.ts +74 -0
  108. package/dist/runtime/feedbackLoopCoordinator.js +446 -0
  109. package/dist/runtime/feedbackLoopDecision.d.ts +44 -0
  110. package/dist/runtime/feedbackLoopDecision.js +207 -0
  111. package/dist/runtime/feedbackLoopRoute.d.ts +5 -0
  112. package/dist/runtime/feedbackLoopRoute.js +13 -0
  113. package/dist/runtime/feedbackLoopSchedule.d.ts +38 -0
  114. package/dist/runtime/feedbackLoopSchedule.js +72 -0
  115. package/dist/runtime/forkGeneration.d.ts +72 -0
  116. package/dist/runtime/forkGeneration.js +295 -0
  117. package/dist/runtime/gitCheckoutCapability.d.ts +7 -0
  118. package/dist/runtime/gitCheckoutCapability.js +97 -0
  119. package/dist/runtime/manualRecoveryState.d.ts +40 -0
  120. package/dist/runtime/manualRecoveryState.js +73 -0
  121. package/dist/runtime/pipelineScheduler.d.ts +13 -1
  122. package/dist/runtime/pipelineScheduler.js +328 -45
  123. package/dist/runtime/replayLifecycle.d.ts +107 -0
  124. package/dist/runtime/replayLifecycle.js +168 -0
  125. package/dist/runtime/runManager.d.ts +54 -1
  126. package/dist/runtime/runManager.js +471 -6
  127. package/dist/runtime/runRetryCoordinator.d.ts +6 -1
  128. package/dist/runtime/runRetryCoordinator.js +43 -9
  129. package/dist/runtime/stageAttemptBootstrap.d.ts +3 -1
  130. package/dist/runtime/stageAttemptBootstrap.js +117 -3
  131. package/dist/runtime/stageProcessLauncher.d.ts +1 -1
  132. package/dist/runtime/stageRecovery.js +1 -1
  133. package/dist/runtime/stageRunner.d.ts +4 -1
  134. package/dist/runtime/stageRunner.js +29 -3
  135. package/dist/runtime/stageWorker.js +69 -2
  136. package/dist/runtime/stageWorkerProtocol.d.ts +1 -1
  137. package/dist/runtime/verifiedStageExecution.d.ts +31 -0
  138. package/dist/runtime/verifiedStageExecution.js +127 -0
  139. package/dist/server/bootstrap.js +1 -0
  140. package/dist/server/http.js +137 -4
  141. package/dist/server/operatorResults.js +2 -0
  142. package/dist/server/projectMcpRoutes.d.ts +6 -0
  143. package/dist/server/projectMcpRoutes.js +38 -0
  144. package/dist/tools/askOperator.d.ts +161 -5
  145. package/dist/tools/askOperator.js +58 -25
  146. package/dist/tools/emitStageEnvelope.d.ts +12 -12
  147. package/dist/tools/emitStageEnvelope.js +112 -24
  148. package/dist/types/completion.d.ts +43 -0
  149. package/dist/types/completion.js +1 -0
  150. package/dist/types/envelope.d.ts +18 -0
  151. package/dist/types/forkChoice.d.ts +9 -4
  152. package/dist/types/forkChoice.js +1 -1
  153. package/dist/types/pipeline.d.ts +41 -2
  154. package/dist/types/preEmitCheck.d.ts +18 -0
  155. package/dist/types/preEmitCheck.js +1 -0
  156. package/dist/types/stage.d.ts +14 -0
  157. package/dist/types/stageflowManifest.d.ts +2 -0
  158. package/dist/ui/assets/index-Yzh42vhf.js +118 -0
  159. package/dist/ui/assets/{index-C3N7MXAC.css → index-uVHiae8s.css} +1 -1
  160. package/dist/ui/index.html +2 -2
  161. package/package.json +5 -1
  162. package/skills/stageflow-author/SKILL.md +20 -2
  163. package/skills/stageflow-author/references/catalog-mapping.md +88 -3
  164. package/skills/stageflow-author/references/catalog-write-conventions.md +1 -1
  165. package/skills/stageflow-author/references/stage-prompt-template.md +63 -4
  166. package/skills/stageflow-session-capture/references/catalog-authoring.md +4 -1
  167. package/dist/ui/assets/index-CSoGDA3A.js +0 -118
@@ -1,12 +1,14 @@
1
1
  import type { StageLogLine } from "../agent/activity.js";
2
2
  import type { AskOperatorPrompt } from "../tools/askOperator.js";
3
3
  import type { StageEnvelope } from "../types/envelope.js";
4
- import type { ResolvedPipelineDag } from "../types/pipeline.js";
4
+ import type { FeedbackLoopConfig, ResolvedPipelineDag } from "../types/pipeline.js";
5
+ import type { CompletionCheck } from "../types/completion.js";
5
6
  import type { StageGateKind } from "../types/stage.js";
6
7
  export type RunStatus = "created" | "running" | "succeeded" | "failed";
7
8
  export type RunPipelineDagSnapshot = ResolvedPipelineDag & {
8
9
  stage_ids: string[];
9
10
  gate_kinds?: Record<string, StageGateKind[]>;
11
+ clone_input_schema?: Record<string, unknown>;
10
12
  };
11
13
  export type StageReadiness = "blocked" | "ready" | "running" | "waiting" | "succeeded" | "failed" | "skipped";
12
14
  export type PipelineTrackNode = {
@@ -19,6 +21,9 @@ export type PipelineTrackNode = {
19
21
  gate_kinds?: StageGateKind[];
20
22
  attempt_count?: number;
21
23
  definition_id?: string;
24
+ feedback_loop?: {
25
+ target: string;
26
+ };
22
27
  };
23
28
  export type PipelineTrackEdge = {
24
29
  from: string;
@@ -70,16 +75,145 @@ export type StageExecution = {
70
75
  stage_id: string;
71
76
  attempt: number;
72
77
  status: StageSnapshot["status"];
78
+ verification_outcome: VerificationOutcome;
73
79
  started_at?: string;
74
80
  finished_at?: string;
75
81
  envelope: StageEnvelope | null;
76
82
  };
77
83
  export type StageExecutionPatch = {
78
84
  status?: StageSnapshot["status"];
85
+ verification_outcome?: VerificationOutcome;
79
86
  started_at?: string;
80
87
  finished_at?: string;
81
88
  envelope?: StageEnvelope | null;
82
89
  };
90
+ /** The durable disposition of completion verification for one stage attempt. */
91
+ export type VerificationOutcome = "not_run" | "passed" | "failed" | "error";
92
+ /** The lifecycle state of one independently-run completion check. */
93
+ export type VerificationCheckStatus = "pending" | "running" | "passed" | "failed" | "skipped";
94
+ /**
95
+ * Durable evidence for one check in one stage execution attempt.
96
+ *
97
+ * `evidence` is intentionally JSON-shaped: each checker owns its evidence
98
+ * schema (for example, command output metadata or an artifact digest), while
99
+ * the run store preserves it without privileging a particular checker.
100
+ */
101
+ export type VerificationCheckResult = {
102
+ run_id: string;
103
+ stage_id: string;
104
+ attempt: number;
105
+ check_id: string;
106
+ check_type: CompletionCheck["type"];
107
+ status: VerificationCheckStatus;
108
+ started_at?: string;
109
+ finished_at?: string;
110
+ evidence?: Record<string, unknown>;
111
+ };
112
+ /** Input for creating or updating a verification-check lifecycle record. */
113
+ export type VerificationCheckResultPatch = {
114
+ check_id: string;
115
+ check_type: CompletionCheck["type"];
116
+ status: VerificationCheckStatus;
117
+ started_at?: string;
118
+ finished_at?: string;
119
+ evidence?: Record<string, unknown>;
120
+ };
121
+ /** Durable lifecycle for one source-owned feedback-loop policy in a run. */
122
+ export type FeedbackLoopState = "active" | "waiting_for_human" | "continued" | "abandoned" | "completed";
123
+ /** Durable lifecycle for one accepted send-back through a feedback loop. */
124
+ export type FeedbackReplayStatus = "scheduled" | "active" | "waiting_for_human" | "completed" | "failed" | "superseded";
125
+ /** Lifecycle for one persistent stage's pass within a feedback replay. */
126
+ export type FeedbackReplayStagePassStatus = "pending" | "running" | "waiting" | "succeeded" | "failed" | "superseded";
127
+ /** Active/history state for a dynamic clone cohort created by a fork parent. */
128
+ export type ForkGenerationStatus = "active" | "completed" | "superseded";
129
+ /** Deferred over-limit send_back awaiting a human feedback-loop decision. */
130
+ export type DeferredFeedbackSendBack = {
131
+ target: string;
132
+ feedback_envelope: StageEnvelope;
133
+ source_attempt: number;
134
+ };
135
+ export type FeedbackLoopRecord = {
136
+ run_id: string;
137
+ loop_id: string;
138
+ source_stage_id: string;
139
+ source_attempt: number;
140
+ policy: FeedbackLoopConfig;
141
+ state: FeedbackLoopState;
142
+ current_replay_id?: string;
143
+ current_replay_number?: number;
144
+ deferred_send_back?: DeferredFeedbackSendBack;
145
+ created_at: string;
146
+ updated_at: string;
147
+ };
148
+ export type FeedbackReplayRecord = {
149
+ run_id: string;
150
+ replay_id: string;
151
+ loop_id: string;
152
+ source_stage_id: string;
153
+ source_attempt: number;
154
+ target_stage_id: string;
155
+ /** One-based accepted send-back count for this loop. */
156
+ replay_number: number;
157
+ max_replays: number;
158
+ replay_session: FeedbackLoopConfig["replay_session"];
159
+ route_stage_ids: string[];
160
+ feedback_envelope: StageEnvelope;
161
+ status: FeedbackReplayStatus;
162
+ created_at: string;
163
+ updated_at: string;
164
+ };
165
+ export type FeedbackReplayStagePassRecord = {
166
+ run_id: string;
167
+ replay_id: string;
168
+ stage_id: string;
169
+ /** Execution attempt whose session this pass resumes or newly creates. */
170
+ stage_attempt: number;
171
+ /** Immutable attempt whose Pi session resume targets; never rebound. */
172
+ session_origin_attempt?: number;
173
+ session_mode: FeedbackLoopConfig["replay_session"];
174
+ status: FeedbackReplayStagePassStatus;
175
+ started_at?: string;
176
+ finished_at?: string;
177
+ emitted_envelope?: StageEnvelope;
178
+ };
179
+ export type ForkGenerationRecord = {
180
+ run_id: string;
181
+ generation_id: string;
182
+ /** Undefined for an initial fan-out that is not part of a replay. */
183
+ replay_id?: string;
184
+ fork_parent_stage_id: string;
185
+ generation_number: number;
186
+ clone_stage_ids: string[];
187
+ status: ForkGenerationStatus;
188
+ created_at: string;
189
+ updated_at: string;
190
+ };
191
+ export type CreateFeedbackLoopInput = Omit<FeedbackLoopRecord, "run_id" | "state" | "current_replay_id" | "current_replay_number" | "created_at" | "updated_at"> & {
192
+ state?: FeedbackLoopState;
193
+ };
194
+ export type FeedbackLoopPatch = Partial<Pick<FeedbackLoopRecord, "state" | "current_replay_id" | "current_replay_number" | "policy">> & {
195
+ /** Set to clear a previously deferred send_back. */
196
+ deferred_send_back?: DeferredFeedbackSendBack | null;
197
+ };
198
+ export type CreateFeedbackReplayInput = Omit<FeedbackReplayRecord, "run_id" | "status" | "created_at" | "updated_at"> & {
199
+ status?: FeedbackReplayStatus;
200
+ };
201
+ export type FeedbackReplayPatch = Partial<Pick<FeedbackReplayRecord, "status">>;
202
+ export type CreateFeedbackReplayStagePassInput = Omit<FeedbackReplayStagePassRecord, "run_id" | "status" | "started_at" | "finished_at" | "emitted_envelope"> & {
203
+ status?: FeedbackReplayStagePassStatus;
204
+ started_at?: string;
205
+ finished_at?: string;
206
+ emitted_envelope?: StageEnvelope;
207
+ };
208
+ export type FeedbackReplayStagePassPatch = Partial<Pick<FeedbackReplayStagePassRecord, "status" | "stage_attempt">> & {
209
+ started_at?: string | null;
210
+ finished_at?: string | null;
211
+ emitted_envelope?: StageEnvelope | null;
212
+ };
213
+ export type CreateForkGenerationInput = Omit<ForkGenerationRecord, "run_id" | "status" | "created_at" | "updated_at"> & {
214
+ status?: ForkGenerationStatus;
215
+ };
216
+ export type ForkGenerationPatch = Partial<Pick<ForkGenerationRecord, "status">>;
83
217
  export type CompactStage = {
84
218
  id: string;
85
219
  status: StageSnapshot["status"];
@@ -103,17 +237,31 @@ export type RunSummary = {
103
237
  waiting_stage_ids?: string[];
104
238
  /** Short prompt text for Today triage; omitted when not waiting. */
105
239
  waiting_summary?: string;
106
- waiting_kind?: AskOperatorPrompt["kind"];
240
+ waiting_kind?: AskOperatorPrompt["kind"] | "feedback_loop_decision";
107
241
  waiting_prompt_id?: string;
108
242
  waiting_artifacts?: string[];
109
243
  waiting_questions?: string[];
110
244
  failed_stage_id?: string;
111
245
  failed_reason?: string;
246
+ /** The active loop, when a run is currently replaying or awaiting a decision. */
247
+ active_feedback_loop?: FeedbackLoopRecord;
112
248
  };
113
249
  export type RunDetail = Omit<RunSummary, "stages"> & {
114
250
  task_yaml: string;
115
251
  stages: StageSnapshot[];
116
252
  pipeline_track: PipelineTrackProjection;
253
+ /** Append-only feedback-loop and replay history, ordered by creation. */
254
+ feedback_loops: FeedbackLoopHistory[];
255
+ };
256
+ export type FeedbackLoopHistory = {
257
+ loop: FeedbackLoopRecord;
258
+ replays: Array<{
259
+ replay: FeedbackReplayRecord;
260
+ stage_passes: FeedbackReplayStagePassRecord[];
261
+ fork_generations: ForkGenerationRecord[];
262
+ }>;
263
+ /** Fork cohorts not associated with a replay, for example the first pass. */
264
+ fork_generations: ForkGenerationRecord[];
117
265
  };
118
266
  export type CreateRunInput = {
119
267
  pipelineId: string;
@@ -156,6 +304,15 @@ export interface RunStore {
156
304
  countStageAttempts(runId: string, stageId: string): Promise<number>;
157
305
  getStageExecution(runId: string, stageId: string, attempt: number): Promise<StageExecution>;
158
306
  updateStageExecution(runId: string, stageId: string, attempt: number, patch: StageExecutionPatch): Promise<void>;
307
+ /** Create or update evidence for one check in a stage execution attempt. */
308
+ upsertVerificationCheckResult(runId: string, stageId: string, result: VerificationCheckResultPatch, options?: {
309
+ attempt?: number;
310
+ }): Promise<void>;
311
+ /**
312
+ * List verification evidence in write order. Without an attempt, returns all
313
+ * attempts for the stage; callers may pass an attempt to inspect one retry.
314
+ */
315
+ listVerificationCheckResults(runId: string, stageId: string, attempt?: number): Promise<VerificationCheckResult[]>;
159
316
  writeEnvelope(runId: string, stageId: string, envelope: StageEnvelope, options?: {
160
317
  attempt?: number;
161
318
  }): Promise<void>;
@@ -167,6 +324,31 @@ export interface RunStore {
167
324
  listRuns(filter?: ListRunsFilter): Promise<RunSummary[]>;
168
325
  readRun(runId: string): Promise<RunDetail>;
169
326
  updatePipelineDag(runId: string, dag: RunPipelineDagSnapshot): Promise<void>;
327
+ createFeedbackLoop(runId: string, input: CreateFeedbackLoopInput): Promise<FeedbackLoopRecord>;
328
+ getFeedbackLoop(runId: string, loopId: string): Promise<FeedbackLoopRecord>;
329
+ listFeedbackLoops(runId: string): Promise<FeedbackLoopRecord[]>;
330
+ /**
331
+ * Patch a feedback loop. When `expectedState` is set, the update is conditional
332
+ * (CAS): returns false if the row exists but state does not match.
333
+ * Throws if the loop is missing.
334
+ */
335
+ updateFeedbackLoop(runId: string, loopId: string, patch: FeedbackLoopPatch, options?: {
336
+ expectedState?: FeedbackLoopState;
337
+ }): Promise<boolean>;
338
+ createFeedbackReplay(runId: string, input: CreateFeedbackReplayInput): Promise<FeedbackReplayRecord>;
339
+ getFeedbackReplay(runId: string, replayId: string): Promise<FeedbackReplayRecord>;
340
+ listFeedbackReplays(runId: string, loopId: string): Promise<FeedbackReplayRecord[]>;
341
+ updateFeedbackReplay(runId: string, replayId: string, patch: FeedbackReplayPatch): Promise<void>;
342
+ createFeedbackReplayStagePass(runId: string, input: CreateFeedbackReplayStagePassInput): Promise<FeedbackReplayStagePassRecord>;
343
+ listFeedbackReplayStagePasses(runId: string, replayId: string): Promise<FeedbackReplayStagePassRecord[]>;
344
+ updateFeedbackReplayStagePass(runId: string, replayId: string, stageId: string, patch: FeedbackReplayStagePassPatch): Promise<void>;
345
+ createForkGeneration(runId: string, input: CreateForkGenerationInput): Promise<ForkGenerationRecord>;
346
+ listForkGenerations(runId: string, options?: {
347
+ replayId?: string;
348
+ forkParentStageId?: string;
349
+ }): Promise<ForkGenerationRecord[]>;
350
+ updateForkGeneration(runId: string, generationId: string, patch: ForkGenerationPatch): Promise<void>;
170
351
  }
171
352
  export declare function stageStatusFromEvents(events: StageLogEvent[]): StageSnapshot["status"];
172
- export declare function deriveStatusFromStages(stages: StageSnapshot[]): RunStatus;
353
+ export declare function findUnhandledFailedStage(stages: StageSnapshot[], dag?: Pick<RunPipelineDagSnapshot, "nodes"> | null): StageSnapshot | undefined;
354
+ export declare function deriveStatusFromStages(stages: StageSnapshot[], dag?: Pick<RunPipelineDagSnapshot, "nodes"> | null): RunStatus;
@@ -1,3 +1,4 @@
1
+ import { predecessorEdges } from "../config/pipelineNeeds.js";
1
2
  export function stageStatusFromEvents(events) {
2
3
  let status = "pending";
3
4
  for (const ev of events) {
@@ -14,12 +15,50 @@ export function stageStatusFromEvents(events) {
14
15
  }
15
16
  return status;
16
17
  }
17
- export function deriveStatusFromStages(stages) {
18
+ function definitionIdForStage(dag, stageId, snapshotsById) {
19
+ const snap = snapshotsById.get(stageId);
20
+ if (snap?.definition_id)
21
+ return snap.definition_id;
22
+ const node = dag.nodes.find((n) => n.id === stageId);
23
+ return node?.definition_id ?? stageId;
24
+ }
25
+ function failureAcceptedByJoin(dag, failedId, snapshotsById) {
26
+ const defId = definitionIdForStage(dag, failedId, snapshotsById);
27
+ for (const node of dag.nodes) {
28
+ const edge = predecessorEdges(node).find((item) => item.id === failedId || item.id === defId);
29
+ if (!edge?.on.includes("failed"))
30
+ continue;
31
+ const join = snapshotsById.get(node.id);
32
+ if (join && join.status !== "skipped")
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+ export function findUnhandledFailedStage(stages, dag) {
38
+ const byId = new Map(stages.map((s) => [s.stage_id, s]));
39
+ return stages.find((stage) => {
40
+ if (stage.status !== "failed")
41
+ return false;
42
+ if (!dag)
43
+ return true;
44
+ return !failureAcceptedByJoin(dag, stage.stage_id, byId);
45
+ });
46
+ }
47
+ function stageResolvedForSuccess(stage, dag, byId) {
48
+ if (stage.status === "succeeded" || stage.status === "skipped")
49
+ return true;
50
+ if (stage.status === "failed" && dag) {
51
+ return failureAcceptedByJoin(dag, stage.stage_id, byId);
52
+ }
53
+ return false;
54
+ }
55
+ export function deriveStatusFromStages(stages, dag) {
18
56
  if (stages.length === 0)
19
57
  return "created";
20
- if (stages.some((s) => s.status === "failed"))
58
+ if (findUnhandledFailedStage(stages, dag))
21
59
  return "failed";
22
- if (stages.every((s) => s.status === "succeeded" || s.status === "skipped")) {
60
+ const byId = new Map(stages.map((s) => [s.stage_id, s]));
61
+ if (stages.every((s) => stageResolvedForSuccess(s, dag, byId))) {
23
62
  return "succeeded";
24
63
  }
25
64
  if (stages.some((s) => s.status === "running" ||
@@ -1,6 +1,10 @@
1
- import type { RunDetail, RunMeta, RunPipelineDagSnapshot, RunSummary, StageSnapshot } from "./port.js";
1
+ import type { FeedbackLoopHistory, FeedbackLoopRecord, RunDetail, RunMeta, RunPipelineDagSnapshot, RunSummary, StageSnapshot } from "./port.js";
2
2
  export { syntheticPendingSnapshot } from "./syntheticStageSnapshot.js";
3
+ export type FeedbackProjectionInput = {
4
+ feedback_loops?: FeedbackLoopHistory[];
5
+ active_feedback_loop?: FeedbackLoopRecord;
6
+ };
3
7
  export declare function overlayPlannedStages(stageIds: string[], snapshots: StageSnapshot[], dag?: Pick<RunPipelineDagSnapshot, "nodes"> | null): StageSnapshot[];
4
8
  export declare function orderStageSnapshots(stageIds: string[], snapshots: StageSnapshot[]): StageSnapshot[];
5
- export declare function projectRunSummary(meta: RunMeta, stages: StageSnapshot[]): RunSummary;
6
- export declare function projectRunDetail(meta: RunMeta, stages: StageSnapshot[], task_yaml: string, dagSnapshot?: RunPipelineDagSnapshot | null): RunDetail;
9
+ export declare function projectRunSummary(meta: RunMeta, stages: StageSnapshot[], feedback?: Pick<FeedbackProjectionInput, "active_feedback_loop">): RunSummary;
10
+ export declare function projectRunDetail(meta: RunMeta, stages: StageSnapshot[], task_yaml: string, dagSnapshot?: RunPipelineDagSnapshot | null, feedback?: FeedbackProjectionInput): RunDetail;
@@ -1,4 +1,4 @@
1
- import { deriveStatusFromStages } from "./port.js";
1
+ import { deriveStatusFromStages, findUnhandledFailedStage } from "./port.js";
2
2
  import { linearCompatDagSnapshot } from "./pipelineDagSnapshot.js";
3
3
  import { syntheticPendingSnapshot } from "./syntheticStageSnapshot.js";
4
4
  import { buildPipelineTrack } from "./trackProjection.js";
@@ -61,8 +61,8 @@ function waitingFieldsFromStages(stages) {
61
61
  }
62
62
  return fields;
63
63
  }
64
- function failedFieldsFromStages(stages) {
65
- const failed = stages.find((s) => s.status === "failed");
64
+ function failedFieldsFromStages(stages, dag) {
65
+ const failed = findUnhandledFailedStage(stages, dag);
66
66
  if (!failed)
67
67
  return {};
68
68
  let failed_reason;
@@ -75,10 +75,10 @@ function failedFieldsFromStages(stages) {
75
75
  ...(failed_reason !== undefined ? { failed_reason } : {}),
76
76
  };
77
77
  }
78
- function resolveListedStatus(stages, meta) {
78
+ function resolveListedStatus(stages, meta, dag) {
79
79
  if (stages.length === 0)
80
80
  return meta.status ?? "created";
81
- const derived = deriveStatusFromStages(stages);
81
+ const derived = deriveStatusFromStages(stages, dag);
82
82
  if (meta.status === "succeeded" && derived === "running") {
83
83
  const hasActiveStage = stages.some((s) => s.status === "running" || s.status === "waiting_for_input");
84
84
  if (!hasActiveStage)
@@ -86,7 +86,18 @@ function resolveListedStatus(stages, meta) {
86
86
  }
87
87
  return derived;
88
88
  }
89
- export function projectRunSummary(meta, stages) {
89
+ export function projectRunSummary(meta, stages, feedback) {
90
+ const dag = meta.pipeline_dag;
91
+ const waiting = waitingFieldsFromStages(stages);
92
+ if (feedback?.active_feedback_loop?.state === "waiting_for_human" &&
93
+ waiting.waiting_stage_id !== undefined &&
94
+ waiting.waiting_kind === undefined) {
95
+ waiting.waiting_kind = "feedback_loop_decision";
96
+ if (waiting.waiting_summary === undefined || waiting.waiting_summary === "Waiting for input") {
97
+ waiting.waiting_summary =
98
+ "Feedback loop limit reached — extend, continue, or abandon";
99
+ }
100
+ }
90
101
  return {
91
102
  run_id: meta.run_id,
92
103
  pipeline_id: meta.pipeline_id,
@@ -94,21 +105,24 @@ export function projectRunSummary(meta, stages) {
94
105
  ...(meta.pipeline_path !== undefined ? { pipeline_path: meta.pipeline_path } : {}),
95
106
  ...(meta.task_path !== undefined ? { task_path: meta.task_path } : {}),
96
107
  ...(meta.project_root !== undefined ? { project_root: meta.project_root } : {}),
97
- status: resolveListedStatus(stages, meta),
108
+ status: resolveListedStatus(stages, meta, dag),
98
109
  created_at: meta.created_at,
99
110
  updated_at: meta.updated_at,
100
111
  stages: compactStages(stages),
101
- ...waitingFieldsFromStages(stages),
102
- ...failedFieldsFromStages(stages),
112
+ ...waiting,
113
+ ...failedFieldsFromStages(stages, dag),
114
+ ...(feedback?.active_feedback_loop !== undefined
115
+ ? { active_feedback_loop: feedback.active_feedback_loop }
116
+ : {}),
103
117
  };
104
118
  }
105
- export function projectRunDetail(meta, stages, task_yaml, dagSnapshot) {
106
- const ordered = dagSnapshot
107
- ? overlayPlannedStages(dagSnapshot.stage_ids, stages, dagSnapshot)
119
+ export function projectRunDetail(meta, stages, task_yaml, dagSnapshot, feedback) {
120
+ const dag = dagSnapshot ?? meta.pipeline_dag ?? null;
121
+ const ordered = dag
122
+ ? overlayPlannedStages(dag.stage_ids, stages, dag)
108
123
  : stages;
109
- const summary = projectRunSummary(meta, ordered);
110
- const snapshot = dagSnapshot ??
111
- linearCompatDagSnapshot(ordered.map((s) => s.stage_id));
124
+ const summary = projectRunSummary(dag ? { ...meta, pipeline_dag: dag } : meta, ordered, { active_feedback_loop: feedback?.active_feedback_loop });
125
+ const snapshot = dag ?? linearCompatDagSnapshot(ordered.map((s) => s.stage_id));
112
126
  const pipeline_track = buildPipelineTrack({
113
127
  dagSnapshot: snapshot,
114
128
  stages: ordered,
@@ -119,5 +133,6 @@ export function projectRunDetail(meta, stages, task_yaml, dagSnapshot) {
119
133
  task_yaml,
120
134
  stages: ordered,
121
135
  pipeline_track,
136
+ feedback_loops: feedback?.feedback_loops ?? [],
122
137
  };
123
138
  }
@@ -1,6 +1,6 @@
1
1
  import type { StageEnvelope } from "../../types/envelope.js";
2
2
  import type { StageLogLine } from "../../agent/activity.js";
3
- import { type CreateRunInput, type CreatedRun, type ListRunsFilter, type RunDetail, type RunMeta, type RunPipelineDagSnapshot, type RunStatus, type RunStore, type RunSummary, type StageExecution, type StageExecutionPatch, type StageLogEvent } from "../port.js";
3
+ import { type CreateFeedbackLoopInput, type CreateFeedbackReplayInput, type CreateFeedbackReplayStagePassInput, type CreateForkGenerationInput, type CreateRunInput, type CreatedRun, type FeedbackLoopPatch, type FeedbackLoopRecord, type FeedbackLoopState, type FeedbackReplayPatch, type FeedbackReplayRecord, type FeedbackReplayStagePassPatch, type FeedbackReplayStagePassRecord, type ForkGenerationPatch, type ForkGenerationRecord, type ListRunsFilter, type RunDetail, type RunMeta, type RunPipelineDagSnapshot, type RunStatus, type RunStore, type RunSummary, type StageExecution, type StageExecutionPatch, type StageLogEvent, type VerificationCheckResult, type VerificationCheckResultPatch } from "../port.js";
4
4
  export declare class SqliteRunStore implements RunStore {
5
5
  private readonly storeRoot;
6
6
  private readonly db;
@@ -22,6 +22,10 @@ export declare class SqliteRunStore implements RunStore {
22
22
  countStageAttempts(runId: string, stageId: string): Promise<number>;
23
23
  getStageExecution(runId: string, stageId: string, attempt: number): Promise<StageExecution>;
24
24
  updateStageExecution(runId: string, stageId: string, attempt: number, patch: StageExecutionPatch): Promise<void>;
25
+ upsertVerificationCheckResult(runId: string, stageId: string, result: VerificationCheckResultPatch, options?: {
26
+ attempt?: number;
27
+ }): Promise<void>;
28
+ listVerificationCheckResults(runId: string, stageId: string, attempt?: number): Promise<VerificationCheckResult[]>;
25
29
  writeEnvelope(runId: string, stageId: string, envelope: StageEnvelope, options?: {
26
30
  attempt?: number;
27
31
  }): Promise<void>;
@@ -40,6 +44,26 @@ export declare class SqliteRunStore implements RunStore {
40
44
  listStageEvents(runId: string, stageId: string, attempt?: number): Promise<StageLogEvent[]>;
41
45
  listRuns(filter?: ListRunsFilter): Promise<RunSummary[]>;
42
46
  readRun(runId: string): Promise<RunDetail>;
47
+ createFeedbackLoop(runId: string, input: CreateFeedbackLoopInput): Promise<FeedbackLoopRecord>;
48
+ getFeedbackLoop(runId: string, loopId: string): Promise<FeedbackLoopRecord>;
49
+ listFeedbackLoops(runId: string): Promise<FeedbackLoopRecord[]>;
50
+ updateFeedbackLoop(runId: string, loopId: string, patch: FeedbackLoopPatch, options?: {
51
+ expectedState?: FeedbackLoopState;
52
+ }): Promise<boolean>;
53
+ createFeedbackReplay(runId: string, input: CreateFeedbackReplayInput): Promise<FeedbackReplayRecord>;
54
+ getFeedbackReplay(runId: string, replayId: string): Promise<FeedbackReplayRecord>;
55
+ listFeedbackReplays(runId: string, loopId: string): Promise<FeedbackReplayRecord[]>;
56
+ updateFeedbackReplay(runId: string, replayId: string, patch: FeedbackReplayPatch): Promise<void>;
57
+ createFeedbackReplayStagePass(runId: string, input: CreateFeedbackReplayStagePassInput): Promise<FeedbackReplayStagePassRecord>;
58
+ listFeedbackReplayStagePasses(runId: string, replayId: string): Promise<FeedbackReplayStagePassRecord[]>;
59
+ updateFeedbackReplayStagePass(runId: string, replayId: string, stageId: string, patch: FeedbackReplayStagePassPatch): Promise<void>;
60
+ createForkGeneration(runId: string, input: CreateForkGenerationInput): Promise<ForkGenerationRecord>;
61
+ listForkGenerations(runId: string, options?: {
62
+ replayId?: string;
63
+ forkParentStageId?: string;
64
+ }): Promise<ForkGenerationRecord[]>;
65
+ updateForkGeneration(runId: string, generationId: string, patch: ForkGenerationPatch): Promise<void>;
66
+ private loadFeedbackLoopHistory;
43
67
  private readPipelineDagSnapshotFromRow;
44
68
  private runMetaFromRow;
45
69
  private getRunRow;