tubeless 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +106 -0
  3. package/dist/abort.d.ts +12 -0
  4. package/dist/abort.d.ts.map +1 -0
  5. package/dist/abort.js +49 -0
  6. package/dist/abort.js.map +1 -0
  7. package/dist/batch.d.ts +22 -0
  8. package/dist/batch.d.ts.map +1 -0
  9. package/dist/batch.js +73 -0
  10. package/dist/batch.js.map +1 -0
  11. package/dist/checkpoint.d.ts +33 -0
  12. package/dist/checkpoint.d.ts.map +1 -0
  13. package/dist/checkpoint.js +71 -0
  14. package/dist/checkpoint.js.map +1 -0
  15. package/dist/child-execution.d.ts +30 -0
  16. package/dist/child-execution.d.ts.map +1 -0
  17. package/dist/child-execution.js +270 -0
  18. package/dist/child-execution.js.map +1 -0
  19. package/dist/cli-command.d.ts +29 -0
  20. package/dist/cli-command.d.ts.map +1 -0
  21. package/dist/cli-command.js +314 -0
  22. package/dist/cli-command.js.map +1 -0
  23. package/dist/cli-parser.d.ts +28 -0
  24. package/dist/cli-parser.d.ts.map +1 -0
  25. package/dist/cli-parser.js +557 -0
  26. package/dist/cli-parser.js.map +1 -0
  27. package/dist/cli-pipeline-command.d.ts +84 -0
  28. package/dist/cli-pipeline-command.d.ts.map +1 -0
  29. package/dist/cli-pipeline-command.js +185 -0
  30. package/dist/cli-pipeline-command.js.map +1 -0
  31. package/dist/cli-types.d.ts +220 -0
  32. package/dist/cli-types.d.ts.map +1 -0
  33. package/dist/cli-types.js +19 -0
  34. package/dist/cli-types.js.map +1 -0
  35. package/dist/cli.d.ts +6 -0
  36. package/dist/cli.d.ts.map +1 -0
  37. package/dist/cli.js +6 -0
  38. package/dist/cli.js.map +1 -0
  39. package/dist/env.d.ts +2 -0
  40. package/dist/env.d.ts.map +1 -0
  41. package/dist/env.js +8 -0
  42. package/dist/env.js.map +1 -0
  43. package/dist/file-utils.d.ts +4 -0
  44. package/dist/file-utils.d.ts.map +1 -0
  45. package/dist/file-utils.js +35 -0
  46. package/dist/file-utils.js.map +1 -0
  47. package/dist/interactive-reporter.d.ts +38 -0
  48. package/dist/interactive-reporter.d.ts.map +1 -0
  49. package/dist/interactive-reporter.js +308 -0
  50. package/dist/interactive-reporter.js.map +1 -0
  51. package/dist/lifecycle.d.ts +17 -0
  52. package/dist/lifecycle.d.ts.map +1 -0
  53. package/dist/lifecycle.js +77 -0
  54. package/dist/lifecycle.js.map +1 -0
  55. package/dist/live-ticker.d.ts +21 -0
  56. package/dist/live-ticker.d.ts.map +1 -0
  57. package/dist/live-ticker.js +377 -0
  58. package/dist/live-ticker.js.map +1 -0
  59. package/dist/mapped-child-progress.d.ts +109 -0
  60. package/dist/mapped-child-progress.d.ts.map +1 -0
  61. package/dist/mapped-child-progress.js +133 -0
  62. package/dist/mapped-child-progress.js.map +1 -0
  63. package/dist/node.d.ts +5 -0
  64. package/dist/node.d.ts.map +1 -0
  65. package/dist/node.js +5 -0
  66. package/dist/node.js.map +1 -0
  67. package/dist/paths.d.ts +9 -0
  68. package/dist/paths.d.ts.map +1 -0
  69. package/dist/paths.js +19 -0
  70. package/dist/paths.js.map +1 -0
  71. package/dist/pipeline-command-marker.d.ts +5 -0
  72. package/dist/pipeline-command-marker.d.ts.map +1 -0
  73. package/dist/pipeline-command-marker.js +21 -0
  74. package/dist/pipeline-command-marker.js.map +1 -0
  75. package/dist/pipeline-diagnostics.d.ts +4 -0
  76. package/dist/pipeline-diagnostics.d.ts.map +1 -0
  77. package/dist/pipeline-diagnostics.js +17 -0
  78. package/dist/pipeline-diagnostics.js.map +1 -0
  79. package/dist/pipeline-module.d.ts +62 -0
  80. package/dist/pipeline-module.d.ts.map +1 -0
  81. package/dist/pipeline-module.js +157 -0
  82. package/dist/pipeline-module.js.map +1 -0
  83. package/dist/pipeline.d.ts +606 -0
  84. package/dist/pipeline.d.ts.map +1 -0
  85. package/dist/pipeline.js +1444 -0
  86. package/dist/pipeline.js.map +1 -0
  87. package/dist/progress.d.ts +4 -0
  88. package/dist/progress.d.ts.map +1 -0
  89. package/dist/progress.js +8 -0
  90. package/dist/progress.js.map +1 -0
  91. package/dist/prompt-select.d.ts +81 -0
  92. package/dist/prompt-select.d.ts.map +1 -0
  93. package/dist/prompt-select.js +197 -0
  94. package/dist/prompt-select.js.map +1 -0
  95. package/dist/rate-limit.d.ts +7 -0
  96. package/dist/rate-limit.d.ts.map +1 -0
  97. package/dist/rate-limit.js +23 -0
  98. package/dist/rate-limit.js.map +1 -0
  99. package/dist/render.d.ts +21 -0
  100. package/dist/render.d.ts.map +1 -0
  101. package/dist/render.js +60 -0
  102. package/dist/render.js.map +1 -0
  103. package/dist/reporter-entry.d.ts +3 -0
  104. package/dist/reporter-entry.d.ts.map +1 -0
  105. package/dist/reporter-entry.js +3 -0
  106. package/dist/reporter-entry.js.map +1 -0
  107. package/dist/reporter.d.ts +50 -0
  108. package/dist/reporter.d.ts.map +1 -0
  109. package/dist/reporter.js +167 -0
  110. package/dist/reporter.js.map +1 -0
  111. package/dist/retry.d.ts +28 -0
  112. package/dist/retry.d.ts.map +1 -0
  113. package/dist/retry.js +59 -0
  114. package/dist/retry.js.map +1 -0
  115. package/dist/run-store-sqlite.d.ts +12 -0
  116. package/dist/run-store-sqlite.d.ts.map +1 -0
  117. package/dist/run-store-sqlite.js +187 -0
  118. package/dist/run-store-sqlite.js.map +1 -0
  119. package/dist/run-store-ui-http.d.ts +5 -0
  120. package/dist/run-store-ui-http.d.ts.map +1 -0
  121. package/dist/run-store-ui-http.js +19 -0
  122. package/dist/run-store-ui-http.js.map +1 -0
  123. package/dist/run-store-ui-page.d.ts +2 -0
  124. package/dist/run-store-ui-page.d.ts.map +1 -0
  125. package/dist/run-store-ui-page.js +759 -0
  126. package/dist/run-store-ui-page.js.map +1 -0
  127. package/dist/run-store-ui-protocol.d.ts +28 -0
  128. package/dist/run-store-ui-protocol.d.ts.map +1 -0
  129. package/dist/run-store-ui-protocol.js +84 -0
  130. package/dist/run-store-ui-protocol.js.map +1 -0
  131. package/dist/run-store-ui-state.d.ts +21 -0
  132. package/dist/run-store-ui-state.d.ts.map +1 -0
  133. package/dist/run-store-ui-state.js +65 -0
  134. package/dist/run-store-ui-state.js.map +1 -0
  135. package/dist/run-store-ui.d.ts +25 -0
  136. package/dist/run-store-ui.d.ts.map +1 -0
  137. package/dist/run-store-ui.js +217 -0
  138. package/dist/run-store-ui.js.map +1 -0
  139. package/dist/run-store.d.ts +109 -0
  140. package/dist/run-store.d.ts.map +1 -0
  141. package/dist/run-store.js +345 -0
  142. package/dist/run-store.js.map +1 -0
  143. package/dist/testing.d.ts +40 -0
  144. package/dist/testing.d.ts.map +1 -0
  145. package/dist/testing.js +83 -0
  146. package/dist/testing.js.map +1 -0
  147. package/dist/tracing-internal.d.ts +21 -0
  148. package/dist/tracing-internal.d.ts.map +1 -0
  149. package/dist/tracing-internal.js +206 -0
  150. package/dist/tracing-internal.js.map +1 -0
  151. package/dist/tracing-json.d.ts +12 -0
  152. package/dist/tracing-json.d.ts.map +1 -0
  153. package/dist/tracing-json.js +8 -0
  154. package/dist/tracing-json.js.map +1 -0
  155. package/dist/tracing-otel.d.ts +36 -0
  156. package/dist/tracing-otel.d.ts.map +1 -0
  157. package/dist/tracing-otel.js +60 -0
  158. package/dist/tracing-otel.js.map +1 -0
  159. package/dist/tracing.d.ts +48 -0
  160. package/dist/tracing.d.ts.map +1 -0
  161. package/dist/tracing.js +2 -0
  162. package/dist/tracing.js.map +1 -0
  163. package/dist/workbench-bin.d.ts +3 -0
  164. package/dist/workbench-bin.d.ts.map +1 -0
  165. package/dist/workbench-bin.js +8 -0
  166. package/dist/workbench-bin.js.map +1 -0
  167. package/dist/workbench-graph.d.ts +3 -0
  168. package/dist/workbench-graph.d.ts.map +1 -0
  169. package/dist/workbench-graph.js +64 -0
  170. package/dist/workbench-graph.js.map +1 -0
  171. package/dist/workbench-inspect.d.ts +3 -0
  172. package/dist/workbench-inspect.d.ts.map +1 -0
  173. package/dist/workbench-inspect.js +65 -0
  174. package/dist/workbench-inspect.js.map +1 -0
  175. package/dist/workbench-plan.d.ts +3 -0
  176. package/dist/workbench-plan.d.ts.map +1 -0
  177. package/dist/workbench-plan.js +72 -0
  178. package/dist/workbench-plan.js.map +1 -0
  179. package/dist/workbench-run.d.ts +13 -0
  180. package/dist/workbench-run.d.ts.map +1 -0
  181. package/dist/workbench-run.js +115 -0
  182. package/dist/workbench-run.js.map +1 -0
  183. package/dist/workbench-shared.d.ts +67 -0
  184. package/dist/workbench-shared.d.ts.map +1 -0
  185. package/dist/workbench-shared.js +163 -0
  186. package/dist/workbench-shared.js.map +1 -0
  187. package/dist/workbench-studio.d.ts +26 -0
  188. package/dist/workbench-studio.d.ts.map +1 -0
  189. package/dist/workbench-studio.js +57 -0
  190. package/dist/workbench-studio.js.map +1 -0
  191. package/dist/workbench-ui.d.ts +3 -0
  192. package/dist/workbench-ui.d.ts.map +1 -0
  193. package/dist/workbench-ui.js +262 -0
  194. package/dist/workbench-ui.js.map +1 -0
  195. package/dist/workbench.d.ts +5 -0
  196. package/dist/workbench.d.ts.map +1 -0
  197. package/dist/workbench.js +43 -0
  198. package/dist/workbench.js.map +1 -0
  199. package/docs/README.md +31 -0
  200. package/docs/agent-guide.md +168 -0
  201. package/docs/api-reference.md +269 -0
  202. package/docs/api-report.json +298 -0
  203. package/docs/child-pipeline-composition.md +185 -0
  204. package/docs/cli.md +118 -0
  205. package/docs/concepts.md +502 -0
  206. package/docs/getting-started.md +162 -0
  207. package/docs/llms.txt +62 -0
  208. package/docs/recipes.md +51 -0
  209. package/docs/studio.md +86 -0
  210. package/examples/best-effort.ts +42 -0
  211. package/examples/cancellation-and-testing.ts +45 -0
  212. package/examples/child-pipeline.ts +51 -0
  213. package/examples/cli-job.ts +86 -0
  214. package/examples/conditional-step.ts +41 -0
  215. package/examples/fan-out-progress.ts +53 -0
  216. package/examples/local-observability.ts +80 -0
  217. package/examples/publish-with-gates.ts +56 -0
  218. package/examples/rendering.ts +34 -0
  219. package/examples/resumable-enrichment.ts +75 -0
  220. package/examples/tracing.ts +35 -0
  221. package/examples/tsconfig.json +15 -0
  222. package/examples/typed-import.ts +54 -0
  223. package/examples/validated-boundaries.ts +75 -0
  224. package/package.json +156 -0
@@ -0,0 +1,185 @@
1
+ # Child-pipeline composition
2
+
3
+ ## What ships
4
+
5
+ Compose a child pipeline as one typed parent step with
6
+ `createSteps<TOptions>().fromPipeline(...)`. Overloads cover identity and
7
+ mapped results. Child lifecycle hooks stay isolated, and child work reports
8
+ through parent-step progress without changing the pipeline run loop or
9
+ reporter contracts.
10
+
11
+ Map a runtime item list onto the same child pipeline with
12
+ `createSteps<TOptions>().forEachPipeline(...)`. Item keys are stable,
13
+ concurrency is bounded, and results keep input order even when children
14
+ finish out of order. All running children settle before an aggregate failure
15
+ is returned, so the parent cannot finalize while child side effects are still
16
+ in flight.
17
+
18
+ Opaque parent steps pass outputs through typed dependencies, skip side
19
+ effects in dry-run, and convert domain validation errors into failed parent
20
+ steps.
21
+
22
+ ## Why not call the child from an ordinary step
23
+
24
+ Invoking a child pipeline from an ordinary parent step and passing the parent
25
+ `PipelineStepContext` gives the child the parent's runtime seams, but it also
26
+ gives the child the parent's raw hooks and parent-only `reportProgress`
27
+ callback.
28
+
29
+ The resulting lifecycle stream is flat. A direct nested run emits the child pipeline, step, finalize, and completion events between the parent wrapper step's start and completion events. Most events have no pipeline path or run identity. The interactive reporter also owns one plan, one step map, and one result. A child start replaces its visible plan, and a child completion disposes the reporter before the parent completes.
30
+
31
+ The package needs a composition contract that preserves typing and runtime behavior without exposing two pipeline frames through hooks designed for one pipeline.
32
+
33
+ ## Goals
34
+
35
+ - Model a child pipeline as one typed step in the parent DAG.
36
+ - Infer parent dependency inputs, parent options, child options, the child result, and any mapped result.
37
+ - Preserve cancellation, logging, timing, working-directory, dry-run, progress, and failure behavior.
38
+ - Keep raw child lifecycle hooks inside the adapter while reporting useful progress through the opaque parent step.
39
+ - Keep the first slice bounded: one opaque child, or a mapped set of the same child.
40
+
41
+ ## Supported: policy skip on opaque steps
42
+
43
+ Policy skip (`step.skippable` / `StepSkipDecision`) is part of the shipped API and
44
+ applies to ordinary steps and to `fromPipeline` adapters (the opaque parent
45
+ step is a normal step under the hood):
46
+
47
+ - Return a non-empty string or `{ reason, value? }` from `skip` to skip without
48
+ calling `run` (or without running the child). Reporters show a yellow skip
49
+ whose terminal report has `status: "skipped"` and `reason: "policy"`.
50
+ - Policy skips unlock required dependents. A bare string (or `{ reason }`
51
+ without `value`) publishes `undefined` as the step output.
52
+ - Any step that declares `skip` is typed so dependents see `TOut | undefined`
53
+ (including `PipelineResultOf<Child> | undefined` for `fromPipeline` without
54
+ `mapResult`). Prefer `{ reason, value }` on every skip path when dependents
55
+ need a real output. With `mapResult`, skip `value` is the parent-facing
56
+ mapped `TOut` — policy skip does not call `mapResult`.
57
+
58
+ A `fromPipeline.skippable` step can policy-skip optional work and publish a
59
+ concrete disabled result via `{ reason, value }`.
60
+
61
+ `forEachPipeline` does not accept `skip` today; gate mapped fan-out with an
62
+ upstream step or filter `items` instead.
63
+
64
+ ## Non-goals
65
+
66
+ - Flatten child steps into the parent plan.
67
+ - Add hierarchical or versioned external lifecycle events.
68
+ - Support namespaced selection such as `parent.child-step`.
69
+ - Add parallel DAG execution, remote checkpoints, or subcommands.
70
+ - Injected runner overrides (substituting a child pipeline implementation at
71
+ test time via parent options) — separate from policy skip.
72
+
73
+ ## Why this shape
74
+
75
+ | Model | Typing | Planning | Hooks and reporting | Selective execution | Failure | Dry run | Cost |
76
+ | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
77
+ | Continue manual shared-context calls | Each wrapper can map options and return a typed value, but each wrapper repeats the contract and structural context passing makes unsafe fields easy to forward. | The parent sees only the wrapper step; the child creates a separate plan at runtime. | Raw child events enter the parent's flat hook stream. The current interactive reporter replaces the parent frame and disposes on child completion. | Parent selection can select the wrapper. Child selection depends on ad hoc mapped `stepIds`. | `runOrThrow` can fail the wrapper, but consistent child-identifying messages and progress require repeated wrapper code. | Each wrapper must remember parent/child dry-run precedence and per-step policy. | Low per call, with continuing duplication and reporter breakage. |
78
+ | Flatten the child DAG into the parent DAG | The API must translate child option and dependency types into a parent definition without losing inference. | Child ids need stable namespacing, and runtime option mapping can change which child plan is valid. | A flat plan fits current hooks only after defining pipeline identity, paths, and collision rules. | Namespaced filters need semantics for parent dependencies, child dependencies, and finalization. | The parent must define how child failures, partial outputs, and child finalization affect the flattened DAG. | The parent must reconcile its dry-run rules with every flattened child step. | High. It requires event identity and selection semantics before the first useful slice. |
79
+ | Opaque typed child-step adapter | A parent-scoped factory can infer dependency inputs and parent options while a typed child pipeline constrains mapped options and results. | The parent plan contains one stable step. The adapter validates the child plan immediately before running it. | Parent hooks see the opaque step only. An internal bridge translates child terminal events into parent-step progress. | Selecting the opaque parent step runs the child according to mapped child options. Nested selection is deferred. | An unusable child result throws a parent-step error that retains the child pipeline and failing step identity. | Framework-owned parent `dryRun` overrides the mapper, then the child applies its own per-step rules. | Medium. It uses existing pipeline and hook primitives without changing the external event model. |
80
+
81
+ Shared raw hooks are rejected because they already corrupt the interactive reporter's single frame and cannot distinguish nested runs. Immediate flattening is rejected because it needs stable identity, selection, dependency, failure, and finalization rules that the current event and plan models don't provide. The opaque adapter addresses the existing composition case without deciding those broader contracts.
82
+
83
+ ## Contract
84
+
85
+ The parent DAG contains one ordinary step. That step maps its typed parent inputs and context into typed child options, validates the child plan, runs the child with an isolated internal hook bridge, and returns either the child result or a mapped parent result.
86
+
87
+ The bridge owns child lifecycle events. Parent and custom hooks receive only the parent pipeline and opaque parent-step events. The bridge reports terminal child-step counts through the parent step's `reportProgress`, so existing reporters keep one live frame.
88
+
89
+ ## API
90
+
91
+ The parent-scoped step factory owns the adapter:
92
+
93
+ ```ts
94
+ const seedIndexStage = step.fromPipeline("seed-index", {
95
+ pipeline: IndexSeedPipeline,
96
+ dependsOn: [seedCatalogStage],
97
+ description: "Seed a precomputed search index",
98
+ mapOptions: (_inputs, context) => ({
99
+ indexDir: context.options.indexDir,
100
+ syncSchema: false,
101
+ }),
102
+ mapResult: () => ({ ran: true, stageId: "seed-index" }),
103
+ });
104
+ ```
105
+
106
+ `StepFactory.fromPipeline` and `StepFactory.fromPipeline.skippable` infer the
107
+ child result when `mapResult` is absent, and infer the mapped result when it
108
+ is present.
109
+
110
+ For runtime-selected sets (any domain — shards, files, jobs, catalog rows):
111
+
112
+ ```ts
113
+ const processShards = step.forEachPipeline("process-shards", {
114
+ pipeline: ShardPipeline,
115
+ dependsOn: [resolveShards],
116
+ items: ({ "resolve-shards": shards }) => shards,
117
+ key: (shard) => shard.id,
118
+ concurrency: (_inputs, context) => context.options.concurrency,
119
+ // Optional presentation only. Defaults to the noun "items".
120
+ progress: { itemNoun: "shards" },
121
+ mapOptions: (shard, _index, _inputs, context) => ({
122
+ shardPath: shard.path,
123
+ outputRoot: context.options.outputRoot,
124
+ }),
125
+ });
126
+ ```
127
+
128
+ The parent sees one opaque `process-shards` step. Progress is domain-neutral by
129
+ default: a one-line item/concurrency summary plus structured `details` rows for
130
+ each in-flight child (`id` + `label`). Interactive reporters render those as
131
+ indented lines under the parent step so high concurrency stays readable. The
132
+ progress bar advances on terminal child steps so long fan-out work does not look
133
+ hung at 0%. Override `progress.itemNoun` or `progress.formatMessage` for domain
134
+ labels without changing scheduling. Duplicate keys fail before any child starts.
135
+ Parent dry-run overrides the mapped child run object's `dryRun` value.
136
+
137
+ ## Semantics matrix
138
+
139
+ | Concern | Contract |
140
+ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
141
+ | Parent plan | The child is one opaque parent step. Its `nestedPipeline` metadata identifies the child pipeline, declared child step ids, and single/fan-out mode; child steps are not added to `PipelinePlan.steps` and runtime selection is not predicted. |
142
+ | Option mapping | `mapOptions(inputs, parentContext)` returns the child's complete run object: domain fields plus any child-specific controls. The adapter applies parent `dryRun` last so framework semantics win. |
143
+ | Other run controls | The adapter does not automatically propagate parent `stepIds`, `targets`, or `continueOnError`. `mapOptions` may choose child-specific values; child `targets` are limited to that child's declared public goals. Mapping parent selection would usually be invalid because parent and child ids occupy different namespaces. Mapping `continueOnError` lets independent child work finish but does not make a failed child usable by the parent. |
144
+ | Runtime context | The adapter forwards `cwd`, `log`, `now`, `sleep`, and `signal` field by field. It does not forward parent hooks or the parent-only `reportProgress` field. |
145
+ | Hook isolation | Parent and custom hooks see only the parent pipeline and opaque parent step. Raw child lifecycle events go only to an internal bridge. |
146
+ | Progress | Single-child `fromPipeline` bridges terminal child steps into the opaque step (`completed` / plan step count, messages prefixed with `childPipelineId/childStepId`). Mapped `forEachPipeline` reports domain-neutral fan-out progress via `toMappedChildStepProgress`: `completed` counts terminal child steps across items, `total` is `items × stepsPerItem`, `message` is a one-line summary, and `details` lists each in-flight item for multi-line reporters. Presentation is optional (`progress.itemNoun` / `progress.formatMessage` / `progress.detailLimit`); neither path creates another live reporter frame. |
147
+ | Result | The default output is the typed child result. `mapResult` may transform it into a domain-specific parent-step output. A child result is usable only when its status is `"completed"`; best-effort child results must be handled through an explicit ordinary step that calls `run()` and inspects the structured result. |
148
+ | Failure | An unusable child result fails the opaque parent step. Its error names the child pipeline and the first failing child step and message, with `code: "TUBELESS_CHILD_FAILED"`, `phase: "execution"`, and `kind: "child"`; its JSON-safe cause chain retains the actionable child failure. When mapped children contain both cancellation and genuine failure, failure takes precedence; the aggregate is cancelled only when every unsuccessful child was cancelled. |
149
+ | Cancellation | The child receives the exact parent `AbortSignal`. An already-aborted or later-aborted signal stops the child and transitions the parent step to `cancelled`. |
150
+ | Dry run | The child receives `dryRun: parentContext.dryRun` and applies its own per-step policy. Child steps with `dryRun: "skip"` are skipped; ordinary child steps run normally. |
151
+ | Selective execution | Selecting or targeting the opaque parent step runs the child according to its mapped controls. Parent selection is never forwarded into the child's ID namespace. |
152
+ | Checkpoints | The adapter adds no checkpoint behavior. The child receives mapped options and the normal runtime context. |
153
+
154
+ ## Guarantees
155
+
156
+ Call sites infer required and optional dependency inputs, parent domain
157
+ options, required child options, the default child result, and a transformed
158
+ `mapResult` output without explicit generic arguments. Missing required child
159
+ options fail at compile time.
160
+
161
+ The parent plan contains only the opaque stage. The child receives the
162
+ parent's working directory, logger, clock, sleep function, and abort signal.
163
+ Parent hooks see no raw child lifecycle events. The internal bridge converts
164
+ the child's canonical step statuses into monotonic progress on the opaque
165
+ parent step.
166
+
167
+ Parent dry-run precedence prevents a child side-effecting step from running.
168
+ An unusable child result fails one opaque parent step and names the child
169
+ pipeline, step, and error, including when `continueOnError` produced a
170
+ best-effort final value. Later cancellation interrupts in-flight child work.
171
+ An invalid child plan fails before the child starts.
172
+
173
+ `fromPipeline` identity (no `mapResult`) uses overloads so the parent-facing
174
+ output stays the child result.
175
+
176
+ ## Out of scope
177
+
178
+ Policy skip plus `TOut | undefined` typing cover skip-without-run for opaque
179
+ `fromPipeline` steps. Runner substitution and any richer skipped-output shape
180
+ beyond policy skip are not supported.
181
+
182
+ Hierarchical or versioned external events, flattened or namespaced child
183
+ selection, and parallel DAG scheduling remain out of scope. Mapped children
184
+ provide bounded parallelism inside one opaque parent step; they do not make
185
+ the pipeline DAG executor parallel.
package/docs/cli.md ADDED
@@ -0,0 +1,118 @@
1
+ # CLI
2
+
3
+ The `tubeless` binary inspects, plans, graphs, and runs exported pipeline
4
+ modules. It requires Bun 1.3.14 or later (`#!/usr/bin/env bun`). Bun loads
5
+ TypeScript pipeline files directly. `npx tubeless` is not supported.
6
+
7
+ ```sh
8
+ bunx tubeless --help
9
+ ```
10
+
11
+ Library imports stay dependency-free compiled JavaScript. The CLI is the Bun
12
+ workbench around those same exports.
13
+
14
+ ## Commands
15
+
16
+ | Command | Accepts | Does |
17
+ | ------------------ | -------------------------------- | ----------------------------------------------------------------------------- |
18
+ | `tubeless inspect` | A pipeline export | Prints identity (`id`, targets, exact steps) plus the default structural plan |
19
+ | `tubeless plan` | A pipeline or command export | Previews selection without executing or requiring domain options |
20
+ | `tubeless graph` | A pipeline export | Writes Mermaid flowchart source |
21
+ | `tubeless run` | A `definePipelineCommand` export | Executes the command's own validated CLI contract |
22
+ | `tubeless ui` | An optional studio catalog | Serves the local run studio; see [studio](./studio.md) |
23
+
24
+ The workbench discovers a single matching export automatically. Pass
25
+ `--export Name` when the file exports more than one. `plan` prefers a marked
26
+ command when a module exports both a pipeline and a command.
27
+
28
+ ```sh
29
+ bunx tubeless inspect ./pipelines/import-pipeline.ts
30
+ bunx tubeless plan ./pipelines/import-pipeline.ts --target normalize --explain
31
+ bunx tubeless graph ./pipelines/import-pipeline.ts --markdown
32
+ bunx tubeless run ./scripts/import.ts -- --source rows.txt --target normalize
33
+ ```
34
+
35
+ Workbench flags stay before `--`. Application flags belong after it.
36
+ `tubeless run` never executes a raw pipeline or guesses domain options.
37
+
38
+ ```sh
39
+ tubeless run --export ImportCommand ./scripts/import.ts -- --source rows.txt --target normalize
40
+ ```
41
+
42
+ For command help:
43
+
44
+ ```sh
45
+ tubeless run ./scripts/import.ts -- --help
46
+ ```
47
+
48
+ ## Inspect
49
+
50
+ ```
51
+ tubeless inspect [options] <pipeline-file>
52
+ ```
53
+
54
+ - `-e, --export <name>` selects a pipeline export
55
+ - `--json` emits identity and the default plan as JSON
56
+
57
+ ## Plan
58
+
59
+ ```
60
+ tubeless plan [options] <pipeline-or-command-file>
61
+ ```
62
+
63
+ - `-e, --export <name>` selects a pipeline or command export
64
+ - `-t, --target <id>` selects a declared target and its prerequisites (repeatable)
65
+ - `-s, --step <id>` selects exact internal steps (repeatable)
66
+ - `--dry-run` shows each step's dry-run disposition
67
+ - `--explain` includes target/dependency selection provenance
68
+ - `--json` emits the structured plan
69
+
70
+ Do not simulate planning with a `--plan` flag on the command itself. Use
71
+ `command.plan()` or `tubeless plan`. `--target` and `--step` cannot be combined.
72
+
73
+ ## Graph
74
+
75
+ ```
76
+ tubeless graph [options] <pipeline-file>
77
+ ```
78
+
79
+ - `-e, --export <name>` selects a pipeline export
80
+ - `-d, --direction <value>` is `BT`, `LR`, `RL`, `TB`, or `TD` (default `TD`)
81
+ - `--descriptions` includes step descriptions in node labels
82
+ - `--markdown` wraps the result in a fenced Mermaid block
83
+
84
+ The same graph is available in process as `pipeline.toMermaid()`.
85
+
86
+ ## Run
87
+
88
+ ```
89
+ tubeless run [options] <command-file> [-- <command-args...>]
90
+ ```
91
+
92
+ - `-e, --export <name>` selects a command export
93
+ - `--store <path>` appends run events to a local SQLite database
94
+
95
+ `run` accepts only a `definePipelineCommand` export. That export owns parsing,
96
+ validation, option mapping, reporting, and the result summary. Omit `mapOptions`
97
+ when validated flags already satisfy same-name pipeline options; keep it when
98
+ names, types, defaults, or derived values differ.
99
+
100
+ A first script is [`cli-job.ts`](../examples/cli-job.ts). Local history with
101
+ `--store` is covered in [the studio](./studio.md).
102
+
103
+ ## Exit codes
104
+
105
+ | Code | Meaning |
106
+ | ---- | ------------ |
107
+ | `0` | Success |
108
+ | `1` | Usage |
109
+ | `2` | Load |
110
+ | `3` | Definition |
111
+ | `4` | Validation |
112
+ | `5` | Planning |
113
+ | `6` | Execution |
114
+ | `7` | Cancellation |
115
+
116
+ These values are also exported as `TUBELESS_WORKBENCH_EXIT_CODE` from
117
+ `tubeless/cli`. SIGINT is forwarded through the command context. Help (`--help`
118
+ or a command's own help) exits `0`.