@bastani/atomic 0.9.16-alpha.6 → 0.9.16-alpha.8

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 (214) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/README.md +4 -4
  4. package/dist/builtin/intercom/broker/broker.ts +617 -22
  5. package/dist/builtin/intercom/broker/client-message-validation.ts +9 -0
  6. package/dist/builtin/intercom/broker/client.ts +200 -10
  7. package/dist/builtin/intercom/broker/pending-send-registry.ts +4 -0
  8. package/dist/builtin/intercom/broker/send-handler.ts +90 -36
  9. package/dist/builtin/intercom/broker/send-signature.ts +20 -8
  10. package/dist/builtin/intercom/group.ts +85 -0
  11. package/dist/builtin/intercom/index.bundle.mjs +680 -53
  12. package/dist/builtin/intercom/package.json +1 -1
  13. package/dist/builtin/intercom/session-target.ts +54 -0
  14. package/dist/builtin/intercom/skills/intercom/SKILL.md +18 -3
  15. package/dist/builtin/intercom/source-ownership.ts +31 -0
  16. package/dist/builtin/intercom/types.ts +115 -0
  17. package/dist/builtin/mcp/index.bundle.mjs +109 -35
  18. package/dist/builtin/mcp/package.json +1 -1
  19. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  20. package/dist/builtin/subagents/package.json +1 -1
  21. package/dist/builtin/subagents/skills/qlty/SKILL.md +123 -0
  22. package/dist/builtin/subagents/skills/qlty/references/coding-with-ai-agents.md +64 -0
  23. package/dist/builtin/subagents/skills/qlty/references/commands.md +291 -0
  24. package/dist/builtin/subagents/skills/qlty/references/plugins-and-extensions.md +228 -0
  25. package/dist/builtin/subagents/skills/qlty/references/quickstart.md +110 -0
  26. package/dist/builtin/subagents/src/extension/index.bundle.mjs +173 -51
  27. package/dist/builtin/web-access/index.bundle.mjs +109 -35
  28. package/dist/builtin/web-access/package.json +1 -1
  29. package/dist/builtin/workflows/CHANGELOG.md +23 -0
  30. package/dist/builtin/workflows/README.md +40 -37
  31. package/dist/builtin/workflows/builtin/adversarial-verification.js +13 -0
  32. package/dist/builtin/workflows/builtin/{shared-prompts.ts → chunk-0x6e303p.js} +137 -133
  33. package/dist/builtin/workflows/builtin/chunk-29wrp38a.js +199 -0
  34. package/dist/builtin/workflows/builtin/chunk-2dqb5s2q.js +602 -0
  35. package/dist/builtin/workflows/builtin/chunk-4febxsv4.js +264 -0
  36. package/dist/builtin/workflows/builtin/chunk-5n10x7j2.js +69730 -0
  37. package/dist/builtin/workflows/builtin/chunk-5wgwscd0.js +4 -0
  38. package/dist/builtin/workflows/builtin/chunk-6fqs7c01.js +155 -0
  39. package/dist/builtin/workflows/builtin/chunk-7430zyas.js +126 -0
  40. package/dist/builtin/workflows/builtin/chunk-7at6dnkr.js +33 -0
  41. package/dist/builtin/workflows/builtin/chunk-82ha8p41.js +175 -0
  42. package/dist/builtin/workflows/builtin/chunk-bfkmzv9h.js +156 -0
  43. package/dist/builtin/workflows/builtin/chunk-c53y8bdh.js +345 -0
  44. package/dist/builtin/workflows/builtin/chunk-cdtd3m3w.js +529 -0
  45. package/dist/builtin/workflows/builtin/chunk-cg9tmks0.js +206 -0
  46. package/dist/builtin/workflows/builtin/chunk-hdpj1dkw.js +2447 -0
  47. package/dist/builtin/workflows/builtin/chunk-hzzn6adg.js +221 -0
  48. package/dist/builtin/workflows/builtin/chunk-nqr34qp3.js +1061 -0
  49. package/dist/builtin/workflows/builtin/chunk-qwzvgxnq.js +404 -0
  50. package/dist/builtin/workflows/builtin/chunk-qx2ptjs3.js +215 -0
  51. package/dist/builtin/workflows/builtin/chunk-v04c5qmd.js +1207 -0
  52. package/dist/builtin/workflows/builtin/chunk-wpckd35c.js +12 -0
  53. package/dist/builtin/workflows/builtin/chunk-x2ghn0je.js +4110 -0
  54. package/dist/builtin/workflows/builtin/chunk-zf28603f.js +1080 -0
  55. package/dist/builtin/workflows/builtin/classify-and-act.js +12 -0
  56. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +10 -0
  57. package/dist/builtin/workflows/builtin/generate-and-filter.js +12 -0
  58. package/dist/builtin/workflows/builtin/goal.js +16 -0
  59. package/dist/builtin/workflows/builtin/index.js +49 -0
  60. package/dist/builtin/workflows/builtin/loop-until-done.js +12 -0
  61. package/dist/builtin/workflows/builtin/open-claude-design.js +11 -0
  62. package/dist/builtin/workflows/builtin/ralph.js +16 -0
  63. package/dist/builtin/workflows/builtin/steering-context.d.ts +6 -0
  64. package/dist/builtin/workflows/builtin/steering-context.js +7 -0
  65. package/dist/builtin/workflows/builtin/tournament.js +13 -0
  66. package/dist/builtin/workflows/package.json +10 -7
  67. package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
  68. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1250 -428
  69. package/dist/builtin/workflows/src/{index.bundle.mjs → index.js} +956 -427
  70. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +403 -0
  71. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +327 -0
  72. package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
  73. package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +60 -0
  74. package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
  75. package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
  76. package/dist/core/agent-session-custom-message-commit.js +12 -5
  77. package/dist/core/agent-session-custom-message-commit.js.map +1 -1
  78. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  79. package/dist/core/agent-session-persistent-custom-messages.js +2 -0
  80. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  81. package/dist/core/agent-session.d.ts.map +1 -1
  82. package/dist/core/agent-session.js +2 -1
  83. package/dist/core/agent-session.js.map +1 -1
  84. package/dist/core/auth-storage.d.ts +2 -0
  85. package/dist/core/auth-storage.d.ts.map +1 -1
  86. package/dist/core/auth-storage.js +4 -0
  87. package/dist/core/auth-storage.js.map +1 -1
  88. package/dist/core/builtin-install-layout.d.ts +1 -1
  89. package/dist/core/builtin-install-layout.d.ts.map +1 -1
  90. package/dist/core/builtin-install-layout.js +1 -1
  91. package/dist/core/builtin-install-layout.js.map +1 -1
  92. package/dist/core/codex-fast-mode.d.ts +10 -7
  93. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  94. package/dist/core/codex-fast-mode.js +31 -13
  95. package/dist/core/codex-fast-mode.js.map +1 -1
  96. package/dist/core/extensions/context-types.d.ts +35 -0
  97. package/dist/core/extensions/context-types.d.ts.map +1 -1
  98. package/dist/core/extensions/context-types.js.map +1 -1
  99. package/dist/core/extensions/index.d.ts +1 -1
  100. package/dist/core/extensions/index.d.ts.map +1 -1
  101. package/dist/core/extensions/index.js.map +1 -1
  102. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  103. package/dist/core/extensions/loader-virtual-modules.js +6 -1
  104. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  105. package/dist/core/model-runtime.d.ts +2 -0
  106. package/dist/core/model-runtime.d.ts.map +1 -1
  107. package/dist/core/model-runtime.js +4 -0
  108. package/dist/core/model-runtime.js.map +1 -1
  109. package/dist/core/runtime-credentials.d.ts +1 -0
  110. package/dist/core/runtime-credentials.d.ts.map +1 -1
  111. package/dist/core/runtime-credentials.js +8 -0
  112. package/dist/core/runtime-credentials.js.map +1 -1
  113. package/dist/core/sdk.d.ts.map +1 -1
  114. package/dist/core/sdk.js +5 -5
  115. package/dist/core/sdk.js.map +1 -1
  116. package/dist/core/session-manager-storage.d.ts +2 -0
  117. package/dist/core/session-manager-storage.d.ts.map +1 -1
  118. package/dist/core/session-manager-storage.js +4 -2
  119. package/dist/core/session-manager-storage.js.map +1 -1
  120. package/dist/core/slash-commands.js +1 -1
  121. package/dist/core/slash-commands.js.map +1 -1
  122. package/dist/core/workflow-stage-admission.d.ts +6 -1
  123. package/dist/core/workflow-stage-admission.d.ts.map +1 -1
  124. package/dist/core/workflow-stage-admission.js +8 -9
  125. package/dist/core/workflow-stage-admission.js.map +1 -1
  126. package/dist/index.d.ts +0 -1
  127. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/footer.js +1 -1
  129. package/dist/modes/interactive/components/footer.js.map +1 -1
  130. package/dist/modes/interactive/interactive-autocomplete.js +1 -1
  131. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  132. package/dist/modes/interactive/interactive-startup.js +1 -1
  133. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  134. package/dist/modes/interactive-engine/isolated-runtime.d.ts +4 -0
  135. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  136. package/dist/modes/interactive-engine/isolated-runtime.js +69 -10
  137. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  138. package/dist/modes/rpc/rpc-client-api.d.ts +18 -2
  139. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  140. package/dist/modes/rpc/rpc-client-api.js +33 -5
  141. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  142. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  143. package/dist/modes/rpc/rpc-command-handler.js +8 -4
  144. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  145. package/dist/modes/rpc/rpc-types.d.ts +13 -0
  146. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  147. package/dist/modes/rpc/rpc-types.js.map +1 -1
  148. package/docs/intercom.md +15 -6
  149. package/docs/packages.md +2 -2
  150. package/docs/providers.md +8 -3
  151. package/docs/quickstart.md +3 -2
  152. package/docs/rpc.md +21 -4
  153. package/docs/settings.md +6 -4
  154. package/docs/skills.md +4 -0
  155. package/docs/subagents.md +1 -1
  156. package/docs/usage.md +1 -1
  157. package/docs/workflows.md +112 -239
  158. package/npm-shrinkwrap.json +32 -32
  159. package/package.json +9 -13
  160. package/dist/builtin/workflows/ambient.d.ts +0 -61
  161. package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +0 -26
  162. package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +0 -391
  163. package/dist/builtin/workflows/builtin/adversarial-verification.ts +0 -34
  164. package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +0 -14
  165. package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +0 -108
  166. package/dist/builtin/workflows/builtin/classify-and-act.ts +0 -40
  167. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +0 -14
  168. package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +0 -96
  169. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +0 -37
  170. package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +0 -41
  171. package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +0 -90
  172. package/dist/builtin/workflows/builtin/generate-and-filter.ts +0 -31
  173. package/dist/builtin/workflows/builtin/goal-artifacts.ts +0 -64
  174. package/dist/builtin/workflows/builtin/goal-convergence.ts +0 -87
  175. package/dist/builtin/workflows/builtin/goal-ledger.ts +0 -139
  176. package/dist/builtin/workflows/builtin/goal-models.ts +0 -72
  177. package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +0 -94
  178. package/dist/builtin/workflows/builtin/goal-prompts.ts +0 -211
  179. package/dist/builtin/workflows/builtin/goal-reducer.ts +0 -176
  180. package/dist/builtin/workflows/builtin/goal-reports.ts +0 -76
  181. package/dist/builtin/workflows/builtin/goal-reverify.ts +0 -305
  182. package/dist/builtin/workflows/builtin/goal-review.ts +0 -139
  183. package/dist/builtin/workflows/builtin/goal-runner.ts +0 -504
  184. package/dist/builtin/workflows/builtin/goal-schemas.ts +0 -89
  185. package/dist/builtin/workflows/builtin/goal-types.ts +0 -164
  186. package/dist/builtin/workflows/builtin/goal.ts +0 -78
  187. package/dist/builtin/workflows/builtin/index.ts +0 -16
  188. package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +0 -87
  189. package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +0 -282
  190. package/dist/builtin/workflows/builtin/loop-until-done.ts +0 -51
  191. package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +0 -305
  192. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +0 -337
  193. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +0 -347
  194. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +0 -313
  195. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -311
  196. package/dist/builtin/workflows/builtin/open-claude-design.ts +0 -57
  197. package/dist/builtin/workflows/builtin/pattern-artifact-root.ts +0 -28
  198. package/dist/builtin/workflows/builtin/progress-scoring.ts +0 -230
  199. package/dist/builtin/workflows/builtin/ralph-core.ts +0 -449
  200. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +0 -100
  201. package/dist/builtin/workflows/builtin/ralph-models.ts +0 -183
  202. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +0 -103
  203. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +0 -100
  204. package/dist/builtin/workflows/builtin/ralph-runner.ts +0 -455
  205. package/dist/builtin/workflows/builtin/ralph.ts +0 -82
  206. package/dist/builtin/workflows/builtin/review-convergence.ts +0 -229
  207. package/dist/builtin/workflows/builtin/selection-math.ts +0 -156
  208. package/dist/builtin/workflows/builtin/steering-context.ts +0 -51
  209. package/dist/builtin/workflows/builtin/tournament-prompts.ts +0 -70
  210. package/dist/builtin/workflows/builtin/tournament-runner.ts +0 -400
  211. package/dist/builtin/workflows/builtin/tournament.ts +0 -73
  212. package/dist/builtin/workflows/builtin/verification-criteria.ts +0 -330
  213. package/dist/builtin/workflows/builtin/verification-prompts.ts +0 -206
  214. package/dist/builtin/workflows/builtin/verification-usage.ts +0 -44
package/docs/workflows.md CHANGED
@@ -36,7 +36,7 @@ Default to a workflow for non-trivial work with a verifiable objective — see [
36
36
  - [Built-in Workflows](#built-in-workflows)
37
37
  - [Writing a Workflow](#writing-a-workflow)
38
38
  - [Scope-Guard Starter Pattern](#scope-guard-starter-pattern)
39
- - [The `workflow()` Definition](#the-workflow-definition)
39
+ - [The `workflow()` definition](#the-workflow-definition)
40
40
  - [WorkflowContext](#workflowcontext)
41
41
  - [Task and Stage Options](#task-and-stage-options)
42
42
  - [StageContext](#stagecontext)
@@ -51,10 +51,9 @@ Default to a workflow for non-trivial work with a verifiable objective — see [
51
51
  - [Workflow Configuration](#workflow-configuration)
52
52
  - [Settings](#settings)
53
53
  - [Package Setup](#package-setup)
54
- - [Programmatic Usage](#programmatic-usage)
55
- - [Fast Inference for Workflow Stages](#fast-inference-for-workflow-stages)
54
+ - [Programmatic usage](#programmatic-usage)
55
+ - [Fast inference for workflow stages](#fast-inference-for-workflow-stages)
56
56
  - [Context Engineering](#context-engineering)
57
- - [Migrating from the `defineWorkflow()` Builder API](#migrating-from-the-defineworkflow-builder-api)
58
57
  - [Design Checklist](#design-checklist)
59
58
  - [Common Mistakes](#common-mistakes)
60
59
  - [Workflow Best Practices](#workflow-best-practices)
@@ -125,6 +124,26 @@ Workflow run identifiers are shown in full everywhere they are presented to user
125
124
 
126
125
  Stage targeting is exact but not UUID-bound, because stage identifiers are not all bare UUIDs. A `stageId` resolves by exact stage id — a bare UUID at the root, the full `runId:stageId` composite for a stage inside a nested workflow, or `tool:<argsHash>` for a `ctx.tool` node — or by exact stage or tool name. Partial names no longer match, so `build` will not select `build-check`. Two stages that share an exact name are still reported as ambiguous, listing the full matching identifiers.
127
126
 
127
+ #### Intercom delivery to pending workflow stages
128
+
129
+ A known workflow stage whose session has not initialized is still addressable by the workflow run's full UUID and its exact authored stage key. From a sibling session in the same workflow Intercom group, use ordinary Intercom delivery:
130
+
131
+ ```ts
132
+ intercom({
133
+ action: "send",
134
+ to: "<runId>:reviewer",
135
+ message: "Scope changed: raw amendment text is now part of the oracle."
136
+ })
137
+ // queued — distinct from live-session delivered
138
+ ```
139
+
140
+ Send material updates through Intercom to every affected workflow stage, including stages that have not started. Atomic queues messages only for known pending stages and delivers them when their sessions initialize, before their first model turn. Live stage delivery is immediate. Use `ask` once the stage session is live and can reply. Unknown run/stage identities retain the ordinary unknown-target failure.
141
+
142
+ The workflows extension persists pending messages with run state across resume/replay and broker restart. Each exact run/stage key accepts 50 queued messages; the next send is refused without eviction. Only sessions in the run's Intercom group may queue them. When the stage session initializes, Atomic delivers its messages FIFO through the ordinary inbound Intercom path **before the first model turn**. The transcript labels them **Messages received before you started**, preserves sender identity and `Sent:` timestamps, and keeps them separate from the stage task prompt. Duplicate logical message IDs and stage-attempt restarts do not redeliver a message.
143
+
144
+ If the destination is skipped, the run is cancelled, or the stage becomes terminal before its session initializes, Atomic marks queued messages undeliverable rather than dropping them. Senders whose messages requested acknowledgment receive a correlated failure notification. Running and completed stages continue through their existing live, late, and post-mortem routes.
145
+
146
+
128
147
  At 80 columns and wider, each `BACKGROUND` card uses two rows so the id is not squeezed beside the workflow name: the first row contains the status glyph and full UUID, and the second contains the workflow name followed by its mode, progress, live-tool total when more than one is active, pending/running `ctx.tool` node names and statuses as space permits, and elapsed/status metadata. Tool nodes are read-only durable graph nodes, not attachable stage chats. The panel renders every qualifying top-level run, so each card is two rows high (plus the existing spacing between cards). Below 80 columns, the panel keeps its collapsed form, omits ids and tool names, and includes a live-tool count when one or more tool nodes are pending or running.
129
148
 
130
149
  For chat surfaces such as workflow status, run detail, dispatch confirmation, and the run picker, a full id wraps onto continuation rows when the card is narrower than the id. The renderer never ellipsizes the id and keeps the card border closed at its minimum layout width, while terminals below that floor — including sub-30-column terminals — can hard-clip the box. An awaiting-input attribution banner is titled `AWAITING INPUT` and contains the same two identity rows — `?` plus the full run id, then the workflow name and optional metadata — while the existing prompt question and options remain below it in the normal prompt UI.
@@ -148,7 +167,7 @@ Below the breakpoint the same run set is represented by the collapsed count line
148
167
  Workflow files are plain TypeScript modules. Create `.atomic/workflows/explain-file.ts`:
149
168
 
150
169
  ```ts
151
- import { workflow } from "@bastani/workflows";
170
+ import { workflow } from "@bastani/atomic/workflows";
152
171
  import { Type } from "typebox";
153
172
 
154
173
  export default workflow({
@@ -231,6 +250,8 @@ The shapes, cheapest first:
231
250
 
232
251
  For every non-trivial workflow task, perform a short workflow-architecture pass before the first launch. Choose the execution shape before starting substantive work; reconnaissance already counts as inline execution. Derive the task's implementation lifecycle needs, whole-codebase research needs, independent work slices, competing strategies, exact API/type/build contracts, schema or generated-artifact contracts, state-transition/lifecycle behavior, deterministic stop conditions, and required evidence.
233
252
 
253
+ For coding tasks, that pass also infers repository intent from repo-level behavior before objectives and acceptance criteria freeze: mine git history (including `git log --show-signature`), merged PRs, issues, commits, and review comments for unwritten conventions — commit signing, message style and issue linking, changelog discipline, PR size and review norms — weighing the requesting user's own activity highest so the authored contract captures norms no doc states. Non-coding tasks mine their analogous available context sources (issue trackers, long-form docs, chat or comment threads, prior artifacts) the same way. Inferred conventions fill contract gaps; they never override the stated objective or explicit repository docs.
254
+
234
255
  Use this compact coverage matrix internally (it may stay concise for a straightforward task), and let every unresolved material row change the graph choice:
235
256
 
236
257
  ```text
@@ -268,7 +289,7 @@ Ask these questions in order and stop at the cheapest shape that satisfies every
268
289
  8. **Is it only specialist evidence-gathering?** If the parent keeps control, no completion gate is needed, and the work is bounded (a debug pass, a parallel research fanout, one noisy investigation), inline subagents are enough—and cheaper than a workflow.
269
290
  9. **Is it truly tiny?** Deterministic, low-risk, single-file/no-test/no-review—answer or edit inline and stop.
270
291
 
271
- A first named workflow launch commits the selected execution shape for the turn. For one task, end the turn after that launch. For an independent queue, the selected shape is a bounded launch wave: issue every planned per-item top-level launch up to the concurrency bound before ending the turn. Do not casually chain unplanned unrelated top-level workflow launches. When one task needs multiple workflow capabilities or dependent items need ordered handoffs, design composition **before** launch: author one custom parent, import project/package definitions or builtins from `@bastani/workflows/builtin`, and call `ctx.workflow(...)`. Nested children preserve their stages and guarantees within the expanded graph up to `maxDepth`, but they remain under the parent's root lifecycle and failure boundary.
292
+ A first named workflow launch commits the selected execution shape for the turn. For one task, end the turn after that launch. For an independent queue, the selected shape is a bounded launch wave: issue every planned per-item top-level launch up to the concurrency bound before ending the turn. Do not casually chain unplanned unrelated top-level workflow launches. When one task needs multiple workflow capabilities or dependent items need ordered handoffs, design composition **before** launch: author one custom parent, import project/package definitions or builtins from `@bastani/atomic/workflows/builtin`, and call `ctx.workflow(...)`. Nested children preserve their stages and guarantees within the expanded graph up to `maxDepth`, but they remain under the parent's root lifecycle and failure boundary.
272
293
 
273
294
  Choose the cheapest complete graph. Routing cues are not a reason to add decorative stages: avoid duplicated research and review loops. Before launch, state the selected graph, why one broad builtin is sufficient or insufficient, the evidence each major stage produces, and the stop/repair conditions. A simple direct match can be one sentence; a composed graph should briefly name its children and task-specific gates.
274
295
 
@@ -386,7 +407,7 @@ A natural-language request for a worktree does not configure runner isolation. I
386
407
  ```ts
387
408
  // .atomic/workflows/issue-to-pr.ts
388
409
  import { spawnSync } from "node:child_process";
389
- import { workflow } from "@bastani/workflows";
410
+ import { workflow } from "@bastani/atomic/workflows";
390
411
  import { Type, type Static } from "typebox";
391
412
 
392
413
  const reviewDecision = Type.Object(
@@ -634,7 +655,7 @@ So every builtin stage prompt carries a **steering propagation contract**:
634
655
  Every bundled workflow wraps its run context once at the definition entry point, so each `ctx.task`, `ctx.chain`, and `ctx.parallel` prompt carries the contract automatically. Do the same in a custom workflow:
635
656
 
636
657
  ```ts
637
- import { withSteeringPropagationContext } from "@bastani/workflows/builtin/steering-context";
658
+ import { withSteeringPropagationContext } from "@bastani/atomic/workflows/builtin/steering-context";
638
659
 
639
660
  export default workflow({
640
661
  name: "my-workflow",
@@ -669,7 +690,7 @@ A long-running stage gets compacted, and compaction ranks lines individually rat
669
690
  Wrap contract text in `keepContext` so it survives verbatim regardless of the compression ratio:
670
691
 
671
692
  ```ts
672
- import { keepContext, workflow } from "@bastani/workflows";
693
+ import { keepContext, workflow } from "@bastani/atomic/workflows";
673
694
 
674
695
  const prompt = [
675
696
  keepContext("Research only. Do not implement code changes."),
@@ -710,7 +731,7 @@ Note what is tagged and what is not: the constraint and the criteria are protect
710
731
  **Steering.** A `send` amendment is authoritative and stages must carry it forward, but it is one short message arriving late into an already-long session, competing against the entire transcript for retention. Tagging it keeps it alive until the stage acts on it:
711
732
 
712
733
  ```
713
- workflow({ action: "send", runId, text:
734
+ intercom({ action: "send", to: `${runId}:<stageKey>`, message:
714
735
  "<keepContext>\nNew requirement: the fix must not change the public API.\n</keepContext>" })
715
736
  ```
716
737
 
@@ -743,7 +764,7 @@ Across these builtins, model-facing stages use compact, outcome-first contracts
743
764
 
744
765
  ### Six composable pattern builtins
745
766
 
746
- The six common patterns are full definitions exported from `@bastani/workflows/builtin`:
767
+ The six common patterns are full definitions exported from `@bastani/atomic/workflows/builtin`:
747
768
 
748
769
  | Workflow | Required input | Bounded/defaulted knobs | Principal declared outputs |
749
770
  |---|---|---|---|
@@ -764,7 +785,7 @@ import {
764
785
  loopUntilDone,
765
786
  ralph,
766
787
  tournament,
767
- } from "@bastani/workflows/builtin";
788
+ } from "@bastani/atomic/workflows/builtin";
768
789
 
769
790
  const research = await ctx.workflow(fanOutAndSynthesize, {
770
791
  inputs: {
@@ -782,6 +803,8 @@ All six can run by name or as nested definitions. Prefer composition over copyin
782
803
  Goal persists the literal objective and immutable acceptance criteria in a run ledger, delegates implementation through bounded orchestrator turns, records receipts, and asks independent reviewers to inspect the current delta. A TypeScript reducer returns `complete`, `blocked`, or `needs_human` rather than trusting free-form completion claims. The complete Goal artifact directory — both its owning run segment and unique `artifact-<id>` segment — is a durable checkpoint. A fresh-ID continuation therefore reuses the source ledger, receipts, and review paths without rerunning replayed producer stages; loading that ledger preserves its existing records without duplicating replayed receipts or reviews. The model-visible `goal-ledger.json` continues to omit internal turn numbers, while a sibling `goal-ledger-state.json` preserves the complete turn-bearing state for lossless continuation reloads. A live chain of continuations also protects that original owner from retention pruning.
783
804
 
784
805
  Goal reviewers derive checks from the literal objective before consulting implementation receipts, inspect the actual checkout delta, and report commands, observed output, and file:line evidence rather than internal reasoning. Shared contracts cover acceptance-matrix traceability, contract-fidelity risks, end-to-end and QA-video evidence, and independent verification. `stop_review_loop` is the authoritative convergence signal: it remains `false` for P0–P2 findings, any `required_by_objective` finding, or unproven implementation/validation requirements; it becomes `true` only when independent evidence proves the objective and only non-blocking or authorized post-approval work remains. The deterministic reducer consumes that signal without reinterpreting free-form prose.
806
+ Goal and Ralph stage prompts — orchestrator, implementation, and reviewer alike — also carry shared code-quality verification guidance that points at the `qlty` skill for linting, auto-formatting, complexity and duplication metrics, and code smells, weighted higher when the objective asks for verifiers or high code quality. Repository-defined checks in `AGENTS.md`/`CLAUDE.md`, package scripts, and CI stay authoritative.
807
+ Both workflows also share repository-intent mining guidance: implementers and reviewers infer maintainer and requesting-user conventions from repository behavior — git history (including `git log --show-signature`), merged PRs, issues and their comments, review comments, commit subjects and trailers, and CI/branch-protection config — covering norms written docs rarely state, such as commit signing, message style and issue linking, changelog discipline, and review etiquette. The dominant, recent, intentional pattern wins over accidental drift, the requesting user's own activity weighs highest, implementers match the inferred conventions (an unsigned commit in a signed history is a miss, not a preference), and reviewers report deviations as convention findings. Behavioral evidence fills contract gaps; it never overrides the literal objective, acceptance criteria, or explicit `AGENTS.md`/`CLAUDE.md` guidance.
785
808
  Goal and Ralph share the same low-confidence finding re-verification and per-round convergence evidence, documented under [`ralph`](#ralph).
786
809
 
787
810
  | Input | Type | Required | Default | Description |
@@ -879,7 +902,7 @@ If required inputs are missing or ambiguous, Atomic asks for them or opens the i
879
902
  Workflow files are TypeScript modules that export a workflow definition:
880
903
 
881
904
  ```ts
882
- import { workflow } from "@bastani/workflows";
905
+ import { workflow } from "@bastani/atomic/workflows";
883
906
  import { Type } from "typebox";
884
907
 
885
908
  export default workflow({
@@ -954,7 +977,6 @@ Authoring basics:
954
977
  - `outputs` declares typed outputs that parent workflows receive from `ctx.workflow(childWorkflow, ...)`.
955
978
  - `run: async (ctx) => { ... }` defines the workflow body.
956
979
 
957
- To migrate an existing file from the removed `defineWorkflow(...).compile()` builder, see [Migrating from the `defineWorkflow()` Builder API](#migrating-from-the-defineworkflow-builder-api) for the full method-to-key mapping, a before/after walkthrough, and a conversion checklist.
958
980
 
959
981
  `prompt` and `task` are aliases for task text inside authored workflow primitives. Prefer `prompt` because it mirrors lower-level `stage.prompt(...)`; `task` remains useful in `ctx.chain(...)` examples.
960
982
 
@@ -1057,7 +1079,7 @@ Protect a stage's role constraints, acceptance criteria, and prohibitions with `
1057
1079
 
1058
1080
  ### Inputs
1059
1081
 
1060
- Inputs are declared with TypeBox `Type.*` schemas in the `inputs` object. Import `Type` from `typebox` directly in workflow files. Workflow packages still declare `typebox` as a peer dependency so TypeBox schemas resolve under `tsc` — see [Programmatic Usage](#programmatic-usage). Common input schemas map to picker kinds and accepted runtime values:
1082
+ Inputs are declared with TypeBox `Type.*` schemas in the `inputs` object. Import `Type` from `typebox` directly in workflow files. Workflow packages still declare `typebox` as a peer dependency so TypeBox schemas resolve under `tsc` — see [Programmatic usage](#programmatic-usage). Common input schemas map to picker kinds and accepted runtime values:
1061
1083
 
1062
1084
  | TypeBox schema | Picker kind | Accepted runtime value |
1063
1085
  |---|---|---|
@@ -1168,7 +1190,7 @@ The same rule applies to inputs: `inputs: { counts: Type.Array(Type.Number()) }`
1168
1190
  When you already have a precise TypeScript type for a deeply-nested serializable value and don't want to hand-write the equivalent TypeBox schema, wrap a permissive runtime schema with `Type.Unsafe<MyType>(...)`. The **static** type becomes exactly `MyType` (so `ctx.inputs`, the `run` return, and `child.outputs` stay precise), while the **runtime** check stays as lenient as the wrapped schema. Use a `type` alias rather than an `interface` for the wrapped type — an `interface` has no implicit index signature, so it does not satisfy the serializable-output constraint:
1169
1191
 
1170
1192
  ```ts
1171
- import { workflow } from "@bastani/workflows";
1193
+ import { workflow } from "@bastani/atomic/workflows";
1172
1194
  import { Type } from "typebox";
1173
1195
 
1174
1196
  type ResearchPacket = {
@@ -1205,7 +1227,7 @@ Tradeoff: `Type.Unsafe<T>()` does not deeply validate at runtime — it trusts t
1205
1227
  - TypeScript checks the `run` return against your declared outputs at **compile time** (a missing required output or wrong value type is a TypeScript error), and TypeBox `Value` checks it at **runtime** (rejecting undeclared keys and enforcing the declared shape recursively).
1206
1228
  - `ctx.workflow(child)` returns a discriminated child result. When `child.exited === false`, `child.outputs` is the child's full declared `outputs` contract; when `child.exited === true`, `child.outputs` is `Partial<TOutputs>` because child `ctx.exit({ outputs })` may intentionally provide only a subset.
1207
1229
 
1208
- Use `Static<typeof schema>` (both `Static` and `TSchema` are re-exported from `@bastani/workflows`) when you need the inferred TypeScript type of a schema directly — for example to type a helper that builds an output value.
1230
+ Use `Static<typeof schema>` (both `Static` and `TSchema` are re-exported from `@bastani/atomic/workflows`) when you need the inferred TypeScript type of a schema directly — for example to type a helper that builds an output value.
1209
1231
 
1210
1232
  ### Stage follow-on user messages
1211
1233
 
@@ -1329,7 +1351,7 @@ User-defined workflows are ordinary TypeScript modules. Import the workflow defi
1329
1351
 
1330
1352
  ```ts
1331
1353
  // .atomic/workflows/shared-research.ts
1332
- import { workflow } from "@bastani/workflows";
1354
+ import { workflow } from "@bastani/atomic/workflows";
1333
1355
  import { Type } from "typebox";
1334
1356
 
1335
1357
  export default workflow({
@@ -1349,7 +1371,7 @@ export default workflow({
1349
1371
  });
1350
1372
 
1351
1373
  // .atomic/workflows/research-and-synthesize.ts
1352
- import { workflow } from "@bastani/workflows";
1374
+ import { workflow } from "@bastani/atomic/workflows";
1353
1375
  import { Type } from "typebox";
1354
1376
  import sharedResearch from "./shared-research.js";
1355
1377
 
@@ -1395,22 +1417,22 @@ import {
1395
1417
  openClaudeDesign,
1396
1418
  ralph,
1397
1419
  tournament,
1398
- } from "@bastani/workflows/builtin";
1420
+ } from "@bastani/atomic/workflows/builtin";
1399
1421
  ```
1400
1422
 
1401
1423
  Or import one individual module:
1402
1424
 
1403
1425
  ```ts
1404
- import goal from "@bastani/workflows/builtin/goal";
1405
- import ralph from "@bastani/workflows/builtin/ralph";
1426
+ import goal from "@bastani/atomic/workflows/builtin/goal";
1427
+ import ralph from "@bastani/atomic/workflows/builtin/ralph";
1406
1428
  ```
1407
1429
 
1408
1430
  Example parent that maps a repository and verifies the synthesis:
1409
1431
 
1410
1432
  ```ts
1411
- import { workflow } from "@bastani/workflows";
1433
+ import { workflow } from "@bastani/atomic/workflows";
1412
1434
  import { Type } from "typebox";
1413
- import { adversarialVerification, fanOutAndSynthesize } from "@bastani/workflows/builtin";
1435
+ import { adversarialVerification, fanOutAndSynthesize } from "@bastani/atomic/workflows/builtin";
1414
1436
 
1415
1437
  export default workflow({
1416
1438
  name: "research-and-verify",
@@ -1570,7 +1592,7 @@ Use a fresh task when one check at a material boundary is enough. This complete
1570
1592
 
1571
1593
  ```ts
1572
1594
  // .atomic/workflows/scope-guard-boundary.ts
1573
- import { workflow } from "@bastani/workflows";
1595
+ import { workflow } from "@bastani/atomic/workflows";
1574
1596
  import { Type, type Static } from "typebox";
1575
1597
 
1576
1598
  const decisionLogSchema = Type.Object(
@@ -1680,7 +1702,7 @@ Use `ctx.stage(...)` when one independent checker needs a retained conversation.
1680
1702
 
1681
1703
  ```ts
1682
1704
  // .atomic/workflows/scope-guard-retained.ts
1683
- import { workflow } from "@bastani/workflows";
1705
+ import { workflow } from "@bastani/atomic/workflows";
1684
1706
  import { Type } from "typebox";
1685
1707
 
1686
1708
  function continueWorker(sessionFile: string | undefined) {
@@ -1755,7 +1777,7 @@ Use a live peer only when steering during generation adds clear value. Both bran
1755
1777
 
1756
1778
  ```ts
1757
1779
  // .atomic/workflows/scope-guard-live.ts
1758
- import { workflow } from "@bastani/workflows";
1780
+ import { workflow } from "@bastani/atomic/workflows";
1759
1781
  import { Type, type Static } from "typebox";
1760
1782
 
1761
1783
  const coordinationSchema = Type.Object(
@@ -1902,9 +1924,9 @@ export default workflow({
1902
1924
 
1903
1925
  The parallel fan-out has one shared parent frontier and downstream persistence waits for both branches. Blocking asks use the guard's retained conversation; the fresh persistence task turns the final transcript into the bounded artifact before correctness review. If Intercom is unavailable, `warn` runs that task as a boundary check, `block` requires `ctx.ui`, and `off` records that no guard approval exists.
1904
1926
 
1905
- ## The `workflow()` Definition
1927
+ ## The `workflow()` definition
1906
1928
 
1907
- `workflow(spec)` is the only supported authoring API. It validates the schema maps, normalizes or infers the name, and returns a frozen branded definition that discovery and `ctx.workflow(...)` accept.
1929
+ Use `workflow(spec)` to author a workflow. It validates the schema maps, normalizes or infers the name, and returns a frozen `WorkflowDefinition` for export, discovery, and `ctx.workflow(...)` composition.
1908
1930
 
1909
1931
  ```typescript
1910
1932
  function workflow<
@@ -1974,6 +1996,19 @@ A recoverable provider or rate-limit block is not the terminal `blocked` status:
1974
1996
 
1975
1997
  A heartbeat the host has already accepted into the parent's queue is beyond that pass, because nothing withdraws a queued message. It is invalidated instead at the moment the parent reads it: the typed card's exact `runId + scheduledAt` identity must still be pending for a current nonterminal run. If the run has since reached a terminal state, this process no longer knows that run, or a durable resume has reused the run id with a later pending boundary, the old heartbeat is excluded from the model's context and cannot steer the parent. That covers all ways a stale card survives — one parked through a long turn while its run finished, one recovered from a previous process at startup, and one admitted before a same-ID durable resume. The card already rendered in your transcript is deliberately left alone: it is a true record that the heartbeat was raised, and rewriting scrollback after the fact would be worse than leaving it. Only the model-facing steer is invalidated.
1976
1998
 
1999
+ ### `budget`
2000
+
2001
+ ```typescript
2002
+ readonly budget?: {
2003
+ readonly maxDurationMs?: number;
2004
+ readonly maxTokens?: number;
2005
+ readonly maxCost?: number;
2006
+ readonly warnAtPercent?: number;
2007
+ };
2008
+ ```
2009
+
2010
+ The optional budget sets duration, token, and cost limits for this workflow. Atomic freezes the declaration into the compiled definition and resolves each field over the extension default when the workflow runs. See [Run budgets](#run-budgets) for precedence and validation rules.
2011
+
1977
2012
  ### `inputs`
1978
2013
 
1979
2014
  ```typescript
@@ -2042,25 +2077,23 @@ interface WorkflowDefinition<
2042
2077
  TInputs extends WorkflowInputValues = WorkflowInputValues,
2043
2078
  TOutputs extends WorkflowOutputValues = WorkflowOutputValues,
2044
2079
  TRunInputs extends WorkflowInputValues = TInputs,
2045
- TDefinitionBrand extends object = {},
2046
2080
  > {
2047
- readonly __piWorkflow: true;
2048
- readonly __runInputs?: TRunInputs;
2049
2081
  readonly name: string;
2050
2082
  readonly normalizedName: string;
2051
2083
  readonly description: string;
2052
2084
  readonly autoAttach?: true;
2053
2085
  readonly heartbeatIntervalMinutes: number;
2086
+ readonly budget?: WorkflowBudget;
2054
2087
  readonly inputs: WorkflowInputSchemaMap;
2055
2088
  readonly outputs?: WorkflowOutputSchemaMap;
2056
2089
  readonly inputBindings?: { readonly worktree?: WorkflowWorktreeInputBinding };
2057
- run(
2058
- ctx: WorkflowRunContext<TInputs, TDefinitionBrand, TOutputs>,
2059
- ): Promise<TOutputs> | TOutputs;
2090
+ run(ctx: WorkflowRunContext<TInputs, TOutputs>): Promise<TOutputs> | TOutputs;
2060
2091
  }
2061
2092
  ```
2062
2093
 
2063
- `workflow({...})` returns definitions that narrow `outputs` to required and carry an internal nominal brand. Do not construct `__piWorkflow` objects by hand: discovery and child composition accept only definitions minted by `workflow({...})`.
2094
+ `TRunInputs` describes the validated inputs accepted by `run(...)` and `ctx.workflow(...)`; it defaults to the workflow's resolved input values.
2095
+
2096
+ `workflow({...})` returns a `WorkflowDefinition` with the resolved name, normalized lookup name, description, runtime defaults, optional budget, schema maps, optional worktree input binding, and `run` function. Authors provide the fields documented above, and Atomic fills the normalized and defaulted values.
2064
2097
 
2065
2098
  ## WorkflowContext
2066
2099
 
@@ -2146,7 +2179,7 @@ ctx.workflow<
2146
2179
  TChildOutputs extends WorkflowOutputValues,
2147
2180
  TChildRunInputs extends WorkflowInputValues = TChildInputs,
2148
2181
  >(
2149
- definition: WorkflowDefinition<TChildInputs, TChildOutputs, TChildRunInputs> & TDefinitionBrand,
2182
+ definition: WorkflowDefinition<TChildInputs, TChildOutputs, TChildRunInputs>,
2150
2183
  ...args: WorkflowRunChildArgs<TChildRunInputs>
2151
2184
  ): Promise<WorkflowChildResult<TChildOutputs>>;
2152
2185
 
@@ -2176,7 +2209,7 @@ const child = await ctx.workflow(sharedResearch, {
2176
2209
  });
2177
2210
  ```
2178
2211
 
2179
- The method accepts only branded definitions, not names, aliases, or path objects. See [Workflow Composition](#workflow-composition) for graph flattening, replay, failure, and parent-exit behavior, and [`WorkflowChildResult`](#workflowchildresult) for the discriminated result.
2212
+ Pass a definition returned by `workflow({...})`. See [Workflow Composition](#workflow-composition) for graph flattening, replay, failure, and parent-exit behavior, and [`WorkflowChildResult`](#workflowchildresult) for the discriminated result.
2180
2213
 
2181
2214
  ### `ctx.stage(name, options?)`
2182
2215
 
@@ -2996,7 +3029,7 @@ The workflow tool action surface is:
2996
3029
  - discovery: `list`, `get`, `inputs`, plus `models` for the configured model catalog
2997
3030
  - execution: named `run` with validated `workflow` and `inputs`
2998
3031
  - inspection: `status`, `stages`, `stage`, `transcript`
2999
- - messaging on nonterminal root runs and run control: `send`, `pause`, `interrupt`, `quit`, `resume`
3032
+ - prompt response: `answer`; run control: `pause`, `interrupt`, `quit`, `resume`; free-form stage communication: ordinary Intercom `send`/`ask` to exact `<runId>:<stageKey>` targets
3000
3033
  - rediscovery: `reload`
3001
3034
 
3002
3035
  Every registered `workflow` tool call has one hard two-minute wall-clock deadline at the shared public tool boundary. The deadline covers request handling through the returned result; for background `run` and `resume`, it therefore covers startup/resume admission and acknowledgement only, not the workflow execution that continues after acknowledgement. A deadline returns one structured result:
@@ -3012,9 +3045,9 @@ Every registered `workflow` tool call has one hard two-minute wall-clock deadlin
3012
3045
  }
3013
3046
  ```
3014
3047
 
3015
- Expiry aborts the request operation signal so work that supports cancellation can stop, discards any later success or error, and never retries the action. The interactive engine remains available for the next command. For mutating actions (`reload`, `run`, `send`, `pause`, `resume`, `interrupt`, and `quit`), the error additionally says that the outcome is unknown and instructs you to inspect workflow status before retrying; a timeout never claims that a mutation succeeded. When a timed-out `run` has already allocated its detached run, the structured result includes that exact full `runId`; inspect `status` with that id before any retry. A timeout before run allocation has no `runId`. Read-only actions (`models`, `list`, `get`, `inputs`, `status`, `stages`, `stage`, and `transcript`) omit that unknown-state guidance.
3048
+ Expiry aborts the request operation signal so work that supports cancellation can stop, discards any later success or error, and never retries the action. The interactive engine remains available for the next command. For mutating actions (`reload`, `run`, `answer`, `pause`, `resume`, `interrupt`, and `quit`), the error additionally says that the outcome is unknown and instructs you to inspect workflow status before retrying; a timeout never claims that a mutation succeeded. When a timed-out `run` has already allocated its detached run, the structured result includes that exact full `runId`; inspect `status` with that id before any retry. A timeout before run allocation has no `runId`. Read-only actions (`models`, `list`, `get`, `inputs`, `status`, `stages`, `stage`, and `transcript`) omit that unknown-state guidance.
3016
3049
 
3017
- From interactive chat, named workflow launches run in the background so the parent chat stays available. Run `/workflow connect <run>` to see agents working and chat with and steer each stage. Inspection and control calls (`status`, `stages`, `stage`, `transcript`, `send`, `pause`, `resume`, `interrupt`, `quit`) remain available while work runs.
3050
+ From interactive chat, named workflow launches run in the background so the parent chat stays available. Run `/workflow connect <run>` to see agents working and chat with and steer each stage. Inspection, prompt-response, and control calls (`status`, `stages`, `stage`, `transcript`, `answer`, `pause`, `resume`, `interrupt`, `quit`) remain available while work runs.
3018
3051
 
3019
3052
  `workflow({ action: "models" })` returns the registry's configured-auth catalog snapshot in registry order. Each entry includes `provider`, `id`, `fullId`, an `isCurrent` marker, and `availableThinkingLevels` derived from the real model's `reasoning` and `thinkingLevelMap` metadata. This is not proof of credentials, entitlements, OAuth freshness, or live provider access, and it exposes no authentication details.
3020
3053
 
@@ -3125,10 +3158,10 @@ workflow({ action: "transcript", runId: "<full-run-uuid>", stageId: "review" })
3125
3158
  workflow({ action: "transcript", runId: "<full-run-uuid>", stageId: "review", tail: 40 })
3126
3159
  workflow({ action: "transcript", runId: "<full-run-uuid>", stageId: "review", limit: 20, includeToolOutput: true })
3127
3160
 
3128
- // send is admitted only while the authoritative root workflow is nonterminal.
3129
- workflow({ action: "send", runId: "<full-run-uuid>", stageId: "review", text: "please focus on tests" })
3130
- workflow({ action: "send", runId: "<full-run-uuid>", stageId: "approval", promptId: "prompt-1", response: true, delivery: "answer" })
3131
- workflow({ action: "send", runId: "<full-run-uuid>", stageId: "review", message: "continue with tests", delivery: "resume" })
3161
+ // Free-form stage communication uses Intercom; prompt responses use workflow answer.
3162
+ intercom({ action: "send", to: "<full-run-uuid>:review", message: "please focus on tests" })
3163
+ workflow({ action: "answer", runId: "<full-run-uuid>", stageId: "approval", promptId: "prompt-1", response: true })
3164
+ workflow({ action: "resume", runId: "<full-run-uuid>", stageId: "review", message: "continue with tests" })
3132
3165
 
3133
3166
  workflow({ action: "pause", runId: "<full-run-uuid>" })
3134
3167
  workflow({ action: "pause", runId: "<full-run-uuid>", stageId: "review" })
@@ -3153,25 +3186,15 @@ Control behavior:
3153
3186
 
3154
3187
  - `runId` requires the full 36-character run UUID for every lifecycle and inspection action, including `status`. User-facing status surfaces print that exact value, so pass it back verbatim; typed prefixes are rejected with a distinct `Run id must be a full 36-character UUID` diagnostic rather than resolved. Because ids are matched exactly and are unique, no run target is ambiguous. Status lists and run pickers show top-level user-launched workflows; nested child runs are implementation details of the expanded parent graph.
3155
3188
  - `status`, `stages`, `stage`, and `transcript` with an explicit full `runId` first use the current session store, then perform one exact DBOS hydration when that id is absent locally. This is inspection only: Atomic does not claim ownership, change status, run workflow code, or resume the workflow. A stale durable `running` root is shown as `crashed` with its resumability and an explicit `/workflow resume <id>` hint; fresh work owned by another Atomic process remains `running`, offers read-only status guidance, and stays protected from local control or resume. Deleted/tombstoned, absent, malformed, cyclic, orphaned, nonreciprocal, out-of-scope, and duplicate-node records report distinct failures instead of inventing a partial graph. `status` without `runId` remains current-session-only and never scans durable history.
3156
- - `status` without `runId` lists every top-level run in the session with a concise per-run summary: the full run id, workflow name, run status, started/ended timing with pause-adjusted elapsed time, currently active stages, and awaiting-input details (count plus the stage, prompt id, kind, and message for each pending human prompt). In-flight runs are listed first. The summaries carry the exact identifiers that `pause`/`resume`/`interrupt`/`quit`/`send` accept, so an orchestrating agent can list runs and act on them directly.
3189
+ - `status` without `runId` lists every top-level run in the session with a concise per-run summary: the full run id, workflow name, run status, started/ended timing with pause-adjusted elapsed time, currently active stages, and awaiting-input details (count plus the stage, prompt id, kind, and message for each pending human prompt). In-flight runs are listed first. The summaries carry the exact identifiers that `answer`, `pause`, `resume`, `interrupt`, and `quit` accept, so an orchestrating agent can list runs and act on them directly.
3157
3190
  - `statusFilter` narrows the `status` run listing: run statuses (`pending`, `running`, `paused`, `blocked`, `completed`, `failed`, `skipped`, `cancelled`, `killed`) match runs directly, `awaiting_input` selects runs with at least one stage awaiting input or pending human prompt, and `all` (the default) includes everything.
3158
3191
  - `format: "json"` on data-bearing inspection actions (`status`, `stages`, `stage`, `transcript`) returns the full structured result; the default text output for `status` is the concise per-run summary list.
3159
3192
  - `status` / `status <runId>` show terminal `ctx.exit(...)` statuses (`completed`, `skipped`, `cancelled`, or `blocked`) and the optional exit reason when one was supplied.
3160
3193
  - `stages` lists stage summaries, including flattened stages from nested `ctx.workflow(...)` imports and `sessionFile`/`transcriptPath` when a stage has a persisted session. Use `statusFilter: "all"` to include completed, failed, skipped, and pending stages.
3161
3194
  - `stage` returns details for one stage by exact stage id or exact stage name, including nested child stages shown in the expanded graph and the persisted `sessionFile` when available. User-facing graph and control messages print full stage IDs; pass one back verbatim, or use the stage's exact name. Prefixes and partial names no longer resolve. Two stages sharing an exact name return an ambiguity diagnostic rather than selecting one.
3162
3195
  - `transcript` is reference-first with a small preview by default: it returns metadata, transcript paths, and up to 5 recent entries. For targeted lookup, quote the exact `sessionFile`/`transcriptPath` value without changing platform separators (preserve Windows backslashes), search it with `rg` or `grep`, then read only small surrounding ranges. Text results include JSON-escaped `sessionFileJson`/`transcriptPathJson` lines for copy-safe path literals. Pass explicit `tail` or `limit` to override the 5-entry preview; `tail` overrides `limit`; `includeToolOutput` includes captured snapshot tool output in snapshot transcript results.
3163
- - `send` operates only while the authoritative root workflow is nonterminal; delivery modes are `auto`, `answer`, `prompt`, `steer`, `followUp`, and `resume`.
3164
- - A terminal root (`completed`, `failed`, `skipped`, `cancelled`, `killed`, or terminal `blocked`) rejects every programmatic send with `status: "failed"`, `code: "WORKFLOW_TERMINAL"`, `delivery: "rejected"`, the requested root run id and terminal status, and guidance to start a new workflow. Proceed inline instead only when the remaining work is small, deterministic, and low risk.
3165
- - Atomic checks an already-terminal root before stage resolution, nested-owner routing, prompt inspection, retained-session probing or revival, handle lookup, message admission, and delivery selection. That rejection creates no agent session or handle, appends no transcript, starts no model/tool/file work, answers no input, and mutates no workflow/stage snapshot. Missing or malformed retained sessions receive the same root-terminal error without being probed.
3166
- - Atomic checks the same shared terminal authority again at the final synchronous SDK message-admission boundary. If a live root terminates while retained-session creation is pending, the send fails with `WORKFLOW_TERMINAL`, disposes its unclaimed provisional session/handle, and admits no prompt, model request, tool/file work, transcript append, or workflow-state mutation. A user-driven attach or Intercom claim remains independent and keeps the retained handle.
3167
- - Prompt answers on a nonterminal root can include `promptId` and can carry answer content in `response`, `text`, or `message`; structured UI prompts usually prefer `response`.
3168
- - Primitive HIL answers are normalized by prompt kind: `input` and `editor` require a text string; `confirm` accepts booleans or trimmed, case-insensitive `true`/`false`, `yes`/`y`, `no`/`n`, `approve`/`reject`, and `confirm`/`deny`; `select` accepts a trimmed, case-insensitive choice label or a 1-based numeric index.
3169
- - An answer that does not match the pending primitive prompt stays pending. Atomic returns a `noop` explaining the expected shape and, for `select`, the available choices; it never silently chooses the first option. Prompt-card answers from the interactive graph keep their existing typed path.
3170
- - For a live idle, non-paused stage, `prompt`, `followUp`, and eligible `auto` delivery all start a fresh prompt immediately; an actively streaming `followUp` remains queued and `steer` remains steering, so neither starts a concurrent prompt. During controlled pause, every context-bearing delivery remains held instead. The result's `delivery` and message describe the action actually taken (`prompt`, `followUp`, `steer`, `answer`, or `resume`), not merely the requested mode. Explicit `resume` against a stage that is not paused is a truthful no-op, and explicit message deliveries cannot bypass a paused stage; resume it first.
3171
- - Delivery timing is mode-specific and deterministic. `steer` (and `auto` against a streaming stage) enters the steering queue and is consumed after the current assistant response finishes its whole tool batch, before the next model request — never between two tool calls of the same response. `followUp` enters the follow-up queue and is consumed only when the agent would otherwise stop. Sequential sends keep submission order *within* the queue they select; there is no global FIFO across the two queues, so a steer submitted after a follow-up is still consumed first. Ordering is promised relative to admission into the selected queue, not relative to when a caller started a request whose session setup or admission finishes later.
3172
- - While the root remains nonterminal, follow-up messaging to an eligible completed child stage can reuse its retained `sessionFile`. After the root terminates, use explicit `/workflow attach <run-id> <stage>` post-mortem chat instead; `workflow send` never admits a retained-session turn after terminal publication.
3173
- - Arbitrary `ctx.ui.custom<T>` widget prompts require the interactive workflow graph and return a clear unsupported message when targeted through `send`.
3174
- - On a nonterminal root, `delivery: "auto"` first answers a pending prompt, then resumes paused work, then steers a streaming stage, and finally starts a fresh prompt when the live stage is idle.
3196
+ - `answer` responds only to a pending primitive or structured human-input prompt. It accepts `promptId` plus `response`, `text`, or `message`, preserves prompt-kind validation, and never sends stage chat, steers, resumes, or starts a model turn.
3197
+ - Send free-form updates through ordinary Intercom to `<runId>:<stageKey>`. Atomic delivers immediately to live stages and queues messages for known stages that have not started, delivering them before their first model turn; unknown stages remain unknown targets. Use `ask` once the target has a reply-capable live session. Use `workflow resume` only for paused workflow control.
3175
3198
  - `pause`, `interrupt`, and `quit` can target one top-level run or `all: true`; `stageId` cannot be combined with `all: true`. Stage-scoped `pause` and `interrupt` controls can target a visible nested child stage from the expanded graph. Atomic routes stage controls to the owning nested run internally.
3176
3199
  - `interrupt` and `quit` can also name one in-flight `ctx.tool` node with `stageId`, by expanded node id, local `tool:<argsHash>` id, or tool name. Both mean the same thing for a tool: abort that single call now. Tool nodes stay non-attachable — this is an abort control, not a chat target. Identifiers resolve exactly first and then uniquely; a name shared by two tool nodes (or by a stage and a tool) returns the same ambiguity diagnostic stages get, listing each match as `<name> (tool)`.
3177
3200
  - Aborting one tool node leaves every sibling stage and sibling tool node running and does not pause the run. The node becomes `cancelled`, writes no replayable checkpoint, and re-runs on a later resume. Whether the run itself survives is ordinary author control flow: an awaited `ctx.tool` that is aborted rejects, exactly as it would for any other failure, unless the workflow catches it. A node that has already settled reports that it is not running rather than silently succeeding.
@@ -3208,7 +3231,7 @@ When a callback was abandoned, its executor stays alive but stops owning the run
3208
3231
 
3209
3232
  When a paused stage interrupted an active model turn, Atomic preserves that turn's existing pause loop: a non-empty resume message is delivered exactly once through the resumed loop, and (if the stage has not finalized) Atomic injects `Continue where you left off. If you believe you are finished with your original task (or a redefined task if the user told you), stop.` before normal completion/readiness handling. A no-message interrupted-turn resume injects the same continuation directly. A different state applies when the stage was idle and waiting for a new stage-chat turn: resuming with a non-empty message starts exactly one fresh prompt containing the text, while an empty resume only releases the pause and does not fabricate a user turn or continuation.
3210
3233
 
3211
- The same continuation applies to user messages queued into a live streaming stage. Steering a turn (Enter in an attached stage chat), queueing a follow-up (Ctrl+F), or using `workflow({ action: "send" })` with `steer`/`followUp` delivery arms the identical continuation prompt, which Atomic injects once when the interrupted turn ends — even if several messages were queued during that turn — so a steered stage returns to its original (or user-redefined) objective instead of stopping after answering the queued message.
3234
+ The same continuation applies to user messages queued into a live streaming stage. Steering a turn (Enter in an attached stage chat) or queueing a follow-up (Ctrl+F) arms the identical continuation prompt, which Atomic injects once when the interrupted turn ends — even if several messages were queued during that turn — so a steered stage returns to its original (or user-redefined) objective instead of stopping after answering the queued message.
3212
3235
 
3213
3236
  Messages delivered to an idle stage start a fresh user turn immediately and receive no continuation nudge; abort, kill, workflow exit, and finalized/fail-fast stage boundaries suppress late prompt creation and continuation injection.
3214
3237
 
@@ -3220,7 +3243,7 @@ These are distinct operations. *Resuming workflow execution* (`/workflow resume`
3220
3243
 
3221
3244
  Any eligible terminal agent stage with a valid retained session opens as an interactive post-mortem chat through the explicit user-driven TUI path: completed-workflow inspection, `/workflow attach`, or `/workflow connect` followed by stage selection, including restored/replayed durable snapshots after a restart. Explicit `/workflow attach <root-run> <nested-stage>` targets are resolved through the expanded graph and routed to the child run that owns the stage while the overlay remains rooted on the requested graph; the resolved owner is preserved when sibling child workflows reuse the same local stage ID.
3222
3245
 
3223
- `workflow({ action: "send" })` is not a post-mortem path. Once the root is terminal, programmatic sends fail closed before retained-session probing or nested-stage routing. Start a new workflow if tracked work remains; proceed inline only for small, deterministic, low-risk work.
3246
+ Intercom and explicit `/workflow attach` own stage communication. The workflow tool has no free-form message action; start a new workflow if tracked work remains after a terminal root.
3224
3247
 
3225
3248
  When a nested stage is reopened after a restart or from another checkout through the explicit TUI path, its session cwd comes from the durable root workflow (resolved workflow cwd first, then original invocation cwd) while stage-control ownership remains with the actual child run. Follow-up turns are appended in place to the stage's retained session (no separate fork), so the agent may still invoke its ordinary tools and cause side effects; only the workflow DAG, run/stage status, results, timings, checkpoints, and topology are immutable. Post-mortem chat does not resume or modify workflow execution state.
3226
3249
 
@@ -3230,7 +3253,7 @@ Every host session replacement or shutdown invalidates post-mortem handles, incl
3230
3253
 
3231
3254
  When a known stage cannot be reopened, the attached chat shows the complete `SESSION UNAVAILABLE` explanation down to the supported 40-column minimum instead of incorrectly labeling an invalid file as an archived transcript. Recoverably failed stages keep their execution-resume semantics and are not silently reopened as post-mortem chat.
3232
3255
 
3233
- Completed stages also remain addressable by blocking `intercom.ask` calls from sibling workflow stages. If an ask reaches a completed target with a retained conversation, Atomic schedules one serialized post-mortem turn in that exact conversation; no manual `workflow send` follow-up is needed.
3256
+ Completed stages also remain addressable by blocking `intercom.ask` calls from sibling workflow stages. If an ask reaches a completed target with a retained conversation, Atomic schedules one serialized post-mortem turn in that exact conversation.
3234
3257
 
3235
3258
  The target sees the original ask, and its normal `intercom.reply` remains correlated to the originating child session and message ID. The parent chat or another session cannot satisfy the waiter. Late-message routing uses single-owner claiming: after the workflow post-mortem router claims a completed-stage ask and assigns its completion promise, later listeners preserve that claim, making bundled extension registration order irrelevant.
3236
3259
 
@@ -3289,13 +3312,13 @@ When a workflow needs human input, answer in the graph viewer or attached stage
3289
3312
  /workflow attach <run-id> <stage-id-or-name>
3290
3313
  ```
3291
3314
 
3292
- Agents can answer primitive and structured pending prompts programmatically with `workflow({ action: "send", delivery: "answer", ... })` only while the root workflow is nonterminal; use `promptId` when it is present in the stage details, and provide answer content with `response`, `text`, or `message`. Arbitrary custom TUI widget prompts intentionally refuse this path in iteration 1 because a generic `T` cannot be reconstructed safely from a non-TUI payload.
3315
+ Agents can answer primitive and structured pending prompts programmatically with `workflow({ action: "answer", ... })` only while the root workflow is nonterminal; use `promptId` when it is present in the stage details, and provide answer content with `response`, `text`, or `message`. Arbitrary custom TUI widget prompts intentionally refuse this path in iteration 1 because a generic `T` cannot be reconstructed safely from a non-TUI payload.
3293
3316
 
3294
3317
  `ctx.ui.custom<T>(factory, options?)` reuses Atomic's TUI component path: the factory receives the same real `(tui, theme, keybindings, done)` types as extension `ctx.ui.custom`, and the workflow resumes with the value passed to `done(value)`. Use `options.label` for a safe display-only graph/status label and `options.replayIdentity` when widget semantics can change without the callsite changing. Do not put secrets in labels or replay identities; only a hash of the identity is stored, and label text is not part of replay identity. Both inline connected rendering and `overlay: true` mount in the graph viewer's attached stage chat: overlay is a placement hint rather than a capability request, so an in-stage `ask_user_question` — which always asks for an overlay — mounts, takes focus, and resolves like any other custom prompt. There is no nested host overlay above the graph chrome; the widget occupies the stage-chat custom-UI slot and `overlayOptions` / `onHandle` are not consumed there.
3295
3318
 
3296
3319
  Prompt answers are replayable only while the source run remains in the live in-memory store. `StageSnapshot.promptAnswerState` is snapshot-safe metadata for continuation: `available` means a matching live answer can be replayed, `unavailable` means the matching prompt node exists but its private answer was purged, and `ambiguous` means multiple matching prompt nodes exist so Atomic asks again. The raw answer lives in a private `PromptAnswerRecord` ledger, is never written to snapshots or persistence, and remains resident in memory until the answer is cleared, the run is removed, or the store is cleared.
3297
3320
 
3298
- Prompt replay keys include the prompt kind, message text, select choices, input/editor initial value, custom prompt identity hash, and hashed author callsite, so changing any of those inputs may intentionally re-ask on continuation. An empty `ctx.ui.select(..., [])` has no answerable choices and throws before creating a prompt node. Arbitrary custom-widget answers cannot be supplied through `workflow send`; focus the `custom` awaiting-input node in the interactive graph instead.
3321
+ Prompt replay keys include the prompt kind, message text, select choices, input/editor initial value, custom prompt identity hash, and hashed author callsite, so changing any of those inputs may intentionally re-ask on continuation. An empty `ctx.ui.select(..., [])` has no answerable choices and throws before creating a prompt node. Arbitrary custom-widget answers cannot be supplied through `workflow answer`; focus the `custom` awaiting-input node in the interactive graph instead.
3299
3322
 
3300
3323
  If the user answers a human-in-the-loop prompt in the workflow UI or stage UI broker, the stage receives the answer directly and the active main chat receives a display-only notice (`triggerTurn: false`, `excludeFromContext: true`) containing a concise answer summary. The notice is rendered for the user and persisted for audit, but it does not wake the model, enter LLM context, or authorize answering any other workflow prompt. Prompt answers sent by the main-chat `workflow` tool are suppressed from this notice because the tool result already informs the current turn.
3301
3324
 
@@ -3303,7 +3326,7 @@ When an interactive, non-schema workflow stage calls `ask_user_question`, Atomic
3303
3326
 
3304
3327
  In this chat-answer flow, choosing the ready option completes the stage and releases dependent stages. Choosing the not-ready option keeps the stage open for a genuine stage-chat turn and brokers readiness again after that turn. A chat answer is never treated as an invisible stay decision. On the readiness gate, **Type something.** sends the typed text as the next stage-chat message (empty or whitespace-only text cannot be submitted). **Chat about this** is a plain option — it does not open an inline editor — and stays by sending `The user would like to chat more about this`.
3305
3328
 
3306
- The readiness prompt can be answered in the attached stage UI or with `workflow({ action: "send", delivery: "answer", ... })`. Ordinary structured-option answers retain their existing readiness behavior. A schema-backed stage that has successfully finalized through `structured_output` is terminal and does not reopen this readiness gate.
3329
+ The readiness prompt can be answered in the attached stage UI or with `workflow({ action: "answer", ... })`. Ordinary structured-option answers retain their existing readiness behavior. A schema-backed stage that has successfully finalized through `structured_output` is terminal and does not reopen this readiness gate.
3307
3330
 
3308
3331
 
3309
3332
  ## Durable Workflows and Cross-Session Resume
@@ -3427,7 +3450,7 @@ A run quit while a `ctx.tool` call was in flight resumes the same way: the unfin
3427
3450
 
3428
3451
  Selecting a completed target—or a checkpointed failed target marked non-resumable—follows a separate read-only open path. Atomic reconstructs root and reciprocal nested child-run snapshots from authoritative checkpoints, remaps persisted source-stage, boundary, and tool references into a stable expanded hierarchy, and never calls the resume dispatcher or runs workflow code, tools, tasks, or prompts. These graphs remain inspectable even when no retained chat transcript survives, including tool-only graphs.
3429
3452
 
3430
- A terminal child stage with a valid retained session may be reopened for detached post-mortem conversation through `/workflow attach` or completed graph inspection. Follow-up is routed to that real child `{runId, stageId}` and may append chat, but it cannot pause, resume, retry, mutate root or child execution state, write a terminal checkpoint, or emit a duplicate lifecycle notice. Programmatic `workflow send` rejects the terminal root before nested-owner routing or session probing. Tool nodes never offer chat attachment.
3453
+ A terminal child stage with a valid retained session may be reopened for detached post-mortem conversation through `/workflow attach` or completed graph inspection. Follow-up is routed to that real child `{runId, stageId}` and may append chat, but it cannot pause, resume, retry, mutate root or child execution state, write a terminal checkpoint, or emit a duplicate lifecycle notice. Tool nodes never offer chat attachment.
3431
3454
 
3432
3455
  New tool checkpoints persist topology. A current-format tool checkpoint created before that additive topology existed still replays safely: its cached output remains authoritative and its callback is never rerun. Root-level inspection derives deterministic fallback identity/order from checkpoint identity and record order. If a topology-less cached tool replays inside a child workflow, Atomic first appends awaited topology metadata with the current child/boundary ownership, without replacing the original output checkpoint. Foreign or malformed checkpoint formats remain excluded.
3433
3456
 
@@ -3490,7 +3513,7 @@ Atomic discovers workflow definitions in this order:
3490
3513
  | `~/.atomic/agent/extensions/workflow/config.json` | Global | `workflows.<name>.path` for user-wide configured paths |
3491
3514
  | `~/.atomic/agent/workflows/*.{ts,js,mjs,cjs}` | Global | Legacy `~/.pi/agent/workflows/` is also checked |
3492
3515
  | Installed Atomic packages | Package | Uses package metadata or conventional `workflows/` directories |
3493
- | Bundled workflows | Built-in | Shipped with `@bastani/workflows` |
3516
+ | Bundled workflows | Built-in | Shipped with `@bastani/atomic/workflows` |
3494
3517
 
3495
3518
  A workflow module may export one default workflow definition and/or named workflow definitions. Discovery checks the default export first, then named exports.
3496
3519
 
@@ -3507,14 +3530,14 @@ To co-locate reusable helpers with your workflows — for example a `ctx.ui.cust
3507
3530
 
3508
3531
  ```ts
3509
3532
  // .atomic/workflows/release-picker.ts
3510
- import { workflow } from "@bastani/workflows";
3533
+ import { workflow } from "@bastani/atomic/workflows";
3511
3534
  import { Type } from "typebox";
3512
3535
  import { tableSelectorFactory } from "./lib/table-selector.js";
3513
3536
  ```
3514
3537
 
3515
3538
  ```ts
3516
3539
  // .atomic/workflows/lib/table-selector.ts
3517
- import type { WorkflowCustomUiFactory } from "@bastani/workflows";
3540
+ import type { WorkflowCustomUiFactory } from "@bastani/atomic/workflows";
3518
3541
 
3519
3542
  export const tableSelectorFactory: WorkflowCustomUiFactory<{ id: string; name: string }> = (
3520
3543
  tui,
@@ -3714,22 +3737,14 @@ atomic -e ./local-workflow-package
3714
3737
 
3715
3738
  Workflow stage sessions inherit the same package and temporary `-e` resource discovery snapshot as the main chat. That means a workflow loaded from an external package or directory can start stages that see the package's extensions/tools, subagents and agent definitions, skills, prompt templates, themes, workflows, and trusted borrowed project-local resources without sharing the parent chat's resource-loader instance. Passing an explicit `resourceLoader` in stage options still opts that stage out of this inheritance.
3716
3739
 
3717
- ## Programmatic Usage
3718
-
3719
- `@bastani/workflows` is an Atomic package extension. It registers:
3740
+ ## Programmatic usage
3720
3741
 
3721
- - `/workflow <name> key=value ...` for interactive named runs
3722
- - `/workflow connect|attach|pause|interrupt|quit|resume|status|inputs|reload` for live control, inspection, and rediscovery
3723
- - the `workflow` tool for named execution, discovery, inspection, messaging, run control, and reload
3742
+ `@bastani/atomic/workflows` is Atomic's published workflow SDK. Import `workflow` from that specifier, import `Type` from `typebox`, and export the definition returned by `workflow({...})`. Keep runtime helpers such as widget factories and shared utilities in a subdirectory outside the top-level discovery scan, such as `.atomic/workflows/lib/`; see [Workflow Locations](#workflow-locations).
3724
3743
 
3725
- The signatures in this reference follow the externally shipped standalone authoring declaration in `packages/workflows/src/authoring.ts`. Atomic's internal runtime types may specialize opaque SDK values or add executor-only integration fields; those are not ordinary workflow-package authoring API.
3726
-
3727
- Workflow definition files must export definitions produced by `workflow({...})`. Keep non-workflow runtime helpers (widget factories, shared utilities) in a subdirectory the discovery scan ignores, such as `.atomic/workflows/lib/` — see [Workflow Locations](#workflow-locations). The former imperative object-form runner is not part of the public SDK, and authored workflow files cannot use `runWorkflow` as a runner from `@bastani/workflows`.
3728
-
3729
- Standalone TypeScript workflow packages type-check the SDK import without a hand-authored `.d.ts`, `declare module` shim, or `tsconfig` `paths` alias. The SDK types ship with `@bastani/atomic`, so a workflow package depends only on `@bastani/atomic` (plus a `typebox` peer):
3744
+ Package authors list both `@bastani/atomic` and `typebox` in `peerDependencies`. The `@bastani/atomic` package publishes compiled JavaScript and declarations for `@bastani/atomic/workflows`, `@bastani/atomic/workflows/builtin`, and each `@bastani/atomic/workflows/builtin/*` module. TypeScript resolves those exports directly under `moduleResolution: NodeNext`. When Atomic executes a workflow file, its runtime loader resolves the same published specifiers to Atomic's in-memory SDK.
3730
3745
 
3731
3746
  ```ts
3732
- import { workflow } from "@bastani/workflows";
3747
+ import { workflow } from "@bastani/atomic/workflows";
3733
3748
  import { Type } from "typebox";
3734
3749
 
3735
3750
  export default workflow({
@@ -3746,29 +3761,7 @@ export default workflow({
3746
3761
  });
3747
3762
  ```
3748
3763
 
3749
- Workflow SDK type resolution depends on the package's other imports:
3750
-
3751
- - A package that imports `@bastani/atomic` anywhere (for example, an extension shipped in the same package) automatically resolves the workflow SDK types. `@bastani/atomic`'s root declarations reference the ambient bridge, so no extra configuration is needed.
3752
- - A pure workflow-only package — one that imports nothing but `@bastani/workflows` — adds a single opt-in so TypeScript loads the ambient bridge. Set it once for the project in `tsconfig.json`:
3753
-
3754
- ```jsonc
3755
- {
3756
- "compilerOptions": {
3757
- "module": "NodeNext",
3758
- "moduleResolution": "NodeNext",
3759
- "types": ["@bastani/atomic/workflows/ambient"]
3760
- }
3761
- }
3762
- ```
3763
-
3764
- or add a single reference directive at the top of one workflow file:
3765
-
3766
- ```ts
3767
- /// <reference types="@bastani/atomic/workflows/ambient" />
3768
- ```
3769
-
3770
- Either form makes `import { workflow } from "@bastani/workflows"
3771
- import { Type } from "typebox"` and the `@bastani/workflows/builtin/*` composition imports resolve under `tsc` (`moduleResolution: NodeNext`) with no hand-authored `.d.ts`, no `declare module` shim, and no `paths` alias. `@bastani/workflows` is not a separate npm package — its types ship with `@bastani/atomic` — so list both `@bastani/atomic` and `typebox` (workflow files import `Type` from `typebox`) in `peerDependencies`. Runtime discovery and loading via `atomic.workflows` are unchanged: Atomic's loader still supplies the SDK when workflow files execute.
3764
+ Programmatic callers import `run` and call `run(definition, inputs)` with an exported definition and validated inputs. Use `createRegistry()` when an integration needs to register, merge, or look up several definitions before selecting one to run. The extension also registers the `/workflow` commands and the `workflow` tool for named execution, discovery, inspection, messaging, run control, and reload.
3772
3765
 
3773
3766
 
3774
3767
  ### `workflow(spec)`
@@ -3783,7 +3776,7 @@ function workflow<
3783
3776
  ): AuthoredWorkflowDefinition<TInputs, TOutputs>;
3784
3777
  ```
3785
3778
 
3786
- Creates the frozen branded definition documented in [The `workflow()` Definition](#the-workflow-definition). Discovery accepts only definitions minted by this function.
3779
+ Creates the frozen definition documented in [The `workflow()` definition](#the-workflow-definition). Export the returned definition or pass it to `ctx.workflow(...)`, `run(...)`, or a registry.
3787
3780
 
3788
3781
  ### `createRegistry(initial?)`
3789
3782
 
@@ -3808,7 +3801,7 @@ interface WorkflowRegistry {
3808
3801
  Creates an immutable-style registry keyed by normalized workflow name. `register`, `merge`, and `remove` return registries rather than mutating the current registry.
3809
3802
 
3810
3803
  ```ts
3811
- import { createRegistry, workflow } from "@bastani/workflows";
3804
+ import { createRegistry, workflow } from "@bastani/atomic/workflows";
3812
3805
  import { Type } from "typebox";
3813
3806
 
3814
3807
  const alpha = workflow({
@@ -4025,16 +4018,8 @@ The factory creates an isolated registry; `cancellationRegistry` is the default
4025
4018
  export type { Static, TSchema } from "typebox";
4026
4019
  ```
4027
4020
 
4028
- These TypeBox types are re-exported for authoring helpers. The runtime `Type` builder is not re-exported; import it from `typebox`.
4021
+ These TypeBox types are re-exported for authoring helpers. Import the runtime `Type` builder from `typebox`.
4029
4022
 
4030
- ### `runWorkflow` (removed)
4031
-
4032
- ```typescript
4033
- /** @deprecated Always throws a migration error. */
4034
- const runWorkflow: never;
4035
- ```
4036
-
4037
- This runtime migration stub exists only so old modules fail at the callsite with a clear error. Use `workflow({...})` for authoring and `run(...)` for programmatic execution.
4038
4023
 
4039
4024
  ### Builtin workflow exports
4040
4025
 
@@ -4049,23 +4034,25 @@ import {
4049
4034
  openClaudeDesign,
4050
4035
  ralph,
4051
4036
  tournament,
4052
- } from "@bastani/workflows/builtin";
4037
+ } from "@bastani/atomic/workflows/builtin";
4053
4038
  ```
4054
4039
 
4055
4040
  Each export is a workflow definition. All nine definitions are available through individual module paths. See [Compose with builtin workflows](#compose-with-builtin-workflows) for a parent workflow example.
4056
4041
 
4057
4042
 
4058
- ## Fast Inference for Workflow Stages
4043
+ ## Fast inference for workflow stages
4044
+
4045
+ Workflow stages can use faster inference on supported OpenAI and GitHub Copilot models so multi-stage runs finish sooner.
4059
4046
 
4060
- Workflow stages can use faster, higher-priority inference on supported providers so multi-stage runs finish sooner. Codex fast mode currently provides this option.
4047
+ ### Fast mode
4061
4048
 
4062
- ### Codex fast mode
4049
+ Use `/fast` to manage fast mode separately for normal chat and workflow-stage sessions. The settings are `codexFastMode.chat` and `codexFastMode.workflow`; workflow stages use the workflow scope, not the chat scope. A stage inside a nested `ctx.workflow(...)` call keeps that workflow scope, and subagents launched by the stage inherit it.
4063
4050
 
4064
- Use `/fast` to manage Codex fast mode separately for normal chat and workflow-stage sessions. The settings are `codexFastMode.chat` and `codexFastMode.workflow`; workflow stages use the workflow scope, not the chat scope. A stage inside a nested `ctx.workflow(...)` call keeps that workflow scope, and subagents launched by the stage inherit it.
4051
+ Fast mode is eligible for supported `openai/*` and `openai-codex/*` providers, provider aliases that use the shared `openai-codex-responses` transport, and GitHub Copilot models whose OAuth account catalog advertises a fast variant. OpenAI requests use the priority service tier. GitHub Copilot requests use the account-supported fast variant without adding the OpenAI service-tier field. Azure OpenAI, OpenRouter, and generic OpenAI-compatible providers are not eligible.
4065
4052
 
4066
- Fast mode is eligible for supported `openai/*` and `openai-codex/*` providers and provider aliases that use the shared `openai-codex-responses` transport. It does not apply to `github-copilot/*`, Azure OpenAI, OpenRouter, or generic OpenAI-compatible providers. Atomic resolves the marker and request tier for the effective model on every fallback attempt, so a supported fallback can be fast even when the primary failed, while an unsupported fallback is not. Workflow stage model labels and stage-launched subagent result labels keep the raw model id and append a separate `fast` marker; graph node cards keep their dependency metadata focused on topology and do not repeat that marker.
4053
+ Atomic resolves eligibility for the effective model on every fallback attempt. A supported fallback can use fast mode even when the primary failed, while an unsupported fallback keeps its normal request behavior. Workflow stage model labels and stage-launched subagent result labels keep the raw model ID and append a separate `fast` marker; graph node cards keep their dependency metadata focused on topology and do not repeat that marker.
4067
4054
 
4068
- Enable workflow fast mode deliberately for broad workflows: parallel fan-out and fallback attempts can multiply priority-tier requests and cost.
4055
+ Enable workflow fast mode deliberately for broad workflows. Parallel fan-out and fallback attempts can multiply fast provider requests and usage.
4069
4056
 
4070
4057
  ## Context Engineering
4071
4058
 
@@ -4241,120 +4228,6 @@ Before turning a process into a workflow, confirm that it suits automation:
4241
4228
 
4242
4229
  For complex workflows, structure the implementation as a pipeline: acquire context, prepare prompts/artifacts, process with LLM stages, parse or validate outputs, and render the final result.
4243
4230
 
4244
- ## Migrating from the `defineWorkflow()` Builder API
4245
-
4246
- [#1457](https://github.com/bastani-inc/atomic/pull/1457) removed the chained builder API — `defineWorkflow(name).description(...).input(...).output(...).worktreeFromInputs(...).run(...).compile()` — and made the single `workflow({ name?, description, inputs, outputs, run })` object form the only authoring API. There is no shim and no deprecation period: workflow files that still call `defineWorkflow(...).compile()` fail discovery with a module-load error until authors migrate them.
4247
-
4248
- Use this section for workflow files that use the previous API. If you are authoring a new workflow, skip it and start from [Writing a Workflow](#writing-a-workflow).
4249
-
4250
- ### What changed
4251
-
4252
- - `import { defineWorkflow, Type } from "@bastani/workflows"` → `workflow` now comes from `@bastani/workflows`, and `Type` comes from the `typebox` package directly. `@bastani/workflows` no longer re-exports `Type`. The `Static` and `TSchema` *type* exports are still re-exported from `@bastani/workflows`, so `import type { Static } from "@bastani/workflows"` keeps working — only the runtime `Type` builder moved.
4253
- - The fluent builder chain became one object literal passed to `workflow({ ... })`.
4254
- - `name` moved from the `defineWorkflow(name)` argument into the object. It is now **optional** — omit it and discovery derives the name from the filename (the recommended style used by the builtins and most examples), or keep it when you want the name to differ from the file's basename.
4255
- - `outputs` is now **required**. Workflows that declared no outputs before must now pass `outputs: {}`.
4256
- - `.compile()` is gone. `workflow({ ... })` returns the frozen, branded definition directly; `export default` it.
4257
- - The imperative object-form `runWorkflow(...)` runner is also removed (it is a `never` placeholder that throws on access). Programmatic execution uses the exported `run(def, inputs)` helper or a registry — see [Programmatic Usage](#programmatic-usage).
4258
-
4259
- ### Builder method → object key
4260
-
4261
- | Removed builder API | New `workflow({ ... })` key |
4262
- | --- | --- |
4263
- | `defineWorkflow("name")` argument | `name: "name"` (optional; derived from the filename when omitted) |
4264
- | `.description(text)` | `description: text` |
4265
- | `.input(key, schema)` (repeatable) | `inputs: { key: schema, ... }` |
4266
- | `.output(key, schema)` (repeatable) | `outputs: { key: schema, ... }` (required, even if `{}`) |
4267
- | `.worktreeFromInputs(binding)` | `worktreeFromInputs: binding` (binding shape unchanged) |
4268
- | `.run(fn)` callback | `run: fn` |
4269
- | `.compile()` terminal | delete — `workflow({ ... })` returns the definition |
4270
-
4271
- `ctx` and every primitive (`ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.stage`, `ctx.workflow`, `ctx.exit`, `ctx.ui`) are unchanged, so **you do not need to rewrite workflow bodies** — only the authoring wrapper changes.
4272
-
4273
- ### Full before / after
4274
-
4275
- Before (removed API):
4276
-
4277
- ```ts
4278
- import { defineWorkflow, Type } from "@bastani/workflows";
4279
-
4280
- export default defineWorkflow("review-changes")
4281
- .description("Run two reviewers in parallel and synthesize a decision.")
4282
- .input("target", Type.String({ description: "Path or change target to review." }))
4283
- .input("base_branch", Type.String({ default: "origin/main" }))
4284
- .output("decision", Type.String())
4285
- .output("concerns", Type.Optional(Type.Array(Type.String())))
4286
- .worktreeFromInputs({ baseBranch: "base_branch" })
4287
- .run(async (ctx) => {
4288
- const target = String(ctx.inputs.target);
4289
- const [quality, runtime] = await ctx.parallel(
4290
- [
4291
- { name: "quality", prompt: `Review quality of ${target}` },
4292
- { name: "runtime", prompt: `Review runtime behavior of ${target}` },
4293
- ],
4294
- { concurrency: 2 },
4295
- );
4296
- return { decision: `${quality.text}\n${runtime.text}`, concerns: [] };
4297
- })
4298
- .compile();
4299
- ```
4300
-
4301
- After (current API):
4302
-
4303
- ```ts
4304
- import { workflow } from "@bastani/workflows";
4305
- import { Type } from "typebox";
4306
-
4307
- export default workflow({
4308
- name: "review-changes", // optional — omit to derive from filename
4309
- description: "Run two reviewers in parallel and synthesize a decision.",
4310
- inputs: {
4311
- target: Type.String({ description: "Path or change target to review." }),
4312
- base_branch: Type.String({ default: "origin/main" }),
4313
- },
4314
- outputs: {
4315
- decision: Type.String(),
4316
- concerns: Type.Optional(Type.Array(Type.String())),
4317
- },
4318
- worktreeFromInputs: { baseBranch: "base_branch" },
4319
- run: async (ctx) => {
4320
- const target = String(ctx.inputs.target);
4321
- const [quality, runtime] = await ctx.parallel(
4322
- [
4323
- { name: "quality", prompt: `Review quality of ${target}` },
4324
- { name: "runtime", prompt: `Review runtime behavior of ${target}` },
4325
- ],
4326
- { concurrency: 2 },
4327
- );
4328
- return { decision: `${quality.text}\n${runtime.text}`, concerns: [] };
4329
- },
4330
- });
4331
- ```
4332
-
4333
- ### Conversion checklist
4334
-
4335
- For each `.atomic/workflows/*.ts` (or workflow-package) file:
4336
-
4337
- 1. Swap the import to `import { workflow } from "@bastani/workflows"` and add `import { Type } from "typebox"`. Drop `defineWorkflow` from the `@bastani/workflows` import. `import type { Static, TSchema }` can stay on the `@bastani/workflows` import if you use those types.
4338
- 2. Replace `defineWorkflow("<name>")` with `workflow({`. You may keep `name: "<name>"` or drop the key entirely to derive the name from the filename.
4339
- 3. Move `.description("<text>")` to a `description: "<text>",` property.
4340
- 4. Collect every `.input(key, schema)` into one `inputs: { key: schema, ... },` map.
4341
- 5. Collect every `.output(key, schema)` into one `outputs: { key: schema, ... },` map. If there were no `.output(...)` calls, add `outputs: {},` — it is now required.
4342
- 6. Move `.worktreeFromInputs(binding)` to a `worktreeFromInputs: binding,` property (same binding shape, unchanged).
4343
- 7. Move the `.run(fn)` callback to a `run: fn,` property; keep the body byte-for-byte identical.
4344
- 8. Delete the trailing `.compile()`, close the object with `})`, and keep `export default`.
4345
- 9. Run `/workflow reload` (or restart Atomic) and `/workflow list` to confirm the file loads. Because `ctx` and its primitives are unchanged, stage behavior, graph layout, resume/quit, and human-input prompts are unaffected.
4346
-
4347
- ### Gotchas
4348
-
4349
- - **`outputs` is required.** The old `.output(...)` calls were optional, and a workflow without outputs compiled successfully. The new object form throws `workflow: outputs must be a schema map` when `outputs` is missing, so declare `outputs: {}` for outputless workflows.
4350
- - **`Type` is no longer re-exported.** `import { Type } from "@bastani/workflows"` fails type-checking; import it from `typebox` instead. (`Static` and `TSchema` *types* are still re-exported from `@bastani/workflows`, so those imports do not need to change.)
4351
- - **`.compile()` does not exist.** Leaving it produces a runtime `TypeError`; `workflow({ ... })` already returns the frozen, branded definition.
4352
- - **`name` is derived from the filename when omitted.** Discovery derives the name from the filename: `review-changes.ts` becomes `review-changes`, so an explicit `name` is only needed when it should differ from the basename.
4353
- - **Do not construct definitions manually.** Discovery rejects hand-built objects carrying `__piWorkflow: true`, and `ctx.workflow(...)` rejects them too. Both accept only definitions minted by `workflow({ ... })`.
4354
- - **The imperative `runWorkflow` runner is gone.** It is now a `never` placeholder that throws on access; use the exported `run(def, inputs)` helper or a registry for programmatic execution.
4355
- - **Keep `outputs` inline for the strictest type checking.** The old builder enforced no-extra-output keys through a `NoExtraOutputs` generic on `.run(fn)`; the object form re-creates that check for inline `outputs` maps, but cannot recover output keys when a schema map is widened or built up before being passed to `workflow({ ... })`. Keep the `outputs` literal inline so the declared-key check stays exact.
4356
-
4357
- Everything else — stage primitives, `ctx.inputs` typing, runtime validation, DAG inference, MCP scoping, resume/quit, worktree binding, model fallback, and the `/workflow` tool contract — is unchanged.
4358
4231
 
4359
4232
  ## Design Checklist
4360
4233
 
@@ -4384,7 +4257,7 @@ Good workflows are information-flow systems, not just prompt sequences. Keep sta
4384
4257
  - Do not guess input keys; inspect with `inputs` or `get` first.
4385
4258
  - Do not call `create`, `update`, or `delete` on the workflow tool; definitions are code-authored.
4386
4259
  - Do not use legacy workflow tool fields like `agent`, `stage`, or run-control `name`.
4387
- - Do not pass strings or path objects to `ctx.workflow(...)`; import the workflow definition from `@bastani/workflows/builtin` or another TypeScript module first.
4260
+ - Do not pass strings or path objects to `ctx.workflow(...)`; import the workflow definition from `@bastani/atomic/workflows/builtin` or another TypeScript module first.
4388
4261
  - Do not create a self-edge or a dependency edge from the current frontier to an existing ancestor. Cyclic workflow graphs are unsupported; redesign or stop before launch when a cycle cannot be removed.
4389
4262
  - Do not model a bounded loop by reopening an earlier node beneath its downstream work. Create distinct tracked work per iteration and keep retained-session follow-up as non-topological activity when it adds no dependency work.
4390
4263
  - Do not claim TypeScript or workflow discovery proves a dynamic workflow acyclic. Discovery diagnoses imports and definition shape; execution, replay, and DBOS hydration are the runtime topology boundary.
@@ -4849,7 +4722,7 @@ During the pre-launch architecture pass, enumerate the slices in the coverage ma
4849
4722
  └─────────────────────────────────────────────────────────────┘
4850
4723
  ```
4851
4724
 
4852
- Run each slice through a child workflow that owns its implement/review/repair lifecycle. Import `goal` or `ralph` from `@bastani/workflows/builtin`, or use a task-specific child when neither builtin matches. Before each child, use a durable `ctx.tool(...)` step to create or check out the slice's explicit branch in its worktree. `worktreeFromInputs` creates a missing target with a detached checkout and reuses an existing target as-is; `base_branch` and `git_worktree_dir` do not create or check out a feature branch by themselves. Create slice N+1's branch from slice N's verified branch, then pass that previous branch as `base_branch` and give the child a distinct `git_worktree_dir`.
4725
+ Run each slice through a child workflow that owns its implement/review/repair lifecycle. Import `goal` or `ralph` from `@bastani/atomic/workflows/builtin`, or use a task-specific child when neither builtin matches. Before each child, use a durable `ctx.tool(...)` step to create or check out the slice's explicit branch in its worktree. `worktreeFromInputs` creates a missing target with a detached checkout and reuses an existing target as-is; `base_branch` and `git_worktree_dir` do not create or check out a feature branch by themselves. Create slice N+1's branch from slice N's verified branch, then pass that previous branch as `base_branch` and give the child a distinct `git_worktree_dir`.
4853
4726
 
4854
4727
  The parent should verify each child before creating the next boundary. If a gate fails, stop at the first failed gate, report that slice as unverified, and retain the earlier verified slices and their branch/worktree records. Do not roll earlier slices back and do not continue past the failure.
4855
4728
 
@@ -4859,8 +4732,8 @@ The calls below are deliberately unrolled. Repeat the downstream shape for the p
4859
4732
  import { spawnSync } from "node:child_process";
4860
4733
  import { resolve } from "node:path";
4861
4734
  import { Type } from "typebox";
4862
- import { workflow } from "@bastani/workflows";
4863
- import { goal } from "@bastani/workflows/builtin";
4735
+ import { workflow } from "@bastani/atomic/workflows";
4736
+ import { goal } from "@bastani/atomic/workflows/builtin";
4864
4737
 
4865
4738
  function spawnCommand(argv: readonly string[], cwd: string) {
4866
4739
  const [command, ...args] = argv;