@osolmaz/pi-workflows 0.9.1 → 0.11.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 (147) hide show
  1. package/README.md +64 -19
  2. package/dist/builtins/autodoc.workflow.d.ts +58 -0
  3. package/dist/builtins/autodoc.workflow.js +266 -0
  4. package/dist/builtins/autodoc.workflow.js.map +1 -0
  5. package/dist/builtins/autoimplement.workflow.d.ts +353 -0
  6. package/dist/builtins/autoimplement.workflow.js +961 -0
  7. package/dist/builtins/autoimplement.workflow.js.map +1 -0
  8. package/dist/builtins/autoplan.workflow.d.ts +58 -0
  9. package/dist/builtins/autoplan.workflow.js +190 -0
  10. package/dist/builtins/autoplan.workflow.js.map +1 -0
  11. package/dist/builtins/catalog.js +9 -1
  12. package/dist/builtins/catalog.js.map +1 -1
  13. package/dist/builtins/index.d.ts +6 -0
  14. package/dist/builtins/index.js +7 -0
  15. package/dist/builtins/index.js.map +1 -0
  16. package/dist/builtins/monitor.workflow.d.ts +29 -3
  17. package/dist/builtins/monitor.workflow.js +325 -13
  18. package/dist/builtins/monitor.workflow.js.map +1 -1
  19. package/dist/builtins/plan-approval.workflow.d.ts +83 -0
  20. package/dist/builtins/plan-approval.workflow.js +148 -0
  21. package/dist/builtins/plan-approval.workflow.js.map +1 -0
  22. package/dist/builtins/plan-presentation.d.ts +7 -0
  23. package/dist/builtins/plan-presentation.js +44 -0
  24. package/dist/builtins/plan-presentation.js.map +1 -0
  25. package/dist/extension/decision-channels.d.ts +132 -0
  26. package/dist/extension/decision-channels.js +1082 -0
  27. package/dist/extension/decision-channels.js.map +1 -0
  28. package/dist/extension/index.d.ts +1 -0
  29. package/dist/extension/index.js +426 -11
  30. package/dist/extension/index.js.map +1 -1
  31. package/dist/extension/widget.js +26 -2
  32. package/dist/extension/widget.js.map +1 -1
  33. package/dist/extension/workflow-tool.d.ts +1 -37
  34. package/dist/extension/workflow-tool.js +1 -42
  35. package/dist/extension/workflow-tool.js.map +1 -1
  36. package/dist/host/rpc-bridge.js +4 -21
  37. package/dist/host/rpc-bridge.js.map +1 -1
  38. package/dist/render/graph-render.js +46 -10
  39. package/dist/render/graph-render.js.map +1 -1
  40. package/dist/viewer/render.js +36 -2
  41. package/dist/viewer/render.js.map +1 -1
  42. package/dist/workflows/catalog.d.ts +1 -0
  43. package/dist/workflows/catalog.js +6 -0
  44. package/dist/workflows/catalog.js.map +1 -1
  45. package/dist/workflows/composition.d.ts +45 -0
  46. package/dist/workflows/composition.js +485 -0
  47. package/dist/workflows/composition.js.map +1 -0
  48. package/dist/workflows/decision-presentation.d.ts +18 -0
  49. package/dist/workflows/decision-presentation.js +417 -0
  50. package/dist/workflows/decision-presentation.js.map +1 -0
  51. package/dist/workflows/decision.d.ts +11 -5
  52. package/dist/workflows/decision.js.map +1 -1
  53. package/dist/workflows/definition.d.ts +22 -3
  54. package/dist/workflows/definition.js +46 -3
  55. package/dist/workflows/definition.js.map +1 -1
  56. package/dist/workflows/engine.d.ts +2 -1
  57. package/dist/workflows/engine.js +199 -18
  58. package/dist/workflows/engine.js.map +1 -1
  59. package/dist/workflows/graph.js +8 -6
  60. package/dist/workflows/graph.js.map +1 -1
  61. package/dist/workflows/human-decision.d.ts +87 -0
  62. package/dist/workflows/human-decision.js +583 -0
  63. package/dist/workflows/human-decision.js.map +1 -0
  64. package/dist/workflows/index.d.ts +5 -2
  65. package/dist/workflows/index.js +4 -1
  66. package/dist/workflows/index.js.map +1 -1
  67. package/dist/workflows/loader.d.ts +5 -4
  68. package/dist/workflows/loader.js +118 -18
  69. package/dist/workflows/loader.js.map +1 -1
  70. package/dist/workflows/schema.d.ts +3 -1
  71. package/dist/workflows/schema.js +64 -2
  72. package/dist/workflows/schema.js.map +1 -1
  73. package/dist/workflows/store.js +39 -2
  74. package/dist/workflows/store.js.map +1 -1
  75. package/dist/workflows/tool-input.d.ts +72 -0
  76. package/dist/workflows/tool-input.js +141 -0
  77. package/dist/workflows/tool-input.js.map +1 -0
  78. package/dist/workflows/types.d.ts +300 -2
  79. package/docs/CONTROLLERS.md +1 -1
  80. package/docs/DESIGN_PHILOSOPHY.md +1 -1
  81. package/docs/HUMAN_DECISIONS.md +371 -0
  82. package/docs/HUMAN_DECISION_PRESENTATIONS.md +323 -0
  83. package/docs/MONITOR.md +46 -18
  84. package/docs/WORKFLOW_COMPOSITION.md +333 -0
  85. package/docs/plans/2026-08-19-human-decision-gates-plan.md +296 -0
  86. package/docs/plans/2026-08-19-human-decision-presentations-plan.md +172 -0
  87. package/docs/plans/2026-08-19-provider-compatible-workflow-tool-schema-plan.md +45 -0
  88. package/docs/plans/2026-08-19-workflow-composition-plan.md +308 -0
  89. package/docs/run-bundles.md +45 -10
  90. package/docs/workflows.md +106 -15
  91. package/examples/workflows/approved-plan.workflow.ts +58 -0
  92. package/examples/workflows/autoimplement.workflow.ts +1 -92
  93. package/examples/workflows/autoplan.workflow.ts +1 -0
  94. package/examples/workflows/human-decision.workflow.ts +62 -0
  95. package/herdr-plugin.toml +1 -1
  96. package/package.json +6 -1
  97. package/schemas/decision-presentation-v1.schema.json +83 -0
  98. package/schemas/human-decision-accepted-v1.schema.json +44 -0
  99. package/schemas/human-decision-accepted-v2.schema.json +50 -0
  100. package/schemas/human-decision-answer-attempt-v1.schema.json +37 -0
  101. package/schemas/human-decision-cancellation-v1.schema.json +15 -0
  102. package/schemas/human-decision-continuation-v1.schema.json +16 -0
  103. package/schemas/human-decision-delivery-v1.schema.json +28 -0
  104. package/schemas/human-decision-delivery-v2.schema.json +36 -0
  105. package/schemas/human-decision-receipt-v1.schema.json +33 -0
  106. package/schemas/human-decision-receipt-v2.schema.json +39 -0
  107. package/schemas/human-decision-request-v1.schema.json +59 -0
  108. package/schemas/human-decision-request-v2.schema.json +69 -0
  109. package/schemas/human-decision-resolution-v1.schema.json +27 -0
  110. package/schemas/human-decision-resolution-v2.schema.json +27 -0
  111. package/schemas/human-decision-settlement-v1.schema.json +28 -0
  112. package/skills/autodoc/SKILL.md +43 -0
  113. package/skills/autoimplement/SKILL.md +58 -0
  114. package/skills/autoimplement/agents/openai.yaml +4 -0
  115. package/skills/autoplan/SKILL.md +25 -0
  116. package/skills/monitor/SKILL.md +8 -1
  117. package/skills/pi-workflows/SKILL.md +5 -3
  118. package/src/builtins/autodoc.workflow.ts +325 -0
  119. package/src/builtins/autoimplement.workflow.ts +1123 -0
  120. package/src/builtins/autoplan.workflow.ts +231 -0
  121. package/src/builtins/catalog.ts +9 -1
  122. package/src/builtins/index.ts +32 -0
  123. package/src/builtins/monitor.workflow.ts +396 -15
  124. package/src/builtins/plan-approval.workflow.ts +190 -0
  125. package/src/builtins/plan-presentation.ts +57 -0
  126. package/src/extension/decision-channels.ts +1533 -0
  127. package/src/extension/index.ts +499 -9
  128. package/src/extension/widget.ts +39 -2
  129. package/src/extension/workflow-tool.ts +5 -81
  130. package/src/host/rpc-bridge.ts +7 -30
  131. package/src/render/graph-render.ts +54 -5
  132. package/src/viewer/render.ts +56 -3
  133. package/src/workflows/catalog.ts +7 -0
  134. package/src/workflows/composition.ts +642 -0
  135. package/src/workflows/decision-presentation.ts +501 -0
  136. package/src/workflows/decision.ts +12 -5
  137. package/src/workflows/definition.ts +118 -8
  138. package/src/workflows/engine.ts +257 -20
  139. package/src/workflows/graph.ts +8 -6
  140. package/src/workflows/human-decision.ts +819 -0
  141. package/src/workflows/index.ts +89 -0
  142. package/src/workflows/loader.ts +186 -18
  143. package/src/workflows/schema.ts +79 -2
  144. package/src/workflows/store.ts +45 -2
  145. package/src/workflows/tool-input.ts +202 -0
  146. package/src/workflows/types.ts +374 -2
  147. package/examples/workflows/elegant-solution.workflow.ts +0 -95
@@ -0,0 +1,296 @@
1
+ ---
2
+ title: Add reusable human decision gates
3
+ author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
4
+ date: 2026-08-19
5
+ ---
6
+
7
+ # Add reusable human decision gates
8
+
9
+ Pi Workflows must let any workflow stop after a proposal, ask the operator in Pi and Telegram, and continue from a verified human choice. A `replan` choice must collect the operator's exact alternative text, send it back to planning, and present the revised plan for another decision.
10
+
11
+ The canonical behavior and public contracts are in [Human decisions](../HUMAN_DECISIONS.md). This plan covers the practical implementation in Pi Workflows without changing Pi core or adding a persistent operating-system service.
12
+
13
+ ## Outcome
14
+
15
+ Workflow authors get three reusable levels:
16
+
17
+ 1. `humanDecision()` adds a custom typed decision gate to any graph.
18
+ 2. The included `plan-approval` workflow provides standard `continue`, `stop`, and `replan` exits.
19
+ 3. The standalone `autodoc` workflow records selected plans and is composed into autoimplement and monitor repair.
20
+
21
+ Pi and Telegram implement one channel interface. Workflows address a named audience and contain no transport details. The first valid human response wins, stale and conflicting responses fail, and crash recovery creates one continuation.
22
+
23
+ ## Scope
24
+
25
+ ### Planning workflow contracts
26
+
27
+ - Add a standalone typed `autodoc` built-in with `ready` and `blocked` exits.
28
+ - Make autodoc locate an already selected plan, update or adopt canonical documents, run documentation checks, and return a documented-plan record.
29
+ - Keep autodoc separate from solution selection and implementation.
30
+ - Make autoimplement locate and record an existing plan from input, conversation context, or referenced documents.
31
+ - Block autoimplement when no clear plan exists.
32
+ - Skip autodoc only when plan discovery verifies current canonical documents or an explicit documentation receipt has a matching plan digest.
33
+ - Route every evidence-driven revision through `autoplan`, then `autodoc`, before implementation resumes.
34
+ - Never run initial autoplan only because the structured plan input is absent.
35
+
36
+ ### Public authoring API
37
+
38
+ - Add `defineHumanChoices()`, `choice()`, and `textInput()`.
39
+ - Add `humanDecision()` as a typed checkpoint helper.
40
+ - Add `humanDecisionEdge()` with exhaustive TypeScript cases.
41
+ - Infer a discriminated response union from the choice contract.
42
+ - Validate the request and selected choice at runtime, including any choice input.
43
+ - Export a reusable typed `plan-approval` workflow.
44
+
45
+ ### Engine and storage
46
+
47
+ - Add a human decision contract to checkpoint definitions and snapshots.
48
+ - Preserve the original workflow input when a human decision continues.
49
+ - Make the accepted response the gate's output in the continuation.
50
+ - Keep legacy checkpoint continuation unchanged.
51
+ - Add immutable records for requests, deliveries, answer attempts, accepted answers, settlement results, and continuation results.
52
+ - Accept the first valid response with a no-replace write.
53
+ - Adopt identical retries and reject conflicting responses.
54
+ - Derive one continuation identity from the accepted decision.
55
+ - Reject stale responses by decision ID and canonical request digest.
56
+ - Rebuild the pending-decision index from immutable records.
57
+
58
+ ### Host and channels
59
+
60
+ - Add a `HumanDecisionChannel` interface outside the engine layer.
61
+ - Add a Pi channel using documented TUI APIs.
62
+ - Add a Telegram channel using the Bot API and private profiles.
63
+ - Add named audience resolution and private channel configuration.
64
+ - Add a setup command that writes private configuration with mode `600`, references an existing mode-`0600` token file, and verifies the Telegram bot without reading the token into a prompt or printing it.
65
+ - Keep Telegram optional. Pi Workflows must start and run normal workflows without Telegram configuration.
66
+ - Use one leased long-poll owner per Telegram profile across active Pi processes.
67
+
68
+ ### Documentation and display
69
+
70
+ - Add the authoring API to `docs/workflows.md`.
71
+ - Add human decision state to `docs/run-bundles.md`.
72
+ - Add channel setup and recovery instructions.
73
+ - Add a custom gate example and a composed plan approval example.
74
+ - Show pending and accepted choices in TypeScript and Rust viewers.
75
+ - Redact credentials, chat IDs, user IDs, message IDs, and private profile names from model-visible and public presentation.
76
+
77
+ ## Non-goals
78
+
79
+ - Change Pi core or use undocumented Pi APIs.
80
+ - Create a Telegram bot or copy a token without explicit source and destination approval.
81
+ - Add Telegram-specific logic to monitor, autoimplement, autoplan, or user workflows.
82
+ - Run an always-on service when no Pi process is active.
83
+ - Promise exactly-once Telegram message creation after an ambiguous Bot API response.
84
+ - Add arbitrary forms in the first release. Choice buttons and one text input cover the required flows.
85
+ - Change existing checkpoints or historical run bundles.
86
+ - Enable human approval by default in existing built-in workflows.
87
+
88
+ ## Design decisions
89
+
90
+ ### Extend checkpoints instead of the engine graph
91
+
92
+ `humanDecision()` returns `nodeType: "checkpoint"`. The engine already knows how to park a run and create a continuation. The added contract defines who can answer, how to validate the answer, and how to deliver the request.
93
+
94
+ The new behavior applies only when a checkpoint carries a human decision contract. Ordinary checkpoints keep using the continuation input supplied by `/workflow answer`.
95
+
96
+ ### Separate workflow intent from delivery
97
+
98
+ A workflow declares its question and choices together with the subject and audience. Private host configuration maps the audience to Pi, Telegram, or later channels. This keeps workflow files portable and lets one Telegram adapter serve every workflow.
99
+
100
+ ### Verify the answer source in the host
101
+
102
+ The model-facing workflow tool is not a human channel. It must reject `answer` while a human decision is waiting. The Pi UI path and Telegram adapter call an internal submission API that assigns the source and actor. Callers cannot supply a trusted source label themselves.
103
+
104
+ ### Keep exact response text
105
+
106
+ The Telegram adapter accepts replan text only as a reply to the exact `ForceReply` prompt created for the pending decision and choice. It validates the numeric user and chat IDs and preserves the received text. Planning receives that string without a choice-classification model call.
107
+
108
+ ### Make answer acceptance exact
109
+
110
+ Channel delivery can be retried or fail independently. Decision acceptance is one atomic no-replace operation. The accepted response and deterministic continuation identity prevent two channels from starting two continuations.
111
+
112
+ ### Handle Telegram delivery limits honestly
113
+
114
+ Telegram Bot API `sendMessage` has no client idempotency key. The adapter writes an intent before sending. A confirmed response records the Telegram message ID. A timeout after a possible send becomes `unknown` and is not retried automatically. This prevents an automatic duplicate at the cost of a possibly missing Telegram notification. Pi remains available, and an operator can request a new delivery attempt.
115
+
116
+ ## Data model
117
+
118
+ Add versioned JSON schemas for:
119
+
120
+ - `human-decision-request-v1`;
121
+ - `human-decision-delivery-v1`;
122
+ - `human-decision-answer-attempt-v1`;
123
+ - `human-decision-accepted-v1`;
124
+ - `human-decision-cancellation-v1`;
125
+ - `human-decision-resolution-v1`;
126
+ - `human-decision-receipt-v1` for redacted continuation state;
127
+ - `human-decision-settlement-v1`; and
128
+ - `human-decision-continuation-v1`.
129
+
130
+ Use canonical JSON for request and answer digests. Keep credential values and private channel configuration outside every schema.
131
+
132
+ Store decision records under the existing workflow state root:
133
+
134
+ ```text
135
+ decisions/<decision-id>/
136
+ request.json
137
+ deliveries/<channel>/<attempt-id>.json
138
+ answers/<attempt-id>.json
139
+ resolution.json
140
+ accepted.json
141
+ cancelled.json
142
+ settlements/<channel>/<attempt-id>.json
143
+ continuation.json
144
+ ```
145
+
146
+ All final records use no-replace creation and adopt only byte-identical content. Temporary files remain in the same filesystem so rename and exclusive-create rules stay valid.
147
+
148
+ A private SQLite index may track pending decisions, channel leases, Telegram update offsets, opaque callback IDs, and known message IDs. It is a projection and can be deleted and rebuilt from immutable records plus current private channel state.
149
+
150
+ ## Work plan
151
+
152
+ ### Contracts and compile-time API
153
+
154
+ - Add generic choice and input types.
155
+ - Build the response union from choice keys.
156
+ - Add `humanDecision()` and exhaustive `humanDecisionEdge()`.
157
+ - Extend workflow validation and definition snapshots.
158
+ - Add compile-time fixtures for every invalid route shape.
159
+
160
+ ### Durable decision storage
161
+
162
+ - Add schemas and generated types.
163
+ - Add canonical request and response digest helpers.
164
+ - Add the decision store with no-replace writes and identical-byte adoption.
165
+ - Add a rebuildable pending-decision projection.
166
+ - Test concurrent writers and partial records.
167
+
168
+ ### Safe human decision continuation
169
+
170
+ - Detect a human decision at `continueRun()`.
171
+ - Validate the accepted response through the node contract.
172
+ - Preserve the parent's original workflow input.
173
+ - Expose the response as the checkpoint output.
174
+ - derive and adopt the continuation identity; and
175
+ - leave the legacy checkpoint path unchanged.
176
+
177
+ Test crashes before acceptance, after acceptance, during continuation creation, and after continuation completion.
178
+
179
+ ### Channel management
180
+
181
+ - Add the channel interface and audience resolver in the extension layer.
182
+ - Start channels during documented session lifecycle hooks.
183
+ - Stop them idempotently during shutdown and reload.
184
+ - Fan out one decision to all audience channels.
185
+ - Send every channel response through the same acceptance method.
186
+ - Settle all confirmed channel messages after acceptance, cancellation, or expiry.
187
+
188
+ ### Pi channel
189
+
190
+ - Render pending decisions in the workflow widget.
191
+ - Open a TUI choice view from the checkpoint notification and workflow command.
192
+ - Open a text editor for text choices.
193
+ - Mark answers from this path as Pi interactive answers.
194
+ - Reject model-tool answers for human decisions.
195
+ - Restore pending decisions after restart.
196
+
197
+ ### Telegram channel
198
+
199
+ - Add private profile and credential-reference parsing.
200
+ - Add a configuration command that accepts a private token-file reference and performs `getMe` verification without copying or displaying the token.
201
+ - Add the shared long-poll lease and durable update offset.
202
+ - Render choice buttons with opaque callback IDs.
203
+ - Implement the bound `ForceReply` text flow.
204
+ - Verify every bound reply field, including the actor, chat, reply message, decision ID, selected choice, and request digest.
205
+ - Record every delivery outcome without leaking request URLs or tokens.
206
+ - Edit confirmed messages after a decision settles.
207
+
208
+ Use a fake Bot API in all automated tests. Do not require a real token or network access.
209
+
210
+ ### Autodoc and autoimplement
211
+
212
+ - Define the typed standalone `autodoc` built-in workflow.
213
+ - Add a durable documented-plan result shared by autodoc, autoimplement, and monitor.
214
+ - Replace autoimplement's missing-input redesign route with existing-plan discovery.
215
+ - Block when discovery finds no clear plan.
216
+ - Skip autodoc for current canonical documents.
217
+ - Route undocumented and revised plans through autodoc.
218
+ - Keep autoplan only on evidence-driven redesign edges.
219
+
220
+ ### Reusable plan approval
221
+
222
+ - Define the typed `plan-approval` built-in workflow.
223
+ - Return named `continue`, `stop`, and `replan` exits.
224
+ - Include the accepted decision receipt in every exit.
225
+ - Return exact instructions from `replan`.
226
+ - Add an example that loops through `autoplan`, plan approval, and implementation.
227
+ - Keep monitor and autoimplement behavior unchanged unless their input requests the approval workflow.
228
+
229
+ ### Viewers and documentation
230
+
231
+ - Display the audience label, choice labels, waiting state, and accepted choice.
232
+ - Keep actor and transport details out of public or model-visible views.
233
+ - Document setup, authoring, cancellation, recovery, Telegram delivery ambiguity, and the no-service limitation.
234
+ - Update the plan with meaningful implementation departures.
235
+
236
+ ## Implementation note
237
+
238
+ The setup command uses an existing mode-`0600` token file instead of collecting and copying a token. This keeps credential ownership with the existing private store and follows the no-copy credential boundary while still verifying the bot with `getMe`.
239
+
240
+ ## Acceptance criteria
241
+
242
+ - Autodoc runs alone, adopts current documents, updates stale documents, and blocks without a selected plan.
243
+ - Autoimplement uses the plan already present in context or documentation and never devises merely because `input.plan` is absent.
244
+ - Every revised plan is documented before implementation resumes.
245
+ - A TypeScript workflow can define custom human choices and route them exhaustively.
246
+ - A text choice returns the exact submitted text.
247
+ - The engine and run snapshot still identify the node as a checkpoint.
248
+ - The workflow tool cannot answer a protected human decision.
249
+ - Pi and Telegram can receive the same decision through one audience profile.
250
+ - The first concurrent valid answer wins and creates one continuation.
251
+ - An identical retry is adopted; a conflicting or stale answer is rejected.
252
+ - The original workflow input survives a human-decision continuation.
253
+ - Old checkpoints and old bundles pass their existing tests unchanged.
254
+ - The Telegram adapter accepts text only from the bound reply and approved numeric actor.
255
+ - A missing Telegram profile leaves Pi decisions usable.
256
+ - A Telegram timeout does not cause an automatic duplicate send.
257
+ - Restart recovery adopts confirmed deliveries and accepted continuations.
258
+ - The standard plan approval workflow loops through replan and requires approval for the revised digest.
259
+ - No credential or private channel value appears in source, fixtures, run bundles, logs, screenshots, or model-visible output.
260
+
261
+ ## Verification
262
+
263
+ Run focused tests while implementing:
264
+
265
+ ```bash
266
+ npx vitest run test/human-decision-api.test.ts test/human-decision-store.test.ts
267
+ npx vitest run test/human-decision-engine.test.ts test/run-resume.test.ts
268
+ npx vitest run test/pi-decision-channel.test.ts test/telegram-decision-channel.test.ts
269
+ npx vitest run test/plan-approval.test.ts test/composition.test.ts
270
+ ```
271
+
272
+ Run all repository gates before review:
273
+
274
+ ```bash
275
+ npm run check
276
+ npm run test:e2e
277
+ cargo test --manifest-path tui/Cargo.toml
278
+ npx slophammer-ts@latest dry .
279
+ npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
280
+ npx -y @simpledoc/simpledoc check
281
+ git diff --check
282
+ ```
283
+
284
+ Run Pi Reviewer against the pushed branch. Fix every P0 and P1 finding and rerun it. Address valid P2 findings, but do not rerun review solely because of a P2-only change. Open or update a pull request and leave it unmerged unless merge is separately authorized.
285
+
286
+ ## Release
287
+
288
+ This work adds compatible public APIs and additive persisted records. Release it as the next minor pre-1.0 version after the compatibility checks, viewer tests, real-Pi tests, and recovery tests pass.
289
+
290
+ ## Contract impact
291
+
292
+ - **Session state:** normal workflow messages and interactive decision results.
293
+ - **Other persistent data:** additive decision records, a rebuildable private channel index, and private channel configuration.
294
+ - **Pi internals:** none.
295
+ - **Public Pi API:** documented extension lifecycle plus command and UI methods only.
296
+ - **Public Pi Workflows API:** typed human choices, `humanDecision()`, `humanDecisionEdge()`, the channel interface, and the `plan-approval` workflow.
@@ -0,0 +1,172 @@
1
+ ---
2
+ title: Add readable human decision presentations
3
+ author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
4
+ date: 2026-08-19
5
+ ---
6
+
7
+ # Add readable human decision presentations
8
+
9
+ Pi Workflows currently sends a structured human decision body to Telegram with
10
+ `JSON.stringify()`. A plan approval therefore reaches the operator as machine
11
+ JSON. Pi also shows only the decision title in its basic selection prompt.
12
+
13
+ The operator must receive clear text in every channel. Structured data must
14
+ remain the durable source for validation, routing, and audit evidence. The
15
+ [human decision presentations specification](../HUMAN_DECISION_PRESENTATIONS.md)
16
+ defines the selected contract.
17
+
18
+ ## Outcome
19
+
20
+ Add one versioned, channel-independent presentation format beside the canonical
21
+ subject. Pi and Telegram render the same complete content with their own native
22
+ layout. The visible presentation and machine subject are both bound to the
23
+ accepted decision.
24
+
25
+ The design uses the existing checkpoint engine, decision store, channel
26
+ interface, and documented Pi extension APIs. It does not require Pi core,
27
+ another service, or a Telegram change.
28
+
29
+ ## Scope
30
+
31
+ - Add `DecisionPresentation v1` and `HumanDecisionRequest v2` schemas and types.
32
+ - Add deterministic presentation normalization and digests.
33
+ - Keep the current typed subject as machine truth.
34
+ - Require an explicit presentation for new structured subjects.
35
+ - Add a reusable plan presenter.
36
+ - Render complete readable messages in Pi and Telegram.
37
+ - Add durable multipart delivery evidence and safe recovery.
38
+ - Update TypeScript and Rust viewers.
39
+ - Preserve v1 definitions and immutable run bundles.
40
+
41
+ ## Non-goals
42
+
43
+ - Do not modify Pi core or use undocumented Pi APIs.
44
+ - Do not change Telegram or create a delivery relay.
45
+ - Do not add a template language or general rich text system.
46
+ - Do not derive new operator text from arbitrary v2 subject data.
47
+ - Do not rewrite v1 requests, accepted answers, or run bundles.
48
+ - Do not store Telegram identifiers or credentials in public durable records.
49
+
50
+ ## Implementation
51
+
52
+ ### 1. Add the durable contracts
53
+
54
+ Create schemas and TypeScript types for the presentation, v2 request, and
55
+ multipart delivery evidence. Keep all v1 schemas and readers unchanged.
56
+
57
+ Generate and validate canonical examples. Reject unknown blocks, object-valued
58
+ display fields, missing summaries, missing presentations, and excessive
59
+ content.
60
+
61
+ ### 2. Add deterministic normalization
62
+
63
+ Add a workflow-core module that validates the flat presentation blocks,
64
+ normalizes line endings, enforces the specification limits, and computes the
65
+ presentation digest. Keep it independent of Pi and Telegram.
66
+
67
+ Add a v1 compatibility presenter. A string body becomes a paragraph. An object
68
+ body becomes readable sections, labels, fields, and lists in stable key order.
69
+ The adapter reads only the historical display body.
70
+
71
+ ### 3. Update the authoring API and request identity
72
+
73
+ Add the preferred `subject` plus `presentation` request form. Keep the current
74
+ `body` form as a deprecated compatibility overload. The preferred form fails
75
+ validation when either value is missing.
76
+
77
+ For v2, compute separate subject and presentation digests and bind the request
78
+ revision, subject, visible presentation, choices, and input prompts into the
79
+ overall request digest. Select the v1 or v2 digest algorithm from the stored
80
+ request schema.
81
+
82
+ ### 4. Add the plan presenter
83
+
84
+ Create one reusable presenter for plan approvals. Derive its summary and
85
+ sections from the same typed plan stored in the subject. Use it in the built-in
86
+ plan approval path and composed workflows. Do not serialize missing or unknown
87
+ fields.
88
+
89
+ ### 5. Narrow the channel boundary
90
+
91
+ Normalize the stored request before channel dispatch. Give each channel only
92
+ the normalized presentation and typed choices. Remove direct channel access to
93
+ the subject and remove the `JSON.stringify()` fallback.
94
+
95
+ ### 6. Render complete Telegram messages
96
+
97
+ Render plain text with no parse mode. Precompute all message parts, split at
98
+ readable boundaries, add deterministic part numbers and a short fingerprint,
99
+ and put buttons on the final part only.
100
+
101
+ Write intent before sending and one durable content receipt for each confirmed
102
+ part. Keep remote IDs in the private projection. Stop automatic delivery after
103
+ an ambiguous send. Never clip approval content.
104
+
105
+ ### 7. Add the full Pi decision view
106
+
107
+ Use documented `ctx.ui.custom()` APIs and existing TUI components to show the
108
+ complete presentation, fingerprint, choices, and text prompt. Support wrapping,
109
+ scrolling, resizing, cancellation, and theme changes. Preserve the abort signal
110
+ that closes Pi when another channel wins.
111
+
112
+ ### 8. Update recovery and viewers
113
+
114
+ Recover confirmed multipart sends without duplication. Resume only when the
115
+ next part is provably unsent. Keep one accepted answer and one continuation.
116
+
117
+ Update the TypeScript and Rust viewers to show the human presentation by
118
+ default and machine data as separate detail. Continue to render v1 through the
119
+ compatibility presenter without changing stored bytes.
120
+
121
+ ### 9. Verify compatibility and failure handling
122
+
123
+ Add tests for:
124
+
125
+ - presentation schemas, normalization, bounds, Unicode, and canonical digests;
126
+ - subject and presentation changes that reject stale answers;
127
+ - old definitions, pending v1 decisions, and accepted v1 decisions;
128
+ - complete and sparse plan presentations;
129
+ - Pi and Telegram content parity;
130
+ - unsafe markup-like text and terminal control characters;
131
+ - single-part and multipart Telegram messages;
132
+ - partial, ambiguous, resumed, and settled delivery;
133
+ - Pi scrolling, input, resize, cancellation, and external settlement;
134
+ - one answer and one continuation under concurrency and crash injection;
135
+ - TypeScript and Rust viewer parity; and
136
+ - absence of raw JSON subject serialization in channel output.
137
+
138
+ ## Rollout
139
+
140
+ The preferred authoring path starts emitting v2 after release. Existing
141
+ body-based workflows continue to create and read v1 until a separate removal is
142
+ approved. No migration runs. Historical records remain immutable.
143
+
144
+ A live Telegram smoke can use an already configured private audience profile.
145
+ The test must not print, copy, or persist credentials. Fake Bot API and real-Pi
146
+ end-to-end tests remain the required automated evidence.
147
+
148
+ ## Acceptance criteria
149
+
150
+ - New structured decisions cannot reach a channel without an explicit readable
151
+ presentation.
152
+ - Telegram and Pi show the same complete decision meaning.
153
+ - Telegram never shows raw JSON or silently truncates approval content.
154
+ - Pi shows the full decision instead of only its title.
155
+ - The accepted record binds the exact subject and visible presentation.
156
+ - V1 definitions and bundles remain readable without rewrites.
157
+ - Ambiguous Telegram sends fail closed without duplicate messages or
158
+ continuation.
159
+ - All repository, documentation, privacy, TypeScript, Rust, and real-Pi checks
160
+ pass.
161
+
162
+ ## Verification
163
+
164
+ ```bash
165
+ npm run check
166
+ npm run test:e2e
167
+ cargo test --manifest-path tui/Cargo.toml
168
+ npx slophammer-ts@latest dry .
169
+ npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
170
+ npx -y @simpledoc/simpledoc check
171
+ npm pack --dry-run --json
172
+ ```
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: Provider-compatible workflow tool schema plan
3
+ author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
4
+ date: 2026-08-19
5
+ ---
6
+
7
+ # Provider-compatible workflow tool schema plan
8
+
9
+ ## Goal
10
+
11
+ Fix the workflow tool schema so strict OpenAI-compatible providers accept it without weakening action validation. Apply the fix to the interactive Pi extension and the headless RPC bridge in PR #33.
12
+
13
+ ## Requirements
14
+
15
+ - Every provider-facing tool schema has `type: "object"` at its root.
16
+ - Provider-facing schemas do not use root unions or conditional object schemas.
17
+ - Each action keeps its current required fields and rejects fields for other actions.
18
+ - The exact action schemas remain the source for TypeScript types and runtime validation.
19
+ - Invalid input is rejected before workflow state changes or RPC output.
20
+ - The existing action names and input fields do not change.
21
+
22
+ ## Implementation
23
+
24
+ 1. Define the exact action object schemas in a shared, Pi-independent workflow module.
25
+ 2. Build the interactive and submission-only schemas from those action objects.
26
+ 3. Generate flat provider-facing object schemas from the same action definitions.
27
+ 4. Parse each tool call against the exact action schema before dispatch.
28
+ 5. Use the shared submission schema in the RPC bridge.
29
+ 6. Add unit tests for valid actions, missing fields, wrong-action fields, nested update data, and both provider schema roots.
30
+ 7. Add an integration test that sends the schemas through Pi's OpenAI Completions adapter to a strict local endpoint.
31
+
32
+ ## Non-goals
33
+
34
+ - Do not change Pi core or provider adapters.
35
+ - Do not add provider-specific branches.
36
+ - Do not add a schema version, new action format, or compatibility path.
37
+ - Do not change persisted workflow data.
38
+
39
+ ## Acceptance
40
+
41
+ - The strict local endpoint accepts both workflow tool schemas.
42
+ - Runtime parsing rejects malformed calls before execution.
43
+ - `npm run check`, `npm run test:e2e`, Rust tests, Slophammer checks, and `git diff --check` pass.
44
+ - Pi Reviewer reports no P0 or P1 findings.
45
+ - PR #33 CI passes before merge.