@chorus-aidlc/chorus-pi 0.17.3 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/chorus-code-reviewer.md +1 -0
- package/agents/chorus-proposal-reviewer.md +3 -2
- package/agents/chorus-task-reviewer.md +4 -0
- package/bin/chorus-mcp-call.sh +1 -1
- package/extensions/chorus.ts +50 -24
- package/lib/lib.ts +158 -53
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +1 -1
- package/skills/chorus/SKILL.md +10 -7
- package/skills/chorus-cli/SKILL.md +1 -1
- package/skills/develop/SKILL.md +6 -4
- package/skills/docs/SKILL.md +1 -1
- package/skills/idea/SKILL.md +1 -1
- package/skills/openspec-aware/SKILL.md +31 -39
- package/skills/orchestrate/SKILL.md +9 -1
- package/skills/proposal/SKILL.md +14 -7
- package/skills/quick-dev/SKILL.md +2 -2
- package/skills/review/SKILL.md +4 -4
- package/skills/spec-lite/SKILL.md +149 -0
- package/skills/yolo/SKILL.md +29 -17
|
@@ -89,6 +89,7 @@ These are the dimensions that per-task review structurally cannot catch. Cover e
|
|
|
89
89
|
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 introduced by the aggregate.
|
|
90
90
|
5. **Feature-level test coverage adequacy** — Across the whole feature, are the integration seams and end-to-end paths tested, or only per-task units? Gaps between tasks.
|
|
91
91
|
6. **Code soundness, simplicity, correctness** — Is the aggregate change correct, reasonably simple, and free of obvious defects when read as one body of work?
|
|
92
|
+
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.
|
|
92
93
|
|
|
93
94
|
**Step 4: Run feature-level build/test**
|
|
94
95
|
|
|
@@ -5,7 +5,7 @@ tools: read, grep, find, ls, bash, mcp, mcpScript
|
|
|
5
5
|
acceptance: { level: "none", reason: "read-only chorus reviewer; verdict is posted via chorus_add_comment to Chorus, not returned to parent; suppress acceptance-report injection" }
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
CRITICAL: READ-ONLY proposal review. You CANNOT edit, write, create files,
|
|
8
|
+
CRITICAL: READ-ONLY proposal review. You CANNOT edit, write, or create files. 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.
|
|
9
9
|
USE THE chorus_* MCP TOOLS for all Chorus data access — do NOT use curl or raw HTTP. The mcp gateway tool is available (the tool name prefix may be chorus_chorus_* or chorus_* depending on the session's MCP exposure mode; probe with a checkin if unsure).
|
|
10
10
|
- chorus_get_proposal({ proposalUuid, section: "full" }) — fetch the full proposal (docs + tasks)
|
|
11
11
|
- chorus_get_comments({ targetType: "proposal", targetUuid }) — prior review comments (check for Round 2+)
|
|
@@ -28,7 +28,7 @@ You have two failure patterns. **Rubber-stamping**: skimming the proposal and wr
|
|
|
28
28
|
=== CRITICAL: DO NOT MODIFY THE PROJECT ===
|
|
29
29
|
You are STRICTLY PROHIBITED from:
|
|
30
30
|
- Creating, modifying, or deleting any files
|
|
31
|
-
-
|
|
31
|
+
- Any shell command beyond read-only inspection (the read-only Bash rule in the CRITICAL line above is the full list)
|
|
32
32
|
- Installing dependencies or packages
|
|
33
33
|
|
|
34
34
|
=== WHAT YOU RECEIVE ===
|
|
@@ -73,6 +73,7 @@ For each task draft, check:
|
|
|
73
73
|
- Do tasks cover ALL requirements from the documents?
|
|
74
74
|
- Are there scope additions not in the original idea?
|
|
75
75
|
- Are there contradictions between documents and tasks?
|
|
76
|
+
- **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.
|
|
76
77
|
|
|
77
78
|
=== FINDING CLASSIFICATION ===
|
|
78
79
|
|
|
@@ -93,6 +93,10 @@ Pick 2-3 probes that fit the specific task: boundary values, missing fields, err
|
|
|
93
93
|
|
|
94
94
|
**Hallucination check**: Flag anything that looks like it could be LLM-fabricated as NOTE — API signatures, CLI flags, config keys, model IDs, endpoint URLs, package names, or any external detail the developer likely wrote from memory rather than referencing docs.
|
|
95
95
|
|
|
96
|
+
**Step 7: Intent alignment**
|
|
97
|
+
|
|
98
|
+
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.
|
|
99
|
+
|
|
96
100
|
=== FINDING CLASSIFICATION ===
|
|
97
101
|
|
|
98
102
|
Every finding MUST be classified as one of:
|
package/bin/chorus-mcp-call.sh
CHANGED
|
@@ -143,7 +143,7 @@ ACCEPT="Accept: application/json, text/event-stream"
|
|
|
143
143
|
CT="Content-Type: application/json"
|
|
144
144
|
|
|
145
145
|
INIT=$(cat <<JSON
|
|
146
|
-
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"chorus-codex-hook","version":"0.
|
|
146
|
+
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"chorus-codex-hook","version":"0.18.1"}}}
|
|
147
147
|
JSON
|
|
148
148
|
)
|
|
149
149
|
|
package/extensions/chorus.ts
CHANGED
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
sessionWorkflow,
|
|
47
47
|
hasSessionMarker,
|
|
48
48
|
extractRunIdFromToolResultEvent,
|
|
49
|
-
|
|
49
|
+
resolveSpecMode,
|
|
50
50
|
buildSessionBanner,
|
|
51
51
|
parseMaxCodeReviewRounds,
|
|
52
52
|
resolveChorusBin,
|
|
@@ -77,7 +77,18 @@ const _mcp = _envUrl && _envKey
|
|
|
77
77
|
})();
|
|
78
78
|
const CHORUS_URL = _envUrl || _mcp.url;
|
|
79
79
|
const CHORUS_API_KEY = _envKey || _mcp.apiKey;
|
|
80
|
-
|
|
80
|
+
|
|
81
|
+
// A neutral SpecModeResult for the not-configured / connection-failed banners,
|
|
82
|
+
// where buildSessionBanner returns before reading the spec fields.
|
|
83
|
+
const NO_SPEC = {
|
|
84
|
+
specMode: "off" as const,
|
|
85
|
+
specReason: "",
|
|
86
|
+
specFail: "",
|
|
87
|
+
openspecUsable: false,
|
|
88
|
+
openspecUsableReason: "",
|
|
89
|
+
openspecHint: "",
|
|
90
|
+
chorusOpenspecActive: false,
|
|
91
|
+
};
|
|
81
92
|
|
|
82
93
|
// Reviewer toggle envs (mirror Claude Code plugin userConfig; Pi has no plugin
|
|
83
94
|
// settings UI, so env vars drive them). Defaults: all enabled.
|
|
@@ -265,11 +276,14 @@ async function closeCallSessions(
|
|
|
265
276
|
// ─── Extension ────────────────────────────────────────────────────────────
|
|
266
277
|
export default function (pi: ExtensionAPI) {
|
|
267
278
|
// SessionStart → checkin + build context (replaces Claude's on-session-start.sh)
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
//
|
|
272
|
-
// connected +
|
|
279
|
+
// Resolves the spec mode once (resolveSpecMode, the TS mirror of the bash
|
|
280
|
+
// resolver) and injects a `## Spec Mode` block, plus a user-visible one-line
|
|
281
|
+
// banner (ctx.ui.notify) mirroring the Claude plugin `systemMessage` / Codex
|
|
282
|
+
// `$chorus` toast:
|
|
283
|
+
// connected + openspec -> "Chorus connected at <url> (spec: OpenSpec)"
|
|
284
|
+
// connected + lite -> "Chorus connected at <url> (spec: spec-lite)"
|
|
285
|
+
// connected + off -> "Chorus connected at <url> (spec: off — free-form)"
|
|
286
|
+
// connected + openspec-requested-but-unusable -> warning "(spec: OpenSpec requested but unusable — …)"
|
|
273
287
|
// not configured -> warning (env vars missing)
|
|
274
288
|
// connection failed -> error (checkin couldn't reach Chorus)
|
|
275
289
|
pi.on("session_start", async (event, ctx) => {
|
|
@@ -279,7 +293,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
279
293
|
configured: false,
|
|
280
294
|
connected: false,
|
|
281
295
|
chorusUrl: CHORUS_URL,
|
|
282
|
-
|
|
296
|
+
spec: NO_SPEC,
|
|
283
297
|
});
|
|
284
298
|
ctx.ui.notify(banner.message, banner.level);
|
|
285
299
|
return;
|
|
@@ -288,13 +302,30 @@ export default function (pi: ExtensionAPI) {
|
|
|
288
302
|
try {
|
|
289
303
|
const checkin = await mcpCall("chorus_checkin");
|
|
290
304
|
connected = true;
|
|
291
|
-
//
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
305
|
+
// Resolve the spec mode once per session (single source of truth — the TS
|
|
306
|
+
// reimplementation of the bash resolver). Rule: explicit CHORUS_SPEC_MODE
|
|
307
|
+
// wins; unset → OpenSpec when usable, else spec-lite.
|
|
308
|
+
const spec = resolveSpecMode(
|
|
309
|
+
{
|
|
310
|
+
specMode: process.env.CHORUS_SPEC_MODE,
|
|
311
|
+
openspecMode: process.env.CHORUS_OPENSPEC_MODE,
|
|
312
|
+
enableOpenSpec: process.env.CLAUDE_PLUGIN_OPTION_ENABLEOPENSPEC,
|
|
313
|
+
projectRoot: ctx.cwd,
|
|
314
|
+
},
|
|
315
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
295
316
|
require("node:fs"),
|
|
317
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
296
318
|
require("node:child_process").execSync,
|
|
297
319
|
);
|
|
320
|
+
// Route note per resolved mode (mirrors the bash/Codex `## Spec Mode` block).
|
|
321
|
+
const specRoute =
|
|
322
|
+
spec.specMode === "lite"
|
|
323
|
+
? "Routing: lite → follow the `spec-lite` skill (/skill:spec-lite). A capability's durable spec is `.chorus/specs/<slug>/spec.md` (edited in place, **never synced**, git history is its record); each change is a dated folder `.chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/` of Chorus-typed docs (`prd.md` required; `tech_design.md` / `adr.md` / `guide.md` / `spec.md` optional) that **are** mirrored 1:1 into persistent Chorus Documents via `chorus mcp call … --arg-file content=<file>` (fallback `chorus-mcp-call.sh`). Put a `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/` locator line in the proposal description. Do NOT scaffold `openspec/changes/` or add an `OpenSpec change slug:` line."
|
|
324
|
+
: spec.specMode === "off"
|
|
325
|
+
? "Routing: off → free-form, no spec artifact. Do NOT create `.chorus/specs/` or `openspec/changes/` files; author document drafts inline via direct MCP."
|
|
326
|
+
: spec.specFail
|
|
327
|
+
? `Routing: openspec → **cannot be honored** — ${spec.specFail}. The proposal / yolo skill MUST halt after resolving the mode; do NOT silently fall back to lite/free-form. Surface this to the user.${spec.openspecHint ? ` Install hint: ${spec.openspecHint}.` : ""}`
|
|
328
|
+
: `CHORUS_OPENSPEC_ACTIVE=1 (${spec.openspecUsableReason})\n\nRouting: openspec → load the openspec-aware skill (/skill:openspec-aware) and follow §3 (OpenSpec authoring) — do NOT re-run the §1 detection block, the answer is already known.\n\nCritical rule (openspec-aware §2 Rule 1): document mirror calls (\`chorus_pm_add_document_draft\` / \`chorus_pm_update_document_draft\` / \`chorus_pm_update_document\`) MUST fill \`content\` from the local file — prefer \`chorus mcp call <tool> '<json>' --arg-file content=<file>\`, falling back to \`chorus-mcp-call.sh\` when \`chorus\` is not on PATH. Do NOT invoke these MCP tools directly with hand-typed \`content\` in OpenSpec mode.`;
|
|
298
329
|
checkinContext = [
|
|
299
330
|
"# Chorus Plugin — Active",
|
|
300
331
|
"",
|
|
@@ -306,16 +337,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
306
337
|
JSON.stringify(checkin, null, 2),
|
|
307
338
|
"```",
|
|
308
339
|
"",
|
|
309
|
-
"##
|
|
340
|
+
"## Spec Mode",
|
|
341
|
+
"",
|
|
342
|
+
`CHORUS_SPEC_MODE=${spec.specMode} (${spec.specReason})`,
|
|
310
343
|
"",
|
|
311
|
-
|
|
312
|
-
os.active
|
|
313
|
-
? "OpenSpec mode is **active**. proposal/develop/yolo skills follow the openspec-aware path."
|
|
314
|
-
: os.optout
|
|
315
|
-
? "OpenSpec was **explicitly turned off** — do not nag."
|
|
316
|
-
: os.hint
|
|
317
|
-
? `Note: this repo has an \`openspec/\` directory but the \`openspec\` CLI is not installed — ${os.hint}. Run \`/skill:chorus enable openspec\` to set it up.`
|
|
318
|
-
: "OpenSpec is not set up in this repo. Spec-driven authoring is optional — free-form works fine. If the user wants spec-driven mode, run `/skill:chorus enable openspec` (§6 walks the install + re-launch).",
|
|
344
|
+
specRoute,
|
|
319
345
|
"",
|
|
320
346
|
"## Quick Reference",
|
|
321
347
|
"- **Sessions**: auto-managed. When you dispatch a WORKER via the `subagent` tool (single/parallel/chain), the extension creates a Chorus session per worker task and injects its UUID + the session workflow into that task automatically; the session is closed when the `subagent` tool call returns (children are ephemeral). Do NOT call chorus_create_session/close_session yourself.",
|
|
@@ -325,13 +351,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
325
351
|
(CHORUS_BIN
|
|
326
352
|
? "- **OpenSpec wrapper**: `bin/chorus-mcp-call.sh` is at `" + CHORUS_BIN + "` — the CLI-absent fallback for OpenSpec-mode document mirrors. Prefer `chorus mcp call <tool> '<json>' --arg-file content=<file>` (chorus >= 0.17.0); use this wrapper only when `chorus` is not on PATH (a bare `chorus-mcp-call.sh` will NOT be on PATH for local-path installs). See /skill:openspec-aware §2."
|
|
327
353
|
: "- **OpenSpec wrapper**: `bin/chorus-mcp-call.sh` was not resolved relative to the extension — it is the CLI-absent fallback for OpenSpec-mode document mirrors (prefer `chorus mcp call <tool> '<json>' --arg-file content=<file>`). If you need it, locate it with `find ~/.pi/agent/npm -path '*chorus-pi/bin/chorus-mcp-call.sh'`. See /skill:openspec-aware §2."),
|
|
328
|
-
"- **Skills**: /skill:chorus, /skill:idea, /skill:proposal, /skill:develop, /skill:review, /skill:quick-dev, /skill:yolo",
|
|
354
|
+
"- **Skills**: /skill:chorus, /skill:idea, /skill:proposal, /skill:develop, /skill:review, /skill:quick-dev, /skill:yolo, /skill:spec-lite, /skill:openspec-aware",
|
|
329
355
|
].join("\n");
|
|
330
356
|
const banner = buildSessionBanner({
|
|
331
357
|
configured: true,
|
|
332
358
|
connected: true,
|
|
333
359
|
chorusUrl: CHORUS_URL,
|
|
334
|
-
|
|
360
|
+
spec,
|
|
335
361
|
});
|
|
336
362
|
ctx.ui.notify(banner.message, banner.level);
|
|
337
363
|
} catch (e) {
|
|
@@ -340,7 +366,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
340
366
|
configured: true,
|
|
341
367
|
connected: false,
|
|
342
368
|
chorusUrl: CHORUS_URL,
|
|
343
|
-
|
|
369
|
+
spec: NO_SPEC,
|
|
344
370
|
});
|
|
345
371
|
ctx.ui.notify(banner.message, banner.level);
|
|
346
372
|
}
|
package/lib/lib.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pure helpers extracted from the chorus-pi extension for unit testing.
|
|
3
3
|
*
|
|
4
|
-
* These functions hold no mutable state and (except for
|
|
4
|
+
* These functions hold no mutable state and (except for resolveSpecMode, which
|
|
5
5
|
* takes injectable fs/execSync) have no I/O — so they can be tested without a
|
|
6
6
|
* running Pi session or a live Chorus instance. The extension imports them
|
|
7
7
|
* from here; tests import the same functions.
|
|
@@ -11,7 +11,7 @@ import { dirname, join } from "node:path";
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Minimal fs surface needed by the config readers below.
|
|
14
|
-
* (
|
|
14
|
+
* (resolveSpecMode already uses FsLike; keep this as the shared type.)
|
|
15
15
|
*/
|
|
16
16
|
export interface FsLike {
|
|
17
17
|
existsSync(p: string): boolean;
|
|
@@ -226,68 +226,168 @@ export function extractRunIdFromToolResultEvent(event: {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
/**
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* exists but the CLI is missing.
|
|
229
|
+
* The resolved spec mode surfaced to the agent. `openspec` = the OpenSpec
|
|
230
|
+
* (openspec-aware) path; `lite` = Chorus-native lightweight specs
|
|
231
|
+
* (`.chorus/specs/<slug>/`); `off` = free-form, no spec artifact.
|
|
233
232
|
*/
|
|
234
|
-
export
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
233
|
+
export type SpecMode = "lite" | "openspec" | "off";
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Inputs to the spec-mode resolver (env values + repo root). Mirrors the
|
|
237
|
+
* canonical bash resolver `public/chorus-plugin/bin/resolve-spec-mode.sh`.
|
|
238
|
+
*/
|
|
239
|
+
export interface SpecModeInputs {
|
|
240
|
+
/** CHORUS_SPEC_MODE — explicit override: "lite" | "openspec" | "off" (else unset/""). */
|
|
241
|
+
specMode?: string;
|
|
242
|
+
/** CHORUS_OPENSPEC_MODE — legacy opt-out: "off" disables OpenSpec. */
|
|
243
|
+
openspecMode?: string;
|
|
244
|
+
/** CLAUDE_PLUGIN_OPTION_ENABLEOPENSPEC — plugin toggle: "false" disables OpenSpec (default "true"). */
|
|
245
|
+
enableOpenSpec?: string;
|
|
246
|
+
/** Repo root to probe for openspec/. */
|
|
247
|
+
projectRoot: string;
|
|
239
248
|
}
|
|
240
249
|
|
|
241
250
|
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
251
|
+
* Resolved spec mode for a repo — the TS mirror of the bash resolver's output
|
|
252
|
+
* vars. `specFail` non-empty ⇒ a stage skill MUST halt (an explicit
|
|
253
|
+
* `CHORUS_SPEC_MODE=openspec` that cannot be honored); `chorusOpenspecActive`
|
|
254
|
+
* is true only when the resolved mode is a USABLE openspec.
|
|
255
|
+
*/
|
|
256
|
+
export interface SpecModeResult {
|
|
257
|
+
specMode: SpecMode;
|
|
258
|
+
specReason: string;
|
|
259
|
+
specFail: string;
|
|
260
|
+
openspecUsable: boolean;
|
|
261
|
+
openspecUsableReason: string;
|
|
262
|
+
openspecHint: string;
|
|
263
|
+
chorusOpenspecActive: boolean;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Resolve the active Chorus spec mode for a repo — the TypeScript reimplementation
|
|
268
|
+
* of `public/chorus-plugin/bin/resolve-spec-mode.sh` (which the bash ports copy
|
|
269
|
+
* byte-identically; the TS ports reimplement + ship a same-contract test). Pure
|
|
270
|
+
* given injectable fs + execSync.
|
|
246
271
|
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
272
|
+
* Rule (per owner): an explicit `CHORUS_SPEC_MODE` wins; when unset, OpenSpec stays
|
|
273
|
+
* the default whenever it is usable (openspec/ dir + CLI, not disabled), and lite
|
|
274
|
+
* is the fallback only when OpenSpec is absent or disabled. An explicit
|
|
275
|
+
* `=openspec` that isn't usable fails fast (`specFail`).
|
|
249
276
|
*/
|
|
250
|
-
export function
|
|
251
|
-
|
|
252
|
-
optout: boolean,
|
|
277
|
+
export function resolveSpecMode(
|
|
278
|
+
inputs: SpecModeInputs,
|
|
253
279
|
fs: FsLike,
|
|
254
280
|
execSync: ExecSync,
|
|
255
|
-
):
|
|
256
|
-
|
|
257
|
-
|
|
281
|
+
): SpecModeResult {
|
|
282
|
+
const projectRoot = inputs.projectRoot || "";
|
|
283
|
+
|
|
284
|
+
// --- Is OpenSpec usable? (needs openspec/ dir + CLI on PATH + not disabled) ---
|
|
285
|
+
// enableOpenSpec toggle is checked BEFORE the legacy CHORUS_OPENSPEC_MODE, so a
|
|
286
|
+
// plugin-level opt-out wins the reason string (matches the bash resolver order).
|
|
287
|
+
let openspecDisabled = false;
|
|
288
|
+
let disabledReason = "";
|
|
289
|
+
if ((inputs.enableOpenSpec ?? "true") !== "true") {
|
|
290
|
+
openspecDisabled = true;
|
|
291
|
+
disabledReason = "enableOpenSpec userConfig=false (plugin-level opt-out)";
|
|
292
|
+
} else if (inputs.openspecMode === "off") {
|
|
293
|
+
openspecDisabled = true;
|
|
294
|
+
disabledReason = "CHORUS_OPENSPEC_MODE=off (legacy opt-out)";
|
|
258
295
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
296
|
+
|
|
297
|
+
let openspecUsable = false;
|
|
298
|
+
let openspecUsableReason = "";
|
|
299
|
+
let openspecHint = "";
|
|
300
|
+
if (openspecDisabled) {
|
|
301
|
+
openspecUsableReason = disabledReason;
|
|
302
|
+
} else if (!fs.existsSync(`${projectRoot}/openspec`)) {
|
|
303
|
+
openspecUsableReason = `no openspec/ directory at ${projectRoot}/openspec`;
|
|
304
|
+
openspecHint = "npm i -g @fission-ai/openspec && openspec init";
|
|
305
|
+
} else if (!openspecCliPresent(execSync)) {
|
|
306
|
+
openspecUsableReason = "openspec/ directory present but `openspec` CLI not on PATH";
|
|
307
|
+
openspecHint = "npm i -g @fission-ai/openspec";
|
|
308
|
+
} else {
|
|
309
|
+
openspecUsable = true;
|
|
310
|
+
openspecUsableReason = "openspec/ directory + openspec CLI both present";
|
|
262
311
|
}
|
|
263
|
-
|
|
312
|
+
|
|
313
|
+
// --- Resolve CHORUS_SPEC_MODE (unset and "" are treated the same, as in bash) ---
|
|
314
|
+
let specMode: SpecMode;
|
|
315
|
+
let specReason: string;
|
|
316
|
+
let specFail = "";
|
|
317
|
+
const raw = inputs.specMode ?? "";
|
|
318
|
+
switch (raw) {
|
|
319
|
+
case "lite":
|
|
320
|
+
specMode = "lite";
|
|
321
|
+
specReason = "explicit — Chorus-native lightweight specs in .chorus/specs/<slug>/";
|
|
322
|
+
break;
|
|
323
|
+
case "off":
|
|
324
|
+
specMode = "off";
|
|
325
|
+
specReason = "explicit — free-form, no spec artifact";
|
|
326
|
+
break;
|
|
327
|
+
case "openspec":
|
|
328
|
+
specMode = "openspec";
|
|
329
|
+
if (openspecUsable) {
|
|
330
|
+
specReason = `explicit; ${openspecUsableReason}`;
|
|
331
|
+
} else if (openspecDisabled) {
|
|
332
|
+
specReason = `explicit, but OpenSpec is disabled: ${openspecUsableReason}`;
|
|
333
|
+
specFail = `config conflict — CHORUS_SPEC_MODE=openspec vs OpenSpec disabled (${openspecUsableReason}); re-enable OpenSpec or set CHORUS_SPEC_MODE=lite`;
|
|
334
|
+
} else {
|
|
335
|
+
specReason = `explicit, but OpenSpec is not installed: ${openspecUsableReason}`;
|
|
336
|
+
specFail = `OpenSpec not usable (${openspecUsableReason})`;
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
case "":
|
|
340
|
+
// Unset: OpenSpec is the default when usable; lite is the fallback otherwise.
|
|
341
|
+
if (openspecUsable) {
|
|
342
|
+
specMode = "openspec";
|
|
343
|
+
specReason = `default — ${openspecUsableReason}; set CHORUS_SPEC_MODE=lite for Chorus-native specs, =off to disable`;
|
|
344
|
+
} else {
|
|
345
|
+
specMode = "lite";
|
|
346
|
+
specReason = `default — OpenSpec not usable (${openspecUsableReason}); using Chorus-native lightweight specs in .chorus/specs/<slug>/`;
|
|
347
|
+
}
|
|
348
|
+
break;
|
|
349
|
+
default:
|
|
350
|
+
// Unrecognized value: treat like unset (OpenSpec-if-usable, else lite).
|
|
351
|
+
if (openspecUsable) {
|
|
352
|
+
specMode = "openspec";
|
|
353
|
+
specReason = `CHORUS_SPEC_MODE='${raw}' unrecognized; falling back to default (${openspecUsableReason})`;
|
|
354
|
+
} else {
|
|
355
|
+
specMode = "lite";
|
|
356
|
+
specReason = `CHORUS_SPEC_MODE='${raw}' unrecognized; OpenSpec not usable, defaulting to lite`;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const chorusOpenspecActive = specMode === "openspec" && specFail === "";
|
|
361
|
+
return {
|
|
362
|
+
specMode,
|
|
363
|
+
specReason,
|
|
364
|
+
specFail,
|
|
365
|
+
openspecUsable,
|
|
366
|
+
openspecUsableReason,
|
|
367
|
+
openspecHint,
|
|
368
|
+
chorusOpenspecActive,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function openspecCliPresent(execSync: ExecSync): boolean {
|
|
264
373
|
try {
|
|
265
374
|
execSync("command -v openspec", { stdio: "ignore" });
|
|
266
|
-
|
|
375
|
+
return true;
|
|
267
376
|
} catch {
|
|
268
|
-
|
|
377
|
+
return false;
|
|
269
378
|
}
|
|
270
|
-
if (!cliPresent) {
|
|
271
|
-
return {
|
|
272
|
-
active: false,
|
|
273
|
-
reason: "openspec/ directory present but `openspec` CLI not on PATH",
|
|
274
|
-
optout: false,
|
|
275
|
-
hint: "install with: npm i -g @fission-ai/openspec",
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
return { active: true, reason: "openspec/ directory + openspec CLI both present", optout: false, hint: "" };
|
|
279
379
|
}
|
|
280
380
|
|
|
281
381
|
/**
|
|
282
382
|
* Build the user-visible one-line startup banner (the Pi equivalent of the
|
|
283
|
-
* Claude plugin's SessionStart `systemMessage` / Codex `$chorus` toast).
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
* -
|
|
287
|
-
* -
|
|
288
|
-
* -
|
|
383
|
+
* Claude plugin's SessionStart `systemMessage` / Codex `$chorus` toast). The
|
|
384
|
+
* suffix reflects the resolved spec mode:
|
|
385
|
+
* - openspec (usable) -> "(spec: OpenSpec)"
|
|
386
|
+
* - lite -> "(spec: spec-lite)"
|
|
387
|
+
* - off -> "(spec: off — free-form)"
|
|
388
|
+
* - openspec requested but unusable (specFail) -> warning, "(spec: OpenSpec unusable — …)"
|
|
289
389
|
*
|
|
290
|
-
* Plus two non-
|
|
390
|
+
* Plus two non-connected states:
|
|
291
391
|
* - not configured -> warning that CHORUS_URL / CHORUS_API_KEY are missing
|
|
292
392
|
* - connection failed -> error that the checkin couldn't reach Chorus
|
|
293
393
|
*
|
|
@@ -302,7 +402,7 @@ export function buildSessionBanner(args: {
|
|
|
302
402
|
configured: boolean;
|
|
303
403
|
connected: boolean;
|
|
304
404
|
chorusUrl: string;
|
|
305
|
-
|
|
405
|
+
spec: SpecModeResult;
|
|
306
406
|
}): SessionBanner {
|
|
307
407
|
// Not configured at all — env vars missing. Warn once so the user knows
|
|
308
408
|
// the plugin loaded but is inert (Claude's hook emits the same warning).
|
|
@@ -321,18 +421,23 @@ export function buildSessionBanner(args: {
|
|
|
321
421
|
};
|
|
322
422
|
}
|
|
323
423
|
|
|
324
|
-
// Connected. Append the
|
|
424
|
+
// Connected. Append the resolved spec-mode suffix.
|
|
425
|
+
const spec = args.spec;
|
|
325
426
|
let suffix: string;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
427
|
+
let level: "info" | "warning" = "info";
|
|
428
|
+
if (spec.specFail) {
|
|
429
|
+
suffix = `(spec: OpenSpec requested but unusable — ${spec.openspecUsableReason}; spec authoring will halt)`;
|
|
430
|
+
level = "warning";
|
|
431
|
+
} else if (spec.specMode === "openspec") {
|
|
432
|
+
suffix = "(spec: OpenSpec)";
|
|
433
|
+
} else if (spec.specMode === "lite") {
|
|
434
|
+
suffix = "(spec: spec-lite)";
|
|
330
435
|
} else {
|
|
331
|
-
suffix = "(
|
|
436
|
+
suffix = "(spec: off — free-form)";
|
|
332
437
|
}
|
|
333
438
|
return {
|
|
334
439
|
message: `Chorus connected at ${args.chorusUrl} ${suffix}`,
|
|
335
|
-
level
|
|
440
|
+
level,
|
|
336
441
|
};
|
|
337
442
|
}
|
|
338
443
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chorus-aidlc/chorus-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Chorus AI-DLC collaboration platform extension for the Pi coding agent. Provides skills for every stage of the AI-DLC lifecycle, read-only reviewer subagents, and session-aware extension hooks. The Chorus MCP server is auto-discovered from the repo's .mcp.json by pi-mcp-adapter — no installer required.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Chorus-AIDLC"
|
package/skills/chorus/SKILL.md
CHANGED
|
@@ -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.
|
|
7
|
+
version: "0.18.1"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -350,20 +350,22 @@ export CHORUS_MAX_CODE_REVIEW_ROUNDS=5 # 0 = unlimited
|
|
|
350
350
|
|
|
351
351
|
When enabled, reviewers run as read-only sub-agents and post a VERDICT comment on the proposal/task/idea. Three possible outcomes: **PASS** (no issues), **PASS WITH NOTES** (minor non-blocking notes), or **FAIL** (BLOCKERs found). Results are advisory — they do not block approval, verification, or ship; the code-review gateway in particular is behavioral (it does not change the Idea's stored status). On a code-review FAIL, fix it via the `/skill:quick-dev` workflow: `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. Disabling reduces token usage but removes the independent quality gate.
|
|
352
352
|
|
|
353
|
-
### 6.
|
|
353
|
+
### 6. Spec mode: OpenSpec (default when usable) vs spec-lite (fallback)
|
|
354
354
|
|
|
355
|
-
|
|
355
|
+
The extension's `session_start` handler resolves one **spec mode** per session (via the TS `resolveSpecMode`, the single source of truth) and injects a `## Spec Mode` section stating it — the stage skills **consume** that value, they don't re-derive it. 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 `/skill:spec-lite`). `CHORUS_SPEC_MODE=off` selects free-form (no spec artifact).
|
|
356
356
|
|
|
357
|
-
|
|
357
|
+
OpenSpec spec-driven path: `/skill:proposal`, `/skill:develop`, and `/skill:yolo` write `proposal.md` / `design.md` / spec deltas on disk and mirror them into Chorus drafts.
|
|
358
|
+
|
|
359
|
+
**When the user wants OpenSpec on** (e.g. they saw `(spec: spec-lite)` / `(spec: off …)` in the banner), actually **enable it for them** — run whichever steps are missing, don't just describe them:
|
|
358
360
|
|
|
359
361
|
```bash
|
|
360
362
|
npm i -g @fission-ai/openspec # 1. install the CLI if it's not on PATH (global, pure Node)
|
|
361
363
|
openspec init # 2. scaffold openspec/ (interactive; pick your editor tooling)
|
|
362
364
|
```
|
|
363
365
|
|
|
364
|
-
The
|
|
366
|
+
The spec mode is resolved **once at session start**, so it can't flip mid-session — after the steps succeed, tell the user to **restart the session**; the `## Spec Mode` section then reads `CHORUS_SPEC_MODE=openspec (…)` and the stage skills fold in the `openspec-aware` skill automatically.
|
|
365
367
|
|
|
366
|
-
To turn
|
|
368
|
+
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). The `## Spec Mode` section always states the resolved mode + reason.
|
|
367
369
|
|
|
368
370
|
---
|
|
369
371
|
|
|
@@ -426,7 +428,8 @@ This is the core overview skill. For stage-specific workflows, use:
|
|
|
426
428
|
| **Development** | `/skill:develop` | Claim Tasks, report work, session & parallel sub-agent integration |
|
|
427
429
|
| **Review** | `/skill:review` | Approve/reject Proposals, verify Tasks, project governance |
|
|
428
430
|
| **Docs** | `/skill:docs` | Consult the live Chorus documentation site to answer product-usage questions — UI workflow, agent/plugin setup, API/MCP, deployment, operations |
|
|
429
|
-
| **OpenSpec mode** | `openspec-aware` |
|
|
431
|
+
| **OpenSpec mode** | `openspec-aware` | **Shared sub-procedure** invoked by `/skill:proposal`, `/skill:develop`, and `/skill: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-mcp-call.sh` wrapper as fallback). See `skills/openspec-aware/SKILL.md`. |
|
|
432
|
+
| **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. See `skills/spec-lite/SKILL.md`. |
|
|
430
433
|
|
|
431
434
|
### Getting Started
|
|
432
435
|
|
package/skills/develop/SKILL.md
CHANGED
|
@@ -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.
|
|
7
|
+
version: "0.18.1"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -140,7 +140,9 @@ Each task and proposal includes a `commentCount` field — use it to decide whic
|
|
|
140
140
|
>
|
|
141
141
|
> When the LAST task of an OpenSpec idea is verified, the extension injects an archive reminder (`openspec-aware` §3.9) — run `openspec archive <slug> --yes`, then mirror each emitted `openspec/specs/<capability>/spec.md` back via §3.8.
|
|
142
142
|
>
|
|
143
|
-
>
|
|
143
|
+
> **Document update flow (spec-lite mode):** if the proposal `description` contains a line `Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/`, the project's PRD / tech_design / … Documents are **mirrors** of the files in that dated folder. To update such a Document, load the `spec-lite` skill (`/skill:spec-lite`) and follow its Mirror section: edit the local `<type>.md` file first, then mirror it via `chorus mcp call chorus_pm_update_document "{\"documentUuid\":\"<uuid>\"}" --arg-file content=<file>` (recorded `documentUuid` from the file's frontmatter), falling back to the `chorus-mcp-call.sh` wrapper when `chorus` is not on `PATH`, `chorus_check_response` halting on error. Same **⛔ do-not-hand-type-`content`** rule as OpenSpec. The durable `.chorus/specs/<slug>/spec.md` is edited in place too but is **never mirrored** (git history is its record). No archive flow — spec-lite has no CLI/validate/archive; on delivery just set `spec.md` `status: done`.
|
|
144
|
+
>
|
|
145
|
+
> In the no-OpenSpec, no-spec-lite fallback (free-form: no locator line), edit the Document content directly via the existing MCP tool with no wrapper, no local file step.
|
|
144
146
|
|
|
145
147
|
### Step 5: Start Working
|
|
146
148
|
|
|
@@ -229,13 +231,13 @@ After the reviewer completes, read its VERDICT:
|
|
|
229
231
|
```
|
|
230
232
|
chorus_get_comments({ targetType: "task", targetUuid: "<task-uuid>" })
|
|
231
233
|
```
|
|
232
|
-
Find
|
|
234
|
+
Find THIS round's `VERDICT:` comment — the one posted after your dispatch, not an older round's — and act on it:
|
|
233
235
|
|
|
234
236
|
- **VERDICT: PASS** — All AC verified, no issues. Proceed to admin verification.
|
|
235
237
|
- **VERDICT: PASS WITH NOTES** — All AC verified, minor notes. Proceed to admin verification (notes are non-blocking).
|
|
236
238
|
- **VERDICT: FAIL** — BLOCKERs found. Do NOT verify. Fix the BLOCKERs listed in the reviewer's comment, then resubmit.
|
|
237
239
|
|
|
238
|
-
If no new `VERDICT:` comment appears after the reviewer returns, it
|
|
240
|
+
If no new `VERDICT:` comment appears after the reviewer returns, check what it *did* post. A comment reporting that the round limit was reached, or any other explicit refusal to review, is a deliberate escalation to a human: STOP — do not respawn, do not self-review, do not post a VERDICT of your own. If it posted nothing at all, respawn it ONCE, telling it to stay within its turn budget and reserve its last turns for the VERDICT, then apply this same check again to what the retry posts. An explicit refusal from the retry still means STOP; only a second true silence lets you review the task yourself as a read-only pass using the checklist and POST the VERDICT comment. **Absence is never a PASS.**
|
|
239
241
|
|
|
240
242
|
> **Final code-review gateway (after the Idea's LAST task is verified):** when the task you just verified is the **last** task of its idea-rooted proposal, the feature is about to ship — the extension nudges you to spawn `chorus-code-reviewer` (gated by `CHORUS_ENABLE_CODE_REVIEWER`, default on). Spawn it yourself via the blocking `subagent` tool, passing the `ideaUuid` + round number; it reviews the Idea's **aggregate** code change across all its tasks (cross-task integration, architecture, security, regression, feature-level coverage) and posts one `VERDICT` comment on the **idea**. `PASS` / `PASS WITH NOTES` → ship; `FAIL` → fix via `/skill:quick-dev` (`chorus_create_tasks` with `proposalUuid` set to the current approved proposal so the fix tasks attach to it — do NOT reopen the verified tasks). Group related small BLOCKERs by default; split only materially large or independently testable fixes. Require AC self-check, independent task review, and admin verification for every fix task. Re-run aggregate review only after every fix is successfully `done`; a failed or cancelled fix stops the loop and escalates, bounded by `CHORUS_MAX_CODE_REVIEW_ROUNDS` (env, default 3; 0 = unlimited). Advisory/behavioral, like the other reviewers. Run it **before** any idea-completion report.
|
|
241
243
|
|
package/skills/docs/SKILL.md
CHANGED
package/skills/idea/SKILL.md
CHANGED
|
@@ -1,75 +1,67 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openspec-aware
|
|
3
|
-
description:
|
|
3
|
+
description: OpenSpec-mode authoring for Chorus PM workflows in Pi. The default whenever OpenSpec is usable; consumes the resolved `## Spec Mode` (never re-detects). Scaffolds `openspec/changes/<slug>/` on disk, and mirrors Markdown files into Chorus document drafts via `chorus mcp call --arg-file` (bash `chorus-mcp-call.sh` wrapper as fallback). Required reading for the proposal, develop, and yolo skills. When OpenSpec is not the resolved mode, this skill no-ops and the caller follows the resolved mode (spec-lite or free-form).
|
|
4
4
|
license: AGPL-3.0
|
|
5
5
|
metadata:
|
|
6
6
|
author: chorus
|
|
7
|
-
version: "0.
|
|
7
|
+
version: "0.18.1"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# OpenSpec-aware Authoring (Pi plugin)
|
|
13
13
|
|
|
14
|
-
This skill is a **shared sub-procedure** invoked by the Chorus stage skills (proposal, develop, yolo)
|
|
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 **
|
|
17
|
-
- Otherwise the calling skill
|
|
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 exists at the project root, and the `openspec` CLI is 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
18
|
|
|
19
|
-
|
|
19
|
+
> **See also — `spec-lite` (the lightweight fallback):** OpenSpec (this skill) stays the default whenever 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 `/skill:spec-lite`.
|
|
20
|
+
|
|
21
|
+
When you reach a point in proposal / develop / yolo where this skill is referenced, **read the resolved mode from the `## Spec Mode` section** (see §1) and branch on it. Do not re-derive the detection — the `session_start` handler has already resolved it once for this session.
|
|
20
22
|
|
|
21
23
|
---
|
|
22
24
|
|
|
23
|
-
## §1.
|
|
25
|
+
## §1. Spec mode — already resolved at session_start
|
|
24
26
|
|
|
25
|
-
The
|
|
27
|
+
The chorus-pi extension's `session_start` handler resolves the spec mode once when the session opens (via the TS `resolveSpecMode`, the single source of truth — the reimplementation of the canonical bash resolver) and writes a `## Spec Mode` section into the injected context stating `CHORUS_SPEC_MODE=<lite|openspec|off>`; when the resolved mode is a usable OpenSpec it also carries a `CHORUS_OPENSPEC_ACTIVE=1` line. That line is present only when `CHORUS_SPEC_MODE` is `openspec` **or unset**, **and all three** of these hold:
|
|
26
28
|
|
|
27
|
-
1.
|
|
29
|
+
1. OpenSpec is not disabled (`CHORUS_OPENSPEC_MODE` not `off`, `enableOpenSpec` not `false`).
|
|
28
30
|
2. The project root contains an `openspec/` directory (i.e. someone ran `openspec init` here).
|
|
29
31
|
3. The `openspec` CLI is on `PATH`.
|
|
30
32
|
|
|
31
|
-
Both signals (2) and (3) are required because the OpenSpec authoring path needs the working directory **and** the CLI
|
|
33
|
+
Both signals (2) and (3) are required because the OpenSpec authoring path needs the working directory **and** the CLI. If signal (2) holds but (3) does not, the `## Spec Mode` section carries an install hint (`npm i -g @fission-ai/openspec`); pass it through if asked.
|
|
32
34
|
|
|
33
35
|
### How to read the value
|
|
34
36
|
|
|
35
|
-
You should already see something like this in your context (look for the `##
|
|
37
|
+
You should already see something like this in your context (look for the `## Spec Mode` section near the top of the conversation):
|
|
36
38
|
|
|
37
39
|
```
|
|
38
|
-
##
|
|
40
|
+
## Spec Mode
|
|
41
|
+
|
|
42
|
+
CHORUS_SPEC_MODE=openspec (default — openspec/ directory + openspec CLI both present)
|
|
39
43
|
|
|
40
44
|
CHORUS_OPENSPEC_ACTIVE=1 (openspec/ directory + openspec CLI both present)
|
|
41
45
|
```
|
|
42
46
|
|
|
43
|
-
or:
|
|
47
|
+
or (resolved to lite / off — no `CHORUS_OPENSPEC_ACTIVE=1` line):
|
|
44
48
|
|
|
45
49
|
```
|
|
46
|
-
##
|
|
50
|
+
## Spec Mode
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
CHORUS_SPEC_MODE=lite (default — OpenSpec not usable: no openspec/ directory at /path/to/repo/openspec)
|
|
49
53
|
```
|
|
50
54
|
|
|
51
55
|
Branch:
|
|
52
56
|
|
|
53
|
-
- `CHORUS_OPENSPEC_ACTIVE=1` → follow §3 (OpenSpec authoring).
|
|
54
|
-
- `CHORUS_OPENSPEC_ACTIVE=
|
|
55
|
-
|
|
56
|
-
### Manual fallback
|
|
57
|
+
- `CHORUS_OPENSPEC_ACTIVE=1` line present → follow §3 (OpenSpec authoring).
|
|
58
|
+
- No `CHORUS_OPENSPEC_ACTIVE=1` line → this skill is a no-op; return to the caller, which follows the resolved `CHORUS_SPEC_MODE` (**spec-lite** or free-form). **Do not** scaffold `openspec/changes/`. **Do not** add the slug line to the proposal description.
|
|
57
59
|
|
|
58
|
-
If
|
|
60
|
+
If the `## Spec Mode` section shows an explicit `CHORUS_SPEC_MODE=openspec` that **cannot be honored** (OpenSpec disabled or not installed — it carries a config-conflict / not-installed reason and no `CHORUS_OPENSPEC_ACTIVE=1` line), the caller MUST **halt** and surface it — do not silently fall back.
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
if [ "${CHORUS_OPENSPEC_MODE:-}" = "off" ]; then
|
|
62
|
-
CHORUS_OPENSPEC_ACTIVE=0
|
|
63
|
-
elif [ ! -d "$PWD/openspec" ]; then
|
|
64
|
-
CHORUS_OPENSPEC_ACTIVE=0
|
|
65
|
-
elif ! openspec --version >/dev/null 2>&1; then
|
|
66
|
-
CHORUS_OPENSPEC_ACTIVE=0
|
|
67
|
-
else
|
|
68
|
-
CHORUS_OPENSPEC_ACTIVE=1
|
|
69
|
-
fi
|
|
70
|
-
```
|
|
62
|
+
### Manual fallback
|
|
71
63
|
|
|
72
|
-
|
|
64
|
+
The mode is resolved by the extension in TypeScript (`resolveSpecMode`) at `session_start` — there is no separate bash resolver to source, and you must **not** hand-roll the rule (a hand-rolled OpenSpec-only check ignores `CHORUS_SPEC_MODE` and the lite/off cases). If the `## Spec Mode` section is genuinely absent (e.g. the checkin/connection failed, or you are a forwarded sub-agent whose parent context was not carried), set `CHORUS_SPEC_MODE` explicitly (`lite` | `openspec` | `off`) and relaunch so the extension re-resolves it — rather than guessing.
|
|
73
65
|
|
|
74
66
|
---
|
|
75
67
|
|
|
@@ -393,15 +385,15 @@ The hook is read-only; you (the agent) perform the archive:
|
|
|
393
385
|
|
|
394
386
|
---
|
|
395
387
|
|
|
396
|
-
## §4.
|
|
388
|
+
## §4. When OpenSpec is not the resolved mode
|
|
397
389
|
|
|
398
|
-
When
|
|
390
|
+
When the resolved mode is not a usable OpenSpec (no `CHORUS_OPENSPEC_ACTIVE=1` line), this skill is a **no-op** — return to the calling skill, which follows the resolved `CHORUS_SPEC_MODE`: **spec-lite** (the default when OpenSpec isn't usable) or free-form (`=off`). From this skill's side:
|
|
399
391
|
|
|
400
392
|
- No `openspec/changes/` folder is created or referenced.
|
|
401
393
|
- No `OpenSpec change slug: …` line is added to the proposal description.
|
|
402
|
-
-
|
|
403
|
-
- Rule 1 (wrapper-only mirror)
|
|
404
|
-
- The §3.9 archive hook does nothing (no slug → silent exit).
|
|
394
|
+
- In **spec-lite**, the caller follows `/skill:spec-lite` (durable `.chorus/specs/<slug>/spec.md` + dated per-change folders mirrored via `--arg-file`); in **free-form**, document drafts are authored via direct MCP `chorus_pm_add_document_draft` calls with inline `content` — same as before this skill existed.
|
|
395
|
+
- Rule 1 (wrapper-only mirror) still applies in spec-lite (mirror from the local file); in free-form there is no local file source of truth.
|
|
396
|
+
- The §3.9 archive hook does nothing (no slug → silent exit); spec-lite has no archive step at all.
|
|
405
397
|
|
|
406
398
|
---
|
|
407
399
|
|
|
@@ -485,8 +477,8 @@ This is project-wide policy: no silent errors.
|
|
|
485
477
|
|
|
486
478
|
When invoked from a stage skill (proposal / develop / yolo):
|
|
487
479
|
|
|
488
|
-
1. Read
|
|
489
|
-
2.
|
|
480
|
+
1. Read the `## Spec Mode` section in the session_start context (§1) — proceed only if it carries the `CHORUS_OPENSPEC_ACTIVE=1` line. If the section is absent, use the fallback in §1 (set `CHORUS_SPEC_MODE` explicitly + relaunch).
|
|
481
|
+
2. No `CHORUS_OPENSPEC_ACTIVE=1` line → no-op; return to the caller per the resolved `CHORUS_SPEC_MODE` (spec-lite or free-form) — see §4.
|
|
490
482
|
3. Otherwise:
|
|
491
483
|
a. Pick `$SLUG` (§3.1).
|
|
492
484
|
b. `openspec new change "$SLUG"` (§3.2).
|
|
@@ -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.
|
|
7
|
+
version: "0.18.1"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -104,6 +104,14 @@ Guidance: start narrow. If a single owner can hold the whole feature in their he
|
|
|
104
104
|
|
|
105
105
|
---
|
|
106
106
|
|
|
107
|
+
## Replying to the agent who woke you (advisory)
|
|
108
|
+
|
|
109
|
+
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.
|
|
110
|
+
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
107
115
|
## Reversed-Conversation gates (you never auto-ship)
|
|
108
116
|
|
|
109
117
|
Chorus is **AI proposes, humans verify**. As orchestrator you enforce that, you do not bypass it:
|
package/skills/proposal/SKILL.md
CHANGED
|
@@ -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.
|
|
7
|
+
version: "0.18.1"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -69,7 +69,9 @@ Elaboration resolved --> Create Proposal --> Add drafts --> Validate --> Submit
|
|
|
69
69
|
|
|
70
70
|
### Step 1: Create an Empty Proposal
|
|
71
71
|
|
|
72
|
-
**
|
|
72
|
+
**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. Free-form mode omits any locator line.
|
|
73
|
+
|
|
74
|
+
**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.
|
|
73
75
|
|
|
74
76
|
```
|
|
75
77
|
chorus_pm_create_proposal({
|
|
@@ -85,15 +87,20 @@ chorus_pm_create_proposal({
|
|
|
85
87
|
|
|
86
88
|
> **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.)
|
|
87
89
|
|
|
88
|
-
### Step 1.5:
|
|
90
|
+
### Step 1.5: Select spec mode
|
|
91
|
+
|
|
92
|
+
The spec mode is **already computed** by the chorus-pi extension's `session_start` handler (`resolveSpecMode`) — do NOT re-derive it. Read the `## Spec Mode` section of your injected context: it states `CHORUS_SPEC_MODE=<lite|openspec|off>` + a routing note. (No `## Spec Mode` in context? See `openspec-aware` §1 manual fallback — never hand-roll the rule.) Act on that value:
|
|
93
|
+
|
|
94
|
+
- If the section says the mode **cannot be honored** (explicit `CHORUS_SPEC_MODE=openspec` but OpenSpec unusable — config-conflict or install-hint reason), **halt** and surface it; do not fall back.
|
|
95
|
+
- Otherwise branch on the resolved mode:
|
|
89
96
|
|
|
90
|
-
|
|
97
|
+
- **resolved = spec-lite** → load the `spec-lite` skill (`/skill:spec-lite`) 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`. **`spec.md` is never mirrored.** Skip Step 2 below. (Tasks via `chorus_pm_add_task_draft`; no `tasks.md`.)
|
|
91
98
|
|
|
92
|
-
-
|
|
99
|
+
- **resolved = OpenSpec** (the `## Spec Mode` section shows `CHORUS_OPENSPEC_ACTIVE=1` — i.e. `CHORUS_SPEC_MODE=openspec` *or* unset, with OpenSpec usable) → follow the `openspec-aware` skill §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.
|
|
93
100
|
|
|
94
|
-
> **⛔ 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-mcp-call.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
|
|
101
|
+
> **⛔ 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-mcp-call.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 with a hand-typed `content` field. Re-typing thousands of lines burns 20k+ content tokens per proposal and breaks byte-equality (`openspec-aware` §2 Rule 1). Skip Step 2 when in OpenSpec mode — the file-fill flow replaces it for documents.
|
|
95
102
|
|
|
96
|
-
-
|
|
103
|
+
- **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`.
|
|
97
104
|
|
|
98
105
|
### Step 2: Add Document Drafts
|
|
99
106
|
|
|
@@ -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.
|
|
7
|
+
version: "0.18.1"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -152,7 +152,7 @@ chorus_submit_for_verify({
|
|
|
152
152
|
})
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
Submitting is not final verification. Spawn the required task-reviewer agent with `subagent_spawn` as described in `/skill:develop`, wait for it, and read
|
|
155
|
+
Submitting is not final verification. Spawn the required task-reviewer agent with `subagent_spawn` as described in `/skill:develop`, wait for it, and read THIS round's `VERDICT:` Task comment — the one posted after your dispatch, not an older round's. `PASS` and `PASS WITH NOTES` continue. On `FAIL`, do not verify or hand off: fix every unresolved BLOCKER, repeat AC self-check and submission, then run a fresh independent task review.
|
|
156
156
|
|
|
157
157
|
### Step 8: Permission-Aware Verification
|
|
158
158
|
|
package/skills/review/SKILL.md
CHANGED
|
@@ -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.
|
|
7
|
+
version: "0.18.1"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -65,12 +65,12 @@ Key responsibilities:
|
|
|
65
65
|
When reviewing proposals, tasks, or an Idea's final aggregate code change, prefer spawning an independent reviewer sub-agent over reviewing manually:
|
|
66
66
|
|
|
67
67
|
1. **Try the reviewer first.** Spawn `chorus-proposal-reviewer` (for proposals), `chorus-task-reviewer` (for tasks), or `chorus-code-reviewer` (the final ship-time gateway over an Idea's aggregate code change, after its last task is verified — pass the `ideaUuid`; it posts its VERDICT on the **idea**) as a read-only sub-agent. **Use the blocking `subagent` tool** (it waits for the VERDICT and returns it) — you must wait for the VERDICT before proceeding. It posts a VERDICT comment with detailed findings.
|
|
68
|
-
2. **Read the VERDICT.** After the reviewer completes, call `chorus_get_comments` and find
|
|
68
|
+
2. **Read the VERDICT.** After the reviewer completes, call `chorus_get_comments` and find THIS round's `VERDICT:` comment — the one posted after your dispatch, not an older round's. There are exactly three possible outcomes:
|
|
69
69
|
- **VERDICT: PASS** — No issues found. Approve (proposals) or mark AC passed and verify (tasks).
|
|
70
70
|
- **VERDICT: PASS WITH NOTES** — Minor non-blocking notes. Still approve/verify. Notes are informational.
|
|
71
71
|
- **VERDICT: FAIL** — BLOCKERs found. Reject (proposals) or reopen (tasks). Fix the specific BLOCKERs listed in the comment before resubmitting.
|
|
72
|
-
3. **No new VERDICT comment?**
|
|
73
|
-
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.
|
|
72
|
+
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: STOP — do not respawn, do not self-review, do not post a VERDICT of your own. If it posted nothing at all, respawn it ONCE, telling it to stay within its turn budget and reserve its last turns for the VERDICT, then apply this same check again to what the retry posts. An explicit refusal from the retry still means STOP; only a second true silence lets you review the item yourself as a read-only pass using the checklists below and POST the VERDICT — **absence is never a PASS**.
|
|
73
|
+
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.
|
|
74
74
|
5. **Fallback.** If the reviewer is unavailable (e.g., agent type not registered, sub-agent spawn fails), review the item yourself using the quality checklists in the workflows below.
|
|
75
75
|
|
|
76
76
|
---
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spec-lite
|
|
3
|
+
description: Lightweight, Chorus-native local specs for Chorus PM workflows in Pi — 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.1"
|
|
8
|
+
category: project-management
|
|
9
|
+
mcp_server: chorus
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# spec-lite — durable local spec + per-change synced docs
|
|
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
|
+
## Mode (how you got here)
|
|
22
|
+
|
|
23
|
+
The spec mode is computed by the chorus-pi extension's `session_start` handler (`resolveSpecMode`),
|
|
24
|
+
**not by you** — the `## Spec Mode` section of your injected context states the resolved
|
|
25
|
+
`CHORUS_SPEC_MODE`. You are here because it resolved to `lite`; if it is anything else, this skill is a
|
|
26
|
+
no-op — return to the caller. (For the record, the rule: an explicit `CHORUS_SPEC_MODE` wins, else
|
|
27
|
+
OpenSpec when usable, else lite.)
|
|
28
|
+
|
|
29
|
+
## The durable local spec — `<slug>/spec.md`
|
|
30
|
+
|
|
31
|
+
`.chorus/specs/<slug>/spec.md` — `<slug>` (kebab-case) names a **capability/feature, not one change**.
|
|
32
|
+
This is the single, cumulative, human-readable "current truth" of the capability: **edited in place**
|
|
33
|
+
by every change, **never mirrored to Chorus, carries no Chorus ids**. Minimal frontmatter only
|
|
34
|
+
(`slug`, `title`, `status: draft|active|done`, `created`), then plain prose — `## Intent`,
|
|
35
|
+
`## Requirements` (prose + `- [ ]` acceptance points, no `SHALL`/scenario grammar), `## Non-goals`.
|
|
36
|
+
Start from the inline **durable `spec.md` template** below. Its git history is the whole record — no changelog
|
|
37
|
+
section, no Chorus round-trip. **This file NEVER enters the mirror loop.**
|
|
38
|
+
|
|
39
|
+
`status` describes the **capability**, not a single change: `active` while any change is in flight,
|
|
40
|
+
`done` when the current change delivers and none is open. A **new** change against a `done` capability
|
|
41
|
+
reopens it to `active`, back to `done` on delivery.
|
|
42
|
+
|
|
43
|
+
### Template — the durable `spec.md`
|
|
44
|
+
|
|
45
|
+
```markdown
|
|
46
|
+
---
|
|
47
|
+
slug: <kebab-case-capability>
|
|
48
|
+
title: <Capability title>
|
|
49
|
+
status: draft # draft | active | done
|
|
50
|
+
created: <YYYY-MM-DD>
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Intent
|
|
54
|
+
<what this capability is for, in prose>
|
|
55
|
+
|
|
56
|
+
## Requirements
|
|
57
|
+
<prose, no SHALL/scenario grammar>
|
|
58
|
+
- [ ] <acceptance point>
|
|
59
|
+
|
|
60
|
+
## Non-goals
|
|
61
|
+
- <explicitly out of scope>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Per-change dated folders — `<slug>/<YYYY-MM-DD>-<change-slug>/`
|
|
65
|
+
|
|
66
|
+
Each change effort is **one dated folder directly under `<slug>/`** (no `changes/` wrapper), e.g.
|
|
67
|
+
`.chorus/specs/<slug>/2026-09-08-add-export/`. Date + slug so same-day changes don't collide and
|
|
68
|
+
folders sort by date. It holds the **Chorus-typed** docs for THAT change — one file per Document type:
|
|
69
|
+
|
|
70
|
+
| File | `Document.type` | Required? |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `prd.md` | `prd` | **yes** — the primary per-change doc |
|
|
73
|
+
| `tech_design.md` | `tech_design` | optional — the "how" |
|
|
74
|
+
| `adr.md` / `guide.md` / `spec.md` | `adr` / `guide` / `spec` | optional |
|
|
75
|
+
|
|
76
|
+
These files **ARE synced** — each maps to **one persistent Chorus Document** of its type. Their
|
|
77
|
+
frontmatter carries the sync ids `proposalUuid` and `documentUuid` (the type is implied by the
|
|
78
|
+
filename). Start from the inline **dated-folder document template** below. A different change to the same
|
|
79
|
+
capability is a different dated folder. The **current change's** folder is edited and re-mirrored
|
|
80
|
+
throughout its effort (until delivery); only **previously-delivered** dated folders are left frozen —
|
|
81
|
+
you don't reach back and rewrite a past change.
|
|
82
|
+
|
|
83
|
+
> **Two files named `spec.md`, different roles.** The durable `<slug>/spec.md` (local only, no ids) is
|
|
84
|
+
> NOT the same as a per-change `spec`-type doc, which would live at `<slug>/<date>-<slug>/spec.md`
|
|
85
|
+
> (synced, carries ids). Prefer `prd.md` as the per-change primary doc to avoid the confusion.
|
|
86
|
+
|
|
87
|
+
### Template — a dated-folder document
|
|
88
|
+
|
|
89
|
+
The document **type is implied by the filename** (`prd.md` → `prd`, `tech_design.md` → `tech_design`, …),
|
|
90
|
+
**NOT** a frontmatter key.
|
|
91
|
+
|
|
92
|
+
```markdown
|
|
93
|
+
---
|
|
94
|
+
title: <Document title as it appears in Chorus>
|
|
95
|
+
proposalUuid: <uuid> # written on first mirror
|
|
96
|
+
documentUuid: # empty until the draft materializes on approval
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
# <Document title>
|
|
100
|
+
<body — this file's bytes are the source of truth for the Chorus Document>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Flow (one change)
|
|
104
|
+
|
|
105
|
+
1. Confirm mode = `lite` (else no-op).
|
|
106
|
+
2. Create the dated folder `<slug>/<YYYY-MM-DD>-<change-slug>/` and write its **synced** change docs —
|
|
107
|
+
`prd.md` (required), `tech_design.md` etc. only if warranted (use the **dated-folder document template** above).
|
|
108
|
+
3. **Update `<slug>/spec.md` in place** to the new cumulative truth (Requirements, acceptance points,
|
|
109
|
+
`status`) — local only, no sync.
|
|
110
|
+
4. Create the proposal container with one literal locator line in `description` (own line, no trailing
|
|
111
|
+
punctuation) so develop finds the change:
|
|
112
|
+
`Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/`
|
|
113
|
+
5. **Mirror the dated folder's docs** to Chorus (below). Add tasks via `chorus_pm_add_task_draft` —
|
|
114
|
+
**no `tasks.md`**, no CLI / validate / archive, no delta grammar. **Tasks live in Chorus.**
|
|
115
|
+
6. Develop → keep editing `spec.md` + the change docs, re-mirroring the change docs as work lands and
|
|
116
|
+
ticking acceptance points. On delivery set the durable `spec.md` `status: done`.
|
|
117
|
+
|
|
118
|
+
## Mirror — only the dated-folder docs (never `spec.md`)
|
|
119
|
+
|
|
120
|
+
Every dated-folder `<type>.md` maps to **one persistent Chorus Document** of that `type`, tracked by
|
|
121
|
+
`documentUuid` in the file's frontmatter. Fill `content` from the file's bytes with `--arg-file` —
|
|
122
|
+
never re-type the body (drifts, burns ~20k tokens). One call per file; resolve identity by
|
|
123
|
+
`documentUuid` / `(proposalUuid, type)`, **never by `title` alone** (a lookup finding zero or >1 MUST
|
|
124
|
+
**halt**). Guard every call with the `chorus_check_response` halt-on-error helper (`openspec-aware`
|
|
125
|
+
§6). No `chorus` on `PATH`? Fall back to `chorus-mcp-call.sh` + `json_encode_file` (`openspec-aware` §3.6);
|
|
126
|
+
the extension resolves the bundled wrapper and surfaces its path in the session_start context.
|
|
127
|
+
**`<slug>/spec.md` is NEVER in this loop.**
|
|
128
|
+
|
|
129
|
+
- **First time a doc is authored** (its dated folder is new): write `proposalUuid` into frontmatter,
|
|
130
|
+
mirror into a proposal **draft** —
|
|
131
|
+
`chorus mcp call chorus_pm_add_document_draft "{\"proposalUuid\":\"$P\",\"type\":\"prd\",\"title\":\"PRD: $TITLE\"}" --arg-file content=".chorus/specs/$SLUG/$DATED/prd.md"`.
|
|
132
|
+
Edit the draft via `chorus_pm_update_document_draft` (returned `draftUuid`) before approval. On
|
|
133
|
+
approval it materializes into a persistent Document — resolve by `(proposalUuid, type)` via
|
|
134
|
+
`chorus_get_documents`, record `documentUuid` in frontmatter, re-mirror once so local == Chorus.
|
|
135
|
+
- **Later edits** (a doc that already has a `documentUuid`): edit the file, then
|
|
136
|
+
`chorus mcp call chorus_pm_update_document "{\"documentUuid\":\"$D\"}" --arg-file content=".chorus/specs/$SLUG/$DATED/<type>.md"`.
|
|
137
|
+
Each update **auto-increments the Document version** — that version history is the change doc's
|
|
138
|
+
record in Chorus, alongside git.
|
|
139
|
+
|
|
140
|
+
## 留痕: git history + Document versions
|
|
141
|
+
|
|
142
|
+
`git log -- .chorus/specs/$SLUG/` is the audit trail — the durable `spec.md`'s in-place diffs plus each
|
|
143
|
+
dated folder's change docs; the mirrored Documents' auto-incremented versions are the parallel record
|
|
144
|
+
in Chorus. No changelog section to maintain. Only `.chorus/specs/` is version-controlled (`.chorus/*` +
|
|
145
|
+
`!.chorus/specs/`).
|
|
146
|
+
|
|
147
|
+
**Single-writer:** the folder is shared — in a multi-task wave only the **orchestrator / main agent**
|
|
148
|
+
edits + re-mirrors; parallel workers report via `chorus_report_work` only, re-reading before any write.
|
|
149
|
+
**Task state lives in Chorus**, not the docs — the `- [ ]` points are acceptance intent, not a tracker.
|
package/skills/yolo/SKILL.md
CHANGED
|
@@ -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.
|
|
7
|
+
version: "0.18.1"
|
|
8
8
|
category: project-management
|
|
9
9
|
mcp_server: chorus
|
|
10
10
|
---
|
|
@@ -188,21 +188,17 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
|
|
|
188
188
|
|
|
189
189
|
#### Step 1.4: Create Proposal
|
|
190
190
|
|
|
191
|
-
1. **
|
|
191
|
+
1. **Read the spec mode (already computed).** The chorus-pi extension's `session_start` handler (`resolveSpecMode`) has already resolved it — do NOT re-derive. Read the `## Spec Mode` section: `CHORUS_SPEC_MODE=<lite|openspec|off>` + a routing note. Act on it: `openspec` (usable, shows `CHORUS_OPENSPEC_ACTIVE=1`) → **2a**; `off` → **2b**; `lite` → **2c**. If it says the mode **cannot be honored** (explicit `openspec` but unusable), **halt** and surface it — do NOT fall back or enter 2a with no OpenSpec. (No `## Spec Mode`? See `openspec-aware` §1 manual fallback.) This matters because yolo runs unattended.
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
- `CHORUS_OPENSPEC_ACTIVE=0` → free-form branch (sub-step 2b below).
|
|
195
|
-
|
|
196
|
-
This is mandatory — yolo runs unattended, so silently picking the wrong mode is exactly the failure scenario the detection contract exists to prevent.
|
|
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.
|
|
193
|
+
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
194
|
|
|
200
195
|
```
|
|
201
196
|
chorus_pm_create_proposal({
|
|
202
197
|
projectUuid: "<project-uuid>",
|
|
203
198
|
title: "<feature name>",
|
|
204
|
-
description: "<summary>\n\nOpenSpec change slug: <slug>",
|
|
205
|
-
// description: "<summary
|
|
199
|
+
description: "<summary>\n\nOpenSpec change slug: <slug>", // OpenSpec (2a)
|
|
200
|
+
// description: "<summary>\n\nSpec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/", // spec-lite (2c)
|
|
201
|
+
// description: "<summary>", // free-form (2b)
|
|
206
202
|
inputType: "idea",
|
|
207
203
|
inputUuids: ["<idea-uuid>"]
|
|
208
204
|
})
|
|
@@ -220,7 +216,7 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
|
|
|
220
216
|
|
|
221
217
|
Then continue to step 3 (task drafts).
|
|
222
218
|
|
|
223
|
-
**2b. Free-form mode (`
|
|
219
|
+
**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
220
|
|
|
225
221
|
```
|
|
226
222
|
chorus_pm_add_document_draft({
|
|
@@ -231,6 +227,8 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
|
|
|
231
227
|
})
|
|
232
228
|
```
|
|
233
229
|
|
|
230
|
+
**2c. spec-lite mode (resolved mode = lite).** Load the `spec-lite` skill (`/skill:spec-lite`). 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-mcp-call.sh` fallback when `chorus` not on `PATH`). **`spec.md` is never mirrored.** No `openspec/changes/` scaffold; no `tasks.md`. Then continue to step 3.
|
|
231
|
+
|
|
234
232
|
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
233
|
```
|
|
236
234
|
# First task
|
|
@@ -272,6 +270,20 @@ In /yolo mode, the agent generates elaboration questions and answers them itself
|
|
|
272
270
|
|
|
273
271
|
---
|
|
274
272
|
|
|
273
|
+
### Reviewer contract (applies to every review gate below)
|
|
274
|
+
|
|
275
|
+
Every gate in Phases 2, 4 and 4.5 follows the same three steps. They are written once here; the phases below only name their entity and their stage-specific actions.
|
|
276
|
+
|
|
277
|
+
1. **Spawn and wait.** Spawn the reviewer as a read-only sub-agent, then wait for it: spawn it via the blocking `subagent` tool, which waits for the reviewer to finish. Read the verdict from the reviewer's `VERDICT:` comment on the entity.
|
|
278
|
+
2. **Read THIS round's VERDICT.** Call `chorus_get_comments` on the entity and find the `VERDICT:` comment posted **after your dispatch**, not an older round's. Do not advance the gate before you have read it.
|
|
279
|
+
3. **No VERDICT for this round?** Check what the reviewer *did* post:
|
|
280
|
+
- **A reported round limit, or any other explicit refusal to review** — a deliberate escalation to a human. STOP: do not respawn, do not self-review, do not post a VERDICT of your own.
|
|
281
|
+
- **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.
|
|
282
|
+
|
|
283
|
+
**Absence is never a PASS**, and a round limit reached by someone else is never yours to clear.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
275
287
|
### Phase 2: Proposal Review Loop
|
|
276
288
|
|
|
277
289
|
After `chorus_pm_submit_proposal`, the extension nudges you to spawn `chorus-proposal-reviewer`. You MUST manually spawn it as a read-only sub-agent via the blocking `subagent` tool (it waits for the VERDICT). Wait for it to complete, then:
|
|
@@ -280,7 +292,7 @@ After `chorus_pm_submit_proposal`, the extension nudges you to spawn `chorus-pro
|
|
|
280
292
|
```
|
|
281
293
|
chorus_get_comments({ targetType: "proposal", targetUuid: "<proposal-uuid>" })
|
|
282
294
|
```
|
|
283
|
-
Look for
|
|
295
|
+
Look for THIS round's `VERDICT:` comment — the one posted after your dispatch, not an older round's.
|
|
284
296
|
|
|
285
297
|
2. **Act on the VERDICT:**
|
|
286
298
|
|
|
@@ -314,7 +326,7 @@ After `chorus_pm_submit_proposal`, the extension nudges you to spawn `chorus-pro
|
|
|
314
326
|
Proposal UUID: <uuid>"
|
|
315
327
|
```
|
|
316
328
|
|
|
317
|
-
4. **No new VERDICT
|
|
329
|
+
4. **No new VERDICT for this round?** Apply step 3 of the **Reviewer contract**, reviewing the proposal yourself if the reviewer stays silent.
|
|
318
330
|
|
|
319
331
|
---
|
|
320
332
|
|
|
@@ -411,7 +423,7 @@ for each task in wave_tasks:
|
|
|
411
423
|
|
|
412
424
|
# 3. Read task-reviewer VERDICT
|
|
413
425
|
comments = chorus_get_comments({ targetType: "task", targetUuid: "<task-uuid>" })
|
|
414
|
-
# Find
|
|
426
|
+
# Find THIS round's "VERDICT:" comment — the one posted after your dispatch, not an older round's
|
|
415
427
|
|
|
416
428
|
# 4. Act on VERDICT — three possible outcomes:
|
|
417
429
|
if VERDICT is "PASS":
|
|
@@ -449,7 +461,7 @@ ESCALATE: "Task '{title}' failed review after {maxRounds} rounds.
|
|
|
449
461
|
|
|
450
462
|
Continue with remaining tasks -- do not halt the entire pipeline for one stuck task.
|
|
451
463
|
|
|
452
|
-
**No new VERDICT
|
|
464
|
+
**No new VERDICT for this round?** Apply step 3 of the **Reviewer contract**, reviewing the task yourself if the reviewer stays silent.
|
|
453
465
|
|
|
454
466
|
---
|
|
455
467
|
|
|
@@ -465,7 +477,7 @@ subagent({ agent: "chorus-code-reviewer",
|
|
|
465
477
|
|
|
466
478
|
# Read its VERDICT on the idea
|
|
467
479
|
comments = chorus_get_comments({ targetType: "idea", targetUuid: "<idea-uuid>" })
|
|
468
|
-
# Find
|
|
480
|
+
# Find THIS round's "VERDICT:" comment — the one posted after your dispatch, not an older round's
|
|
469
481
|
```
|
|
470
482
|
|
|
471
483
|
Act on the VERDICT:
|
|
@@ -479,7 +491,7 @@ ESCALATE: "Idea '<title>' failed code review after {CHORUS_MAX_CODE_REVIEW_ROUND
|
|
|
479
491
|
Last BLOCKERs: <list>. Manual intervention needed. Idea UUID: <uuid>"
|
|
480
492
|
```
|
|
481
493
|
|
|
482
|
-
**No new VERDICT
|
|
494
|
+
**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.
|
|
483
495
|
|
|
484
496
|
> The code-review gateway is **behavioral**, consistent with the proposal/task reviewers: its verdict is advisory and does not change the Idea's stored status. The /yolo orchestrator honors it — PASS to ship, FAIL to loop. It runs **before** the completion report so the report is never written for a feature with an outstanding FAIL.
|
|
485
497
|
|