@osolmaz/pi-workflows 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +4 -3
  2. package/dist/builtins/autoimplement.workflow.d.ts +485 -126
  3. package/dist/builtins/autoimplement.workflow.js +17 -105
  4. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  5. package/dist/builtins/catalog.js +4 -4
  6. package/dist/builtins/index.d.ts +2 -1
  7. package/dist/builtins/index.js +2 -1
  8. package/dist/builtins/index.js.map +1 -1
  9. package/dist/builtins/monitor.workflow.d.ts +2 -4
  10. package/dist/builtins/monitor.workflow.js +26 -128
  11. package/dist/builtins/monitor.workflow.js.map +1 -1
  12. package/dist/builtins/pi-agent-group.d.ts +72 -0
  13. package/dist/builtins/pi-agent-group.js +1087 -0
  14. package/dist/builtins/pi-agent-group.js.map +1 -0
  15. package/dist/builtins/plan-approval.workflow.d.ts +39 -5
  16. package/dist/builtins/plan-approval.workflow.js +92 -14
  17. package/dist/builtins/plan-approval.workflow.js.map +1 -1
  18. package/dist/builtins/plan-change.workflow.d.ts +301 -0
  19. package/dist/builtins/plan-change.workflow.js +256 -0
  20. package/dist/builtins/plan-change.workflow.js.map +1 -0
  21. package/dist/builtins/plan-presentation.js +2 -2
  22. package/dist/builtins/plan-presentation.js.map +1 -1
  23. package/dist/builtins/sanity-check.workflow.d.ts +5 -3
  24. package/dist/builtins/sanity-check.workflow.js +105 -21
  25. package/dist/builtins/sanity-check.workflow.js.map +1 -1
  26. package/dist/extension/decision-channels.d.ts +2 -2
  27. package/dist/extension/decision-channels.js +22 -28
  28. package/dist/extension/decision-channels.js.map +1 -1
  29. package/dist/extension/index.js +62 -33
  30. package/dist/extension/index.js.map +1 -1
  31. package/dist/extension/session-events.d.ts +2 -2
  32. package/dist/extension/widget.js +23 -3
  33. package/dist/extension/widget.js.map +1 -1
  34. package/dist/render/graph-render.js +1 -2
  35. package/dist/render/graph-render.js.map +1 -1
  36. package/dist/viewer/render.js +7 -6
  37. package/dist/viewer/render.js.map +1 -1
  38. package/dist/workflows/catalog.js +7 -2
  39. package/dist/workflows/catalog.js.map +1 -1
  40. package/dist/workflows/composition.js +8 -0
  41. package/dist/workflows/composition.js.map +1 -1
  42. package/dist/workflows/decision-presentation.d.ts +1 -1
  43. package/dist/workflows/decision-presentation.js +51 -38
  44. package/dist/workflows/decision-presentation.js.map +1 -1
  45. package/dist/workflows/engine.d.ts +2 -2
  46. package/dist/workflows/engine.js +14 -13
  47. package/dist/workflows/engine.js.map +1 -1
  48. package/dist/workflows/errors.d.ts +13 -0
  49. package/dist/workflows/errors.js +15 -0
  50. package/dist/workflows/errors.js.map +1 -1
  51. package/dist/workflows/human-decision.d.ts +16 -4
  52. package/dist/workflows/human-decision.js +175 -72
  53. package/dist/workflows/human-decision.js.map +1 -1
  54. package/dist/workflows/index.d.ts +2 -2
  55. package/dist/workflows/index.js +1 -1
  56. package/dist/workflows/index.js.map +1 -1
  57. package/dist/workflows/progress.d.ts +1 -0
  58. package/dist/workflows/progress.js +15 -3
  59. package/dist/workflows/progress.js.map +1 -1
  60. package/dist/workflows/schema.js +10 -0
  61. package/dist/workflows/schema.js.map +1 -1
  62. package/dist/workflows/store.js +5 -0
  63. package/dist/workflows/store.js.map +1 -1
  64. package/dist/workflows/types.d.ts +33 -45
  65. package/docs/HUMAN_DECISIONS.md +25 -35
  66. package/docs/HUMAN_DECISION_PRESENTATIONS.md +14 -24
  67. package/docs/MONITOR.md +5 -11
  68. package/docs/WORKFLOW_COMPOSITION.md +8 -7
  69. package/docs/plans/2026-08-21-plan-change-approval-policy-plan.md +322 -0
  70. package/docs/plans/2026-08-21-sanity-check-plan.md +202 -94
  71. package/docs/run-bundles.md +6 -6
  72. package/docs/workflows.md +26 -6
  73. package/examples/workflows/approved-plan.workflow.ts +19 -46
  74. package/herdr-plugin.toml +1 -1
  75. package/package.json +7 -7
  76. package/schemas/human-decision-accepted-v1.schema.json +15 -3
  77. package/schemas/human-decision-continuation-v1.schema.json +10 -1
  78. package/schemas/human-decision-delivery-v1.schema.json +8 -0
  79. package/schemas/human-decision-receipt-v1.schema.json +8 -0
  80. package/schemas/human-decision-request-v1.schema.json +24 -4
  81. package/skills/autoimplement/SKILL.md +27 -0
  82. package/skills/monitor/SKILL.md +31 -3
  83. package/skills/pi-workflows/SKILL.md +2 -1
  84. package/skills/sanity-check/SKILL.md +44 -0
  85. package/src/builtins/autoimplement.workflow.ts +19 -118
  86. package/src/builtins/catalog.ts +4 -4
  87. package/src/builtins/index.ts +11 -0
  88. package/src/builtins/monitor.workflow.ts +27 -150
  89. package/src/builtins/pi-agent-group.ts +1407 -0
  90. package/src/builtins/plan-approval.workflow.ts +157 -24
  91. package/src/builtins/plan-change.workflow.ts +321 -0
  92. package/src/builtins/plan-presentation.ts +2 -2
  93. package/src/builtins/sanity-check.workflow.ts +186 -41
  94. package/src/extension/decision-channels.ts +29 -59
  95. package/src/extension/index.ts +79 -41
  96. package/src/extension/session-events.ts +2 -2
  97. package/src/extension/widget.ts +24 -5
  98. package/src/render/graph-render.ts +1 -2
  99. package/src/viewer/render.ts +7 -6
  100. package/src/workflows/catalog.ts +7 -2
  101. package/src/workflows/composition.ts +9 -0
  102. package/src/workflows/decision-presentation.ts +56 -43
  103. package/src/workflows/engine.ts +17 -15
  104. package/src/workflows/errors.ts +24 -0
  105. package/src/workflows/human-decision.ts +218 -101
  106. package/src/workflows/index.ts +5 -11
  107. package/src/workflows/progress.ts +18 -3
  108. package/src/workflows/schema.ts +17 -0
  109. package/src/workflows/store.ts +5 -0
  110. package/src/workflows/types.ts +39 -56
  111. package/dist/builtins/sanity-check-session.d.ts +0 -17
  112. package/dist/builtins/sanity-check-session.js +0 -168
  113. package/dist/builtins/sanity-check-session.js.map +0 -1
  114. package/schemas/human-decision-accepted-v2.schema.json +0 -50
  115. package/schemas/human-decision-delivery-v2.schema.json +0 -36
  116. package/schemas/human-decision-receipt-v2.schema.json +0 -39
  117. package/schemas/human-decision-request-v2.schema.json +0 -69
  118. package/schemas/human-decision-resolution-v2.schema.json +0 -27
  119. package/src/builtins/sanity-check-session.ts +0 -205
@@ -60,6 +60,10 @@ export default defineWorkflow({
60
60
  approve: humanDecision({
61
61
  audience: "operator",
62
62
  choices: planChoices,
63
+ onTimeout: {
64
+ afterMs: 10 * 60_000,
65
+ response: { choice: "continue" },
66
+ },
63
67
  request: ({ outputs }) => ({
64
68
  title: "Approve the implementation plan",
65
69
  subject: outputs.propose,
@@ -106,6 +110,8 @@ type PlanDecision =
106
110
 
107
111
  `humanDecisionEdge()` requires one destination for every choice. A missing or extra case is a TypeScript error. Runtime validation applies the same choice and input contract before an answer can win.
108
112
 
113
+ `onTimeout` is optional. It supplies a positive duration and a response that satisfies the same choice contract. The request stores the resulting absolute deadline and validated response. When no human answer wins before the deadline, the decision recovery owner applies that response with `timeout` provenance. Omitting `onTimeout` keeps an indefinite wait. A request cannot combine `onTimeout` with a separate `expiresAt` value.
114
+
109
115
  ## Checkpoint behavior
110
116
 
111
117
  A human decision still has `nodeType: "checkpoint"`. The helper adds a human decision contract to that checkpoint.
@@ -118,9 +124,7 @@ When the engine reaches the node, it:
118
124
  4. asks the configured channels to deliver it; and
119
125
  5. parks the run in `waiting` state.
120
126
 
121
- When a valid answer is accepted, the continuation keeps the workflow's original input. The human answer becomes the checkpoint node's output in the continuation. Existing checkpoints keep their current behavior, where `/workflow answer` supplies the continuation input.
122
-
123
- This distinction preserves old workflow definitions and run bundles.
127
+ When a valid answer is accepted, the continuation keeps the workflow's original input. The human answer becomes the checkpoint node's output in the continuation. Ordinary checkpoints keep their current behavior, where `/workflow answer` supplies the continuation input.
124
128
 
125
129
  ## Request and response contracts
126
130
 
@@ -136,12 +140,12 @@ A new decision request contains:
136
140
  - the logical audience;
137
141
  - the complete choice contract;
138
142
  - the canonical request digest;
139
- - an optional expiry rule; and
143
+ - an optional absolute deadline and automatic response; and
140
144
  - the creation time.
141
145
 
142
- The presentation is an explicit display allowlist. A channel does not receive the subject and cannot infer operator text from it. The request digest binds the subject, visible presentation, title, revision, choices, and input prompts. Each choice has a stable ID and may have no input or one validated text input contract.
146
+ The presentation is an explicit display allowlist. A channel does not receive the subject and cannot infer operator text from it. The request digest binds the subject, visible presentation, title, revision, choices, input prompts, deadline, and automatic response. Each choice has a stable ID and may have no input or one validated text input contract.
143
147
 
144
- The former `body` form remains available for existing workflow definitions. It creates a v1 request and uses a deterministic readable compatibility formatter. Oversized historical bodies receive a bounded readable prefix and an explicit omission notice with the full body digest and size. V1 request bytes and digests do not change.
148
+ Human decisions use only `pi-workflows.human-decision-request.v1`. The former `body` request and all human-decision `v2` records are invalid. This is an alpha hard cutover: old waiting runs and decision state must be reset rather than migrated or reinterpreted.
145
149
 
146
150
  A submitted response contains:
147
151
 
@@ -268,9 +272,9 @@ Decision records live next to workflow run bundles under the pi-workflows state
268
272
  - channel settlement results; and
269
273
  - the continuation request and result.
270
274
 
271
- The resolution record uses a no-replace create. The first valid acceptance, cancellation, or expiry writer wins. Acceptance then materializes the matching accepted-answer detail. Cancellation or expiry materializes cancellation detail. A retry with the same response and idempotency key adopts the existing answer. A conflicting answer receives an `already decided` result.
275
+ The resolution record uses a no-replace create. A valid human answer or eligible timeout response claims the same immutable resolution. The saved result includes `human` or `timeout` provenance. A timeout result has no human actor or channel identity. Cancellation writes a terminal tombstone that takes precedence over automatic continuation. A retry adopts the existing matching result. A conflicting or late answer receives an `already decided` result.
272
276
 
273
- The continuation run ID is derived from the accepted decision ID. Recovery adopts an existing matching continuation or creates it once. A crash after answer acceptance cannot run the next workflow step twice. The continuation run carries a redacted receipt with the decision ID, request digest, gate node ID, choice, acceptance time, and answer digest. Actor, channel, event, and idempotency provenance stays only in the private decision records and never enters the run bundle or model context.
277
+ The continuation run ID is derived from the decision ID. Recovery adopts an existing matching continuation or creates it once. A crash after resolution cannot run the next workflow step twice. The continuation record and redacted receipt carry the resolution provenance, decision ID, request digest, gate node ID, choice, acceptance time, and answer digest. Human actor, channel, event, and idempotency details stay only in the private decision records and never enter the run bundle or model context.
274
278
 
275
279
  SQLite may index pending decisions and channel leases, but immutable decision files remain the source of truth. The index is disposable and rebuildable.
276
280
 
@@ -286,33 +290,19 @@ pi-workflows keeps solution choice, documentation, and implementation in separat
286
290
 
287
291
  `autoimplement` first finds the clear existing plan in its input, the conversation, or referenced canonical documents. It blocks when no clear plan exists. A caller can bypass discovery and autodoc only by supplying both the explicit plan and a `documentation` receipt whose plan digest matches it. A plan without that current-document evidence enters autodoc so the canonical documents are inspected and adopted or updated. The absence of a structured `plan` input never authorizes `autoplan`.
288
292
 
289
- If later implementation, verification, review, comments, or CI evidence invalidates the plan, autoimplement runs `autoplan`, sends the revised plan through `autodoc`, and then resumes implementation. An optional approval policy inserts `plan-approval` after the revised documentation.
293
+ If later implementation, verification, review, comments, or CI evidence invalidates the plan, Autoimplement enters the shared plan-change workflow. That workflow runs Autoplan, Autodoc, plan approval, and bounded exact-text replanning. Monitor uses the same workflow for each new repair plan and passes the selected plan into Autoimplement without a second decision.
290
294
 
291
295
  ## Reusable plan approval workflow
292
296
 
293
- pi-workflows ships a typed `plan-approval` workflow built on `humanDecision()`. Its input contains the documented plan, plan digest, audience, and display summary. It has three named exits:
294
-
295
- - `continue`, with the approval receipt;
296
- - `stop`, with the stop receipt; and
297
- - `replan`, with the exact instructions and receipt.
298
-
299
- A parent can include it without copying prompts or channel handling:
297
+ pi-workflows ships a typed `plan-approval` workflow built on `humanDecision()`. Its policy has three modes:
300
298
 
301
- ```typescript
302
- includes: {
303
- approval: includeWorkflow(planApproval, {
304
- input: ({ outputs }) => ({
305
- plan: outputs.devise.plan,
306
- planDigest: outputs.devise.planDigest,
307
- audience: "operator",
308
- }),
309
- }),
310
- },
311
- ```
299
+ - `auto` asks the configured audience and continues after the configured deadline. It defaults to audience `operator` and 10 minutes.
300
+ - `required` waits for an explicit human answer.
301
+ - `skip` creates no human decision and continues immediately.
312
302
 
313
- A `replan` exit returns to `autoplan` with the previous plan and the exact human instructions as new evidence. The revised plan passes through `autodoc`, receives a new digest, and enters a new approval decision. Step limits bound repeated replanning.
303
+ The workflow has `continue`, `stop`, and exact-text `replan` exits. Continue reports `human`, `timeout`, or `skipped` provenance. Stop and replan always require a human answer.
314
304
 
315
- Monitor repair composes `autoplan`, `autodoc`, optional `plan-approval`, `autoimplement`, and a fresh target check. Autoimplement can mount the same approval workflow after evidence-driven redesign. Existing behavior stays unchanged when no approval policy is present.
305
+ The internal plan-change workflow composes Autoplan, Autodoc, and plan approval once. It owns the replan count, plan digest, and positive revision. Autoimplement and Monitor include this workflow instead of copying approval routes. A plan supplied by the caller or already selected by Monitor bypasses another decision. Only a changed plan digest enters the gate.
316
306
 
317
307
  ## Recovery and cancellation
318
308
 
@@ -325,27 +315,27 @@ Recovery follows these rules:
325
315
  - ambiguous Telegram sends are not retried automatically;
326
316
  - duplicate channel updates are harmless;
327
317
  - stale responses are rejected;
328
- - one accepted response creates one continuation;
329
- - the winning channel dismisses any pending Pi dialog;
318
+ - one human or timeout response creates one continuation;
319
+ - the winning human answer or timeout policy dismisses any pending Pi dialog;
330
320
  - confirmed channel settlement is adopted without another remote call;
331
321
  - failed channel settlement has a bounded retry count and cannot create an unbounded record loop; and
332
322
  - cancellation resolves the owned waiting decision from durable state, even after restart, closes pending views, and prevents a later answer from continuing the run.
333
323
 
334
- A decision with no available channel remains waiting and reports the configuration problem. It does not silently continue or choose a default.
324
+ A required decision with no available channel remains waiting and reports the configuration problem. An automatic decision does not need a channel to apply its saved response after the deadline. A skipped plan policy creates no decision.
335
325
 
336
326
  ## Compatibility
337
327
 
338
- Ordinary checkpoints, their continuation input behavior, and existing run bundles remain unchanged. V2 requests, accepted records, receipts, resolutions, and multipart delivery records are additive. Older viewers ignore them. Updated viewers label a human decision as a checkpoint, show the readable presentation and its fingerprint, and keep the canonical subject separate. Private channel configuration and transport identifiers remain hidden.
328
+ This alpha change updates the current request, accepted-result, receipt, resolution, continuation, and snapshot contracts in place. Old active runs refuse resume through normal source and definition identity checks. There is no compatibility reader, migration, dual path, or new schema generation. Updated viewers label a human decision as a checkpoint, show its deadline and automatic action when present, and keep the canonical subject separate. Private channel configuration and transport identifiers remain hidden.
339
329
 
340
330
  The engine remains independent from Pi and Telegram. Core code owns decision contracts, validation, durable acceptance, and continuation. The Pi extension owns UI and channel lifecycle. The Telegram adapter owns Bot API translation. Workflow definitions own only the question, choices, audience, and routes.
341
331
 
342
332
  ## Contract impact
343
333
 
344
334
  - **Session state:** Pi records normal workflow messages and interactive decision results.
345
- - **Other persistent data:** additive decision records, a rebuildable private channel index, and private channel configuration.
335
+ - **Other persistent data:** decision requests and resolutions can carry a deadline, automatic response, and resolution provenance in the existing decision store. The private channel index remains rebuildable.
346
336
  - **Pi internals:** none.
347
337
  - **Public Pi API:** documented extension lifecycle and UI methods only.
348
- - **Public pi-workflows API:** typed human choices, `humanDecision()`, `humanDecisionEdge()`, the channel interface, and the `plan-approval` workflow.
338
+ - **Public pi-workflows API:** typed human choices, `humanDecision().onTimeout`, `humanDecisionEdge()`, the channel interface, the plan approval policy, and the shared plan-change workflow.
349
339
 
350
340
  ## Verification requirements
351
341
 
@@ -1,8 +1,8 @@
1
1
  # Human decision presentations
2
2
 
3
- This contract is implemented. New human decision requests separate their canonical
4
- subject from the complete readable message shown to an operator. Historical requests
5
- that use `body` remain compatible through a deterministic readable formatter.
3
+ This contract is implemented. Human decision requests separate their canonical
4
+ subject from the complete readable message shown to an operator. Human decisions use
5
+ one v1 contract with no legacy body form or parallel schema version.
6
6
  The implementation plan is in
7
7
  [the human decision presentations plan](plans/2026-08-19-human-decision-presentations-plan.md).
8
8
 
@@ -52,7 +52,7 @@ not see `subject` unless the workflow explicitly copies selected text into
52
52
 
53
53
  ## Request contract
54
54
 
55
- A new request uses `pi-workflows.human-decision-request.v2`. It contains:
55
+ A request uses `pi-workflows.human-decision-request.v1`. It contains:
56
56
 
57
57
  | Field | Required | Meaning |
58
58
  | -------------------- | -------- | ----------------------------------------------- |
@@ -255,25 +255,15 @@ It omits absent sections. It never serializes the plan object. `plan-approval`,
255
255
  `monitor`, `autoplan`, `autodoc`, and `autoimplement` reuse this presenter where
256
256
  they ask a person to approve a plan.
257
257
 
258
- ## Compatibility
258
+ ## Alpha cutover
259
259
 
260
- Current v1 requests remain immutable.
260
+ The human-decision request, accepted result, receipt, delivery, and resolution use one
261
+ v1 schema family. The current structured subject and explicit presentation contract
262
+ replaced the former body request and parallel v2 records in place.
261
263
 
262
- - A v1 string body becomes one readable paragraph at delivery time.
263
- - A v1 object body uses a deterministic compatibility formatter. It converts
264
- stable key order into readable labels and fields with sections and lists.
265
- - An oversized v1 body remains answerable. The formatter keeps a bounded readable
266
- prefix and adds an explicit omission notice with the full body digest and size.
267
- It never hides omitted content behind an ellipsis.
268
- - The compatibility formatter reads only the historical `body`, which was
269
- already the display field. It never reads a new structured subject.
270
- - V1 request bytes and digests do not change.
271
- - Pending and accepted v1 decisions continue to use v1 validation and digest
272
- rules.
273
- - New preferred authoring emits v2. The existing body form remains a deprecated
274
- compatibility overload until a separate removal is approved.
275
-
276
- No migration rewrites run bundles or decision records.
264
+ Old waiting runs and human-decision state are incompatible. The runtime fails with a
265
+ reset instruction instead of reading, migrating, or silently reinterpreting them.
266
+ There is no compatibility reader, dual path, alias, or migration.
277
267
 
278
268
  ## Privacy and security
279
269
 
@@ -311,10 +301,10 @@ npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
311
301
  npx -y @simpledoc/simpledoc check
312
302
  ```
313
303
 
314
- Tests must cover schema validation, canonical digests, readable legacy bodies,
304
+ Tests must cover schema validation, canonical digests, structured-value formatting,
315
305
  plan rendering, Pi and Telegram content parity, unsafe text, Unicode, multipart
316
- delivery, ambiguous sends, recovery, stale answers, viewer output, and proof
317
- that no channel serializes a subject as JSON.
306
+ delivery, ambiguous sends, recovery, stale answers, viewer output, incompatible-state
307
+ failure, and proof that no channel serializes a subject as JSON.
318
308
 
319
309
  ## Boundaries
320
310
 
package/docs/MONITOR.md CHANGED
@@ -38,7 +38,7 @@ The monitor checks a target, sends one status notification after every accepted
38
38
 
39
39
  `repair` must set `authorized: true`. It can constrain scope, repository, base branch, merge behavior, and other implementation constraints. Omitted `merge` means the repair can prepare but cannot merge a pull request; merging requires explicit `merge: true`. Without this object the monitor is observation-only. Repair authority does not permit a protected model, benchmark, credential, hardware, spending, or scope change.
40
40
 
41
- `repair.approval` is optional. It contains a logical `audience` and `maxReplans` from 1 through 20. When present, monitor sends the documented repair plan through the reusable human `plan-approval` workflow. Continue starts implementation, stop ends the repair truthfully, and replan preserves exact operator text before autoplan and autodoc run again. The model-facing workflow tool cannot approve the gate.
41
+ `repair.approval` uses `auto`, `required`, or `skip` mode. When omitted, Monitor uses `auto` with audience `operator`, a 10-minute timeout, and three allowed replans. Auto mode asks and then continues with the exact plan if no answer is accepted by the deadline. Required mode waits for an explicit human answer. Skip mode asks nothing. Continue starts implementation, stop ends the repair truthfully, and replan preserves exact operator text before the shared plan-change workflow runs again. The model-facing workflow tool cannot approve the gate.
42
42
 
43
43
  `reportWhen` is removed. The monitor always reports after every accepted check.
44
44
 
@@ -102,17 +102,11 @@ prepare
102
102
  ├─ continue → schedule → sleep → check
103
103
  └─ repair → repairGuard
104
104
  ├─ blocked → repairBlocked → repairReport → finish
105
- └─ initialDesign: autoplan
105
+ └─ planChange
106
106
  ├─ blocked → repairBlocked
107
- └─ documentation: autodoc
107
+ └─ ready → implementation: autoimplement
108
108
  ├─ blocked → repairBlocked
109
- ├─ no approval implementation: autoimplement
110
- └─ approval: plan-approval
111
- ├─ stop → repairBlocked
112
- ├─ replan → initialDesign
113
- └─ continue → implementation: autoimplement
114
- ├─ blocked → repairBlocked
115
- └─ completed → check
109
+ └─ completedcheck
116
110
  ```
117
111
 
118
112
  - `prepare` is a `compute` node that validates and applies input defaults.
@@ -122,7 +116,7 @@ prepare
122
116
  - `report` is a `notify` node that queues exactly one report.
123
117
  - `decide` is a `compute` node that applies the route and check safety limit.
124
118
  - `repairGuard` stops a repeated issue when a completed repair did not change its fingerprint or observed target state.
125
- - `initialDesign`, `documentation`, optional `approval`, and `implementation` are included workflows. Replan returns exact operator text to initialDesign. Autoimplement can enter nested `autoplan`, then autodoc, when later evidence requires redesign.
119
+ - `planChange` and `implementation` are included workflows. Plan change owns Autoplan, Autodoc, approval policy, and exact-text replanning. Autoimplement receives the selected plan without another decision and enters its own plan-change mount only when later evidence requires a changed plan.
126
120
  - `repairBlocked` and `repairReport` preserve a truthful blocked result and user notification.
127
121
  - `schedule` is a function `action` that publishes the next-check time.
128
122
  - `sleep` is the existing runtime-owned shell wait.
@@ -265,7 +265,7 @@ monitor
265
265
 
266
266
  `autoimplement` requires a clear existing plan, but the structured `plan` input is optional because the plan can already be in conversation context or canonical documentation. It blocks when it cannot find a clear plan. It skips autodoc when documentation is current and includes autodoc when documentation is missing or stale. The absence of `input.plan` never routes to initial autoplan.
267
267
 
268
- Autoimplement includes `autoplan` only as evidence-driven `redesign`. When implementation, verification, review, comments, or CI proves that the approach is wrong, the revised plan passes through autodoc before implementation resumes. Local bugs go to a fix step instead.
268
+ Autoimplement includes the shared plan-change workflow only as evidence-driven `redesign`. When implementation, verification, review, comments, or CI proves that the approach is wrong, the shared workflow runs Autoplan, Autodoc, the configured plan decision, and bounded replanning before implementation resumes. Local bugs go to a fix step instead. Existing supplied or discovered plans bypass the decision.
269
269
 
270
270
  Review rounds record findings at every severity from P0 through P2. P0 or P1 findings require another implementation and review round. A P2-only round can be addressed, but the workflow does not run the reviewer again solely because P2 work changed files.
271
271
 
@@ -281,16 +281,17 @@ Monitor remains observation-only unless its input explicitly authorizes mutation
281
281
 
282
282
  ```text
283
283
  check
284
- -> initialDesign: autoplan
285
- -> documentation: autodoc
286
- -> approval: plan-approval when requested
287
- -> replan: initialDesign
284
+ -> planChange
285
+ -> autoplan
286
+ -> autodoc
287
+ -> plan-approval
288
+ -> replan: autoplan
288
289
  -> implementation: autoimplement
289
- -> redesign: autoplan -> autodoc when needed
290
+ -> redesign: planChange when needed
290
291
  -> check
291
292
  ```
292
293
 
293
- The outer `autoplan` creates the first plan. Autodoc records it before implementation. An optional plan approval gate can continue, stop, or return exact replan instructions to autoplan. The inner redesign mount revises a plan only when new evidence invalidates it and records the revision through autodoc. The monitor checks the target again after implementation and does not trust a repair claim by itself.
294
+ The shared plan-change workflow creates, records, and gates each repair plan. Its default policy asks the `operator` audience and continues after 10 minutes without an answer. Required mode waits for an explicit answer. Skip mode creates no decision. Monitor passes the selected plan into Autoimplement, so Autoimplement does not ask about that digest again. A later Autoimplement redesign uses the same shared workflow for the changed digest. The monitor checks the target again after implementation and does not trust a repair claim by itself.
294
295
 
295
296
  A protected change to model choice, benchmark method, credentials, hardware, spending authority, or another user decision exits as blocked. The workflow never changes the protected part of the task silently.
296
297
 
@@ -0,0 +1,322 @@
1
+ ---
2
+ title: Plan Change Approval Policy Plan
3
+ author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
4
+ date: 2026-08-21
5
+ ---
6
+
7
+ # Plan change approval policy plan
8
+
9
+ ## Summary
10
+
11
+ Autoimplement and Monitor must use one shared workflow whenever they create or revise a plan. The shared workflow plans, records the plan in canonical documentation, asks for the configured human decision, and handles bounded replanning.
12
+
13
+ The default policy is autonomous. It asks the `operator` audience to continue, stop, or replan. If no valid answer is accepted within 10 minutes, it continues with the exact presented plan. A required policy waits for an explicit answer. A skip policy continues immediately without creating a human decision.
14
+
15
+ The timeout default belongs to the general `humanDecision()` contract. It must be durable, deterministic, bound to the exact request and plan digest, and separate from a human answer. Autoimplement and Monitor must not contain their own decision timers or copies of approval and replan logic.
16
+
17
+ ## Goals
18
+
19
+ The change must provide these results:
20
+
21
+ - Every plan newly created or changed by Autoimplement or Monitor passes through one shared plan-change workflow.
22
+ - Existing supplied or discovered plans do not receive a new decision.
23
+ - The default policy asks for a decision and continues after 10 minutes when there is no answer.
24
+ - Operators can require an explicit answer or skip the decision.
25
+ - Pi and Telegram show and settle the same durable decision.
26
+ - A human answer, timeout default, and cancellation cannot create more than one continuation.
27
+ - Cancellation remains immediate and terminal.
28
+ - Repeated entry with the same plan digest does not create another decision.
29
+ - Monitor does not cause Autoimplement to ask again for the plan that Monitor already selected.
30
+
31
+ ## Approval policy
32
+
33
+ Autoimplement and Monitor use the same policy shape:
34
+
35
+ ```ts
36
+ type PlanApprovalPolicy = {
37
+ mode: "auto" | "required" | "skip";
38
+ audience?: string;
39
+ timeoutMinutes?: number;
40
+ maxReplans?: number;
41
+ };
42
+ ```
43
+
44
+ The workflow input field remains `approval`. When it is absent, parsing supplies this policy:
45
+
46
+ ```json
47
+ {
48
+ "mode": "auto",
49
+ "audience": "operator",
50
+ "timeoutMinutes": 10,
51
+ "maxReplans": 3
52
+ }
53
+ ```
54
+
55
+ The modes have these meanings:
56
+
57
+ - `auto` creates a human decision. It continues with the presented plan when no valid answer is accepted before the deadline.
58
+ - `required` creates a human decision with no automatic response. It waits for `continue`, `stop`, or `replan`.
59
+ - `skip` creates no human decision and continues immediately.
60
+
61
+ `audience` defaults to `operator`. `timeoutMinutes` applies only to `auto` and defaults to 10. `maxReplans` defaults to 3 and bounds the exact-instructions replan loop. Parsers reject unknown fields, unsupported combinations, non-positive timeouts, and invalid replan limits.
62
+
63
+ The skills must map common requests as follows:
64
+
65
+ ```json
66
+ {
67
+ "approval": {
68
+ "mode": "required"
69
+ }
70
+ }
71
+ ```
72
+
73
+ Use this for requests such as “block on plan changes.”
74
+
75
+ ```json
76
+ {
77
+ "approval": {
78
+ "mode": "skip"
79
+ }
80
+ }
81
+ ```
82
+
83
+ Use this for requests such as “do not block on plan changes.” Omitting `approval` uses the 10-minute autonomous default.
84
+
85
+ ## Shared plan-change workflow
86
+
87
+ Add one internal finite workflow that owns this sequence:
88
+
89
+ ```text
90
+ autoplan
91
+ -> autodoc
92
+ -> plan-approval
93
+ -> continue -> ready
94
+ -> stop -> blocked
95
+ -> replan -> autoplan
96
+ ```
97
+
98
+ The workflow receives the planning problem, scope, constraints, repository and document context, previous plan, new evidence, and approval policy. It returns either:
99
+
100
+ - `ready`, with the selected plan, plan digest, canonical documents, revision, and decision provenance; or
101
+ - `blocked`, with the reason and evidence.
102
+
103
+ The workflow owns the replan count and passes exact operator instructions back to Autoplan. Each changed plan gets a new positive revision. The plan digest binds the plan, documentation result, approval request, and final output.
104
+
105
+ The workflow bypasses the human-decision node in `skip` mode but still records `skipped` as the plan selection provenance. It does not create a synthetic human receipt.
106
+
107
+ Promote the existing approved-plan composition pattern into this internal built-in workflow. Keep `plan-approval` as the low-level reusable decision workflow. Do not add a standalone `plan-approval` skill.
108
+
109
+ ## Human-decision timeout default
110
+
111
+ Extend the public `humanDecision()` definition with a general optional timeout response:
112
+
113
+ ```ts
114
+ humanDecision({
115
+ audience: "operator",
116
+ choices,
117
+ request,
118
+ onTimeout: {
119
+ afterMs: 10 * 60_000,
120
+ response: { choice: "continue" },
121
+ },
122
+ });
123
+ ```
124
+
125
+ The policy may also be derived from the node context so the shared plan-change workflow can use its parsed input. `afterMs` must be a positive finite duration. The response must satisfy the same typed choice and input contract as a human response. An absent `onTimeout` keeps the current indefinite wait.
126
+
127
+ When the engine creates the request, it computes and persists:
128
+
129
+ - the absolute expiry time;
130
+ - the validated default response;
131
+ - the request and presentation digests;
132
+ - the exact node and attempt identity; and
133
+ - the plan digest and revision already present in the decision subject.
134
+
135
+ The request digest includes the deadline policy and default response. A changed deadline, response, plan, or revision therefore creates a different request identity.
136
+
137
+ ## Resolution and provenance
138
+
139
+ A timeout default is an automatic workflow-policy resolution. It is not a human answer and must not use a human actor or channel identity.
140
+
141
+ The existing resolution record gains a distinct timeout-default outcome and provenance in place. Human acceptance, timeout default, and cancellation all compete for the same immutable resolution record. The first valid resolution wins. The accepted workflow output states whether the result came from:
142
+
143
+ - `human`;
144
+ - `timeout`;
145
+ - `skipped`; or
146
+ - `cancelled`, where a terminal record is exposed.
147
+
148
+ A human answer is valid only before the deadline and while no terminal resolution exists. A late answer cannot replace a timeout default. A timeout resolver must re-read an existing resolution and adopt it rather than create another result.
149
+
150
+ The plan-approval continue result carries the plan digest, decision revision, response, and resolution provenance. A timeout result carries no human actor. A skipped result carries no human-decision receipt.
151
+
152
+ ## Recovery and ownership
153
+
154
+ Use the existing one-second human-decision recovery loop. Do not add a service, daemon, controller, or second timer system.
155
+
156
+ For an unresolved request with an eligible timeout default, the current owner must:
157
+
158
+ 1. confirm that the parent run is still waiting at the same request;
159
+ 2. confirm that the request deadline has passed;
160
+ 3. confirm that the run and decision are not cancelled;
161
+ 4. atomically write or adopt the timeout-default resolution;
162
+ 5. create or adopt the deterministic continuation record;
163
+ 6. start the continuation only when the owning process can claim it; and
164
+ 7. settle every open Pi and Telegram view.
165
+
166
+ If no owner is active at the deadline, the request becomes eligible at that time. The next active owner resolves it and starts or adopts the continuation. The saved absolute deadline means a restart does not restart the 10-minute period.
167
+
168
+ Auto mode must continue after the deadline even when no decision channel is configured. Required mode remains waiting and reports the missing channel configuration. Skip mode does not use a channel.
169
+
170
+ ## Cancellation and races
171
+
172
+ Explicit workflow cancellation remains terminal. It must cancel the waiting decision, close channel views, and prevent a timeout continuation.
173
+
174
+ The cancellation path and timeout resolver must check the durable run cancellation state before and after claiming the decision resolution and before starting a continuation. If cancellation races with an automatic resolution, the cancelled run and its deterministic continuation must not execute more workflow nodes. A later recovery pass must not revive either run.
175
+
176
+ A human answer and timeout default use the immutable resolution as their race boundary. Tests must cover both winners at the deadline boundary and prove that only one continuation can exist.
177
+
178
+ ## Autoimplement integration
179
+
180
+ Autoimplement must use the shared plan-change workflow only for a changed plan produced by its internal redesign route.
181
+
182
+ These plans bypass the gate:
183
+
184
+ - an explicit plan supplied in the Autoimplement input;
185
+ - a current plan found by plan discovery; and
186
+ - a plan passed by Monitor after Monitor completed the shared plan-change workflow.
187
+
188
+ When new evidence routes Autoimplement to redesign, the shared workflow receives the current plan as `previousPlan` and the new issue as evidence. A changed ready plan returns to implementation. A stopped or exhausted plan change returns blocked. The same digest must not create another decision after resume or route re-entry.
189
+
190
+ Remove Autoimplement’s duplicate approval route, approval input mapping, and replan guard. Keep only policy parsing and the mapping into the shared workflow.
191
+
192
+ ## Monitor integration
193
+
194
+ Each Monitor repair that requires a new plan enters the shared plan-change workflow. This includes every exact-instructions replan requested by the operator.
195
+
196
+ A ready plan is passed to Autoimplement with its plan digest and canonical documentation state. Autoimplement treats it as selected and does not ask about it again. If Autoimplement later produces a changed plan because of implementation, review, or CI evidence, Autoimplement uses the shared workflow for that new digest.
197
+
198
+ Remove Monitor’s duplicate approval route, plan-approval input mapping, and replan guard. Keep the monitor repair authorization, no-progress protection, and post-repair observation unchanged.
199
+
200
+ ## Skills and examples
201
+
202
+ Update the Autoimplement and Monitor skills with complete one-shot calls for:
203
+
204
+ - omitted approval, which uses the 10-minute autonomous default;
205
+ - `approval.mode: "required"`; and
206
+ - `approval.mode: "skip"`.
207
+
208
+ The examples must retain the required task, scope, constraints, repository, base branch, merge authority, and other workflow-specific input. They must not show a model calling the answer action for a protected human decision.
209
+
210
+ Update the workflow authoring skill and examples to explain `humanDecision().onTimeout`, timeout provenance, and the rule that only a policy-defined response can run automatically.
211
+
212
+ ## Public contracts and persisted data
213
+
214
+ Change the current alpha contracts in place:
215
+
216
+ - add `onTimeout` to the public typed human-decision definition;
217
+ - add the persisted deadline and default response to the current human-decision request contract;
218
+ - add timeout-default provenance to the current accepted result, receipt, resolution, continuation, and channel settlement handling where it applies;
219
+ - add the shared `PlanApprovalPolicy` and plan-change input and output types;
220
+ - change Autoimplement and Monitor approval inputs to the shared policy; and
221
+ - keep existing camelCase JSON fields and current schema and contract identifiers.
222
+
223
+ Persisted request and resolution JSON remains under the current human-decision state root. Run bundles remain under the current run store. Add no migration reader, dual read, dual write, alias, feature flag, or new schema generation.
224
+
225
+ Increment the affected built-in revisions as one hard alpha cutover:
226
+
227
+ - Autoimplement revision 6 to 7;
228
+ - Monitor revision 7 to 8; and
229
+ - plan-approval revision 2 to 3.
230
+
231
+ Older active runs refuse resume through the existing source-change behavior. The new internal plan-change workflow does not need to be a user-facing catalog entry or skill.
232
+
233
+ ## Documentation
234
+
235
+ When implementation ships, update:
236
+
237
+ - `docs/HUMAN_DECISIONS.md` for timeout responses, provenance, recovery, races, and channel behavior;
238
+ - `docs/WORKFLOW_COMPOSITION.md` for the shared plan-change workflow;
239
+ - `docs/workflows.md` for the public API and Autoimplement behavior;
240
+ - `docs/MONITOR.md` for repair-plan decisions;
241
+ - `docs/run-bundles.md` for request, resolution, continuation, and snapshot fields;
242
+ - Autoimplement and Monitor skill text and examples; and
243
+ - package examples and generated layout fixtures affected by the graph change.
244
+
245
+ Keep the previous human-decision and composition plans unchanged as historical records.
246
+
247
+ ## Implementation steps
248
+
249
+ 1. Add and validate the typed `humanDecision().onTimeout` contract. Bind the validated default response and absolute deadline into request identity and persisted request data.
250
+ 2. Extend the human-decision store with one atomic timeout-default resolution operation. Preserve one immutable resolution and one deterministic continuation.
251
+ 3. Update extension decision recovery to resolve eligible defaults, recover after restart, settle channels, and start only the owned continuation. Keep cancellation checks around resolution and continuation claims.
252
+ 4. Update Pi and Telegram decision presentation and settlement so the deadline and automatic action are clear and a completed timeout closes pending views.
253
+ 5. Extend plan-approval with the shared policy and explicit human, timeout, and skipped provenance.
254
+ 6. Add the internal shared plan-change workflow with Autoplan, Autodoc, plan approval, and bounded exact-instructions replanning.
255
+ 7. Replace the duplicate Autoimplement planning approval path with the shared workflow and gate only changed internal redesign results.
256
+ 8. Replace the duplicate Monitor repair-plan approval path with the shared workflow and pass its selected plan to Autoimplement without another decision.
257
+ 9. Update public exports, current schemas, built-in revisions, skills, examples, documentation, and generated fixtures.
258
+ 10. Run all unit, integration, real-Pi, documentation, formatting, dependency-boundary, and package-resource checks.
259
+
260
+ ## Tests
261
+
262
+ Use fake clocks, temporary directories, and fake channels. Tests must not call a real model, use a real Telegram credential, or mutate a real remote.
263
+
264
+ Cover:
265
+
266
+ - `onTimeout` type and runtime validation;
267
+ - absent timeout behavior;
268
+ - the default 10-minute deadline;
269
+ - custom positive timeout values;
270
+ - invalid timeout and response combinations;
271
+ - auto, required, and skip policy parsing;
272
+ - human continue, stop, and exact-text replan;
273
+ - human-answer and timeout-default races with each winner;
274
+ - timeout provenance without a human actor;
275
+ - late answer rejection;
276
+ - immediate terminal cancellation before and during timeout resolution;
277
+ - one continuation after concurrent or repeated recovery;
278
+ - restart recovery before and after the deadline;
279
+ - auto mode with no configured channel;
280
+ - required mode with no configured channel;
281
+ - Pi and Telegram delivery, expiry text, and settlement;
282
+ - plan digest and positive revision binding;
283
+ - no duplicate decision for the same digest;
284
+ - bounded replans;
285
+ - Autoimplement changed-plan routing;
286
+ - Autoimplement supplied and discovered plan bypass;
287
+ - Monitor repair-plan routing;
288
+ - no second decision when Monitor passes its selected plan to Autoimplement;
289
+ - later Autoimplement redesign after Monitor selection;
290
+ - built-in revision refusal for old active runs;
291
+ - package skill and workflow discovery; and
292
+ - real-Pi continuation with a short fake-clock deadline and no real model.
293
+
294
+ ## Verification
295
+
296
+ Run:
297
+
298
+ ```bash
299
+ npm run check
300
+ npm run test:e2e
301
+ npx slophammer-ts@latest dry .
302
+ npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
303
+ npx -y @simpledoc/simpledoc check
304
+ git diff --check
305
+ ```
306
+
307
+ Inspect the complete public diff before each commit, push, or pull-request update. Verify GitHub CI and Pi Reviewer before delivery.
308
+
309
+ ## Boundaries
310
+
311
+ This work changes only the pi-workflows repository. It may change the workflow engine, extension decision recovery, built-in workflow composition, skills, tests, examples, fixtures, and documentation needed for this policy.
312
+
313
+ It must not:
314
+
315
+ - change Pi core or use undocumented Pi APIs;
316
+ - add a service, daemon, controller, scheduler, or persistence location;
317
+ - change external services, credentials, Telegram configuration, CI policy, or unrelated repositories;
318
+ - add a standalone plan-approval skill;
319
+ - queue, revive, or extend deferred successor turns;
320
+ - add compatibility readers, migrations, dual paths, new schema generations, aliases, or feature flags;
321
+ - merge the implementation pull request; or
322
+ - publish a package or release.