cclaw-cli 5.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-linter/brainstorm.js +1 -1
- package/dist/artifact-linter/design.js +1 -1
- package/dist/artifact-linter/scope.js +1 -1
- package/dist/artifact-linter/shared.d.ts +40 -12
- package/dist/artifact-linter/shared.js +88 -62
- package/dist/content/skills-elicitation.js +29 -15
- package/dist/content/skills.js +10 -4
- package/dist/content/stage-schema.d.ts +29 -0
- package/dist/content/stage-schema.js +7 -0
- package/dist/content/stages/brainstorm.js +2 -2
- package/dist/content/stages/design.js +2 -2
- package/dist/content/stages/scope.js +2 -2
- package/dist/content/subagents.js +3 -1
- package/dist/content/templates.d.ts +2 -2
- package/dist/content/templates.js +17 -11
- package/dist/delegation.d.ts +16 -0
- package/dist/delegation.js +64 -3
- package/dist/harness-adapters.js +1 -1
- package/dist/internal/advance-stage/advance.d.ts +2 -0
- package/dist/internal/advance-stage/advance.js +2 -1
- package/package.json +1 -1
|
@@ -24,7 +24,7 @@ export async function lintBrainstormStage(ctx) {
|
|
|
24
24
|
findings.push({
|
|
25
25
|
section: "qa_log_unconverged",
|
|
26
26
|
required: !floor.skipQuestionsAdvisory,
|
|
27
|
-
rule: "[P1] qa_log_unconverged — Q&A Log has not converged for this stage. Continue elicitation until forcing-question
|
|
27
|
+
rule: "[P1] qa_log_unconverged — Q&A Log has not converged for this stage. Continue elicitation until every forcing-question topic id is tagged with `[topic:<id>]` on at least one row, the last 2 rows produce no decision-changing impact (Ralph-Loop), or an explicit user stop-signal row is appended.",
|
|
28
28
|
found: floor.ok,
|
|
29
29
|
details: floor.details
|
|
30
30
|
});
|
|
@@ -224,7 +224,7 @@ export async function lintDesignStage(ctx) {
|
|
|
224
224
|
findings.push({
|
|
225
225
|
section: "qa_log_unconverged",
|
|
226
226
|
required: !floor.skipQuestionsAdvisory,
|
|
227
|
-
rule: "[P1] qa_log_unconverged — Q&A Log has not converged for this stage. Continue elicitation until forcing-question
|
|
227
|
+
rule: "[P1] qa_log_unconverged — Q&A Log has not converged for this stage. Continue elicitation until every forcing-question topic id is tagged with `[topic:<id>]` on at least one row, the last 2 rows produce no decision-changing impact (Ralph-Loop), or an explicit user stop-signal row is appended.",
|
|
228
228
|
found: floor.ok,
|
|
229
229
|
details: floor.details
|
|
230
230
|
});
|
|
@@ -25,7 +25,7 @@ export async function lintScopeStage(ctx) {
|
|
|
25
25
|
findings.push({
|
|
26
26
|
section: "qa_log_unconverged",
|
|
27
27
|
required: !floor.skipQuestionsAdvisory,
|
|
28
|
-
rule: "[P1] qa_log_unconverged — Q&A Log has not converged for this stage. Continue elicitation until forcing-question
|
|
28
|
+
rule: "[P1] qa_log_unconverged — Q&A Log has not converged for this stage. Continue elicitation until every forcing-question topic id is tagged with `[topic:<id>]` on at least one row, the last 2 rows produce no decision-changing impact (Ralph-Loop), or an explicit user stop-signal row is appended.",
|
|
29
29
|
found: floor.ok,
|
|
30
30
|
details: floor.details
|
|
31
31
|
});
|
|
@@ -5,6 +5,20 @@ import { type FlowStage, type FlowTrack } from "../types.js";
|
|
|
5
5
|
* convergence floor is enforced.
|
|
6
6
|
*/
|
|
7
7
|
export declare const ELICITATION_STAGES: ReadonlySet<FlowStage>;
|
|
8
|
+
/**
|
|
9
|
+
* Wave 24 (v6.0.0) — language-neutral forcing-question topic descriptor.
|
|
10
|
+
*
|
|
11
|
+
* Each forcing-question row in a stage's checklist now declares topics as
|
|
12
|
+
* `id: human-readable label` pairs (e.g. `pain: what pain are we solving`).
|
|
13
|
+
* The `id` (kebab-case ASCII) is the machine-key authors stamp on Q&A Log
|
|
14
|
+
* rows via `[topic:<id>]` so the linter can verify coverage in ANY natural
|
|
15
|
+
* language (RU/EN/UA/etc.). Wave 23's English keyword fallback was removed
|
|
16
|
+
* because it silently mis-reported convergence on RU/UA Q&A.
|
|
17
|
+
*/
|
|
18
|
+
export interface ForcingQuestionTopic {
|
|
19
|
+
id: string;
|
|
20
|
+
topic: string;
|
|
21
|
+
}
|
|
8
22
|
export interface QaLogFloorOptions {
|
|
9
23
|
/**
|
|
10
24
|
* When true, downgrades the finding to advisory (`required: false`).
|
|
@@ -12,11 +26,12 @@ export interface QaLogFloorOptions {
|
|
|
12
26
|
*/
|
|
13
27
|
skipQuestions?: boolean;
|
|
14
28
|
/**
|
|
15
|
-
* Optional pre-extracted forcing-question
|
|
16
|
-
* evaluator calls `extractForcingQuestions(stage)` which
|
|
17
|
-
* stage's checklist row.
|
|
29
|
+
* Optional pre-extracted forcing-question topic descriptors. When
|
|
30
|
+
* omitted, the evaluator calls `extractForcingQuestions(stage)` which
|
|
31
|
+
* scans the stage's checklist row. Strings are accepted as topic IDs
|
|
32
|
+
* (label = id) for callers that build their own list.
|
|
18
33
|
*/
|
|
19
|
-
forcingQuestions?: string
|
|
34
|
+
forcingQuestions?: ReadonlyArray<ForcingQuestionTopic | string>;
|
|
20
35
|
}
|
|
21
36
|
export interface QaLogFloorResult {
|
|
22
37
|
/** Whether convergence is satisfied (passes the gate). */
|
|
@@ -53,17 +68,30 @@ export interface QaLogFloorResult {
|
|
|
53
68
|
details: string;
|
|
54
69
|
}
|
|
55
70
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
71
|
+
* Parse a single checklist row into the list of forcing-question topic
|
|
72
|
+
* descriptors it declares. Returns `null` when the row is not a
|
|
73
|
+
* forcing-questions header. Throws when the header is found but its
|
|
74
|
+
* body does not match the Wave 24 `id: topic; id: topic; ...` syntax
|
|
75
|
+
* — authors fix the stage definition rather than silently ship
|
|
76
|
+
* un-coverable topics.
|
|
77
|
+
*
|
|
78
|
+
* Exposed for unit tests that exercise the parser without depending on
|
|
79
|
+
* the live stage schema.
|
|
80
|
+
*/
|
|
81
|
+
export declare function parseForcingQuestionsRow(row: string, context?: string): ForcingQuestionTopic[] | null;
|
|
82
|
+
/**
|
|
83
|
+
* Extract forcing-question topics from a stage's checklist.
|
|
84
|
+
*
|
|
85
|
+
* Wave 24 (v6.0.0): only the new `id: topic; id: topic; ...` syntax is
|
|
86
|
+
* accepted. Throws when the syntax is malformed so authors fix the
|
|
87
|
+
* stage definition rather than silently shipping un-coverable topics.
|
|
61
88
|
*
|
|
62
89
|
* Returns empty array when no forcing-questions row is present (caller
|
|
63
|
-
*
|
|
64
|
-
*
|
|
90
|
+
* treats absence as "no forcing requirement" — convergence falls back
|
|
91
|
+
* to the no-new-decisions / stop-signal detectors). Returning [] when
|
|
92
|
+
* the row exists but lists no segments is also legal.
|
|
65
93
|
*/
|
|
66
|
-
export declare function extractForcingQuestions(stage: FlowStage):
|
|
94
|
+
export declare function extractForcingQuestions(stage: FlowStage): ForcingQuestionTopic[];
|
|
67
95
|
/**
|
|
68
96
|
* Evaluate the Q&A Log convergence floor for a brainstorm / scope /
|
|
69
97
|
* design artifact. Returns ok=true when convergence is reached or any
|
|
@@ -86,15 +86,70 @@ function detectStopSignal(rows) {
|
|
|
86
86
|
return false;
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
|
|
93
|
-
|
|
89
|
+
* Validate the kebab-case ASCII shape of a forcing-question topic ID.
|
|
90
|
+
* Wave 24 enforces that IDs are short, language-neutral identifiers
|
|
91
|
+
* authors can paste into a `[topic:<id>]` tag without typos.
|
|
92
|
+
*/
|
|
93
|
+
const TOPIC_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/u;
|
|
94
|
+
function isValidTopicId(id) {
|
|
95
|
+
return TOPIC_ID_PATTERN.test(id);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Parse a single checklist row into the list of forcing-question topic
|
|
99
|
+
* descriptors it declares. Returns `null` when the row is not a
|
|
100
|
+
* forcing-questions header. Throws when the header is found but its
|
|
101
|
+
* body does not match the Wave 24 `id: topic; id: topic; ...` syntax
|
|
102
|
+
* — authors fix the stage definition rather than silently ship
|
|
103
|
+
* un-coverable topics.
|
|
104
|
+
*
|
|
105
|
+
* Exposed for unit tests that exercise the parser without depending on
|
|
106
|
+
* the live stage schema.
|
|
107
|
+
*/
|
|
108
|
+
export function parseForcingQuestionsRow(row, context = "row") {
|
|
109
|
+
const headerMatch = /\*\*\s*[A-Za-z]+\s+forcing\s+questions\s*\([^)]*\)\s*\*\*\s*(?:[—\-–:]+)?\s*(.+)/iu.exec(row);
|
|
110
|
+
if (!headerMatch)
|
|
111
|
+
return null;
|
|
112
|
+
const body = (headerMatch[1] ?? "").trim();
|
|
113
|
+
if (body.length === 0)
|
|
114
|
+
return [];
|
|
115
|
+
// Take everything up to the first sentence-ending `.` followed by a
|
|
116
|
+
// space + capital letter. We split on `;` only; commas are part of
|
|
117
|
+
// human labels. Authors stop the list with `.` so the trailing
|
|
118
|
+
// prose ("Tag the matching ...") is excluded.
|
|
119
|
+
const listSection = body.split(/\.\s+(?=[A-Z])/u)[0] ?? body;
|
|
120
|
+
const segments = listSection
|
|
121
|
+
.split(/;\s*/u)
|
|
122
|
+
.map((segment) => segment.trim())
|
|
123
|
+
.filter((segment) => segment.length > 0);
|
|
124
|
+
const topics = [];
|
|
125
|
+
for (const segment of segments) {
|
|
126
|
+
const match = /^[`*_]?\s*([A-Za-z0-9][A-Za-z0-9-]*)\s*[`*_]?\s*:\s*(.+?)\s*$/u.exec(segment);
|
|
127
|
+
if (!match) {
|
|
128
|
+
throw new Error(`parseForcingQuestionsRow(${context}): segment "${segment}" does not match required \`id: topic\` syntax. Wave 24 (v6.0.0) requires \`id: topic; id: topic; ...\` form.`);
|
|
129
|
+
}
|
|
130
|
+
const id = (match[1] ?? "").toLowerCase();
|
|
131
|
+
const topic = (match[2] ?? "").replace(/[`*_]+$/u, "").trim();
|
|
132
|
+
if (!isValidTopicId(id)) {
|
|
133
|
+
throw new Error(`parseForcingQuestionsRow(${context}): invalid topic id "${id}" in segment "${segment}". IDs must match ${TOPIC_ID_PATTERN.source}.`);
|
|
134
|
+
}
|
|
135
|
+
if (topic.length === 0) {
|
|
136
|
+
throw new Error(`parseForcingQuestionsRow(${context}): empty topic label after id "${id}" in segment "${segment}".`);
|
|
137
|
+
}
|
|
138
|
+
topics.push({ id, topic });
|
|
139
|
+
}
|
|
140
|
+
return topics;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Extract forcing-question topics from a stage's checklist.
|
|
144
|
+
*
|
|
145
|
+
* Wave 24 (v6.0.0): only the new `id: topic; id: topic; ...` syntax is
|
|
146
|
+
* accepted. Throws when the syntax is malformed so authors fix the
|
|
147
|
+
* stage definition rather than silently shipping un-coverable topics.
|
|
94
148
|
*
|
|
95
149
|
* Returns empty array when no forcing-questions row is present (caller
|
|
96
|
-
*
|
|
97
|
-
*
|
|
150
|
+
* treats absence as "no forcing requirement" — convergence falls back
|
|
151
|
+
* to the no-new-decisions / stop-signal detectors). Returning [] when
|
|
152
|
+
* the row exists but lists no segments is also legal.
|
|
98
153
|
*/
|
|
99
154
|
export function extractForcingQuestions(stage) {
|
|
100
155
|
let checklist;
|
|
@@ -105,61 +160,29 @@ export function extractForcingQuestions(stage) {
|
|
|
105
160
|
return [];
|
|
106
161
|
}
|
|
107
162
|
for (const row of checklist) {
|
|
108
|
-
const
|
|
109
|
-
if (
|
|
163
|
+
const parsed = parseForcingQuestionsRow(row, `stage=${stage}`);
|
|
164
|
+
if (parsed === null)
|
|
110
165
|
continue;
|
|
111
|
-
|
|
112
|
-
.replace(/\.$/u, "")
|
|
113
|
-
.trim();
|
|
114
|
-
if (body.length === 0)
|
|
115
|
-
return [];
|
|
116
|
-
return body
|
|
117
|
-
.split(/,\s*(?:and\s+)?|\s+and\s+/iu)
|
|
118
|
-
.map((topic) => topic.trim())
|
|
119
|
-
.filter((topic) => topic.length > 0)
|
|
120
|
-
.map((topic) => topic
|
|
121
|
-
.replace(/^[*_`]+|[*_`]+$/gu, "")
|
|
122
|
-
.replace(/^(?:what|who|where|which|how|is|are|do|does|did|can|will|would|could|should|may|might)\s+/iu, "")
|
|
123
|
-
.replace(/\?+$/u, "")
|
|
124
|
-
.trim())
|
|
125
|
-
.filter((topic) => topic.length > 0);
|
|
166
|
+
return parsed;
|
|
126
167
|
}
|
|
127
168
|
return [];
|
|
128
169
|
}
|
|
129
170
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
171
|
+
* Detect whether a Q&A Log row carries an explicit `[topic:<id>]` tag
|
|
172
|
+
* for the requested forcing-topic id. Matching is case-insensitive on
|
|
173
|
+
* the id, ASCII-only on the tag boundary. NO keyword fallback: the user
|
|
174
|
+
* must stamp the tag in any cell of the row.
|
|
133
175
|
*/
|
|
134
|
-
function
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
"for", "and", "or", "but", "if", "then", "else", "with", "without", "by", "as",
|
|
138
|
-
"we", "us", "our", "they", "them", "their", "you", "your", "i", "me", "my",
|
|
139
|
-
"this", "that", "these", "those", "it", "its", "do", "does", "did", "can",
|
|
140
|
-
"will", "would", "should", "could", "may", "might", "any", "some", "no", "not",
|
|
141
|
-
"from", "into", "onto", "upon", "than", "very", "much", "many", "more", "most",
|
|
142
|
-
"must", "have", "has", "had", "been", "being", "where", "when", "while",
|
|
143
|
-
"what", "which", "who", "whose", "whom", "why", "how", "non"
|
|
144
|
-
]);
|
|
145
|
-
return topic
|
|
146
|
-
.toLowerCase()
|
|
147
|
-
.split(/[\s\-/.,;:()\[\]{}'"`*_]+/u)
|
|
148
|
-
.map((token) => token.replace(/[^\p{L}\p{N}-]/gu, ""))
|
|
149
|
-
.filter((token) => token.length >= 3 && !STOP_WORDS.has(token));
|
|
150
|
-
}
|
|
151
|
-
function isTopicAddressed(topic, rows) {
|
|
152
|
-
const keywords = topicKeywords(topic);
|
|
153
|
-
if (keywords.length === 0)
|
|
154
|
-
return true;
|
|
155
|
-
const minHits = keywords.length === 1 ? 1 : Math.min(2, keywords.length);
|
|
176
|
+
function isTopicAddressed(id, rows) {
|
|
177
|
+
const needle = id.toLowerCase();
|
|
178
|
+
const tagPattern = /\[topic:\s*([A-Za-z0-9][A-Za-z0-9-]*)\s*\]/giu;
|
|
156
179
|
for (const row of rows) {
|
|
157
|
-
const haystack = row.join(" | ")
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (
|
|
180
|
+
const haystack = row.join(" | ");
|
|
181
|
+
tagPattern.lastIndex = 0;
|
|
182
|
+
let match;
|
|
183
|
+
while ((match = tagPattern.exec(haystack)) !== null) {
|
|
184
|
+
const candidate = (match[1] ?? "").toLowerCase();
|
|
185
|
+
if (candidate === needle)
|
|
163
186
|
return true;
|
|
164
187
|
}
|
|
165
188
|
}
|
|
@@ -210,18 +233,21 @@ export function evaluateQaLogFloor(qaLogBody, track, stage, options = {}) {
|
|
|
210
233
|
const count = substantiveRows.length;
|
|
211
234
|
const hasStopSignal = detectStopSignal(rows);
|
|
212
235
|
const skipQuestionsAdvisory = options.skipQuestions === true;
|
|
213
|
-
const forcingTopics = options.forcingQuestions ?? extractForcingQuestions(stage);
|
|
236
|
+
const forcingTopics = (options.forcingQuestions ?? extractForcingQuestions(stage)).map((entry) => (typeof entry === "string" ? { id: entry, topic: entry } : entry));
|
|
214
237
|
const forcingCovered = [];
|
|
215
238
|
const forcingPending = [];
|
|
216
239
|
for (const topic of forcingTopics) {
|
|
217
|
-
if (isTopicAddressed(topic, rows))
|
|
218
|
-
forcingCovered.push(topic);
|
|
240
|
+
if (isTopicAddressed(topic.id, rows))
|
|
241
|
+
forcingCovered.push(topic.id);
|
|
219
242
|
else
|
|
220
|
-
forcingPending.push(topic);
|
|
243
|
+
forcingPending.push(topic.id);
|
|
221
244
|
}
|
|
222
245
|
const noNewDecisions = lastTwoRowsAllNoDecision(substantiveRows);
|
|
223
246
|
const allForcingCovered = forcingTopics.length > 0 ? forcingPending.length === 0 : count >= 1;
|
|
224
247
|
const ok = allForcingCovered || noNewDecisions || hasStopSignal;
|
|
248
|
+
const pendingIdsBracket = forcingPending.length > 0
|
|
249
|
+
? `[${forcingPending.join(", ")}]`
|
|
250
|
+
: "[none]";
|
|
225
251
|
let details;
|
|
226
252
|
if (ok) {
|
|
227
253
|
if (allForcingCovered && forcingTopics.length > 0) {
|
|
@@ -232,7 +258,7 @@ export function evaluateQaLogFloor(qaLogBody, track, stage, options = {}) {
|
|
|
232
258
|
}
|
|
233
259
|
else if (noNewDecisions) {
|
|
234
260
|
const remaining = forcingPending.length > 0
|
|
235
|
-
? ` ${forcingPending.length} forcing topic
|
|
261
|
+
? ` ${forcingPending.length} forcing topic IDs still pending: ${pendingIdsBracket} (Ralph-Loop convergence overrode coverage).`
|
|
236
262
|
: " Ralph-Loop convergence detector says no new decision-changing rows in the last 2 turns.";
|
|
237
263
|
details = `Q&A Log converged via no-new-decisions detector at ${count} row(s).${remaining}`;
|
|
238
264
|
}
|
|
@@ -241,10 +267,10 @@ export function evaluateQaLogFloor(qaLogBody, track, stage, options = {}) {
|
|
|
241
267
|
}
|
|
242
268
|
}
|
|
243
269
|
else if (skipQuestionsAdvisory) {
|
|
244
|
-
details = `Q&A Log unconverged at ${count} row(s); --skip-questions flag downgraded the finding to advisory.
|
|
270
|
+
details = `Q&A Log unconverged at ${count} row(s); --skip-questions flag downgraded the finding to advisory. Forcing topic IDs pending: ${pendingIdsBracket}.`;
|
|
245
271
|
}
|
|
246
272
|
else {
|
|
247
|
-
details = `Q&A Log unconverged at ${count} row(s).
|
|
273
|
+
details = `Q&A Log unconverged at ${count} row(s). Forcing topic IDs pending: ${pendingIdsBracket}. Tag each Q&A row with \`[topic:<id>]\` to mark coverage, append a no-new-decisions pair, or record an explicit user stop-signal row.`;
|
|
248
274
|
}
|
|
249
275
|
// Surface advisory budget hint for harness UI without re-introducing a
|
|
250
276
|
// blocking count. `recommended` is the soft budget per track/stage.
|
|
@@ -47,7 +47,7 @@ These behaviors are the exact reason this skill exists. The linter will block yo
|
|
|
47
47
|
- Ask exactly one question per turn and wait for the answer before asking the next one.
|
|
48
48
|
- Use harness-native question tools first; prose fallback is allowed only when the tool is unavailable.
|
|
49
49
|
- Keep a running Q&A trace in the active artifact under \`## Q&A Log\` in \`${RUNTIME_ROOT}/artifacts/\` as append-only rows.
|
|
50
|
-
- **Convergence floor**: do NOT advance the stage (do NOT call \`stage-complete.mjs\`) until Q&A converges. Convergence is reached when ANY of: (a)
|
|
50
|
+
- **Convergence floor**: do NOT advance the stage (do NOT call \`stage-complete.mjs\`) until Q&A converges. Convergence is reached when ANY of: (a) every forcing-question topic id is tagged \`[topic:<id>]\` on at least one \`## Q&A Log\` row, (b) the last 2 substantive rows produce no decision-changing impact (\`skip\`/\`continue\`/\`no-change\`/\`done\`), or (c) an explicit user stop-signal row is recorded. The linter rule \`qa_log_unconverged\` enforces this; \`stage-complete\` will fail otherwise. Wave 24 (v6.0.0) made the topic tag MANDATORY (no English keyword fallback) so the gate works in any natural language.
|
|
51
51
|
- **NEVER run shell hash commands** (\`shasum\`, \`sha256sum\`, \`md5sum\`, \`Get-FileHash\`, \`certutil\`, etc.) to compute artifact hashes. If a linter ever asks you for a hash, that is a linter bug — report failure and stop, do not auto-fix in bash.
|
|
52
52
|
- **NEVER paste cclaw command lines into chat** (e.g. \`node .cclaw/hooks/stage-complete.mjs ... --evidence-json '{...}'\`). Run them via the tool layer; report only the resulting summary. The user does not run cclaw manually and seeing the command line is noise.
|
|
53
53
|
|
|
@@ -127,24 +127,38 @@ How to use the columns:
|
|
|
127
127
|
- \`skipped (already covered: turn N)\` — answered implicitly by an earlier reply; cite the turn.
|
|
128
128
|
- \`waived (user override)\` — user explicitly waived this question.
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
### Topic tagging (MANDATORY for forcing-question rows)
|
|
131
|
+
|
|
132
|
+
Each forcing question has a stable topic id (kebab-case ASCII, e.g. \`pain\`, \`do-nothing\`, \`data-flow\`). Tag the matching Q&A Log row's \`Decision impact\` cell with \`[topic:<id>]\` so the linter can verify coverage in any natural language. This is a **HARD requirement** in Wave 24 (v6.0.0): the linter no longer keyword-matches English question prose, so an un-tagged row does NOT count toward coverage even if the answer fully addresses the topic.
|
|
133
|
+
|
|
134
|
+
RU example (after asking \`pain\` in Russian):
|
|
135
|
+
|
|
136
|
+
\`\`\`
|
|
137
|
+
| Turn | Question | User answer (1-line) | Decision impact |
|
|
138
|
+
|---|---|---|---|
|
|
139
|
+
| 1 | Какую боль мы решаем? | Регистрация занимает 30 минут. | scope-shaping [topic:pain] |
|
|
140
|
+
\`\`\`
|
|
141
|
+
|
|
142
|
+
Multiple tags in one row are allowed when one answer covers several topics: \`[topic:pain] [topic:do-nothing]\`. Stop-signal rows do NOT need a tag.
|
|
143
|
+
|
|
144
|
+
Stage forcing question lists (id → topic):
|
|
131
145
|
|
|
132
146
|
- **Brainstorm**:
|
|
133
|
-
- What pain are we solving?
|
|
134
|
-
- What is the most direct path?
|
|
135
|
-
- What happens if we do nothing?
|
|
136
|
-
- Who is the operator/user impacted first?
|
|
137
|
-
- What are non-negotiable no-go boundaries?
|
|
147
|
+
- \`pain\` — What pain are we solving?
|
|
148
|
+
- \`direct-path\` — What is the most direct path?
|
|
149
|
+
- \`do-nothing\` — What happens if we do nothing?
|
|
150
|
+
- \`operator\` — Who is the operator/user impacted first?
|
|
151
|
+
- \`no-go\` — What are non-negotiable no-go boundaries?
|
|
138
152
|
- **Scope**:
|
|
139
|
-
- What is definitely in and definitely out?
|
|
140
|
-
- Which decisions are already locked upstream?
|
|
141
|
-
- What is the rollback path if this fails?
|
|
142
|
-
- What are the top failure modes we must design for?
|
|
153
|
+
- \`in-out\` — What is definitely in and definitely out?
|
|
154
|
+
- \`locked-upstream\` — Which decisions are already locked upstream?
|
|
155
|
+
- \`rollback\` — What is the rollback path if this fails?
|
|
156
|
+
- \`failure-modes\` — What are the top failure modes we must design for?
|
|
143
157
|
- **Design**:
|
|
144
|
-
- What is the data flow end-to-end?
|
|
145
|
-
- Where are the seams/interfaces and ownership boundaries?
|
|
146
|
-
- Which invariants must always hold?
|
|
147
|
-
- What will we explicitly NOT refactor now?
|
|
158
|
+
- \`data-flow\` — What is the data flow end-to-end?
|
|
159
|
+
- \`seams\` — Where are the seams/interfaces and ownership boundaries?
|
|
160
|
+
- \`invariants\` — Which invariants must always hold?
|
|
161
|
+
- \`not-refactor\` — What will we explicitly NOT refactor now?
|
|
148
162
|
|
|
149
163
|
## One-Way Override (Irreversible Decisions)
|
|
150
164
|
|
package/dist/content/skills.js
CHANGED
|
@@ -360,10 +360,16 @@ function mergedAntiPatterns(philosophy, execution) {
|
|
|
360
360
|
}
|
|
361
361
|
function completionParametersBlock(schema, track) {
|
|
362
362
|
const gateList = schema.executionModel.requiredGates.map((g) => `\`${g.id}\``).join(", ");
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
363
|
+
// Wave 24 (v6.0.0): mandatory agents are dropped on `quick` track. Surface
|
|
364
|
+
// the empty list so the rendered SKILL.md doesn't tell quick-track runs to
|
|
365
|
+
// dispatch agents the linter is going to skip.
|
|
366
|
+
const trackAwareMandatoryAgents = track === "quick" ? [] : schema.reviewLens.mandatoryDelegations;
|
|
367
|
+
const mandatoryAgents = trackAwareMandatoryAgents;
|
|
368
|
+
const mandatory = trackAwareMandatoryAgents.length > 0
|
|
369
|
+
? trackAwareMandatoryAgents.map((a) => `\`${a}\``).join(", ")
|
|
370
|
+
: track === "quick" && schema.reviewLens.mandatoryDelegations.length > 0
|
|
371
|
+
? "none (skipped: quick track — Wave 24)"
|
|
372
|
+
: "none";
|
|
367
373
|
const resolvedNextStage = nextStageForTrack(schema.stage, track);
|
|
368
374
|
const nextStage = resolvedNextStage ?? "done";
|
|
369
375
|
const nextDescription = nextStage === "done"
|
|
@@ -55,6 +55,35 @@ export declare function validateSkillEnvelope(value: unknown): SkillEnvelopeVali
|
|
|
55
55
|
export declare function parseSkillEnvelope(raw: string): SkillEnvelope | null;
|
|
56
56
|
/** Transition guard: agents with `mode: "mandatory"` in auto-subagent dispatch for this stage. */
|
|
57
57
|
export declare function mandatoryDelegationsForStage(stage: FlowStage, complexityTier?: StageComplexityTier): string[];
|
|
58
|
+
/**
|
|
59
|
+
* Wave 24 (v6.0.0) — track-aware mandatory delegation lookup.
|
|
60
|
+
*
|
|
61
|
+
* Returns `[]` (skip the gate entirely) when the run is on a small-fix
|
|
62
|
+
* track or classified as a software bugfix:
|
|
63
|
+
*
|
|
64
|
+
* - `track === "quick"` — the quick track is for trivial single-purpose
|
|
65
|
+
* fixes (landing-page copy, doc edits, config tweaks). Mandatory
|
|
66
|
+
* subagent dispatch is theatre on that surface area.
|
|
67
|
+
* - `taskClass === "software-bugfix"` — bugfixes carry a RED-first
|
|
68
|
+
* repro contract; the test author + reviewer in the tdd/review
|
|
69
|
+
* stages already cover the safety surface, so mandatory upstream
|
|
70
|
+
* delegation only burns tokens.
|
|
71
|
+
*
|
|
72
|
+
* Otherwise returns the registered mandatory list for the stage at the
|
|
73
|
+
* given tier. Callers (gate-evidence, advance-stage validator,
|
|
74
|
+
* subagents.ts table generator) MUST go through this helper instead of
|
|
75
|
+
* `mandatoryDelegationsForStage` so the track-aware drop applies
|
|
76
|
+
* uniformly.
|
|
77
|
+
*
|
|
78
|
+
* NOTE: the user query also calls this `lite/quick`. There is no `lite`
|
|
79
|
+
* FlowTrack — the closest concept in cclaw is the `quick` track plus the
|
|
80
|
+
* brainstorm `lightweight` complexity tier. We key on the FlowTrack
|
|
81
|
+
* because the run-level decision is what matters at gate time;
|
|
82
|
+
* complexity tier is a per-stage knob that doesn't survive the dispatch
|
|
83
|
+
* boundary.
|
|
84
|
+
*/
|
|
85
|
+
export type MandatoryDelegationTaskClass = "software-standard" | "software-trivial" | "software-bugfix";
|
|
86
|
+
export declare function mandatoryAgentsFor(stage: FlowStage, track: FlowTrack, taskClass?: MandatoryDelegationTaskClass | null, complexityTier?: StageComplexityTier): string[];
|
|
58
87
|
export declare function stageSchema(stage: FlowStage, track?: FlowTrack): StageSchema;
|
|
59
88
|
export declare function orderedStageSchemas(track?: FlowTrack): StageSchema[];
|
|
60
89
|
export declare function stageGateIds(stage: FlowStage, track?: FlowTrack): string[];
|
|
@@ -811,6 +811,13 @@ export function mandatoryDelegationsForStage(stage, complexityTier = "standard")
|
|
|
811
811
|
.find((row) => row.stage === stage);
|
|
812
812
|
return summary ? summary.mandatoryAgents : [];
|
|
813
813
|
}
|
|
814
|
+
export function mandatoryAgentsFor(stage, track, taskClass, complexityTier = "standard") {
|
|
815
|
+
if (track === "quick")
|
|
816
|
+
return [];
|
|
817
|
+
if (taskClass === "software-bugfix")
|
|
818
|
+
return [];
|
|
819
|
+
return mandatoryDelegationsForStage(stage, complexityTier);
|
|
820
|
+
}
|
|
814
821
|
export function stageSchema(stage, track = "standard") {
|
|
815
822
|
const rawInput = stage === "tdd" ? tddStageForTrack(track) : STAGE_SCHEMA_MAP[stage];
|
|
816
823
|
const base = normalizeStageSchemaInput(rawInput);
|
|
@@ -36,9 +36,9 @@ export const BRAINSTORM = {
|
|
|
36
36
|
},
|
|
37
37
|
executionModel: {
|
|
38
38
|
checklist: [
|
|
39
|
-
"**ADAPTIVE ELICITATION COMES FIRST (no exceptions, no subagent dispatch before).** Load `.cclaw/skills/adaptive-elicitation/SKILL.md`. Walk the brainstorm forcing questions one-at-a-time via the harness-native question tool, append one row to `## Q&A Log` (`Turn | Question | User answer (1-line) | Decision impact`) after each user answer. Continue until forcing-
|
|
39
|
+
"**ADAPTIVE ELICITATION COMES FIRST (no exceptions, no subagent dispatch before).** Load `.cclaw/skills/adaptive-elicitation/SKILL.md`. Walk the brainstorm forcing questions one-at-a-time via the harness-native question tool, append one row to `## Q&A Log` (`Turn | Question | User answer (1-line) | Decision impact`) after each user answer **and stamp the row's `Decision impact` cell with the matching `[topic:<id>]` tag** (e.g. `[topic:pain]`). Continue until every forcing-question topic id is tagged on a row OR Ralph-Loop convergence detector says no new decision-changing rows in last 2 iterations OR user records an explicit stop-signal row. Only then proceed to delegations, drafts, or analysis. The linter `qa_log_unconverged` rule will block `stage-complete` if convergence is not reached.",
|
|
40
40
|
"**Explore project context** — after the elicitation loop converges, inspect existing files/docs/recent activity to refine the Discovered context section; capture matching files/patterns/seeds in `Context > Discovered context` so downstream stages don't redo discovery.",
|
|
41
|
-
"**Brainstorm forcing questions (must be covered or explicitly waived)** — what pain are we solving
|
|
41
|
+
"**Brainstorm forcing questions (must be covered or explicitly waived)** — `pain: what pain are we solving`; `direct-path: what is the direct path`; `do-nothing: what happens if we do nothing`; `operator: who is the first operator/user affected`; `no-go: what no-go boundaries are non-negotiable`. Tag the matching `## Q&A Log` row's `Decision impact` cell with `[topic:<id>]` (e.g. `[topic:pain]`) so the linter can verify coverage in any natural language. Tags are MANDATORY for forcing-question rows; un-tagged rows do NOT count toward coverage.",
|
|
42
42
|
"**Classify stage depth** — choose `lite` for clear low-risk tasks, `standard` for normal engineering/product changes, or `deep` for ambiguity, architecture, external dependency, security/data risk, or explicit think-bigger requests.",
|
|
43
43
|
"**Write the Problem Decision Record** — pick a free-form `Frame type` label that names how this work is framed (examples: product, technical-maintenance, research-spike, ops-incident, infrastructure), then fill the universal Framing fields: affected user/role/operator, current state/failure mode/opportunity, desired observable outcome, evidence/signal, why now, do-nothing consequence, and non-goals.",
|
|
44
44
|
"**Premise check (one pass)** — answer the three gstack-style questions in the artifact body: *Right problem? Direct path? What if we do nothing?* Take a position; do not hedge.",
|
|
@@ -41,8 +41,8 @@ export const DESIGN = {
|
|
|
41
41
|
},
|
|
42
42
|
executionModel: {
|
|
43
43
|
checklist: [
|
|
44
|
-
"**ADAPTIVE ELICITATION COMES FIRST (no exceptions, no subagent dispatch before).** Load `.cclaw/skills/adaptive-elicitation/SKILL.md`. Walk the design forcing questions one-at-a-time via the harness-native question tool, append one row to `## Q&A Log` (`Turn | Question | User answer (1-line) | Decision impact`) after each user answer. Continue until forcing-
|
|
45
|
-
"**Design forcing questions (must be covered or explicitly waived)** — what is the end-to-end data flow
|
|
44
|
+
"**ADAPTIVE ELICITATION COMES FIRST (no exceptions, no subagent dispatch before).** Load `.cclaw/skills/adaptive-elicitation/SKILL.md`. Walk the design forcing questions one-at-a-time via the harness-native question tool, append one row to `## Q&A Log` (`Turn | Question | User answer (1-line) | Decision impact`) after each user answer **and stamp the row's `Decision impact` cell with the matching `[topic:<id>]` tag** (e.g. `[topic:data-flow]`). Continue until every forcing-question topic id is tagged on a row OR Ralph-Loop convergence detector says no new decision-changing rows in last 2 iterations OR user records an explicit stop-signal row. Only then proceed to research, investigator pass, architecture lock, or any delegations. The linter `qa_log_unconverged` rule will block `stage-complete` if convergence is not reached.",
|
|
45
|
+
"**Design forcing questions (must be covered or explicitly waived)** — `data-flow: what is the end-to-end data flow`; `seams: where are seams/ownership boundaries`; `invariants: which invariants must hold`; `not-refactor: what will explicitly NOT be refactored now`. Tag the matching `## Q&A Log` row's `Decision impact` cell with `[topic:<id>]` (e.g. `[topic:data-flow]`) so the linter can verify coverage in any natural language. Tags are MANDATORY for forcing-question rows; un-tagged rows do NOT count toward coverage.",
|
|
46
46
|
"**Out-of-scope carry-forward (do NOT re-author)** — scope OWNS the out-of-scope list. Cite scope's `## In Scope / Out of Scope > Out of Scope` via `## Upstream Handoff > Decisions carried forward`; do NOT add a separate `## NOT in scope` section in the design artifact. Add a row to `## Spec Handoff` only if a design-stage decision NEWLY excludes something not already in scope's out-of-scope.",
|
|
47
47
|
"Compact design lock — design does not decide what to build; it decides how the approved scope works. For simple slices, produce a tight lock: upstream handoff, existing fit, architecture boundary, one labeled diagram, data/state flow, critical path, failure/rescue, trust boundaries, test/perf expectations, rollout/rollback, rejected alternative, and spec handoff.",
|
|
48
48
|
"Trivial-Change Escape Hatch — for <=3 files, no new interfaces, and no cross-module data flow, produce a mini-design (rationale, changed files, one risk) and proceed to spec.",
|
|
@@ -46,8 +46,8 @@ export const SCOPE = {
|
|
|
46
46
|
},
|
|
47
47
|
executionModel: {
|
|
48
48
|
checklist: [
|
|
49
|
-
"**ADAPTIVE ELICITATION COMES FIRST (no exceptions, no subagent dispatch before).** Load `.cclaw/skills/adaptive-elicitation/SKILL.md`. Walk the scope forcing questions one-at-a-time via the harness-native question tool, append one row to `## Q&A Log` (`Turn | Question | User answer (1-line) | Decision impact`) after each user answer. Continue until forcing-
|
|
50
|
-
"**Scope forcing questions (must be covered or explicitly waived)** — what is definitely in/out
|
|
49
|
+
"**ADAPTIVE ELICITATION COMES FIRST (no exceptions, no subagent dispatch before).** Load `.cclaw/skills/adaptive-elicitation/SKILL.md`. Walk the scope forcing questions one-at-a-time via the harness-native question tool, append one row to `## Q&A Log` (`Turn | Question | User answer (1-line) | Decision impact`) after each user answer **and stamp the row's `Decision impact` cell with the matching `[topic:<id>]` tag** (e.g. `[topic:in-out]`). Continue until every forcing-question topic id is tagged on a row OR Ralph-Loop convergence detector says no new decision-changing rows in last 2 iterations OR user records an explicit stop-signal row. Only then propose the scope contract draft, recommend a mode, or dispatch any delegations. The linter `qa_log_unconverged` rule will block `stage-complete` if convergence is not reached.",
|
|
50
|
+
"**Scope forcing questions (must be covered or explicitly waived)** — `in-out: what is definitely in/out`; `locked-upstream: which upstream decisions are locked`; `rollback: what rollback path protects users if scope assumptions fail`; `failure-modes: what are the top failure modes we must design for`. Tag the matching `## Q&A Log` row's `Decision impact` cell with `[topic:<id>]` (e.g. `[topic:in-out]`) so the linter can verify coverage in any natural language. Tags are MANDATORY for forcing-question rows; un-tagged rows do NOT count toward coverage.",
|
|
51
51
|
"**Scope contract first** — read brainstorm handoff, name upstream decisions used, explicit drift, confidence, unresolved questions, and next-stage risk hints; draft the in-scope/out-of-scope/deferred/discretion contract before any design choice.",
|
|
52
52
|
"**Premise carry-forward (do NOT re-author)** — brainstorm OWNS the premise check (right problem / direct path / what if nothing). Cite brainstorm's `## Premise Check` section in `## Upstream Handoff > Decisions carried forward`. Add a row to `## Premise Drift` only when the scope-stage Q&A surfaced NEW evidence that materially changes the brainstorm answer (e.g. new constraint, new user signal). Otherwise mark `Premise Drift: None` — do not duplicate the brainstorm premise table.",
|
|
53
53
|
"**Conditional 10-star boundary** — for deep/high-risk/product-strategy work, show what would make the product meaningfully better, then explicitly choose what ships now, what is deferred, and what is excluded without vague `later/for now` placeholders. Skip this for straightforward repair work and record `not needed: compact scope`.",
|
|
@@ -16,7 +16,9 @@ function automaticStageDelegationTable() {
|
|
|
16
16
|
}).join("\n");
|
|
17
17
|
return `| Stage | Mandatory agents | Proactive agents |
|
|
18
18
|
|---|---|---|
|
|
19
|
-
${rows}
|
|
19
|
+
${rows}
|
|
20
|
+
|
|
21
|
+
> **Track-aware skip (Wave 24, v6.0.0):** mandatory agents are skipped entirely when \`track === "quick"\` OR \`taskClass === "software-bugfix"\`. Use \`mandatoryAgentsFor(stage, track, taskClass)\` from \`src/content/stage-schema.ts\` for the authoritative list at runtime. Proactive agents stay enforced because they fire only on triggers (high blast radius, security-sensitive paths, etc.), not on every run.`;
|
|
20
22
|
}
|
|
21
23
|
function stageSummary(stage) {
|
|
22
24
|
return stageDelegationSummary("standard").find((row) => row.stage === stage)
|
|
@@ -6,6 +6,6 @@ export declare const RULEBOOK_MARKDOWN = "# Cclaw Rulebook\n\n## MUST_ALWAYS\n-
|
|
|
6
6
|
* loading skills. Three hard rules cover the most common Wave 22 regressions
|
|
7
7
|
* (premature draft, premature subagent dispatch, command-line echo to chat).
|
|
8
8
|
*/
|
|
9
|
-
export declare const CURSOR_GUIDELINES_RULE_MDC = "---\ndescription: cclaw zero-install behavior baseline (always-on)\nglobs:\n - \"**/*\"\nalwaysApply: true\n---\n\n<!-- cclaw-managed-cursor-guidelines-rule -->\n\n# Cclaw Baseline Guidelines\n\nThese three rules apply to every Cursor agent session in this project,\nregardless of whether stage skills loaded.\n\n## 1. Q&A floor before drafting (brainstorm/scope/design)\n\nBefore drafting any `.cclaw/artifacts/01-brainstorm-*.md`,\n`02-scope-*.md`, or `03-design-*.md`, verify that the artifact's\n`## Q&A Log` table demonstrates Ralph-Loop convergence:
|
|
10
|
-
export declare const CURSOR_WORKFLOW_RULE_MDC = "---\ndescription: cclaw workflow guardrails for Cursor agent sessions\nglobs:\n - \"**/*\"\nalwaysApply: true\n---\n\n<!-- cclaw-managed-cursor-workflow-rule -->\n\n# Cclaw Workflow Guardrails\n\n## Activation Rule\n\nBefore responding to coding work:\n1. Read `.cclaw/state/flow-state.json`.\n2. Start with `/cc` or continue with `/cc`.\n3. If no software-stage flow applies, respond normally.\n\n## Stage Order\n\n`brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship`\n\nTrack-specific skips are allowed only when `flow-state.track` + `skippedStages` explicitly say so.\n\n## Task Classification\n\n| Class | Route |\n|---|---|\n| non-trivial software work | `/cc <idea>` |\n| trivial software fix | `/cc <idea>` (quick track) |\n| bugfix with repro | `/cc <idea>` and enforce RED-first in tdd |\n| pure question / non-software | direct answer (no stage flow) |\n\n## Command Surface\n\n- `/cc` = entry and resume.\n- `/cc` = only progression path.\n- Knowledge capture and recall use the `learnings` skill when requested.\n\n## Verification Discipline\n\n- No completion claim without fresh command evidence in this turn.\n- Do not mark gates passed from memory.\n- Keep evidence in `.cclaw/artifacts/`; archive through closeout via `/cc` or cancel early via `node .cclaw/hooks/cancel-run.mjs`.\n\n## Delegation And Approvals\n\n- Machine-only checks in design/plan/tdd/review/ship should auto-dispatch when tooling supports it.\n- **For brainstorm / scope / design stages**: ask user input continuously via adaptive elicitation (one question per turn through the harness-native question tool \u2014 `AskQuestion` in Cursor). Walk the stage forcing-questions list one-by-one. Do NOT batch and do NOT defer to a single approval gate at the end. The `qa_log_unconverged` linter rule will block `stage-complete` when convergence is not reached (forcing
|
|
9
|
+
export declare const CURSOR_GUIDELINES_RULE_MDC = "---\ndescription: cclaw zero-install behavior baseline (always-on)\nglobs:\n - \"**/*\"\nalwaysApply: true\n---\n\n<!-- cclaw-managed-cursor-guidelines-rule -->\n\n# Cclaw Baseline Guidelines\n\nThese three rules apply to every Cursor agent session in this project,\nregardless of whether stage skills loaded.\n\n## 1. Q&A floor before drafting (brainstorm/scope/design)\n\nBefore drafting any `.cclaw/artifacts/01-brainstorm-*.md`,\n`02-scope-*.md`, or `03-design-*.md`, verify that the artifact's\n`## Q&A Log` table demonstrates Ralph-Loop convergence: every\nforcing-question topic id is tagged `[topic:<id>]` on at least one row\n(see the stage's forcing-questions checklist for the id list), the last\n2 turns produce no new decision-changing impact, OR an explicit user\nstop-signal row is recorded. Walk the stage forcing questions one at a\ntime via the `AskQuestion` tool. If you find yourself proposing a\ndraft after 1-2 questions while forcing topic ids remain untagged, STOP\nand continue the loop.\n\nThe `qa_log_unconverged` linter rule will block `stage-complete` when\nconvergence has not been reached. Wave 24 (v6.0.0) made `[topic:<id>]`\ntagging mandatory; the English keyword fallback was removed because it\nmis-reported convergence on RU/UA Q&A logs.\n\n## 2. Mandatory subagents run after Q&A approval\n\nFor brainstorm / scope / design, mandatory subagents (\n`product-discovery`, `critic`, `planner`, `architect`,\n`test-author`) run **only AFTER the user approves the elicitation\noutcome**, never before the Q&A loop converges. Dispatching them early\npreempts the user dialogue and violates the elicitation contract \u2014 the\nlinter will block stage-complete.\n\nSee each stage's \"Run Phase: post-elicitation\" rows in the materialized\nAutomatic Subagent Dispatch table.\n\n## 3. Never echo cclaw command lines to chat\n\nThe user does not run cclaw helpers (`node .cclaw/hooks/...`) manually.\nNEVER paste full command lines, `--evidence-json '{...}'` payloads,\n`--waive-delegation=...`, or shell hash commands (`shasum`,\n`sha256sum`, `Get-FileHash`, `certutil`, etc.) into chat. Run the\nhelper via the tool layer and report only the resulting summary. On\nfailure, report a compact human-readable summary plus the helper JSON in\na single fenced `json` block.\n";
|
|
10
|
+
export declare const CURSOR_WORKFLOW_RULE_MDC = "---\ndescription: cclaw workflow guardrails for Cursor agent sessions\nglobs:\n - \"**/*\"\nalwaysApply: true\n---\n\n<!-- cclaw-managed-cursor-workflow-rule -->\n\n# Cclaw Workflow Guardrails\n\n## Activation Rule\n\nBefore responding to coding work:\n1. Read `.cclaw/state/flow-state.json`.\n2. Start with `/cc` or continue with `/cc`.\n3. If no software-stage flow applies, respond normally.\n\n## Stage Order\n\n`brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship`\n\nTrack-specific skips are allowed only when `flow-state.track` + `skippedStages` explicitly say so.\n\n## Task Classification\n\n| Class | Route |\n|---|---|\n| non-trivial software work | `/cc <idea>` |\n| trivial software fix | `/cc <idea>` (quick track) |\n| bugfix with repro | `/cc <idea>` and enforce RED-first in tdd |\n| pure question / non-software | direct answer (no stage flow) |\n\n## Command Surface\n\n- `/cc` = entry and resume.\n- `/cc` = only progression path.\n- Knowledge capture and recall use the `learnings` skill when requested.\n\n## Verification Discipline\n\n- No completion claim without fresh command evidence in this turn.\n- Do not mark gates passed from memory.\n- Keep evidence in `.cclaw/artifacts/`; archive through closeout via `/cc` or cancel early via `node .cclaw/hooks/cancel-run.mjs`.\n\n## Delegation And Approvals\n\n- Machine-only checks in design/plan/tdd/review/ship should auto-dispatch when tooling supports it.\n- **For brainstorm / scope / design stages**: ask user input continuously via adaptive elicitation (one question per turn through the harness-native question tool \u2014 `AskQuestion` in Cursor). Walk the stage forcing-questions list one-by-one. **Tag each Q&A Log row's `Decision impact` cell with `[topic:<id>]`** (the id is given in the stage's forcing-questions checklist) so the linter can verify coverage in any natural language. Do NOT batch and do NOT defer to a single approval gate at the end. The `qa_log_unconverged` linter rule will block `stage-complete` when convergence is not reached (forcing topic ids untagged AND last 2 turns still produce decision-changing rows AND no stop-signal).\n- **For other stages** (spec/plan/tdd/build/review/ship): ask user input only at explicit approval gates (scope mode, plan approval, challenge resolution, ship finalization), not for routine progress updates.\n- If you find yourself proposing a draft after 1-2 questions in brainstorm/scope/design, STOP \u2014 go back to the forcing-questions list and continue.\n- Mandatory subagents in brainstorm/scope/design run only AFTER the user approves the elicitation outcome (see each stage's \"Run Phase: post-elicitation\" rows). Dispatching them before the Q&A loop converges violates the contract.\n- Never echo cclaw command lines (`node .cclaw/hooks/...`, `--evidence-json '{...}'`) to chat \u2014 the user does not run cclaw manually. Run helpers via the tool layer; report only the resulting summary.\n- If harness capabilities are partial, record waiver reasons in delegation logs.\n\n## Routing Source Of Truth\n\n- Primary router: `.cclaw/skills/using-cclaw/SKILL.md`.\n- Stage behavior: current stage skill plus `.cclaw/state/flow-state.json`.\n- Preamble budget: keep role/status announcements brief and avoid repeating\n them unless the stage or role changes.\n";
|
|
11
11
|
export declare function buildRulesJson(): Record<string, unknown>;
|
|
@@ -86,9 +86,10 @@ export const ARTIFACT_TEMPLATES = {
|
|
|
86
86
|
## Q&A Log
|
|
87
87
|
| Turn | Question | User answer (1-line) | Decision impact |
|
|
88
88
|
|---|---|---|---|
|
|
89
|
-
| 1 | | |
|
|
89
|
+
| 1 | | | scope-shaping [topic:pain] |
|
|
90
90
|
|
|
91
91
|
> Append-only by turn. Add one row after each user answer; do not rewrite prior rows.
|
|
92
|
+
> **Topic tag is MANDATORY for forcing-question rows.** Stamp \`[topic:<id>]\` in the \`Decision impact\` cell so the linter can verify coverage in any natural language (RU/EN/UA/etc.). Brainstorm IDs: \`pain\`, \`direct-path\`, \`do-nothing\`, \`operator\`, \`no-go\`. Multiple tags allowed when one answer covers several topics. Stop-signal rows do NOT need a tag. Wave 24 (v6.0.0) removed the English keyword fallback.
|
|
92
93
|
|
|
93
94
|
## Approach Tier
|
|
94
95
|
- Tier: lite | standard | deep
|
|
@@ -209,9 +210,10 @@ ${MARKDOWN_CODE_FENCE}
|
|
|
209
210
|
## Q&A Log
|
|
210
211
|
| Turn | Question | User answer (1-line) | Decision impact |
|
|
211
212
|
|---|---|---|---|
|
|
212
|
-
| 1 | | |
|
|
213
|
+
| 1 | | | scope-shaping [topic:in-out] |
|
|
213
214
|
|
|
214
215
|
> Append-only by turn. Add one row after each user answer; do not rewrite prior rows.
|
|
216
|
+
> **Topic tag is MANDATORY for forcing-question rows.** Stamp \`[topic:<id>]\` in the \`Decision impact\` cell so the linter can verify coverage in any natural language (RU/EN/UA/etc.). Scope IDs: \`in-out\`, \`locked-upstream\`, \`rollback\`, \`failure-modes\`. Multiple tags allowed when one answer covers several topics. Stop-signal rows do NOT need a tag. Wave 24 (v6.0.0) removed the English keyword fallback.
|
|
215
217
|
|
|
216
218
|
## Pre-Scope System Audit
|
|
217
219
|
| Check | Command | Findings |
|
|
@@ -447,9 +449,10 @@ ${MARKDOWN_CODE_FENCE}
|
|
|
447
449
|
## Q&A Log
|
|
448
450
|
| Turn | Question | User answer (1-line) | Decision impact |
|
|
449
451
|
|---|---|---|---|
|
|
450
|
-
| 1 | | |
|
|
452
|
+
| 1 | | | architecture-shaping [topic:data-flow] |
|
|
451
453
|
|
|
452
454
|
> Append-only by turn. Add one row after each user answer; do not rewrite prior rows.
|
|
455
|
+
> **Topic tag is MANDATORY for forcing-question rows.** Stamp \`[topic:<id>]\` in the \`Decision impact\` cell so the linter can verify coverage in any natural language (RU/EN/UA/etc.). Design IDs: \`data-flow\`, \`seams\`, \`invariants\`, \`not-refactor\`. Multiple tags allowed when one answer covers several topics. Stop-signal rows do NOT need a tag. Wave 24 (v6.0.0) removed the English keyword fallback.
|
|
453
456
|
|
|
454
457
|
## Codebase Investigation
|
|
455
458
|
| File | Current responsibility | Patterns discovered | Existing fit / reuse candidate |
|
|
@@ -1484,16 +1487,19 @@ regardless of whether stage skills loaded.
|
|
|
1484
1487
|
|
|
1485
1488
|
Before drafting any \`.cclaw/artifacts/01-brainstorm-*.md\`,
|
|
1486
1489
|
\`02-scope-*.md\`, or \`03-design-*.md\`, verify that the artifact's
|
|
1487
|
-
\`## Q&A Log\` table demonstrates Ralph-Loop convergence:
|
|
1488
|
-
|
|
1489
|
-
the
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1490
|
+
\`## Q&A Log\` table demonstrates Ralph-Loop convergence: every
|
|
1491
|
+
forcing-question topic id is tagged \`[topic:<id>]\` on at least one row
|
|
1492
|
+
(see the stage's forcing-questions checklist for the id list), the last
|
|
1493
|
+
2 turns produce no new decision-changing impact, OR an explicit user
|
|
1494
|
+
stop-signal row is recorded. Walk the stage forcing questions one at a
|
|
1495
|
+
time via the \`AskQuestion\` tool. If you find yourself proposing a
|
|
1496
|
+
draft after 1-2 questions while forcing topic ids remain untagged, STOP
|
|
1493
1497
|
and continue the loop.
|
|
1494
1498
|
|
|
1495
1499
|
The \`qa_log_unconverged\` linter rule will block \`stage-complete\` when
|
|
1496
|
-
convergence has not been reached.
|
|
1500
|
+
convergence has not been reached. Wave 24 (v6.0.0) made \`[topic:<id>]\`
|
|
1501
|
+
tagging mandatory; the English keyword fallback was removed because it
|
|
1502
|
+
mis-reported convergence on RU/UA Q&A logs.
|
|
1497
1503
|
|
|
1498
1504
|
## 2. Mandatory subagents run after Q&A approval
|
|
1499
1505
|
|
|
@@ -1565,7 +1571,7 @@ Track-specific skips are allowed only when \`flow-state.track\` + \`skippedStage
|
|
|
1565
1571
|
## Delegation And Approvals
|
|
1566
1572
|
|
|
1567
1573
|
- Machine-only checks in design/plan/tdd/review/ship should auto-dispatch when tooling supports it.
|
|
1568
|
-
- **For brainstorm / scope / design stages**: ask user input continuously via adaptive elicitation (one question per turn through the harness-native question tool — \`AskQuestion\` in Cursor). Walk the stage forcing-questions list one-by-one. Do NOT batch and do NOT defer to a single approval gate at the end. The \`qa_log_unconverged\` linter rule will block \`stage-complete\` when convergence is not reached (forcing
|
|
1574
|
+
- **For brainstorm / scope / design stages**: ask user input continuously via adaptive elicitation (one question per turn through the harness-native question tool — \`AskQuestion\` in Cursor). Walk the stage forcing-questions list one-by-one. **Tag each Q&A Log row's \`Decision impact\` cell with \`[topic:<id>]\`** (the id is given in the stage's forcing-questions checklist) so the linter can verify coverage in any natural language. Do NOT batch and do NOT defer to a single approval gate at the end. The \`qa_log_unconverged\` linter rule will block \`stage-complete\` when convergence is not reached (forcing topic ids untagged AND last 2 turns still produce decision-changing rows AND no stop-signal).
|
|
1569
1575
|
- **For other stages** (spec/plan/tdd/build/review/ship): ask user input only at explicit approval gates (scope mode, plan approval, challenge resolution, ship finalization), not for routine progress updates.
|
|
1570
1576
|
- If you find yourself proposing a draft after 1-2 questions in brainstorm/scope/design, STOP — go back to the forcing-questions list and continue.
|
|
1571
1577
|
- Mandatory subagents in brainstorm/scope/design run only AFTER the user approves the elicitation outcome (see each stage's "Run Phase: post-elicitation" rows). Dispatching them before the Q&A loop converges violates the contract.
|
package/dist/delegation.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type SubagentFallback } from "./harness-adapters.js";
|
|
2
|
+
import { type MandatoryDelegationTaskClass } from "./content/stage-schema.js";
|
|
2
3
|
import type { FlowStage } from "./types.js";
|
|
3
4
|
export type DelegationMode = "mandatory" | "proactive";
|
|
4
5
|
export type DelegationStatus = "scheduled" | "launched" | "acknowledged" | "completed" | "failed" | "waived" | "stale";
|
|
@@ -143,6 +144,13 @@ export declare function appendDelegation(projectRoot: string, entry: DelegationE
|
|
|
143
144
|
export declare function expectedFulfillmentMode(fallbacks: SubagentFallback[]): DelegationFulfillmentMode;
|
|
144
145
|
export declare function checkMandatoryDelegations(projectRoot: string, stage: FlowStage, options?: {
|
|
145
146
|
repairFeatureSystem?: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Optional task class for the active run. When set to
|
|
149
|
+
* `"software-bugfix"`, the mandatory delegation gate is skipped
|
|
150
|
+
* entirely (Wave 24). Callers that don't classify the run leave
|
|
151
|
+
* this undefined and rely on the track-based skip.
|
|
152
|
+
*/
|
|
153
|
+
taskClass?: MandatoryDelegationTaskClass | null;
|
|
146
154
|
}): Promise<{
|
|
147
155
|
satisfied: boolean;
|
|
148
156
|
missing: string[];
|
|
@@ -160,4 +168,12 @@ export declare function checkMandatoryDelegations(projectRoot: string, stage: Fl
|
|
|
160
168
|
staleWorkers: string[];
|
|
161
169
|
/** Expected fulfillment mode for the active harness set. */
|
|
162
170
|
expectedMode: DelegationFulfillmentMode;
|
|
171
|
+
/**
|
|
172
|
+
* Wave 24 (v6.0.0): true when `mandatoryAgentsFor` returned [] for
|
|
173
|
+
* this (track, taskClass) combination — i.e. the gate was skipped
|
|
174
|
+
* entirely on quick track or software-bugfix runs. The skip is also
|
|
175
|
+
* recorded as a `mandatory_delegations_skipped_by_track` event in
|
|
176
|
+
* `delegation-events.jsonl` for audit traceability.
|
|
177
|
+
*/
|
|
178
|
+
skippedByTrack: boolean;
|
|
163
179
|
}>;
|
package/dist/delegation.js
CHANGED
|
@@ -7,7 +7,7 @@ import { readConfig } from "./config.js";
|
|
|
7
7
|
import { exists, withDirectoryLock, writeFileSafe } from "./fs-utils.js";
|
|
8
8
|
import { HARNESS_ADAPTERS } from "./harness-adapters.js";
|
|
9
9
|
import { readFlowState } from "./runs.js";
|
|
10
|
-
import { stageSchema } from "./content/stage-schema.js";
|
|
10
|
+
import { mandatoryAgentsFor, stageSchema } from "./content/stage-schema.js";
|
|
11
11
|
const execFileAsync = promisify(execFile);
|
|
12
12
|
const TERMINAL_DELEGATION_STATUSES = new Set(["completed", "failed", "waived", "stale"]);
|
|
13
13
|
export const DELEGATION_DISPATCH_SURFACES = [
|
|
@@ -320,6 +320,21 @@ export async function readDelegationLedger(projectRoot) {
|
|
|
320
320
|
return { runId: activeRunId, entries: [] };
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Wave 24 (v6.0.0) audit-only event types that live in
|
|
325
|
+
* `delegation-events.jsonl` but do NOT carry a delegation lifecycle
|
|
326
|
+
* payload (no agent/spanId). The parser must accept them so they
|
|
327
|
+
* don't show up as corrupt lines.
|
|
328
|
+
*/
|
|
329
|
+
const NON_DELEGATION_AUDIT_EVENTS = new Set([
|
|
330
|
+
"mandatory_delegations_skipped_by_track"
|
|
331
|
+
]);
|
|
332
|
+
function isAuditEventLine(parsed) {
|
|
333
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
334
|
+
return false;
|
|
335
|
+
const evt = parsed.event;
|
|
336
|
+
return typeof evt === "string" && NON_DELEGATION_AUDIT_EVENTS.has(evt);
|
|
337
|
+
}
|
|
323
338
|
export async function readDelegationEvents(projectRoot) {
|
|
324
339
|
const filePath = delegationEventsPath(projectRoot);
|
|
325
340
|
if (!(await exists(filePath))) {
|
|
@@ -338,6 +353,11 @@ export async function readDelegationEvents(projectRoot) {
|
|
|
338
353
|
if (isDelegationEvent(parsed)) {
|
|
339
354
|
events.push(parsed);
|
|
340
355
|
}
|
|
356
|
+
else if (isAuditEventLine(parsed)) {
|
|
357
|
+
// Wave 24 audit-only row (e.g. mandatory_delegations_skipped_by_track).
|
|
358
|
+
// Not a delegation lifecycle event but valid audit content.
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
341
361
|
else {
|
|
342
362
|
corruptLines.push(index + 1);
|
|
343
363
|
}
|
|
@@ -451,7 +471,17 @@ export async function checkMandatoryDelegations(projectRoot, stage, options = {}
|
|
|
451
471
|
const flowState = await readFlowState(projectRoot, {
|
|
452
472
|
repairFeatureSystem: options.repairFeatureSystem
|
|
453
473
|
});
|
|
454
|
-
const mandatory =
|
|
474
|
+
const mandatory = mandatoryAgentsFor(stage, flowState.track, options.taskClass ?? null);
|
|
475
|
+
const skippedByTrack = mandatory.length === 0 &&
|
|
476
|
+
stageSchema(stage, flowState.track).mandatoryDelegations.length > 0;
|
|
477
|
+
if (skippedByTrack) {
|
|
478
|
+
await recordMandatorySkippedByTrack(projectRoot, {
|
|
479
|
+
stage,
|
|
480
|
+
track: flowState.track,
|
|
481
|
+
taskClass: options.taskClass ?? null,
|
|
482
|
+
runId: flowState.activeRunId
|
|
483
|
+
});
|
|
484
|
+
}
|
|
455
485
|
const { activeRunId } = flowState;
|
|
456
486
|
const ledger = await readDelegationLedger(projectRoot);
|
|
457
487
|
const events = await readDelegationEvents(projectRoot);
|
|
@@ -553,6 +583,37 @@ export async function checkMandatoryDelegations(projectRoot, stage, options = {}
|
|
|
553
583
|
legacyInferredCompletions,
|
|
554
584
|
corruptEventLines: events.corruptLines,
|
|
555
585
|
staleWorkers,
|
|
556
|
-
expectedMode
|
|
586
|
+
expectedMode,
|
|
587
|
+
skippedByTrack
|
|
557
588
|
};
|
|
558
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Wave 24 (v6.0.0) — append a non-delegation audit event to
|
|
592
|
+
* `delegation-events.jsonl` recording that the mandatory delegation
|
|
593
|
+
* gate was skipped because of the active track / task class. Plays the
|
|
594
|
+
* same audit role as a `waived` row but does NOT carry an agent —
|
|
595
|
+
* downstream tooling treats `event === "mandatory_delegations_skipped_by_track"`
|
|
596
|
+
* lines as informational.
|
|
597
|
+
*
|
|
598
|
+
* Failures are swallowed: the audit log is best-effort. Missing the
|
|
599
|
+
* event must never block stage advance because the gate skip itself is
|
|
600
|
+
* authoritative.
|
|
601
|
+
*/
|
|
602
|
+
async function recordMandatorySkippedByTrack(projectRoot, params) {
|
|
603
|
+
const eventsPath = delegationEventsPath(projectRoot);
|
|
604
|
+
const payload = {
|
|
605
|
+
event: "mandatory_delegations_skipped_by_track",
|
|
606
|
+
stage: params.stage,
|
|
607
|
+
track: params.track,
|
|
608
|
+
taskClass: params.taskClass,
|
|
609
|
+
runId: params.runId,
|
|
610
|
+
ts: new Date().toISOString()
|
|
611
|
+
};
|
|
612
|
+
try {
|
|
613
|
+
await fs.mkdir(path.dirname(eventsPath), { recursive: true });
|
|
614
|
+
await fs.appendFile(eventsPath, `${JSON.stringify(payload)}\n`, "utf8");
|
|
615
|
+
}
|
|
616
|
+
catch {
|
|
617
|
+
// best-effort audit; never block stage advance.
|
|
618
|
+
}
|
|
619
|
+
}
|
package/dist/harness-adapters.js
CHANGED
|
@@ -349,7 +349,7 @@ Before responding to a coding request:
|
|
|
349
349
|
|
|
350
350
|
Three rules apply to every cclaw stage in this project, regardless of which skills loaded:
|
|
351
351
|
|
|
352
|
-
1. **Q&A convergence before drafting** — for brainstorm / scope / design, walk the stage forcing questions one at a time via the harness-native question tool (Claude \`AskUserQuestion\`, Cursor \`AskQuestion\`, Codex \`request_user_input\`, Gemini \`ask_user\`). The \`qa_log_unconverged\` linter rule will block \`stage-complete\` when convergence has not been reached. Convergence is satisfied when ANY of: (a)
|
|
352
|
+
1. **Q&A convergence before drafting** — for brainstorm / scope / design, walk the stage forcing questions one at a time via the harness-native question tool (Claude \`AskUserQuestion\`, Cursor \`AskQuestion\`, Codex \`request_user_input\`, Gemini \`ask_user\`). The \`qa_log_unconverged\` linter rule will block \`stage-complete\` when convergence has not been reached. Convergence is satisfied when ANY of: (a) every forcing-question topic id is tagged \`[topic:<id>]\` in at least one \`## Q&A Log\` row, (b) the last 2 substantive rows produce no decision-changing impact (Ralph-Loop), or (c) an explicit user stop-signal row is recorded. The fixed count floor (10 for standard) was removed in Wave 23. Wave 24 (v6.0.0) made \`[topic:<id>]\` tagging mandatory (no English keyword fallback) so the gate works in any natural language.
|
|
353
353
|
2. **Subagents run after Q&A approval** — mandatory subagents in brainstorm / scope / design (\`product-discovery\`, \`critic\`, \`planner\`, \`architect\`, \`test-author\`) run only AFTER the user approves the elicitation outcome. See each stage's "Run Phase: post-elicitation" rows in the materialized Automatic Subagent Dispatch table.
|
|
354
354
|
3. **No command-line echo to chat** — the user does not run cclaw helpers manually. Never paste \`node .cclaw/hooks/...\` invocations, \`--evidence-json '{...}'\` payloads, or shell hash commands (\`shasum\`, \`sha256sum\`, \`Get-FileHash\`, \`certutil\`, etc.) into chat. Run helpers via the tool layer; report only the resulting summary.
|
|
355
355
|
|
|
@@ -19,6 +19,8 @@ interface InternalValidationReport {
|
|
|
19
19
|
corruptEventLines: number[];
|
|
20
20
|
staleWorkers: string[];
|
|
21
21
|
expectedMode: string;
|
|
22
|
+
/** Wave 24: true when mandatoryAgentsFor returned [] for the run's track / taskClass. */
|
|
23
|
+
skippedByTrack: boolean;
|
|
22
24
|
};
|
|
23
25
|
gates: {
|
|
24
26
|
ok: boolean;
|
|
@@ -111,7 +111,8 @@ export async function buildValidationReport(projectRoot, flowState, options = {}
|
|
|
111
111
|
legacyInferredCompletions: delegation.legacyInferredCompletions,
|
|
112
112
|
corruptEventLines: delegation.corruptEventLines,
|
|
113
113
|
staleWorkers: delegation.staleWorkers,
|
|
114
|
-
expectedMode: delegation.expectedMode
|
|
114
|
+
expectedMode: delegation.expectedMode,
|
|
115
|
+
skippedByTrack: delegation.skippedByTrack
|
|
115
116
|
},
|
|
116
117
|
gates: {
|
|
117
118
|
ok: gates.ok,
|