@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.
@@ -4,7 +4,7 @@ description: Adversarial read-only review of a submitted Chorus proposal — doc
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
  ---
@@ -19,7 +19,8 @@ You have been asked to **review a submitted Chorus proposal**. Your job is **not
19
19
 
20
20
  ## Hard rules (READ-ONLY)
21
21
 
22
- - **You are READ-ONLY.** Do NOT edit, write, or create files. Do NOT run Bash. Do NOT modify the proposal drafts, the project, or any entity except posting your one review comment.
22
+ - **You are READ-ONLY.** Do NOT edit, write, or create files. Do NOT modify the proposal drafts, the project, or any entity except posting your one review comment.
23
+ - **Bash is READ-ONLY inspection only:** ls, cat, grep/rg, find, git ls-files/log/show/diff. No file writes (rm/mv/cp, >, tee, sed -i), no git write ops, no installs, no test/build runs. Use it to confirm a file or directory exists before flagging it as missing.
23
24
  - **Keep your comment under 800 characters.** PASS items: names only. NOTE items: one-line description. BLOCKER items: evidence + expected/actual.
24
25
  - **Classify every finding** as BLOCKER (blocks implementation) or NOTE (non-blocking). Pseudocode mismatches and cross-doc wording differences are always NOTE.
25
26
  - **End with a single line beginning `VERDICT:`** followed by exactly one of `PASS`, `PASS WITH NOTES`, or `FAIL`. Has BLOCKERs → FAIL. Only NOTEs → PASS WITH NOTES. Nothing → PASS.
@@ -66,6 +67,7 @@ chorus_get_elaboration({ ideaUuid: "<idea-uuid>" })
66
67
  - Do tasks cover ALL requirements from the documents?
67
68
  - Are there scope additions not in the original idea?
68
69
  - Are there contradictions between documents and tasks?
70
+ - **Intent alignment** — You already have the originating Idea (`inputUuids[0]`) + its elaboration; also read its human comments (`chorus_get_comments({ targetType: "idea", targetUuid })`, `author.type == "user"`). Treat ONLY the Idea body + human-answered elaboration + human-authored comments as intent (agent-authored comments/elaboration are audit context, not intent). Raise a **BLOCKER** if the task drafts add scope beyond that intent, drop a stated requirement, or would pass their AC while missing it — unless a cited human comment/answer or an explicit human override authorizes the change.
69
71
 
70
72
  ## Finding classification
71
73
 
@@ -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.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -154,7 +154,7 @@ chorus_submit_for_verify({
154
154
  })
155
155
  ```
156
156
 
157
- Submitting is not final verification. Spawn the required task-reviewer skill through `sessions_spawn` as described in `/develop`, wait for it, and read the newest `VERDICT:` Task comment. `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.
157
+ Submitting is not final verification. Spawn the required task-reviewer skill through `sessions_spawn` as described in `/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.
158
158
 
159
159
  ### Step 8: Permission-Aware Verification
160
160
 
@@ -177,7 +177,7 @@ Quick Tasks support sub-agent execution just like proposal-based tasks. **Sessio
177
177
  - **Main agent**: create quick tasks, work them yourself, or hand task UUIDs to sub-agents
178
178
  - **Sub-agents**: create your own session (`chorus_create_session`), checkin/checkout per task, pass `sessionUuid` to `chorus_update_task` / `chorus_report_work`, and close the session on exit — see `/develop` for the full manual protocol
179
179
 
180
- > OpenClaw has no Agent Teams / `TeamCreate` primitive; if you need to run several quick tasks, work them sequentially as the main agent (or dispatch generic sub-agents one at a time).
180
+ > There is no team object to create: to run several quick tasks in parallel, dispatch one sub-agent per task with `sessions_spawn`, issuing them in a single message. If `sessions_spawn` is unavailable or workers fail repeatedly, work the tasks sequentially as the main agent.
181
181
 
182
182
  ---
183
183
 
@@ -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.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -67,12 +67,12 @@ Key responsibilities:
67
67
  When reviewing proposals, tasks, or an Idea's final aggregate code change, get an independent VERDICT before approving/verifying/shipping. On OpenClaw there is **no PostToolUse hook** to remind you — invoke the review yourself, inline.
68
68
 
69
69
  1. **Preferred — spawn a reviewer sub-agent.** Use the OpenClaw `sessions_spawn` tool to spawn a sub-agent whose `task` tells it to **invoke the `/proposal-reviewer` skill** (for proposals), the `/task-reviewer` skill (for tasks), or the `/code-reviewer` skill (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**) — all bundled with this plugin — against the entity. Wait for it (poll the `subagents` tool or use `sessions_yield` — do NOT detach; you must have the VERDICT before proceeding). The sub-agent inherits the plugin skills, so the reviewer skill is available to it; it posts a VERDICT comment with detailed findings. Example task prompt: `Run the /proposal-reviewer skill to review proposalUuid <uuid>; post your VERDICT comment when done.`
70
- 2. **Read the VERDICT.** After the reviewer completes, call `chorus_get_comments` and find the most recent comment containing `VERDICT:`. There are exactly three possible outcomes:
70
+ 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:
71
71
  - **VERDICT: PASS** — No issues found. Approve (proposals) or mark AC passed and verify (tasks).
72
72
  - **VERDICT: PASS WITH NOTES** — Minor non-blocking notes. Still approve/verify. Notes are informational.
73
73
  - **VERDICT: FAIL** — BLOCKERs found. Reject (proposals) or reopen (tasks). For a **code-review gateway** FAIL, do not reopen the verified tasks — instead 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. 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. Fix the specific BLOCKERs listed in the comment before resubmitting.
74
- 3. **No new VERDICT comment?** The sub-agent exhausted its turn budget before posting. Respawn it ONCE with an explicit prompt like: *"Stay within your turn budget. Skip deep source verificationbatch all MCP fetches up front, skim for obvious BLOCKERs only, and reserve your last few turns to post the VERDICT comment."* If the second attempt also fails to post, review manually (step 5).
75
- 4. **Track rounds.** Count existing VERDICT comments before spawning. After 3 rounds of FAIL on the same item, stop the loop and escalate to human review.
74
+ 3. **No new VERDICT comment?** Check what the reviewer *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: STOPdo 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 item yourself as a read-only pass using the checklists below and POST the VERDICT — **absence is never a PASS**.
75
+ 4. **Track rounds.** Count existing VERDICT comments before spawning. After 3 rounds of FAIL on the same item, stop the loop and escalate to human review: post a comment saying the round limit was reached and a human decision is needed, and post no VERDICT. Nobody — including you on a later turn — may replace that escalation with a self-reviewed VERDICT.
76
76
  5. **Fallback — review it yourself (no `sessions_spawn` on the host).** If spawning is unavailable (disabled by policy, or the spawn fails), perform the review yourself as a **focused, read-only pass** using the quality checklists in the workflows below: read the entity, its comments, and the relevant documents/code, run read-only test/build commands where applicable, and do NOT modify anything. Then record your VERDICT via `chorus_add_comment` ending with a `VERDICT:` line (PASS / PASS WITH NOTES / FAIL), classifying every finding as BLOCKER or NOTE. The `/proposal-reviewer`, `/task-reviewer`, and `/code-reviewer` skills are the authoritative checklists for this manual pass — read the relevant one and follow its procedure.
77
77
 
78
78
  ---
@@ -332,7 +332,7 @@ chorus_pm_update_document({ documentUuid: "<doc-uuid>", content: "Updated..." })
332
332
  - **Unblock the team** — Prioritize proposal reviews to keep PM and Developer work flowing
333
333
  - **Use delete sparingly** — Prefer closing over deleting; closing preserves history
334
334
  - **Document decisions** — Use comments to explain approval/rejection reasoning
335
- - **Verify between waves** — In sequential wave execution, verify tasks to `done` between waves to unblock downstream dependencies
335
+ - **Verify between waves** — In wave-based execution, verify tasks to `done` between waves to unblock downstream dependencies
336
336
 
337
337
  ---
338
338
 
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: spec-lite
3
+ description: Lightweight, Chorus-native local specs for Chorus PM workflows on OpenClaw — a durable local spec `.chorus/specs/<slug>/spec.md` (one per capability/feature) edited in place and NEVER synced (git history is its record), plus one dated folder per change effort `.chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/` holding Chorus-typed docs (prd.md, tech_design.md, …) that ARE mirrored 1:1 into persistent Chorus Documents via `--arg-file`. The fallback when OpenSpec isn't in use; a low-token alternative to the heavier openspec-aware path. Read from proposal / develop / yolo when the spec mode resolves to `lite`.
4
+ license: AGPL-3.0
5
+ metadata:
6
+ author: chorus
7
+ version: "0.18.0"
8
+ category: project-management
9
+ mcp_server: chorus
10
+ ---
11
+
12
+ # spec-lite — durable local spec + per-change synced docs (OpenClaw plugin)
13
+
14
+ A **shared sub-procedure** for the Chorus stage skills (proposal, develop, yolo) — the lightweight
15
+ spec mode, modelled on **superpowers** (a durable spec that lives on, plus per-effort artifacts):
16
+ one **durable local spec** per capability (`<slug>/spec.md`, edited in place, **never synced** — git
17
+ history is its 留痕), plus one **dated folder per change effort** (`<slug>/<YYYY-MM-DD>-<change-slug>/`
18
+ of Chorus-typed docs — `prd.md`, … — that **are** mirrored 1:1 into persistent Chorus Documents).
19
+ No new CLI, MCP tool, backend, or schema — mirroring reuses the existing document tools.
20
+
21
+ > **Tool namespace:** Chorus MCP tools are exposed under a `chorus__` prefix on OpenClaw (e.g.
22
+ > `chorus__chorus_pm_add_document_draft`). Bare names are used in prose for readability — prepend
23
+ > `chorus__` when invoking the MCP tools directly. **Document-mirror calls do NOT go through the MCP
24
+ > harness** — they use the `chorus` CLI (`chorus mcp call`, preferred) or the `chorus-api.sh` wrapper
25
+ > (fallback), independent of the `chorus__` namespacing (see Mirror below).
26
+
27
+ ## Mode (how you got here)
28
+
29
+ **OpenClaw has no SessionStart hook and no injected `## Spec Mode` value** — unlike the Claude Code /
30
+ Codex / Pi ports, nothing precomputes the mode into your context. The mode is **resolved by sourcing
31
+ the resolver the plugin ships** (`bin/resolve-spec-mode.sh`, byte-identical to the Claude Code copy),
32
+ the moment a stage skill reaches its spec-mode step — see `openspec-aware` §1 for the locate-and-source
33
+ block. Never re-derive the rule from prose. You can also print the resolved mode any time with **`/chorus spec`** (or the
34
+ `## /chorus status` block). You are here because that resolution returned **`lite`**; if it returned
35
+ anything else, this skill is a no-op — return to the caller. (For the record, the rule: an explicit
36
+ `CHORUS_SPEC_MODE` wins, else OpenSpec when usable, else lite.)
37
+
38
+ ## The durable local spec — `<slug>/spec.md`
39
+
40
+ `.chorus/specs/<slug>/spec.md` — `<slug>` (kebab-case) names a **capability/feature, not one change**.
41
+ This is the single, cumulative, human-readable "current truth" of the capability: **edited in place**
42
+ by every change, **never mirrored to Chorus, carries no Chorus ids**. Minimal frontmatter only
43
+ (`slug`, `title`, `status: draft|active|done`, `created`), then plain prose — `## Intent`,
44
+ `## Requirements` (prose + `- [ ]` acceptance points, no `SHALL`/scenario grammar), `## Non-goals`.
45
+ Start from the inline **durable `spec.md` template** below.
46
+ Its git history is the whole record — no changelog section, no Chorus round-trip. **This file NEVER
47
+ enters the mirror loop.**
48
+
49
+ `status` describes the **capability**, not a single change: `active` while any change is in flight,
50
+ `done` when the current change delivers and none is open. A **new** change against a `done` capability
51
+ reopens it to `active`, back to `done` on delivery.
52
+
53
+ ### Template — the durable `spec.md`
54
+
55
+ ```markdown
56
+ ---
57
+ slug: <kebab-case-capability>
58
+ title: <Capability title>
59
+ status: draft # draft | active | done
60
+ created: <YYYY-MM-DD>
61
+ ---
62
+
63
+ ## Intent
64
+ <what this capability is for, in prose>
65
+
66
+ ## Requirements
67
+ <prose, no SHALL/scenario grammar>
68
+ - [ ] <acceptance point>
69
+
70
+ ## Non-goals
71
+ - <explicitly out of scope>
72
+ ```
73
+
74
+ ## Per-change dated folders — `<slug>/<YYYY-MM-DD>-<change-slug>/`
75
+
76
+ Each change effort is **one dated folder directly under `<slug>/`** (no `changes/` wrapper), e.g.
77
+ `.chorus/specs/<slug>/2026-09-08-add-export/`. Date + slug so same-day changes don't collide and
78
+ folders sort by date. It holds the **Chorus-typed** docs for THAT change — one file per Document type:
79
+
80
+ | File | `Document.type` | Required? |
81
+ |---|---|---|
82
+ | `prd.md` | `prd` | **yes** — the primary per-change doc |
83
+ | `tech_design.md` | `tech_design` | optional — the "how" |
84
+ | `adr.md` / `guide.md` / `spec.md` | `adr` / `guide` / `spec` | optional |
85
+
86
+ These files **ARE synced** — each maps to **one persistent Chorus Document** of its type. Their
87
+ frontmatter carries the sync ids `proposalUuid` and `documentUuid` (the type is implied by the
88
+ filename). Start from the inline **dated-folder document template** below. A different change to the same
89
+ capability is a different dated folder. The **current change's** folder is edited and re-mirrored
90
+ throughout its effort (until delivery); only **previously-delivered** dated folders are left frozen —
91
+ you don't reach back and rewrite a past change.
92
+
93
+ > **Two files named `spec.md`, different roles.** The durable `<slug>/spec.md` (local only, no ids) is
94
+ > NOT the same as a per-change `spec`-type doc, which would live at `<slug>/<date>-<slug>/spec.md`
95
+ > (synced, carries ids). Prefer `prd.md` as the per-change primary doc to avoid the confusion.
96
+
97
+ ### Template — a dated-folder document
98
+
99
+ The document **type is implied by the filename** (`prd.md` → `prd`, `tech_design.md` → `tech_design`, …),
100
+ **NOT** a frontmatter key.
101
+
102
+ ```markdown
103
+ ---
104
+ title: <Document title as it appears in Chorus>
105
+ proposalUuid: <uuid> # written on first mirror
106
+ documentUuid: # empty until the draft materializes on approval
107
+ ---
108
+
109
+ # <Document title>
110
+ <body — this file's bytes are the source of truth for the Chorus Document>
111
+ ```
112
+
113
+ ## Flow (one change)
114
+
115
+ 1. Confirm mode = `lite` (resolved by the shipped resolver, `openspec-aware` §1; else no-op).
116
+ 2. Create the dated folder `<slug>/<YYYY-MM-DD>-<change-slug>/` and write its **synced** change docs —
117
+ `prd.md` (required), `tech_design.md` etc. only if warranted (use the **dated-folder document template** above).
118
+ 3. **Update `<slug>/spec.md` in place** to the new cumulative truth (Requirements, acceptance points,
119
+ `status`) — local only, no sync.
120
+ 4. Create the proposal container with one literal locator line in `description` (own line, no trailing
121
+ punctuation) so develop finds the change:
122
+ `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/`
123
+ 5. **Mirror the dated folder's docs** to Chorus (below). Add tasks via `chorus_pm_add_task_draft` —
124
+ **no `tasks.md`**, no CLI / validate / archive, no delta grammar. **Tasks live in Chorus.**
125
+ 6. Develop → keep editing `spec.md` + the change docs, re-mirroring the change docs as work lands and
126
+ ticking acceptance points. On delivery set the durable `spec.md` `status: done`.
127
+
128
+ ## Mirror — only the dated-folder docs (never `spec.md`)
129
+
130
+ Every dated-folder `<type>.md` maps to **one persistent Chorus Document** of that `type`, tracked by
131
+ `documentUuid` in the file's frontmatter. Fill `content` from the file's bytes with `--arg-file` —
132
+ never re-type the body (drifts, burns ~20k tokens). One call per file; resolve identity by
133
+ `documentUuid` / `(proposalUuid, type)`, **never by `title` alone** (a lookup finding zero or >1 MUST
134
+ **halt**). Guard every call with the `chorus_check_response` halt-on-error helper (`openspec-aware`
135
+ §6). No `chorus` on `PATH`? Fall back to the `chorus-api.sh` wrapper + `json_encode_file`
136
+ (`openspec-aware` §3.6; the OpenClaw wrapper is invoked as `chorus-api.sh mcp-tool <tool> "$PAYLOAD"`).
137
+ **`<slug>/spec.md` is NEVER in this loop.**
138
+
139
+ - **First time a doc is authored** (its dated folder is new): write `proposalUuid` into frontmatter,
140
+ mirror into a proposal **draft** —
141
+ `chorus mcp call chorus_pm_add_document_draft "{\"proposalUuid\":\"$P\",\"type\":\"prd\",\"title\":\"PRD: $TITLE\"}" --arg-file content=".chorus/specs/$SLUG/$DATED/prd.md"`.
142
+ Edit the draft via `chorus_pm_update_document_draft` (returned `draftUuid`) before approval. On
143
+ approval it materializes into a persistent Document — resolve by `(proposalUuid, type)` via
144
+ `chorus_get_documents`, record `documentUuid` in frontmatter, re-mirror once so local == Chorus.
145
+ - **Later edits** (a doc that already has a `documentUuid`): edit the file, then
146
+ `chorus mcp call chorus_pm_update_document "{\"documentUuid\":\"$D\"}" --arg-file content=".chorus/specs/$SLUG/$DATED/<type>.md"`.
147
+ Each update **auto-increments the Document version** — that version history is the change doc's
148
+ record in Chorus, alongside git.
149
+
150
+ ## 留痕: git history + Document versions
151
+
152
+ `git log -- .chorus/specs/$SLUG/` is the audit trail — the durable `spec.md`'s in-place diffs plus each
153
+ dated folder's change docs; the mirrored Documents' auto-incremented versions are the parallel record
154
+ in Chorus. No changelog section to maintain. Only `.chorus/specs/` is version-controlled (`.chorus/*` +
155
+ `!.chorus/specs/`).
156
+
157
+ **Single-writer:** the folder is shared — in a multi-task wave only the **orchestrator / main agent**
158
+ edits + re-mirrors; parallel workers report via `chorus_report_work` only, re-reading before any write.
159
+ **Task state lives in Chorus**, not the docs — the `- [ ]` points are acceptance intent, not a tracker.
@@ -4,7 +4,7 @@ description: Adversarial verification of a submitted Chorus task against its AC
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
  ---
@@ -58,6 +58,10 @@ chorus_get_document({ documentUuid: "<doc-uuid>" })
58
58
 
59
59
  **Hallucination check:** Flag anything that looks 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.
60
60
 
61
+ **Step 7: Intent alignment**
62
+
63
+ 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.
64
+
61
65
  ## Finding classification
62
66
 
63
67
  **BLOCKER** — blocks correctness: AC not actually implemented; build or test failures; implementation diverges from proposal documents (semantic contradiction); edge cases causing runtime errors; missing error handling for required scenarios.
@@ -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.17.2"
7
+ version: "0.18.0"
8
8
  category: project-management
9
9
  mcp_server: chorus
10
10
  ---
@@ -15,7 +15,7 @@ Full-auto AI-DLC pipeline. User provides a prompt; agent drives the entire lifec
15
15
 
16
16
  > **Tool namespace:** Chorus tools are exposed by the connected MCP server under a `chorus__` prefix on OpenClaw (e.g. `chorus__chorus_pm_create_proposal`). Bare names are used below for readability — prepend `chorus__` when invoking. See `/chorus` for the full rule.
17
17
 
18
- > **OpenClaw adaptations summarized (details inline below):** (1) elaboration is **self-answered as plain text** — no `AskUserQuestion`, no user interaction; (2) reviewers run **inline** after each submit — spawn a sub-agent with the OpenClaw `sessions_spawn` tool and tell it to run the `/proposal-reviewer` or `/task-reviewer` skill, with a read-only self-review fallback when `sessions_spawn` is unavailable; (3) sessions are **manual** if you dispatch sub-agents (no SubagentStart hook); (4) task execution is **sequential main-agent waves** — OpenClaw has no Agent Teams / `TeamCreate` primitive.
18
+ > **OpenClaw adaptations summarized (details inline below):** (1) elaboration is **self-answered as plain text** — no `AskUserQuestion`, no user interaction; (2) reviewers run **inline** after each submit — spawn a sub-agent with the OpenClaw `sessions_spawn` tool and tell it to run the `/proposal-reviewer` or `/task-reviewer` skill, with a read-only self-review fallback when `sessions_spawn` is unavailable; (3) sessions are **manual** if you dispatch sub-agents (no SubagentStart hook); (4) task execution dispatches **one sub-agent per unblocked task via `sessions_spawn`** (whole wave in one message), falling back to **sequential main-agent waves** when `sessions_spawn` is unavailable or workers fail repeatedly there is no team object to create first.
19
19
 
20
20
  ---
21
21
 
@@ -25,7 +25,7 @@ Full-auto AI-DLC pipeline. User provides a prompt; agent drives the entire lifec
25
25
 
26
26
  1. **Planning** -- create project, idea, self-elaboration, proposal with docs & tasks
27
27
  2. **Proposal Review** -- proposal-reviewer adversarial loop
28
- 3. **Execution** -- sequential, dependency-ordered task execution by the main agent
28
+ 3. **Execution** -- dependency-ordered waves: one worker sub-agent per unblocked task, or sequential main-agent execution as fallback
29
29
  4. **Verification** -- task-reviewer adversarial loop + admin verify
30
30
  5. **Report** -- completion summary
31
31
 
@@ -42,7 +42,8 @@ Full-auto AI-DLC pipeline. User provides a prompt; agent drives the entire lifec
42
42
  Admin Approve --> Tasks materialize
43
43
  |
44
44
  v
45
- Sequential wave execution (main agent: loop chorus_get_unblocked_tasks)
45
+ Wave execution (loop chorus_get_unblocked_tasks; one sessions_spawn worker
46
+ per task, or sequential main-agent fallback)
46
47
  | (implement task + task-reviewer per task)
47
48
  v
48
49
  Admin Verify each task --> unblock next
@@ -188,21 +189,19 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
188
189
 
189
190
  #### Step 1.4: Create Proposal
190
191
 
191
- 1. **Detect OpenSpec mode (inline).** Load the `openspec-aware` skill and run its **§1 inline three-check detection** (`CHORUS_OPENSPEC_MODE != "off"`, an `openspec/` directory at the project root, and the `openspec` CLI on `PATH`).
192
+ 1. **Resolve the spec mode (inline).** OpenClaw has no SessionStart hook to precompute it — so you resolve the **whole** contract yourself, not just "is OpenSpec active". 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`). Rule: an explicit `CHORUS_SPEC_MODE` 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**. Route: `openspec` (usable) → **2a**; `off` → **2b**; `lite` → **2c**. If the resolution says the mode **cannot be honored** (explicit `CHORUS_SPEC_MODE=openspec` but OpenSpec unusable), **halt** and surface it — do NOT fall back or enter 2a with no OpenSpec.
192
193
 
193
- > **OpenClaw note:** there is no Claude Code SessionStart hook to precompute `CHORUS_OPENSPEC_ACTIVE`. You must run the three checks yourself, inline, here. This is mandatory — yolo runs unattended, so silently picking the wrong mode is exactly the failure scenario the detection contract exists to prevent.
194
+ > **OpenClaw note:** this is mandatory — yolo runs unattended, so silently picking the wrong mode is exactly the failure scenario the resolution contract exists to prevent.
194
195
 
195
- - All three checks pass spec-driven branch (sub-step 2a below).
196
- - Any check fails (or `CHORUS_OPENSPEC_MODE=off`) → free-form branch (sub-step 2b below).
197
-
198
- 2. **Create the empty proposal container.** In OpenSpec mode, the `description` MUST contain the literal line `OpenSpec change slug: <slug>` (use the `$SLUG` you'll pick in 2a); in free-form mode, omit that line.
196
+ 2. **Create the empty proposal container.** The `description` MUST carry the mode's locator line — OpenSpec: `OpenSpec change slug: <slug>`; spec-lite: `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/`; free-form: none. `description` is only settable at creation, so decide the slug/dated-path first.
199
197
 
200
198
  ```
201
199
  chorus_pm_create_proposal({
202
200
  projectUuid: "<project-uuid>",
203
201
  title: "<feature name>",
204
- description: "<summary>\n\nOpenSpec change slug: <slug>", // OpenSpec mode
205
- // description: "<summary>", // free-form mode
202
+ description: "<summary>\n\nOpenSpec change slug: <slug>", // OpenSpec (2a)
203
+ // description: "<summary>\n\nSpec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/", // spec-lite (2c)
204
+ // description: "<summary>", // free-form (2b)
206
205
  inputType: "idea",
207
206
  inputUuids: ["<idea-uuid>"]
208
207
  })
@@ -210,7 +209,7 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
210
209
 
211
210
  Then branch:
212
211
 
213
- **2a. OpenSpec mode (all three checks pass).** Follow `openspec-aware` §3 end-to-end:
212
+ **2a. OpenSpec mode (resolved mode = openspec, usable).** Follow `openspec-aware` §3 end-to-end:
214
213
  - Pick `$SLUG`, run `openspec new change "$SLUG"` (§3.1–§3.2).
215
214
  - Author `proposal.md`, `design.md`, and one `specs/<capability>/spec.md` per capability locally on disk (§3.3). ADDED Requirements only; per-spec fallback to free-form Markdown if MODIFIED/REMOVED is needed.
216
215
  - Define the `chorus_check_response` helper (§6); prefer `chorus mcp call … --arg-file content=<file>` for mirrors (§3.4/§3.6) — the bash-wrapper fallback's `json_encode_file` is only needed when `chorus` is not on `PATH`.
@@ -220,7 +219,7 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
220
219
 
221
220
  Then continue to step 3 (task drafts).
222
221
 
223
- **2b. Free-form mode (any check fails).** Add a tech design document draft directly via MCP, content authored inline:
222
+ **2b. Free-form mode (resolved mode = free-form).** Only when step 1 resolved to free-form — i.e. explicit `CHORUS_SPEC_MODE=off` (unset never comes here: it resolves to OpenSpec when usable, else spec-lite/2c). Add a tech design document draft directly via MCP, content authored inline:
224
223
 
225
224
  ```
226
225
  chorus_pm_add_document_draft({
@@ -231,6 +230,8 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
231
230
  })
232
231
  ```
233
232
 
233
+ **2c. spec-lite mode (resolved mode = lite).** Load the `spec-lite` skill. Pick `$SLUG` (a **capability**). Ensure the durable `.chorus/specs/<slug>/spec.md` exists (local-only, no 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 (shape = the `spec-lite` skill's inline dated-folder document template) — `prd.md` (primary), optional `tech_design.md`… The `description` carries the `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/` locator (step 2). Mirror **each** dated-folder `<type>.md` to its persistent Document byte-exact — first time `chorus mcp call chorus_pm_add_document_draft "{\"proposalUuid\":\"<uuid>\",\"type\":\"prd\",\"title\":\"PRD: <feature>\"}" --arg-file content=.chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/prd.md`, later edits via `chorus_pm_update_document` against the recorded `documentUuid` (`chorus-api.sh mcp-tool …` fallback when `chorus` not on `PATH`). **`spec.md` is never mirrored.** No `openspec/changes/` scaffold; no `tasks.md`. Then continue to step 3.
234
+
234
235
  3. **Add task drafts incrementally** (use returned `draftUuid` for dependency chaining). `acceptanceCriteriaItems` is **required** on every draft — at least one non-blank criterion, or the call is rejected:
235
236
  ```
236
237
  # First task
@@ -272,6 +273,20 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
272
273
 
273
274
  ---
274
275
 
276
+ ### Reviewer contract (applies to every review gate below)
277
+
278
+ 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.
279
+
280
+ 1. **Spawn and wait.** Spawn the reviewer as a read-only sub-agent, then wait for it: spawn it with `sessions_spawn` and wait by polling the `subagents` tool or via `sessions_yield` — do **not** detach. The verdict is the `VERDICT:` comment the reviewer posts, not the spawn result.
281
+ 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.
282
+ 3. **No VERDICT for this round?** Check what the reviewer *did* post:
283
+ - **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.
284
+ - **Nothing at all** — respawn 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 entity yourself as a read-only pass and POST the VERDICT, then proceed on what you posted rather than looping forever.
285
+
286
+ **Absence is never a PASS**, and a round limit reached by someone else is never yours to clear.
287
+
288
+ ---
289
+
275
290
  ### Phase 2: Proposal Review Loop
276
291
 
277
292
  > **OpenClaw difference:** there is no PostToolUse hook injecting a "spawn the reviewer" reminder. Run the reviewer **inline**, right after `chorus_pm_submit_proposal`.
@@ -288,7 +303,7 @@ Then:
288
303
  ```
289
304
  chorus_get_comments({ targetType: "proposal", targetUuid: "<proposal-uuid>" })
290
305
  ```
291
- Look for the most recent comment containing `VERDICT:`.
306
+ Look for THIS round's `VERDICT:` comment the one posted after your dispatch, not an older round's.
292
307
 
293
308
  2. **Act on the VERDICT:**
294
309
 
@@ -322,15 +337,15 @@ Then:
322
337
  Proposal UUID: <uuid>"
323
338
  ```
324
339
 
325
- 4. **No new VERDICT comment after a spawned reviewer returns?** It exhausted its turn budget. Respawn it ONCE with a concise-budget hint: *"Stay within turn budget. Skip deep source 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 and post the VERDICT yourself the pipeline cannot loop forever on a silent reviewer.
340
+ 4. **No new VERDICT for this round?** Apply step 3 of the **Reviewer contract**, reviewing the proposal yourself if the reviewer stays silent.
326
341
 
327
342
  ---
328
343
 
329
- ### Phase 3: Task Execution (Sequential Waves)
344
+ ### Phase 3: Task Execution (Waves)
330
345
 
331
346
  After proposal approval, tasks exist in `open` status. Execute them in dependency-ordered waves.
332
347
 
333
- > **OpenClaw difference:** OpenClaw has **no Agent Teams / `TeamCreate` primitive**. Run waves **sequentially as the main agent**: loop `chorus_get_unblocked_tasks`, implement each ready task yourself, verify it, then loop again for the next wave. Do NOT call `TeamCreate` it does not exist on OpenClaw. (Under the Claude Code plugin, each wave can be dispatched in parallel via `TeamCreate`; that is a Claude-Code-only optimization that degrades to the sequential loop here.)
348
+ > **OpenClaw difference:** there is no team or group object to create — parallelism comes from dispatching **one sub-agent per unblocked task** with OpenClaw's own `sessions_spawn` tool, issuing the whole wave in a single message. If your host does not expose `sessions_spawn`, or spawned workers fail repeatedly, run waves **sequentially as the main agent**: loop `chorus_get_unblocked_tasks`, implement each ready task yourself, verify it, then loop again for the next wave. The sequential loop below is written for that fallback and is always safe; see `/develop` §"Optional: sub-agent dispatch" for the parallel form (workers need the manual session instructions, since there is no SubagentStart hook).
334
349
 
335
350
  ```
336
351
  wave = 1
@@ -363,7 +378,7 @@ loop:
363
378
  wave += 1
364
379
  ```
365
380
 
366
- > **Optional sub-agent dispatch:** if your OpenClaw host supports generic worker sub-agents (not Agent Teams), you may hand one task to a sub-agent at a time. Because there is no SubagentStart hook, the worker prompt **must** include the manual session instructions explicitly — see `/develop` "Optional: sub-agent dispatch". The main agent still owns review + verification. This does not change the sequential, wave-by-wave structure above.
381
+ > **Parallel form:** to run a wave in parallel instead of serially, dispatch one worker sub-agent per unblocked task with `sessions_spawn`, issuing the whole wave in a single message, then wait for the wave before verifying. Because there is no SubagentStart hook, each worker prompt **must** include the manual session instructions explicitly — see `/develop` "Optional: sub-agent dispatch". The main agent still owns review + verification, and the wave-by-wave dependency structure above is unchanged.
367
382
 
368
383
  ---
369
384
 
@@ -393,7 +408,7 @@ for the just-submitted task:
393
408
 
394
409
  # 3. Read task-reviewer VERDICT
395
410
  comments = chorus_get_comments({ targetType: "task", targetUuid: "<task-uuid>" })
396
- # Find the most recent comment containing "VERDICT:"
411
+ # Find THIS round's "VERDICT:" comment the one posted after your dispatch, not an older round's
397
412
 
398
413
  # 4. Act on VERDICT — three possible outcomes:
399
414
  if VERDICT is "PASS":
@@ -429,7 +444,7 @@ ESCALATE: "Task '{title}' failed review after {maxRounds} rounds.
429
444
 
430
445
  Continue with remaining tasks -- do not halt the entire pipeline for one stuck task.
431
446
 
432
- **No new VERDICT comment after a spawned task-reviewer 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 still no VERDICT, fall back to reviewing manually and post the VERDICT yourself do not loop indefinitely.
447
+ **No new VERDICT for this round?** Apply step 3 of the **Reviewer contract**, reviewing the task yourself if the reviewer stays silent.
433
448
 
434
449
  ---
435
450
 
@@ -450,7 +465,7 @@ ESCALATE: "Idea '{title}' failed code review after {maxCodeReviewRounds} rounds.
450
465
  Last BLOCKERs: <list>. Manual intervention needed. Idea UUID: <uuid>"
451
466
  ```
452
467
 
453
- **No VERDICT comment after the code-reviewer returns?** It exhausted its turn budget (larger than the task-reviewer's because it reviews the whole feature). Respawn ONCE with a concise-budget hint; if still silent, fall back to a manual read-only pass and post the VERDICT yourself.
468
+ **No new VERDICT for this round?** Apply step 3 of the **Reviewer contract**, reviewing the idea's aggregate change yourself if the reviewer stays silent.
454
469
 
455
470
  > The gateway is **behavioral** like the other two reviewers: its verdict is advisory and does not change the Idea's stored status; the orchestrator honors it. It runs **before** the completion report so the report is never written while a FAIL is outstanding.
456
471
 
package/src/commands.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ChorusMcpClient } from "./mcp-client.js";
2
+ import { resolveSpecModeFromEnv, type SpecModeResult } from "./spec-mode.js";
2
3
 
3
4
  // ===== Response types from Chorus MCP tools =====
4
5
  //
@@ -62,7 +63,7 @@ interface AssignmentsResponse {
62
63
 
63
64
  // ===== Skill catalog =====
64
65
  //
65
- // All 9 skills bundled with the Chorus OpenClaw plugin
66
+ // All 11 skills bundled with the Chorus OpenClaw plugin
66
67
  // (packages/openclaw-plugin/skills/*/SKILL.md). The `name` here matches each
67
68
  // skill's SKILL.md frontmatter `name`, which is exactly the slash command
68
69
  // OpenClaw exposes (see invocation hint below).
@@ -76,7 +77,8 @@ const PLUGIN_SKILLS = [
76
77
  { name: "quick-dev", description: "Skip Idea→Proposal — create tasks directly, execute, verify" },
77
78
  { name: "review", description: "Approve/reject proposals, verify tasks, project governance" },
78
79
  { name: "yolo", description: "Full-auto AI-DLC pipeline — from prompt to done" },
79
- { name: "openspec-aware", description: "Opt-in OpenSpec authoring for PM workflows when the openspec CLI is present" },
80
+ { name: "openspec-aware", description: "OpenSpec-mode authoring for PM workflows (the default when openspec/ + CLI present)" },
81
+ { name: "spec-lite", description: "Chorus-native lightweight local specs (.chorus/specs/<slug>/) — the fallback when OpenSpec isn't usable" },
80
82
  { name: "chorus-cli", description: "Install, configure agents (chorus agents add|remove|list), env vars, and chorus mcp operations" },
81
83
  ] as const;
82
84
 
@@ -104,7 +106,28 @@ function formatSkillsList(): string {
104
106
  ].join("\n");
105
107
  }
106
108
 
107
- function formatStatus(checkin: CheckinResponse, connectionStatus: string): string {
109
+ // Spec-mode lines for the status block. Since OpenClaw has no SessionStart hook
110
+ // to precompute the mode, `/chorus` is the resolver's real runtime caller and the
111
+ // user-visible surface: it prints `CHORUS_SPEC_MODE=<mode> (<reason>)`, the
112
+ // `CHORUS_OPENSPEC_ACTIVE=1` line only for a usable OpenSpec, and a halt warning
113
+ // when an explicit `=openspec` cannot be honored. The stage skills resolve the
114
+ // SAME contract inline (see src/spec-mode.ts — the single source of truth).
115
+ function specModeLines(spec: SpecModeResult): string[] {
116
+ const lines = [`CHORUS_SPEC_MODE=${spec.specMode} (${spec.specReason})`];
117
+ if (spec.chorusOpenspecActive) {
118
+ lines.push(`CHORUS_OPENSPEC_ACTIVE=1 (${spec.openspecUsableReason})`);
119
+ }
120
+ if (spec.specFail) {
121
+ lines.push(`WARNING: spec-mode halt — ${spec.specFail}`);
122
+ }
123
+ return lines;
124
+ }
125
+
126
+ function formatStatus(
127
+ checkin: CheckinResponse,
128
+ connectionStatus: string,
129
+ spec: SpecModeResult,
130
+ ): string {
108
131
  const projects = Object.values(checkin?.activeProjects ?? {});
109
132
  const activeIdeaTotal = projects.reduce(
110
133
  (total, p) => total + (p.activeIdeaCount ?? 0),
@@ -116,11 +139,26 @@ function formatStatus(checkin: CheckinResponse, connectionStatus: string): strin
116
139
  `Active projects: ${projects.length} (${activeIdeaTotal} active idea(s))`,
117
140
  ...projects.map((p) => ` - ${p.name ?? "(unnamed)"}: ${p.activeIdeaCount ?? 0}`),
118
141
  `Notifications: ${checkin?.notifications?.unread ?? 0} unread`,
142
+ ...specModeLines(spec),
119
143
  `Skills: ${PLUGIN_SKILLS.map((s) => s.name).join(", ")}`,
120
144
  ];
121
145
  return lines.join("\n");
122
146
  }
123
147
 
148
+ // Detailed spec-mode view for `/chorus spec` — the mode + reason, the openspec
149
+ // usability breakdown, and the install hint when OpenSpec is merely missing.
150
+ function formatSpec(spec: SpecModeResult): string {
151
+ const lines = [
152
+ "Spec mode (resolved inline — OpenClaw has no SessionStart hook):",
153
+ ...specModeLines(spec).map((l) => ` ${l}`),
154
+ ` OpenSpec usable: ${spec.openspecUsable ? "yes" : "no"} (${spec.openspecUsableReason})`,
155
+ ];
156
+ if (spec.openspecHint) {
157
+ lines.push(` Enable OpenSpec: ${spec.openspecHint}`);
158
+ }
159
+ return lines.join("\n");
160
+ }
161
+
124
162
  function formatTaskList(taskTracker: Record<string, TaskTrackerProject> | undefined): string {
125
163
  const lines: string[] = [];
126
164
  let total = 0;
@@ -159,6 +197,7 @@ const HELP_TEXT = [
159
197
  "Chorus commands:",
160
198
  " /chorus Show connection status and summary",
161
199
  " /chorus status Same as above",
200
+ " /chorus spec Show the resolved spec mode (OpenSpec / spec-lite / off)",
162
201
  " /chorus tasks List assigned tasks",
163
202
  " /chorus ideas List assigned ideas",
164
203
  " /chorus skills List available Chorus skills",
@@ -171,28 +210,40 @@ function errorText(prefix: string, err: unknown): string {
171
210
 
172
211
  // ===== Registration =====
173
212
 
213
+ // Default spec-mode resolver: wires the real env + cwd. Injectable so the
214
+ // command test can pass a deterministic result without touching the disk/PATH.
215
+ function defaultResolveSpec(): SpecModeResult {
216
+ return resolveSpecModeFromEnv(process.env, process.cwd());
217
+ }
218
+
174
219
  export function registerChorusCommands(
175
220
  api: { registerCommand: (command: unknown) => void },
176
221
  mcpClient: ChorusMcpClient,
177
- getStatus: () => string
222
+ getStatus: () => string,
223
+ resolveSpec: () => SpecModeResult = defaultResolveSpec,
178
224
  ): void {
179
225
  api.registerCommand({
180
226
  name: "chorus",
181
- description: "Chorus plugin commands: status, tasks, ideas, skills",
227
+ description: "Chorus plugin commands: status, spec, tasks, ideas, skills",
182
228
  acceptsArgs: true,
183
229
  async handler(ctx: { args?: string }) {
184
230
  const sub = (ctx.args ?? "").trim().toLowerCase();
185
231
 
186
- // /chorus or /chorus status — connection + checkin summary via slim client.
232
+ // /chorus or /chorus status — connection + checkin + spec-mode summary.
187
233
  if (!sub || sub === "status") {
188
234
  try {
189
235
  const checkin = (await mcpClient.callTool("chorus_checkin", {})) as CheckinResponse;
190
- return { text: formatStatus(checkin, getStatus()) };
236
+ return { text: formatStatus(checkin, getStatus(), resolveSpec()) };
191
237
  } catch (err) {
192
238
  return { text: errorText("Failed to check in", err), isError: true };
193
239
  }
194
240
  }
195
241
 
242
+ // /chorus spec — the resolved spec mode (the resolver's user-visible surface).
243
+ if (sub === "spec") {
244
+ return { text: formatSpec(resolveSpec()) };
245
+ }
246
+
196
247
  // /chorus tasks — assigned tasks via chorus_get_my_assignments.
197
248
  if (sub === "tasks") {
198
249
  try {