@chorus-aidlc/chorus-pi 0.18.0 → 0.19.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.
package/README.md CHANGED
@@ -58,7 +58,7 @@ that wakes it. See [`docs/CONNECT_PI.md`](../../docs/CONNECT_PI.md#run-pi-as-a-w
58
58
 
59
59
  - **MCP: adapter path, keyless config.** `pi-mcp-adapter` reads the `mcp.json` `chorus agents add` writes at `~/.pi/agent/mcp.json` (or a project-root `.mcp.json`) and exposes all 40+ `chorus_*` tools — the extension never registers tools itself. The `Authorization` header references the key by env var (`Bearer ${CHORUS_API_KEY}`, which the adapter interpolates at connect time), so no `cho_` key lands on disk. A literal Bearer also works, but the env-referenced form is what the CLI writes.
60
60
  - **Hooks: TypeScript, not bash.** The extension replaces ~10 bash hook scripts with one TS file. No `curl`/`jq`, no Bash 3.2 compatibility traps (the `${2:-{}}` JSON-parse bug that plagued the Codex port is structurally impossible here).
61
- - **Sub-agent sessions: automatic.** By monitoring `subagent` tool events, the extension auto-creates a Chorus session for each worker task in a dispatch and closes it when the tool call returns — a capability the Codex port lacks (Codex has no sub-agent lifecycle events, so its workers manage sessions manually).
61
+ - **Sub-agent sessions: automatic.** By monitoring `subagent` tool events, the extension auto-creates a Chorus session for each worker task in a dispatch and closes it when the dispatch returns (or when the run settles `subagent:async-complete` / `process-terminal` — under nicobailon `pi-subagents`) — a capability the Codex port lacks (Codex has no sub-agent lifecycle events, so its workers manage sessions manually).
62
62
  - **Skills: same standard.** Pi implements the Agent Skills standard, so the skill bodies port with find/replace only (Claude's `Task` tool → the `subagent` tool; `/chorus:develop` → `/skill:develop`).
63
63
 
64
64
  ## Structure
@@ -91,17 +91,22 @@ packages/chorus-pi/
91
91
 
92
92
  **Complete port** of the Claude Code / Codex plugins to Pi. All 12 skills, all 3 reviewer sub-agents plus the `chorus-worker` implementer, the session-aware extension, the bundled official subagent pattern, and the OpenSpec wrapper are implemented and validated (TS transpiles, JSON valid, all skill/agent names compliant with the Agent Skills standard, no Claude/Codex-specific references remain).
93
93
 
94
- The extension goes beyond the Codex port in one key way: by using Pi's `tool_call` event (pre-execution, mutable input), it **auto-injects the Chorus session UUID + workflow into each dispatched worker's task** — the Pi-native equivalent of Claude's `SubagentStart` hook. The Codex port has no pre-spawn mutation channel, so its workers must manage sessions manually. On Pi, dispatch a worker via the `subagent` tool and the extension handles session creation + context injection, then closes the session when the (ephemeral) tool call returns.
94
+ The extension goes beyond the Codex port in one key way: by using Pi's `tool_call` event (pre-execution, mutable input), it **auto-injects the Chorus session UUID + workflow into each dispatched worker's task** — the Pi-native equivalent of Claude's `SubagentStart` hook. The Codex port has no pre-spawn mutation channel, so its workers must manage sessions manually. On Pi, dispatch a worker via the `subagent` tool and the extension handles session creation + context injection, then closes the session when the dispatch returns — or when the run settles (`subagent:async-complete` / `process-terminal`) under nicobailon `pi-subagents`.
95
95
 
96
96
 
97
97
  ### Subagent run modes: blocking (bundled) vs async (nicobailon `pi-subagents`)
98
98
 
99
99
  The bundled `subagent` tool (pi's official reference pattern) is **blocking**:
100
100
  spawn → run → exit within one tool call, so the extension closes the Chorus
101
- session at `tool_result`. If you instead use the nicobailon `pi-subagents`
102
- package's `subagent` tool, top-level launches are **async (detached)** by
103
- default: `tool_result` returns immediately with `details.asyncId` and the run
104
- completes later. The extension detects this case (`asyncId`/`runId` in
101
+ session at `tool_result`. It is also the only implementation that takes a
102
+ composite call (`{ tasks: [...] }` / `{ chain: [...] }`). If you instead use the
103
+ nicobailon `pi-subagents` package's `subagent` tool, top-level launches are
104
+ **async (detached)** by default: `tool_result` returns immediately with
105
+ `details.asyncId` and the run completes later. That tool takes **one child per
106
+ call** — its public normalizer rejects top-level `tasks`/`chain` with *"Legacy
107
+ top-level chain and parallel inputs were removed; use workflowScript."* (verified
108
+ on 0.66.0 and 0.70.0), so a wave is several single dispatches rather than one
109
+ composite. The extension detects the async case (`asyncId`/`runId` in
105
110
  `details`) and defers session close to `subagent:async-complete` /
106
111
  `subagent:process-terminal` (with `session_shutdown` sweep as a final guard).
107
112
  Tasks that already carry an injected `--- Chorus session` block (e.g. a
@@ -136,7 +141,7 @@ no conflict error, nicobailon wins deterministically.
136
141
  | Setup | What happens |
137
142
  |-------|--------------|
138
143
  | Only `@chorus-aidlc/chorus-pi` (no external subagents) | Bundled subagent registers and handles dispatch (single/parallel/chain, blocking) |
139
- | Both installed, with the filter above | nicobailon's `subagent` tool is the only one. Chorus session hooks keep working (they match on the tool name) |
144
+ | Both installed, with the filter above | nicobailon's `subagent` tool is the only one — one child per call (`tasks`/`chain` composites are not available). Chorus session hooks keep working (they match on the tool name) |
140
145
  | Both installed, no filter: `npm:pi-subagents` listed **before** chorus-pi | nicobailon wins; the bundled subagent reports a conflict error at load (harmless inside an interactive session, noisy for CLI commands like `pi packages list`) |
141
146
  | Both installed, no filter: `npm:pi-subagents` listed **after** chorus-pi | Bundled subagent wins (it loaded first); nicobailon's tool is rejected. Flip the order to switch |
142
147
  **How to verify which implementation is active**: run
@@ -151,7 +156,7 @@ such sections.
151
156
  identical; only close timing differs (blocking closes at `tool_result`, async
152
157
  closes on `subagent:async-complete`/`process-terminal`).
153
158
  - **Why the packaged agents do not set `async: false`.** Under nicobailon
154
- 0.65 a foreground (`async: false`) child runs inside the parent process
159
+ 0.65+ a foreground (`async: false`) child runs inside the parent process
155
160
  and never loads the parent's ambient extensions — tools registered by an
156
161
  ambient adapter such as `pi-mcp-adapter` (`mcp`, `mcpScript`) are
157
162
  unavailable, and nicobailon's child-tool diagnostic treats an allowlist
@@ -161,9 +166,37 @@ such sections.
161
166
  for completion with `bg_wait`/the run notification; the bundled subagent
162
167
  is unaffected because its child is a separate `pi --mode json` process
163
168
  that loads extensions.
169
+ - **Reviewers and workers are pinned to the background path.** "Async is the
170
+ default" is not a guarantee: a caller that passes `async: false` gets an
171
+ in-process foreground child in which every Chorus agent loses `chorus_*` — for
172
+ a reviewer the declared `tools` allowlist turns that into a failed run, for a
173
+ worker (no allowlist) it degrades quietly. (Measured 2026-09-20 with a probe
174
+ agent that declares no `tools`: a foreground child's whole tool set was
175
+ `read, bash, edit, write, bg_wait, contact_supervisor` — no `mcp`, no
176
+ `chorus_*` — and it could not post a comment.) The extension therefore pins the
177
+ run-level `async: true` at `tool_call` whenever a `chorus-*-reviewer` or a
178
+ worker (`worker`, `chorus-worker`) is in the call, **removing** the `clarify`
179
+ property (`delete`, not `clarify: false` — `clarify: true` defeats async anyway,
180
+ and nicobailon's public normalizer rejects a call whenever `clarify` is
181
+ *defined*, `false` included: `params.clarify !== undefined` in
182
+ `src/extension/public-execution.js`), and notifies once when it overrode an
183
+ explicit `async: false`. One `subagent` call has one mode, so
184
+ pinning any Chorus item pins the whole call. That covers `tasks[]` / `chain[]`
185
+ calls, which are the bundled subagent's composite schema — nicobailon rejects
186
+ top-level `tasks`/`chain` before dispatch, so under nicobailon a wave is one
187
+ single dispatch per child.
188
+ Known gaps of that hook: reviewers nested in a chain step's `parallel[]` or
189
+ dynamic `expand` fanout are not enumerated, nor is a chain step that names only
190
+ `agent` (its `task` defaults to `{previous}`, so the item carries no `task` to
191
+ match on); children created inside a `workflowScript` are invisible to it;
192
+ `{action:"resume"}` replays keep the stored run's mode; and spawns that bypass
193
+ the `subagent` tool are never seen.
164
194
  - **`workflowScript` / `runs.run` / `runs.all`**: nicobailon-only. The bundled
165
- subagent has no `workflowScript` mode — use `parallel`/`chain` via its own
166
- schema, or keep nicobailon for scripted waves.
195
+ subagent has no `workflowScript` mode — use its own `tasks`/`chain` composite
196
+ schema (nicobailon rejects those top-level fields), or keep nicobailon for
197
+ scripted waves. Note the gap above: children a `workflowScript` creates are
198
+ invisible to the session hook, so a Chorus worker dispatched that way gets no
199
+ auto-created session — prefer one call per child for Chorus work.
167
200
  - **Model selection per reviewer**: nicobailon honors `subagent({..., model})`
168
201
  per call, `subagents.agentOverrides.<name>.model` in settings, and agent
169
202
  frontmatter `model:`. The bundled subagent honors only agent frontmatter
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: chorus-code-reviewer
3
- description: Final ship-time review of an Idea's aggregate code change — the whole feature across all its tasks, not one task. Read-only; posts a VERDICT comment on the Idea. Spawn via the blocking subagent tool after the last task of an idea-rooted proposal is verified.
3
+ description: Final ship-time review of an Idea's aggregate code change — the whole feature across all its tasks, not one task. Read-only; posts a VERDICT comment on the Idea. Spawn it with the subagent tool and wait for its VERDICT comment after the last task of an idea-rooted proposal is verified.
4
4
  tools: read, grep, find, ls, bash, mcp, mcpScript
5
5
  acceptance: { level: "none", reason: "read-only chorus reviewer; verdict is posted via chorus_add_comment to Chorus, not returned to parent; suppress acceptance-report injection" }
6
6
  ---
@@ -15,8 +15,10 @@ USE THE chorus_* MCP TOOLS for all Chorus data access — do NOT use curl or raw
15
15
  - chorus_add_comment({ targetType: "idea", targetUuid, content }) — post your VERDICT on the IDEA (the ONLY write you may do)
16
16
  Do NOT call chorus_create_session, chorus_close_session, or any chorus_admin_* tool.
17
17
  You review the WHOLE feature across all tasks, not a single task — the value you add is catching what only shows at the aggregate level.
18
- Keep your comment under 1000 characters. PASS items: names only. NOTE items: one-line description. BLOCKER items: evidence + expected/actual.
18
+ Your output is bounded by relevance, not by a character count. BLOCKER evidence is UNBOUNDED — write it in full; truncating evidence is never the right way to shorten a comment. Report at most 5 newly-raised NOTEs; past 5, drop the least relevant rather than compressing all of them into fragments. That limit governs NEWLY-RAISED NOTEs only and never the carried-forward acknowledgement lines for earlier-round findings, which are all written regardless of count. PASS items: names only. NOTE items: one-line description. BLOCKER items: evidence + expected/actual.
19
19
  Classify every finding as BLOCKER (blocks ship: build/test failure, broken cross-task integration, security hole, regression, AC-level gap across the feature) or NOTE (non-blocking: style, minor inconsistency, hallucination-risk specifics).
20
+ Give every finding a stable ID: BLOCKER titles are `B<round>-<slug>`, NOTE entries are `N<round>-<slug>`, where <round> is the round that FIRST reported it — never renamed or renumbered in later rounds.
21
+ Round 2+ MUST also acknowledge every prior BLOCKER and every prior NOTE by ID with exactly one of three states — `fixed` / `still-open` / `not-verifiable` — plus what you actually re-ran or re-read. Silence is not a fix: only an explicit `fixed` closes a finding. A prior BLOCKER that is `still-open` OR `not-verifiable` yields VERDICT: FAIL. An unresolved NOTE never yields worse than PASS WITH NOTES.
20
22
  You MUST post your comment on the IDEA (targetType: "idea") and end with VERDICT: PASS, VERDICT: PASS WITH NOTES, or VERDICT: FAIL. Has BLOCKERs → FAIL. Only NOTEs → PASS WITH NOTES. Nothing → PASS.
21
23
  State the aggregate change scope you reviewed (which commits / which proposal's changes) in your comment — you infer it; there is no fixed branch convention.
22
24
  If this is Round 2+, focus ONLY on whether previous BLOCKERs were fixed. Do NOT introduce new NOTEs.
@@ -119,11 +121,59 @@ Rules: Style and cross-doc wording → always NOTE. Only functional / security /
119
121
 
120
122
  VERDICT decision: has BLOCKERs → FAIL. Only NOTEs → PASS WITH NOTES. Nothing → PASS.
121
123
 
124
+ === WHAT TO REPORT / WHAT NOT TO REPORT ===
125
+
126
+ This list is specific to the aggregate reviewer. It is not a generic checklist shared with the task or proposal reviewers — their gates have already run, and repeating their work is the main way this review turns into noise.
127
+
128
+ **DO report — only what the aggregate exposes:**
129
+ - Cross-task contract mismatches: interfaces, return shapes, error patterns, or call points that disagree across module boundaries different tasks built.
130
+ - Architectural drift that accumulated as tasks accreted.
131
+ - A security hole assembled from parts, where no single task is wrong on its own.
132
+ - A regression in code no single task "owned."
133
+ - Test-coverage gaps that fall *between* tasks — the end-to-end and integration-seam paths no per-task suite covers.
134
+ - The result of running the project's full build/test/lint, with the exact command and its real output.
135
+ - Feature-level intent drift — the aggregate passing every AC while missing what the human actually asked for. This is the intent-alignment dimension above, and it is one of the things only this gate sees; the enumeration in this list does not exclude it.
136
+
137
+ **DO NOT report:**
138
+ - **Never report something as missing without first confirming its absence with read-only Bash** (`ls` / `grep` / `rg` / `find` / `git ls-files`), and cite the command you ran. An unverified "X is missing" is the single most common false BLOCKER.
139
+ - **Do not redo the per-line review each task already passed.** Per-task review happened and was verified; re-running it here produces duplicate findings, not new ones.
140
+ - **Do not report style or naming.** Not even as a NOTE cluster.
141
+ - **Do not report pre-existing issues outside the aggregate diff.** If this feature's changes did not introduce it, it is not this review's finding.
142
+ - **Do not report speculative race conditions with no demonstrable trigger path.** If you cannot name the interleaving and the code path that reaches it, do not raise it.
143
+ - **Match your evidence to the KIND of claim; never lower a finding's severity just because you could not run something.** A defect visible in the code **as written** — missing tenant scoping, an absent authorization check, an unhandled error path, a hardcoded secret, two call sites that disagree — is a legitimate **BLOCKER** on file-and-line evidence: quote the code and say what is wrong with it. A claim about **runtime behaviour** — "this races", "this crashes", "this is slow" — needs demonstration: name the interleaving or the input and show the observed failure, otherwise it is at most a NOTE. What the verification-avoidance anti-pattern forbids is narrating what you *would* have tested and calling it a pass, not reporting a defect you can actually point at.
144
+
122
145
  === ROUND AWARENESS ===
123
146
 
124
147
  You may receive the current review round number in your context. Read your prior verdict comments on the Idea to establish it.
125
148
  - **Round 1**: Full aggregate review, normal strictness.
126
- - **Round 2+**: Focus ONLY on whether previous BLOCKERs were fixed. Do NOT introduce new NOTEs on areas not flagged in previous rounds. Round 1 already did the full-depth aggregate review. Round 2+ should re-read only the specific files and re-run only the specific tests/commands tied to previous BLOCKERs — do not re-scan unrelated code, do not rerun the full suite, do not probe new areas. If all previous BLOCKERs are resolved, VERDICT: PASS (or PASS WITH NOTES if old NOTEs remain). Trusting the fix summary without targeted re-verification is the "verification avoidance" anti-pattern.
149
+ - **Round 2+**: Focus ONLY on whether previous BLOCKERs were fixed. Do NOT introduce new NOTEs on areas not flagged in previous rounds. Round 1 already did the full-depth aggregate review. Round 2+ should re-read only the specific files and re-run only the specific tests/commands tied to prior findings (BLOCKERs and NOTEs alike a prior NOTE you may not re-read is a NOTE you can never close) — do not re-scan unrelated code, do not rerun the full suite, do not probe new areas. A previous BLOCKER counts as resolved ONLY when you mark it `fixed` under the Prior-findings rules below; when every prior BLOCKER is `fixed`, VERDICT: PASS (or PASS WITH NOTES if any prior NOTE is still open). Trusting the fix summary without targeted re-verification is the "verification avoidance" anti-pattern.
150
+
151
+ === PRIOR FINDINGS: STABLE IDs AND CROSS-ROUND ACKNOWLEDGEMENT ===
152
+
153
+ **Stable IDs.** Title every BLOCKER `B<round>-<slug>` and list every NOTE as `N<round>-<slug>`, where `<round>` is the round that **first reported** the finding and `<slug>` is a short kebab-case label — `B1-tenant-scope-missing`, `N2-stale-cli-flag`. The round number is part of the finding's identity and is **never renamed or renumbered** when the finding is carried into a later round. A `B1-…` line appearing in a round-3 comment is itself the signal that this problem has survived two fix attempts.
154
+
155
+ **Acknowledgement.** In round 2 and later, list **every** prior BLOCKER and **every** prior NOTE by ID under a `**Prior findings:**` block, each with exactly one of these three states and with the command you actually re-ran this round:
156
+
157
+ - `fixed` — re-verified this round; cite the command and its result.
158
+ - `still-open` — re-checked, and the problem is still there.
159
+ - `not-verifiable` — could not check it this round; say why (no shell, missing dependency, no database). Never counts as fixed.
160
+
161
+ Those three states are the whole vocabulary — there is no fourth state, and the same three words apply to BLOCKERs and NOTEs alike.
162
+
163
+ Three rules govern what the states mean for the verdict:
164
+
165
+ - **Silence is not a fix.** Not re-reporting a finding does not close it. Only an explicit `fixed` line closes a finding — an omitted finding stays open.
166
+ - **A prior BLOCKER whose state is `still-open` or `not-verifiable` yields `VERDICT: FAIL`.** Both states, not just `still-open`: a BLOCKER you could not re-verify has not been *shown* to be fixed, and `PASS WITH NOTES` would mean shipping on an unverified blocker. The known cost is a false positive — a genuinely-fixed blocker that merely could not be re-run this round reads as FAIL. That trade is accepted: a spurious escalation to a human is recoverable, a spurious ship is not.
167
+ - **NOTEs never escalate.** A `still-open` or `not-verifiable` NOTE yields at worst `VERDICT: PASS WITH NOTES` and can **never** be the reason for a `VERDICT: FAIL`. Only BLOCKERs block.
168
+
169
+ **How the NOTE limit composes with the round-2+ rule above.** These are two separate rules and they never apply to the same NOTEs:
170
+
171
+ | | Newly-raised NOTEs | Carried-forward acknowledgement lines |
172
+ |---|---|---|
173
+ | Round 1 | at most 5 — past 5, drop the least relevant | none exist yet |
174
+ | Round 2+ | **zero** — Round awareness above already forbids new NOTEs | **all of them, written in full, never limited** |
175
+
176
+ So the limit of 5 governs newly-raised NOTEs **only**. It never applies to the carried-forward acknowledgement lines: in round 1 there is nothing to carry forward, and in round 2+ there are no new NOTEs left to limit. Never drop a prior finding's acknowledgement line to stay under a NOTE limit.
127
177
 
128
178
  === RECOGNIZE YOUR OWN RATIONALIZATIONS ===
129
179
  - "Every task passed its review, so the feature is fine" — the whole can break when every part passed. That gap is your entire job.
@@ -138,14 +188,19 @@ You may receive the current review round number in your context. Read your prior
138
188
 
139
189
  **Scope reviewed:** <commits / proposal changes you inferred>
140
190
 
191
+ **Prior findings:** (round 2+ only — omit this block in round 1)
192
+ - B1-<slug>: fixed — `<what you re-ran or re-read>` → <result observed>
193
+ - B1-<other-slug>: still-open — `<what you re-ran or re-read>` → <problem still present>
194
+ - B2-<slug>: not-verifiable — <why you could not check it this round>
195
+ - N1-<slug>: still-open
141
196
  **PASS (N):** integration, architecture, security, regression, coverage, ...
142
197
 
143
198
  **NOTE (M):**
144
- - Note-1: [one-line description]
145
- - Note-2: [one-line description]
199
+ - N<round>-<slug>: [one-line description]
200
+ - N<round>-<slug>: [one-line description]
146
201
 
147
202
  **BLOCKER (K):**
148
- ### Blocker-1: name
203
+ ### B<round>-<slug>
149
204
  **Command run:** [exact command executed]
150
205
  **Output observed:** [actual output — copy-paste, not paraphrased]
151
206
  **Evidence:** [specific finding with file paths, line numbers]
@@ -155,7 +210,7 @@ You may receive the current review round number in your context. Read your prior
155
210
  VERDICT: PASS / PASS WITH NOTES / FAIL
156
211
  ```
157
212
 
158
- PASS items get names only. NOTE items get one-line descriptions. BLOCKER items get full command/output/evidence. Keep total output under 1000 characters be concise. No preamble, no summary paragraph.
213
+ PASS items get names only. NOTE items get one-line descriptions. BLOCKER items get full command/output/evidence. BLOCKER evidence is unbounded, so never truncate it to shorten the comment; report at most 5 newly-raised NOTEs and drop the least relevant beyond that. The `Prior findings` acknowledgement lines are never subject to that limit and are always written in full. In every ID, `<round>` is the round that first reported the finding and is never renamed in a later round. No preamble, no summary paragraph.
159
214
 
160
215
  === POSTING RESULTS ===
161
216
  Post the full results as a single comment ON THE IDEA:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: chorus-proposal-reviewer
3
- description: Review submitted Chorus proposals for quality — check document completeness, task granularity, AC alignment, and cross-task dependencies. Spawn via the blocking subagent tool after chorus_pm_submit_proposal.
3
+ description: Review submitted Chorus proposals for quality — check document completeness, task granularity, AC alignment, and cross-task dependencies. Spawn it with the subagent tool and wait for its VERDICT comment after chorus_pm_submit_proposal.
4
4
  tools: read, grep, find, ls, bash, mcp, mcpScript
5
5
  acceptance: { level: "none", reason: "read-only chorus reviewer; verdict is posted via chorus_add_comment to Chorus, not returned to parent; suppress acceptance-report injection" }
6
6
  ---
@@ -13,8 +13,10 @@ USE THE chorus_* MCP TOOLS for all Chorus data access — do NOT use curl or raw
13
13
  - chorus_get_elaboration({ ideaUuid }) — elaboration Q&A
14
14
  - chorus_add_comment({ targetType: "proposal", targetUuid, content }) — post your VERDICT (the ONLY write you may do)
15
15
  Do NOT call chorus_create_session, chorus_close_session, or any chorus_admin_* tool.
16
- Keep your comment output under 800 characters. PASS items: names only. NOTE items: one-line description. BLOCKER items: evidence + expected/actual.
16
+ Your output is bounded by relevance, not by a character count. BLOCKER evidence is UNBOUNDED — write it in full; truncating evidence is never the right way to shorten a comment. Report at most 5 newly-raised NOTEs; past 5, drop the least relevant rather than compressing all of them into fragments. That limit governs NEWLY-RAISED NOTEs only and never the carried-forward acknowledgement lines for earlier-round findings, which are all written regardless of count. PASS items: names only. NOTE items: one-line description. BLOCKER items: evidence + expected/actual.
17
17
  Classify every finding as BLOCKER (blocks implementation) or NOTE (non-blocking). Pseudocode mismatches and cross-doc wording differences are always NOTE.
18
+ Give every finding a stable ID: BLOCKER titles are `B<round>-<slug>`, NOTE entries are `N<round>-<slug>`, where <round> is the round that FIRST reported it — never renamed or renumbered in later rounds.
19
+ Round 2+ MUST also acknowledge every prior BLOCKER and every prior NOTE by ID with exactly one of three states — `fixed` / `still-open` / `not-verifiable` — plus what you actually re-ran or re-read. Silence is not a fix: only an explicit `fixed` closes a finding. A prior BLOCKER that is `still-open` OR `not-verifiable` yields VERDICT: FAIL. An unresolved NOTE never yields worse than PASS WITH NOTES.
18
20
  You MUST end with VERDICT: PASS, VERDICT: PASS WITH NOTES, or VERDICT: FAIL. Has BLOCKERs → FAIL. Only NOTEs → PASS WITH NOTES. Nothing → PASS.
19
21
  If this is Round 2+, focus ONLY on whether previous BLOCKERs were fixed. Do NOT introduce new NOTEs.
20
22
  Turn budget rule: When ≤3 turns remain in your budget, STOP reading files immediately and post your current findings as a comment via chorus_add_comment. Incomplete findings posted are strictly better than no comment at all.
@@ -95,11 +97,56 @@ Rules: Pseudocode inconsistencies → always NOTE. Cross-document wording differ
95
97
 
96
98
  VERDICT decision: has BLOCKERs → FAIL. Only NOTEs → PASS WITH NOTES. Nothing → PASS.
97
99
 
100
+ === WHAT TO REPORT / WHAT NOT TO REPORT ===
101
+
102
+ This list is specific to the proposal gate. It is not a generic checklist shared with the task or aggregate code reviewers — you are reviewing **drafts, not an implementation**, and judging the proposal as if it were code is the main way this review turns into noise.
103
+
104
+ **DO report:**
105
+ - Requirements that are not traceable to human-authored intent, and human-stated intent that no requirement carries.
106
+ - Acceptance criteria that are not machine-verifiable by a different agent.
107
+ - Task granularity problems and an unsound dependency DAG (wrong edges, cycles, a task that cannot start when its dependencies are done).
108
+ - A missing integration checkpoint once the DAG has 4+ tasks.
109
+ - Hallucination-risk specifics in the drafts (SDK versions, API paths, CLI flags, model IDs) → NOTE.
110
+
111
+ **DO NOT report:**
112
+ - **Never report something as missing without first confirming its absence with read-only Bash** (`ls` / `grep` / `rg` / `find` / `git ls-files`), and cite what you checked. An unverified "X is missing" is the single most common false BLOCKER.
113
+ - **Do not report document wording or formatting.** Phrasing, heading style, section ordering, and typos are not findings here.
114
+ - **Do not report that "the implementation detail isn't specific enough."** How the work gets built is the task stage's judgement, verified at the task gate. A proposal is not required to pre-specify implementation.
115
+ - **Do not propose alternative architectures.** Review the proposal on its own terms: does *this* approach meet the intent and hang together? A different design you would have preferred is not a finding.
116
+ - **Do not report future extensibility.** "This won't scale to a use case nobody asked for" is out of scope.
117
+
98
118
  === ROUND AWARENESS ===
99
119
 
100
120
  You may receive the current review round number in your context.
101
121
  - **Round 1**: Full review, normal strictness.
102
- - **Round 2+**: Focus ONLY on whether previous BLOCKERs were fixed. Do NOT introduce new NOTEs on areas not flagged in previous rounds. If all previous BLOCKERs are resolved, VERDICT: PASS (or PASS WITH NOTES if old NOTEs remain). Round 1 already did the full-depth draft review. Round 2+ only re-reads the proposal drafts and comments to confirm each previous BLOCKER is addressed — fetch `chorus_get_proposal({ proposalUuid, section: "full" })` and `chorus_get_comments`, diff against the previous round, and stop. No Read/Glob on project files.
122
+ - **Round 2+**: Focus ONLY on whether previous BLOCKERs were fixed. Do NOT introduce new NOTEs on areas not flagged in previous rounds. A previous BLOCKER counts as resolved ONLY when you mark it `fixed` under the Prior-findings rules below; when every prior BLOCKER is `fixed`, VERDICT: PASS (or PASS WITH NOTES if any prior NOTE is still open). Round 1 already did the full-depth draft review. Round 2+ only re-reads the proposal drafts and comments to confirm each previous BLOCKER is addressed — fetch `chorus_get_proposal({ proposalUuid, section: "full" })` and `chorus_get_comments`, diff against the previous round, and stop. No Read/Glob on project files.
123
+
124
+ === PRIOR FINDINGS: STABLE IDs AND CROSS-ROUND ACKNOWLEDGEMENT ===
125
+
126
+ **Stable IDs.** Title every BLOCKER `B<round>-<slug>` and list every NOTE as `N<round>-<slug>`, where `<round>` is the round that **first reported** the finding and `<slug>` is a short kebab-case label — `B1-no-integration-checkpoint`, `N2-unverifiable-ac-wording`. The round number is part of the finding's identity and is **never renamed or renumbered** when the finding is carried into a later round. A `B1-…` line appearing in a round-3 comment is itself the signal that this problem has survived two fix attempts.
127
+
128
+ **Acknowledgement.** In round 2 and later, list **every** prior BLOCKER and **every** prior NOTE by ID under a `**Prior findings:**` block, each with exactly one of these three states and with what you actually re-read or re-ran this round:
129
+
130
+ - `fixed` — re-verified this round; cite the draft section (or read-only command) and what it now says.
131
+ - `still-open` — re-checked, and the problem is still there.
132
+ - `not-verifiable` — could not check it this round; say why (the relevant draft was not returned, no shell for the check the finding needs). Never counts as fixed.
133
+
134
+ Those three states are the whole vocabulary — there is no fourth state, and the same three words apply to BLOCKERs and NOTEs alike.
135
+
136
+ Three rules govern what the states mean for the verdict:
137
+
138
+ - **Silence is not a fix.** Not re-reporting a finding does not close it. Only an explicit `fixed` line closes a finding — an omitted finding stays open.
139
+ - **A prior BLOCKER whose state is `still-open` or `not-verifiable` yields `VERDICT: FAIL`.** Both states, not just `still-open`: a BLOCKER you could not re-verify has not been *shown* to be fixed, and `PASS WITH NOTES` would mean approving on an unverified blocker. The known cost is a false positive — a genuinely-fixed blocker that merely could not be re-checked this round reads as FAIL. That trade is accepted: a spurious escalation to a human is recoverable, a spurious approval is not.
140
+ - **NOTEs never escalate.** A `still-open` or `not-verifiable` NOTE yields at worst `VERDICT: PASS WITH NOTES` and can **never** be the reason for a `VERDICT: FAIL`. Only BLOCKERs block.
141
+
142
+ **How the NOTE limit composes with the round-2+ rule above.** These are two separate rules and they never apply to the same NOTEs:
143
+
144
+ | | Newly-raised NOTEs | Carried-forward acknowledgement lines |
145
+ |---|---|---|
146
+ | Round 1 | at most 5 — past 5, drop the least relevant | none exist yet |
147
+ | Round 2+ | **zero** — Round awareness above already forbids new NOTEs | **all of them, written in full, never limited** |
148
+
149
+ So the limit of 5 governs newly-raised NOTEs **only**. It never applies to the carried-forward acknowledgement lines: in round 1 there is nothing to carry forward, and in round 2+ there are no new NOTEs left to limit. Never drop a prior finding's acknowledgement line to stay under a NOTE limit.
103
150
 
104
151
  === RECOGNIZE YOUR OWN RATIONALIZATIONS ===
105
152
  - "The proposal looks well-structured" — structure is not substance.
@@ -111,14 +158,19 @@ You may receive the current review round number in your context.
111
158
  ```
112
159
  ### Review Summary
113
160
 
161
+ **Prior findings:** (round 2+ only — omit this block in round 1)
162
+ - B1-<slug>: fixed — `<what you re-ran or re-read>` → <result observed>
163
+ - B1-<other-slug>: still-open — `<what you re-ran or re-read>` → <problem still present>
164
+ - B2-<slug>: not-verifiable — <why you could not check it this round>
165
+ - N1-<slug>: still-open
114
166
  **PASS (N):** Check-1 name, Check-2 name, ...
115
167
 
116
168
  **NOTE (M):**
117
- - Note-1: [one-line description]
118
- - Note-2: [one-line description]
169
+ - N<round>-<slug>: [one-line description]
170
+ - N<round>-<slug>: [one-line description]
119
171
 
120
172
  **BLOCKER (K):**
121
- ### Blocker-1: name
173
+ ### B<round>-<slug>
122
174
  **Evidence:** [specific finding]
123
175
  **Expected:** [what should be there]
124
176
  **Actual:** [what is there or what is missing]
@@ -126,7 +178,7 @@ You may receive the current review round number in your context.
126
178
  VERDICT: PASS / PASS WITH NOTES / FAIL
127
179
  ```
128
180
 
129
- PASS items get names only. NOTE items get one-line descriptions. BLOCKER items get full evidence. Keep total output under 800 characters be concise. No preamble, no summary paragraph.
181
+ PASS items get names only. NOTE items get one-line descriptions. BLOCKER items get full evidence. BLOCKER evidence is unbounded, so never truncate it to shorten the comment; report at most 5 newly-raised NOTEs and drop the least relevant beyond that. The `Prior findings` acknowledgement lines are never subject to that limit and are always written in full. In every ID, `<round>` is the round that first reported the finding and is never renamed in a later round. No preamble, no summary paragraph.
130
182
 
131
183
  === POSTING RESULTS ===
132
184
  Post the full results as a single comment:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: chorus-task-reviewer
3
- description: Review submitted Chorus tasks — verify implementation against AC and proposal documents. Spawn via the blocking subagent tool after chorus_submit_for_verify.
3
+ description: Review submitted Chorus tasks — verify implementation against AC and proposal documents. Spawn it with the subagent tool and wait for its VERDICT comment after chorus_submit_for_verify.
4
4
  tools: read, grep, find, ls, bash, mcp, mcpScript
5
5
  acceptance: { level: "none", reason: "read-only chorus reviewer; verdict is posted via chorus_add_comment to Chorus, not returned to parent; suppress acceptance-report injection" }
6
6
  ---
@@ -14,8 +14,10 @@ USE THE chorus_* MCP TOOLS for all Chorus data access — do NOT use curl or raw
14
14
  - chorus_get_document({ documentUuid }) — full doc body if needed
15
15
  - chorus_add_comment({ targetType: "task", targetUuid, content }) — post your VERDICT (the ONLY write you may do)
16
16
  Do NOT call chorus_create_session, chorus_close_session, or any chorus_admin_* tool — the extension owns session lifecycle and the main agent owns admin actions.
17
- Keep your comment output under 800 characters. PASS items: names only. NOTE items: one-line description. BLOCKER items: command + output + evidence.
18
- Classify every finding as BLOCKER (blocks correctness: build/test failure, AC not implemented, semantic contradiction) or NOTE (non-blocking: pseudocode mismatch, wording difference, style suggestion).
17
+ Your output is bounded by relevance, not by a character count. BLOCKER evidence is UNBOUNDED — write it in full; truncating evidence is never the right way to shorten a comment. Report at most 5 newly-raised NOTEs; past 5, drop the least relevant rather than compressing all of them into fragments. That limit governs NEWLY-RAISED NOTEs only and never the carried-forward acknowledgement lines for earlier-round findings, which are all written regardless of count. PASS items: names only. NOTE items: one-line description. BLOCKER items: command + output + evidence.
18
+ Classify every finding as BLOCKER (blocks correctness: build/test failure, AC not implemented, semantic contradiction, and the default dimensions below — a bug no AC covers, reimplementation of something already available, a security defect this task wrote, a test that would pass under a wrong implementation, a masked failure of a required operation) or NOTE (non-blocking: pseudocode mismatch, wording difference, style suggestion).
19
+ Give every finding a stable ID: BLOCKER titles are `B<round>-<slug>`, NOTE entries are `N<round>-<slug>`, where <round> is the round that FIRST reported it — never renamed or renumbered in later rounds.
20
+ Round 2+ MUST also acknowledge every prior BLOCKER and every prior NOTE by ID with exactly one of three states — `fixed` / `still-open` / `not-verifiable` — plus what you actually re-ran or re-read. Silence is not a fix: only an explicit `fixed` closes a finding. A prior BLOCKER that is `still-open` OR `not-verifiable` yields VERDICT: FAIL. An unresolved NOTE never yields worse than PASS WITH NOTES.
19
21
  You MUST end with VERDICT: PASS, VERDICT: PASS WITH NOTES, or VERDICT: FAIL. Has BLOCKERs → FAIL. Only NOTEs → PASS WITH NOTES. Nothing → PASS.
20
22
  If this is Round 2+, focus ONLY on whether previous BLOCKERs were fixed. Do NOT introduce new NOTEs.
21
23
  Turn budget rule: When ≤3 turns remain in your budget, STOP reading files AND stop running bash/tests immediately and post your current findings as a comment via chorus_add_comment. Incomplete findings posted are strictly better than no comment at all.
@@ -93,6 +95,19 @@ Pick 2-3 probes that fit the specific task: boundary values, missing fields, err
93
95
 
94
96
  **Hallucination check**: Flag anything that looks like it could be LLM-fabricated as NOTE — API signatures, CLI flags, config keys, model IDs, endpoint URLs, package names, or any external detail the developer likely wrote from memory rather than referencing docs.
95
97
 
98
+ **Code quality and correctness beyond the AC — checked by default**
99
+
100
+ The AC were written before the code existed: they describe what to build, never how well it was built. Anything that depends on the code **as written** cannot be in the AC, so "no AC covers it" is not a reason to stay silent.
101
+
102
+ - **Correctness without an AC.** Behaviour that is simply wrong, where no AC happens to speak to it → **BLOCKER**. You do not need an acceptance criterion to report a bug.
103
+ - **Reimplementation.** Prefer, in this order: the platform's own feature → the standard library or a dependency already present → an existing utility in this repo → new code. New code that duplicates something already available → **BLOCKER**, and name the existing thing with its path. "This could be shorter" with nothing named is not a finding.
104
+ - **Security in this task's own code.** A missing authorization check, a query missing tenant/account scoping, injection (SQL / command / path), a secret in source or logs, unsafe deserialization → **BLOCKER**. Do not defer to the aggregate gate: it looks for risk that appears only when tasks are combined, not for a hole one task wrote by itself.
105
+ - **Tests that cannot fail.** Ask one question of each test offered as covering an AC: **would it fail if the behaviour were implemented wrongly?** If no — it asserts a tautology, snapshots nothing, or only restates what the code already does → **BLOCKER**: that AC is unverified. Judge the test's capability, never its mechanism: a mock, a spy, or a call-count assertion is not itself a defect, and when the AC *is* about invocation ("the callback runs exactly once", "the handler is not called on the error path") asserting the call **is** direct verification of that contract. Thin-but-real tests → NOTE.
106
+ - **Silent failure.** A **required** operation whose failure is masked — an empty catch that hides it, an ignored rejected promise, a failure path that reports success — or masking that violates a stated error contract → **BLOCKER**. Deliberate degradation is not a finding: work that is explicitly optional or best-effort (telemetry, cache population, post-run reconstruction), whose failure is recorded and which is designed not to propagate, is working as intended. Recording the error is itself the visibility the no-silent-errors principle asks for, so "logged and not propagated" is not by itself a defect — ask whether the feature depends on the operation that failed.
107
+ - **Maintainability, leftovers, diff hygiene → NOTE:** a function doing several unrelated things, deep nesting, copy-pasted blocks inside this diff, unnamed magic values; unused imports/exports, commented-out code, debug logging, TODOs this task introduced; changes unrelated to this task bundled into the same diff; `any` or unchecked nullables on the interface this task owns; a query inside a loop or an unbounded fetch. Any of these becomes a **BLOCKER** only if it makes an AC unverifiable or changes behaviour outside this task's scope.
108
+
109
+ **Severity rule.** A quality finding is a NOTE by default and becomes a BLOCKER only when you can **name the concrete defect** — the existing utility being duplicated and where it lives, the missing check, the assertion that cannot fail. Taste never blocks: if you cannot point at it, it is a NOTE or it is nothing. Report the cheapest concrete change, never a redesign.
110
+
96
111
  **Step 7: Intent alignment**
97
112
 
98
113
  Resolve the originating Idea (this task's proposal → `inputUuids[0]`) and read its body + human-answered elaboration + human-authored comments (`answeredBy.type` / `author.type == "user"`; agent-authored entries are audit context, not intent). Beyond the task's own AC, raise a **BLOCKER** if the delivered work drifts from that intent — unrequested scope, a dropped requirement, or AC-passing-but-intent-missing — unless a cited human entry or an explicit human override authorizes it.
@@ -114,15 +129,60 @@ Every finding MUST be classified as one of:
114
129
  - Style/naming suggestions
115
130
  - Non-semantic inconsistencies
116
131
 
117
- Rules: Pseudocode inconsistencies → always NOTE. Cross-document wording differences always NOTE. Only functional/behavioral issues BLOCKER.
132
+ Rules: Style, naming, and pseudocode inconsistencies → always NOTE. Functional, security, and verification-integrity issuesBLOCKER. A quality finding blocks only when you can name the concrete defect.
118
133
 
119
134
  VERDICT decision: has BLOCKERs → FAIL. Only NOTEs → PASS WITH NOTES. Nothing → PASS.
120
135
 
136
+ === WHAT TO REPORT / WHAT NOT TO REPORT ===
137
+
138
+ This list is specific to the task gate. It is not a generic checklist shared with the proposal or aggregate code reviewers — each of those gates sees something you do not, and reaching into their scope is the main way this review turns into noise.
139
+
140
+ **DO report:**
141
+ - The result of running this task's tests/build, quoting the real output — exact command, exit code, the relevant lines.
142
+ - **Match your evidence to the KIND of claim; never lower a finding's severity just because you could not run something.** An acceptance criterion the code plainly fails **as written** — the AC demands tenant scoping and the query has none, demands an authorization check that is absent, demands an error path that is unhandled — is a **BLOCKER** on file-and-line evidence: quote the code. A claim about **runtime behaviour** needs a named trigger path or observed output, else it is at most a NOTE. Having no shell changes which evidence you cite, never the severity ceiling.
143
+ - Judgements made against **this task's AC and this task's diff**, and nothing wider — with one explicit exception: the intent-alignment step above. Checking the delivered work against the originating Idea's human-authored intent is IN scope and is never "wider"; intent drift stays a BLOCKER.
144
+ - An acceptance criterion that is not actually covered by the implementation → BLOCKER.
145
+ - Behaviour that contradicts the approved proposal documents the task was built from.
146
+
147
+ **DO NOT report:**
148
+ - **Never report something as missing without first confirming its absence with read-only Bash** (`ls` / `grep` / `rg` / `find` / `git ls-files`), and cite the command you ran. An unverified "X is missing" is the single most common false BLOCKER.
149
+ - **Do not re-litigate decisions inside an already-approved proposal.** The proposal gate closed; disagreeing with an approved design is not a finding against this task.
150
+ - **Do not report pre-existing problems this task never touched** — unless this task's change makes one reachable, worse, or newly load-bearing, which makes it this change's problem and in scope. If the task's diff did not introduce it, it is not this review's finding.
151
+ - **Do not report gaps that belong to a different task** — the aggregate code reviewer owns inter-task gaps and will see it at the feature level. Work another task in the same proposal owns is out of scope here, even when you can see it is missing.
152
+ - **Never raise a BLOCKER for absent end-to-end integration tests.** Feature-level coverage across tasks is the aggregate code reviewer's dimension, not this gate's. This task's own AC is the standard here.
153
+
121
154
  === ROUND AWARENESS ===
122
155
 
123
156
  You may receive the current review round number in your context.
124
157
  - **Round 1**: Full review, normal strictness.
125
- - **Round 2+**: Focus ONLY on whether previous BLOCKERs were fixed. Do NOT introduce new NOTEs on areas not flagged in previous rounds. If all previous BLOCKERs are resolved, VERDICT: PASS (or PASS WITH NOTES if old NOTEs remain). Round 1 already did the full-depth review. Round 2+ should only re-read the specific files and re-run the specific tests/commands tied to previous BLOCKERs — do not re-scan unrelated code, do not rerun the full test suite, and do not probe new areas. Trusting the developer's diff summary without targeted re-verification is the "verification avoidance" anti-pattern.
158
+ - **Round 2+**: Focus ONLY on whether previous BLOCKERs were fixed. Do NOT introduce new NOTEs on areas not flagged in previous rounds. A previous BLOCKER counts as resolved ONLY when you mark it `fixed` under the Prior-findings rules below; when every prior BLOCKER is `fixed`, VERDICT: PASS (or PASS WITH NOTES if any prior NOTE is still open). Round 1 already did the full-depth review. Round 2+ should only re-read the specific files and re-run the specific tests/commands tied to prior findings (BLOCKERs and NOTEs alike a prior NOTE you may not re-read is a NOTE you can never close) — do not re-scan unrelated code, do not rerun the full test suite, and do not probe new areas. Trusting the developer's diff summary without targeted re-verification is the "verification avoidance" anti-pattern.
159
+
160
+ === PRIOR FINDINGS: STABLE IDs AND CROSS-ROUND ACKNOWLEDGEMENT ===
161
+
162
+ **Stable IDs.** Title every BLOCKER `B<round>-<slug>` and list every NOTE as `N<round>-<slug>`, where `<round>` is the round that **first reported** the finding and `<slug>` is a short kebab-case label — `B1-ac3-not-implemented`, `N2-stale-cli-flag`. The round number is part of the finding's identity and is **never renamed or renumbered** when the finding is carried into a later round. A `B1-…` line appearing in a round-3 comment is itself the signal that this problem has survived two fix attempts.
163
+
164
+ **Acknowledgement.** In round 2 and later, list **every** prior BLOCKER and **every** prior NOTE by ID under a `**Prior findings:**` block, each with exactly one of these three states and with the command you actually re-ran this round:
165
+
166
+ - `fixed` — re-verified this round; cite the command and its result.
167
+ - `still-open` — re-checked, and the problem is still there.
168
+ - `not-verifiable` — could not check it this round; say why (missing dependency, no database, environment read-only). Never counts as fixed.
169
+
170
+ Those three states are the whole vocabulary — there is no fourth state, and the same three words apply to BLOCKERs and NOTEs alike.
171
+
172
+ Three rules govern what the states mean for the verdict:
173
+
174
+ - **Silence is not a fix.** Not re-reporting a finding does not close it. Only an explicit `fixed` line closes a finding — an omitted finding stays open.
175
+ - **A prior BLOCKER whose state is `still-open` or `not-verifiable` yields `VERDICT: FAIL`.** Both states, not just `still-open`: a BLOCKER you could not re-verify has not been *shown* to be fixed, and `PASS WITH NOTES` would mean passing the task on an unverified blocker. The known cost is a false positive — a genuinely-fixed blocker that merely could not be re-run this round reads as FAIL. That trade is accepted: a spurious escalation to a human is recoverable, a spurious pass is not.
176
+ - **NOTEs never escalate.** A `still-open` or `not-verifiable` NOTE yields at worst `VERDICT: PASS WITH NOTES` and can **never** be the reason for a `VERDICT: FAIL`. Only BLOCKERs block.
177
+
178
+ **How the NOTE limit composes with the round-2+ rule above.** These are two separate rules and they never apply to the same NOTEs:
179
+
180
+ | | Newly-raised NOTEs | Carried-forward acknowledgement lines |
181
+ |---|---|---|
182
+ | Round 1 | at most 5 — past 5, drop the least relevant | none exist yet |
183
+ | Round 2+ | **zero** — Round awareness above already forbids new NOTEs | **all of them, written in full, never limited** |
184
+
185
+ So the limit of 5 governs newly-raised NOTEs **only**. It never applies to the carried-forward acknowledgement lines: in round 1 there is nothing to carry forward, and in round 2+ there are no new NOTEs left to limit. Never drop a prior finding's acknowledgement line to stay under a NOTE limit.
126
186
 
127
187
  === RECOGNIZE YOUR OWN RATIONALIZATIONS ===
128
188
  - "The code looks correct based on my reading" — reading is not verification. Run it.
@@ -135,14 +195,19 @@ You may receive the current review round number in your context.
135
195
  ```
136
196
  ### Review Summary
137
197
 
198
+ **Prior findings:** (round 2+ only — omit this block in round 1)
199
+ - B1-<slug>: fixed — `<what you re-ran or re-read>` → <result observed>
200
+ - B1-<other-slug>: still-open — `<what you re-ran or re-read>` → <problem still present>
201
+ - B2-<slug>: not-verifiable — <why you could not check it this round>
202
+ - N1-<slug>: still-open
138
203
  **PASS (N):** AC-1 name, AC-2 name, ...
139
204
 
140
205
  **NOTE (M):**
141
- - Note-1: [one-line description]
142
- - Note-2: [one-line description]
206
+ - N<round>-<slug>: [one-line description]
207
+ - N<round>-<slug>: [one-line description]
143
208
 
144
209
  **BLOCKER (K):**
145
- ### Blocker-1: name
210
+ ### B<round>-<slug>
146
211
  **Command run:** [exact command executed]
147
212
  **Output observed:** [actual output — copy-paste, not paraphrased]
148
213
  **Evidence:** [specific finding with file paths, line numbers]
@@ -152,7 +217,7 @@ You may receive the current review round number in your context.
152
217
  VERDICT: PASS / PASS WITH NOTES / FAIL
153
218
  ```
154
219
 
155
- PASS items get names only. NOTE items get one-line descriptions. BLOCKER items get full command/output/evidence. Keep total output under 800 characters be concise. No preamble, no summary paragraph.
220
+ PASS items get names only. NOTE items get one-line descriptions. BLOCKER items get full command/output/evidence. BLOCKER evidence is unbounded, so never truncate it to shorten the comment; report at most 5 newly-raised NOTEs and drop the least relevant beyond that. The `Prior findings` acknowledgement lines are never subject to that limit and are always written in full. In every ID, `<round>` is the round that first reported the finding and is never renamed in a later round. No preamble, no summary paragraph.
156
221
 
157
222
  === POSTING RESULTS ===
158
223
  Post the full results as a single comment:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: chorus-worker
3
- description: General-purpose Chorus implementer subagent that claims and completes ONE Chorus task end-to-end via the develop workflow. Dispatch it via the blocking subagent tool (single or parallel mode) for wave-based execution.
3
+ description: "General-purpose Chorus implementer subagent that claims and completes ONE Chorus task end-to-end via the develop workflow. Dispatch one per worker with the subagent tool (the bundled subagent also takes a `tasks: [...]` composite) for wave-based execution, and wait for the run to settle."
4
4
  ---
5
5
 
6
6
  You are a Chorus implementer. Your job is to take ONE assigned Chorus task and drive it from open to `to_verify` by writing real, working code — then hand back to the main agent for independent review and admin verification. You do NOT review, verify, or approve your own work.
@@ -79,7 +79,7 @@ chorus_submit_for_verify({ taskUuid: "<task-uuid>", summary: "<what you built +
79
79
  === HARD LIMITS ===
80
80
 
81
81
  - Do **NOT** admin-verify or approve your own work. `chorus_admin_verify_task`, `chorus_mark_acceptance_criteria`, and proposal approval are the main agent's / orchestrator's job — after you submit, the main agent spawns `chorus-task-reviewer` and acts on its VERDICT.
82
- - Do **NOT** call `chorus_create_session` or `chorus_close_session` — the chorus-pi extension owns session lifecycle (it created your session and closes it when the dispatching `subagent` tool call returns).
82
+ - Do **NOT** call `chorus_create_session` or `chorus_close_session` — the chorus-pi extension owns session lifecycle (it created your session, and it closes it when the dispatch returns a blocking implementation — or when the run settles under nicobailon `pi-subagents`).
83
83
  - Work on **ONE** task. If you cannot complete it (missing knowledge, hard blocker), `chorus_release_task` it, add a comment explaining why, and report that back — do not leave it half-claimed.
84
84
 
85
85
  === OUTPUT FORMAT (REQUIRED) ===
@@ -143,7 +143,7 @@ ACCEPT="Accept: application/json, text/event-stream"
143
143
  CT="Content-Type: application/json"
144
144
 
145
145
  INIT=$(cat <<JSON
146
- {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"chorus-codex-hook","version":"0.18.0"}}}
146
+ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"chorus-codex-hook","version":"0.19.0"}}}
147
147
  JSON
148
148
  )
149
149
 
@@ -17,6 +17,17 @@
17
17
  * injecting session context — a capability the Codex port
18
18
  * lacks (Codex has no pre-spawn mutation channel, so its
19
19
  * workers must manage sessions manually).
20
+ * → pin every REVIEWER and WORKER task to the background
21
+ * (`async: true`), so it keeps the ambient MCP tools it
22
+ * needs (reviewers: chorus_add_comment for the VERDICT;
23
+ * workers: chorus_session_checkin_task / chorus_update_task
24
+ * / chorus_report_work / chorus_submit_for_verify). A
25
+ * foreground (`async: false`) child is in-process and never
26
+ * loads ambient extensions, so those tools are missing:
27
+ * a reviewer's declared allowlist makes that a failed run,
28
+ * a worker (no allowlist) degrades silently. The bundled
29
+ * subagent ignores the flag (its child is a separate `pi`
30
+ * process) and is unaffected.
20
31
  * - tool_result → close the ephemeral worker session(s) once the `subagent`
21
32
  * - tool_result → for the official blocking subagent, close the ephemeral
22
33
  * worker session(s) once the `subagent` tool call returns
@@ -41,6 +52,8 @@
41
52
 
42
53
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
43
54
  import {
55
+ forceSubagentCallAsync,
56
+ isReviewerAgent,
44
57
  isWorkerAgent,
45
58
  subagentTaskItems,
46
59
  sessionWorkflow,
@@ -344,9 +357,9 @@ export default function (pi: ExtensionAPI) {
344
357
  specRoute,
345
358
  "",
346
359
  "## Quick Reference",
347
- "- **Sessions**: auto-managed. When you dispatch a WORKER via the `subagent` tool (single/parallel/chain), the extension creates a Chorus session per worker task and injects its UUID + the session workflow into that task automatically; the session is closed when the `subagent` tool call returns (children are ephemeral). Do NOT call chorus_create_session/close_session yourself.",
360
+ "- **Sessions**: auto-managed. When you dispatch a WORKER via the `subagent` tool (one child per call; the bundled subagent also takes `tasks`/`chain` composites — nicobailon `pi-subagents` rejects those top-level fields, so a wave there is one single dispatch per worker), the extension creates a Chorus session per worker task and injects its UUID + the session workflow into that task automatically; the extension closes that session when the dispatch returns (blocking implementations) or when the run settles — `subagent:async-complete` / `process-terminal` under nicobailon `pi-subagents`, which is where Chorus agents run by default. Do NOT call chorus_create_session/close_session yourself.",
348
361
  "- **Notifications**: chorus_get_notifications() fetches and auto-marks read.",
349
- "- **Reviewer sub-agents**: after submit_proposal/submit_for_verify the extension nudges you to spawn chorus-proposal-reviewer / chorus-task-reviewer. Use the blocking `subagent` tool so it waits for the VERDICT; reviewers do NOT get a Chorus session.",
362
+ "- **Reviewer sub-agents**: after submit_proposal/submit_for_verify the extension nudges you to spawn chorus-proposal-reviewer / chorus-task-reviewer. Dispatch it with the `subagent` tool and wait for its VERDICT comment — reviewers are pinned to the background/async path, because a foreground child has no `mcp` and could not post the comment; reviewers do NOT get a Chorus session.",
350
363
  "- **Code-review gateway**: bounded by `CHORUS_MAX_CODE_REVIEW_ROUNDS` (current: " + (MAX_CODE_REVIEW_ROUNDS === 0 ? "unlimited" : String(MAX_CODE_REVIEW_ROUNDS)) + "; on FAIL, fix via /skill:quick-dev and re-run — after the limit, escalate the Idea's feature-level BLOCKERs to a human instead of shipping.",
351
364
  (CHORUS_BIN
352
365
  ? "- **OpenSpec wrapper**: `bin/chorus-mcp-call.sh` is at `" + CHORUS_BIN + "` — the CLI-absent fallback for OpenSpec-mode document mirrors. Prefer `chorus mcp call <tool> '<json>' --arg-file content=<file>` (chorus >= 0.17.0); use this wrapper only when `chorus` is not on PATH (a bare `chorus-mcp-call.sh` will NOT be on PATH for local-path installs). See /skill:openspec-aware §2."
@@ -386,15 +399,37 @@ export default function (pi: ExtensionAPI) {
386
399
  // `subagent` invocation (single / parallel / chain), create a Chorus session
387
400
  // and inject its UUID + the session workflow into that task. The ephemeral
388
401
  // child pi subprocess spawned for that task receives the UUID in its prompt.
389
- pi.on("tool_call", async (event, _ctx) => {
402
+ pi.on("tool_call", async (event, ctx) => {
390
403
  if (!CONFIGURED || event.toolName !== "subagent") return;
404
+ const items = subagentTaskItems(event.input);
405
+ // Every Chorus agent this extension spawns needs ambient MCP tools: the
406
+ // reviewers post their VERDICT with chorus_add_comment, and the workers run
407
+ // the whole task lifecycle (chorus_session_checkin_task / chorus_update_task
408
+ // / chorus_report_work / chorus_submit_for_verify) — both agent bodies forbid
409
+ // curl. Under the nicobailon `pi-subagents` implementation an in-process
410
+ // foreground child (`async: false`) never loads the parent's ambient
411
+ // extensions, so those tools simply do not exist: for a reviewer the declared
412
+ // `tools` allowlist turns that into a failed run, for a worker (no allowlist)
413
+ // it degrades silently. Pin both to the background path whatever the caller
414
+ // asked for; the bundled subagent ignores the flag and keeps blocking + MCP.
415
+ // The pin is CALL-level: one call has one mode, derived from the top-level
416
+ // `async` (an item-level one is read by nothing), so a composite carrying a
417
+ // Chorus agent is pinned as a whole.
418
+ if (items.some((item) => isReviewerAgent(item.agent) || isWorkerAgent(item.agent)) && forceSubagentCallAsync(event.input)) {
419
+ // The caller asked for foreground and we overrode it — surface that
420
+ // instead of silently changing how their dispatch runs.
421
+ ctx.ui.notify(
422
+ "Chorus agents need ambient `mcp`/`chorus_*` tools, which a foreground child does not have — this dispatch was pinned to the background (async) path.",
423
+ "info",
424
+ );
425
+ }
391
426
  // Positive worker classification: only canonical worker agents get a Chorus
392
427
  // session + task-lifecycle injection. The three Chorus reviewers are not
393
428
  // workers (read-only), and the example scout/planner/reviewer agents are
394
429
  // read-only too — injecting the session workflow into them adds irrelevant
395
430
  // instructions and unnecessary chorus_create_session traffic. See isWorkerAgent().
396
431
  const created: string[] = [];
397
- for (const item of subagentTaskItems(event.input)) {
432
+ for (const item of items) {
398
433
  if (!isWorkerAgent(item.agent)) continue;
399
434
  // Manual main-agent template already injected — never double-inject.
400
435
  if (hasSessionMarker(item.task)) continue;
@@ -453,15 +488,15 @@ export default function (pi: ExtensionAPI) {
453
488
  if (native && (NUDGE_TOOL_NAMES as readonly string[]).includes(native)) {
454
489
  const nudges: Record<string, { spawn: string; enabled: boolean }> = {
455
490
  chorus_pm_submit_proposal: {
456
- spawn: "spawn chorus-proposal-reviewer to review the proposal (blocking subagent tool), then close the agent",
491
+ spawn: "spawn chorus-proposal-reviewer to review the proposal, wait for its VERDICT comment, then close the agent",
457
492
  enabled: ENABLE_PROPOSAL_REVIEWER,
458
493
  },
459
494
  chorus_submit_for_verify: {
460
- spawn: "spawn chorus-task-reviewer to review the task (blocking subagent tool), then close the agent",
495
+ spawn: "spawn chorus-task-reviewer to review the task, wait for its VERDICT comment, then close the agent",
461
496
  enabled: ENABLE_TASK_REVIEWER,
462
497
  },
463
498
  chorus_admin_verify_task: {
464
- spawn: "if this was the last task of an idea-rooted proposal: spawn chorus-code-reviewer over the idea's aggregate change (blocking subagent tool), then remind to archive the openspec change",
499
+ spawn: "if this was the last task of an idea-rooted proposal: spawn chorus-code-reviewer over the idea's aggregate change, wait for its VERDICT comment, then remind to archive the openspec change",
465
500
  enabled: ENABLE_CODE_REVIEWER,
466
501
  },
467
502
  };
package/lib/lib.ts CHANGED
@@ -119,12 +119,15 @@ export function isWorkerAgent(name: string): boolean {
119
119
  * - parallel: { tasks: [{ agent, task }, ...] }
120
120
  * - chain: { chain: [{ agent, task }, ...] }
121
121
  *
122
- * Each returned holder carries the agent name, the current task text, and a
122
+ * Each returned holder carries the agent name, the current task text, a
123
123
  * `setTask` that writes back into the SAME input object in place — so the
124
124
  * extension can inject the Chorus session workflow into a worker's task before
125
125
  * the ephemeral child `pi` process is spawned (pi's `tool_call` event input is
126
126
  * mutable). Holders with a non-string / empty agent or task are skipped.
127
127
  *
128
+ * Pinning a call to the background path is a separate, CALL-level operation
129
+ * (one call has exactly one mode) — see forceSubagentCallAsync().
130
+ *
128
131
  * Replaces the old persistent-model agentId extraction: the official subagent
129
132
  * children are ephemeral (spawn → run → exit within one tool call) and expose
130
133
  * no `sa_<uuid>` agentId to map, so there is nothing to parse out of a result.
@@ -142,6 +145,11 @@ export function subagentTaskItems(input: unknown): SubagentTaskItem[] {
142
145
  const collect = (holder: Record<string, unknown>): void => {
143
146
  const agent = typeof holder.agent === "string" ? holder.agent : "";
144
147
  const task = typeof holder.task === "string" ? holder.task : "";
148
+ // Both fields are required to enumerate — do NOT relax this to agent-only
149
+ // to widen the pin: the worker path writes sessionWorkflow() into `task`,
150
+ // and a chain step may deliberately omit it (ChainItem.task is optional and
151
+ // defaults to {previous}), so an agent-only enumeration would corrupt that
152
+ // step's prompt instead of pinning it.
145
153
  if (!agent || !task) return;
146
154
  items.push({
147
155
  agent,
@@ -161,6 +169,57 @@ export function subagentTaskItems(input: unknown): SubagentTaskItem[] {
161
169
  return items;
162
170
  }
163
171
 
172
+ /**
173
+ * Force a `subagent` CALL onto the background (async) path: writes
174
+ * `async: true` into the ROOT input object in place, and REMOVES `clarify`.
175
+ *
176
+ * Takes the CALL object, not a task item, because `async` is a run-level
177
+ * parameter: it exists only as a top-level field (pi-subagents
178
+ * `extension/schemas.ts` — neither `ParallelTaskSchema` nor `ChainItem` has one,
179
+ * and `ChainItem` even sets `additionalProperties: false`), and both mode
180
+ * decisions read it there (`requestedAsync = effectiveParams.async ??
181
+ * asyncByDefault`, `runsForeground = ... (dispatchParams.async ??
182
+ * asyncByDefault) !== true` in `src/runs/foreground/subagent-executor.ts`). An
183
+ * item-level `async` on `tasks[]` / `chain[]` is read by nothing — writing one
184
+ * is a silent no-op, which is exactly how a "pin the reviewer" fix can look
185
+ * right while doing nothing for composite calls.
186
+ *
187
+ * Used for agents that need ambient MCP tools. Under the nicobailon
188
+ * `pi-subagents` implementation an in-process foreground child
189
+ * (`async: false`) never loads the parent's ambient extensions, so `mcp` /
190
+ * `mcpScript` / `chorus_*` do not exist for it. Verified 2026-09-20 with a probe
191
+ * agent that declares no `tools` allowlist (so no diagnostic can fire): its
192
+ * whole tool set was `read, bash, edit, write, bg_wait, contact_supervisor` —
193
+ * no `mcp`, no `chorus_*` — and it could not post a comment. The bundled
194
+ * subagent ignores the flag entirely (its child is a separate `pi` process,
195
+ * which does load extensions), so forcing it is safe under either
196
+ * implementation.
197
+ *
198
+ * `clarify` is DELETED, not assigned: it disables async in nicobailon's
199
+ * executor (`effectiveAsync = requestedAsync && clarify !== true`), and the
200
+ * public normalizer (`normalizePublicSubagentExecution`) additionally rejects
201
+ * any call where the property is merely **present** — `params.clarify !==
202
+ * undefined` in `src/extension/public-execution.js` (0.70.0:91, same in
203
+ * 0.66.0), "Public workflowScript execution does not support clarify UI." — so
204
+ * a leftover `clarify: false` would be a hard pre-dispatch rejection rather
205
+ * than a pin. Deleting mirrors the package's own
206
+ * applyForceTopLevelAsyncOverride() (that helper also honors `foregroundOnly`;
207
+ * a Chorus agent cannot run foreground at all, so this one deliberately does
208
+ * not).
209
+ *
210
+ * @returns true when the caller had explicitly asked for foreground
211
+ * (`async: false`) and was overridden — so the caller can surface the override
212
+ * instead of silently changing the dispatch mode.
213
+ */
214
+ export function forceSubagentCallAsync(input: unknown): boolean {
215
+ if (!input || typeof input !== "object") return false;
216
+ const obj = input as Record<string, unknown>;
217
+ const overrodeExplicitForeground = obj.async === false;
218
+ obj.async = true;
219
+ if ("clarify" in obj) delete obj.clarify;
220
+ return overrodeExplicitForeground;
221
+ }
222
+
164
223
  /**
165
224
  * Build the session-workflow suffix injected into a spawned worker's task
166
225
  * (via the tool_call event's mutable input). The subprocess receives this
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chorus-aidlc/chorus-pi",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Chorus AI-DLC collaboration platform extension for the Pi coding agent. Provides skills for every stage of the AI-DLC lifecycle, read-only reviewer subagents, and session-aware extension hooks. The Chorus MCP server is auto-discovered from the repo's .mcp.json by pi-mcp-adapter — no installer required.",
5
5
  "author": {
6
6
  "name": "Chorus-AIDLC"
@@ -4,7 +4,7 @@ description: Optional divergent-then-convergent dialogue for fuzzy ideas. Invoke
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -4,7 +4,7 @@ description: Chorus AI Agent collaboration platform — overview, common tools,
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -112,7 +112,7 @@ Results can be filtered by project(s) using optional HTTP headers in your `.mcp.
112
112
 
113
113
  ### Session (Sub-Agents Only)
114
114
 
115
- The Chorus Pi extension **fully automates** session lifecycle. When you spawn a worker via `subagent_spawn`, the extension auto-creates a Chorus session and maps it to the `agentId`; when you `subagent_manage close` the agent, it closes the session. Sub-agents only need to:
115
+ The Chorus Pi extension **fully automates** session lifecycle. When you dispatch a worker with the `subagent` tool, the extension auto-creates a Chorus session and injects its UUID + workflow into that worker's task; it closes the session when the dispatch returns (bundled subagent) or when the run settles (`subagent:async-complete` / `process-terminal` under nicobailon `pi-subagents`). Sub-agents only need to:
116
116
 
117
117
  1. `chorus_session_checkin_task` — before starting work on a task
118
118
  2. `chorus_session_checkout_task` — when done with a task
@@ -331,7 +331,7 @@ The table below shows default tool availability for each preset (no custom permi
331
331
 
332
332
  ### 5. Review Agent Configuration
333
333
 
334
- The extension includes three independent review agents. After proposal submission, task verification, or the last task of an idea-rooted proposal being verified, the extension nudges you to spawn the reviewer via `subagent_spawn`. You must spawn it manually — it is NOT auto-launched. All are **enabled by default**.
334
+ The extension includes three independent review agents. After proposal submission, task verification, or the last task of an idea-rooted proposal being verified, the extension nudges you to dispatch the reviewer with the `subagent` tool. You must spawn it manually — it is NOT auto-launched. All are **enabled by default**.
335
335
 
336
336
  | Setting | Controls | Default |
337
337
  |---------|----------|---------|
@@ -372,7 +372,7 @@ To turn OpenSpec off, set `CHORUS_OPENSPEC_MODE=off` — the mode then falls bac
372
372
  ## Execution Rules
373
373
 
374
374
  1. **Always check in first** — Call `chorus_checkin()` at session start (the extension does this automatically and injects the result)
375
- 2. **Sessions are automatic** — The extension creates, heartbeats, and closes sessions on `subagent_spawn` / `subagent_manage close`. Never call `chorus_create_session` or `chorus_close_session` yourself.
375
+ 2. **Sessions are automatic** — The extension creates the session when you dispatch a worker with the `subagent` tool, and closes it when the dispatch returns (bundled subagent) or when the run settles (nicobailon `pi-subagents`). Never call `chorus_create_session` or `chorus_close_session` yourself.
376
376
  3. **Session checkin is sub-agent only** — Sub-agents call `chorus_session_checkin_task` / `chorus_session_checkout_task` and pass `sessionUuid`. Main agent skips session tools entirely.
377
377
  4. **Stay in your role** — Only use tools available to your role
378
378
  5. **Report progress** — Use `chorus_report_work` or `chorus_add_comment`
@@ -382,7 +382,7 @@ To turn OpenSpec off, set `CHORUS_OPENSPEC_MODE=off` — the mode then falls bac
382
382
  9. **Document decisions** — Add comments explaining your reasoning
383
383
  10. **Respect the review process** — Submit work for verification; don't assume it's done until Admin verifies
384
384
  11. **Always use AskUserQuestion for human interaction** — NEVER display questions as plain text; use interactive radio buttons (the `ask_user_question` tool)
385
- 12. **Close sub-agents after use** — Pi limits concurrent sub-agents; after a reviewer/worker finishes, call `subagent_manage close` to release the slot. `completed` does not release it.
385
+ 12. **No close step** — a `subagent` dispatch owns its children's whole lifecycle: the bundled subagent's children exit within the call, and a nicobailon run settles on its own, so there is nothing to close (inspect a run with `subagent({ action: "status" })` if needed).
386
386
 
387
387
  ---
388
388
 
@@ -4,7 +4,7 @@ description: How to install, configure, and use the `chorus` CLI — install it,
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -4,7 +4,7 @@ description: Chorus Development workflow — claim tasks, report work, manage se
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -225,7 +225,7 @@ chorus_submit_for_verify({
225
225
 
226
226
  > `to_verify` does NOT unblock downstream tasks — only `done` (after admin verification) does.
227
227
 
228
- > **Review Agent:** After `chorus_submit_for_verify`, the Chorus extension nudges you to spawn `chorus-task-reviewer` — an independent, read-only review agent. You MUST spawn it yourself (it is NOT auto-launched). **Use the blocking `subagent` tool** (it waits for the VERDICT and returns it) — wait for the VERDICT before proceeding. The reviewer posts a VERDICT comment on the task.
228
+ > **Review Agent:** After `chorus_submit_for_verify`, the Chorus extension nudges you to spawn `chorus-task-reviewer` — an independent, read-only review agent. You MUST spawn it yourself (it is NOT auto-launched). **Spawn it with the `subagent` tool and wait for its VERDICT** — wait before proceeding. The verdict is the comment the reviewer posts on the task, not the call's return value.
229
229
 
230
230
  After the reviewer completes, read its VERDICT:
231
231
  ```
@@ -239,7 +239,7 @@ Find THIS round's `VERDICT:` comment — the one posted after your dispatch, not
239
239
 
240
240
  If no new `VERDICT:` comment appears after the reviewer returns, check what it *did* post. A comment reporting that the round limit was reached, or any other explicit refusal to review, is a deliberate escalation to a human: STOP — do not respawn, do not self-review, do not post a VERDICT of your own. If it posted nothing at all, respawn it ONCE, telling it to stay within its turn budget and reserve its last turns for the VERDICT, then apply this same check again to what the retry posts. An explicit refusal from the retry still means STOP; only a second true silence lets you review the task yourself as a read-only pass using the checklist and POST the VERDICT comment. **Absence is never a PASS.**
241
241
 
242
- > **Final code-review gateway (after the Idea's LAST task is verified):** when the task you just verified is the **last** task of its idea-rooted proposal, the feature is about to ship — the extension nudges you to spawn `chorus-code-reviewer` (gated by `CHORUS_ENABLE_CODE_REVIEWER`, default on). Spawn it yourself via the blocking `subagent` tool, passing the `ideaUuid` + round number; it reviews the Idea's **aggregate** code change across all its tasks (cross-task integration, architecture, security, regression, feature-level coverage) and posts one `VERDICT` comment on the **idea**. `PASS` / `PASS WITH NOTES` → ship; `FAIL` → fix via `/skill:quick-dev` (`chorus_create_tasks` with `proposalUuid` set to the current approved proposal so the fix tasks attach to it — do NOT reopen the verified tasks). Group related small BLOCKERs by default; split only materially large or independently testable fixes. Require AC self-check, independent task review, and admin verification for every fix task. Re-run aggregate review only after every fix is successfully `done`; a failed or cancelled fix stops the loop and escalates, bounded by `CHORUS_MAX_CODE_REVIEW_ROUNDS` (env, default 3; 0 = unlimited). Advisory/behavioral, like the other reviewers. Run it **before** any idea-completion report.
242
+ > **Final code-review gateway (after the Idea's LAST task is verified):** when the task you just verified is the **last** task of its idea-rooted proposal, the feature is about to ship — the extension nudges you to spawn `chorus-code-reviewer` (gated by `CHORUS_ENABLE_CODE_REVIEWER`, default on). Spawn it yourself with the `subagent` tool, passing the `ideaUuid` + round number; it reviews the Idea's **aggregate** code change across all its tasks (cross-task integration, architecture, security, regression, feature-level coverage) and posts one `VERDICT` comment on the **idea**. `PASS` / `PASS WITH NOTES` → ship; `FAIL` → fix via `/skill:quick-dev` (`chorus_create_tasks` with `proposalUuid` set to the current approved proposal so the fix tasks attach to it — do NOT reopen the verified tasks). Group related small BLOCKERs by default; split only materially large or independently testable fixes. Require AC self-check, independent task review, and admin verification for every fix task. Re-run aggregate review only after every fix is successfully `done`; a failed or cancelled fix stops the loop and escalates, bounded by `CHORUS_MAX_CODE_REVIEW_ROUNDS` (env, default 3; 0 = unlimited). Advisory/behavioral, like the other reviewers. Run it **before** any idea-completion report.
243
243
 
244
244
  ### Step 9: Handle Review Feedback
245
245
 
@@ -267,7 +267,7 @@ If the task you just self-verified was the LAST one of its Idea (every Task acro
267
267
 
268
268
  ## Session (Sub-Agents Only)
269
269
 
270
- The Chorus extension **fully automates** session lifecycle — a Chorus session is created (on `subagent` dispatch, via `tool_call` task injection) and closed (when the blocking `subagent` call returns) by the extension. Sub-agents only do 3 things manually:
270
+ The Chorus extension **fully automates** session lifecycle — a Chorus session is created (on `subagent` dispatch, via `tool_call` task injection) and closed by the extension (when the call returns for a blocking implementation, or when the run settles — `subagent:async-complete` / `process-terminal` under nicobailon `pi-subagents`). Sub-agents only do 3 things manually:
271
271
 
272
272
  1. `chorus_session_checkin_task({ sessionUuid, taskUuid })` — before starting work
273
273
  2. `chorus_session_checkout_task({ sessionUuid, taskUuid })` — when done (recommended; plugin also auto-checkouts on exit)
@@ -279,15 +279,22 @@ The Chorus extension **fully automates** session lifecycle — a Chorus session
279
279
 
280
280
  ## Parallel Sub-Agent Integration
281
281
 
282
- Use the `subagent` tool to run multiple Chorus workers in parallel; Chorus provides full work observability. The `subagent` tool is **blocking** a parallel dispatch runs every worker to completion and returns their aggregated output in one call (there is no async spawn, no `agentId`, and no manual close). The `chorus-pi` extension automates session lifecycle: when you dispatch a `chorus-worker`, it creates a Chorus session and injects the session UUID + workflow into that worker's task; when the `subagent` call returns, it closes the session.
282
+ Use the `subagent` tool to run multiple Chorus workers in parallel; Chorus provides full work observability. **Both the dispatch shape and the run mode depend on the installed implementation**, so pick the shape that matches the active one:
283
283
 
284
- > The `subagent` tool has three modes — **single** (`{ agent, task }`), **parallel** (`{ tasks: [...] }`, max 8 per call, concurrency 4), and **chain** (`{ chain: [...] }`, sequential with a `{previous}` placeholder). Dispatch `agent: "chorus-worker"` for Chorus task implementation.
284
+ | Implementation | Wave call shape | Run mode |
285
+ |---|---|---|
286
+ | Bundled subagent (pi's official reference pattern) | one composite call: `subagent({ tasks: [...] })` or `{ chain: [...] }` | blocking — the call returns when the workers exit |
287
+ | nicobailon `pi-subagents` | **one single dispatch per worker** (`subagent({ agent, task })`), issued together for the same wave — it rejects top-level `tasks`/`chain` before dispatch | async/background by default — each call returns a run receipt; completion arrives on the run notification / `bg_wait` |
288
+
289
+ Either way there is no `agentId` to track and no manual close. The `chorus-pi` extension automates session lifecycle: when you dispatch a `chorus-worker`, it creates a Chorus session and injects the session UUID + workflow into that worker's task, then closes the session when the dispatch returns (blocking) or when the run settles (`subagent:async-complete` / `process-terminal`).
290
+
291
+ > The three modes — **single** (`{ agent, task }`), **parallel** (`{ tasks: [...] }`, max 8 per call, concurrency 4), and **chain** (`{ chain: [...] }`, sequential with a `{previous}` placeholder) — are the **bundled** subagent's schema. Under nicobailon `pi-subagents` the public tool rejects top-level `tasks`/`chain` (`Legacy top-level chain and parallel inputs were removed; use workflowScript.`), and its `workflowScript` children are invisible to the extension's session hook — so under nicobailon dispatch one worker per call. Dispatch `agent: "chorus-worker"` for Chorus task implementation.
285
292
 
286
293
  ### Two-Layer Architecture
287
294
 
288
295
  | Layer | System | Purpose |
289
296
  |-------|--------|---------|
290
- | **Orchestration** | The `subagent` tool (single / parallel / chain) | Dispatching workers to isolated pi subprocesses and collecting their results |
297
+ | **Orchestration** | The `subagent` tool (one child per call; bundled-only `tasks`/`chain` composites) | Dispatching workers to isolated pi subprocesses and collecting their results |
291
298
  | **Work Tracking** | Chorus | Task lifecycle, session observability, activity stream |
292
299
 
293
300
  ### Team Lead Workflow
@@ -297,9 +304,9 @@ Use the `subagent` tool to run multiple Chorus workers in parallel; Chorus provi
297
304
  chorus_checkin()
298
305
  chorus_list_tasks({ projectUuid: "<project-uuid>" })
299
306
 
300
- # 2. Dispatch a worker per ready task in ONE blocking parallel call (max 8).
301
- # Pass only task + project UUIDs — the chorus-pi extension auto-injects the
302
- # session UUID + workflow into each worker's task.
307
+ # 2. Dispatch a worker per ready task in ONE wave the call shape follows the
308
+ # installed implementation:
309
+ # bundled subagent → one composite call, max 8 entries:
303
310
  subagent({
304
311
  tasks: [
305
312
  { agent: "chorus-worker",
@@ -307,14 +314,23 @@ subagent({
307
314
  // ... one entry per ready task, max 8 (batch into multiple calls if more)
308
315
  ]
309
316
  })
310
- # The call BLOCKS until every worker finishes and returns their outputs.
311
- # For a single task, use single mode: subagent({ agent: "chorus-worker", task: "..." })
317
+ # nicobailon pi-subagents one single dispatch per worker (it rejects
318
+ # top-level `tasks`); issue them together for the same wave:
319
+ subagent({ agent: "chorus-worker", task: "Your Chorus task UUID: <task-uuid>\n..." }) # task 1
320
+ subagent({ agent: "chorus-worker", task: "Your Chorus task UUID: <task-uuid>\n..." }) # task 2
321
+ # Pass only task + project UUIDs — the chorus-pi extension auto-injects the
322
+ # session UUID + workflow into each worker's task.
323
+ # The composite call returns when every worker finishes (bundled subagent); each
324
+ # nicobailon call returns a run receipt (async by default) — wait for the wave to
325
+ # settle either way.
326
+ # For a single task, single mode is all you need:
327
+ # subagent({ agent: "chorus-worker", task: "..." })
312
328
  ```
313
329
 
314
330
  **What the Team Lead prompt needs:**
315
331
  - Task UUID(s) + Project UUID
316
332
  - NO session UUID, NO workflow boilerplate — the extension auto-injects everything
317
- - No `agentId` to track and no close step — the blocking call owns the worker's whole lifecycle
333
+ - No `agentId` to track and no close step — the dispatch owns the worker's whole lifecycle
318
334
 
319
335
  ### Sub-Agent Workflow
320
336
 
@@ -338,7 +354,7 @@ chorus_submit_for_verify({ taskUuid: "<my-task-uuid>", summary: "..." })
338
354
 
339
355
  # The worker's final message is returned to the Team Lead as the subagent result.
340
356
  # DO NOT call chorus_close_session — the extension closes the session when the
341
- # blocking `subagent` call returns.
357
+ # dispatch returns (blocking) or when the run settles (async).
342
358
  ```
343
359
 
344
360
  ### Handling Task Dependencies (DAG)
@@ -347,12 +363,12 @@ chorus_submit_for_verify({ taskUuid: "<my-task-uuid>", summary: "..." })
347
363
 
348
364
  **Wave-based execution (recommended):**
349
365
  1. `chorus_get_unblocked_tasks` — find ready tasks
350
- 2. Dispatch a `chorus-worker` per ready task in ONE blocking `subagent({ tasks: [...] })` call (max 8; batch if more). The call returns when the whole wave has finished (each worker at `to_verify`).
366
+ 2. Dispatch a `chorus-worker` per ready task as one wave — one `subagent({ tasks: [...] })` call under the bundled subagent (max 8; batch if more), or one `subagent({ agent, task })` call per ready task under nicobailon `pi-subagents` (which rejects top-level `tasks`). Wait for the whole wave to settle (each worker at `to_verify`): the bundled call returns when its workers exit; under nicobailon each call returns a run receipt, so wait on the run notifications / `bg_wait`.
351
367
  3. **Verify each task** — spawn `chorus-task-reviewer`, act on its VERDICT, then `chorus_admin_verify_task` → `done`.
352
368
  4. `chorus_get_unblocked_tasks` — find newly unblocked tasks (Wave 2)
353
369
  5. Repeat until all tasks done
354
370
 
355
- > **Critical:** `to_verify` does NOT resolve dependencies — only `done` or `closed` does. The Team Lead must verify tasks between waves. The blocking `subagent` call already released each worker's slot on return, so there is nothing to close.
371
+ > **Critical:** `to_verify` does NOT resolve dependencies — only `done` or `closed` does. The Team Lead must verify tasks between waves. The dispatch already released each worker's slot once the wave settled, so there is nothing to close.
356
372
 
357
373
  ### Multiple Tasks Per Sub-Agent
358
374
 
@@ -365,7 +381,7 @@ subagent({
365
381
  })
366
382
  ```
367
383
 
368
- For strictly dependent stages where each step consumes the previous output, use chain mode: `subagent({ chain: [{ agent: "chorus-worker", task: "..." }, { agent: "chorus-worker", task: "... {previous} ..." }] })`.
384
+ For strictly dependent stages where each step consumes the previous output, use chain mode — **bundled subagent only** (nicobailon `pi-subagents` has no top-level `chain`; run its stages as separate dispatches, or as `workflowScript` steps, which the session hook cannot reach): `subagent({ chain: [{ agent: "chorus-worker", task: "..." }, { agent: "chorus-worker", task: "... {previous} ..." }] })`.
369
385
 
370
386
  ### MCP Access for Sub-Agents
371
387
 
@@ -4,7 +4,7 @@ description: Chorus documentation router — consult the live Chorus docs site t
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -4,7 +4,7 @@ description: Chorus Idea workflow — claim ideas, run elaboration rounds, and p
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -4,7 +4,7 @@ description: OpenSpec-mode authoring for Chorus PM workflows in Pi. The default
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -4,7 +4,7 @@ description: Multi-agent orchestration playbook — coordinate OTHER agents and
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -77,7 +77,7 @@ The extension ships three read-only reviewer subagents. As orchestrator you spaw
77
77
  | `chorus-task-reviewer` | a task is submitted for verify | one task vs its acceptance criteria (VERDICT on the task) |
78
78
  | `chorus-code-reviewer` | the idea's last task is verified | the idea's **aggregate** code change — the final ship gateway (VERDICT on the idea) |
79
79
 
80
- Spawn a reviewer via `subagent_spawn` with the reviewer skill and the target UUID; after it posts, `subagent_manage close` it to release Pi's concurrency slot. Each posts exactly one `VERDICT: PASS` / `PASS WITH NOTES` / `FAIL` comment. Verdicts are **advisory** — they do not auto-approve, auto-verify, or hard-block; you read the BLOCKERs and decide. A `FAIL` means route the BLOCKERs back for a fix before advancing (for a code-review FAIL, add fix tasks to the *approved* proposal via `/skill:quick-dev` and re-run once they are `done`). See `/skill:review` for the full pattern.
80
+ Dispatch a reviewer with the `subagent` tool `{ agent: "chorus-proposal-reviewer" | "chorus-task-reviewer" | "chorus-code-reviewer", task: "<target UUID> + what to review" }` — and wait for its VERDICT comment. There is no handle to close and no slot to release: the dispatch owns the child's lifecycle, and a reviewer gets no Chorus session. Each posts exactly one `VERDICT: PASS` / `PASS WITH NOTES` / `FAIL` comment. Verdicts are **advisory** — they do not auto-approve, auto-verify, or hard-block; you read the BLOCKERs and decide. A `FAIL` means route the BLOCKERs back for a fix before advancing (for a code-review FAIL, add fix tasks to the *approved* proposal via `/skill:quick-dev` and re-run once they are `done`). See `/skill:review` for the full pattern.
81
81
 
82
82
  ---
83
83
 
@@ -4,7 +4,7 @@ description: Chorus Proposal workflow — create proposals with document and tas
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -4,7 +4,7 @@ description: Quick Task workflow — skip Idea→Proposal, create tasks directly
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -152,7 +152,7 @@ chorus_submit_for_verify({
152
152
  })
153
153
  ```
154
154
 
155
- Submitting is not final verification. Spawn the required task-reviewer agent with `subagent_spawn` as described in `/skill:develop`, wait for it, and read THIS round's `VERDICT:` Task comment — the one posted after your dispatch, not an older round's. `PASS` and `PASS WITH NOTES` continue. On `FAIL`, do not verify or hand off: fix every unresolved BLOCKER, repeat AC self-check and submission, then run a fresh independent task review.
155
+ Submitting is not final verification. Dispatch the required `chorus-task-reviewer` with the `subagent` tool as described in `/skill:develop`, wait for its VERDICT, and read THIS round's `VERDICT:` Task comment — the one posted after your dispatch, not an older round's. `PASS` and `PASS WITH NOTES` continue. On `FAIL`, do not verify or hand off: fix every unresolved BLOCKER, repeat AC self-check and submission, then run a fresh independent task review.
156
156
 
157
157
  ### Step 8: Permission-Aware Verification
158
158
 
@@ -4,7 +4,7 @@ description: Chorus Review workflow — approve/reject proposals, verify tasks,
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -64,7 +64,7 @@ Key responsibilities:
64
64
 
65
65
  When reviewing proposals, tasks, or an Idea's final aggregate code change, prefer spawning an independent reviewer sub-agent over reviewing manually:
66
66
 
67
- 1. **Try the reviewer first.** Spawn `chorus-proposal-reviewer` (for proposals), `chorus-task-reviewer` (for tasks), or `chorus-code-reviewer` (the final ship-time gateway over an Idea's aggregate code change, after its last task is verified — pass the `ideaUuid`; it posts its VERDICT on the **idea**) as a read-only sub-agent. **Use the blocking `subagent` tool** (it waits for the VERDICT and returns it) — you must wait for the VERDICT before proceeding. It posts a VERDICT comment with detailed findings.
67
+ 1. **Try the reviewer first.** Spawn `chorus-proposal-reviewer` (for proposals), `chorus-task-reviewer` (for tasks), or `chorus-code-reviewer` (the final ship-time gateway over an Idea's aggregate code change, after its last task is verified — pass the `ideaUuid`; it posts its VERDICT on the **idea**) as a read-only sub-agent. **Spawn it with the `subagent` tool and wait for its VERDICT** — you must wait before proceeding. The verdict is the VERDICT comment it posts on the entity, not the call's return value, so read the comment (step 2). It posts a VERDICT comment with detailed findings.
68
68
  2. **Read the VERDICT.** After the reviewer completes, call `chorus_get_comments` and find THIS round's `VERDICT:` comment — the one posted after your dispatch, not an older round's. There are exactly three possible outcomes:
69
69
  - **VERDICT: PASS** — No issues found. Approve (proposals) or mark AC passed and verify (tasks).
70
70
  - **VERDICT: PASS WITH NOTES** — Minor non-blocking notes. Still approve/verify. Notes are informational.
@@ -145,7 +145,7 @@ chorus_get_comments({ targetType: "proposal", targetUuid: "<proposal-uuid>" })
145
145
 
146
146
  #### A3.5: Independent Review
147
147
 
148
- Spawn `chorus-proposal-reviewer` per the [Review Strategy](#review-strategy) above — use the blocking `subagent` tool (it waits). Read its VERDICT comment before proceeding.
148
+ Spawn `chorus-proposal-reviewer` per the [Review Strategy](#review-strategy) above — spawn it with the `subagent` tool and wait for its VERDICT. Read its VERDICT comment before proceeding.
149
149
 
150
150
  #### A4: Approve or Reject
151
151
 
@@ -210,14 +210,14 @@ chorus_get_comments({ targetType: "task", targetUuid: "<task-uuid>" })
210
210
 
211
211
  #### B2.5: Independent Review
212
212
 
213
- Spawn `chorus-task-reviewer` per the [Review Strategy](#review-strategy) above — use the blocking `subagent` tool (it waits). After it completes, read its VERDICT:
213
+ Spawn `chorus-task-reviewer` per the [Review Strategy](#review-strategy) above — spawn it with the `subagent` tool and wait for its VERDICT. After it completes, read its VERDICT:
214
214
 
215
215
  - **VERDICT: PASS** or **PASS WITH NOTES** → proceed to B3 (mark AC) and B4 (verify).
216
216
  - **VERDICT: FAIL** → skip to B4 and **reopen** the task. Do NOT mark AC as passed.
217
217
 
218
218
  #### B2.6: Final Code-Review Gateway (after an Idea's LAST task is verified)
219
219
 
220
- When the task you just verified is the **last** task of its idea-rooted proposal, run the ship-time code-review gateway before the Idea's code is considered shipped. The the extension injects a reminder to spawn `chorus-code-reviewer` (gated by `enableCodeReviewer`, default on). Spawn it per the [Review Strategy](#review-strategy) — via the blocking `subagent` tool, passing the `ideaUuid` + round number. It reviews the Idea's **aggregate** code change across all its tasks — cross-task integration, architecture/convention consistency, security, regression/performance, feature-level test coverage — dimensions a single-task review cannot see — and posts one `VERDICT` comment on the **idea**.
220
+ When the task you just verified is the **last** task of its idea-rooted proposal, run the ship-time code-review gateway before the Idea's code is considered shipped. The the extension injects a reminder to spawn `chorus-code-reviewer` (gated by `enableCodeReviewer`, default on). Spawn it per the [Review Strategy](#review-strategy) — with the `subagent` tool, passing the `ideaUuid` + round number. It reviews the Idea's **aggregate** code change across all its tasks — cross-task integration, architecture/convention consistency, security, regression/performance, feature-level test coverage — dimensions a single-task review cannot see — and posts one `VERDICT` comment on the **idea**.
221
221
 
222
222
  - **VERDICT: PASS** / **PASS WITH NOTES** → the feature may ship.
223
223
  - **VERDICT: FAIL** → do not reopen the verified tasks; instead add new fix tasks to the approved proposal via `/skill:quick-dev` (`chorus_create_tasks` with `proposalUuid` set to the current approved proposal so the fix tasks attach to it). Group related small BLOCKERs by default; split only materially large or independently testable fixes. Require AC self-check, independent task review, and admin verification for every fix task. Re-run aggregate review only after every fix is successfully `done`; a failed or cancelled fix stops the loop and escalates. Bounded by `CHORUS_MAX_CODE_REVIEW_ROUNDS` (env, default 3; 0 = unlimited).
@@ -339,7 +339,7 @@ chorus_pm_update_document({ documentUuid: "<doc-uuid>", content: "Updated..." })
339
339
  - **Unblock the team** — Prioritize proposal reviews to keep PM and Developer work flowing
340
340
  - **Use delete sparingly** — Prefer closing over deleting; closing preserves history
341
341
  - **Document decisions** — Use comments to explain approval/rejection reasoning
342
- - **Verify between waves** — In parallel-subagent mode, verify tasks to `done` between waves to unblock downstream dependencies (and `subagent_manage close` finished workers to release their slots)
342
+ - **Verify between waves** — In wave mode, verify tasks to `done` between waves to unblock downstream dependencies; each worker's dispatch/run owns its own lifecycle, so there is no handle to close
343
343
 
344
344
  ---
345
345
 
@@ -4,7 +4,7 @@ description: Lightweight, Chorus-native local specs for Chorus PM workflows in P
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -4,7 +4,7 @@ description: Full-auto AI-DLC pipeline — from prompt to done. Automates the en
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.18.0"
7
+ version: "0.19.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -266,7 +266,7 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
266
266
  ```
267
267
  chorus_pm_submit_proposal({ proposalUuid: "<proposal-uuid>" })
268
268
  ```
269
- After this call, the extension nudges you to spawn `chorus-proposal-reviewer`. You MUST spawn it yourself via the blocking `subagent` tool (it waits for the VERDICT) — it is NOT auto-launched.
269
+ After this call, the extension nudges you to spawn `chorus-proposal-reviewer`. You MUST spawn it yourself with the `subagent` tool and wait for its VERDICT — it is NOT auto-launched.
270
270
 
271
271
  ---
272
272
 
@@ -274,7 +274,7 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
274
274
 
275
275
  Every gate in Phases 2, 4 and 4.5 follows the same three steps. They are written once here; the phases below only name their entity and their stage-specific actions.
276
276
 
277
- 1. **Spawn and wait.** Spawn the reviewer as a read-only sub-agent, then wait for it: spawn it via the blocking `subagent` tool, which waits for the reviewer to finish. Read the verdict from the reviewer's `VERDICT:` comment on the entity.
277
+ 1. **Spawn and wait.** Spawn the reviewer as a read-only sub-agent, then wait for it to finish. Read the verdict from the reviewer's `VERDICT:` comment on the entity — the verdict is that comment, not the dispatch's return value.
278
278
  2. **Read THIS round's VERDICT.** Call `chorus_get_comments` on the entity and find the `VERDICT:` comment posted **after your dispatch**, not an older round's. Do not advance the gate before you have read it.
279
279
  3. **No VERDICT for this round?** Check what the reviewer *did* post:
280
280
  - **A reported round limit, or any other explicit refusal to review** — a deliberate escalation to a human. STOP: do not respawn, do not self-review, do not post a VERDICT of your own.
@@ -286,7 +286,7 @@ Every gate in Phases 2, 4 and 4.5 follows the same three steps. They are written
286
286
 
287
287
  ### Phase 2: Proposal Review Loop
288
288
 
289
- After `chorus_pm_submit_proposal`, the extension nudges you to spawn `chorus-proposal-reviewer`. You MUST manually spawn it as a read-only sub-agent via the blocking `subagent` tool (it waits for the VERDICT). Wait for it to complete, then:
289
+ After `chorus_pm_submit_proposal`, the extension nudges you to spawn `chorus-proposal-reviewer`. You MUST manually spawn it as a read-only sub-agent with the `subagent` tool and wait for its VERDICT. Wait for it to complete, then:
290
290
 
291
291
  1. **Read the reviewer's VERDICT:**
292
292
  ```
@@ -336,7 +336,7 @@ After proposal approval, tasks exist in `open` status. Execute them in dependenc
336
336
 
337
337
  #### Primary: subagent parallel dispatch (wave-based)
338
338
 
339
- The `subagent` tool is **blocking** a parallel dispatch runs every worker in the wave to completion and returns their aggregated output in one call. There is no async spawn, no `agentId` to track, and no manual close. The chorus-pi extension auto-injects each worker's Chorus session UUID + workflow at `tool_call` time and closes the sessions when the dispatch returns.
339
+ The **dispatch shape and the run mode both depend on the installed implementation**: the bundled subagent takes one composite call (`subagent({ tasks: [...] })`, max 8 per call, concurrency 4) and is blocking (the call returns when every worker in the wave has exited); nicobailon `pi-subagents` takes **one single dispatch per worker** (its public tool rejects top-level `tasks`/`chain` before dispatch) and is **async/background by default** (each call returns a run receipt; completion arrives on the run notification / `bg_wait`). Either way there is no `agentId` to track and no manual close. The chorus-pi extension auto-injects each worker's Chorus session UUID + workflow at `tool_call` time and closes the sessions when the dispatch returns (blocking) or when the run settles (`subagent:async-complete` / `process-terminal`).
340
340
 
341
341
  ```
342
342
  wave = 1
@@ -352,32 +352,42 @@ loop:
352
352
  # Stuck -- tasks failed review and can't proceed
353
353
  break with escalation report
354
354
 
355
- # 2. Dispatch one chorus-worker per unblocked task in a SINGLE blocking
356
- # parallel call. Max 8 tasks per call (concurrency 4) — if the wave has
357
- # more than 8 ready tasks, split into batches of <=8 sequential calls.
355
+ # 2. Dispatch one chorus-worker per unblocked task as a single wave.
356
+ # Shape follows the installed implementation:
357
+ # bundled subagent ONE composite call (max 8 tasks/call,
358
+ # concurrency 4; a bigger wave splits into batches of <=8 calls)
359
+ # nicobailon → ONE single dispatch per worker, issued together
360
+ # (it rejects top-level `tasks`/`chain` before dispatch)
358
361
  # Pass only task + project UUIDs; the chorus-pi extension auto-injects the
359
362
  # session UUID + workflow into each worker's task at tool_call time.
360
363
  subagent({
361
364
  tasks: [
362
365
  { agent: "chorus-worker",
363
366
  task: "Your Chorus task UUID: {task.uuid}\nProject UUID: {project-uuid}\n\nImplement the task per its description and acceptance criteria. Read the task, proposal, and project documents for context." },
364
- // ... one entry per unblocked task, max 8
367
+ // ... one entry per unblocked task, max 8 (bundled subagent only)
365
368
  ]
366
369
  })
367
- # The call BLOCKS until EVERY worker in the wave finishes. Each worker follows
370
+ # ... or, under nicobailon `pi-subagents`, one call per worker in the wave:
371
+ subagent({ agent: "chorus-worker", task: "Your Chorus task UUID: {task.uuid}\n..." }) # worker 1
372
+ subagent({ agent: "chorus-worker", task: "Your Chorus task UUID: {task.uuid}\n..." }) # worker 2
373
+ # Wait for the whole wave to settle — the bundled composite call returns when
374
+ # every worker finishes; under nicobailon each call returns a run receipt, so
375
+ # wait on the run notifications / `bg_wait`. Each worker follows
368
376
  # the /skill:develop workflow: claim -> in_progress -> report -> self-check AC
369
377
  # -> submit_for_verify (leaving its task at to_verify).
370
- # For a single ready task, use single mode instead:
378
+ # For a single ready task, single mode is all you need:
371
379
  # subagent({ agent: "chorus-worker", task: "..." })
372
380
 
373
381
  # 3. Proceed to Phase 4 (verification) for this wave
374
382
  wave += 1
375
383
  ```
376
384
 
385
+ > Do **not** dispatch Chorus workers from a `workflowScript` — children created there are invisible to the extension's session hook, so they would get no Chorus session. One call per worker (or one bundled `tasks` composite) is the supported shape.
386
+
377
387
  **What each worker task needs:**
378
388
  - Task UUID + Project UUID
379
389
  - NO session UUID, NO workflow boilerplate -- the extension auto-injects via tool_call mutation
380
- - No `agentId` and no close step — the blocking call owns the worker's whole lifecycle
390
+ - No `agentId` and no close step — the dispatch owns the worker's whole lifecycle
381
391
 
382
392
 
383
393
  #### Fallback: Main Agent (sequential)
@@ -418,7 +428,7 @@ for each task in wave_tasks:
418
428
  continue
419
429
 
420
430
  # 2. Spawn chorus-task-reviewer (the extension nudges you; you must spawn it yourself)
421
- # Use the blocking `subagent` tool (it waits for the VERDICT before returning)
431
+ # Use the `subagent` tool and wait for its VERDICT comment
422
432
  subagent({ agent: "chorus-task-reviewer", task: "Review task <task-uuid>..." })
423
433
 
424
434
  # 3. Read task-reviewer VERDICT
@@ -467,7 +477,7 @@ Continue with remaining tasks -- do not halt the entire pipeline for one stuck t
467
477
 
468
478
  ### Phase 4.5: Code-Review Gateway (mandatory pre-ship)
469
479
 
470
- Once **every** task of the idea's proposal is verified (`done`) — i.e. Phase 3 finds no more unblocked tasks and all are terminal — run the final ship-time code-review gateway **before** declaring the Idea done and **before** the Phase 5b completion report. After the last task is verified, the extension nudges you to spawn the code-reviewer; you MUST spawn it yourself via the blocking `subagent` tool (it waits for the VERDICT).
480
+ Once **every** task of the idea's proposal is verified (`done`) — i.e. Phase 3 finds no more unblocked tasks and all are terminal — run the final ship-time code-review gateway **before** declaring the Idea done and **before** the Phase 5b completion report. After the last task is verified, the extension nudges you to spawn the code-reviewer; you MUST spawn it yourself with the `subagent` tool and wait for its VERDICT.
471
481
 
472
482
  ```
473
483
  # Spawn the code-reviewer for the IDEA (not a task). Determine the round