@osolmaz/pi-workflows 0.13.2 → 0.13.4

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 (134) hide show
  1. package/README.md +5 -3
  2. package/dist/builtins/autodoc.workflow.d.ts +5 -5
  3. package/dist/builtins/autoimplement.workflow.d.ts +77 -73
  4. package/dist/builtins/autoimplement.workflow.js +47 -8
  5. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  6. package/dist/builtins/autoplan.workflow.d.ts +8 -8
  7. package/dist/builtins/autoplan.workflow.js +44 -35
  8. package/dist/builtins/autoplan.workflow.js.map +1 -1
  9. package/dist/builtins/catalog.js +2 -2
  10. package/dist/builtins/change-verification.workflow.d.ts +1 -1
  11. package/dist/builtins/plain-summary.workflow.d.ts +1 -1
  12. package/dist/builtins/plan-approval.workflow.d.ts +1 -1
  13. package/dist/builtins/plan-change.workflow.d.ts +29 -29
  14. package/dist/builtins/sanity-check.workflow.d.ts +3 -3
  15. package/dist/builtins/workspace-preparation.workflow.d.ts +1 -1
  16. package/dist/controllers/index.d.ts +2 -2
  17. package/dist/controllers/index.js.map +1 -1
  18. package/dist/controllers/sqlite.js +85 -23
  19. package/dist/controllers/sqlite.js.map +1 -1
  20. package/dist/controllers/types.d.ts +34 -0
  21. package/dist/controllers/workflow-engine-scheduler.d.ts +5 -1
  22. package/dist/controllers/workflow-engine-scheduler.js +83 -1
  23. package/dist/controllers/workflow-engine-scheduler.js.map +1 -1
  24. package/dist/controllers/workflows.d.ts +8 -1
  25. package/dist/controllers/workflows.js +36 -0
  26. package/dist/controllers/workflows.js.map +1 -1
  27. package/dist/extension/controller-host.d.ts +2 -1
  28. package/dist/extension/controller-host.js +1 -1
  29. package/dist/extension/controller-host.js.map +1 -1
  30. package/dist/extension/follow-up-coordinator.d.ts +27 -0
  31. package/dist/extension/follow-up-coordinator.js +131 -0
  32. package/dist/extension/follow-up-coordinator.js.map +1 -0
  33. package/dist/extension/index.d.ts +9 -0
  34. package/dist/extension/index.js +460 -75
  35. package/dist/extension/index.js.map +1 -1
  36. package/dist/extension/session-events.d.ts +2 -3
  37. package/dist/extension/session-events.js +11 -10
  38. package/dist/extension/session-events.js.map +1 -1
  39. package/dist/extension/widget.js +9 -0
  40. package/dist/extension/widget.js.map +1 -1
  41. package/dist/state/database.d.ts +2 -1
  42. package/dist/state/database.js +11 -13
  43. package/dist/state/database.js.map +1 -1
  44. package/dist/state/json.js +6 -1
  45. package/dist/state/json.js.map +1 -1
  46. package/dist/state/mutation.d.ts +1 -1
  47. package/dist/state/mutation.js.map +1 -1
  48. package/dist/state/prune.d.ts +18 -0
  49. package/dist/state/prune.js +373 -0
  50. package/dist/state/prune.js.map +1 -0
  51. package/dist/state/schema.d.ts +1 -1
  52. package/dist/state/schema.js +116 -18
  53. package/dist/state/schema.js.map +1 -1
  54. package/dist/viewer/cli.d.ts +3 -1
  55. package/dist/viewer/cli.js +55 -4
  56. package/dist/viewer/cli.js.map +1 -1
  57. package/dist/viewer/render.js +19 -1
  58. package/dist/viewer/render.js.map +1 -1
  59. package/dist/workflows/composition.d.ts +2 -0
  60. package/dist/workflows/composition.js +15 -0
  61. package/dist/workflows/composition.js.map +1 -1
  62. package/dist/workflows/definition.d.ts +6 -3
  63. package/dist/workflows/definition.js +3 -0
  64. package/dist/workflows/definition.js.map +1 -1
  65. package/dist/workflows/engine.d.ts +6 -1
  66. package/dist/workflows/engine.js +301 -35
  67. package/dist/workflows/engine.js.map +1 -1
  68. package/dist/workflows/human-decision.d.ts +1 -0
  69. package/dist/workflows/human-decision.js +25 -9
  70. package/dist/workflows/human-decision.js.map +1 -1
  71. package/dist/workflows/index.d.ts +3 -1
  72. package/dist/workflows/index.js +2 -0
  73. package/dist/workflows/index.js.map +1 -1
  74. package/dist/workflows/json-patch.d.ts +47 -0
  75. package/dist/workflows/json-patch.js +298 -0
  76. package/dist/workflows/json-patch.js.map +1 -0
  77. package/dist/workflows/schema.js +29 -1
  78. package/dist/workflows/schema.js.map +1 -1
  79. package/dist/workflows/settings.d.ts +128 -0
  80. package/dist/workflows/settings.js +199 -0
  81. package/dist/workflows/settings.js.map +1 -0
  82. package/dist/workflows/store.d.ts +75 -2
  83. package/dist/workflows/store.js +1349 -89
  84. package/dist/workflows/store.js.map +1 -1
  85. package/dist/workflows/tool-input.d.ts +22 -0
  86. package/dist/workflows/tool-input.js +43 -0
  87. package/dist/workflows/tool-input.js.map +1 -1
  88. package/dist/workflows/types.d.ts +40 -5
  89. package/docs/2026-08-25-workflow-follow-ups.md +132 -0
  90. package/docs/2026-08-25-workflow-settings.md +169 -0
  91. package/docs/DEFERRED_TURNS.md +6 -0
  92. package/docs/DESIGN_PHILOSOPHY.md +2 -0
  93. package/docs/SQLITE_STATE.md +31 -18
  94. package/docs/WORKFLOW_COMPOSITION.md +16 -1
  95. package/docs/WORKFLOW_STEP_MESSAGES.md +4 -2
  96. package/docs/plans/2026-08-25-live-workflow-settings-plan.md +532 -0
  97. package/docs/plans/2026-08-25-workflow-run-storage-plan.md +67 -0
  98. package/docs/tui-viewer.md +7 -6
  99. package/docs/workflows.md +22 -1
  100. package/examples/workflows/live-settings.workflow.ts +93 -0
  101. package/herdr-plugin.toml +1 -1
  102. package/package.json +1 -1
  103. package/skills/autoplan/SKILL.md +6 -6
  104. package/src/builtins/autoimplement.workflow.ts +56 -8
  105. package/src/builtins/autoplan.workflow.ts +45 -37
  106. package/src/builtins/catalog.ts +2 -2
  107. package/src/controllers/index.ts +6 -0
  108. package/src/controllers/sqlite.ts +129 -35
  109. package/src/controllers/types.ts +40 -0
  110. package/src/controllers/workflow-engine-scheduler.ts +103 -1
  111. package/src/controllers/workflows.ts +68 -0
  112. package/src/extension/controller-host.ts +6 -1
  113. package/src/extension/follow-up-coordinator.ts +151 -0
  114. package/src/extension/index.ts +538 -88
  115. package/src/extension/session-events.ts +15 -13
  116. package/src/extension/widget.ts +8 -0
  117. package/src/state/database.ts +12 -12
  118. package/src/state/json.ts +6 -1
  119. package/src/state/mutation.ts +4 -1
  120. package/src/state/prune.ts +502 -0
  121. package/src/state/schema.ts +116 -18
  122. package/src/viewer/cli.ts +52 -5
  123. package/src/viewer/render.ts +43 -1
  124. package/src/workflows/composition.ts +19 -0
  125. package/src/workflows/definition.ts +19 -5
  126. package/src/workflows/engine.ts +363 -35
  127. package/src/workflows/human-decision.ts +41 -11
  128. package/src/workflows/index.ts +43 -0
  129. package/src/workflows/json-patch.ts +375 -0
  130. package/src/workflows/schema.ts +31 -1
  131. package/src/workflows/settings.ts +430 -0
  132. package/src/workflows/store.ts +1977 -184
  133. package/src/workflows/tool-input.ts +58 -0
  134. package/src/workflows/types.ts +43 -4
@@ -0,0 +1,132 @@
1
+ ---
2
+ title: Continue normal work after a workflow finishes
3
+ author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
4
+ date: 2026-08-25
5
+ ---
6
+
7
+ # Continue normal work after a workflow finishes
8
+
9
+ A workflow can save several normal user prompts while it runs. Pi Workflows sends them in order only after the workflow is officially terminal and its final response has settled. The completed workflow stays terminal and does not enter its start node again.
10
+
11
+ Follow-up prompts are separate from workflow settings. JSON Patch changes future workflow behavior. Follow-up actions create later normal user turns.
12
+
13
+ ## Queue and remove prompts
14
+
15
+ Use the workflow tool once for each prompt:
16
+
17
+ ```json
18
+ {
19
+ "action": "queue-follow-up",
20
+ "prompt": "Release the package and verify the published files."
21
+ }
22
+ ```
23
+
24
+ The result contains a stable `followUpId` and order number.
25
+
26
+ Remove an unsent prompt with:
27
+
28
+ ```json
29
+ {
30
+ "action": "remove-follow-up",
31
+ "followUpId": "follow-up-0123456789abcdef0123456789abcdef01234567"
32
+ }
33
+ ```
34
+
35
+ A model can remove only a prompt added by the same session tool source. A controller can remove only its own prompt. A verified human can remove any unsent prompt in the owned run.
36
+
37
+ Direct commands are also available:
38
+
39
+ ```text
40
+ /workflow queue-follow-up Release the package and verify the published files.
41
+ /workflow remove-follow-up follow-up-0123456789abcdef0123456789abcdef01234567
42
+ ```
43
+
44
+ Controllers use:
45
+
46
+ ```ts
47
+ await ctx.workflows.queueFollowUp({
48
+ requestKey: "release-after-run",
49
+ runId,
50
+ prompt: "Release the package and verify the published files.",
51
+ });
52
+
53
+ await ctx.workflows.removeFollowUp({
54
+ requestKey: "remove-release-after-run",
55
+ runId,
56
+ followUpId,
57
+ });
58
+ ```
59
+
60
+ The host creates the actor and stable request identity. Prompt data cannot claim human or controller authority.
61
+
62
+ ## Ordering and run states
63
+
64
+ Prompts keep database acceptance order. Several user messages can therefore add several prompts while one workflow response is in progress.
65
+
66
+ - Running, paused, parked, and waiting workflows accept new prompts.
67
+ - Pause and park keep prompts queued.
68
+ - A checkpoint continuation takes the queued prompts with it in the same transaction that creates the new run.
69
+ - Successful completion records the terminal run before prompts can become ready.
70
+ - Failed, timed-out, and cancelled workflows cancel every unsent prompt.
71
+ - A terminal workflow rejects new prompts. Repeating an earlier request ID can still return its first result.
72
+
73
+ A repeated request ID with the same prompt returns the first result. Reusing that ID with different content fails.
74
+
75
+ ## Final response barrier
76
+
77
+ Each run saves one final-response state:
78
+
79
+ - `not-needed`: the workflow has no final presentation;
80
+ - `pending`: the terminal run has a final response that has not settled;
81
+ - `settled`: the active session branch contains the presentation message and its completed assistant response;
82
+ - `unavailable`: the extension recorded a definite timeout, failure, or restart gap.
83
+
84
+ Successful completion changes queued prompts to `pending_presentation` when a final response is required. It changes them directly to `ready` when no final response is required. A settled or unavailable presentation changes pending prompts to ready.
85
+
86
+ The extension includes the run ID in documented presentation-message details. On restart, it scans the active durable branch for the presentation message and completed assistant response. It saves the observed entry IDs. It does not treat an in-memory event alone as proof.
87
+
88
+ ## Delivery
89
+
90
+ The follow-up coordinator handles one prompt at a time:
91
+
92
+ 1. Wait for terminal workflow state and a completed final-response barrier.
93
+ 2. Wait for the target session to be idle and have no active workflow.
94
+ 3. Claim the first ready prompt with a lease.
95
+ 4. Scan the active session branch for its stable follow-up ID.
96
+ 5. If the message already exists, save its session entry ID without sending it again.
97
+ 6. Otherwise, call documented `pi.sendUserMessage` with the prompt and a short nonsecret ID in a Markdown comment.
98
+ 7. After Pi saves the normal user message, scan the branch and save its entry ID and send time.
99
+ 8. Wait for that user turn and any workflow it starts to finish before taking the next prompt.
100
+
101
+ `pi.sendUserMessage` returns no message ID. Branch evidence is required after a new send and after restart.
102
+
103
+ The follow-up message is a normal user message. It can start normal work or a different workflow. It cannot resume or reactivate the completed workflow.
104
+
105
+ ## Failure handling
106
+
107
+ - A crash before claim leaves the prompt ready.
108
+ - A crash after claim lets another process continue after the lease expires.
109
+ - A crash before message append leaves no branch evidence and permits a retry.
110
+ - A crash after append but before the database update is resolved by finding the follow-up ID in the active branch.
111
+ - A definite send failure releases the claim.
112
+ - An unavailable session leaves the prompt pending.
113
+ - Branching follows the active Pi branch. A message on another branch does not count as delivered on the active branch.
114
+
115
+ Current documented Pi APIs cannot guarantee exactly-once model execution. Pi Workflows can guarantee only its saved order, claim, active-branch evidence, and local delivery state. Delivery also needs a later live Pi process, database, target session, and provider. This feature adds no external service.
116
+
117
+ ## Saved states
118
+
119
+ A follow-up is in one of these states:
120
+
121
+ - `queued`;
122
+ - `pending_presentation`;
123
+ - `ready`;
124
+ - `sent`;
125
+ - `removed`;
126
+ - `cancelled`.
127
+
128
+ A lease is temporary claim data, not another follow-up state.
129
+
130
+ `workflow_follow_up_queues` stores presentation state and per-run order. `workflow_follow_ups` stores each prompt, source, state, session entry ID, and times. Prompt text stays in the existing content-addressed value store. Normal status views show IDs, order, source type, and state without printing full prompts.
131
+
132
+ This is part of the same alpha hard change as workflow settings. It adds no Pi core change, private Pi API, external queue, compatibility reader, dual write, alias, fallback, or second schema.
@@ -0,0 +1,169 @@
1
+ ---
2
+ title: Change workflow settings during a run
3
+ author: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
4
+ date: 2026-08-25
5
+ ---
6
+
7
+ # Change workflow settings during a run
8
+
9
+ A workflow can expose a JSON settings value that authorized actors can change while the workflow runs. A change affects only future node attempts and future settings routes. It does not change the workflow definition, immutable input, completed work, or a running node.
10
+
11
+ ## Declare settings
12
+
13
+ Use `workflowSettings()` in `defineWorkflow()`:
14
+
15
+ ```ts
16
+ import {
17
+ allowSettingsPath,
18
+ defineWorkflow,
19
+ settingsRoute,
20
+ workflowSettings,
21
+ } from "@osolmaz/pi-workflows";
22
+
23
+ const settings = workflowSettings({
24
+ initial: {
25
+ instructions: [],
26
+ merge: false,
27
+ route: "normal",
28
+ },
29
+ parse: parseSettings,
30
+ paths: [
31
+ allowSettingsPath("/instructions", {
32
+ read: ["session", "human"],
33
+ add: ["session", "human"],
34
+ remove: ["session", "human"],
35
+ replace: ["session", "human"],
36
+ }),
37
+ allowSettingsPath("/merge", {
38
+ read: ["session", "human"],
39
+ replace: ["human"],
40
+ }),
41
+ allowSettingsPath("/route", {
42
+ read: ["session", "human"],
43
+ replace: ["session", "human"],
44
+ }),
45
+ ],
46
+ validateChange: ({ before, after, actor }) => {
47
+ if (actor.type === "session" && before.merge === false && after.merge === true) {
48
+ throw new Error("A model cannot grant merge authority");
49
+ }
50
+ },
51
+ });
52
+
53
+ export default defineWorkflow({
54
+ name: "example",
55
+ settings,
56
+ startAt: "choose",
57
+ nodes: {
58
+ choose: settingsRoute({
59
+ run: ({ settings }) => ({ route: settings.route }),
60
+ }),
61
+ normal: compute({ run: ({ settings }) => settings.instructions }),
62
+ careful: compute({ run: ({ settings }) => settings.instructions }),
63
+ },
64
+ edges: [
65
+ {
66
+ from: "choose",
67
+ switch: { on: "$.route", cases: { normal: "normal", careful: "careful" } },
68
+ },
69
+ ],
70
+ });
71
+ ```
72
+
73
+ The parser must validate without changing the patched value. `validateChange` can reject a valid JSON shape because of a cross-field or authority rule.
74
+
75
+ A workflow without `settings` has no editable settings. A change request fails clearly.
76
+
77
+ ## Change settings
78
+
79
+ The workflow tool accepts RFC 6902 JSON Patch:
80
+
81
+ ```json
82
+ {
83
+ "action": "change-settings",
84
+ "scopeId": "run-id:$root:1",
85
+ "expectedChangeNumber": 3,
86
+ "patch": [
87
+ { "op": "replace", "path": "/route", "value": "careful" },
88
+ { "op": "add", "path": "/instructions/-", "value": "Run the full test suite." }
89
+ ]
90
+ }
91
+ ```
92
+
93
+ A direct verified user can use:
94
+
95
+ ```text
96
+ /workflow change-settings [{"op":"replace","path":"/merge","value":false}]
97
+ ```
98
+
99
+ Controllers use the same saved change path:
100
+
101
+ ```ts
102
+ await ctx.workflows.changeSettings({
103
+ requestKey: "disable-merge",
104
+ runId,
105
+ scopeId,
106
+ expectedChangeNumber: 3,
107
+ patch: [{ op: "replace", path: "/merge", value: false }],
108
+ });
109
+ ```
110
+
111
+ Each request has a stable host-issued ID. Repeating the same ID and content returns the first result. Reusing an ID with different content fails. When one model response emits several workflow tool calls, the extension applies them in source order so a settings change cannot race past a later step submission.
112
+
113
+ `expectedChangeNumber` protects the complete settings value. A JSON Patch `test` operation can protect one field. When no expected number is supplied, the patch applies to the latest saved value in database acceptance order.
114
+
115
+ ## Path permissions
116
+
117
+ Paths use RFC 6901 JSON Pointer. They deny access by default. Rules apply to their path and descendants on segment boundaries. The most specific rule wins.
118
+
119
+ The permissions mean:
120
+
121
+ - `read`: use a path as the source of `test`, `copy`, or `move`;
122
+ - `add`: add a new object value or array item;
123
+ - `remove`: remove a value;
124
+ - `replace`: replace an existing value.
125
+
126
+ `copy` checks source read and destination add or replace. `move` checks source read and remove, then destination add or replace.
127
+
128
+ `read` is not a privacy filter. Workflow code in that scope receives the complete typed settings value.
129
+
130
+ The host records the actor outside the patch:
131
+
132
+ - `session` for a model workflow-tool call;
133
+ - `human` for a direct command or verified human decision;
134
+ - `controller` for a controller request;
135
+ - `policy` only for existing saved policy code.
136
+
137
+ Patch data cannot claim another actor type.
138
+
139
+ ## Runtime rules
140
+
141
+ Each node attempt gets one deep-copied, frozen settings value, scope ID, change number, and settings hash. A change accepted while that node runs does not alter it. The next attempt or node captures the latest value.
142
+
143
+ Use `settingsRoute()` when a future route must use the latest settings. It is a pure compute node. The database compares its change number in the same transaction that saves the route result. If the number is stale, the engine reruns only that pure route. It never repeats an agent step, shell command, function action, or other external effect because settings changed.
144
+
145
+ A settings scope accepts changes when its workflow chain is running, paused, parked, or waiting at a checkpoint. A terminal completed, failed, timed-out, or cancelled run rejects new changes.
146
+
147
+ ## Included workflows and checkpoints
148
+
149
+ The root workflow has one settings scope when it declares settings. Each included-workflow invocation that declares settings gets its own scope. Its ID uses the root run, mount path, and saved invocation number. Re-entry gets fresh settings.
150
+
151
+ Parent and child settings do not mix automatically. The child initializer receives its mapped child input.
152
+
153
+ A checkpoint creates a continuation run. The transaction that creates that run transfers the active settings scopes to it. Scope IDs, change numbers, saved changes, and current values remain unchanged. This keeps one ordered settings history across the logical workflow chain.
154
+
155
+ ## Limits
156
+
157
+ One patch can contain at most 256 operations and 64 KiB. One JSON Pointer can contain at most 1 KiB. The resulting settings value can contain at most 256 KiB.
158
+
159
+ The engine rejects malformed operations, unknown fields, invalid indexes, invalid pointer escapes, non-JSON values, unsafe object access, and invalid moves. A rejected patch changes nothing.
160
+
161
+ Workflow settings must not contain credentials or raw secrets. Use the existing credential stores and authority checks instead.
162
+
163
+ ## Saved state and inspection
164
+
165
+ Each settings scope uses one `resources` row of type `settings`. Its resource revision is the public change number. `workflow_settings` stores the current value. `workflow_setting_changes` stores every accepted patch, actor, source, old hash, new hash, and time.
166
+
167
+ Node attempts store the scope ID, change number, and settings hash they used. Workflow status, the TypeScript viewer, and `piw` show bounded settings data without printing private values by default.
168
+
169
+ This is an alpha hard change. The schema keeps its existing v1 name and changes in place. Incompatible old local state remains untouched and must be backed up or reset. There is no old-state reader, second schema, dual write, alias, fallback, or feature flag.
@@ -260,6 +260,12 @@ The `launch_failure` notification kind is removed. Queued launch failure creates
260
260
 
261
261
  Pending `launch_failure` rows from the earlier alpha contract are incompatible. Pi Workflows must stop with a clear controller-store reset instruction. It must not reinterpret, migrate, or silently delete those rows.
262
262
 
263
+ ## Post-completion follow-ups
264
+
265
+ Deferred turns repair one stranded workflow turn after interruption. Ordered post-completion prompts are different. They represent user-requested normal work after successful completion, use `workflow_follow_up_queues` and `workflow_follow_ups`, and are delivered by the separate follow-up coordinator. Neither feature reads or changes the other's rows.
266
+
267
+ See [Continue normal work after a workflow finishes](2026-08-25-workflow-follow-ups.md).
268
+
263
269
  ## Availability limits
264
270
 
265
271
  Pi Workflows can guarantee only the facts under its control:
@@ -32,6 +32,8 @@ Runs should survive interruption and remain safe to resume. Save the run input a
32
32
 
33
33
  Immutable SQLite events are the record of what happened. Domain rows are current projections written in the same transaction. Viewers derive their answers from those facts instead of creating another source of truth.
34
34
 
35
+ Live workflow settings follow the same rule. The workflow owns one typed JSON value and path policy. The engine owns ordered JSON Patch application and fixed node bindings. Lifecycle actions, such as queued post-completion prompts, stay outside the settings value.
36
+
35
37
  Reading shared state never gives mutation authority. Every durable write checks its actor, expected resource revision, and current lease generation when ownership is required. Follow-up work uses deterministic effects and idempotent receipts so partial failure can converge safely.
36
38
 
37
39
  ## Boundaries
@@ -18,6 +18,7 @@ The database stores:
18
18
  - human-decision requests, submissions, resolutions, and cancellations
19
19
  - controller resources, finalizers, effects, and child workflows
20
20
  - notifications and deferred turns
21
+ - workflow settings, accepted JSON Patch changes, and post-completion follow-up prompts
21
22
  - nonsecret channel cursors, inbox records, messages, and settlement receipts
22
23
  - canonical JSON, text, and large text values
23
24
 
@@ -57,7 +58,7 @@ Four record groups provide the common lifecycle rules.
57
58
 
58
59
  ### Resources
59
60
 
60
- `resources` identifies each mutable aggregate and holds its current revision. Runs, decisions, controller resources, effects, channels, notifications, deferred turns, and session segments have stable resource identities.
61
+ `resources` identifies each mutable aggregate and holds its current revision. Runs, settings scopes, follow-up queues and items, decisions, controller resources, effects, channels, notifications, deferred turns, and session segments have stable resource identities.
61
62
 
62
63
  Every accepted domain command compares its expected revision and increments it once.
63
64
 
@@ -93,18 +94,20 @@ Local effects use deterministic transactions. Run queue settlement effects are c
93
94
 
94
95
  The shared records do not replace domain schemas. The following `STRICT` tables keep the state explicit:
95
96
 
96
- | Area | Tables |
97
- | ------------------- | ------------------------------------------------------------------------------------------------------------- |
98
- | Schema and projects | `schema_meta`, `projects` |
99
- | Content | `blobs` |
100
- | Shared lifecycle | `resources`, `leases`, `events` |
101
- | Workflows | `workflow_definitions`, `runs`, `run_steps`, `run_bindings`, `run_queue`, `node_attempts`, `workflow_updates` |
102
- | Session capture | `session_segments`, `session_entries`, `session_events` |
103
- | Human decisions | `human_decisions`, `human_decision_resolutions`, `human_decision_submissions`, `continuations` |
104
- | Controllers | `controller_resources`, `controller_finalizers`, `controller_queue`, `controller_workflows` |
105
- | Effects | `effects`, `effect_attempts` |
106
- | Pi delivery | `notifications`, `turn_intents` |
107
- | Channels | `channels`, `channel_cursors`, `channel_inbox`, `channel_messages`, `channel_message_parts` |
97
+ | Area | Tables |
98
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
99
+ | Schema and projects | `schema_meta`, `projects` |
100
+ | Content | `blobs` |
101
+ | Shared lifecycle | `resources`, `leases`, `events` |
102
+ | Workflows | `workflow_definitions`, `runs`, `run_sources`, `run_steps`, `run_bindings`, `run_queue`, `node_attempts`, `workflow_updates` |
103
+ | Live settings | `workflow_settings`, `workflow_setting_changes` |
104
+ | Post-run follow-ups | `workflow_follow_up_queues`, `workflow_follow_ups` |
105
+ | Session capture | `session_segments`, `session_entries`, `attempt_entries`, `session_events` |
106
+ | Human decisions | `human_decisions`, `human_decision_resolutions`, `human_decision_submissions`, `continuations` |
107
+ | Controllers | `controller_resources`, `controller_finalizers`, `controller_queue`, `controller_workflows` |
108
+ | Effects | `effects`, `effect_attempts` |
109
+ | Pi delivery | `notifications`, `turn_intents` |
110
+ | Channels | `channels`, `channel_cursors`, `channel_inbox`, `channel_messages`, `channel_message_parts` |
108
111
 
109
112
  Foreign keys join projects, runs, attempts, decisions, controllers, effects, and channel records. Partial unique indexes enforce one active node attempt per run, one queued or running reservation per Pi session, one decision winner, and one deterministic effect key. A parked waiting parent does not block its continuation. Reserving that continuation settles the parked parent queue in the same transaction, so a failed reservation leaves the parent recoverable.
110
113
 
@@ -112,15 +115,17 @@ Foreign keys join projects, runs, attempts, decisions, controllers, effects, and
112
115
 
113
116
  `blobs` stores canonical JSON and UTF-8 text as bytes. Its primary key is the 32-byte SHA-256 digest of the bytes.
114
117
 
115
- Insertion verifies the digest, media type, byte length, and exact bytes. Repeated content adopts the existing row. This replaces separate artifact files while keeping large prompts, outputs, errors, session payloads, and rendered channel text deduplicated. A writable open removes blobs that no foreign-key column references.
118
+ Insertion verifies the digest, media type, byte length, and exact bytes. Repeated content adopts the existing row. This replaces separate artifact files while keeping outputs, errors, settled Pi entries, and rendered channel text deduplicated. Opening the database never deletes blobs. The explicit prune command removes unreferenced blobs after it deletes safe old run trees.
116
119
 
117
- Runs do not store a nested `WorkflowRunState` blob. `runs` stores scalar run facts and hashes for independent values. `node_attempts` stores each prompt and output once. `run_steps` stores ordered attempt membership and only stores an output override when a continuation changes a carried checkpoint answer. Readers derive `steps`, `outputs`, and `results` from these rows. Compact trace events do not copy node outputs, run inputs, or final outputs.
120
+ Runs do not store a nested `WorkflowRunState` blob. `runs` stores run-level facts and hashes for independent values. `run_sources` stores source identity without source JSON blobs. `node_attempts` stores structured workflow outputs and small execution receipts. `session_entries` is the only stored copy of each settled Pi entry. `attempt_entries` links an attempt to its prompt, response, first, and last Pi entries. `run_steps` stores ordered attempt membership and only stores an output override when a continuation changes a carried checkpoint answer.
121
+
122
+ Readers derive `steps`, `outputs`, `results`, carried-step count, current-node fields, waiting state, source objects, and continuation decision receipts from these rows. Compact trace events do not copy prompts, node outputs, run inputs, final outputs, action receipts, or assistant receipts.
118
123
 
119
124
  ### Assistant-message attempts
120
125
 
121
126
  An agent definition records `expectedOutput` as either a submitted-output description or `{ "kind": "assistant-message", "maxChars"?: number }`. Omitted `maxChars` means that Pi Workflows adds no character limit.
122
127
 
123
- A completed assistant-message attempt stores the exact text through the normal output blob. Its result record also stores a receipt with the text digest, final Pi session entry ID when available, optional author-supplied limit, and whether recovery adopted an existing response. Session tables keep the prompt-to-response entry range and the normal Pi message events.
128
+ A completed interactive assistant-message attempt uses the settled Pi response entry as its output. It does not store a second output blob. Its small receipt keeps the text digest, final Pi session entry ID, optional author-supplied limit, and whether recovery adopted an existing response. A noninteractive attempt with no captured response entry keeps one normal output blob.
124
129
 
125
130
  An interrupted assistant-message attempt keeps its attempt ID when the origin Pi session resumes it. The executor adopts a matching completed assistant child from the active Pi branch instead of displaying the response twice. Submitted and non-agent attempts keep their normal fresh-attempt resume behavior.
126
131
 
@@ -145,7 +150,7 @@ COMMIT
145
150
 
146
151
  Any failed check rolls back the complete command.
147
152
 
148
- Session-event batches use `session_events` as their journal. They update the contiguous segment counter in the same transaction and do not create a generic `session.events_appended` event for each flush. The recorder stores lifecycle boundaries and settled events. It discards token deltas and incremental tool progress.
153
+ Session-event batches use `session_events` as their journal. They update the contiguous segment counter in the same transaction and do not create a generic `session.events_appended` event for each flush. The recorder stores order, IDs, roles, status, and timing boundaries. It discards token deltas, completed text, thinking text, tool arguments, tool results, and incremental tool progress. The settled Pi entries keep the replay content.
149
154
 
150
155
  A TypeScript write permit carries the expected facts between layers. It is not authority by itself. The store verifies durable ownership and revision data again inside the transaction.
151
156
 
@@ -176,7 +181,11 @@ The same rule applies to run terminal outcomes, continuation admission, queue se
176
181
 
177
182
  ## Read contract
178
183
 
179
- Status is a pure projection of domain rows, immutable facts, current leases, and effect receipts.
184
+ Status is a pure projection of domain rows, immutable facts, current leases, and effect results.
185
+
186
+ A settings scope uses its resource revision as its public change number. Each accepted patch, current value, and node binding is saved in one transaction. A checkpoint continuation keeps the same settings resources and transfers them to the continuation run.
187
+
188
+ A follow-up queue records acceptance order and final-presentation state. Successful terminalization changes queued items to ready or pending presentation in the same transaction as the terminal run fact. Failure, timeout, and cancellation cancel unsent items. Delivery uses item leases and active Pi branch evidence.
180
189
 
181
190
  - A terminal run fact overrides stale queue presentation.
182
191
  - An accepted decision is accepted even if its continuation effect is still pending.
@@ -213,11 +222,15 @@ Supported commands are:
213
222
  pi-workflows state status
214
223
  pi-workflows state verify
215
224
  pi-workflows state backup /absolute/path/to/state-backup.sqlite
225
+ pi-workflows state prune --before 2026-08-01T00:00:00Z --dry-run
226
+ pi-workflows state prune --before 2026-08-01T00:00:00Z --backup /absolute/path/to/before-prune.sqlite --apply
216
227
  ```
217
228
 
218
229
  `status` reports only safe counts, file size, active leases, and unsettled effects.
219
230
  It does not print actor IDs, channel references, payloads, or credentials.
220
231
 
232
+ `prune --dry-run` reports complete terminal run trees older than the cutoff and the trees that safety checks block. It does not change the database. `prune --apply` requires a new absolute backup path. It verifies the backup, locks maintenance, rechecks the same selection in an exclusive transaction, and refuses trees with live queues, active leases, unsettled effects, controller references, channel references, or step links from runs outside the tree. It deletes the safe aggregates, removes blobs with no remaining foreign-key reference, checkpoints the WAL, vacuums the file, and runs integrity and foreign-key checks. Pi Workflows never runs prune at startup.
233
+
221
234
  ## Alpha cutover
222
235
 
223
236
  This is a hard cut. Pi Workflows has no normal reader or writer for older live storage. It does not use dual reads, dual writes, aliases, versioned state roots, or automatic import.
@@ -198,6 +198,21 @@ A child cannot read parent state except through mapped input. A parent sees only
198
198
 
199
199
  Each mount entry starts with empty local outputs and results. Earlier invocation data stays in the trace but cannot satisfy callbacks in a later invocation. The latest named exit replaces the parent-visible mount output.
200
200
 
201
+ ### Workflow settings
202
+
203
+ A root workflow and each included-workflow invocation can declare separate settings. Included settings are isolated by default and use the mount path plus saved invocation number as their scope. Re-entry creates fresh settings. A child initializer receives the mapped child input.
204
+
205
+ A parent can override one invocation's initial child settings explicitly:
206
+
207
+ ```ts
208
+ includeWorkflow(child, {
209
+ input: ({ outputs }) => ({ task: outputs.plan }),
210
+ settings: ({ settings }) => ({ ...settings, route: "careful" }),
211
+ });
212
+ ```
213
+
214
+ A checkpoint continuation keeps the same active settings scopes, change numbers, and saved changes. The continuation transaction transfers those scopes and queued follow-ups to the new run before execution resumes.
215
+
201
216
  ### Limits and progress
202
217
 
203
218
  The root `maxSteps` limits all real node attempts in the run. Each child `maxSteps` limits one invocation. Include entry and exit transitions are recorded but do not count as user-authored node attempts.
@@ -267,7 +282,7 @@ monitor
267
282
 
268
283
  ## Autoplan, autodoc, and autoimplement
269
284
 
270
- `autoplan` accepts the problem, scope, constraints, an optional previous plan, and new evidence. It records two through four practical candidates plus the ideal, automatically selects the best in-scope solution, and records one rejection reason for every other explicit candidate. It includes `plain-summary` on ready and blocked routes, so the user sees one short normal assistant response before the parent continues. The response calls the choice a plan selected for approval and cannot satisfy the later human decision. Autoplan exits through `ready` or `blocked` and returns the full candidates, selection, detailed plan when ready, plain summary, plan digest, and change status.
285
+ `autoplan` accepts the problem, scope, constraints, an optional previous plan, and new evidence. Its `solutions` step records two through four practical candidates and asks whether each is a Long term elegant and production ready solution. Its `holyGrail` step describes the Holy grail separately. Its `select` step chooses the best in-scope option and records one rejection reason for every other explicit candidate, including the Holy grail. It includes `plain-summary` on ready and blocked routes, so the user sees one short normal assistant response before the parent continues. The response calls the choice a plan selected for approval and cannot satisfy the later human decision. Autoplan exits through `ready` or `blocked` and returns the full candidates, selection, detailed plan when ready, plain summary, plan digest, and change status.
271
286
 
272
287
  `autodoc` accepts an already selected plan or finds it in the active conversation and referenced canonical documents. It adopts current documentation or updates the canonical specification and implementation plan, runs documentation checks, and returns a documented-plan record. It never selects a solution or implements one.
273
288
 
@@ -55,6 +55,8 @@ The request carries optional presentation data for the run title and node status
55
55
 
56
56
  One pure formatter remains responsible for the model prompt used by both executors. Interactive delivery must not shorten, summarize, or rebuild the model prompt from display fields.
57
57
 
58
+ Before the step contract, the formatter adds the active settings scope, change number, bounded current value, allowed model paths, and exact `change-settings` action when that scope declares settings. It also shows the `queue-follow-up` and `remove-follow-up` actions. Actor identity is never part of model input. The extension derives it from the documented tool call.
59
+
58
60
  ## Compact display
59
61
 
60
62
  The collapsed card shows only useful workflow identity and current work. For example:
@@ -104,7 +106,7 @@ The two message types must not share delivery code that can accidentally change
104
106
 
105
107
  Interactive step deliveries use `sendMessage` instead of `sendUserMessage`. Existing session entries remain readable and are not rewritten.
106
108
 
107
- The custom prompt and visible assistant response are normal documented Pi session messages. pi-workflows adds no Pi session schema, private entry type, or separate persistent store. SQLite stores the full prompt, exact assistant text, conversation range, and additive digest receipt in existing content-addressed records, so this behavior adds no durable format.
109
+ The custom prompt and visible assistant response are normal documented Pi session messages. pi-workflows adds no Pi session schema, private entry type, or separate persistent store. SQLite stores each settled Pi entry once. Small relational links connect the workflow attempt to its prompt, response, first, and last entries. The attempt keeps only the assistant digest receipt. It does not copy the prompt or visible response into another blob.
108
110
 
109
111
  If the renderer is unavailable, Pi still retains the custom message content. pi-workflows does not add a fallback path that sends a duplicate user message.
110
112
 
@@ -123,7 +125,7 @@ Tests verify:
123
125
  - interactive and RPC executors give the model the same complete prompt
124
126
  - one step message starts one model turn
125
127
  - collapsed rendering does not show the full prompt
126
- - expanded rendering shows the full prompt and exact contract ids
128
+ - expanded rendering shows the full prompt, settings scope and change number, allowed paths, and exact contract ids
127
129
  - long and missing display fields render safely
128
130
  - reminders and resumed deliveries keep the active attempt id
129
131
  - submitted steps still reject stale attempts after timeout or cancellation