@osolmaz/pi-workflows 0.12.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 (257) hide show
  1. package/README.md +56 -31
  2. package/dist/builtins/autodoc.workflow.d.ts +4 -4
  3. package/dist/builtins/autoimplement.workflow.d.ts +797 -142
  4. package/dist/builtins/autoimplement.workflow.js +17 -105
  5. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  6. package/dist/builtins/autoplan.workflow.d.ts +126 -13
  7. package/dist/builtins/autoplan.workflow.js +200 -29
  8. package/dist/builtins/autoplan.workflow.js.map +1 -1
  9. package/dist/builtins/catalog.js +7 -5
  10. package/dist/builtins/catalog.js.map +1 -1
  11. package/dist/builtins/index.d.ts +4 -2
  12. package/dist/builtins/index.js +3 -1
  13. package/dist/builtins/index.js.map +1 -1
  14. package/dist/builtins/monitor.workflow.d.ts +56 -23
  15. package/dist/builtins/monitor.workflow.js +508 -329
  16. package/dist/builtins/monitor.workflow.js.map +1 -1
  17. package/dist/builtins/pi-agent-group.d.ts +72 -0
  18. package/dist/builtins/pi-agent-group.js +1087 -0
  19. package/dist/builtins/pi-agent-group.js.map +1 -0
  20. package/dist/builtins/plain-summary.workflow.d.ts +32 -0
  21. package/dist/builtins/plain-summary.workflow.js +134 -0
  22. package/dist/builtins/plain-summary.workflow.js.map +1 -0
  23. package/dist/builtins/plan-approval.workflow.d.ts +39 -5
  24. package/dist/builtins/plan-approval.workflow.js +92 -14
  25. package/dist/builtins/plan-approval.workflow.js.map +1 -1
  26. package/dist/builtins/plan-change.workflow.d.ts +449 -0
  27. package/dist/builtins/plan-change.workflow.js +256 -0
  28. package/dist/builtins/plan-change.workflow.js.map +1 -0
  29. package/dist/builtins/plan-presentation.js +2 -2
  30. package/dist/builtins/plan-presentation.js.map +1 -1
  31. package/dist/builtins/sanity-check.workflow.d.ts +50 -6
  32. package/dist/builtins/sanity-check.workflow.js +150 -28
  33. package/dist/builtins/sanity-check.workflow.js.map +1 -1
  34. package/dist/controllers/effects.d.ts +3 -2
  35. package/dist/controllers/effects.js +8 -1
  36. package/dist/controllers/effects.js.map +1 -1
  37. package/dist/controllers/index.d.ts +1 -1
  38. package/dist/controllers/index.js +1 -1
  39. package/dist/controllers/index.js.map +1 -1
  40. package/dist/controllers/manager.d.ts +1 -0
  41. package/dist/controllers/manager.js +41 -17
  42. package/dist/controllers/manager.js.map +1 -1
  43. package/dist/controllers/sqlite.d.ts +107 -85
  44. package/dist/controllers/sqlite.js +1611 -1486
  45. package/dist/controllers/sqlite.js.map +1 -1
  46. package/dist/controllers/store.d.ts +9 -8
  47. package/dist/controllers/store.js +1 -31
  48. package/dist/controllers/store.js.map +1 -1
  49. package/dist/controllers/types.d.ts +3 -0
  50. package/dist/controllers/workflow-engine-scheduler.d.ts +1 -1
  51. package/dist/controllers/workflow-engine-scheduler.js +3 -5
  52. package/dist/controllers/workflow-engine-scheduler.js.map +1 -1
  53. package/dist/controllers/workflows.d.ts +2 -2
  54. package/dist/controllers/workflows.js +11 -10
  55. package/dist/controllers/workflows.js.map +1 -1
  56. package/dist/extension/controller-host.js +2 -2
  57. package/dist/extension/controller-host.js.map +1 -1
  58. package/dist/extension/decision-channels.d.ts +4 -2
  59. package/dist/extension/decision-channels.js +333 -146
  60. package/dist/extension/decision-channels.js.map +1 -1
  61. package/dist/extension/executor.d.ts +9 -2
  62. package/dist/extension/executor.js +89 -2
  63. package/dist/extension/executor.js.map +1 -1
  64. package/dist/extension/herdr-viewer.d.ts +0 -1
  65. package/dist/extension/herdr-viewer.js +0 -4
  66. package/dist/extension/herdr-viewer.js.map +1 -1
  67. package/dist/extension/index.js +209 -127
  68. package/dist/extension/index.js.map +1 -1
  69. package/dist/extension/recorder.d.ts +3 -10
  70. package/dist/extension/recorder.js +13 -22
  71. package/dist/extension/recorder.js.map +1 -1
  72. package/dist/extension/session-events.d.ts +2 -2
  73. package/dist/extension/step-message.d.ts +7 -2
  74. package/dist/extension/step-message.js +94 -2
  75. package/dist/extension/step-message.js.map +1 -1
  76. package/dist/extension/widget.js +29 -3
  77. package/dist/extension/widget.js.map +1 -1
  78. package/dist/host/rpc-executor.d.ts +3 -0
  79. package/dist/host/rpc-executor.js +2 -0
  80. package/dist/host/rpc-executor.js.map +1 -1
  81. package/dist/host/runner.d.ts +3 -6
  82. package/dist/host/runner.js +83 -32
  83. package/dist/host/runner.js.map +1 -1
  84. package/dist/render/graph-render.js +15 -10
  85. package/dist/render/graph-render.js.map +1 -1
  86. package/dist/state/database.d.ts +35 -0
  87. package/dist/state/database.js +287 -0
  88. package/dist/state/database.js.map +1 -0
  89. package/dist/state/index.d.ts +4 -0
  90. package/dist/state/index.js +5 -0
  91. package/dist/state/index.js.map +1 -0
  92. package/dist/state/json.d.ts +6 -0
  93. package/dist/state/json.js +38 -0
  94. package/dist/state/json.js.map +1 -0
  95. package/dist/state/mutation.d.ts +57 -0
  96. package/dist/state/mutation.js +255 -0
  97. package/dist/state/mutation.js.map +1 -0
  98. package/dist/state/schema.d.ts +6 -0
  99. package/dist/state/schema.js +473 -0
  100. package/dist/state/schema.js.map +1 -0
  101. package/dist/viewer/cli.d.ts +2 -2
  102. package/dist/viewer/cli.js +142 -122
  103. package/dist/viewer/cli.js.map +1 -1
  104. package/dist/viewer/render.d.ts +4 -4
  105. package/dist/viewer/render.js +9 -23
  106. package/dist/viewer/render.js.map +1 -1
  107. package/dist/viewer/session-reducer.d.ts +1 -1
  108. package/dist/viewer/session-reducer.js +1 -1
  109. package/dist/viewer/tui.d.ts +3 -3
  110. package/dist/viewer/tui.js +15 -13
  111. package/dist/viewer/tui.js.map +1 -1
  112. package/dist/viewer/watch.d.ts +2 -5
  113. package/dist/viewer/watch.js +13 -13
  114. package/dist/viewer/watch.js.map +1 -1
  115. package/dist/workflows/catalog.js +7 -2
  116. package/dist/workflows/catalog.js.map +1 -1
  117. package/dist/workflows/composition.js +20 -2
  118. package/dist/workflows/composition.js.map +1 -1
  119. package/dist/workflows/decision-presentation.d.ts +1 -1
  120. package/dist/workflows/decision-presentation.js +51 -38
  121. package/dist/workflows/decision-presentation.js.map +1 -1
  122. package/dist/workflows/definition.d.ts +6 -2
  123. package/dist/workflows/definition.js +17 -0
  124. package/dist/workflows/definition.js.map +1 -1
  125. package/dist/workflows/engine.d.ts +7 -7
  126. package/dist/workflows/engine.js +196 -115
  127. package/dist/workflows/engine.js.map +1 -1
  128. package/dist/workflows/errors.d.ts +16 -3
  129. package/dist/workflows/errors.js +18 -3
  130. package/dist/workflows/errors.js.map +1 -1
  131. package/dist/workflows/human-decision.d.ts +45 -8
  132. package/dist/workflows/human-decision.js +641 -274
  133. package/dist/workflows/human-decision.js.map +1 -1
  134. package/dist/workflows/index.d.ts +5 -6
  135. package/dist/workflows/index.js +4 -5
  136. package/dist/workflows/index.js.map +1 -1
  137. package/dist/workflows/progress.d.ts +1 -0
  138. package/dist/workflows/progress.js +15 -3
  139. package/dist/workflows/progress.js.map +1 -1
  140. package/dist/workflows/schema.js +27 -1
  141. package/dist/workflows/schema.js.map +1 -1
  142. package/dist/workflows/store.d.ts +83 -103
  143. package/dist/workflows/store.js +907 -951
  144. package/dist/workflows/store.js.map +1 -1
  145. package/dist/workflows/types.d.ts +88 -126
  146. package/docs/CONTROLLERS.md +10 -8
  147. package/docs/DEFERRED_TURNS.md +2 -2
  148. package/docs/DESIGN_PHILOSOPHY.md +4 -2
  149. package/docs/HUMAN_DECISIONS.md +36 -47
  150. package/docs/HUMAN_DECISION_PRESENTATIONS.md +14 -24
  151. package/docs/MONITOR.md +179 -237
  152. package/docs/SQLITE_STATE.md +219 -0
  153. package/docs/WORKFLOW_COMPOSITION.md +27 -26
  154. package/docs/WORKFLOW_STEP_MESSAGES.md +20 -18
  155. package/docs/WORKFLOW_UPDATES.md +10 -9
  156. package/docs/development.md +9 -9
  157. package/docs/live-replay-protocol.md +28 -37
  158. package/docs/plans/2026-08-16-workflow-updates-plan.md +1 -1
  159. package/docs/plans/2026-08-19-human-decision-gates-plan.md +1 -1
  160. package/docs/plans/2026-08-21-autoimplement-timeout-fallback-plan.md +1 -1
  161. package/docs/plans/2026-08-21-plan-change-approval-policy-plan.md +322 -0
  162. package/docs/plans/2026-08-21-sanity-check-plan.md +217 -95
  163. package/docs/plans/2026-08-22-goal-finishing-monitor-plan.md +200 -0
  164. package/docs/plans/2026-08-23-assistant-agent-completion-plan.md +701 -0
  165. package/docs/plans/2026-08-23-sqlite-state-plan.md +970 -0
  166. package/docs/plans/replayable-run-bundles-implementation-plan.md +2 -2
  167. package/docs/plans/session-event-replay-implementation-plan.md +1 -1
  168. package/docs/plans/tui-viewer-implementation-plan.md +1 -1
  169. package/docs/session-event-journal.md +63 -434
  170. package/docs/tui-viewer.md +13 -15
  171. package/docs/workflows.md +127 -53
  172. package/examples/workflows/approved-plan.workflow.ts +19 -46
  173. package/examples/workflows/plain-summary.workflow.ts +1 -0
  174. package/herdr-plugin.toml +1 -1
  175. package/package.json +7 -7
  176. package/plugins/herdr/viewer.mjs +1 -11
  177. package/schemas/human-decision-accepted-v1.schema.json +15 -3
  178. package/schemas/human-decision-continuation-v1.schema.json +10 -1
  179. package/schemas/human-decision-delivery-v1.schema.json +8 -0
  180. package/schemas/human-decision-receipt-v1.schema.json +8 -0
  181. package/schemas/human-decision-request-v1.schema.json +24 -4
  182. package/skills/autoimplement/SKILL.md +27 -0
  183. package/skills/autoplan/SKILL.md +5 -4
  184. package/skills/monitor/SKILL.md +91 -135
  185. package/skills/pi-workflows/SKILL.md +9 -8
  186. package/skills/sanity-check/SKILL.md +44 -0
  187. package/src/builtins/autoimplement.workflow.ts +19 -118
  188. package/src/builtins/autoplan.workflow.ts +290 -40
  189. package/src/builtins/catalog.ts +7 -5
  190. package/src/builtins/index.ts +18 -1
  191. package/src/builtins/monitor.workflow.ts +673 -382
  192. package/src/builtins/pi-agent-group.ts +1407 -0
  193. package/src/builtins/plain-summary.workflow.ts +185 -0
  194. package/src/builtins/plan-approval.workflow.ts +157 -24
  195. package/src/builtins/plan-change.workflow.ts +321 -0
  196. package/src/builtins/plan-presentation.ts +2 -2
  197. package/src/builtins/sanity-check.workflow.ts +248 -48
  198. package/src/controllers/effects.ts +7 -0
  199. package/src/controllers/index.ts +0 -6
  200. package/src/controllers/manager.ts +72 -32
  201. package/src/controllers/sqlite.ts +2420 -1948
  202. package/src/controllers/store.ts +17 -38
  203. package/src/controllers/types.ts +3 -0
  204. package/src/controllers/workflow-engine-scheduler.ts +4 -8
  205. package/src/controllers/workflows.ts +45 -21
  206. package/src/extension/controller-host.ts +1 -2
  207. package/src/extension/decision-channels.ts +456 -203
  208. package/src/extension/executor.ts +105 -2
  209. package/src/extension/herdr-viewer.ts +0 -5
  210. package/src/extension/index.ts +251 -151
  211. package/src/extension/recorder.ts +13 -22
  212. package/src/extension/session-events.ts +2 -2
  213. package/src/extension/step-message.ts +106 -4
  214. package/src/extension/widget.ts +32 -5
  215. package/src/host/rpc-executor.ts +4 -0
  216. package/src/host/runner.ts +93 -41
  217. package/src/render/graph-render.ts +12 -6
  218. package/src/state/database.ts +375 -0
  219. package/src/state/index.ts +29 -0
  220. package/src/state/json.ts +42 -0
  221. package/src/state/mutation.ts +435 -0
  222. package/src/state/schema.ts +475 -0
  223. package/src/viewer/cli.ts +151 -144
  224. package/src/viewer/render.ts +19 -30
  225. package/src/viewer/session-reducer.ts +1 -1
  226. package/src/viewer/tui.ts +18 -17
  227. package/src/viewer/watch.ts +13 -17
  228. package/src/workflows/catalog.ts +7 -2
  229. package/src/workflows/composition.ts +21 -3
  230. package/src/workflows/decision-presentation.ts +56 -43
  231. package/src/workflows/definition.ts +36 -3
  232. package/src/workflows/engine.ts +228 -130
  233. package/src/workflows/errors.ts +27 -3
  234. package/src/workflows/human-decision.ts +921 -349
  235. package/src/workflows/index.ts +20 -30
  236. package/src/workflows/progress.ts +18 -3
  237. package/src/workflows/schema.ts +36 -1
  238. package/src/workflows/store.ts +1300 -1163
  239. package/src/workflows/types.ts +100 -137
  240. package/dist/builtins/sanity-check-session.d.ts +0 -17
  241. package/dist/builtins/sanity-check-session.js +0 -168
  242. package/dist/builtins/sanity-check-session.js.map +0 -1
  243. package/dist/workflows/artifacts.d.ts +0 -40
  244. package/dist/workflows/artifacts.js +0 -155
  245. package/dist/workflows/artifacts.js.map +0 -1
  246. package/dist/workflows/migrate-sources.d.ts +0 -42
  247. package/dist/workflows/migrate-sources.js +0 -133
  248. package/dist/workflows/migrate-sources.js.map +0 -1
  249. package/docs/run-bundles.md +0 -481
  250. package/schemas/human-decision-accepted-v2.schema.json +0 -50
  251. package/schemas/human-decision-delivery-v2.schema.json +0 -36
  252. package/schemas/human-decision-receipt-v2.schema.json +0 -39
  253. package/schemas/human-decision-request-v2.schema.json +0 -69
  254. package/schemas/human-decision-resolution-v2.schema.json +0 -27
  255. package/src/builtins/sanity-check-session.ts +0 -205
  256. package/src/workflows/artifacts.ts +0 -188
  257. package/src/workflows/migrate-sources.ts +0 -178
@@ -0,0 +1,321 @@
1
+ import {
2
+ compute,
3
+ defineWorkflow,
4
+ includeWorkflow,
5
+ includedResult,
6
+ } from "../workflows/definition.js";
7
+ import type { WorkflowNodeContext } from "../workflows/types.js";
8
+ import autodocWorkflow, { type AutodocInput, type DocumentedPlan } from "./autodoc.workflow.js";
9
+ import autoplanWorkflow, { type AutoplanInput, type AutoplanReady } from "./autoplan.workflow.js";
10
+ import planApprovalWorkflow, {
11
+ parsePlanApprovalPolicy,
12
+ type PlanApprovalContinue,
13
+ type NormalizedPlanApprovalInput,
14
+ type PlanApprovalPolicy,
15
+ type PlanApprovalResolution,
16
+ type ResolvedPlanApprovalPolicy,
17
+ } from "./plan-approval.workflow.js";
18
+
19
+ export type PlanChangeInput = {
20
+ task: string;
21
+ scope?: string;
22
+ constraints?: string[];
23
+ repository?: string;
24
+ documents?: string[];
25
+ previousPlan?: unknown;
26
+ newEvidence?: unknown;
27
+ approval?: PlanApprovalPolicy;
28
+ };
29
+
30
+ export type NormalizedPlanChangeInput = Omit<PlanChangeInput, "approval"> & {
31
+ approval: ResolvedPlanApprovalPolicy;
32
+ };
33
+
34
+ export type PlanChangeReady = {
35
+ status: "ready";
36
+ plan: unknown;
37
+ planDigest: string;
38
+ documents: string[];
39
+ revision: number;
40
+ approval: PlanApprovalResolution;
41
+ documentation: DocumentedPlan["documentation"];
42
+ };
43
+
44
+ export type PlanChangeBlocked = {
45
+ status: "blocked";
46
+ reason: string;
47
+ evidence: unknown;
48
+ };
49
+
50
+ function requireRecord(value: unknown, label: string): Record<string, unknown> {
51
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
52
+ throw new Error(`${label} must be an object`);
53
+ }
54
+ return value as Record<string, unknown>;
55
+ }
56
+
57
+ function requireString(value: unknown, label: string): string {
58
+ if (typeof value !== "string" || value.trim().length === 0) {
59
+ throw new Error(`${label} must be a non-empty string`);
60
+ }
61
+ return value.trim();
62
+ }
63
+
64
+ function requireExactKeys(
65
+ value: Record<string, unknown>,
66
+ allowed: readonly string[],
67
+ label: string,
68
+ ): void {
69
+ const unexpected = Object.keys(value).filter((key) => !allowed.includes(key));
70
+ if (unexpected.length > 0) throw new Error(`${label} has unknown field ${unexpected[0]}`);
71
+ }
72
+
73
+ function parseStringArray(value: unknown, label: string): string[] | undefined {
74
+ if (value === undefined) return undefined;
75
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
76
+ throw new Error(`${label} must be an array of strings`);
77
+ }
78
+ return [...value] as string[];
79
+ }
80
+
81
+ function parseInput(value: unknown): NormalizedPlanChangeInput {
82
+ const input = requireRecord(value, "plan change input");
83
+ requireExactKeys(
84
+ input,
85
+ [
86
+ "task",
87
+ "scope",
88
+ "constraints",
89
+ "repository",
90
+ "documents",
91
+ "previousPlan",
92
+ "newEvidence",
93
+ "approval",
94
+ ],
95
+ "plan change input",
96
+ );
97
+ const constraints = parseStringArray(input.constraints, "plan change constraints");
98
+ const documents = parseStringArray(input.documents, "plan change documents");
99
+ return {
100
+ task: requireString(input.task, "plan change task"),
101
+ ...(input.scope !== undefined
102
+ ? { scope: requireString(input.scope, "plan change scope") }
103
+ : {}),
104
+ ...(constraints !== undefined ? { constraints } : {}),
105
+ ...(input.repository !== undefined
106
+ ? { repository: requireString(input.repository, "plan change repository") }
107
+ : {}),
108
+ ...(documents !== undefined ? { documents } : {}),
109
+ ...(input.previousPlan !== undefined ? { previousPlan: input.previousPlan } : {}),
110
+ ...(input.newEvidence !== undefined ? { newEvidence: input.newEvidence } : {}),
111
+ approval: parsePlanApprovalPolicy(input.approval),
112
+ };
113
+ }
114
+
115
+ function currentDesign(context: Pick<WorkflowNodeContext, "outputs">): AutoplanReady {
116
+ const result = includedResult(autoplanWorkflow, context.outputs.design);
117
+ if (result.exit !== "ready") throw new Error("plan change design did not return a ready plan");
118
+ return result.output;
119
+ }
120
+
121
+ function currentDocumentation(context: Pick<WorkflowNodeContext, "outputs">): DocumentedPlan {
122
+ const result = includedResult(autodocWorkflow, context.outputs.documentation);
123
+ if (result.exit !== "ready") {
124
+ throw new Error("plan change documentation did not return a ready plan");
125
+ }
126
+ return result.output;
127
+ }
128
+
129
+ function currentApproval(context: Pick<WorkflowNodeContext, "outputs">): PlanApprovalContinue {
130
+ const result = includedResult(planApprovalWorkflow, context.outputs.approval);
131
+ if (result.exit !== "continue") {
132
+ throw new Error("plan change approval did not return continue");
133
+ }
134
+ return result.output;
135
+ }
136
+
137
+ function latestReplanInstructions(outputs: Record<string, unknown>): string | undefined {
138
+ if (outputs.approval === undefined) return undefined;
139
+ const result = includedResult(planApprovalWorkflow, outputs.approval);
140
+ return result.exit === "replan" ? result.output.instructions : undefined;
141
+ }
142
+
143
+ function blockedResult(context: WorkflowNodeContext): PlanChangeBlocked {
144
+ if (context.outputs.approval !== undefined) {
145
+ const approval = includedResult(planApprovalWorkflow, context.outputs.approval);
146
+ if (approval.exit === "stop") {
147
+ return {
148
+ status: "blocked",
149
+ reason: "The operator stopped the proposed plan change.",
150
+ evidence: approval.output,
151
+ };
152
+ }
153
+ }
154
+ const candidates = ["replanGuard", "assessPlan", "approval", "documentation", "design"];
155
+ for (let index = context.state.steps.length - 1; index >= 0; index -= 1) {
156
+ const step = context.state.steps[index];
157
+ if (step === undefined || !candidates.some((candidate) => step.nodeId.startsWith(candidate))) {
158
+ continue;
159
+ }
160
+ const output = step.output as Record<string, unknown> | null;
161
+ if (output !== null && typeof output === "object") {
162
+ const reason = output.reason;
163
+ if (typeof reason === "string" && reason.length > 0) {
164
+ return { status: "blocked", reason, evidence: step.output };
165
+ }
166
+ }
167
+ }
168
+ return {
169
+ status: "blocked",
170
+ reason: "The plan change could not continue within its configured policy.",
171
+ evidence: null,
172
+ };
173
+ }
174
+
175
+ export const planChangeWorkflow = defineWorkflow({
176
+ source: import.meta.url,
177
+ name: "plan-change",
178
+ input: parseInput,
179
+ startAt: "start",
180
+ maxSteps: 400,
181
+ includes: {
182
+ design: includeWorkflow(autoplanWorkflow, {
183
+ input: ({ input, outputs }): AutoplanInput => {
184
+ const request = input as NormalizedPlanChangeInput;
185
+ const prior =
186
+ outputs.design === undefined
187
+ ? request.previousPlan
188
+ : (() => {
189
+ const result = includedResult(autoplanWorkflow, outputs.design);
190
+ return result.exit === "ready" ? result.output.plan : request.previousPlan;
191
+ })();
192
+ const instructions = latestReplanInstructions(outputs);
193
+ return {
194
+ problem: request.task,
195
+ ...(request.scope !== undefined ? { scope: request.scope } : {}),
196
+ ...(request.constraints !== undefined ? { constraints: request.constraints } : {}),
197
+ ...(prior !== undefined ? { previousPlan: prior } : {}),
198
+ ...(instructions === undefined
199
+ ? request.newEvidence !== undefined
200
+ ? { newEvidence: request.newEvidence }
201
+ : {}
202
+ : {
203
+ newEvidence: {
204
+ priorEvidence: request.newEvidence,
205
+ operatorInstructions: instructions,
206
+ },
207
+ }),
208
+ };
209
+ },
210
+ }),
211
+ documentation: includeWorkflow(autodocWorkflow, {
212
+ input: (context): AutodocInput => {
213
+ const request = context.input as NormalizedPlanChangeInput;
214
+ const design = currentDesign(context);
215
+ return {
216
+ task: request.task,
217
+ plan: design.plan,
218
+ ...(request.repository !== undefined ? { repository: request.repository } : {}),
219
+ ...(request.documents !== undefined ? { documents: request.documents } : {}),
220
+ evidence: request.newEvidence,
221
+ };
222
+ },
223
+ }),
224
+ approval: includeWorkflow(planApprovalWorkflow, {
225
+ input: (context): NormalizedPlanApprovalInput => {
226
+ const request = context.input as NormalizedPlanChangeInput;
227
+ const documented = currentDocumentation(context);
228
+ const revision =
229
+ context.state.steps.filter((step) => step.nodeId === "approval/routePolicy").length + 1;
230
+ return {
231
+ task: request.task,
232
+ plan: documented.plan,
233
+ planDigest: documented.planDigest,
234
+ approval: request.approval,
235
+ revision,
236
+ };
237
+ },
238
+ }),
239
+ },
240
+ exits: {
241
+ ready: {
242
+ from: "finalize",
243
+ validate: (value: unknown): PlanChangeReady => value as PlanChangeReady,
244
+ },
245
+ blocked: {
246
+ from: "blocked",
247
+ validate: (value: unknown): PlanChangeBlocked => value as PlanChangeBlocked,
248
+ },
249
+ },
250
+ nodes: {
251
+ start: compute({ run: () => ({ route: "design" }) }),
252
+ assessPlan: compute({
253
+ run: (context) => {
254
+ const request = context.input as NormalizedPlanChangeInput;
255
+ const design = currentDesign(context);
256
+ return request.previousPlan !== undefined && design.changed !== true
257
+ ? {
258
+ route: "blocked",
259
+ reason: "Planning returned the same plan for the unresolved evidence.",
260
+ evidence: design,
261
+ }
262
+ : { route: "document", planDigest: design.planDigest };
263
+ },
264
+ }),
265
+ replanGuard: compute({
266
+ run: (context) => {
267
+ const request = context.input as NormalizedPlanChangeInput;
268
+ const replans = context.state.steps.filter(
269
+ (step) => step.nodeId === "approval/replan",
270
+ ).length;
271
+ return replans > request.approval.maxReplans
272
+ ? {
273
+ route: "blocked",
274
+ reason: `Plan approval reached the ${request.approval.maxReplans}-replan safety limit.`,
275
+ replans,
276
+ limit: request.approval.maxReplans,
277
+ }
278
+ : { route: "design", replans, limit: request.approval.maxReplans };
279
+ },
280
+ }),
281
+ finalize: compute({
282
+ run: (context) => {
283
+ const documented = currentDocumentation(context);
284
+ const approval = currentApproval(context);
285
+ const revision = context.state.steps.filter(
286
+ (step) => step.nodeId === "approval/routePolicy",
287
+ ).length;
288
+ return {
289
+ status: "ready",
290
+ plan: documented.plan,
291
+ planDigest: documented.planDigest,
292
+ documents: documented.documentation.files,
293
+ revision,
294
+ approval: approval.resolution,
295
+ documentation: documented.documentation,
296
+ } satisfies PlanChangeReady;
297
+ },
298
+ }),
299
+ blocked: compute({ run: blockedResult }),
300
+ },
301
+ edges: [
302
+ { from: "start", to: "design" },
303
+ { from: "design.ready", to: "assessPlan" },
304
+ { from: "design.blocked", to: "blocked" },
305
+ {
306
+ from: "assessPlan",
307
+ switch: { on: "$.route", cases: { document: "documentation", blocked: "blocked" } },
308
+ },
309
+ { from: "documentation.ready", to: "approval" },
310
+ { from: "documentation.blocked", to: "blocked" },
311
+ { from: "approval.continue", to: "finalize" },
312
+ { from: "approval.stop", to: "blocked" },
313
+ { from: "approval.replan", to: "replanGuard" },
314
+ {
315
+ from: "replanGuard",
316
+ switch: { on: "$.route", cases: { design: "design", blocked: "blocked" } },
317
+ },
318
+ ],
319
+ });
320
+
321
+ export default planChangeWorkflow;
@@ -1,6 +1,6 @@
1
1
  import {
2
- legacyDecisionPresentation,
3
2
  normalizeDecisionPresentation,
3
+ valueDecisionPresentation,
4
4
  } from "../workflows/decision-presentation.js";
5
5
  import type { DecisionPresentation } from "../workflows/types.js";
6
6
 
@@ -10,7 +10,7 @@ export function presentPlan(input: {
10
10
  planDigest: string;
11
11
  revision: number;
12
12
  }): DecisionPresentation {
13
- const plan = legacyDecisionPresentation(input.plan);
13
+ const plan = valueDecisionPresentation(input.plan);
14
14
  const summary =
15
15
  readableSummary(planSummary(input.plan)) ??
16
16
  readableSummary(`Review the implementation plan for ${input.task}.`)!;