@chorus-aidlc/chorus-openclaw-plugin 0.17.2 → 0.18.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.
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Spec-mode resolver for the Chorus OpenClaw plugin.
3
+ *
4
+ * This is the TypeScript reimplementation of the canonical bash resolver
5
+ * `public/chorus-plugin/bin/resolve-spec-mode.sh` (which the bash ports copy
6
+ * byte-identically; the TS ports reimplement + ship a same-contract test). It
7
+ * mirrors the chorus-pi port's `resolveSpecMode` in
8
+ * `packages/chorus-pi/lib/lib.ts` and is the **single source of truth** for the
9
+ * spec mode on OpenClaw.
10
+ *
11
+ * OpenClaw has no SessionStart hook and no per-session context-injection
12
+ * channel, so nothing precomputes the mode into the agent's context. Instead:
13
+ * - the `/chorus` command calls `resolveSpecModeFromEnv` so the resolved mode
14
+ * is a user-visible surface (its one real runtime caller), and
15
+ * - the stage skills (proposal / develop / yolo / openspec-aware) resolve the
16
+ * SAME contract inline, pointing back at this file as the authoritative rule.
17
+ *
18
+ * `resolveSpecMode` itself is pure given injectable fs + execSync, so it can be
19
+ * unit-tested without touching the disk or PATH.
20
+ */
21
+ import { existsSync } from "node:fs";
22
+ import { execSync } from "node:child_process";
23
+ /**
24
+ * Resolve the active Chorus spec mode for a repo. Pure given injectable fs +
25
+ * execSync.
26
+ *
27
+ * Rule (per owner): an explicit `CHORUS_SPEC_MODE` wins; when unset, OpenSpec
28
+ * stays the default whenever it is usable (openspec/ dir + CLI, not disabled),
29
+ * and lite is the fallback only when OpenSpec is absent or disabled. An explicit
30
+ * `=openspec` that isn't usable fails fast (`specFail`).
31
+ */
32
+ export function resolveSpecMode(inputs, fs, execSync) {
33
+ const projectRoot = inputs.projectRoot || "";
34
+ // --- Is OpenSpec usable? (needs openspec/ dir + CLI on PATH + not disabled) ---
35
+ // enableOpenSpec toggle is checked BEFORE the legacy CHORUS_OPENSPEC_MODE, so a
36
+ // plugin-level opt-out wins the reason string (matches the bash resolver order).
37
+ let openspecDisabled = false;
38
+ let disabledReason = "";
39
+ if ((inputs.enableOpenSpec ?? "true") !== "true") {
40
+ openspecDisabled = true;
41
+ disabledReason = "enableOpenSpec userConfig=false (plugin-level opt-out)";
42
+ }
43
+ else if (inputs.openspecMode === "off") {
44
+ openspecDisabled = true;
45
+ disabledReason = "CHORUS_OPENSPEC_MODE=off (legacy opt-out)";
46
+ }
47
+ let openspecUsable = false;
48
+ let openspecUsableReason = "";
49
+ let openspecHint = "";
50
+ if (openspecDisabled) {
51
+ openspecUsableReason = disabledReason;
52
+ }
53
+ else if (!fs.existsSync(`${projectRoot}/openspec`)) {
54
+ openspecUsableReason = `no openspec/ directory at ${projectRoot}/openspec`;
55
+ openspecHint = "npm i -g @fission-ai/openspec && openspec init";
56
+ }
57
+ else if (!openspecCliPresent(execSync)) {
58
+ openspecUsableReason = "openspec/ directory present but `openspec` CLI not on PATH";
59
+ openspecHint = "npm i -g @fission-ai/openspec";
60
+ }
61
+ else {
62
+ openspecUsable = true;
63
+ openspecUsableReason = "openspec/ directory + openspec CLI both present";
64
+ }
65
+ // --- Resolve CHORUS_SPEC_MODE (unset and "" are treated the same, as in bash) ---
66
+ let specMode;
67
+ let specReason;
68
+ let specFail = "";
69
+ const raw = inputs.specMode ?? "";
70
+ switch (raw) {
71
+ case "lite":
72
+ specMode = "lite";
73
+ specReason = "explicit — Chorus-native lightweight specs in .chorus/specs/<slug>/";
74
+ break;
75
+ case "off":
76
+ specMode = "off";
77
+ specReason = "explicit — free-form, no spec artifact";
78
+ break;
79
+ case "openspec":
80
+ specMode = "openspec";
81
+ if (openspecUsable) {
82
+ specReason = `explicit; ${openspecUsableReason}`;
83
+ }
84
+ else if (openspecDisabled) {
85
+ specReason = `explicit, but OpenSpec is disabled: ${openspecUsableReason}`;
86
+ specFail = `config conflict — CHORUS_SPEC_MODE=openspec vs OpenSpec disabled (${openspecUsableReason}); re-enable OpenSpec or set CHORUS_SPEC_MODE=lite`;
87
+ }
88
+ else {
89
+ specReason = `explicit, but OpenSpec is not installed: ${openspecUsableReason}`;
90
+ specFail = `OpenSpec not usable (${openspecUsableReason})`;
91
+ }
92
+ break;
93
+ case "":
94
+ // Unset: OpenSpec is the default when usable; lite is the fallback otherwise.
95
+ if (openspecUsable) {
96
+ specMode = "openspec";
97
+ specReason = `default — ${openspecUsableReason}; set CHORUS_SPEC_MODE=lite for Chorus-native specs, =off to disable`;
98
+ }
99
+ else {
100
+ specMode = "lite";
101
+ specReason = `default — OpenSpec not usable (${openspecUsableReason}); using Chorus-native lightweight specs in .chorus/specs/<slug>/`;
102
+ }
103
+ break;
104
+ default:
105
+ // Unrecognized value: treat like unset (OpenSpec-if-usable, else lite).
106
+ if (openspecUsable) {
107
+ specMode = "openspec";
108
+ specReason = `CHORUS_SPEC_MODE='${raw}' unrecognized; falling back to default (${openspecUsableReason})`;
109
+ }
110
+ else {
111
+ specMode = "lite";
112
+ specReason = `CHORUS_SPEC_MODE='${raw}' unrecognized; OpenSpec not usable, defaulting to lite`;
113
+ }
114
+ }
115
+ const chorusOpenspecActive = specMode === "openspec" && specFail === "";
116
+ return {
117
+ specMode,
118
+ specReason,
119
+ specFail,
120
+ openspecUsable,
121
+ openspecUsableReason,
122
+ openspecHint,
123
+ chorusOpenspecActive,
124
+ };
125
+ }
126
+ function openspecCliPresent(execSync) {
127
+ try {
128
+ execSync("command -v openspec", { stdio: "ignore" });
129
+ return true;
130
+ }
131
+ catch {
132
+ return false;
133
+ }
134
+ }
135
+ /**
136
+ * Convenience wrapper that wires the real Node `fs.existsSync` + `child_process.execSync`
137
+ * and reads the spec-mode env vars off a process-env-shaped bag. This is the
138
+ * `/chorus` command's real runtime caller. Kept out of `resolveSpecMode` so the
139
+ * core stays pure/injectable for tests.
140
+ */
141
+ export function resolveSpecModeFromEnv(env, projectRoot) {
142
+ const fs = { existsSync: (p) => existsSync(p) };
143
+ const exec = (cmd, opts) => {
144
+ execSync(cmd, opts);
145
+ };
146
+ return resolveSpecMode({
147
+ specMode: env.CHORUS_SPEC_MODE,
148
+ openspecMode: env.CHORUS_OPENSPEC_MODE,
149
+ enableOpenSpec: env.CLAUDE_PLUGIN_OPTION_ENABLEOPENSPEC,
150
+ projectRoot,
151
+ }, fs, exec);
152
+ }
153
+ //# sourceMappingURL=spec-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-mode.js","sourceRoot":"","sources":["../src/spec-mode.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chorus-aidlc/chorus-openclaw-plugin",
3
- "version": "0.17.2",
3
+ "version": "0.18.0",
4
4
  "description": "OpenClaw plugin for Chorus AI-DLC collaboration platform — native MCP + SSE real-time events",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -49,6 +49,7 @@
49
49
  "!src/**/__tests__",
50
50
  "dist",
51
51
  "skills",
52
+ "bin/resolve-spec-mode.sh",
52
53
  "openclaw.plugin.json",
53
54
  "README.md"
54
55
  ]
@@ -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.17.2"
7
+ version: "0.18.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.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -321,18 +321,20 @@ The plugin bundles three independent **review skills**: `/proposal-reviewer`, `/
321
321
 
322
322
  Results are advisory — they do not hard-block approval, verification, or ship (the code-review gateway is behavioral — it does not change the Idea's stored status), but you should act on a FAIL by fixing the listed BLOCKERs before proceeding. For a code-review FAIL, the orchestrator invokes **quick-dev** (`/quick-dev`) to create new tasks on the original approved proposal; it does not reopen completed tasks or apply untracked fixes. Group related small BLOCKERs by default and split only materially large or independently testable fixes. Every fix task must pass AC self-check, independent task review, and admin verification. Re-run aggregate review only after all fixes are successfully `done`; a failed or cancelled fix stops the loop and escalates. Keep `maxCodeReviewRounds` authoritative.
323
323
 
324
- ### 6. Enable OpenSpec Mode (Optional)
324
+ ### 6. Spec mode: OpenSpec (default when usable) vs spec-lite (fallback)
325
325
 
326
- Opt-in spec-driven path: `/proposal`, `/develop`, `/yolo` write `proposal.md` / `design.md` / spec deltas on disk and mirror them into Chorus drafts. Fully optional free-form authoring works without it. The stage skills re-check the three activation signals inline (OpenClaw has no SessionStart hook): `CHORUS_OPENSPEC_MODE` ≠ `off`, an `openspec/` directory at the project root, and the `openspec` CLI on `PATH`.
326
+ Every PM authoring flow (`/proposal`, `/develop`, `/yolo`) runs in one **spec mode**. OpenClaw has **no SessionStart hook** to precompute it, so the stage skills resolve the mode by sourcing the resolver the plugin ships (`bin/resolve-spec-mode.sh`, byte-identical to the Claude Code copy and drift-guarded; `/chorus spec` prints the same result from the TS mirror `src/spec-mode.ts`). Resolution: an explicit `CHORUS_SPEC_MODE` (`lite`/`openspec`/`off`) wins; when unset, **OpenSpec is the default whenever it is usable** (`CHORUS_OPENSPEC_MODE` ≠ `off`, an `openspec/` directory at the project root, and the `openspec` CLI on `PATH`). When OpenSpec is absent or disabled, the mode falls back to **spec-lite** — a Chorus-native, git-tracked model with a durable local `.chorus/specs/<slug>/spec.md` per capability (never synced) plus dated per-change folders `<slug>/<YYYY-MM-DD>-<change-slug>/` of Chorus-typed docs mirrored 1:1 into Chorus (see the `/spec-lite` skill). `CHORUS_SPEC_MODE=off` selects free-form (no spec artifact).
327
327
 
328
- **When the user wants it on**, actually **enable it for them** run whichever steps are missing, don't just describe them:
328
+ In the OpenSpec path, `/proposal`, `/develop`, `/yolo` write `proposal.md` / `design.md` / spec deltas on disk and mirror them into Chorus drafts (see `/openspec-aware`).
329
+
330
+ **When the user wants OpenSpec on** (they saw spec-lite/off from `/chorus spec` and want the OpenSpec path), actually **enable it for them** — run whichever steps are missing, don't just describe them:
329
331
 
330
332
  ```bash
331
333
  npm i -g @fission-ai/openspec # 1. install the CLI if it's not on PATH (global, pure Node)
332
334
  openspec init --tools none # 2. scaffold the openspec/ directory
333
335
  ```
334
336
 
335
- OpenSpec has no OpenClaw integration (it's not in the `--tools` list), so `--tools none` is correct — Chorus's detection only needs the `openspec/` directory and the stage skills drive the CLI directly. There's no SessionStart banner on OpenClaw — the stage skills re-check the three signals inline, so once the directory and CLI are both present they fold in `/openspec-aware` automatically. To turn it off, set `CHORUS_OPENSPEC_MODE=off`.
337
+ OpenSpec has no OpenClaw integration (it's not in the `--tools` list), so `--tools none` is correct — Chorus's resolution only needs the `openspec/` directory + the CLI, and the stage skills drive the CLI directly. There's no SessionStart banner on OpenClaw — the stage skills re-resolve the mode by sourcing the shipped resolver, so once the directory and CLI are both present the mode resolves to `openspec` and they fold in `/openspec-aware` automatically. To turn OpenSpec off, set `CHORUS_OPENSPEC_MODE=off` — the mode then falls back to **spec-lite** (or set `CHORUS_SPEC_MODE=off` for free-form). `/chorus spec` always prints the resolved mode + reason.
336
338
 
337
339
  ---
338
340
 
@@ -456,7 +458,8 @@ This is the core overview skill. For stage-specific workflows, use:
456
458
  | **Development** | `/develop` | Claim Tasks, report work, manual session & sub-agent management |
457
459
  | **Review** | `/review` | Approve/reject Proposals, verify Tasks, project governance |
458
460
  | **Docs** | `/docs` | Consult the live Chorus documentation site to answer product-usage questions — UI workflow, agent/plugin setup, API/MCP, deployment, operations |
459
- | **OpenSpec mode** | `openspec-aware` | Opt-in **shared sub-procedure** invoked by `/proposal`, `/develop`, and `/yolo` whenever the user has the `openspec` CLI installed. Scaffolds `openspec/changes/<slug>/` on disk and mirrors files into Chorus document drafts via the `chorus-api.sh` wrapper. Runs an inline three-check detection (no SessionStart hook on OpenClaw). Skips silently in fallback mode. |
461
+ | **OpenSpec mode** | `openspec-aware` | **Shared sub-procedure** invoked by `/proposal`, `/develop`, `/yolo` when the resolved spec mode is a usable OpenSpec (the default when `openspec/` + CLI present and not disabled). Scaffolds `openspec/changes/<slug>/` on disk and mirrors files into Chorus document drafts via `chorus mcp call --arg-file` (`chorus-api.sh` wrapper as fallback). Resolves the whole spec-mode contract by sourcing the shipped `bin/resolve-spec-mode.sh` (no SessionStart hook on OpenClaw). No-op when the mode isn't a usable OpenSpec. |
462
+ | **spec-lite mode** | `spec-lite` | **Shared sub-procedure** and the fallback when OpenSpec isn't usable (or `CHORUS_SPEC_MODE=lite`). Durable local `.chorus/specs/<slug>/spec.md` (never synced) + dated per-change folders of Chorus-typed docs mirrored 1:1 into Chorus via `--arg-file`. No CLI/validation/archive. |
460
463
 
461
464
  ### Getting Started
462
465
 
@@ -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.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -4,7 +4,7 @@ description: Final ship-time review of an Idea's aggregate code change — the w
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -61,6 +61,7 @@ Read each task's work report (in its comments) — the developers describe what
61
61
  4. **Regression risk / impact on untouched areas / performance** — does the change break or degrade code no single task owned? N+1s, hot-path cost, shared-state contention.
62
62
  5. **Feature-level test coverage adequacy** — across the whole feature, are integration seams and end-to-end paths tested, or only per-task units? Gaps between tasks.
63
63
  6. **Code soundness, simplicity, correctness** — is the aggregate change correct, reasonably simple, free of obvious defects read as one body of work.
64
+ 7. **Intent alignment (whole-feature)** — Also read the Idea's resolved elaboration (`chorus_get_elaboration`); using ONLY human-authored intent (Idea body + human-answered elaboration + human-authored comments; agent-authored entries are audit context, not intent) as the baseline, judge whether the aggregate change still serves the original intent. Flag scope creep, dropped requirements, or intent missed despite passing AC as a **BLOCKER**, unless a cited human entry / human override authorizes it.
64
65
 
65
66
  **Step 4: Run feature-level build/test.** Run the project's declared commands. A broken build or failing tests is an automatic FAIL. Record command + exit code + relevant output. Results are context — verify each dimension independently.
66
67
 
@@ -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.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -25,7 +25,7 @@ Developer Agents take Tasks created by PM Agents (via `/proposal`) and turn them
25
25
  claim --> in_progress --> report work --> self-check AC --> submit for verify --> reviewer --> Admin /review
26
26
  ```
27
27
 
28
- For multi-task execution, OpenClaw runs **sequential waves** (the main agent works tasks in dependency order) — see [Wave-Based Execution](#wave-based-execution-on-openclaw) below.
28
+ For multi-task execution, dispatch **one sub-agent per unblocked task** with `sessions_spawn` (whole wave in one message), falling back to **sequential waves** (the main agent works tasks in dependency order) when `sessions_spawn` is unavailable or workers fail repeatedly — see [Wave-Based Execution](#wave-based-execution-on-openclaw) below.
29
29
 
30
30
  ---
31
31
 
@@ -151,8 +151,10 @@ Each task and proposal includes a `commentCount` field — use it to decide whic
151
151
  > **⛔ Do not** call `chorus_pm_update_document` directly from the MCP harness with a hand-typed `content` field in OpenSpec mode. The local file is the source of truth; agent-typed content drifts and burns tokens (`openspec-aware` §2 Rule 1).
152
152
  >
153
153
  > When the LAST task of an OpenSpec idea is verified, run the archive flow yourself (`openspec-aware` §3.9): run `openspec archive <slug> --yes`, then mirror each emitted `openspec/specs/<capability>/spec.md` back via §3.8. **OpenClaw has no PostToolUse hook to remind you** — check after each verify whether the just-verified task was the last of its idea, and if so trigger the archive flow yourself.
154
+
155
+ > **Document update flow (spec-lite mode):** if the originating proposal `description` contains a locator line `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/`, the `prd` / `tech_design` / … Documents are **persistent mirrors** of the Chorus-typed docs in that **dated change folder**. Load the `spec-lite` skill. Locate the dated folder from the locator line (not by title/type). Edit those `<type>.md` files in place and update the capability's durable `.chorus/specs/<slug>/spec.md` in place too — but **`spec.md` is never mirrored** (local only, no ids). Tick `- [ ]` acceptance points, then re-mirror each edited dated-folder file via `chorus mcp call chorus_pm_update_document … --arg-file content=.chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/<type>.md` against its `documentUuid` (each update auto-increments the Document version = its modification history; `chorus-api.sh mcp-tool …` fallback when `chorus` not on `PATH`), with `chorus_check_response` halting on error. **Git history is the audit trail** (`git log -- .chorus/specs/<slug>/`) — no archive flow, no changelog section. (OpenClaw resolves the spec mode inline; see `openspec-aware` §1.)
154
156
  >
155
- > In the no-OpenSpec fallback (no slug line, or no `openspec` CLI), edit the Document content directly via the existing MCP tool with no wrapper, no local file step.
157
+ > In the no-OpenSpec, no-spec-lite fallback (no locator line, or resolved mode = free-form), edit the Document content directly via the existing MCP tool with no wrapper, no local file step.
156
158
 
157
159
  ### Step 5: Start Working
158
160
 
@@ -250,12 +252,12 @@ Obtain an independent VERDICT before the task is verified:
250
252
  ```
251
253
  chorus_get_comments({ targetType: "task", targetUuid: "<task-uuid>" })
252
254
  ```
253
- Find the most recent comment containing `VERDICT:`:
255
+ Find THIS round's `VERDICT:` comment the one posted after your dispatch, not an older round's:
254
256
  - **VERDICT: PASS** — All AC verified, no issues. Proceed to admin verification.
255
257
  - **VERDICT: PASS WITH NOTES** — All AC verified, minor notes. Proceed to admin verification (notes are non-blocking).
256
258
  - **VERDICT: FAIL** — BLOCKERs found. Do NOT verify. Fix the BLOCKERs listed in the reviewer's comment, then resubmit (Step 9).
257
259
 
258
- If you spawned a sub-agent and no new `VERDICT:` comment appears after it returns, it exhausted its turn budget. Respawn it ONCE with a concise-budget hint: *"Stay within turn budget. Skip deep verification. Fetch task/proposal/comments, run only the core tests, and post your VERDICT within the first 12 turns."* If the second attempt still produces no VERDICT, fall back to reviewing manually (Step 8.5 fallback) and post the VERDICT yourself.
260
+ 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 fall back to reviewing manually (Step 8.5 fallback) and post the VERDICT yourself. **Absence is never a PASS.**
259
261
 
260
262
  > **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 — run the ship-time code-review gateway before declaring the Idea done. Inline (no hook on OpenClaw), same mechanism as Step 8.5: spawn a sub-agent via `sessions_spawn` whose `task` tells it to **invoke the `/code-reviewer` skill** against the idea (pass the `ideaUuid` + round number), and wait for it; fallback is a read-only self-review following the `/code-reviewer` procedure. It reviews the Idea's **aggregate** code change (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 the **quick-dev** workflow (`/quick-dev`): `chorus_create_tasks` with `proposalUuid` set to the **current approved proposal** so the fix tasks attach to it (do not reopen old tasks). Group related small BLOCKERs into one cohesive task by default; split only materially large or independently testable fixes. Each fix task must self-check its acceptance criteria and pass independent task review plus admin verification. Re-run the gateway only after every fix task is successfully `done`; if there is a failed or cancelled fix task, stop and escalate instead. Advisory/behavioral. Run it **before** any idea-completion report.
261
263
 
@@ -301,9 +303,9 @@ To keep a long-running session visible/active, send `chorus_session_heartbeat({
301
303
 
302
304
  ## Wave-Based Execution on OpenClaw
303
305
 
304
- > **OpenClaw difference:** OpenClaw has **no Agent Teams / `TeamCreate` primitive**. The Claude Code plugin can spawn a parallel team per wave; on OpenClaw you (the main agent) execute tasks **sequentially** in dependency order. This is slower than parallel teams but completes the same pipeline.
306
+ > **OpenClaw difference:** there is no team or group object to create. Parallelism, where available, comes from dispatching **one sub-agent per unblocked task** with OpenClaw's own `sessions_spawn` tool, issuing the whole wave in a single message — see §"Optional: sub-agent dispatch" below, which also covers the manual session instructions workers need (no SubagentStart hook here). When `sessions_spawn` is unavailable or workers fail repeatedly, you (the main agent) execute tasks **sequentially** in dependency order. That is slower but completes the same pipeline.
305
307
 
306
- ### Sequential wave loop
308
+ ### Sequential wave loop (fallback, always safe)
307
309
 
308
310
  ```
309
311
  loop:
@@ -332,11 +334,11 @@ loop:
332
334
 
333
335
  > **Critical:** `to_verify` does NOT resolve dependencies — only `done` or `closed` does. A task must be **verified to `done`** (by an Admin, or by you if you hold `task:admin`) before its dependents become unblocked. If you lack `task:admin`, submit each task for verify and ask the project's admin to verify between waves, then re-run `chorus_get_unblocked_tasks`.
334
336
 
335
- > **Claude-Code-only optimization (degrades to sequential here):** under the Claude Code plugin, each wave can be dispatched in parallel via `TeamCreate` + per-task sub-agents. OpenClaw has no such primitive, so the loop above runs serially. Do NOT attempt to call `TeamCreate` on OpenClaw it does not exist.
337
+ > **Parallel form:** to run a wave in parallel, dispatch one sub-agent per unblocked task in a single message (`sessions_spawn`) instead of the serial `for` loop above, then wait for the wave and verify. Everything else in the loop is unchanged.
336
338
 
337
339
  ### Optional: sub-agent dispatch
338
340
 
339
- If your OpenClaw host *does* support spawning worker sub-agents (not Agent Teams, just generic sub-agents), you may hand each a task. Because there is no SubagentStart hook, the worker prompt **must** include the manual session instructions explicitly:
341
+ If your OpenClaw host *does* support spawning worker sub-agents (`sessions_spawn`), you may hand each a task — one per unblocked task, all dispatched in one message so the wave runs in parallel. Because there is no SubagentStart hook, the worker prompt **must** include the manual session instructions explicitly:
340
342
 
341
343
  ```
342
344
  Your Chorus task UUID: <task-uuid>
@@ -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.17.2"
7
+ version: "0.18.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.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -1,61 +1,73 @@
1
1
  ---
2
2
  name: openspec-aware
3
- description: Opt-in OpenSpec-mode authoring for Chorus PM workflows on OpenClaw. Runs inline three-check detection for the local `openspec` CLI, scaffolds `openspec/changes/<slug>/` on disk, and mirrors Markdown files into Chorus document drafts via `chorus mcp call --arg-file` (bash `chorus-api.sh` wrapper as fallback). Required reading for the proposal, develop, and yolo skills whenever the user has the `openspec` CLI installed.
3
+ description: OpenSpec-mode authoring for Chorus PM workflows on OpenClaw — the default whenever OpenSpec is usable. Resolves the whole spec-mode contract (lite/openspec/off) by sourcing the plugin's shipped `bin/resolve-spec-mode.sh` (no SessionStart hook on OpenClaw), scaffolds `openspec/changes/<slug>/` on disk, and mirrors Markdown files into Chorus document drafts via `chorus mcp call --arg-file` (bash `chorus-api.sh` wrapper as fallback). When OpenSpec isn't usable the mode resolves to spec-lite (see the spec-lite skill). Required reading for the proposal, develop, and yolo skills.
4
4
  license: AGPL-3.0
5
5
  metadata:
6
6
  author: chorus
7
- version: "0.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
11
11
 
12
12
  # OpenSpec-aware Authoring (OpenClaw plugin)
13
13
 
14
- This skill is a **shared sub-procedure** invoked by the Chorus stage skills (proposal, develop, yolo) whenever the user wants spec-driven authoring through the [OpenSpec CLI](https://github.com/Fission-AI/OpenSpec). It is opt-in:
14
+ This skill is a **shared sub-procedure** invoked by the Chorus stage skills (proposal, develop, yolo) when the resolved spec mode is a **usable OpenSpec** — spec-driven authoring through the [OpenSpec CLI](https://github.com/Fission-AI/OpenSpec):
15
15
 
16
- - Activates when **all three** signals hold (see §1): `CHORUS_OPENSPEC_MODE` is not `off`, an `openspec/` directory exists at the project root, and the `openspec` CLI is on `PATH`.
17
- - Otherwise the calling skill falls back to its existing free-form behavior.
16
+ - Activates when the resolved spec mode is a **usable OpenSpec** (see §1): `CHORUS_SPEC_MODE=openspec` *or* unset, **and** `CHORUS_OPENSPEC_MODE` not `off`, an `openspec/` directory at the project root, and the `openspec` CLI on `PATH`.
17
+ - Otherwise the calling skill follows the resolved mode **spec-lite** (the default when OpenSpec isn't usable) or free-form (`CHORUS_SPEC_MODE=off`).
18
+
19
+ > **See also — `spec-lite` (the lightweight fallback):** OpenSpec (this skill) stays the default whenever it is usable. When OpenSpec is absent or disabled — or `CHORUS_SPEC_MODE=lite` — the mode resolves to **spec-lite**: a durable local `.chorus/specs/<slug>/spec.md` (never synced) + per-change dated folders `<slug>/<YYYY-MM-DD>-<change-slug>/` of Chorus-typed docs mirrored 1:1 into Chorus via the same `--arg-file` transport. See the `spec-lite` skill.
18
20
 
19
21
  > **Tool namespace:** Chorus MCP tools are exposed under a `chorus__` prefix on OpenClaw (e.g. `chorus__chorus_pm_create_proposal`). Bare names are used in prose for readability — prepend `chorus__` when invoking the MCP tools directly. **Document-mirror calls do NOT go through the MCP harness at all** — they go through the `chorus` CLI (`chorus mcp call`, preferred) or the `chorus-api.sh` wrapper (fallback) (see §2 Rule 1), which talk to the Chorus MCP endpoint over HTTP using your API key, independent of the `chorus__` namespacing.
20
22
 
21
23
  ---
22
24
 
23
- ## §1. Detection — run inline, every time (no SessionStart hook on OpenClaw)
25
+ ## §1. Resolve the spec mode — run the shipped resolver, every time (no SessionStart hook on OpenClaw)
24
26
 
25
- > **OpenClaw difference:** the Claude Code plugin precomputes `CHORUS_OPENSPEC_ACTIVE` once in a SessionStart hook and injects it into context. **OpenClaw does not run that hook.** You MUST compute activeness yourself, inline, the moment you reach this skill from a stage skill. Do not look for an injected `CHORUS_OPENSPEC_ACTIVE` value — it will not exist on OpenClaw.
27
+ > **OpenClaw difference:** the Claude Code / Codex / Pi ports precompute the spec mode once (in a SessionStart hook or an extension handler) and inject a `## Spec Mode` value into context. **OpenClaw does none of that.** You MUST resolve the mode yourself, at the moment you reach this skill from a stage skill. Do not look for an injected `CHORUS_OPENSPEC_ACTIVE` / `## Spec Mode` value — it will not exist on OpenClaw. Resolve the **whole** contract (lite / openspec / off), not just "is OpenSpec active".
28
+ >
29
+ > **Never hand-roll the rule in this Markdown.** The plugin ships the *canonical* resolver — `bin/resolve-spec-mode.sh`, byte-identical to the Claude Code copy and enforced so by `test-resolver-drift.sh` — precisely so this skill can **source** it instead of reproducing it. A prose copy of the rule drifts from the real one (it has); a sourced script cannot.
26
30
 
27
- Compute the value with the **three checks**. `CHORUS_OPENSPEC_ACTIVE` is `1` only when **all three** hold:
31
+ **The rule:** an explicit `CHORUS_SPEC_MODE` (`lite`/`openspec`/`off`) wins; when unset, **OpenSpec is the default whenever it is usable** — else **spec-lite**. OpenSpec is *usable* only when **all three** hold:
28
32
 
29
- 1. `CHORUS_OPENSPEC_MODE` is **not** set to `off` (explicit opt-out wins).
33
+ 1. OpenSpec is not disabled (`CHORUS_OPENSPEC_MODE` **not** `off`; the `enableOpenSpec` plugin toggle not `false`).
30
34
  2. The project root contains an `openspec/` directory (i.e. someone ran `openspec init` here).
31
35
  3. The `openspec` CLI is on `PATH`.
32
36
 
33
- Both signals (2) and (3) are required because the OpenSpec authoring path needs the working directory **and** the CLI — having one without the other leaves the workflow unrunnable. If signal (2) holds but (3) does not, surface a hint to the user — "OpenSpec repo detected — install with: `npm i -g @fission-ai/openspec`" — rather than silently choosing free-form.
37
+ Both signals (2) and (3) are required because the OpenSpec authoring path needs the working directory **and** the CLI — having one without the other leaves the workflow unrunnable. When OpenSpec is *requested but usable=false* (signal 2 holds but 3 does not, say), surface a hint to the user — "OpenSpec repo detected — install with: `npm i -g @fission-ai/openspec`".
34
38
 
35
- ### Inline detection block (run this)
39
+ ### Resolution block (run this)
36
40
 
37
41
  ```bash
38
- # Run the three checks directly. PROJECT_DIR is your project root
39
- # (OpenClaw does not export CLAUDE_PROJECT_DIR default to $PWD).
40
- PROJECT_DIR="${PWD}"
41
- if [ "${CHORUS_OPENSPEC_MODE:-}" = "off" ]; then
42
- CHORUS_OPENSPEC_ACTIVE=0
43
- elif [ ! -d "${PROJECT_DIR}/openspec" ]; then
44
- CHORUS_OPENSPEC_ACTIVE=0
45
- elif ! openspec --version >/dev/null 2>&1; then
46
- CHORUS_OPENSPEC_ACTIVE=0 # consider surfacing the install hint to the user
47
- else
48
- CHORUS_OPENSPEC_ACTIVE=1
49
- fi
50
- echo "CHORUS_OPENSPEC_ACTIVE=$CHORUS_OPENSPEC_ACTIVE"
42
+ # PROJECT_ROOT is your project root OpenClaw does not export
43
+ # CLAUDE_PROJECT_DIR, so default to $PWD. The resolver reads it.
44
+ PROJECT_ROOT="${PWD}"
45
+
46
+ # Locate the resolver the plugin ships. Covers the npm install, a global npm
47
+ # install, and a linked dev checkout; the first hit wins.
48
+ RESOLVER=""
49
+ for candidate in \
50
+ "$PWD/node_modules/@chorus-aidlc/chorus-openclaw-plugin/bin/resolve-spec-mode.sh" \
51
+ "$(npm root -g 2>/dev/null)/@chorus-aidlc/chorus-openclaw-plugin/bin/resolve-spec-mode.sh" \
52
+ "$PWD/packages/openclaw-plugin/bin/resolve-spec-mode.sh"
53
+ do
54
+ [ -f "$candidate" ] && { RESOLVER="$candidate"; break; }
55
+ done
56
+
57
+ # shellcheck source=/dev/null
58
+ . "$RESOLVER" # sets SPEC_MODE, SPEC_REASON, SPEC_FAIL, OPENSPEC_HINT, CHORUS_OPENSPEC_ACTIVE
59
+ echo "SPEC_MODE=$SPEC_MODE CHORUS_OPENSPEC_ACTIVE=$CHORUS_OPENSPEC_ACTIVE SPEC_FAIL=${SPEC_FAIL} REASON=${SPEC_REASON}"
51
60
  ```
52
61
 
62
+ If `RESOLVER` came out empty (the loop found nothing), **halt** and ask the user to run `/chorus spec` — which resolves the same contract from the plugin's `src/spec-mode.ts` — and paste the result. Do **not** substitute your own detection.
63
+
53
64
  Branch on the result:
54
65
 
66
+ - `SPEC_FAIL` non-empty (explicit `CHORUS_SPEC_MODE=openspec` that can't be honored — either a config conflict or OpenSpec not installed; `OPENSPEC_HINT` carries the install command when it is merely missing) → the caller MUST **halt** and surface it verbatim. Do not silently fall back.
55
67
  - `CHORUS_OPENSPEC_ACTIVE=1` → follow §3 (OpenSpec authoring).
56
- - `CHORUS_OPENSPEC_ACTIVE=0` → return to the calling skill's free-form path. **Do not** scaffold `openspec/changes/`. **Do not** add the slug line to the proposal description.
68
+ - Otherwise (`SPEC_MODE=lite` or `off`, no fail) this skill is a **no-op**; return to the calling skill, which follows the resolved mode: **spec-lite** (the default when OpenSpec isn't usable — see the `spec-lite` skill) or free-form (`off`). **Do not** scaffold `openspec/changes/`. **Do not** add the slug line to the proposal description.
57
69
 
58
- Run this detection inline whenever proposal / develop / yolo reference this skill. There is no host-injected value to read on OpenClaw; recomputing the three checks is the contract.
70
+ Run this resolution whenever proposal / develop / yolo reference this skill. There is no host-injected value to read on OpenClaw but "resolve it yourself" means *run the shipped resolver*, never re-derive the rule from this document's prose.
59
71
 
60
72
  ---
61
73
 
@@ -363,15 +375,15 @@ When the trigger holds, you perform the archive:
363
375
 
364
376
  ---
365
377
 
366
- ## §4. Fallback authoring (no openspec)
378
+ ## §4. Fallback authoring (resolved mode is not a usable OpenSpec)
367
379
 
368
- When the §1 detection puts the agent in fallback mode (`CHORUS_OPENSPEC_ACTIVE=0`), this skill is a **no-op**. Return to the calling skill's free-form path:
380
+ When the §1 resolution does not yield a usable OpenSpec (`CHORUS_OPENSPEC_ACTIVE=0`, no `SPEC_FAIL`), this skill is a **no-op** return to the calling skill, which follows the resolved mode: **spec-lite** (the default when OpenSpec isn't usable — see the `spec-lite` skill) or free-form (`CHORUS_SPEC_MODE=off`). From this skill's side, regardless of which:
369
381
 
370
382
  - No `openspec/changes/` folder is created or referenced.
371
383
  - No `OpenSpec change slug: …` line is added to the proposal description.
372
- - Document drafts are authored via direct MCP `chorus_pm_add_document_draft` calls with inline `content` — same as before this skill existed.
373
- - Rule 1 (wrapper-only mirror) does not apply — there is no local file source of truth.
374
- - The §3.9 archive flow does nothing (no slug no archive).
384
+ - The §3.9 archive flow does nothing (no OpenSpec slug no archive).
385
+
386
+ In **spec-lite** the caller mirrors the dated-folder docs under `.chorus/specs/<slug>/` via the same `--arg-file` transport + Rule 1 / Rule 2 (that is the `spec-lite` skill's job). In **free-form** (`off`) there is no local file source of truth, so document drafts are authored via direct MCP `chorus_pm_add_document_draft` calls with inline `content` — same as before this skill existed, and Rule 1 (file-fill mirror) does not apply.
375
387
 
376
388
  ---
377
389
 
@@ -455,9 +467,9 @@ This is project-wide policy: no silent errors.
455
467
 
456
468
  When invoked from a stage skill (proposal / develop / yolo):
457
469
 
458
- 1. **Run the §1 inline three-check detection yourself** (no SessionStart hook on OpenClaw). Compute `CHORUS_OPENSPEC_ACTIVE` from: `CHORUS_OPENSPEC_MODE != off` + `openspec/` dir present + `openspec` CLI on PATH.
459
- 2. If `CHORUS_OPENSPEC_ACTIVE=0` → return to caller's free-form path (§4).
460
- 3. Otherwise:
470
+ 1. **Run the §1 resolution yourself** (no SessionStart hook on OpenClaw) by sourcing the shipped `bin/resolve-spec-mode.sh`. It resolves the whole contract (lite / openspec / off); do NOT hand-roll an OpenSpec-only check or re-derive the rule from prose.
471
+ 2. If `SPEC_FAIL` is set (explicit `CHORUS_SPEC_MODE=openspec` unusable) → **halt** and surface it. If `CHORUS_OPENSPEC_ACTIVE=0` (resolved mode = spec-lite or free-form) no-op; return to the caller per the resolved mode (§4).
472
+ 3. Otherwise (`CHORUS_OPENSPEC_ACTIVE=1`):
461
473
  a. Pick `$SLUG` (§3.1).
462
474
  b. `openspec new change "$SLUG"` (§3.2).
463
475
  c. Author `proposal.md`, `design.md`, `specs/<capability>/spec.md` (§3.2–§3.3). Mix `ADDED` / `MODIFIED` / `REMOVED` / `RENAMED` blocks as needed; remember `MODIFIED` overwrites the whole Requirement.
@@ -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.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -106,6 +106,14 @@ Guidance: start narrow. If a single owner can hold the whole feature in their he
106
106
 
107
107
  ---
108
108
 
109
+ ## Replying to the agent who woke you (advisory)
110
+
111
+ When an agent wakes a peer on a shared idea or task — an orchestrator dispatching a worker, or any agent `@mention`-ing another — the wake surfaces the **waker's live session anchor**: a note naming the waking agent and telling the woken peer that the waker has an open conversation on this idea. If you are the woken peer, **reply on the same idea/task resource** (comment there rather than opening a brand-new session) and your reply lands back in the waker's existing live session, keeping the collaboration on one thread instead of scattering into a fresh one.
112
+
113
+ This is **advisory, not routing.** There is no automatic server subscription and nothing is force-delivered — replying on the shared resource is simply *where a reply lands* (via the existing return path), not a guaranteed channel. When the waker's origin is **offline** at wake time, no live anchor is surfaced and the exchange degrades to **notify-only**: the reply reaches the waker as an ordinary notification it picks up on its next turn. Only idea/theme-anchored wakes carry this anchor; ad-hoc wakes with no shared idea do not.
114
+
115
+ ---
116
+
109
117
  ## Reversed-Conversation gates (you never auto-ship)
110
118
 
111
119
  Chorus is **AI proposes, humans verify**. As orchestrator you enforce that, you do not bypass it:
@@ -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.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -71,7 +71,9 @@ Elaboration resolved --> Create Proposal --> Add drafts --> Validate --> Submit
71
71
 
72
72
  ### Step 1: Create an Empty Proposal
73
73
 
74
- **Recommended approach:** Create the proposal container first without any drafts, then incrementally add document and task drafts one by one.
74
+ **Resolve the spec mode (Step 1.5) BEFORE this create.** In OpenSpec and spec-lite modes the container's `description` MUST carry a locator line (`OpenSpec change slug: <slug>` or `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/`), and `description` can only be set at creation — decide the mode + slug/dated-path first and include that line in this single call. Do NOT create a bare container and then realize you needed it. Free-form mode omits any locator line. (OpenClaw has no SessionStart hook — you resolve the mode inline here; see Step 1.5.)
75
+
76
+ **Recommended approach:** Create the proposal container first (with the mode's locator line in `description` when applicable), then incrementally add document and task drafts one by one.
75
77
 
76
78
  ```
77
79
  chorus_pm_create_proposal({
@@ -87,19 +89,20 @@ chorus_pm_create_proposal({
87
89
 
88
90
  > **A theme cannot be a proposal input** — `chorus_pm_create_proposal` rejects any input idea with `isContainer = true`. Derive a child idea from the theme and write the proposal on the child instead. (See the theme-ideas section of the `/idea` skill.)
89
91
 
90
- ### Step 1.5: Detect OpenSpec mode
92
+ ### Step 1.5: Select spec mode
91
93
 
92
- Before authoring document drafts, **load the `openspec-aware` skill** and run its **§1 inline detection** (three checks `CHORUS_OPENSPEC_MODE != "off"`, an `openspec/` directory at the project root, and the `openspec` CLI on `PATH`).
94
+ Resolve the spec mode **inline** — OpenClaw has no SessionStart hook to precompute it, so you compute the **whole** contract yourself (not just "is OpenSpec active"), every time you reach this step. Load the `openspec-aware` skill and run its **§1 resolution block (which sources the plugin's shipped `bin/resolve-spec-mode.sh`)**, which yields one of `lite` / `openspec` / `off` (the canonical resolver, byte-identical to the Claude Code copy; `/chorus spec` prints the same result from the TS mirror `src/spec-mode.ts`). The rule: an explicit `CHORUS_SPEC_MODE` (`lite`/`openspec`/`off`) wins; when unset, **OpenSpec is the default whenever usable** (`CHORUS_OPENSPEC_MODE` `off`, an `openspec/` directory at the project root, and the `openspec` CLI on `PATH`), else **spec-lite**.
93
95
 
94
- > **OpenClaw note:** there is no Claude Code SessionStart hook to precompute `CHORUS_OPENSPEC_ACTIVE`. You must run the three checks yourself, inline, every time you reach this step. See `openspec-aware` §1.
96
+ - If the resolution says the mode **cannot be honored** (explicit `CHORUS_SPEC_MODE=openspec` but OpenSpec unusable config-conflict or not-installed), **halt** and surface it; do not silently fall back.
97
+ - Otherwise branch on the resolved mode:
95
98
 
96
- Branch on the result:
99
+ - **resolved = spec-lite** (the default when OpenSpec isn't usable, or explicit `CHORUS_SPEC_MODE=lite`) → load the `spec-lite` skill and follow it: pick `$SLUG` (a **capability**, not one change). Ensure the durable `.chorus/specs/<slug>/spec.md` exists (local-only, **no Chorus ids**; use the `spec-lite` skill's inline durable-spec template) and update it in place. Create this change's **dated folder** `.chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/` with its **synced** Chorus-typed docs (`prd.md` primary, optional `tech_design.md`…; use the `spec-lite` skill's inline dated-folder document template). Put the literal locator line `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/` in the **Step 1 create** `description`, then mirror **each** dated-folder `<type>.md` to its persistent Document (`chorus_pm_add_document_draft --arg-file` first time, `chorus_pm_update_document --arg-file` after) via `chorus mcp call … --arg-file content=.chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/<type>.md` (fallback = `chorus-api.sh mcp-tool …` when `chorus` is not on `PATH`). **`spec.md` is never mirrored.** Skip Step 2 below. (Tasks via `chorus_pm_add_task_draft`; no `tasks.md`.)
97
100
 
98
- - **OpenSpec active (all three checks pass)** → follow `openspec-aware` §3. Pick `$SLUG`, scaffold `openspec/changes/<slug>/`, author `proposal.md` / `design.md` / `specs/<capability>/spec.md` locally, then create the proposal container (Step 1 above) with the literal line `OpenSpec change slug: <slug>` in `description`, and mirror each local file into a document draft.
101
+ - **resolved = OpenSpec** (unset with `openspec/` + CLI present and not disabled, or explicit `CHORUS_SPEC_MODE=openspec` that is usable) → follow `openspec-aware` §3. Pick `$SLUG`, scaffold `openspec/changes/<slug>/`, author `proposal.md` / `design.md` / `specs/<capability>/spec.md` locally, then put the literal line `OpenSpec change slug: <slug>` in the **Step 1 create** `description`, and mirror each local file into a document draft.
99
102
 
100
103
  > **⛔ Mandatory in OpenSpec mode:** mirror calls fill `content` from the local file — prefer `chorus mcp call … --arg-file content=<file>`, falling back to the `chorus-api.sh` wrapper with `json_encode_file` when `chorus` is not on `PATH` — see `openspec-aware` §3.6. Do **not** call `chorus_pm_add_document_draft` directly from the MCP harness with a hand-typed `content` field. Re-typing thousands of lines through the LLM burns 20k+ content tokens per proposal and breaks byte-equality with the local source of truth (`openspec-aware` §2 Rule 1 explains the full reasoning). Skip Step 2 below when in OpenSpec mode — the file-fill flow in `openspec-aware` §3.6 replaces it for documents.
101
104
 
102
- - **OpenSpec inactive (any check fails, or `CHORUS_OPENSPEC_MODE=off`)** → proceed with Step 2 unchanged. Author drafts inline as free-form Markdown via direct MCP `chorus_pm_add_document_draft`.
105
+ - **resolved = free-form** (explicit `CHORUS_SPEC_MODE=off`) → proceed with Step 2 unchanged. Author drafts inline as free-form Markdown via direct MCP `chorus_pm_add_document_draft`.
103
106
 
104
107
  ### Step 2: Add Document Drafts
105
108
 
@@ -236,11 +239,11 @@ Obtain an independent VERDICT before considering the proposal ready for Admin ap
236
239
  ```
237
240
  chorus_get_comments({ targetType: "proposal", targetUuid: "<proposal-uuid>" })
238
241
  ```
239
- Find the most recent comment containing `VERDICT:`:
242
+ Find THIS round's `VERDICT:` comment the one posted after your dispatch, not an older round's:
240
243
  - **PASS** / **PASS WITH NOTES** — proceed; an Admin can approve (notes are non-blocking).
241
244
  - **FAIL** — go to Step 6 and fix the BLOCKERs before resubmitting.
242
245
 
243
- If you spawned a sub-agent and no new `VERDICT:` comment appears after it returns, it likely exhausted its turn budget. Respawn it ONCE with a concise-budget hint: *"Stay within turn budget. Skip deep verification. Fetch proposal + comments + idea only, skim for obvious BLOCKERs, and post your VERDICT within the first 10 turns."* If still no VERDICT, fall back to reviewing manually (Step 5.5 fallback) and post the VERDICT yourself.
246
+ 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 fall back to reviewing manually (Step 5.5 fallback) and post the VERDICT yourself. **Absence is never a PASS.**
244
247
 
245
248
  ### Step 6: Handle Feedback
246
249