valent-pipeline 0.5.0 → 0.5.1

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.
package/bin/cli.js CHANGED
@@ -41,6 +41,49 @@ program
41
41
  await upgrade(options);
42
42
  });
43
43
 
44
+ // status command — board read-model (composes pipeline-state.json + backlog + artifacts)
45
+ program
46
+ .command('status')
47
+ .description('Show pipeline status as a board read-model (human summary, or --json for the board-state contract)')
48
+ .option('--json', 'Emit the board-state JSON contract to stdout (for a board/notifier/CI)')
49
+ .option('--out <path>', 'Write the board-state JSON to a file (e.g. board-state.json for a board to watch)')
50
+ .option('--root <dir>', 'Project root to inspect (defaults to the current directory)')
51
+ .option('--no-audit', 'Skip merging the per-agent token/wall-clock audit trail into the board')
52
+ .action(async (options) => {
53
+ const { statusCmd } = await import('../src/commands/status.js');
54
+ await statusCmd(options);
55
+ });
56
+
57
+ // board command — serves the read-only board SPA + read API (a projection, never a write path)
58
+ program
59
+ .command('board')
60
+ .description('Serve a read-only board UI (Backlog + Kanban) over a local HTTP server')
61
+ .option('--port <n>', 'Port to listen on (default 7777)', '7777')
62
+ .option('--host <addr>', 'Host/interface to bind (default 127.0.0.1; 0.0.0.0 warns loudly)', '127.0.0.1')
63
+ .option('--root <dir>', 'Project root to project the board over (defaults to the current directory)')
64
+ .option('--open', 'Open the board in the default browser once it is listening')
65
+ .option('--no-audit', 'Skip merging the per-agent token/wall-clock cost trail into the board')
66
+ .action(async (options) => {
67
+ const { boardCmd } = await import('../src/commands/board.js');
68
+ await boardCmd(options);
69
+ });
70
+
71
+ // audit command — per-agent, per-story token + wall-clock trail (reads Workflow journals)
72
+ program
73
+ .command('audit')
74
+ .description('Per-agent, per-story audit trail (tokens + wall-clock), read from Workflow run journals')
75
+ .option('--story <id>', 'Filter to a single story')
76
+ .option('--run <runId>', 'Filter to a single workflow run')
77
+ .option('--json', 'Emit the audit JSON contract to stdout')
78
+ .option('--out <path>', 'Write the audit JSON to a file')
79
+ .option('--file <path>', 'Audit a single workflow journal file (wf_*.json)')
80
+ .option('--session-dir <dir>', 'Scan a specific session dir\'s workflows/ folder')
81
+ .option('--project-dir <dir>', 'Project dir whose ~/.claude/projects session journals to read (default: cwd)')
82
+ .action(async (options) => {
83
+ const { auditCmd } = await import('../src/commands/audit.js');
84
+ await auditCmd(options);
85
+ });
86
+
44
87
  // config validate command
45
88
  const configCmd = program
46
89
  .command('config')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valent-pipeline",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "v3 multi-agent AI pipeline for software development lifecycle",
5
5
  "type": "module",
6
6
  "bin": {
@@ -166,10 +166,32 @@ const MODELS = buildModelMap(a.models)
166
166
  // undefined => the agent inherits the main-loop (session) model.
167
167
  const modelFor = (role) => MODELS[String(role).toUpperCase()]
168
168
 
169
+ // --- per-agent reasoning effort (thinking budget) ----------------------------
170
+ // Optional, config-driven, mirrors `models`. args.reasoning is a level->roles map inverted to
171
+ // role->trigger-phrase. EMPTY default => no trigger injected unless the config opts in, so
172
+ // behavior is unchanged out of the box. Static + args only => journal-replay safe.
173
+ const REASONING_PHRASES = { think: 'think', 'think-hard': 'think hard', 'think-harder': 'think harder', ultrathink: 'ultrathink' }
174
+ const DEFAULT_REASONING = {} // blank control surface — fill `reasoning` in pipeline-config.yaml to use it
175
+ function buildReasoningMap(cfg) {
176
+ const map = { ...DEFAULT_REASONING }
177
+ if (cfg && typeof cfg === 'object' && !Array.isArray(cfg)) {
178
+ for (const level of Object.keys(REASONING_PHRASES)) {
179
+ for (const role of cfg[level] || []) {
180
+ if (typeof role === 'string') map[role.toUpperCase()] = REASONING_PHRASES[level]
181
+ }
182
+ }
183
+ }
184
+ return map
185
+ }
186
+ const REASONING = buildReasoningMap(a.reasoning)
187
+ const reasoningFor = (role) => REASONING[String(role).toUpperCase()]
188
+
169
189
  function buildPrompt({ role, promptFile, storyId, taskSubject, trigger, returnContract }) {
170
190
  const outputDir = `stories/${storyId}/output`
191
+ const think = reasoningFor(role) // undefined unless config opts this role into a thinking tier
171
192
  return [
172
193
  `You are **${role}**, for story ${storyId} in the valent-pipeline (sprint ${sprintId} planning).`,
194
+ ...(think ? ['', `Before you act, ${think} about the hardest parts of this task.`] : []),
173
195
  '',
174
196
  '## Setup',
175
197
  `1. Read your core prompt: \`.valent-pipeline/prompts/${promptFile}\` — identity, protocols, step sequence.`,
@@ -157,10 +157,33 @@ const MODELS = buildModelMap(a.models)
157
157
  // undefined => the agent inherits the main-loop (session) model.
158
158
  const modelFor = (role) => MODELS[String(role).toUpperCase()]
159
159
 
160
- const retroPrompt = (instruction, returnContract) =>
161
- `You are **RETROSPECTIVE**, analyzing story batch ${batchNumber} in the valent-pipeline. ` +
162
- `Read \`.valent-pipeline/prompts/retrospective.md\` and the step file named in the task. ${instruction} ` +
163
- (returnContract || 'Return your findings as the JSON object specified.')
160
+ // --- reasoning effort (thinking budget) --------------------------------------
161
+ // Optional, config-driven, mirrors `models`. args.reasoning is a level->roles map inverted to
162
+ // role->trigger-phrase. EMPTY default => nothing injected unless the config opts in. Every agent
163
+ // here shares the RETROSPECTIVE identity, so the knob keys on that role. Static + args only.
164
+ const REASONING_PHRASES = { think: 'think', 'think-hard': 'think hard', 'think-harder': 'think harder', ultrathink: 'ultrathink' }
165
+ const DEFAULT_REASONING = {} // blank control surface — fill `reasoning` in pipeline-config.yaml to use it
166
+ function buildReasoningMap(cfg) {
167
+ const map = { ...DEFAULT_REASONING }
168
+ if (cfg && typeof cfg === 'object' && !Array.isArray(cfg)) {
169
+ for (const level of Object.keys(REASONING_PHRASES)) {
170
+ for (const role of cfg[level] || []) {
171
+ if (typeof role === 'string') map[role.toUpperCase()] = REASONING_PHRASES[level]
172
+ }
173
+ }
174
+ }
175
+ return map
176
+ }
177
+ const REASONING = buildReasoningMap(a.reasoning)
178
+ const reasoningFor = (role) => REASONING[String(role).toUpperCase()]
179
+
180
+ const retroPrompt = (instruction, returnContract) => {
181
+ const think = reasoningFor('RETROSPECTIVE') // undefined unless config opts RETROSPECTIVE into a tier
182
+ return `You are **RETROSPECTIVE**, analyzing story batch ${batchNumber} in the valent-pipeline. ` +
183
+ (think ? `Before you act, ${think} about the hardest parts of this task. ` : '') +
184
+ `Read \`.valent-pipeline/prompts/retrospective.md\` and the step file named in the task. ${instruction} ` +
185
+ (returnContract || 'Return your findings as the JSON object specified.')
186
+ }
164
187
 
165
188
  // A stable de-dup key so loop-until-dry converges (don't re-count the same finding).
166
189
  const findingKey = (f) => `${(f.summary || '').toLowerCase().trim().slice(0, 80)}`
@@ -35,12 +35,17 @@
35
35
  * their structured returns.
36
36
  *
37
37
  * args (either form):
38
- * { stories: [{ storyId, projectType?, profiles? }, ...], projectType?, profiles?, maxRejectionCycles?, models? }
39
- * { storyId, projectType, profiles?, maxRejectionCycles?, models? } // single-story (back-compat)
38
+ * { stories: [{ storyId, projectType?, profiles? }, ...], projectType?, profiles?, maxRejectionCycles?, models?, reasoning? }
39
+ * { storyId, projectType, profiles?, maxRejectionCycles?, models?, reasoning? } // single-story (back-compat)
40
40
  *
41
41
  * `models` is the pipeline-config.yaml `models` tier->roles map (e.g. { opus:[...], sonnet:[...],
42
42
  * haiku:[...] }); the invoking skill passes it through so per-agent model tiers stay config-driven
43
43
  * and editable via `valent configure`. Omit it to use the baked-in default assignment.
44
+ *
45
+ * `reasoning` is the pipeline-config.yaml `reasoning` level->roles map (e.g. { ultrathink:[...],
46
+ * 'think-harder':[...], 'think-hard':[...], think:[...] }); it injects a thinking-effort trigger
47
+ * into a role's prompt. BLANK by default — omit it (or leave the levels empty) and nothing is
48
+ * injected, behavior unchanged. It is a config-driven control surface, parallel to `models`.
44
49
  */
45
50
 
46
51
  export const meta = {
@@ -206,14 +211,39 @@ const MODELS = buildModelMap(a.models)
206
211
  // undefined => the agent inherits the main-loop (session) model.
207
212
  const modelFor = (role) => MODELS[String(role).toUpperCase()]
208
213
 
214
+ // --- per-agent reasoning effort (thinking budget) ----------------------------
215
+ // Optional, config-driven, mirrors `models`. args.reasoning is a level->roles map; we invert it
216
+ // to role->trigger-phrase and overlay it on a baked-in default. The default is EMPTY — no role
217
+ // gets a thinking trigger unless the config opts it in, so behavior is unchanged out of the box.
218
+ // The phrase (when present) is injected into the agent prompt by buildPrompt; Claude Code
219
+ // escalates the thinking budget on these triggers. Static + args only => journal-replay safe.
220
+ const REASONING_PHRASES = { think: 'think', 'think-hard': 'think hard', 'think-harder': 'think harder', ultrathink: 'ultrathink' }
221
+ const DEFAULT_REASONING = {} // blank control surface — fill `reasoning` in pipeline-config.yaml to use it
222
+ function buildReasoningMap(cfg) {
223
+ const map = { ...DEFAULT_REASONING }
224
+ if (cfg && typeof cfg === 'object' && !Array.isArray(cfg)) {
225
+ for (const level of Object.keys(REASONING_PHRASES)) {
226
+ for (const role of cfg[level] || []) {
227
+ if (typeof role === 'string') map[role.toUpperCase()] = REASONING_PHRASES[level]
228
+ }
229
+ }
230
+ }
231
+ return map
232
+ }
233
+ const REASONING = buildReasoningMap(a.reasoning)
234
+ // undefined => inject no thinking trigger for this role.
235
+ const reasoningFor = (role) => REASONING[String(role).toUpperCase()]
236
+
209
237
  // --- prompt builder: mirrors providers/claude-code/spawn.template.md so spawned agents
210
238
  // get full pipeline context (core prompt + shared context + step-at-execution + the
211
239
  // handoff contract), not a terse one-liner. ------------------------------------------
212
240
 
213
241
  function buildPrompt({ role, promptFile, storyId, taskRef, taskSubject, trigger, completion, returnContract }) {
214
242
  const outputDir = `stories/${storyId}/output`
243
+ const think = reasoningFor(role) // undefined unless config opts this role into a thinking tier
215
244
  return [
216
245
  `You are **${role}**, for story ${storyId} in the valent-pipeline.`,
246
+ ...(think ? ['', `Before you act, ${think} about the hardest parts of this task.`] : []),
217
247
  '',
218
248
  '## Setup',
219
249
  `1. Read your core prompt: \`.valent-pipeline/prompts/${promptFile}\` — identity, protocols, step sequence.`,
@@ -141,6 +141,22 @@ Ask the user if they want to adjust any assignments. Common adjustments:
141
141
 
142
142
  Note: agents that were skipped due to project type (e.g., FEND for backend-api) should still appear in the model list -- they are inactive but remain configured.
143
143
 
144
+ ### Step 6b: Reasoning Effort (optional)
145
+
146
+ The `reasoning` section is a per-agent thinking-budget control surface that mirrors `models`. It maps an effort level to a list of agent roles; at spawn time the Workflow orchestrators inject that level's thinking trigger into the role's prompt. Levels, increasing: `think` < `think-hard` < `think-harder` < `ultrathink`.
147
+
148
+ It is **blank by default** — every level is an empty list, so nothing is injected and behavior is unchanged. Leave it blank unless the user wants to deepen specific agents' reasoning. Deeper thinking raises output quality but also token cost, so recommend the user re-check `/valent-review-cost` (or `valent audit`) after enabling it.
149
+
150
+ ```yaml
151
+ reasoning:
152
+ ultrathink: []
153
+ think-harder: [] # e.g. ["CRITIC", "JUDGE"] to make the gates deliberate harder
154
+ think-hard: []
155
+ think: []
156
+ ```
157
+
158
+ Common adjustments: add the quality gates (`CRITIC`, `JUDGE`, `READINESS`) under `think-harder` when a project keeps shipping subtle defects. This currently affects the **Workflow** orchestrators only (the `reasoning` arg); the prose Lead ignores it.
159
+
144
160
  ---
145
161
 
146
162
  ## Writing the Config File
@@ -61,6 +61,9 @@ Read these as needed to answer questions:
61
61
  **"How do I change which model an agent uses?"**
62
62
  → Edit the `models` section in `.valent-pipeline/pipeline-config.yaml`. Agents are assigned to opus/sonnet/haiku tiers.
63
63
 
64
+ **"Which agents cost the most / what's taking the most time?"**
65
+ → `/valent-review-cost` analyzes per-agent token and wall-clock spend across stories (from the Workflow run journals) and recommends model-tier changes. Or get the raw numbers with `node .valent-pipeline/bin/cli.js audit --json`.
66
+
64
67
  **"What happens when an agent gets rejected?"**
65
68
  → Peer-to-peer: READINESS rejects specs back to authors, CRITIC rejects code to devs, QA-B routes bugs to devs. Lead only handles JUDGE rejections and circuit breaker (after max_rejection_cycles). See `.valent-pipeline/docs/lead-lifecycle.md`.
66
69
 
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: valent-review-cost
3
+ description: 'Review per-agent token and wall-clock cost across stories and recommend model-tier (opus/sonnet/haiku) changes. Use when the user says "review cost", "what is taking the most time", "which agents cost the most", "tune model configs", or asks about pipeline token/time spend.'
4
+ ---
5
+
6
+ # valent-review-cost
7
+
8
+ Analyze where the pipeline spends tokens and wall-clock time, per agent and per story, then recommend concrete `models` tier changes. Read-only — you inspect run journals and config; you never modify the pipeline.
9
+
10
+ ## Data Sources
11
+
12
+ 1. **The audit trail** — per-agent tokens + wall-clock, read from the Workflow run journals the harness already writes (no instrumentation). Get it as JSON:
13
+
14
+ ```bash
15
+ node .valent-pipeline/bin/cli.js audit --json
16
+ ```
17
+
18
+ Or get the full board with cost merged onto each story card:
19
+
20
+ ```bash
21
+ node .valent-pipeline/bin/cli.js status --json
22
+ ```
23
+
24
+ Scope it when needed: `audit --story <id>` for one story, `audit --run <runId>` for one run.
25
+
26
+ 2. **The configured model tiers** — read the `models` section of `.valent-pipeline/pipeline-config.yaml`. It maps each tier to the roles assigned to it:
27
+
28
+ ```yaml
29
+ models:
30
+ opus: [READINESS, CRITIC, JUDGE, ...] # judgment-heavy
31
+ sonnet: [REQS, QA-A, QA-B, BEND, FEND, ...] # spec + build
32
+ haiku: [RESOLVE, Embed, Help, ...] # mechanical / CLI-runner
33
+ ```
34
+
35
+ ## How to Analyze
36
+
37
+ Run `audit --json` and parse the contract:
38
+ - `totals` — grand `tokens`, `agent_ms` (summed agent busy-time), `elapsed_ms` (true wall clock), `invocations`, `stories`, `runs`.
39
+ - `stories[]` — each `{ story, tokens, agent_ms, invocations, roles[] }`, sorted by tokens.
40
+ - `stories[].roles[]` — each `{ role, tokens, agent_ms, toolCalls, invocations, attempts, models[] }`, sorted by tokens. `models[]` is the model the role **actually ran on**.
41
+
42
+ Then:
43
+
44
+ 1. **Rank the spend.** Identify the top token consumers and top time consumers, both per role (aggregated across stories) and per story. Name the 3–5 biggest.
45
+
46
+ 2. **Cross-reference actual model vs configured tier.** For each costly role, compare `roles[].models[]` (what it ran on) against the `models` map (what it's configured for). Flag mismatches loudly — a role running on a higher tier than configured usually means the per-role tiers weren't applied and everything used the session default model. That is the first and biggest lever.
47
+
48
+ 3. **Separate "expensive because of tier" from "expensive because of rework."** If a role's `invocations` (or `attempts`) is > 1, its cost is inflated by rejection/rework cycles, not just model tier. CRITIC especially fans out (3 passes + triage) and re-runs on rejection. For those, note that prompt/spec quality or rejection-cap tuning may cut cost more than a tier change.
49
+
50
+ 4. **Recommend tier changes** with reasoning:
51
+ - High-token, low-judgment roles (RESOLVE and other CLI-runners; mechanical IO) on opus/sonnet → propose dropping a tier.
52
+ - Spec/build roles (REQS, QA-A, QA-B, dev agents) on opus → consider sonnet unless quality data argues otherwise.
53
+ - Quality gates (READINESS, CRITIC, JUDGE) → judgment is the point; keep on opus unless they're cheap anyway.
54
+ - Always tie each recommendation to the numbers (e.g. "CRITIC = 47% of story spend, ran on opus, 8 invocations from the rejection loop").
55
+
56
+ ## Output Format
57
+
58
+ Keep it short and decision-ready:
59
+
60
+ 1. **Headline** — total spend (tokens, true elapsed), top 3 cost drivers.
61
+ 2. **Per-role table** — role · tokens · agent-time · invocations · model-ran-on · configured-tier · mismatch?
62
+ 3. **Recommendations** — a short ordered list of specific `models` edits, each with the number that justifies it. Show the exact `pipeline-config.yaml` `models` change to make.
63
+ 4. **Caveats** — note that `agent_ms` is summed busy-time (exceeds `elapsed_ms` when agents run in parallel), and that rework-driven cost is fixed by prompt/spec quality, not tiers.
64
+
65
+ ## Notes
66
+
67
+ - If `audit` finds no journals (`totals.stories == 0`), say so: cost data only exists after at least one Workflow run. Point the user at `--file` / `--session-dir` if their journals live elsewhere.
68
+ - Audit covers the Claude Code (Workflow) provider. The Codex provider produces no journal, so there is no cost data for Codex runs yet.
69
+ - Do not edit `pipeline-config.yaml` yourself — present the recommended changes and let the user (or `/valent-configure`) apply them.
@@ -29,7 +29,7 @@ Use the standard 200k context window. Workflow `agent()` calls run in their own
29
29
 
30
30
  ### Step 1: Load Pipeline Config
31
31
 
32
- Read and follow `.valent-pipeline/steps/orchestration/load-pipeline-config.md`. Set `{epic_id}` from the argument. Also capture the entire `models` section (the `{ opus:[...], sonnet:[...], haiku:[...] }` tier→roles map) — pass it as the `models` arg to every Workflow call below so per-agent model tiers stay config-driven (editable via `/valent-configure`). If the config has no `models` section, omit the arg and the workflows use their baked-in default assignment.
32
+ Read and follow `.valent-pipeline/steps/orchestration/load-pipeline-config.md`. Set `{epic_id}` from the argument. Also capture the entire `models` section (the `{ opus:[...], sonnet:[...], haiku:[...] }` tier→roles map) — pass it as the `models` arg to every Workflow call below so per-agent model tiers stay config-driven (editable via `/valent-configure`). If the config has no `models` section, omit the arg and the workflows use their baked-in default assignment. Likewise capture the `reasoning` section (level→roles thinking-effort map) and pass it as the `reasoning` arg to every Workflow call; it is blank by default (injects nothing). Omit it if absent or all levels are empty.
33
33
 
34
34
  ### Step 2: Validate Epic
35
35
 
@@ -62,7 +62,7 @@ Invoke `plan.workflow.js` via the **Workflow tool**:
62
62
  ```js
63
63
  Workflow({
64
64
  scriptPath: '.valent-pipeline/orchestrators/claude-code/plan.workflow.js',
65
- args: { stories: [{ storyId, projectType }, ...candidates], sprintId: '{epic_id}-sprint-{n}', velocity: {sprint.initial_velocity_points or current calibrated velocity}, models: <config.models or omit> }
65
+ args: { stories: [{ storyId, projectType }, ...candidates], sprintId: '{epic_id}-sprint-{n}', velocity: {sprint.initial_velocity_points or current calibrated velocity}, models: <config.models or omit>, reasoning: <config.reasoning or omit> }
66
66
  })
67
67
  ```
68
68
 
@@ -74,7 +74,7 @@ Feed the planned batch straight into `sprint.workflow.js`:
74
74
  ```js
75
75
  Workflow({
76
76
  scriptPath: '.valent-pipeline/orchestrators/claude-code/sprint.workflow.js',
77
- args: { stories: <plan output .stories>, maxRejectionCycles: {quality.max_rejection_cycles or 5}, models: <config.models or omit> }
77
+ args: { stories: <plan output .stories>, maxRejectionCycles: {quality.max_rejection_cycles or 5}, models: <config.models or omit>, reasoning: <config.reasoning or omit> }
78
78
  })
79
79
  ```
80
80
 
@@ -89,7 +89,7 @@ Invoke `retro.workflow.js`:
89
89
  ```js
90
90
  Workflow({
91
91
  scriptPath: '.valent-pipeline/orchestrators/claude-code/retro.workflow.js',
92
- args: { batchNumber: {n}, sprintId: '{epic_id}-sprint-{n}', models: <config.models or omit> }
92
+ args: { batchNumber: {n}, sprintId: '{epic_id}-sprint-{n}', models: <config.models or omit>, reasoning: <config.reasoning or omit> }
93
93
  })
94
94
  ```
95
95
 
@@ -24,7 +24,7 @@ Use the standard 200k context window. Workflow `agent()` calls run in their own
24
24
 
25
25
  ### Step 1: Load Pipeline Config
26
26
 
27
- Read and follow `.valent-pipeline/steps/orchestration/load-pipeline-config.md`. Also capture the entire `models` section (the `{ opus:[...], sonnet:[...], haiku:[...] }` tier→roles map) — pass it as the `models` arg to every Workflow call below so per-agent model tiers stay config-driven (editable via `/valent-configure`). If the config has no `models` section, omit the arg and the workflows use their baked-in default assignment.
27
+ Read and follow `.valent-pipeline/steps/orchestration/load-pipeline-config.md`. Also capture the entire `models` section (the `{ opus:[...], sonnet:[...], haiku:[...] }` tier→roles map) — pass it as the `models` arg to every Workflow call below so per-agent model tiers stay config-driven (editable via `/valent-configure`). If the config has no `models` section, omit the arg and the workflows use their baked-in default assignment. Likewise capture the `reasoning` section (level→roles thinking-effort map) and pass it as the `reasoning` arg to every Workflow call; it is blank by default (injects nothing). Omit it if absent or all levels are empty.
28
28
 
29
29
  ### Step 2: Build Cross-Epic Dependency Map
30
30
 
@@ -65,7 +65,7 @@ Invoke `plan.workflow.js` via the **Workflow tool**:
65
65
  ```js
66
66
  Workflow({
67
67
  scriptPath: '.valent-pipeline/orchestrators/claude-code/plan.workflow.js',
68
- args: { stories: [{ storyId, projectType }, ...candidates], sprintId: 'project-sprint-{n}', velocity: {sprint.initial_velocity_points or current calibrated velocity}, models: <config.models or omit> }
68
+ args: { stories: [{ storyId, projectType }, ...candidates], sprintId: 'project-sprint-{n}', velocity: {sprint.initial_velocity_points or current calibrated velocity}, models: <config.models or omit>, reasoning: <config.reasoning or omit> }
69
69
  })
70
70
  ```
71
71
 
@@ -77,7 +77,7 @@ Feed the planned batch straight into `sprint.workflow.js`:
77
77
  ```js
78
78
  Workflow({
79
79
  scriptPath: '.valent-pipeline/orchestrators/claude-code/sprint.workflow.js',
80
- args: { stories: <plan output .stories>, maxRejectionCycles: {quality.max_rejection_cycles or 5}, models: <config.models or omit> }
80
+ args: { stories: <plan output .stories>, maxRejectionCycles: {quality.max_rejection_cycles or 5}, models: <config.models or omit>, reasoning: <config.reasoning or omit> }
81
81
  })
82
82
  ```
83
83
 
@@ -92,7 +92,7 @@ Invoke `retro.workflow.js`:
92
92
  ```js
93
93
  Workflow({
94
94
  scriptPath: '.valent-pipeline/orchestrators/claude-code/retro.workflow.js',
95
- args: { batchNumber: {n}, sprintId: 'project-sprint-{n}', models: <config.models or omit> }
95
+ args: { batchNumber: {n}, sprintId: 'project-sprint-{n}', models: <config.models or omit>, reasoning: <config.reasoning or omit> }
96
96
  })
97
97
  ```
98
98
 
@@ -29,7 +29,7 @@ If no argument is provided, resolve the next work item from the backlog (see Ste
29
29
 
30
30
  ### Step 1: Load Pipeline Config
31
31
 
32
- Read and follow `.valent-pipeline/steps/orchestration/load-pipeline-config.md`. Capture `project.type` and the story's `testing_profiles` — these become the Workflow's `projectType` and `profiles` args. Also capture the entire `models` section (the `{ opus:[...], sonnet:[...], haiku:[...] }` tier→roles map) — pass it as the `models` arg so per-agent model tiers stay config-driven (editable via `/valent-configure`). If the config has no `models` section, omit the arg and the workflow uses its baked-in default assignment.
32
+ Read and follow `.valent-pipeline/steps/orchestration/load-pipeline-config.md`. Capture `project.type` and the story's `testing_profiles` — these become the Workflow's `projectType` and `profiles` args. Also capture the entire `models` section (the `{ opus:[...], sonnet:[...], haiku:[...] }` tier→roles map) — pass it as the `models` arg so per-agent model tiers stay config-driven (editable via `/valent-configure`). If the config has no `models` section, omit the arg and the workflow uses its baked-in default assignment. Likewise capture the `reasoning` section (the `{ ultrathink:[...], 'think-harder':[...], 'think-hard':[...], think:[...] }` level→roles map) and pass it as the `reasoning` arg; it is blank by default (injects nothing). Omit the arg if the section is absent or all levels are empty.
33
33
 
34
34
  ### Step 1b: Resolve Next Work Item (when no argument provided)
35
35
 
@@ -52,7 +52,7 @@ Invoke the Workflow at `.valent-pipeline/orchestrators/claude-code/sprint.workfl
52
52
  ```js
53
53
  Workflow({
54
54
  scriptPath: '.valent-pipeline/orchestrators/claude-code/sprint.workflow.js',
55
- args: { storyId: '<resolved story id>', projectType: '<project.type>', profiles: [/* testing_profiles */], maxRejectionCycles: <quality.max_rejection_cycles or 5>, models: <config.models or omit> }
55
+ args: { storyId: '<resolved story id>', projectType: '<project.type>', profiles: [/* testing_profiles */], maxRejectionCycles: <quality.max_rejection_cycles or 5>, models: <config.models or omit>, reasoning: <config.reasoning or omit> }
56
56
  })
57
57
  ```
58
58
 
@@ -71,7 +71,8 @@ Every Workflow invocation returns a `runId`. If a run is interrupted — context
71
71
  ## Notes
72
72
 
73
73
  - **State model.** The **journal is the state of record.** `pipeline-state.json`, `sprint-{n}-status.yaml`, and the markdown handoffs are **derived, human-readable views** that agents write for visibility — the orchestrator never reads them back to make a control-flow decision (its state lives in JS variables the journal captures). The non-atomic multi-file desync the prose Lead can hit is structurally impossible here.
74
- - **Planned sprint batches.** To run a planned batch instead of one story, pass `args: { stories: [{ storyId, projectType, profiles }, ...], maxRejectionCycles, models }`. Produce that batch by running `plan.workflow.js` first (`args: { stories: [{ storyId, projectType }], sprintId, velocity, models }`), then feed its `{ sprintId, stories: [...] }` straight into `sprint.workflow.js`. After a batch ships, run `retro.workflow.js` (`args: { batchNumber, sprintId, models }`) to learn from it. Pass the same `config.models` map to all three. There is no `sprint-cycle` wrapper yet — run the three in sequence.
74
+ - **Planned sprint batches.** To run a planned batch instead of one story, pass `args: { stories: [{ storyId, projectType, profiles }, ...], maxRejectionCycles, models, reasoning }`. Produce that batch by running `plan.workflow.js` first (`args: { stories: [{ storyId, projectType }], sprintId, velocity, models, reasoning }`), then feed its `{ sprintId, stories: [...] }` straight into `sprint.workflow.js`. After a batch ships, run `retro.workflow.js` (`args: { batchNumber, sprintId, models, reasoning }`) to learn from it. Pass the same `config.models` and `config.reasoning` maps to all three. There is no `sprint-cycle` wrapper yet — run the three in sequence.
75
+ - **Per-agent reasoning effort.** The `reasoning` arg is a config-driven control surface (level→roles) that injects a thinking trigger into a role's prompt — blank by default, so it changes nothing until you fill it. Levels: `think` < `think-hard` < `think-harder` < `ultrathink`. Deeper thinking raises quality and token cost; check `valent audit` after enabling it. Edit it in `.valent-pipeline/pipeline-config.yaml` under `reasoning:`.
75
76
  - **Per-agent models.** Each workflow assigns a model tier per agent: gates (READINESS/CRITIC/JUDGE) → opus, spec/build → sonnet, CLI-runner/IO steps → haiku. This comes from `config.models` (passed as the `models` arg); edit it with `/valent-configure` → "Model Assignments". Omitting the arg falls back to the same assignment baked into the script.
76
77
  - **Known simplifications.** A CRITIC rejection currently re-runs ALL dev agents (not just the targeted one); there is no PMCP/visual-validation stage in the Workflow path yet. See `.valent-pipeline/orchestrators/claude-code/README.md`.
77
78
  - Do **not** adopt the Lead persona or read `lead.md` in this skill — that is the prose-Lead path. The orchestration here is the Workflow script.