cclaw-cli 0.51.7 → 0.51.9
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.js +56 -17
- package/dist/content/hooks.js +5 -5
- package/dist/content/next-command.js +19 -3
- package/dist/content/review-loop.js +37 -10
- package/dist/content/skills.js +8 -4
- package/dist/content/stages/brainstorm.js +8 -5
- package/dist/content/stages/design.js +3 -2
- package/dist/content/stages/scope.js +17 -15
- package/dist/content/start-command.js +3 -2
- package/dist/content/templates.js +19 -13
- package/dist/internal/advance-stage.js +64 -4
- package/package.json +1 -1
package/dist/artifact-linter.js
CHANGED
|
@@ -85,6 +85,15 @@ function sectionBodyByName(sections, section) {
|
|
|
85
85
|
}
|
|
86
86
|
return null;
|
|
87
87
|
}
|
|
88
|
+
function sectionBodyByAnyName(sections, sectionNames) {
|
|
89
|
+
const bodies = sectionNames.flatMap((section) => {
|
|
90
|
+
const body = sectionBodyByName(sections, section);
|
|
91
|
+
return body === null ? [] : [`### ${section}\n${body}`];
|
|
92
|
+
});
|
|
93
|
+
if (bodies.length === 0)
|
|
94
|
+
return null;
|
|
95
|
+
return bodies.join("\n");
|
|
96
|
+
}
|
|
88
97
|
export function extractMarkdownSectionBody(markdown, section) {
|
|
89
98
|
return sectionBodyByName(extractH2Sections(markdown), section);
|
|
90
99
|
}
|
|
@@ -243,7 +252,7 @@ function meaningfulLineCount(sectionBody) {
|
|
|
243
252
|
.filter((line) => line.length > 0)
|
|
244
253
|
.filter((line) => !line.startsWith("<!--"))
|
|
245
254
|
.filter((line) => !/^[-:| ]+$/u.test(line))
|
|
246
|
-
.filter((line) => /[
|
|
255
|
+
.filter((line) => /[\p{L}\p{N}]/u.test(line))
|
|
247
256
|
.length;
|
|
248
257
|
}
|
|
249
258
|
function lineHasToken(line, token) {
|
|
@@ -355,6 +364,35 @@ function getMarkdownTableRows(sectionBody) {
|
|
|
355
364
|
}
|
|
356
365
|
return rows;
|
|
357
366
|
}
|
|
367
|
+
function getApproachRows(sectionBody) {
|
|
368
|
+
const tableRows = getMarkdownTableRows(sectionBody).map((row) => row.join(" "));
|
|
369
|
+
const headingRows = sectionBody
|
|
370
|
+
.split(/\r?\n/u)
|
|
371
|
+
.map((line) => line.trim())
|
|
372
|
+
.filter((line) => /^#{3,6}\s+\S/u.test(line))
|
|
373
|
+
.map((line) => line.replace(/^#{3,6}\s+/u, ""));
|
|
374
|
+
const bulletRows = sectionBody
|
|
375
|
+
.split(/\r?\n/u)
|
|
376
|
+
.map((line) => line.trim())
|
|
377
|
+
.filter((line) => /^(?:[-*]|\d+\.)\s+\S/u.test(line));
|
|
378
|
+
return [...tableRows, ...headingRows, ...bulletRows];
|
|
379
|
+
}
|
|
380
|
+
function hasSemanticChallenger(row) {
|
|
381
|
+
const normalized = row
|
|
382
|
+
.replace(/[_`*]/gu, " ")
|
|
383
|
+
.replace(/\s+/gu, " ")
|
|
384
|
+
.trim()
|
|
385
|
+
.toLowerCase();
|
|
386
|
+
const isChallenger = /\bchallenger\b/u.test(normalized);
|
|
387
|
+
if (!isChallenger)
|
|
388
|
+
return false;
|
|
389
|
+
return (/\bhigher[-\s]?upside\b/u.test(normalized) ||
|
|
390
|
+
/\bhigh[-\s]?upside\b/u.test(normalized) ||
|
|
391
|
+
/\bupside\s*:?\s*(?:high|higher|strong|large|meaningful)\b/u.test(normalized) ||
|
|
392
|
+
/\b(?:high|higher|strong|large|meaningful)\s+upside\b/u.test(normalized) ||
|
|
393
|
+
/\b(?:10-star|ten-star|ambitious|higher leverage|leverage)\b/u.test(normalized) ||
|
|
394
|
+
/\bhigh\b/u.test(normalized));
|
|
395
|
+
}
|
|
358
396
|
function parseBinaryFlag(value) {
|
|
359
397
|
const normalized = value.trim().toLowerCase();
|
|
360
398
|
if (/^(?:y|yes|true|1)$/u.test(normalized))
|
|
@@ -1331,7 +1369,11 @@ export async function lintArtifact(projectRoot, stage, track = "standard") {
|
|
|
1331
1369
|
: null;
|
|
1332
1370
|
for (const v of schema.artifactValidation) {
|
|
1333
1371
|
const sectionKey = normalizeHeadingTitle(v.section).toLowerCase();
|
|
1334
|
-
const
|
|
1372
|
+
const scopeBoundaryAlias = stage === "scope" && sectionKey === "in scope / out of scope";
|
|
1373
|
+
const body = scopeBoundaryAlias
|
|
1374
|
+
? sectionBodyByAnyName(sections, ["In Scope / Out of Scope", "In Scope", "Out of Scope"])
|
|
1375
|
+
: sectionBodyByName(sections, v.section);
|
|
1376
|
+
const hasHeading = body !== null;
|
|
1335
1377
|
const effectiveRequiredFromOverride = overrideSet
|
|
1336
1378
|
? overrideSet.has(sectionKey) ? true : false
|
|
1337
1379
|
: v.required;
|
|
@@ -1340,7 +1382,6 @@ export async function lintArtifact(projectRoot, stage, track = "standard") {
|
|
|
1340
1382
|
: stage === "scope" && sectionKey === "pre-scope system audit" && scopePreAuditEnabled
|
|
1341
1383
|
? true
|
|
1342
1384
|
: effectiveRequiredFromOverride;
|
|
1343
|
-
const body = hasHeading ? sectionBodyByName(sections, v.section) : null;
|
|
1344
1385
|
const validation = body === null
|
|
1345
1386
|
? { ok: false, details: `No ## heading matching required section "${v.section}".` }
|
|
1346
1387
|
: validateSectionBody(body, v.validationRule, v.section);
|
|
@@ -1403,12 +1444,8 @@ export async function lintArtifact(projectRoot, stage, track = "standard") {
|
|
|
1403
1444
|
.map((line) => line.trim())
|
|
1404
1445
|
.filter((line) => /^(?:[-*]|\d+\.)\s+\S/u.test(line));
|
|
1405
1446
|
const rowCount = Math.max(tableRows.length, bulletRows.length);
|
|
1406
|
-
const
|
|
1407
|
-
|
|
1408
|
-
return /\bchallenger\b/iu.test(joined) && /\bhigher[-\s]?upside\b/iu.test(joined);
|
|
1409
|
-
});
|
|
1410
|
-
const hasChallengerFromBullets = bulletRows.some((row) => /\bchallenger\b/iu.test(row) && /\bhigher[-\s]?upside\b/iu.test(row));
|
|
1411
|
-
const hasChallenger = hasChallengerFromTable || hasChallengerFromBullets;
|
|
1447
|
+
const approachRows = getApproachRows(approachesBody);
|
|
1448
|
+
const hasChallenger = approachRows.some(hasSemanticChallenger);
|
|
1412
1449
|
findings.push({
|
|
1413
1450
|
section: "Distinct Approaches Enforcement",
|
|
1414
1451
|
required: true,
|
|
@@ -1421,11 +1458,11 @@ export async function lintArtifact(projectRoot, stage, track = "standard") {
|
|
|
1421
1458
|
findings.push({
|
|
1422
1459
|
section: "Challenger Alternative Enforcement",
|
|
1423
1460
|
required: true,
|
|
1424
|
-
rule: "Approaches must include one option
|
|
1461
|
+
rule: "Approaches must include one challenger option with explicit high/higher upside.",
|
|
1425
1462
|
found: hasChallenger,
|
|
1426
1463
|
details: hasChallenger
|
|
1427
|
-
? "
|
|
1428
|
-
: "Missing
|
|
1464
|
+
? "Semantic challenger with high/higher upside detected."
|
|
1465
|
+
: "Missing a challenger option with explicit high/higher upside. Example: `| C | challenger | high upside | More ambitious path with clear trade-offs |`."
|
|
1429
1466
|
});
|
|
1430
1467
|
}
|
|
1431
1468
|
const reactionIndex = headingLineIndex(raw, "Approach Reaction");
|
|
@@ -1455,15 +1492,17 @@ export async function lintArtifact(projectRoot, stage, track = "standard") {
|
|
|
1455
1492
|
: "No explicit `approved`/`approval` marker found in Selected Direction."
|
|
1456
1493
|
});
|
|
1457
1494
|
if (!brainstormShortCircuitActivated) {
|
|
1458
|
-
const
|
|
1495
|
+
const reactionBody = sectionBodyByName(sections, "Approach Reaction");
|
|
1496
|
+
const reactionTrace = /\b(?:reaction|feedback|concern(?:s)?)\b/iu.test(directionBody) ||
|
|
1497
|
+
(reactionIndex >= 0 && reactionIndex < directionIndex && meaningfulLineCount(reactionBody ?? "") > 0);
|
|
1459
1498
|
findings.push({
|
|
1460
1499
|
section: "Direction Reaction Trace",
|
|
1461
1500
|
required: true,
|
|
1462
|
-
rule: "Selected Direction
|
|
1501
|
+
rule: "Selected Direction must be traceable to a prior Approach Reaction section or explicitly reference user reaction/feedback/concerns.",
|
|
1463
1502
|
found: reactionTrace,
|
|
1464
1503
|
details: reactionTrace
|
|
1465
|
-
? "Selected Direction
|
|
1466
|
-
: "Selected Direction
|
|
1504
|
+
? "Selected Direction is traceable to prior user reaction."
|
|
1505
|
+
: "Selected Direction is not traceable to user reaction. Add `## Approach Reaction` before it, or mention the user's reaction/concerns in the rationale."
|
|
1467
1506
|
});
|
|
1468
1507
|
}
|
|
1469
1508
|
}
|
|
@@ -1613,7 +1652,7 @@ export async function lintArtifact(projectRoot, stage, track = "standard") {
|
|
|
1613
1652
|
const strictScopeGuards = parsedFrontmatter.hasFrontmatter ||
|
|
1614
1653
|
headingPresent(sections, "Locked Decisions (D-XX)");
|
|
1615
1654
|
const scopeSections = [
|
|
1616
|
-
|
|
1655
|
+
sectionBodyByAnyName(sections, ["In Scope / Out of Scope", "In Scope", "Out of Scope"]) ?? "",
|
|
1617
1656
|
sectionBodyByName(sections, "Scope Summary") ?? "",
|
|
1618
1657
|
lockedDecisionsBody
|
|
1619
1658
|
].join("\n");
|
package/dist/content/hooks.js
CHANGED
|
@@ -78,7 +78,7 @@ async function main() {
|
|
|
78
78
|
const cliEntrypoint = process.env.CCLAW_CLI_JS || CCLAW_CLI_ENTRYPOINT;
|
|
79
79
|
if (!cliEntrypoint || cliEntrypoint.trim().length === 0) {
|
|
80
80
|
process.stderr.write(
|
|
81
|
-
"[cclaw] " + HELPER_NAME + ": local Node runtime entrypoint is missing. Re-run npx cclaw-cli sync or
|
|
81
|
+
"[cclaw] " + HELPER_NAME + ": local Node runtime entrypoint is missing. Re-run npx cclaw-cli sync, or set CCLAW_CLI_JS=/absolute/path/to/dist/cli.js for this session.\\n"
|
|
82
82
|
);
|
|
83
83
|
process.exitCode = 1;
|
|
84
84
|
return;
|
|
@@ -89,7 +89,7 @@ async function main() {
|
|
|
89
89
|
if (!stat.isFile()) throw new Error("not-file");
|
|
90
90
|
} catch {
|
|
91
91
|
process.stderr.write(
|
|
92
|
-
"[cclaw] " + HELPER_NAME + ": local Node runtime entrypoint not found at " + cliEntrypoint + ". Re-run npx cclaw-cli sync or
|
|
92
|
+
"[cclaw] " + HELPER_NAME + ": local Node runtime entrypoint not found at " + cliEntrypoint + ". Re-run npx cclaw-cli sync, or set CCLAW_CLI_JS=/absolute/path/to/dist/cli.js for this session.\\n"
|
|
93
93
|
);
|
|
94
94
|
process.exitCode = 1;
|
|
95
95
|
return;
|
|
@@ -176,7 +176,7 @@ function printUsage() {
|
|
|
176
176
|
process.stderr.write(
|
|
177
177
|
"Usage: node " +
|
|
178
178
|
RUNTIME_ROOT +
|
|
179
|
-
"/hooks/stage-complete.mjs <stage> [--passed=...] [--evidence-json=...] [--waive-delegation=...] [--waiver-reason=...]\\n"
|
|
179
|
+
"/hooks/stage-complete.mjs <stage> [--passed=...] [--evidence-json=...] [--waive-delegation=...] [--waiver-reason=...] [--json]\\n"
|
|
180
180
|
);
|
|
181
181
|
}
|
|
182
182
|
|
|
@@ -202,7 +202,7 @@ async function main() {
|
|
|
202
202
|
const cliEntrypoint = process.env.CCLAW_CLI_JS || CCLAW_CLI_ENTRYPOINT;
|
|
203
203
|
if (!cliEntrypoint || cliEntrypoint.trim().length === 0) {
|
|
204
204
|
process.stderr.write(
|
|
205
|
-
"[cclaw] stage-complete: local Node runtime entrypoint is missing. Re-run npx cclaw-cli sync or
|
|
205
|
+
"[cclaw] stage-complete: local Node runtime entrypoint is missing. Re-run npx cclaw-cli sync, or set CCLAW_CLI_JS=/absolute/path/to/dist/cli.js for this session.\\n"
|
|
206
206
|
);
|
|
207
207
|
process.exitCode = 1;
|
|
208
208
|
return;
|
|
@@ -213,7 +213,7 @@ async function main() {
|
|
|
213
213
|
if (!stat.isFile()) throw new Error("not-file");
|
|
214
214
|
} catch {
|
|
215
215
|
process.stderr.write(
|
|
216
|
-
"[cclaw] stage-complete: local Node runtime entrypoint not found at " + cliEntrypoint + ". Re-run npx cclaw-cli sync or
|
|
216
|
+
"[cclaw] stage-complete: local Node runtime entrypoint not found at " + cliEntrypoint + ". Re-run npx cclaw-cli sync, or set CCLAW_CLI_JS=/absolute/path/to/dist/cli.js for this session.\\n"
|
|
217
217
|
);
|
|
218
218
|
process.exitCode = 1;
|
|
219
219
|
return;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
import { nextStage as nextStageForTrack } from "../flow-state.js";
|
|
2
3
|
import { conversationLanguagePolicyMarkdown } from "./language-policy.js";
|
|
3
4
|
import { stageSchema } from "./stage-schema.js";
|
|
4
5
|
import { closeoutChainInline, closeoutNextCommandGuidance, closeoutSubstateInline, closeoutSubstateProtocolBullets } from "./closeout-guidance.js";
|
|
@@ -152,6 +153,15 @@ export function nextCommandSkillMarkdown() {
|
|
|
152
153
|
const delegationPath = delegationLogPathLine();
|
|
153
154
|
const reconciliationNoticesPath = reconciliationNoticesPathLine();
|
|
154
155
|
const stageRows = ["brainstorm", "scope", "design", "spec", "plan", "tdd", "review", "ship"]
|
|
156
|
+
.map((stage) => {
|
|
157
|
+
const skillMd = `${RUNTIME_ROOT}/skills/${stageSkillFolder(stage)}/SKILL.md`;
|
|
158
|
+
const standardNext = nextStageForTrack(stage, "standard") ?? "(terminal)";
|
|
159
|
+
const mediumNext = nextStageForTrack(stage, "medium") ?? "not in track";
|
|
160
|
+
const quickNext = nextStageForTrack(stage, "quick") ?? "not in track";
|
|
161
|
+
return `| \`${stage}\` | \`${standardNext}\` | \`${mediumNext}\` | \`${quickNext}\` | \`${skillMd}\` |`;
|
|
162
|
+
})
|
|
163
|
+
.join("\n");
|
|
164
|
+
const naturalStageRows = ["brainstorm", "scope", "design", "spec", "plan", "tdd", "review", "ship"]
|
|
155
165
|
.map((stage) => {
|
|
156
166
|
const schema = stageSchema(stage);
|
|
157
167
|
const next = schema.next === "done" ? "(terminal)" : schema.next;
|
|
@@ -258,12 +268,18 @@ Otherwise (non-terminal \`next\`): load the next stage skill and begin execution
|
|
|
258
268
|
|
|
259
269
|
## Stage order
|
|
260
270
|
|
|
261
|
-
This table is the critical path. After \`ship\`, \`/cc-next\` continues closeout via ${closeoutSubstateInline()}: ${closeoutChainInline()}.
|
|
271
|
+
This table is the track-aware critical path. It must match \`flow-state.json.track\`; do not follow the natural schema edge when the active track skips a stage. After \`ship\`, \`/cc-next\` continues closeout via ${closeoutSubstateInline()}: ${closeoutChainInline()}.
|
|
262
272
|
|
|
263
|
-
| Stage |
|
|
264
|
-
|
|
273
|
+
| Stage | Standard next | Medium next | Quick next | Skill path |
|
|
274
|
+
|---|---|---|---|---|
|
|
265
275
|
${stageRows}
|
|
266
276
|
|
|
277
|
+
Natural schema edge reference for diagnostics only:
|
|
278
|
+
|
|
279
|
+
| Stage | Natural next | Skill path |
|
|
280
|
+
|---|---|---|
|
|
281
|
+
${naturalStageRows}
|
|
282
|
+
|
|
267
283
|
## Anti-patterns
|
|
268
284
|
|
|
269
285
|
- Advancing when \`blocked\` is non-empty for the current stage.
|
|
@@ -535,6 +535,33 @@ function normalizeStopReason(value) {
|
|
|
535
535
|
return "user_opt_out";
|
|
536
536
|
return null;
|
|
537
537
|
}
|
|
538
|
+
function parseScoreCell(value) {
|
|
539
|
+
const percent = /([0-9]*\.?[0-9]+)\s*%/u.exec(value)?.[1];
|
|
540
|
+
if (percent !== undefined) {
|
|
541
|
+
const score = Number(percent) / 100;
|
|
542
|
+
return Number.isFinite(score) ? clampScore(score) : null;
|
|
543
|
+
}
|
|
544
|
+
const direct = Number(value.trim());
|
|
545
|
+
if (Number.isFinite(direct)) {
|
|
546
|
+
return clampScore(direct > 1 ? direct / 100 : direct);
|
|
547
|
+
}
|
|
548
|
+
const embedded = /([0-9]*\.?[0-9]+)/u.exec(value)?.[1];
|
|
549
|
+
if (embedded === undefined)
|
|
550
|
+
return null;
|
|
551
|
+
const score = Number(embedded);
|
|
552
|
+
if (!Number.isFinite(score))
|
|
553
|
+
return null;
|
|
554
|
+
return clampScore(score > 1 ? score / 100 : score);
|
|
555
|
+
}
|
|
556
|
+
function parseFindingsCountCell(value) {
|
|
557
|
+
if (/\b(?:none|zero|clear|no findings?|0)\b/iu.test(value))
|
|
558
|
+
return 0;
|
|
559
|
+
const match = /(\d+)/u.exec(value);
|
|
560
|
+
if (!match)
|
|
561
|
+
return null;
|
|
562
|
+
const count = Number(match[1]);
|
|
563
|
+
return Number.isInteger(count) && count >= 0 ? count : null;
|
|
564
|
+
}
|
|
538
565
|
function parseIterationsTable(sectionBody) {
|
|
539
566
|
const rows = [];
|
|
540
567
|
const lines = sectionBody.split(/\r?\n/gu);
|
|
@@ -548,22 +575,22 @@ function parseIterationsTable(sectionBody) {
|
|
|
548
575
|
.map((cell) => cell.trim());
|
|
549
576
|
if (cells.length < 3)
|
|
550
577
|
continue;
|
|
551
|
-
if (
|
|
578
|
+
if (/^iteration$/iu.test(cells[0] ?? ""))
|
|
552
579
|
continue;
|
|
553
580
|
if (/^-+$/u.test((cells[0] ?? "").replace(/:/gu, "")))
|
|
554
581
|
continue;
|
|
555
|
-
const iteration = Number(cells[0]);
|
|
556
|
-
const qualityScore =
|
|
557
|
-
const findingsCount =
|
|
582
|
+
const iteration = Number(/(\d+)/u.exec(cells[0] ?? "")?.[1] ?? "");
|
|
583
|
+
const qualityScore = parseScoreCell(cells[1] ?? "");
|
|
584
|
+
const findingsCount = parseFindingsCountCell(cells[2] ?? "");
|
|
558
585
|
if (!Number.isInteger(iteration) || iteration < 1)
|
|
559
586
|
continue;
|
|
560
|
-
if (
|
|
587
|
+
if (qualityScore === null)
|
|
561
588
|
continue;
|
|
562
|
-
if (
|
|
589
|
+
if (findingsCount === null)
|
|
563
590
|
continue;
|
|
564
591
|
rows.push({
|
|
565
592
|
iteration,
|
|
566
|
-
qualityScore
|
|
593
|
+
qualityScore,
|
|
567
594
|
findingsCount
|
|
568
595
|
});
|
|
569
596
|
}
|
|
@@ -593,11 +620,11 @@ export function extractReviewLoopEnvelopeFromArtifact(markdown, stage, artifactP
|
|
|
593
620
|
if (iterations.length === 0)
|
|
594
621
|
return null;
|
|
595
622
|
const stopReasonFromSection = normalizeStopReason(/-\s*Stop reason:\s*([a-z_]+)/iu.exec(sectionBody)?.[1]);
|
|
596
|
-
const targetFromSection =
|
|
623
|
+
const targetFromSection = parseScoreCell(/-\s*Target score:\s*([^\n]+)/iu.exec(sectionBody)?.[1] ?? "");
|
|
597
624
|
const maxFromSection = Number(/-\s*Max iterations:\s*(\d+)/iu.exec(sectionBody)?.[1] ?? "");
|
|
598
625
|
const header = parseHeaderMeta(markdown);
|
|
599
|
-
const targetScore =
|
|
600
|
-
?
|
|
626
|
+
const targetScore = targetFromSection !== null
|
|
627
|
+
? targetFromSection
|
|
601
628
|
: REVIEW_LOOP_DEFAULT_TARGET_SCORE;
|
|
602
629
|
const maxIterationsCandidate = Number.isInteger(maxFromSection) && maxFromSection > 0
|
|
603
630
|
? maxFromSection
|
package/dist/content/skills.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RUNTIME_ROOT, STAGE_TO_SKILL_FOLDER } from "../constants.js";
|
|
2
|
+
import { nextStage as nextStageForTrack } from "../flow-state.js";
|
|
2
3
|
import { stageExamples } from "./examples.js";
|
|
3
4
|
import { reviewStackAwareRoutes, reviewStackAwareRoutingSummary, stageAutoSubagentDispatch, stageSchema, stageTrackRenderContext } from "./stage-schema.js";
|
|
4
5
|
import { conversationLanguagePolicyMarkdown } from "./language-policy.js";
|
|
@@ -201,13 +202,15 @@ function mergedAntiPatterns(philosophy, execution) {
|
|
|
201
202
|
}
|
|
202
203
|
function completionParametersBlock(schema, track) {
|
|
203
204
|
const gateList = schema.executionModel.requiredGates.map((g) => `\`${g.id}\``).join(", ");
|
|
205
|
+
const mandatoryAgents = schema.reviewLens.mandatoryDelegations;
|
|
204
206
|
const mandatory = schema.reviewLens.mandatoryDelegations.length > 0
|
|
205
207
|
? schema.reviewLens.mandatoryDelegations.map((a) => `\`${a}\``).join(", ")
|
|
206
208
|
: "none";
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
+
const resolvedNextStage = nextStageForTrack(schema.stage, track);
|
|
210
|
+
const nextStage = resolvedNextStage ?? "done";
|
|
211
|
+
const nextDescription = nextStage === "done"
|
|
209
212
|
? "flow complete"
|
|
210
|
-
: stageSchema(
|
|
213
|
+
: stageSchema(nextStage, track).skillDescription;
|
|
211
214
|
return `## Completion Parameters
|
|
212
215
|
|
|
213
216
|
- \`stage\`: \`${schema.stage}\`
|
|
@@ -217,8 +220,9 @@ function completionParametersBlock(schema, track) {
|
|
|
217
220
|
- \`mandatory delegations\`: ${mandatory}
|
|
218
221
|
- \`completion helper\`: \`node .cclaw/hooks/stage-complete.mjs ${schema.stage}\`
|
|
219
222
|
- \`completion helper with evidence\`: \`node .cclaw/hooks/stage-complete.mjs ${schema.stage} --evidence-json '{"<gate_id>":"<evidence note>"}' --passed=<gate_id>[,<gate_id>]\`
|
|
223
|
+
- \`completion helper JSON diagnostics\`: append \`--json\` to receive a machine-readable validation failure summary.
|
|
220
224
|
- Fill \`## Learnings\` before closeout: either \`- None this stage.\` or JSON bullets with required keys \`type\`, \`trigger\`, \`action\`, \`confidence\` (knowledge-schema compatible).
|
|
221
|
-
- Record mandatory delegation completion/waiver in \`${RUNTIME_ROOT}/state/delegation-log.json\` with rationale as needed.
|
|
225
|
+
- Record mandatory delegation completion/waiver in \`${RUNTIME_ROOT}/state/delegation-log.json\` with rationale as needed.${mandatoryAgents.length > 0 ? ` If a mandatory delegation cannot run in this harness, use \`--waive-delegation=${mandatoryAgents.join(",")} --waiver-reason="<why safe>"\` on the completion helper.` : ""}
|
|
222
226
|
- Never edit raw \`flow-state.json\` to complete a stage, even in advisory mode; that bypasses validation, gate evidence, and Learnings harvest. If the helper fails, stop and report the exact command/output instead of applying a manual state workaround.
|
|
223
227
|
- Completion protocol: verify required gates, update the artifact, then use the completion helper with \`--evidence-json\` and \`--passed\` for every satisfied gate.
|
|
224
228
|
`;
|
|
@@ -38,11 +38,12 @@ export const BRAINSTORM = {
|
|
|
38
38
|
checklist: [
|
|
39
39
|
"**Explore project context** — inspect existing files/docs/recent activity before asking what to build.",
|
|
40
40
|
"**Classify depth and scope** — pick Lightweight / Standard / Deep; decompose independent subsystems before deeper work.",
|
|
41
|
-
"**
|
|
41
|
+
"**Use compact discovery for simple apps** — for concrete low-risk asks (todo app, landing page, local widget), do one context pass, compare one baseline and one challenger, then ask for one explicit approval; do not drag the user through a full workshop.",
|
|
42
|
+
"**Short-circuit concrete asks** — for unambiguous implementation-only requests, write a compact brainstorm stub (context, problem, approved intent, constraints, assumptions) and ask for one explicit approval.",
|
|
42
43
|
"**Ask only decision-changing questions** — one at a time; if answers would not change approach, state the assumption and continue.",
|
|
43
44
|
"**Compare 2-3 distinct approaches** — include real trade-offs, withhold recommendation, and include one higher-upside challenger.",
|
|
44
45
|
"**Collect reaction before recommending** — ask which option feels closest and what concern remains, then recommend based on that reaction.",
|
|
45
|
-
"**Write and tighten the artifact** — scale sections to complexity, optionally add a compact diagram, then patch contradictions, weak trade-offs, placeholders, and
|
|
46
|
+
"**Write and tighten the artifact** — scale sections to complexity, optionally add a compact diagram, then patch contradictions, weak trade-offs, placeholders, ambiguity, and weak handoff language.",
|
|
46
47
|
"**Request explicit approval** — state exactly what direction is being approved; do not advance without approval and artifact review.",
|
|
47
48
|
"**Handoff** — only after approval, complete the stage and point to `/cc-next`."
|
|
48
49
|
],
|
|
@@ -50,12 +51,13 @@ export const BRAINSTORM = {
|
|
|
50
51
|
"Start from observed project context; if the idea is vague, first narrow the project type.",
|
|
51
52
|
"Ask at most one question per turn, only when decision-changing; if using a structured question tool, send exactly one question object, not a multi-question form.",
|
|
52
53
|
"If likely answers do not change architecture or scope boundaries, choose the default and state the assumption.",
|
|
54
|
+
"For simple greenfield web apps, present a compact A/B choice with one recommended path and one higher-upside challenger; keep the artifact concise but structurally complete.",
|
|
53
55
|
"Show approaches before the recommendation; include a higher-upside challenger and gather reaction first.",
|
|
54
56
|
"State exactly what is being approved, then **STOP** until the user explicitly approves the artifact."
|
|
55
57
|
],
|
|
56
58
|
process: [
|
|
57
59
|
"Explore project context and classify depth/scope.",
|
|
58
|
-
"
|
|
60
|
+
"Use compact discovery for simple apps, short-circuit implementation-only asks, or ask one decision-changing question at a time.",
|
|
59
61
|
"Compare 2-3 distinct approaches, including a higher-upside challenger.",
|
|
60
62
|
"Collect reaction, then recommend with rationale tied to that reaction.",
|
|
61
63
|
"Optionally park promising non-selected ideas in `.cclaw/seeds/`.",
|
|
@@ -74,6 +76,7 @@ export const BRAINSTORM = {
|
|
|
74
76
|
"2-3 approaches with trade-offs are recorded, including one higher-upside challenger option.",
|
|
75
77
|
"User reaction to approaches is captured before final recommendation.",
|
|
76
78
|
"Final recommendation explicitly reflects user reaction.",
|
|
79
|
+
"Selected Direction includes the handoff to the track-aware next stage: scope on standard, spec on medium when scope/design are skipped.",
|
|
77
80
|
"When a promising option is parked, a seed file is created under `.cclaw/seeds/` and referenced in the artifact.",
|
|
78
81
|
"Approved direction and approval marker are present.",
|
|
79
82
|
"Assumptions and open questions are captured (or explicitly marked as none)."
|
|
@@ -119,9 +122,9 @@ export const BRAINSTORM = {
|
|
|
119
122
|
{ section: "Clarifying Questions", required: false, validationRule: "Must capture question, answer, and decision impact for each clarifying question." },
|
|
120
123
|
{ section: "Approach Tier", required: true, validationRule: "Must classify depth as Lightweight/Standard/Deep and explain why." },
|
|
121
124
|
{ section: "Short-Circuit Decision", required: false, validationRule: "Must include Status/Why/Scope handoff lines when short-circuit is discussed; compact stubs are valid for concrete asks." },
|
|
122
|
-
{ section: "Approaches", required: true, validationRule: "Must compare 2-3
|
|
125
|
+
{ section: "Approaches", required: true, validationRule: "Must compare 2-3 distinct options with real trade-offs; include one option marked as a challenger with explicit high/higher upside." },
|
|
123
126
|
{ section: "Approach Reaction", required: true, validationRule: "Must appear before Selected Direction and summarize user reaction before recommendation, including `Closest option`, `Concerns`, and what changed after reaction." },
|
|
124
|
-
{ section: "Selected Direction", required: true, validationRule: "Must include the selected approach, an explicit approval marker,
|
|
127
|
+
{ section: "Selected Direction", required: true, validationRule: "Must include the selected approach, an explicit approval marker, rationale traceable to the prior Approach Reaction, and a track-aware next-stage handoff." },
|
|
125
128
|
{ section: "Design", required: false, validationRule: "Must cover architecture, key components, and data flow scaled to complexity." },
|
|
126
129
|
{ section: "Visual Companion", required: false, validationRule: "If architecture/data-flow complexity is medium+, include compact ASCII/Mermaid diagram or explicitly justify omission." },
|
|
127
130
|
{ section: "Assumptions and Open Questions", required: false, validationRule: "Must capture unresolved assumptions/open questions, or explicitly state none." }
|
|
@@ -40,6 +40,7 @@ export const DESIGN = {
|
|
|
40
40
|
},
|
|
41
41
|
executionModel: {
|
|
42
42
|
checklist: [
|
|
43
|
+
"Compact design lock — for simple greenfield/product slices, produce a tight but complete design spine: codebase investigation, architecture boundary, one labeled diagram, data flow, failure/rescue table, test/perf expectations, and handoff. Do not run a sprawling workshop when a strong engineering lock fits on one page.",
|
|
43
44
|
"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.",
|
|
44
45
|
"Tiered Research Fleet — run `research/research-fleet.md` before lock; record `.cclaw/artifacts/02a-research.md` and summarize concrete decisions in `## Research Fleet Synthesis`.",
|
|
45
46
|
"Design Doc Check — read upstream artifacts and current design docs; latest superseding doc wins.",
|
|
@@ -52,7 +53,7 @@ export const DESIGN = {
|
|
|
52
53
|
"Capture leftovers — seed high-upside deferred ideas, list unresolved decisions with defaults, document distribution for new artifact types, and cross-reference deferred items to scope or unresolved decisions."
|
|
53
54
|
],
|
|
54
55
|
interactionProtocol: [
|
|
55
|
-
"Review section-by-section: investigator first, critic second, then reconcile.",
|
|
56
|
+
"Review section-by-section: investigator first, critic second, then reconcile. For simple apps, collapse this into one compact design lock with explicit risks and a single approval stop.",
|
|
56
57
|
"Present each issue one at a time; do not batch issues or move sections until current issues are resolved.",
|
|
57
58
|
decisionProtocolInstruction("each issue", "describe concretely with file/line references, present labeled options (A/B/C) with trade-offs, effort estimate (S/M/L/XL), risk level (Low/Med/High), and mark one as (recommended)", "recommend the option that closes the issue with the smallest blast radius and clearest verification path"),
|
|
58
59
|
"If a section has no issues, say 'No issues found' and move on.",
|
|
@@ -60,7 +61,7 @@ export const DESIGN = {
|
|
|
60
61
|
"Take a firm position, push back on weak framing, and call out suboptimal architecture with concrete alternatives.",
|
|
61
62
|
"Classify ambiguity before acting: ask, enumerate-and-pick, or propose a hypothesis with validation path.",
|
|
62
63
|
"Before final approval, run the critic pass, reconcile material findings, and bound retries with the review-loop policy.",
|
|
63
|
-
"For baseline approval, present the full design and **STOP** until explicit approval.",
|
|
64
|
+
"For baseline approval, present the full design plus exact spec handoff and **STOP** until explicit approval.",
|
|
64
65
|
"**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be completed or explicitly waived, then close via `node .cclaw/hooks/stage-complete.mjs design`."
|
|
65
66
|
],
|
|
66
67
|
process: [
|
|
@@ -45,34 +45,35 @@ export const SCOPE = {
|
|
|
45
45
|
},
|
|
46
46
|
executionModel: {
|
|
47
47
|
checklist: [
|
|
48
|
-
"**CEO pass first** — read brainstorm, name the job-to-be-done, challenge whether this is the right product slice, and propose the highest-leverage scope in one pass.",
|
|
49
|
-
"**Pick one of four gstack modes** — SCOPE EXPANSION, SELECTIVE EXPANSION, HOLD SCOPE, or SCOPE REDUCTION.
|
|
50
|
-
"**Draft the 10-star vs
|
|
48
|
+
"**Compact CEO pass first** — read brainstorm, name the job-to-be-done, challenge whether this is the right product slice, and propose the highest-leverage scope in one pass. For simple apps, keep this to a tight scope contract instead of a full strategy workshop.",
|
|
49
|
+
"**Pick one of four gstack modes with the user** — SCOPE EXPANSION, SELECTIVE EXPANSION, HOLD SCOPE, or SCOPE REDUCTION. Recommend one, state why and what signal would change it, then STOP for the user's mode/scope approval before writing the final artifact.",
|
|
50
|
+
"**Draft the 10-star vs current-slice boundary** — show what would make the product meaningfully better, then explicitly choose what ships now, what is deferred, and what is excluded without using vague `later/for now` placeholders.",
|
|
51
51
|
"**Premise and leverage check** — test right problem, direct path, no-action outcome, existing-code leverage, and reversibility before asking the user anything.",
|
|
52
52
|
"**Compare implementation alternatives** — include minimum viable, product-grade, and ideal architecture options with effort/risk/reuse, then recommend one.",
|
|
53
|
-
"**Run outside voice before final approval** —
|
|
53
|
+
"**Run outside voice before final approval** — for simple/low-risk scope, record one concise adversarial self-check row; for complex/high-risk/configured scope, iterate until threshold. Record the loop summary in `## Spec Review Loop`, but do not treat it as user approval.",
|
|
54
54
|
"**Ask only one decision-changing question** — if the user rejects the contract but is unsure, offer 3-4 concrete scope moves instead of open-ended interrogation.",
|
|
55
|
-
"**Write the scope contract** — include in-scope/out-of-scope, discretion areas, deferred items, locked decisions, error/rescue notes, completion dashboard, scope summary, and explicit approval."
|
|
55
|
+
"**Write the scope contract after approval** — include in-scope/out-of-scope, discretion areas, deferred items, locked decisions, error/rescue notes, completion dashboard, scope summary, next-stage handoff, and explicit approval evidence."
|
|
56
56
|
],
|
|
57
57
|
interactionProtocol: [
|
|
58
58
|
decisionProtocolInstruction("scope mode selection", "present expand/selective/hold/reduce as labeled options with trade-offs and mark one as (recommended)", "recommend the option that best covers the prime-directive failure modes, four data-flow paths, observability, and deferred handling for the in-scope set with the smallest blast radius. Base your recommendation on default heuristics: greenfield -> expand, enhancement -> selective, bugfix/hotfix/refactor -> hold, broad blast radius -> reduce"),
|
|
59
|
-
"Do not walk the full checklist by default. Lead with
|
|
59
|
+
"Do not walk the full checklist by default. Lead with a proposed scope contract and the one decision that matters most; label the mode as recommended, not selected, until the user answers.",
|
|
60
|
+
"For simple web-app flows, default to HOLD SCOPE or SELECTIVE EXPANSION, show the exact in/out/deferred contract as a proposal, and STOP for one explicit approval before writing the final scope artifact or completing the stage.",
|
|
60
61
|
"Challenge premise first, take a firm position, and name one concrete condition that would change it.",
|
|
61
62
|
"Push back on weak framing: vague scope needs a specific user/problem, platform vision needs a narrow wedge, social proof needs behavioral evidence.",
|
|
62
63
|
"Resolve one structural scope issue at a time; otherwise state the assumption and move on.",
|
|
63
64
|
"If the user says no but cannot name the change, offer concrete moves: keep scope, add one obvious adjacent capability, reduce to wedge, or re-open stack/product direction.",
|
|
64
65
|
`Before final approval, record outside-voice findings and a \`## Spec Review Loop\` table using ${reviewLoopPolicySummary("scope")}`,
|
|
65
|
-
"**STOP.** Wait for explicit approval of the scope contract before advancing.",
|
|
66
|
-
"**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be completed or explicitly waived. Then close with `node .cclaw/hooks/stage-complete.mjs scope --passed=scope_mode_selected,scope_contract_written,scope_user_approved --evidence-json '{\"scope_mode_selected\":\"<mode + rationale>\",\"scope_contract_written\":\"<artifact path + sections>\"}'`.
|
|
66
|
+
"**STOP.** Wait for explicit user approval of the scope mode and scope contract before writing final approval language or advancing.",
|
|
67
|
+
"**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be completed or explicitly waived. If no isolated planner is available, use `--waive-delegation=planner --waiver-reason=\"role-switch scope self-review completed\"`. Then close with `node .cclaw/hooks/stage-complete.mjs scope --passed=scope_mode_selected,scope_contract_written,scope_user_approved --evidence-json '{\"scope_mode_selected\":\"<user-approved mode + rationale>\",\"scope_contract_written\":\"<artifact path + sections>\",\"scope_user_approved\":\"<explicit user approval quote or summary>\"}'`. `scope_user_approved` must cite the user's approval; review-loop evidence alone is not approval."
|
|
67
68
|
],
|
|
68
69
|
process: [
|
|
69
70
|
"Run configured pre-scope audit only when enabled.",
|
|
70
|
-
"Run the gstack-style CEO scope pass: job-to-be-done, premise challenge, 10-star upside, smallest useful wedge, and what would change the recommendation.",
|
|
71
|
+
"Run the gstack-style CEO scope pass scaled to risk: job-to-be-done, premise challenge, 10-star upside, smallest useful wedge, and what would change the recommendation.",
|
|
71
72
|
"Compare minimum viable, product-grade, and ideal architecture scope alternatives with explicit reuse/effort/risk.",
|
|
72
|
-
"
|
|
73
|
+
"Recommend a scope mode with explicit rationale, then ask for user opt-in before treating it as selected.",
|
|
73
74
|
"Run outside voice / adversarial self-check before final approval and record a valid `## Spec Review Loop` table.",
|
|
74
75
|
"Write explicit scope contract, discretion areas, deferred items, error/rescue registry, and D-XX locked decisions.",
|
|
75
|
-
"Produce scope summary
|
|
76
|
+
"Produce scope summary, completion dashboard, and exact next-stage handoff before asking final approval."
|
|
76
77
|
],
|
|
77
78
|
requiredGates: [
|
|
78
79
|
{ id: "scope_mode_selected", description: "One scope mode was explicitly selected." },
|
|
@@ -88,11 +89,12 @@ export const SCOPE = {
|
|
|
88
89
|
"Locked Decisions section lists stable D-XX IDs for non-negotiable boundaries.",
|
|
89
90
|
"Premise challenge findings documented.",
|
|
90
91
|
"Outside Voice findings and dispositions are recorded (accept/reject/defer with rationale) before final approval.",
|
|
91
|
-
`Spec review loop summary includes a table with columns Iteration, Quality Score, Findings, plus Stop reason, Target score, and Max iterations.
|
|
92
|
+
`Spec review loop summary includes a table with columns Iteration, Quality Score, Findings, plus Stop reason, Target score, and Max iterations. This is outside-voice evidence only; it does not satisfy user approval. ${reviewLoopPolicySummary("scope")}`,
|
|
92
93
|
reviewLoopSecondOpinionSummary("scope"),
|
|
93
94
|
"Deferred items list with one-line rationale for each.",
|
|
94
95
|
"When an upside deferred idea is parked, a seed file is created under `.cclaw/seeds/` and referenced in the artifact.",
|
|
95
|
-
"Completion dashboard lists per-section status, critical/open gaps, decision count, and unresolved items (or `None`)."
|
|
96
|
+
"Completion dashboard lists per-section status, critical/open gaps, decision count, and unresolved items (or `None`).",
|
|
97
|
+
"Scope Summary includes a next-stage handoff naming the track-aware successor (`design` for standard, `spec` for medium) and the decisions/artifacts it must carry forward."
|
|
96
98
|
],
|
|
97
99
|
inputs: ["brainstorm artifact", "timeline constraints", "product priorities"],
|
|
98
100
|
requiredContext: [
|
|
@@ -144,14 +146,14 @@ export const SCOPE = {
|
|
|
144
146
|
{ section: "Implementation Alternatives", required: false, validationRule: "2-3 options with Name, Summary, Effort, Risk, Pros, Cons, and Reuses. Must include minimal viable and ideal architecture options." },
|
|
145
147
|
{ section: "Scope Mode", required: true, validationRule: "Must state selected mode and rationale with default heuristic justification." },
|
|
146
148
|
{ section: "Mode-Specific Analysis", required: false, validationRule: "Deep/complex scope only: document the analysis matching the selected mode. Default path may record a concise mode rationale instead." },
|
|
147
|
-
{ section: "In Scope / Out of Scope", required: true, validationRule: "Two separate explicit lists. Out-of-scope must not be empty." },
|
|
149
|
+
{ section: "In Scope / Out of Scope", required: true, validationRule: "Two separate explicit lists. Canonical form is one `## In Scope / Out of Scope` section with `### In Scope` and `### Out of Scope`; legacy split `## In Scope` and `## Out of Scope` headings are accepted. Out-of-scope must not be empty." },
|
|
148
150
|
{ section: "Discretion Areas", required: false, validationRule: "Explicit list of implementer decision zones, or 'None' if scope is fully locked." },
|
|
149
151
|
{ section: "Deferred Items", required: false, validationRule: "Each item has one-line rationale. If empty, state 'None' explicitly." },
|
|
150
152
|
{ section: "Error & Rescue Registry", required: false, validationRule: "Each scoped capability has: failure mode, detection method, fallback decision." },
|
|
151
153
|
{ section: "Outside Voice Findings", required: false, validationRule: "Must list external/adversarial findings and disposition (accept/reject/defer) with rationale." },
|
|
152
154
|
{ section: "Spec Review Loop", required: false, validationRule: `Must record iterations, quality score per iteration, stop reason, and unresolved concerns. Enforce ${reviewLoopPolicySummary("scope")}` },
|
|
153
155
|
{ section: "Completion Dashboard", required: true, validationRule: "Lists per-review-section status, count of critical/open gaps, resolved decisions, and unresolved decisions (or 'None')." },
|
|
154
|
-
{ section: "Scope Summary", required: true, validationRule: "Clean summary: mode, strongest challenges, recommended path, accepted scope, deferred, excluded." },
|
|
156
|
+
{ section: "Scope Summary", required: true, validationRule: "Clean summary: mode, strongest challenges, recommended path, accepted scope, deferred, excluded, and track-aware next-stage handoff." },
|
|
155
157
|
{ section: "Dream State Mapping", required: false, validationRule: "If present (complex projects): CURRENT STATE, THIS PLAN, 12-MONTH IDEAL, and alignment verdict." },
|
|
156
158
|
{ section: "Temporal Interrogation", required: false, validationRule: "If present (complex projects): timeline simulation table with decision pressures and lock-now vs defer verdicts." }
|
|
157
159
|
]
|
|
@@ -70,7 +70,7 @@ ${conversationLanguagePolicyMarkdown()}
|
|
|
70
70
|
Skip detection quietly if no markers are found — do NOT invent a stack.
|
|
71
71
|
|
|
72
72
|
5. Read \`${flowPath}\`.
|
|
73
|
-
6. If flow already has completed stages, warn the user that starting a new tracked flow will reset progress. Ask for confirmation before proceeding.
|
|
73
|
+
6. If flow already has completed stages, warn the user that starting a new tracked flow will reset progress. Ask for confirmation before proceeding. A fresh init placeholder state with \`completedStages: []\`, no passed gates, and no \`00-idea.md\` is **not** an active flow; do not ask the user to resume it.
|
|
74
74
|
7. **Track heuristic** — classify the idea text and **recommend** a track (the user can override before any state mutation):
|
|
75
75
|
- First, load \`${RUNTIME_ROOT}/config.yaml\`. If \`trackHeuristics\` is defined, apply those per-track vocabulary hints (\`fallback\`, \`tracks.<id>.{triggers,veto}\`) on top of the built-in defaults. Evaluation order is always \`standard -> medium -> quick\` (narrow-to-broad).
|
|
76
76
|
- **quick** (\`spec → tdd → review → ship\`) — single-purpose work where the spec is essentially already known.
|
|
@@ -152,7 +152,7 @@ description: "Unified entry point for the cclaw flow. No args = resume/next. Wit
|
|
|
152
152
|
|
|
153
153
|
## HARD-GATE
|
|
154
154
|
|
|
155
|
-
Do **not** silently discard an existing flow when the user provides a prompt. If completed stages exist, inform and confirm before resetting.
|
|
155
|
+
Do **not** silently discard an existing flow when the user provides a prompt. If completed stages exist, inform and confirm before resetting. A freshly initialized placeholder state with \`completedStages: []\`, no passed gates, and no \`${RUNTIME_ROOT}/artifacts/00-idea.md\` is not an active flow; classify the prompt and start normally.
|
|
156
156
|
|
|
157
157
|
${conversationLanguagePolicyMarkdown()}
|
|
158
158
|
## Protocol
|
|
@@ -168,6 +168,7 @@ ${conversationLanguagePolicyMarkdown()}
|
|
|
168
168
|
- Inform: "You have an active flow at stage **{currentStage}** with {N} completed stages. Starting a new tracked flow will reset progress."
|
|
169
169
|
- Ask: "Continue with reset? (A) Yes, start fresh (B) No, resume current flow"
|
|
170
170
|
- If (B) → switch to Path B behavior.
|
|
171
|
+
If \`completedStages\` is empty, all gate \`passed\` arrays are empty, and \`${RUNTIME_ROOT}/artifacts/00-idea.md\` is missing, treat it as a fresh init placeholder — do **not** ask whether to continue the current flow.
|
|
171
172
|
7. **Classify the idea** using the heuristic below and present one compact Start framing summary (class, track, stack, origin docs, seed recalls, next action). Wait for explicit confirmation or override before mutating any state only when reset/conflict/ambiguity makes it necessary.
|
|
172
173
|
- If \`${RUNTIME_ROOT}/config.yaml\` defines \`trackHeuristics\`, apply those vocabulary hints (\`fallback\`, \`tracks.<id>.{triggers,veto}\`) on top of built-in defaults. Evaluation order is fixed: \`standard -> medium -> quick\`. (Honest note: this is advisory prose; the LLM applies it, not a Node-level router.)
|
|
173
174
|
|
|
@@ -46,10 +46,10 @@ export const ARTIFACT_TEMPLATES = {
|
|
|
46
46
|
- Scope handoff:
|
|
47
47
|
|
|
48
48
|
## Approaches
|
|
49
|
-
| Approach | Role | Architecture | Trade-offs | Recommendation |
|
|
50
|
-
|
|
51
|
-
| A | baseline | | | |
|
|
52
|
-
| B | challenger
|
|
49
|
+
| Approach | Role | Upside | Architecture | Trade-offs | Recommendation |
|
|
50
|
+
|---|---|---|---|---|---|
|
|
51
|
+
| A | baseline | modest | | | |
|
|
52
|
+
| B | challenger | high | | | |
|
|
53
53
|
|
|
54
54
|
## Approach Reaction
|
|
55
55
|
- Closest option:
|
|
@@ -58,8 +58,9 @@ export const ARTIFACT_TEMPLATES = {
|
|
|
58
58
|
|
|
59
59
|
## Selected Direction
|
|
60
60
|
- **Approach:**
|
|
61
|
-
- **Rationale:**
|
|
61
|
+
- **Rationale:** Trace this to the prior Approach Reaction.
|
|
62
62
|
- **Approval:** pending
|
|
63
|
+
- **Next-stage handoff:** On standard track, hand this to \`scope\`; on medium track, hand this directly to \`spec\` with explicit requirements/constraints.
|
|
63
64
|
|
|
64
65
|
${SEED_SHELF_SECTION}
|
|
65
66
|
|
|
@@ -126,18 +127,18 @@ ${SEED_SHELF_SECTION}
|
|
|
126
127
|
| HOUR 6+ (polish/tests) | | | |
|
|
127
128
|
|
|
128
129
|
## Scope Mode
|
|
129
|
-
- [ ]
|
|
130
|
-
- [ ]
|
|
131
|
-
- [ ]
|
|
132
|
-
- [ ]
|
|
130
|
+
- [ ] SCOPE EXPANSION — dream bigger; user explicitly opts into the larger product slice.
|
|
131
|
+
- [ ] SELECTIVE EXPANSION — hold baseline scope and cherry-pick one high-leverage addition.
|
|
132
|
+
- [ ] HOLD SCOPE — preserve the approved brainstorm direction with maximum rigor.
|
|
133
|
+
- [ ] SCOPE REDUCTION — strip to the smallest useful wedge when risk/blast radius is too high.
|
|
133
134
|
|
|
134
135
|
## Mode-Specific Analysis
|
|
135
136
|
- **Selected mode:**
|
|
136
137
|
- **Analysis:**
|
|
137
|
-
- (
|
|
138
|
-
- (SELECTIVE: hold-scope baseline, cherry-picked expansions)
|
|
139
|
-
- (HOLD:
|
|
140
|
-
- (
|
|
138
|
+
- (SCOPE EXPANSION: 10-star opportunities, delight features)
|
|
139
|
+
- (SELECTIVE EXPANSION: hold-scope baseline, cherry-picked expansions)
|
|
140
|
+
- (HOLD SCOPE: approved slice with maximum rigor)
|
|
141
|
+
- (SCOPE REDUCTION: ruthless cuts, follow-up split)
|
|
141
142
|
|
|
142
143
|
## Requirements (stable IDs)
|
|
143
144
|
| ID | Requirement (observable outcome) | Priority | Source (origin doc / prompt line) |
|
|
@@ -187,6 +188,8 @@ ${SEED_SHELF_SECTION}
|
|
|
187
188
|
|---|---|---|---|
|
|
188
189
|
| 1 | 0.00 | 0 | continue/stop |
|
|
189
190
|
- Stop reason:
|
|
191
|
+
- Target score: 0.800
|
|
192
|
+
- Max iterations: 3
|
|
190
193
|
- Unresolved concerns:
|
|
191
194
|
|
|
192
195
|
## Completion Dashboard
|
|
@@ -199,6 +202,7 @@ ${SEED_SHELF_SECTION}
|
|
|
199
202
|
- Accepted scope:
|
|
200
203
|
- Deferred:
|
|
201
204
|
- Explicitly excluded:
|
|
205
|
+
- Next-stage handoff: identify whether the next stage is \`design\` (standard track) or \`spec\` (medium track), and list the exact artifacts/decisions it must carry forward.
|
|
202
206
|
|
|
203
207
|
## Learnings
|
|
204
208
|
- None this stage.
|
|
@@ -384,6 +388,8 @@ ${MARKDOWN_CODE_FENCE}
|
|
|
384
388
|
|---|---|---|---|
|
|
385
389
|
| 1 | 0.00 | 0 | continue/stop |
|
|
386
390
|
- Stop reason:
|
|
391
|
+
- Target score: 0.800
|
|
392
|
+
- Max iterations: 3
|
|
387
393
|
- Unresolved concerns:
|
|
388
394
|
|
|
389
395
|
## NOT in scope
|
|
@@ -19,7 +19,6 @@ import { runTddLoopStatusCommand } from "./tdd-loop-status.js";
|
|
|
19
19
|
import { runTddRedEvidenceCommand } from "./tdd-red-evidence.js";
|
|
20
20
|
import { extractReviewLoopEnvelopeFromArtifact } from "../content/review-loop.js";
|
|
21
21
|
const AUTO_REVIEW_LOOP_GATE_BY_STAGE = {
|
|
22
|
-
scope: "scope_user_approved",
|
|
23
22
|
design: "design_architecture_locked"
|
|
24
23
|
};
|
|
25
24
|
function unique(values) {
|
|
@@ -163,6 +162,30 @@ function validateReviewLoopGateEvidence(stage, evidence) {
|
|
|
163
162
|
}
|
|
164
163
|
return null;
|
|
165
164
|
}
|
|
165
|
+
function validateUserApprovalEvidence(evidence) {
|
|
166
|
+
const normalized = evidence.trim();
|
|
167
|
+
if (normalized.length === 0) {
|
|
168
|
+
return "must cite explicit user approval.";
|
|
169
|
+
}
|
|
170
|
+
const reviewLoopEnvelope = (() => {
|
|
171
|
+
try {
|
|
172
|
+
return pickReviewLoopEnvelope(JSON.parse(normalized));
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
})();
|
|
178
|
+
if (reviewLoopEnvelope) {
|
|
179
|
+
return "must cite explicit user approval; review-loop evidence is outside-voice evidence, not user approval.";
|
|
180
|
+
}
|
|
181
|
+
if (/\b(?:approved|approval|user approved|confirmed|accepted|yes|ok)\b/iu.test(normalized)) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
if (/\b(?:утвержд(?:аю|ено|ен|ена)|подтвержд(?:аю|ено|ен|ена)|соглас(?:ен|на|овано)|да|ок|принято)\b/iu.test(normalized)) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
return "must cite explicit user approval (for example `user approved the scope contract` or `пользователь утвердил scope`).";
|
|
188
|
+
}
|
|
166
189
|
// Per-gate validators keyed by `${stage}:${gateId}`. Returning a non-null
|
|
167
190
|
// string surfaces the reason as an `advance-stage` failure so evidence is
|
|
168
191
|
// guaranteed to carry the structural breadcrumbs downstream tooling
|
|
@@ -186,7 +209,7 @@ const GATE_EVIDENCE_VALIDATORS = {
|
|
|
186
209
|
}
|
|
187
210
|
return null;
|
|
188
211
|
},
|
|
189
|
-
"scope:scope_user_approved": (evidence) =>
|
|
212
|
+
"scope:scope_user_approved": (evidence) => validateUserApprovalEvidence(evidence),
|
|
190
213
|
"design:design_architecture_locked": (evidence) => validateReviewLoopGateEvidence("design", evidence)
|
|
191
214
|
};
|
|
192
215
|
function validateGateEvidenceShape(stage, gateId, evidence) {
|
|
@@ -195,6 +218,11 @@ function validateGateEvidenceShape(stage, gateId, evidence) {
|
|
|
195
218
|
return null;
|
|
196
219
|
return validator(evidence.trim());
|
|
197
220
|
}
|
|
221
|
+
function reviewLoopArtifactFixHint(stage, gateId) {
|
|
222
|
+
if (AUTO_REVIEW_LOOP_GATE_BY_STAGE[stage] !== gateId)
|
|
223
|
+
return "";
|
|
224
|
+
return " Add a `## Spec Review Loop` table to the artifact with rows like `| 1 | 0.80 | 0 |` plus `- Stop reason: quality_threshold_met`, `- Target score: 0.80`, and `- Max iterations: 3`; then omit this gate from manual evidence so stage-complete can auto-hydrate it.";
|
|
225
|
+
}
|
|
198
226
|
function parseStringList(raw) {
|
|
199
227
|
if (!Array.isArray(raw))
|
|
200
228
|
return [];
|
|
@@ -395,9 +423,14 @@ function parseAdvanceStageArgs(tokens) {
|
|
|
395
423
|
let waiveDelegations = [];
|
|
396
424
|
let waiverReason;
|
|
397
425
|
let quiet = false;
|
|
426
|
+
let json = false;
|
|
398
427
|
for (let i = 0; i < flagTokens.length; i += 1) {
|
|
399
428
|
const token = flagTokens[i];
|
|
400
429
|
const nextToken = flagTokens[i + 1];
|
|
430
|
+
if (token === "--json") {
|
|
431
|
+
json = true;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
401
434
|
if (token === "--quiet") {
|
|
402
435
|
quiet = true;
|
|
403
436
|
continue;
|
|
@@ -461,7 +494,8 @@ function parseAdvanceStageArgs(tokens) {
|
|
|
461
494
|
evidenceByGate: parseEvidenceByGate(evidenceJson),
|
|
462
495
|
waiveDelegations: unique(waiveDelegations),
|
|
463
496
|
waiverReason,
|
|
464
|
-
quiet
|
|
497
|
+
quiet,
|
|
498
|
+
json
|
|
465
499
|
};
|
|
466
500
|
}
|
|
467
501
|
function parseVerifyFlowStateDiffArgs(tokens) {
|
|
@@ -771,7 +805,7 @@ async function runAdvanceStage(projectRoot, args, io) {
|
|
|
771
805
|
? existing
|
|
772
806
|
: "";
|
|
773
807
|
const issue = validateGateEvidenceShape(args.stage, gateId, effectiveEvidence);
|
|
774
|
-
return issue ? [`${gateId}: ${issue}`] : [];
|
|
808
|
+
return issue ? [`${gateId}: ${issue}${reviewLoopArtifactFixHint(args.stage, gateId)}`] : [];
|
|
775
809
|
});
|
|
776
810
|
if (malformedGateEvidence.length > 0) {
|
|
777
811
|
io.stderr.write(`cclaw internal advance-stage: gate evidence format check failed: ${malformedGateEvidence.join(" | ")}.\n`);
|
|
@@ -802,9 +836,35 @@ async function runAdvanceStage(projectRoot, args, io) {
|
|
|
802
836
|
};
|
|
803
837
|
const validation = await buildValidationReport(projectRoot, candidateState);
|
|
804
838
|
if (!validation.ok) {
|
|
839
|
+
if (args.json) {
|
|
840
|
+
io.stdout.write(`${JSON.stringify({
|
|
841
|
+
ok: false,
|
|
842
|
+
command: "advance-stage",
|
|
843
|
+
stage: args.stage,
|
|
844
|
+
kind: "validation-failed",
|
|
845
|
+
delegation: validation.delegation,
|
|
846
|
+
gates: validation.gates,
|
|
847
|
+
completedStages: validation.completedStages,
|
|
848
|
+
nextActions: [
|
|
849
|
+
...(validation.delegation.missing.length > 0
|
|
850
|
+
? [`Complete or waive mandatory delegation(s): ${validation.delegation.missing.join(", ")}.`]
|
|
851
|
+
: []),
|
|
852
|
+
...(validation.delegation.missingEvidence.length > 0
|
|
853
|
+
? ["Add evidenceRefs for role-switch delegation completion or use an explicit waiver reason."]
|
|
854
|
+
: []),
|
|
855
|
+
...(validation.gates.issues.length > 0
|
|
856
|
+
? ["Fix the artifact/gate issue shown in gates.issues, then rerun stage-complete."]
|
|
857
|
+
: []),
|
|
858
|
+
...(validation.completedStages.issues.length > 0
|
|
859
|
+
? ["Repair previously completed stage gate closure before advancing."]
|
|
860
|
+
: [])
|
|
861
|
+
]
|
|
862
|
+
})}\n`);
|
|
863
|
+
}
|
|
805
864
|
io.stderr.write(`cclaw internal advance-stage: validation failed for stage "${args.stage}".\n`);
|
|
806
865
|
if (validation.delegation.missing.length > 0) {
|
|
807
866
|
io.stderr.write(`- missing delegations: ${validation.delegation.missing.join(", ")}\n`);
|
|
867
|
+
io.stderr.write(` next action: complete the delegation, or rerun with --waive-delegation=${validation.delegation.missing.join(",")} --waiver-reason="<why safe>".\n`);
|
|
808
868
|
}
|
|
809
869
|
if (validation.delegation.missingEvidence.length > 0) {
|
|
810
870
|
io.stderr.write(`- role-switch evidence missing: ${validation.delegation.missingEvidence.join(", ")}\n`);
|