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 +1 @@
1
- export declare const SCHEMA_SQL = "\nCREATE TABLE IF NOT EXISTS runs (\n run_id TEXT PRIMARY KEY,\n pipeline_id TEXT NOT NULL,\n task_id TEXT,\n task_yaml TEXT NOT NULL,\n status TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n checkout_root TEXT,\n pipeline_dag_json TEXT,\n git_sha TEXT,\n ci_pr_url TEXT,\n ci_job_url TEXT,\n pipeline_path TEXT,\n task_path TEXT,\n project_root TEXT\n);\n\nCREATE TABLE IF NOT EXISTS stages (\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n status TEXT,\n summary TEXT,\n envelope_json TEXT,\n started_at TEXT,\n finished_at TEXT,\n PRIMARY KEY (run_id, stage_id),\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE TABLE IF NOT EXISTS stage_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n at TEXT NOT NULL,\n event TEXT NOT NULL,\n payload_json TEXT,\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_runs_status_created\n ON runs (status, created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_runs_pipeline_created\n ON runs (pipeline_id, created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_stage_events_run_stage_at\n ON stage_events (run_id, stage_id, at);\n\nCREATE TABLE IF NOT EXISTS stage_executions (\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n attempt INTEGER NOT NULL,\n status TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT,\n envelope_json TEXT,\n PRIMARY KEY (run_id, stage_id, attempt),\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_stage_executions_run_stage\n ON stage_executions (run_id, stage_id, attempt);\n";
1
+ export declare const SCHEMA_SQL = "\nCREATE TABLE IF NOT EXISTS runs (\n run_id TEXT PRIMARY KEY,\n pipeline_id TEXT NOT NULL,\n task_id TEXT,\n task_yaml TEXT NOT NULL,\n status TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n checkout_root TEXT,\n pipeline_dag_json TEXT,\n git_sha TEXT,\n ci_pr_url TEXT,\n ci_job_url TEXT,\n pipeline_path TEXT,\n task_path TEXT,\n project_root TEXT\n);\n\nCREATE TABLE IF NOT EXISTS stages (\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n status TEXT,\n summary TEXT,\n envelope_json TEXT,\n started_at TEXT,\n finished_at TEXT,\n PRIMARY KEY (run_id, stage_id),\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE TABLE IF NOT EXISTS stage_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n at TEXT NOT NULL,\n event TEXT NOT NULL,\n payload_json TEXT,\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_runs_status_created\n ON runs (status, created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_runs_pipeline_created\n ON runs (pipeline_id, created_at DESC);\n\nCREATE INDEX IF NOT EXISTS idx_stage_events_run_stage_at\n ON stage_events (run_id, stage_id, at);\n\nCREATE TABLE IF NOT EXISTS stage_executions (\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n attempt INTEGER NOT NULL,\n status TEXT NOT NULL,\n verification_outcome TEXT NOT NULL DEFAULT 'not_run',\n started_at TEXT,\n finished_at TEXT,\n envelope_json TEXT,\n PRIMARY KEY (run_id, stage_id, attempt),\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_stage_executions_run_stage\n ON stage_executions (run_id, stage_id, attempt);\n\nCREATE TABLE IF NOT EXISTS verification_check_results (\n run_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n attempt INTEGER NOT NULL,\n check_id TEXT NOT NULL,\n check_type TEXT NOT NULL,\n status TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT,\n evidence_json TEXT,\n PRIMARY KEY (run_id, stage_id, attempt, check_id),\n FOREIGN KEY (run_id, stage_id, attempt)\n REFERENCES stage_executions(run_id, stage_id, attempt)\n);\n\nCREATE INDEX IF NOT EXISTS idx_verification_check_results_execution\n ON verification_check_results (run_id, stage_id, attempt, check_id);\n\nCREATE TABLE IF NOT EXISTS feedback_loops (\n run_id TEXT NOT NULL,\n loop_id TEXT NOT NULL,\n source_stage_id TEXT NOT NULL,\n source_attempt INTEGER NOT NULL,\n policy_json TEXT NOT NULL,\n state TEXT NOT NULL,\n current_replay_id TEXT,\n current_replay_number INTEGER,\n deferred_send_back_json TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n PRIMARY KEY (run_id, loop_id),\n FOREIGN KEY (run_id) REFERENCES runs(run_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_feedback_loops_run_state\n ON feedback_loops (run_id, state, created_at);\n\nCREATE TABLE IF NOT EXISTS feedback_replays (\n run_id TEXT NOT NULL,\n replay_id TEXT NOT NULL,\n loop_id TEXT NOT NULL,\n source_stage_id TEXT NOT NULL,\n source_attempt INTEGER NOT NULL,\n target_stage_id TEXT NOT NULL,\n replay_number INTEGER NOT NULL,\n max_replays INTEGER NOT NULL,\n replay_session TEXT NOT NULL,\n route_stage_ids_json TEXT NOT NULL,\n feedback_envelope_json TEXT NOT NULL,\n status TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n PRIMARY KEY (run_id, replay_id),\n FOREIGN KEY (run_id, loop_id) REFERENCES feedback_loops(run_id, loop_id)\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_feedback_replays_loop_number\n ON feedback_replays (run_id, loop_id, replay_number);\nCREATE INDEX IF NOT EXISTS idx_feedback_replays_run_loop\n ON feedback_replays (run_id, loop_id, created_at);\n\nCREATE TABLE IF NOT EXISTS feedback_replay_stage_passes (\n run_id TEXT NOT NULL,\n replay_id TEXT NOT NULL,\n stage_id TEXT NOT NULL,\n stage_attempt INTEGER NOT NULL,\n session_origin_attempt INTEGER,\n session_mode TEXT NOT NULL,\n status TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT,\n emitted_envelope_json TEXT,\n PRIMARY KEY (run_id, replay_id, stage_id),\n FOREIGN KEY (run_id, replay_id) REFERENCES feedback_replays(run_id, replay_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_feedback_replay_stage_passes_replay\n ON feedback_replay_stage_passes (run_id, replay_id, stage_id);\n\nCREATE TABLE IF NOT EXISTS fork_generations (\n run_id TEXT NOT NULL,\n generation_id TEXT NOT NULL,\n replay_id TEXT,\n fork_parent_stage_id TEXT NOT NULL,\n generation_number INTEGER NOT NULL,\n clone_stage_ids_json TEXT NOT NULL,\n status TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n PRIMARY KEY (run_id, generation_id),\n FOREIGN KEY (run_id, replay_id) REFERENCES feedback_replays(run_id, replay_id)\n);\n\nCREATE UNIQUE INDEX IF NOT EXISTS idx_fork_generations_parent_number\n ON fork_generations (run_id, fork_parent_stage_id, generation_number);\nCREATE INDEX IF NOT EXISTS idx_fork_generations_run_replay\n ON fork_generations (run_id, replay_id, created_at);\n";
@@ -53,6 +53,7 @@ CREATE TABLE IF NOT EXISTS stage_executions (
53
53
  stage_id TEXT NOT NULL,
54
54
  attempt INTEGER NOT NULL,
55
55
  status TEXT NOT NULL,
56
+ verification_outcome TEXT NOT NULL DEFAULT 'not_run',
56
57
  started_at TEXT,
57
58
  finished_at TEXT,
58
59
  envelope_json TEXT,
@@ -62,4 +63,102 @@ CREATE TABLE IF NOT EXISTS stage_executions (
62
63
 
63
64
  CREATE INDEX IF NOT EXISTS idx_stage_executions_run_stage
64
65
  ON stage_executions (run_id, stage_id, attempt);
66
+
67
+ CREATE TABLE IF NOT EXISTS verification_check_results (
68
+ run_id TEXT NOT NULL,
69
+ stage_id TEXT NOT NULL,
70
+ attempt INTEGER NOT NULL,
71
+ check_id TEXT NOT NULL,
72
+ check_type TEXT NOT NULL,
73
+ status TEXT NOT NULL,
74
+ started_at TEXT,
75
+ finished_at TEXT,
76
+ evidence_json TEXT,
77
+ PRIMARY KEY (run_id, stage_id, attempt, check_id),
78
+ FOREIGN KEY (run_id, stage_id, attempt)
79
+ REFERENCES stage_executions(run_id, stage_id, attempt)
80
+ );
81
+
82
+ CREATE INDEX IF NOT EXISTS idx_verification_check_results_execution
83
+ ON verification_check_results (run_id, stage_id, attempt, check_id);
84
+
85
+ CREATE TABLE IF NOT EXISTS feedback_loops (
86
+ run_id TEXT NOT NULL,
87
+ loop_id TEXT NOT NULL,
88
+ source_stage_id TEXT NOT NULL,
89
+ source_attempt INTEGER NOT NULL,
90
+ policy_json TEXT NOT NULL,
91
+ state TEXT NOT NULL,
92
+ current_replay_id TEXT,
93
+ current_replay_number INTEGER,
94
+ deferred_send_back_json TEXT,
95
+ created_at TEXT NOT NULL,
96
+ updated_at TEXT NOT NULL,
97
+ PRIMARY KEY (run_id, loop_id),
98
+ FOREIGN KEY (run_id) REFERENCES runs(run_id)
99
+ );
100
+
101
+ CREATE INDEX IF NOT EXISTS idx_feedback_loops_run_state
102
+ ON feedback_loops (run_id, state, created_at);
103
+
104
+ CREATE TABLE IF NOT EXISTS feedback_replays (
105
+ run_id TEXT NOT NULL,
106
+ replay_id TEXT NOT NULL,
107
+ loop_id TEXT NOT NULL,
108
+ source_stage_id TEXT NOT NULL,
109
+ source_attempt INTEGER NOT NULL,
110
+ target_stage_id TEXT NOT NULL,
111
+ replay_number INTEGER NOT NULL,
112
+ max_replays INTEGER NOT NULL,
113
+ replay_session TEXT NOT NULL,
114
+ route_stage_ids_json TEXT NOT NULL,
115
+ feedback_envelope_json TEXT NOT NULL,
116
+ status TEXT NOT NULL,
117
+ created_at TEXT NOT NULL,
118
+ updated_at TEXT NOT NULL,
119
+ PRIMARY KEY (run_id, replay_id),
120
+ FOREIGN KEY (run_id, loop_id) REFERENCES feedback_loops(run_id, loop_id)
121
+ );
122
+
123
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_feedback_replays_loop_number
124
+ ON feedback_replays (run_id, loop_id, replay_number);
125
+ CREATE INDEX IF NOT EXISTS idx_feedback_replays_run_loop
126
+ ON feedback_replays (run_id, loop_id, created_at);
127
+
128
+ CREATE TABLE IF NOT EXISTS feedback_replay_stage_passes (
129
+ run_id TEXT NOT NULL,
130
+ replay_id TEXT NOT NULL,
131
+ stage_id TEXT NOT NULL,
132
+ stage_attempt INTEGER NOT NULL,
133
+ session_origin_attempt INTEGER,
134
+ session_mode TEXT NOT NULL,
135
+ status TEXT NOT NULL,
136
+ started_at TEXT,
137
+ finished_at TEXT,
138
+ emitted_envelope_json TEXT,
139
+ PRIMARY KEY (run_id, replay_id, stage_id),
140
+ FOREIGN KEY (run_id, replay_id) REFERENCES feedback_replays(run_id, replay_id)
141
+ );
142
+
143
+ CREATE INDEX IF NOT EXISTS idx_feedback_replay_stage_passes_replay
144
+ ON feedback_replay_stage_passes (run_id, replay_id, stage_id);
145
+
146
+ CREATE TABLE IF NOT EXISTS fork_generations (
147
+ run_id TEXT NOT NULL,
148
+ generation_id TEXT NOT NULL,
149
+ replay_id TEXT,
150
+ fork_parent_stage_id TEXT NOT NULL,
151
+ generation_number INTEGER NOT NULL,
152
+ clone_stage_ids_json TEXT NOT NULL,
153
+ status TEXT NOT NULL,
154
+ created_at TEXT NOT NULL,
155
+ updated_at TEXT NOT NULL,
156
+ PRIMARY KEY (run_id, generation_id),
157
+ FOREIGN KEY (run_id, replay_id) REFERENCES feedback_replays(run_id, replay_id)
158
+ );
159
+
160
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_fork_generations_parent_number
161
+ ON fork_generations (run_id, fork_parent_stage_id, generation_number);
162
+ CREATE INDEX IF NOT EXISTS idx_fork_generations_run_replay
163
+ ON fork_generations (run_id, replay_id, created_at);
65
164
  `;
@@ -1,4 +1,4 @@
1
1
  import type { RunPipelineDagSnapshot } from "./port.js";
2
2
  export declare function mintCloneInstanceId(catalogId: string, n: number): string;
3
- export declare function mintCloneInstanceIds(catalogId: string, count: number): string[];
3
+ export declare function mintCloneInstanceIds(catalogId: string, count: number, startAt?: number): string[];
4
4
  export declare function definitionIdForInstance(snapshot: RunPipelineDagSnapshot | null | undefined, instanceId: string): string;
@@ -11,9 +11,10 @@ export function mintCloneInstanceId(catalogId, n) {
11
11
  guardStageId(instanceId);
12
12
  return instanceId;
13
13
  }
14
- export function mintCloneInstanceIds(catalogId, count) {
14
+ export function mintCloneInstanceIds(catalogId, count, startAt = 1) {
15
15
  guardPositiveInteger(count, "count");
16
- return Array.from({ length: count }, (_, i) => mintCloneInstanceId(catalogId, i + 1));
16
+ guardPositiveInteger(startAt, "startAt");
17
+ return Array.from({ length: count }, (_, i) => mintCloneInstanceId(catalogId, startAt + i));
17
18
  }
18
19
  export function definitionIdForInstance(snapshot, instanceId) {
19
20
  const node = snapshot?.nodes.find((n) => n.id === instanceId);
@@ -1,14 +1,38 @@
1
+ import { isNeedTerminalState, predecessorEdges } from "../config/pipelineNeeds.js";
1
2
  import { instancesOfDefinition, linearCompatDagSnapshot, } from "./pipelineDagSnapshot.js";
3
+ import { definitionIdForInstance } from "./stageInstanceId.js";
2
4
  function stageStarted(stage) {
3
5
  return stage.events.some((e) => e.event === "started" || e.event === "resumed");
4
6
  }
5
- function runHalted(stages) {
6
- return stages.some((s) => s.status === "failed");
7
- }
8
7
  function predecessorIds(dag, needs) {
9
8
  const preds = instancesOfDefinition(dag, needs);
10
9
  return preds.length > 0 ? preds : [needs];
11
10
  }
11
+ function inboundFromIds(dag, node) {
12
+ if (!node)
13
+ return [];
14
+ const ids = [];
15
+ for (const edge of predecessorEdges(node)) {
16
+ ids.push(...predecessorIds(dag, edge.id));
17
+ }
18
+ return ids;
19
+ }
20
+ function failureIsAccepted(dag, failedId, snapshotsById) {
21
+ const defId = definitionIdForInstance(dag, failedId);
22
+ for (const node of dag.nodes) {
23
+ const edge = predecessorEdges(node).find((item) => item.id === failedId || item.id === defId);
24
+ if (!edge?.on.includes("failed"))
25
+ continue;
26
+ const join = snapshotsById.get(node.id);
27
+ if (join && join.status !== "skipped")
28
+ return true;
29
+ }
30
+ return false;
31
+ }
32
+ function runHalted(dag, stages, snapshotsById) {
33
+ return stages.some((s) => s.status === "failed" &&
34
+ !failureIsAccepted(dag, s.stage_id, snapshotsById));
35
+ }
12
36
  function computeLayers(dag) {
13
37
  const layers = new Map();
14
38
  const nodeById = new Map(dag.nodes.map((n) => [n.id, n]));
@@ -16,12 +40,11 @@ function computeLayers(dag) {
16
40
  const cached = layers.get(id);
17
41
  if (cached !== undefined)
18
42
  return cached;
19
- const node = nodeById.get(id);
20
- if (!node?.needs) {
43
+ const fromIds = inboundFromIds(dag, nodeById.get(id));
44
+ if (fromIds.length === 0) {
21
45
  layers.set(id, 0);
22
46
  return 0;
23
47
  }
24
- const fromIds = predecessorIds(dag, node.needs);
25
48
  const l = Math.max(...fromIds.map(layerFor)) + 1;
26
49
  layers.set(id, l);
27
50
  return l;
@@ -47,17 +70,22 @@ function deriveReadiness(stage, dag, snapshotsById, halted) {
47
70
  return { readiness: "skipped" };
48
71
  }
49
72
  const node = dag.nodes.find((n) => n.id === stage.stage_id);
50
- const need = node?.needs;
51
- if (need) {
52
- const fromIds = predecessorIds(dag, need);
53
- const unresolved = fromIds.filter((id) => {
54
- const predecessor = snapshotsById.get(id);
55
- return !predecessor || predecessor.status !== "succeeded";
56
- });
57
- if (unresolved.length > 0) {
58
- return { readiness: "blocked", blocked_by: unresolved };
73
+ const unresolved = [];
74
+ if (node) {
75
+ for (const edge of predecessorEdges(node)) {
76
+ for (const fromId of predecessorIds(dag, edge.id)) {
77
+ const predecessor = snapshotsById.get(fromId);
78
+ if (!predecessor ||
79
+ !isNeedTerminalState(predecessor.status) ||
80
+ !edge.on.includes(predecessor.status)) {
81
+ unresolved.push(fromId);
82
+ }
83
+ }
59
84
  }
60
85
  }
86
+ if (unresolved.length > 0) {
87
+ return { readiness: "blocked", blocked_by: unresolved };
88
+ }
61
89
  return { readiness: "ready" };
62
90
  }
63
91
  export function buildPipelineTrack(input) {
@@ -66,7 +94,7 @@ export function buildPipelineTrack(input) {
66
94
  input.stages.map((s) => s.stage_id);
67
95
  const dag = input.dagSnapshot ?? linearCompatDagSnapshot(stageIds);
68
96
  const layers = computeLayers(dag);
69
- const halted = runHalted(input.stages);
97
+ const halted = runHalted(dag, input.stages, snapshotsById);
70
98
  const nodes = dag.stage_ids.map((stageId) => {
71
99
  const stage = snapshotsById.get(stageId);
72
100
  if (!stage) {
@@ -87,23 +115,27 @@ export function buildPipelineTrack(input) {
87
115
  if (blocked_by?.length)
88
116
  trackNode.blocked_by = blocked_by;
89
117
  const gateKinds = dag.gate_kinds?.[definitionId] ?? dag.gate_kinds?.[stageId];
90
- if (gateKinds?.length)
118
+ if (gateKinds !== undefined)
91
119
  trackNode.gate_kinds = gateKinds;
120
+ const feedbackTarget = node?.feedback_loop?.target;
121
+ if (feedbackTarget !== undefined && feedbackTarget.trim() !== "") {
122
+ trackNode.feedback_loop = { target: feedbackTarget };
123
+ }
92
124
  return trackNode;
93
125
  });
94
126
  const edges = [];
95
127
  for (const node of dag.nodes) {
96
- if (!node.needs)
97
- continue;
98
- const fromIds = predecessorIds(dag, node.needs);
99
- for (const fromId of fromIds) {
100
- const fromStage = snapshotsById.get(fromId);
101
- const edge = { from: fromId, to: node.id };
102
- if (fromStage?.status === "succeeded" &&
103
- fromStage.envelope?.summary) {
104
- edge.envelope_summary = fromStage.envelope.summary;
128
+ for (const edge of predecessorEdges(node)) {
129
+ const fromIds = predecessorIds(dag, edge.id);
130
+ for (const fromId of fromIds) {
131
+ const fromStage = snapshotsById.get(fromId);
132
+ const trackEdge = { from: fromId, to: node.id };
133
+ if (fromStage?.status === "succeeded" &&
134
+ fromStage.envelope?.summary) {
135
+ trackEdge.envelope_summary = fromStage.envelope.summary;
136
+ }
137
+ edges.push(trackEdge);
105
138
  }
106
- edges.push(edge);
107
139
  }
108
140
  }
109
141
  return { nodes, edges };
@@ -0,0 +1,19 @@
1
+ import type { RunStore, StageExecution, VerificationCheckResult } from "./port.js";
2
+ /**
3
+ * The audit trail for every execution of one stage. This is deliberately a
4
+ * stage-scoped read model: command output and other check evidence can be
5
+ * substantial, so it does not belong in a run listing or summary.
6
+ */
7
+ export type StageVerificationAttempt = Pick<StageExecution, "attempt" | "status" | "verification_outcome" | "started_at" | "finished_at"> & {
8
+ checks: VerificationCheckResult[];
9
+ };
10
+ export type StageVerificationHistory = {
11
+ run_id: string;
12
+ stage_id: string;
13
+ attempts: StageVerificationAttempt[];
14
+ manual_recovery?: {
15
+ status: "available" | "stopped";
16
+ failed_attempt: number;
17
+ };
18
+ };
19
+ export declare function readStageVerificationHistory(store: RunStore, runId: string, stageId: string): Promise<StageVerificationHistory>;
@@ -0,0 +1,30 @@
1
+ import { manualRecoveryHistory, readManualRecoveryState, } from "../runtime/manualRecoveryState.js";
2
+ export async function readStageVerificationHistory(store, runId, stageId) {
3
+ const run = await store.readRun(runId);
4
+ if (!run.stages.some((stage) => stage.stage_id === stageId)) {
5
+ throw new Error(`Stage not found: ${stageId}`);
6
+ }
7
+ const executions = await store.listStageExecutions(runId, stageId);
8
+ const attempts = await Promise.all(executions.map(async (execution) => ({
9
+ attempt: execution.attempt,
10
+ status: execution.status,
11
+ verification_outcome: execution.verification_outcome,
12
+ ...(execution.started_at !== undefined
13
+ ? { started_at: execution.started_at }
14
+ : {}),
15
+ ...(execution.finished_at !== undefined
16
+ ? { finished_at: execution.finished_at }
17
+ : {}),
18
+ checks: await store.listVerificationCheckResults(runId, stageId, execution.attempt),
19
+ })));
20
+ const recovery = manualRecoveryHistory(await readManualRecoveryState(store, runId, stageId));
21
+ if (recovery === undefined) {
22
+ return { run_id: runId, stage_id: stageId, attempts };
23
+ }
24
+ return {
25
+ run_id: runId,
26
+ stage_id: stageId,
27
+ attempts,
28
+ manual_recovery: recovery,
29
+ };
30
+ }
@@ -1,18 +1,54 @@
1
1
  import type { RunPipelineDagSnapshot } from "../runstore/port.js";
2
2
  import type { StageEnvelope } from "../types/envelope.js";
3
- import type { ResolvedPipelineDag } from "../types/pipeline.js";
3
+ import type { NeedTerminalState, ResolvedPipelineDag } from "../types/pipeline.js";
4
+ import { type ActiveCohort } from "./forkGeneration.js";
4
5
  import type { StageScheduleState } from "./pipelineScheduler.js";
5
6
  export declare function isCloneInstance(dag: ResolvedPipelineDag, stageId: string): boolean;
6
7
  export declare function sequentialLaterCloneIds(dag: ResolvedPipelineDag, stageId: string, completedEnvelopes: Map<string, StageEnvelope>): string[];
7
- export declare function cloneFanoutConflict(dag: ResolvedPipelineDag, envelope: StageEnvelope): string | undefined;
8
+ export declare function skipRejectedNeedDependents(dag: ResolvedPipelineDag, parentId: string, states: Map<string, StageScheduleState>, observed: NeedTerminalState, onSkip: (stageId: string) => void): void;
9
+ export declare function failureIsAccepted(dag: ResolvedPipelineDag, failedId: string, states: Map<string, StageScheduleState>): boolean;
10
+ /**
11
+ * A fresh clone_forks fanout targeting a catalog id that already has
12
+ * instances is only safe when every existing instance was put back to
13
+ * `pending` by the current retry cascade (`retryDownstreamIds`) — that means
14
+ * an ancestor was legitimately retried and this parent re-emitted the same
15
+ * fanout for reactivation. Instances that are still active/terminal, or
16
+ * merely `pending` without having gone through a retry reset (e.g. an
17
+ * envelope processed twice in one live run), are rejected as before.
18
+ *
19
+ * Feedback-loop replay is different: the prior cohort was superseded
20
+ * (`skipped`) and a new generation must mint fresh `~N` ids, so
21
+ * `forceFreshCloneIds` bypasses the conflict.
22
+ */
23
+ export declare function cloneFanoutConflict(dag: ResolvedPipelineDag, envelope: StageEnvelope, states: Map<string, StageScheduleState>, retryDownstreamIds: ReadonlySet<string>, options?: {
24
+ forceFreshCloneIds?: boolean;
25
+ }): string | undefined;
26
+ export declare function nextFreeCloneSuffix(snapshot: RunPipelineDagSnapshot, catalogId: string): number;
8
27
  export declare function protectedClonableChildIds(dag: ResolvedPipelineDag, predecessorId: string, envelope: StageEnvelope): Set<string>;
28
+ /**
29
+ * Downstream closure for a retry, resolved through clone-instance ids and
30
+ * static multi-parent edges. Catalog-level children (e.g. a join stage) are
31
+ * registered in `childrenOf` under the *definition* id, not per clone
32
+ * instance, so a plain childrenOf walk starting above a fanout dead-ends at
33
+ * the instance ids it discovers transitively. Resolving definition-id
34
+ * children and `predecessorEdges` join successors at every step means
35
+ * retrying an ancestor or a single clone instance still reaches the join
36
+ * and everything after it, without pulling in sibling terminals.
37
+ */
9
38
  export declare function cloneRetryDownstream(dag: ResolvedPipelineDag, stageIds: string[]): Set<string>;
10
39
  export declare function cloneFailureContinuesSchedule(dag: ResolvedPipelineDag, stageId: string, completedEnvelopes: Map<string, StageEnvelope>): boolean;
11
40
  export declare function cloneFailFastSkipIds(dag: ResolvedPipelineDag, stageId: string, completedEnvelopes: Map<string, StageEnvelope>): string[];
12
- export declare function cloneScheduleAllowsRun(dag: ResolvedPipelineDag, stageId: string, states: Map<string, StageScheduleState>, completedEnvelopes: Map<string, StageEnvelope>): boolean;
41
+ export declare function cloneScheduleAllowsRun(dag: ResolvedPipelineDag, stageId: string, states: Map<string, StageScheduleState>, completedEnvelopes: Map<string, StageEnvelope>, options?: {
42
+ activeCohortForNeeds?: (needsId: string) => ActiveCohort;
43
+ activeCloneIdsForNeeds?: (needsId: string) => Set<string> | null;
44
+ }): boolean;
13
45
  export type ApplyCloneForksResult = {
14
46
  dag: RunPipelineDagSnapshot;
15
47
  skippedIds: string[];
48
+ /** Freshly minted instance ids keyed by clonable catalog successor id. */
49
+ mintedBySuccessor?: Map<string, string[]>;
16
50
  };
17
- export declare function applyCloneForksToSchedule(dag: ResolvedPipelineDag, predecessorId: string, envelope: StageEnvelope, states: Map<string, StageScheduleState>): ApplyCloneForksResult;
51
+ export declare function applyCloneForksToSchedule(dag: ResolvedPipelineDag, predecessorId: string, envelope: StageEnvelope, states: Map<string, StageScheduleState>, options?: {
52
+ forceFreshCloneIds?: boolean;
53
+ }): ApplyCloneForksResult;
18
54
  export declare function applyCloneForksFromEnvelopes(dag: ResolvedPipelineDag, states: Map<string, StageScheduleState>, completedEnvelopes: Map<string, StageEnvelope>): ApplyCloneForksResult;