stageflow 0.12.0 → 0.15.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 (115) hide show
  1. package/README.md +5 -1
  2. package/dist/agent/claudeAdapter.js +31 -1
  3. package/dist/agent/piAdapter.d.ts +6 -4
  4. package/dist/agent/piAdapter.js +71 -26
  5. package/dist/agent/piIsolatedMcp.d.ts +23 -0
  6. package/dist/agent/piIsolatedMcp.js +223 -0
  7. package/dist/agent/piIsolatedMcpProbe.d.ts +15 -0
  8. package/dist/agent/piIsolatedMcpProbe.js +292 -0
  9. package/dist/agent/port.d.ts +4 -2
  10. package/dist/agent/port.js +1 -1
  11. package/dist/cli/initTemplates.d.ts +1 -1
  12. package/dist/cli/initTemplates.js +1 -1
  13. package/dist/cli/migrateYamlCommand.d.ts +18 -0
  14. package/dist/cli/migrateYamlCommand.js +180 -0
  15. package/dist/cli/runOutput.js +26 -18
  16. package/dist/cli/validateCommand.d.ts +1 -0
  17. package/dist/cli/validateCommand.js +2 -0
  18. package/dist/cli.js +15 -1
  19. package/dist/config/browseCatalog.js +1 -1
  20. package/dist/config/createPipeline.d.ts +1 -1
  21. package/dist/config/createPipeline.js +23 -11
  22. package/dist/config/createStage.d.ts +1 -1
  23. package/dist/config/createStage.js +10 -5
  24. package/dist/config/legacyYaml.d.ts +28 -0
  25. package/dist/config/legacyYaml.js +98 -0
  26. package/dist/config/loadOutcome.d.ts +2 -1
  27. package/dist/config/loadOutcome.js +4 -1
  28. package/dist/config/loadPipeline.d.ts +2 -0
  29. package/dist/config/loadPipeline.js +151 -8
  30. package/dist/config/loadStage.d.ts +9 -1
  31. package/dist/config/loadStage.js +207 -29
  32. package/dist/config/loadStageflowManifest.js +10 -0
  33. package/dist/config/loadTask.js +14 -0
  34. package/dist/config/materializeStageModels.d.ts +7 -0
  35. package/dist/config/materializeStageModels.js +35 -0
  36. package/dist/config/mergePipelineIncludes.d.ts +5 -1
  37. package/dist/config/mergePipelineIncludes.js +82 -4
  38. package/dist/config/migrateYaml.d.ts +19 -0
  39. package/dist/config/migrateYaml.js +627 -0
  40. package/dist/config/modelField.d.ts +13 -0
  41. package/dist/config/modelField.js +16 -0
  42. package/dist/config/normalizePipelineStageEntry.js +48 -3
  43. package/dist/config/parseCompletionContract.d.ts +5 -2
  44. package/dist/config/parseCompletionContract.js +16 -3
  45. package/dist/config/parsePreEmitChecks.js +4 -0
  46. package/dist/config/pipelineNeeds.js +3 -3
  47. package/dist/config/pipelineStageKeys.d.ts +11 -0
  48. package/dist/config/pipelineStageKeys.js +15 -0
  49. package/dist/config/printTargetYaml.d.ts +28 -0
  50. package/dist/config/printTargetYaml.js +243 -0
  51. package/dist/config/resolveModel.d.ts +20 -0
  52. package/dist/config/resolveModel.js +35 -0
  53. package/dist/config/resolveStageMcpServers.d.ts +35 -0
  54. package/dist/config/resolveStageMcpServers.js +207 -0
  55. package/dist/config/validateCatalog.d.ts +5 -1
  56. package/dist/config/validateCatalog.js +87 -6
  57. package/dist/config/validateCompletionContract.js +5 -0
  58. package/dist/config/yamlDialect.d.ts +29 -0
  59. package/dist/config/yamlDialect.js +327 -0
  60. package/dist/envelope/payloadSchema.d.ts +37 -5
  61. package/dist/envelope/payloadSchema.js +272 -22
  62. package/dist/mcp/catalogTools.js +1 -0
  63. package/dist/mcp/readArtifact.d.ts +2 -0
  64. package/dist/mcp/readArtifact.js +25 -1
  65. package/dist/package-meta.d.ts +1 -1
  66. package/dist/package-meta.js +1 -1
  67. package/dist/runstore/pipelineDagSnapshot.js +2 -0
  68. package/dist/runstore/port.d.ts +1 -0
  69. package/dist/runtime/completionCheckRunner.d.ts +3 -2
  70. package/dist/runtime/completionCheckRunner.js +3 -2
  71. package/dist/runtime/envelopeRouting.d.ts +2 -2
  72. package/dist/runtime/pipelineRunner.d.ts +3 -0
  73. package/dist/runtime/pipelineRunner.js +23 -3
  74. package/dist/runtime/runManager.js +3 -2
  75. package/dist/runtime/stageAttemptBootstrap.d.ts +2 -2
  76. package/dist/runtime/stageAttemptBootstrap.js +38 -2
  77. package/dist/runtime/stageRunner.d.ts +2 -2
  78. package/dist/runtime/taskInput.d.ts +7 -0
  79. package/dist/runtime/taskInput.js +60 -0
  80. package/dist/runtime/verifiedStageExecution.d.ts +2 -0
  81. package/dist/runtime/verifiedStageExecution.js +13 -2
  82. package/dist/server/http.js +23 -4
  83. package/dist/server/projectMcpRoutes.d.ts +6 -0
  84. package/dist/server/projectMcpRoutes.js +38 -0
  85. package/dist/types/completion.d.ts +6 -0
  86. package/dist/types/pipeline.d.ts +23 -2
  87. package/dist/types/preEmitCheck.d.ts +4 -6
  88. package/dist/types/stage.d.ts +24 -4
  89. package/dist/types/stageflowManifest.d.ts +2 -0
  90. package/dist/types/task.d.ts +1 -0
  91. package/dist/ui/assets/index-GtyiWiZg.js +118 -0
  92. package/dist/ui/index.html +1 -1
  93. package/package.json +4 -1
  94. package/skills/stageflow-author/SKILL.md +20 -2
  95. package/skills/stageflow-author/assets/examples/branch-decision/hotfix.yaml +9 -1
  96. package/skills/stageflow-author/assets/examples/branch-decision/release-gate.pipeline.yaml +6 -2
  97. package/skills/stageflow-author/assets/examples/branch-decision/run-tests.yaml +9 -1
  98. package/skills/stageflow-author/assets/examples/branch-decision/ship.yaml +16 -1
  99. package/skills/stageflow-author/assets/examples/linear-review/draft.yaml +12 -2
  100. package/skills/stageflow-author/assets/examples/linear-review/publish.yaml +16 -1
  101. package/skills/stageflow-author/assets/examples/linear-review/review-loop.pipeline.yaml +11 -2
  102. package/skills/stageflow-author/assets/examples/linear-review/review.yaml +19 -1
  103. package/skills/stageflow-author/assets/examples/non-sdlc-digest/gather.yaml +9 -1
  104. package/skills/stageflow-author/assets/examples/non-sdlc-digest/research-digest.pipeline.yaml +6 -2
  105. package/skills/stageflow-author/assets/examples/non-sdlc-digest/send.yaml +16 -1
  106. package/skills/stageflow-author/assets/examples/non-sdlc-digest/summarize.yaml +9 -1
  107. package/skills/stageflow-author/references/catalog-mapping.md +80 -17
  108. package/skills/stageflow-author/references/catalog-write-conventions.md +2 -2
  109. package/skills/stageflow-author/references/stage-prompt-template.md +63 -4
  110. package/skills/stageflow-session-capture/SKILL.md +1 -1
  111. package/skills/stageflow-session-capture/assets/example-pipeline/example.pipeline.yaml +2 -1
  112. package/skills/stageflow-session-capture/assets/example-pipeline/implement.yaml +13 -2
  113. package/skills/stageflow-session-capture/assets/example-pipeline/research.yaml +18 -2
  114. package/skills/stageflow-session-capture/references/catalog-authoring.md +6 -3
  115. package/dist/ui/assets/index-CXGz4nL7.js +0 -118
@@ -13,7 +13,7 @@
13
13
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&display=swap" />
14
14
  <link rel="icon" type="image/svg+xml" href="/stageflow-icon.svg" />
15
15
  <title>Stageflow</title>
16
- <script type="module" crossorigin src="/assets/index-CXGz4nL7.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-GtyiWiZg.js"></script>
17
17
  <link rel="stylesheet" crossorigin href="/assets/index-uVHiae8s.css">
18
18
  </head>
19
19
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stageflow",
3
- "version": "0.12.0",
3
+ "version": "0.15.0",
4
4
  "description": "Stageflow — CLI pipeline runtime for configurable stages on Pi",
5
5
  "type": "module",
6
6
  "bin": {
@@ -63,9 +63,12 @@
63
63
  "@anthropic-ai/claude-agent-sdk": "^0.3.263",
64
64
  "@earendil-works/pi-ai": "^0.84.1",
65
65
  "@earendil-works/pi-coding-agent": "^0.84.1",
66
+ "@earendil-works/pi-tui": "^0.84.4",
66
67
  "@modelcontextprotocol/node": "^2.0.0",
67
68
  "@modelcontextprotocol/server": "^2.0.0",
68
69
  "better-sqlite3": "^13.0.3",
70
+ "jiti": "^2.7.0",
71
+ "pi-mcp-adapter": "^2.32.1",
69
72
  "typebox": "^1.3.7",
70
73
  "yaml": "^2.9.0",
71
74
  "zod": "^4.4.3"
@@ -35,9 +35,27 @@ Ask until you can name every step, how they sequence, any decision point, and an
35
35
 
36
36
  ## Map
37
37
 
38
- Read [`references/catalog-mapping.md`](references/catalog-mapping.md). Apply those rules to the confirmed structure. Read [`references/stage-prompt-template.md`](references/stage-prompt-template.md) for each stage's `system_prompt`, `model`, and `gate_kinds`.
38
+ Read [`references/catalog-mapping.md`](references/catalog-mapping.md). Apply those rules to the confirmed structure. Read [`references/stage-prompt-template.md`](references/stage-prompt-template.md) for each stage's `system_prompt`, optional `model`, `io`, `verify`, and `gate_kinds`. Apply the Anti-patterns section below while mapping. When the human needs hard advance gates, plan body `verify` and wiring `on_verify_fail`. When a review can demand changes, include an address-feedback stage before approve/ship.
39
39
 
40
- **Done when** you have a pipeline id, one stage id per step, `needs` / `fork` wiring, and a prompt plan per stage.
40
+ **Done when** you have a pipeline id, one stage id per step, `needs` / `fork` wiring, a prompt plan per stage, and any `verify` / `on_verify_fail` / remediation stages the structure requires.
41
+
42
+ ## Anti-patterns
43
+
44
+ Author these contracts explicitly. They come from long runs that failed after hours of real work.
45
+
46
+ | Always do | How |
47
+ |---|---|
48
+ | End every attempt on a tool call | Prompt: last call is `emit_stage_envelope`, or `ask_operator` while waiting on HITL. Each clonable instance emits on its own. |
49
+ | Land required files in the attempt artifact dir | Prompt `write_stage_artifact`. Body `verify` with `type: artifact` and `when` including `after`. `ask_operator` `artifact_backed` references that same path. |
50
+ | Make implement stages change the checkout | Body `verify` `checkout_changes` + `path_fields`; `io.output.schema` with required checkout-relative path arrays. Exploration-only is its own stage or a failure emit. |
51
+ | Hand successors clean envelopes | `summary` / `payload` = outcomes and artifact pointers only. Name required `io.output.schema` fields in the prompt. |
52
+ | Give review blockers a place to land | Wire `review → address-feedback → approve/ship`. Put `verdict` / `blocking_findings` (or equivalent) in the review `io.output.schema`. |
53
+ | Bind success to the full checklist | If a plan lists N commands, success requires all of them in `commands_run` with exit 0. Cheap checks first; slow suites last or in a verify stage. Size `timeout_ms` to the work. |
54
+ | Use configured models on every sibling | Same configured `model` family after a fork/fan-out unless the human asks otherwise. Reliable model on publish/PR stages. |
55
+ | Finish publish in the accept attempt | After `artifact_backed` accept: side effects (commit/push/PR) → final artifact → emit in the same attempt. Fix emit/schema errors by re-emitting. |
56
+ | Emit full clone assignments | When `clonable: true`, parent success uses `clone_forks` with full envelopes matching `io.input.schema`. Leave clonable unset when the clone count is unknown. |
57
+
58
+ Throwaway checkout deliverables (HTML spike, scratch file): prompt builtin `write` at a checkout-relative path, `artifacts: []` on emit, path in `payload`.
41
59
 
42
60
  ## Locate
43
61
 
@@ -7,4 +7,12 @@ system_prompt: |
7
7
 
8
8
  When finished, call emit_stage_envelope exactly once with status, summary,
9
9
  artifacts, and a payload describing what you repaired.
10
- model: anthropic/claude-sonnet-4-5
10
+ io:
11
+ output:
12
+ schema:
13
+ type: object
14
+ required:
15
+ - repaired
16
+ properties:
17
+ repaired:
18
+ type: boolean
@@ -1,4 +1,5 @@
1
1
  id: release-gate
2
+ model: anthropic/claude-sonnet-4-5
2
3
  stages:
3
4
  - id: run-tests
4
5
  uses: ./run-tests.yaml
@@ -6,7 +7,10 @@ stages:
6
7
  select: one
7
8
  - id: hotfix
8
9
  uses: ./hotfix.yaml
9
- needs: run-tests
10
+ needs: [run-tests]
10
11
  - id: ship
11
12
  uses: ./ship.yaml
12
- needs: run-tests
13
+ needs: [run-tests]
14
+ on_verify_fail:
15
+ mode: manual
16
+ retry_safety: side_effecting
@@ -10,4 +10,12 @@ system_prompt: |
10
10
  On a success emit, include fork_choice naming immediate successor id(s) that
11
11
  should run. Name only ids listed as this stage's children. select: one →
12
12
  exactly one id: hotfix when the results fail, ship when they pass.
13
- model: anthropic/claude-sonnet-4-5
13
+ io:
14
+ output:
15
+ schema:
16
+ type: object
17
+ required:
18
+ - ready
19
+ properties:
20
+ ready:
21
+ type: boolean
@@ -7,4 +7,19 @@ system_prompt: |
7
7
 
8
8
  When finished, call emit_stage_envelope exactly once with status, summary,
9
9
  artifacts, and a payload describing what you shipped.
10
- model: anthropic/claude-sonnet-4-5
10
+ io:
11
+ output:
12
+ schema:
13
+ type: object
14
+ required:
15
+ - shipped
16
+ properties:
17
+ shipped:
18
+ type: boolean
19
+ verify:
20
+ - id: ship-notes
21
+ type: artifact
22
+ path: ship.md
23
+ nonempty: true
24
+ when:
25
+ - after
@@ -5,5 +5,15 @@ system_prompt: |
5
5
  Write the draft via write_stage_artifact (for example draft.md).
6
6
 
7
7
  When finished, call emit_stage_envelope exactly once with status, summary,
8
- artifacts, and a payload the next stage can use.
9
- model: anthropic/claude-sonnet-4-5
8
+ artifacts, and a payload the next stage can use. Your last tool call in this
9
+ attempt must be emit_stage_envelope (or ask_operator if waiting on HITL).
10
+ An empty final message is a stage failure.
11
+ io:
12
+ output:
13
+ schema:
14
+ type: object
15
+ required:
16
+ - title
17
+ properties:
18
+ title:
19
+ type: string
@@ -7,4 +7,19 @@ system_prompt: |
7
7
 
8
8
  When finished, call emit_stage_envelope exactly once with status, summary,
9
9
  artifacts, and a payload describing what you produced.
10
- model: anthropic/claude-sonnet-4-5
10
+ io:
11
+ output:
12
+ schema:
13
+ type: object
14
+ required:
15
+ - published
16
+ properties:
17
+ published:
18
+ type: boolean
19
+ verify:
20
+ - id: published-artifact
21
+ type: artifact
22
+ path: published.md
23
+ nonempty: true
24
+ when:
25
+ - after
@@ -1,10 +1,19 @@
1
1
  id: review-loop
2
+ model: anthropic/claude-sonnet-4-5
2
3
  stages:
3
4
  - id: draft
4
5
  uses: ./draft.yaml
5
6
  - id: review
6
7
  uses: ./review.yaml
7
- needs: draft
8
+ needs: [draft]
9
+ on_verify_fail:
10
+ mode: repair
11
+ max_attempts: 3
12
+ retry_safety: idempotent
13
+ include_failed_checks: true
8
14
  - id: publish
9
15
  uses: ./publish.yaml
10
- needs: review
16
+ needs: [review]
17
+ on_verify_fail:
18
+ mode: manual
19
+ retry_safety: side_effecting
@@ -12,4 +12,22 @@ system_prompt: |
12
12
  again with artifact_backed in this same stage. Do not complete yet.
13
13
  4. Call emit_stage_envelope with an advancing success status only after the
14
14
  operator accepts. Never emit before accept.
15
- model: anthropic/claude-sonnet-4-5
15
+ io:
16
+ output:
17
+ schema:
18
+ type: object
19
+ required:
20
+ - verdict
21
+ properties:
22
+ verdict:
23
+ type: string
24
+ verify:
25
+ - id: review-accepted
26
+ type: gate
27
+ kind: artifact_backed
28
+ - id: review-artifact
29
+ type: artifact
30
+ path: review.md
31
+ nonempty: true
32
+ when:
33
+ - after
@@ -6,4 +6,12 @@ system_prompt: |
6
6
 
7
7
  When finished, call emit_stage_envelope exactly once with status, summary,
8
8
  artifacts, and a payload the next stage can use.
9
- model: anthropic/claude-sonnet-4-5
9
+ io:
10
+ output:
11
+ schema:
12
+ type: object
13
+ required:
14
+ - source_count
15
+ properties:
16
+ source_count:
17
+ type: integer
@@ -1,10 +1,14 @@
1
1
  id: research-digest
2
+ model: anthropic/claude-sonnet-4-5
2
3
  stages:
3
4
  - id: gather
4
5
  uses: ./gather.yaml
5
6
  - id: summarize
6
7
  uses: ./summarize.yaml
7
- needs: gather
8
+ needs: [gather]
8
9
  - id: send
9
10
  uses: ./send.yaml
10
- needs: summarize
11
+ needs: [summarize]
12
+ on_verify_fail:
13
+ mode: manual
14
+ retry_safety: side_effecting
@@ -7,4 +7,19 @@ system_prompt: |
7
7
 
8
8
  When finished, call emit_stage_envelope exactly once with status, summary,
9
9
  artifacts, and a payload describing who received the digest.
10
- model: anthropic/claude-sonnet-4-5
10
+ io:
11
+ output:
12
+ schema:
13
+ type: object
14
+ required:
15
+ - sent
16
+ properties:
17
+ sent:
18
+ type: boolean
19
+ verify:
20
+ - id: sent-record
21
+ type: artifact
22
+ path: sent.md
23
+ nonempty: true
24
+ when:
25
+ - after
@@ -6,4 +6,12 @@ system_prompt: |
6
6
 
7
7
  When finished, call emit_stage_envelope exactly once with status, summary,
8
8
  artifacts, and a payload the next stage can use.
9
- model: anthropic/claude-sonnet-4-5
9
+ io:
10
+ output:
11
+ schema:
12
+ type: object
13
+ required:
14
+ - headline
15
+ properties:
16
+ headline:
17
+ type: string
@@ -16,7 +16,7 @@ Collision handling lives in [`catalog-write-conventions.md`](catalog-write-conve
16
16
 
17
17
  ## Sequencing
18
18
 
19
- `needs` is a single parent stage id, or an array of at least two parent ids when one step waits for several earlier steps. Strings default to `on: [succeeded]`. `{ id, on }` accepts a non-empty unique subset of `succeeded` | `failed` | `skipped`.
19
+ `needs` is a parent stage id, or a non-empty array (length ≥ 1). Strings default to `on: [succeeded]`. `{ id, on }` accepts a non-empty unique subset of `succeeded` | `failed` | `skipped`.
20
20
 
21
21
  | Human says | Pipeline shape |
22
22
  |---|---|
@@ -28,30 +28,93 @@ Collision handling lives in [`catalog-write-conventions.md`](catalog-write-conve
28
28
 
29
29
  Default `select` is `one` unless the human says more than one branch can run.
30
30
 
31
- Map linear chains, sibling fan-out, generic fan-in (`needs` array), and single-level `fork`. A runtime clone count is not knowable from a static description — leave `clonable` and `clone_forks` unset. Clone-list joins (one catalog parent, `priorEnvelopes`) are not this shape.
31
+ Map linear chains, sibling fan-out, generic fan-in (`needs` array), and single-level `fork`.
32
32
 
33
33
  A review, approval, or sign-off step is a gated stage: put `gate_kinds` on that stage file and follow [`stage-prompt-template.md`](stage-prompt-template.md).
34
34
 
35
+ ## Review then fix then approve
36
+
37
+ When the human wants a review that can demand changes, then a later approve/ship:
38
+
39
+ ```
40
+ … → review → address-feedback → approve
41
+ ```
42
+
43
+ Do not wire approve/ship as the immediate child of review. Pipelines are forward-only; without an address-feedback stage, blocking findings have nowhere to land. The address-feedback stage is the only post-review editor; the approve stage is the backstop.
44
+
45
+ ## Verify and on_verify_fail
46
+
47
+ Put `verify` on the **stage body** (the `uses:` file or inline entry). Put `on_verify_fail` on the **pipeline** stage entry (the `uses:` wrapper). Use when the human needs a hard gate before advance:
48
+
49
+ | Need | Catalog |
50
+ |---|---|
51
+ | Required file under the attempt artifact dir | `verify` with `type: artifact`, `path`, usually `nonempty: true`, `when: [after]` |
52
+ | HITL must complete this attempt | `type: gate` with `kind` that appears in the stage's `gate_kinds` (default `when: [emit]`) |
53
+ | Implement must change the checkout | `type: checkout_changes` with `path_fields` naming required arrays in `io.output.schema` |
54
+ | Auto-retry when after-phase verify fails | `on_verify_fail: { mode: repair, max_attempts: N, retry_safety: idempotent, include_failed_checks: true }` |
55
+ | Side-effecting publish / ship | `on_verify_fail: { mode: manual, retry_safety: side_effecting }` |
56
+
57
+ `on_verify_fail` requires at least one after-phase `verify` item. Check `id` values must be unique within the stage. Check discriminator is `type:` (gate widgets still use `kind:`). Wire these for writer stages and final gates; prompts alone do not enforce them.
58
+
59
+ ## Clonable successors
60
+
61
+ A runtime clone count is not knowable from a vague description — leave `clonable` and `clone_forks` unset and prefer sibling stages or a single review.
62
+
63
+ When the human explicitly wants N parallel instances of **one** successor catalog id (e.g. several review lenses, several prototype variants):
64
+
65
+ - On that successor pipeline entry: `clonable: true` and `clone_cap` (integer ≥ 2).
66
+ - That successor must have at least one child (a join / address-feedback / collect stage). It cannot be a DAG leaf.
67
+ - Parent success emit uses `clone_forks` for that successor (not `fork_choice`). Each clone assignment is a full envelope; validate assignments with `io.input.schema` on the clonable stage body.
68
+ - Join stages that wait on the clonable parent read clone-list `priorEnvelopes`, not `priorEnvelopesByStage`.
69
+
70
+ `fork` + `fork_choice` picks which **branch stage ids** run. `clone_forks` spawns **N instances** of one successor id.
71
+
72
+ ## Models
73
+
74
+ Confirm providers before writing. After a fork, give every sibling the same configured `model` unless the human asks for different ones. Prefer a reliable configured model on side-effecting final stages.
75
+
35
76
  ## Worked examples
36
77
 
37
78
  ### Review loop (linear)
38
79
 
39
- Three sequential steps; the middle one is a sign-off.
80
+ Three sequential steps; the middle one is a sign-off. Reject/revise stays inside the gated `review` stage.
40
81
 
41
82
  ```yaml
42
83
  id: review-loop
84
+ model: anthropic/claude-sonnet-4-5
43
85
  stages:
44
86
  - id: draft
45
87
  uses: ./draft.yaml
46
88
  - id: review
47
89
  uses: ./review.yaml
48
- needs: draft
90
+ needs: [draft]
49
91
  - id: publish
50
92
  uses: ./publish.yaml
51
- needs: review
93
+ needs: [review]
94
+ ```
95
+
96
+ `review` carries `gate_kinds: [artifact_backed]` and emit-phase `verify` `type: gate`. Full set: [`../assets/examples/linear-review/`](../assets/examples/linear-review/).
97
+
98
+ ### Review with separate fix stage
99
+
100
+ When review can emit blockers and a later stage approves or ships, insert address-feedback between them:
101
+
102
+ ```yaml
103
+ stages:
104
+ - id: implement
105
+ uses: ./implement.yaml
106
+ - id: review
107
+ uses: ./review.yaml
108
+ needs: [implement]
109
+ - id: address-feedback
110
+ uses: ./address-feedback.yaml
111
+ needs: [review]
112
+ - id: approve
113
+ uses: ./approve.yaml
114
+ needs: [address-feedback]
52
115
  ```
53
116
 
54
- `review` carries `gate_kinds: [artifact_backed]`. Full set: [`../assets/examples/linear-review/`](../assets/examples/linear-review/).
117
+ Do not make `approve` / `ship` `needs: review` when blockers are expected.
55
118
 
56
119
  ### Release gate (fork, select one)
57
120
 
@@ -66,10 +129,10 @@ stages:
66
129
  select: one
67
130
  - id: hotfix
68
131
  uses: ./hotfix.yaml
69
- needs: run-tests
132
+ needs: [run-tests]
70
133
  - id: ship
71
134
  uses: ./ship.yaml
72
- needs: run-tests
135
+ needs: [run-tests]
73
136
  ```
74
137
 
75
138
  Full set: [`../assets/examples/branch-decision/`](../assets/examples/branch-decision/).
@@ -85,10 +148,10 @@ stages:
85
148
  uses: ./gather.yaml
86
149
  - id: summarize
87
150
  uses: ./summarize.yaml
88
- needs: gather
151
+ needs: [gather]
89
152
  - id: send
90
153
  uses: ./send.yaml
91
- needs: summarize
154
+ needs: [summarize]
92
155
  ```
93
156
 
94
157
  Full set: [`../assets/examples/non-sdlc-digest/`](../assets/examples/non-sdlc-digest/).
@@ -103,10 +166,10 @@ stages:
103
166
  uses: ./intake.yaml
104
167
  - id: collect-quotes
105
168
  uses: ./collect-quotes.yaml
106
- needs: intake
169
+ needs: [intake]
107
170
  - id: collect-notes
108
171
  uses: ./collect-notes.yaml
109
- needs: intake
172
+ needs: [intake]
110
173
  ```
111
174
 
112
175
  No `fork` field. Both siblings run.
@@ -121,10 +184,10 @@ stages:
121
184
  uses: ./clarify.yaml
122
185
  - id: research
123
186
  uses: ./research.yaml
124
- needs: clarify
187
+ needs: [clarify]
125
188
  - id: validation
126
189
  uses: ./validation.yaml
127
- needs: clarify
190
+ needs: [clarify]
128
191
  - id: synthesize
129
192
  uses: ./synthesize.yaml
130
193
  needs:
@@ -132,7 +195,7 @@ stages:
132
195
  - validation
133
196
  ```
134
197
 
135
- `needs` array length is ≥ 2. The join reads `priorEnvelopesByStage`, not clone-list `priorEnvelopes`.
198
+ `needs` array length ≥ 2 is keyed fan-in. The join reads `priorEnvelopesByStage`, not clone-list `priorEnvelopes`.
136
199
 
137
200
  ### Fork, select subset
138
201
 
@@ -146,10 +209,10 @@ stages:
146
209
  select: subset
147
210
  - id: email
148
211
  uses: ./email.yaml
149
- needs: choose-channels
212
+ needs: [choose-channels]
150
213
  - id: post
151
214
  uses: ./post.yaml
152
- needs: choose-channels
215
+ needs: [choose-channels]
153
216
  ```
154
217
 
155
218
  The success emit names one or more of those successor ids in `fork_choice`.
@@ -18,9 +18,9 @@ One directory (or the project root) holds:
18
18
  | File | Required fields |
19
19
  |---|---|
20
20
  | `<pipeline-id>.pipeline.yaml` | `id` matching the filename stem; `stages:` object entries |
21
- | `<stage-id>.yaml` | `id`, `system_prompt`, `model`; `gate_kinds` when the step is gated |
21
+ | `<stage-id>.yaml` | `id`, `system_prompt`; `model` when not inherited; `io` / `verify` / `gate_kinds` when the step needs them |
22
22
 
23
- Each pipeline stage entry has `id` and `uses: ./<id>.yaml` (path relative to the pipeline file). Non-root stages add `needs: <parent-id>`, or `needs:` as an array of at least two parent ids (strings or `{ id, on }`) when the stage waits for several parents. A deciding stage adds `fork:`. Filename stem matches `id` on every file.
23
+ Each pipeline stage entry has `id` and `uses: ./<id>.yaml` (path relative to the pipeline file). Non-root stages add `needs: <parent-id>` or `needs: [<parent-id>, …]` (length ≥ 1; strings or `{ id, on }`). A deciding stage adds `fork:`. Put `on_verify_fail` on the pipeline entry when after-phase `verify` should repair or wait for an operator. Filename stem matches `id` on every file. Optional pipeline-root `model` fills stages that omit `model`.
24
24
 
25
25
  ## Collisions
26
26
 
@@ -1,6 +1,6 @@
1
1
  # Stage prompt template
2
2
 
3
- Every external stage file has `id`, `system_prompt`, and `model`. Filename stem matches `id`. Default `model` is `anthropic/claude-sonnet-4-5`. When the human names a different model, write that string verbatim.
3
+ Every external stage file has `id` and `system_prompt`. Filename stem matches `id`. `model` is optional when the pipeline or `stageflow.yaml` supplies it — otherwise set it on the stage. Default when writing a concrete string is `anthropic/claude-sonnet-4-5`. When the human names a different model, write that string verbatim. Before writing models, confirm at least one provider is `configured` (see the skill Provider gate). Sibling stages after a fork should use the same configured model family unless the human asks otherwise.
4
4
 
5
5
  ## Base
6
6
 
@@ -14,12 +14,48 @@ system_prompt: |
14
14
  Write any output this step produces via write_stage_artifact.
15
15
 
16
16
  When finished, call emit_stage_envelope exactly once with status, summary,
17
- artifacts, and a payload the next stage can use.
17
+ artifacts, and a payload the next stage can use. Your last tool call in this
18
+ attempt must be emit_stage_envelope (or ask_operator if waiting on HITL).
19
+ An empty final message is a stage failure.
18
20
  model: anthropic/claude-sonnet-4-5
19
21
  ```
20
22
 
21
23
  The prompt has no `ask_operator` line. It ends on the `emit_stage_envelope` instruction. Call `emit_stage_envelope` once per attempt.
22
24
 
25
+ When the pipeline entry will carry after-phase `verify` with `type: artifact`, add this line after the goal:
26
+
27
+ ```
28
+ Required files must be created with write_stage_artifact. Checkout write/edit
29
+ does not satisfy after-phase verify.
30
+ ```
31
+
32
+ When the stage must change the project checkout (implement, patch, edit source), add:
33
+
34
+ ```
35
+ Success requires real checkout edits. Put checkout-relative paths in
36
+ payload.changed_files. Do not put stages/.../artifacts paths there. A report
37
+ or map alone is a failure emit, not success.
38
+ ```
39
+
40
+ When the deliverable is throwaway checkout content instead of a stage artifact:
41
+
42
+ ```
43
+ Write the deliverable with builtin write at <checkout-relative path>.
44
+ Set artifacts: [] on emit. Put that path in payload.<field>.
45
+ ```
46
+
47
+ ## Summary and payload
48
+
49
+ Add to every stage (base, gated, or fork):
50
+
51
+ ```
52
+ summary and payload are handed to the next stage verbatim. State outcomes and
53
+ artifact pointers only. Do not repeat this stage's prohibitions in summary —
54
+ downstream will obey them literally.
55
+ ```
56
+
57
+ When `io.output.schema` is on the stage file, name the required fields in the prompt so the emit matches.
58
+
23
59
  ## Gated
24
60
 
25
61
  When the step is a review, approval, or sign-off, add `gate_kinds` and resolve the gate before emit. Match this sequence: `write_stage_artifact` → `ask_operator` → `emit_stage_envelope` on accept.
@@ -32,7 +68,8 @@ system_prompt: |
32
68
  <one-line goal>. Get operator acceptance before completing this stage.
33
69
 
34
70
  1. Write the reviewable output via write_stage_artifact.
35
- 2. Call ask_operator with kind artifact_backed referencing that artifact path.
71
+ 2. Call ask_operator with kind artifact_backed referencing that artifact path
72
+ (stage artifact path, not a checkout path).
36
73
  3. On reject or change text: revise the artifact and call ask_operator again
37
74
  with artifact_backed in this same stage. Do not complete yet.
38
75
  4. Call emit_stage_envelope with an advancing success status only after the
@@ -42,6 +79,14 @@ model: anthropic/claude-sonnet-4-5
42
79
 
43
80
  Use the `gate_kinds` value that matches the human's review: `artifact_backed` for a file to accept, `confirm` for yes/no, `free_text` for an open reply, `multi_question` for a batch. `ask_operator` does not complete the stage.
44
81
 
82
+ When the gated stage also publishes or opens a PR after accept, append:
83
+
84
+ ```
85
+ After the operator accepts, immediately finish publish side effects in this
86
+ same attempt (commit, push, open PR as required), write any final artifact,
87
+ then emit. Do not pause after accept. Do not ask_operator for emit/schema errors.
88
+ ```
89
+
45
90
  ## Fork
46
91
 
47
92
  When the pipeline entry has `fork`, the success emit names immediate successors in `fork_choice`. Add this block to the base (or gated) prompt:
@@ -50,6 +95,20 @@ When the pipeline entry has `fork`, the success emit names immediate successors
50
95
  On a success emit, include fork_choice naming immediate successor id(s) that
51
96
  should run. Name only ids listed as this stage's children. select: one →
52
97
  exactly one id. select: subset → one or more of those ids.
98
+ Normalize free_text answers before mapping (trim, lowercase, collapse spaces
99
+ and hyphens). If unmapped, ask_operator again; do not emit.
100
+ ```
101
+
102
+ `fork_choice` on a failure emit is ignored. Leave `clone_forks` unset unless the human explicitly asked for clonable fan-out (see [`catalog-mapping.md`](catalog-mapping.md)).
103
+
104
+ ## Clonable parent
105
+
106
+ When a successor is `clonable: true`, the parent success emit uses `clone_forks` (not `fork_choice`) for that successor. Add:
107
+
108
+ ```
109
+ On success, include clone_forks covering every clonable successor exactly once.
110
+ Each clone entry is a full envelope with status, summary, artifacts, and payload
111
+ matching that successor's io.input.schema — not a stub object.
53
112
  ```
54
113
 
55
- `fork_choice` on a failure emit is ignored. Leave `clone_forks` unset.
114
+ Clonable child stages use the base (or gated) prompt. Remind them they are one clone among others and must emit independently.
@@ -74,7 +74,7 @@ Read [`references/catalog-authoring.md`](references/catalog-authoring.md) before
74
74
  `node scripts/resolve-catalog-id.mjs --text "<phrase>" --dir <write-dir> --kind pipeline|stage`
75
75
 
76
76
  Use the printed `{"id":"..."}`.
77
- 3. Write one external stage YAML per phase (`id`, `system_prompt`, `model`). End every `system_prompt` with an `emit_stage_envelope` footer. Omit `gate_kinds`. Filename stem must match `id`.
77
+ 3. Write one external stage YAML per phase (`id`, `system_prompt`, optional `model`). End every `system_prompt` with an `emit_stage_envelope` footer. Omit `gate_kinds`. Filename stem must match `id`.
78
78
  4. Write one pipeline YAML that wires the stages with `uses:` and `needs:` in dependency order.
79
79
 
80
80
  Do not write a `*.task.yaml`.
@@ -1,7 +1,8 @@
1
1
  id: example
2
+ model: anthropic/claude-sonnet-4-5
2
3
  stages:
3
4
  - id: research
4
5
  uses: ./research.yaml
5
6
  - id: implement
6
7
  uses: ./implement.yaml
7
- needs: research
8
+ needs: [research]
@@ -4,5 +4,16 @@ system_prompt: |
4
4
  there. Prefer what already worked in the source session.
5
5
 
6
6
  When finished, call emit_stage_envelope once with status, summary, artifacts,
7
- and a payload describing what you produced.
8
- model: anthropic/claude-sonnet-4-5
7
+ and a payload describing what you produced. Your last tool call in this
8
+ attempt must be emit_stage_envelope. An empty final message is a stage failure.
9
+ io:
10
+ output:
11
+ schema:
12
+ type: object
13
+ required:
14
+ - changed_files
15
+ properties:
16
+ changed_files:
17
+ type: array
18
+ items:
19
+ type: string
@@ -4,5 +4,21 @@ system_prompt: |
4
4
  unknown, and the next useful work.
5
5
 
6
6
  When finished, call emit_stage_envelope once with status, summary, artifacts,
7
- and a payload the next stage can use.
8
- model: anthropic/claude-sonnet-4-5
7
+ and a payload the next stage can use. Your last tool call in this attempt must
8
+ be emit_stage_envelope. An empty final message is a stage failure.
9
+ io:
10
+ output:
11
+ schema:
12
+ type: object
13
+ required:
14
+ - known
15
+ - unknown
16
+ properties:
17
+ known:
18
+ type: array
19
+ items:
20
+ type: string
21
+ unknown:
22
+ type: array
23
+ items:
24
+ type: string