cclaw-cli 0.48.31 → 0.48.33
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 +609 -10
- package/dist/config.d.ts +1 -1
- package/dist/config.js +82 -4
- package/dist/content/examples.js +23 -6
- package/dist/content/hook-inline-snippets.d.ts +80 -0
- package/dist/content/hook-inline-snippets.js +270 -0
- package/dist/content/ideate-command.d.ts +6 -2
- package/dist/content/ideate-command.js +43 -16
- package/dist/content/ideate-frames.d.ts +31 -0
- package/dist/content/ideate-frames.js +140 -0
- package/dist/content/ideate-ranking.d.ts +25 -0
- package/dist/content/ideate-ranking.js +65 -0
- package/dist/content/node-hooks.js +9 -197
- package/dist/content/review-loop.d.ts +192 -0
- package/dist/content/review-loop.js +689 -0
- package/dist/content/seed-shelf.d.ts +36 -0
- package/dist/content/seed-shelf.js +236 -0
- package/dist/content/skills.js +77 -4
- package/dist/content/stage-schema.d.ts +1 -1
- package/dist/content/stage-schema.js +18 -2
- package/dist/content/stages/brainstorm.js +20 -4
- package/dist/content/stages/design.js +36 -8
- package/dist/content/stages/plan.js +5 -0
- package/dist/content/stages/review.js +5 -0
- package/dist/content/stages/schema-types.d.ts +29 -0
- package/dist/content/stages/scope.js +22 -6
- package/dist/content/stages/ship.js +6 -0
- package/dist/content/stages/spec.js +6 -0
- package/dist/content/stages/tdd.js +6 -0
- package/dist/content/start-command.js +24 -18
- package/dist/content/templates.js +108 -4
- package/dist/internal/advance-stage.js +143 -1
- package/dist/trace-matrix.d.ts +14 -0
- package/dist/trace-matrix.js +55 -1
- package/dist/types.d.ts +27 -0
- package/package.json +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface SeedShelfEntry {
|
|
2
|
+
fileName: string;
|
|
3
|
+
absPath: string;
|
|
4
|
+
relPath: string;
|
|
5
|
+
createdOn: string;
|
|
6
|
+
title: string;
|
|
7
|
+
triggerWhen: string[];
|
|
8
|
+
sourceStage: string | null;
|
|
9
|
+
sourceArtifact: string | null;
|
|
10
|
+
hypothesis: string | null;
|
|
11
|
+
action: string | null;
|
|
12
|
+
summary: string;
|
|
13
|
+
raw: string;
|
|
14
|
+
}
|
|
15
|
+
export interface SeedTemplateInput {
|
|
16
|
+
title: string;
|
|
17
|
+
triggerWhen: readonly string[];
|
|
18
|
+
hypothesis: string;
|
|
19
|
+
action: string;
|
|
20
|
+
sourceStage?: string;
|
|
21
|
+
sourceArtifact?: string;
|
|
22
|
+
createdAt?: Date;
|
|
23
|
+
}
|
|
24
|
+
export interface ResolvedSeedPath {
|
|
25
|
+
fileName: string;
|
|
26
|
+
absPath: string;
|
|
27
|
+
relPath: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function seedShelfDir(projectRoot: string): string;
|
|
30
|
+
export declare function seedSlug(title: string): string;
|
|
31
|
+
export declare function seedFileName(title: string, createdAt?: Date): string;
|
|
32
|
+
export declare function resolveSeedPathForWrite(projectRoot: string, title: string, createdAt?: Date): Promise<ResolvedSeedPath>;
|
|
33
|
+
export declare function readSeedShelf(projectRoot: string): Promise<SeedShelfEntry[]>;
|
|
34
|
+
export declare function seedMatchesPrompt(seed: SeedShelfEntry, prompt: string): boolean;
|
|
35
|
+
export declare function findMatchingSeeds(projectRoot: string, prompt: string, maxMatches?: number): Promise<SeedShelfEntry[]>;
|
|
36
|
+
export declare function renderSeedTemplate(input: SeedTemplateInput): string;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse } from "yaml";
|
|
4
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
5
|
+
const SEED_FILE_NAME_PATTERN = /^SEED-(\d{4}-\d{2}-\d{2})-([a-z0-9]+(?:-[a-z0-9]+)*)(?:-(\d+))?\.md$/u;
|
|
6
|
+
const DEFAULT_MAX_MATCHES = 3;
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
function normalizeTriggerList(value) {
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return value
|
|
13
|
+
.map((item) => typeof item === "string" || typeof item === "number" ? String(item).trim() : "")
|
|
14
|
+
.filter((item) => item.length > 0);
|
|
15
|
+
}
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
return value
|
|
18
|
+
.split(/,\s*/u)
|
|
19
|
+
.map((item) => item.trim())
|
|
20
|
+
.filter((item) => item.length > 0);
|
|
21
|
+
}
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
function parseSeedFrontmatter(raw) {
|
|
25
|
+
if (!raw.startsWith("---")) {
|
|
26
|
+
return { values: {}, body: raw };
|
|
27
|
+
}
|
|
28
|
+
const lines = raw.split(/\r?\n/u);
|
|
29
|
+
let closingIndex = -1;
|
|
30
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
31
|
+
if (lines[index]?.trim() === "---") {
|
|
32
|
+
closingIndex = index;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (closingIndex < 0) {
|
|
37
|
+
return { values: {}, body: raw };
|
|
38
|
+
}
|
|
39
|
+
const frontmatterRaw = lines.slice(1, closingIndex).join("\n");
|
|
40
|
+
const body = lines.slice(closingIndex + 1).join("\n");
|
|
41
|
+
try {
|
|
42
|
+
const parsed = parse(frontmatterRaw);
|
|
43
|
+
if (isRecord(parsed)) {
|
|
44
|
+
return { values: parsed, body };
|
|
45
|
+
}
|
|
46
|
+
return { values: {}, body };
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return { values: {}, body };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function firstHeading(body) {
|
|
53
|
+
const match = /^#\s+(.+)$/mu.exec(body);
|
|
54
|
+
if (!match)
|
|
55
|
+
return null;
|
|
56
|
+
const title = match[1]?.trim() ?? "";
|
|
57
|
+
return title.length > 0 ? title : null;
|
|
58
|
+
}
|
|
59
|
+
function firstNonEmptyParagraph(body) {
|
|
60
|
+
const lines = body.split(/\r?\n/u);
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
const trimmed = line.trim();
|
|
63
|
+
if (trimmed.length === 0)
|
|
64
|
+
continue;
|
|
65
|
+
if (/^#\s+/u.test(trimmed))
|
|
66
|
+
continue;
|
|
67
|
+
if (/^[-*]\s+/u.test(trimmed))
|
|
68
|
+
continue;
|
|
69
|
+
return trimmed;
|
|
70
|
+
}
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
function fromFileNameFallbackTitle(fileName) {
|
|
74
|
+
const stem = fileName.replace(/\.md$/u, "");
|
|
75
|
+
const withoutPrefix = stem.replace(/^SEED-\d{4}-\d{2}-\d{2}-/u, "");
|
|
76
|
+
return withoutPrefix
|
|
77
|
+
.split("-")
|
|
78
|
+
.filter((part) => part.length > 0 && !/^\d+$/u.test(part))
|
|
79
|
+
.map((part) => part[0]?.toUpperCase() + part.slice(1))
|
|
80
|
+
.join(" ")
|
|
81
|
+
.trim();
|
|
82
|
+
}
|
|
83
|
+
export function seedShelfDir(projectRoot) {
|
|
84
|
+
return path.join(projectRoot, RUNTIME_ROOT, "seeds");
|
|
85
|
+
}
|
|
86
|
+
export function seedSlug(title) {
|
|
87
|
+
const normalized = title
|
|
88
|
+
.toLowerCase()
|
|
89
|
+
.trim()
|
|
90
|
+
.replace(/[`"'“”‘’()[\]{}<>]/gu, " ")
|
|
91
|
+
.replace(/[^a-z0-9]+/gu, "-")
|
|
92
|
+
.replace(/^-+/u, "")
|
|
93
|
+
.replace(/-+$/u, "");
|
|
94
|
+
if (normalized.length === 0) {
|
|
95
|
+
return "seed";
|
|
96
|
+
}
|
|
97
|
+
return normalized.slice(0, 48);
|
|
98
|
+
}
|
|
99
|
+
function isoDate(value) {
|
|
100
|
+
return value.toISOString().slice(0, 10);
|
|
101
|
+
}
|
|
102
|
+
export function seedFileName(title, createdAt = new Date()) {
|
|
103
|
+
return `SEED-${isoDate(createdAt)}-${seedSlug(title)}.md`;
|
|
104
|
+
}
|
|
105
|
+
async function pathExists(absPath) {
|
|
106
|
+
try {
|
|
107
|
+
await fs.stat(absPath);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export async function resolveSeedPathForWrite(projectRoot, title, createdAt = new Date()) {
|
|
115
|
+
const seedsDir = seedShelfDir(projectRoot);
|
|
116
|
+
await fs.mkdir(seedsDir, { recursive: true });
|
|
117
|
+
const baseFile = seedFileName(title, createdAt);
|
|
118
|
+
const baseStem = baseFile.replace(/\.md$/u, "");
|
|
119
|
+
let candidate = baseFile;
|
|
120
|
+
let index = 2;
|
|
121
|
+
while (await pathExists(path.join(seedsDir, candidate))) {
|
|
122
|
+
candidate = `${baseStem}-${index}.md`;
|
|
123
|
+
index += 1;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
fileName: candidate,
|
|
127
|
+
absPath: path.join(seedsDir, candidate),
|
|
128
|
+
relPath: path.join(RUNTIME_ROOT, "seeds", candidate)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export async function readSeedShelf(projectRoot) {
|
|
132
|
+
const dir = seedShelfDir(projectRoot);
|
|
133
|
+
let names = [];
|
|
134
|
+
try {
|
|
135
|
+
names = await fs.readdir(dir);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
const entries = [];
|
|
141
|
+
for (const fileName of names) {
|
|
142
|
+
if (!SEED_FILE_NAME_PATTERN.test(fileName))
|
|
143
|
+
continue;
|
|
144
|
+
const absPath = path.join(dir, fileName);
|
|
145
|
+
let raw = "";
|
|
146
|
+
try {
|
|
147
|
+
raw = await fs.readFile(absPath, "utf8");
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const frontmatter = parseSeedFrontmatter(raw);
|
|
153
|
+
const title = (typeof frontmatter.values.title === "string" && frontmatter.values.title.trim().length > 0
|
|
154
|
+
? frontmatter.values.title.trim()
|
|
155
|
+
: null) ??
|
|
156
|
+
firstHeading(frontmatter.body) ??
|
|
157
|
+
fromFileNameFallbackTitle(fileName) ??
|
|
158
|
+
"Untitled seed";
|
|
159
|
+
const triggerWhen = normalizeTriggerList(frontmatter.values.trigger_when ?? frontmatter.values.triggerWhen);
|
|
160
|
+
const sourceStage = typeof frontmatter.values.source_stage === "string"
|
|
161
|
+
? frontmatter.values.source_stage
|
|
162
|
+
: typeof frontmatter.values.sourceStage === "string"
|
|
163
|
+
? frontmatter.values.sourceStage
|
|
164
|
+
: null;
|
|
165
|
+
const sourceArtifact = typeof frontmatter.values.source_artifact === "string"
|
|
166
|
+
? frontmatter.values.source_artifact
|
|
167
|
+
: typeof frontmatter.values.sourceArtifact === "string"
|
|
168
|
+
? frontmatter.values.sourceArtifact
|
|
169
|
+
: null;
|
|
170
|
+
const hypothesis = typeof frontmatter.values.hypothesis === "string" ? frontmatter.values.hypothesis : null;
|
|
171
|
+
const action = typeof frontmatter.values.action === "string" ? frontmatter.values.action : null;
|
|
172
|
+
const createdOn = SEED_FILE_NAME_PATTERN.exec(fileName)?.[1] ?? "1970-01-01";
|
|
173
|
+
const summary = firstNonEmptyParagraph(frontmatter.body);
|
|
174
|
+
entries.push({
|
|
175
|
+
fileName,
|
|
176
|
+
absPath,
|
|
177
|
+
relPath: path.join(RUNTIME_ROOT, "seeds", fileName),
|
|
178
|
+
createdOn,
|
|
179
|
+
title,
|
|
180
|
+
triggerWhen,
|
|
181
|
+
sourceStage,
|
|
182
|
+
sourceArtifact,
|
|
183
|
+
hypothesis,
|
|
184
|
+
action,
|
|
185
|
+
summary,
|
|
186
|
+
raw
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
entries.sort((a, b) => b.fileName.localeCompare(a.fileName));
|
|
190
|
+
return entries;
|
|
191
|
+
}
|
|
192
|
+
export function seedMatchesPrompt(seed, prompt) {
|
|
193
|
+
const normalizedPrompt = prompt.toLowerCase().trim();
|
|
194
|
+
if (normalizedPrompt.length === 0)
|
|
195
|
+
return false;
|
|
196
|
+
if (seed.triggerWhen.length === 0)
|
|
197
|
+
return false;
|
|
198
|
+
return seed.triggerWhen.some((trigger) => normalizedPrompt.includes(trigger.toLowerCase()));
|
|
199
|
+
}
|
|
200
|
+
export async function findMatchingSeeds(projectRoot, prompt, maxMatches = DEFAULT_MAX_MATCHES) {
|
|
201
|
+
const seeds = await readSeedShelf(projectRoot);
|
|
202
|
+
const matches = seeds.filter((seed) => seedMatchesPrompt(seed, prompt));
|
|
203
|
+
return matches.slice(0, Math.max(1, maxMatches));
|
|
204
|
+
}
|
|
205
|
+
export function renderSeedTemplate(input) {
|
|
206
|
+
const triggerWhen = [...input.triggerWhen].map((item) => item.trim()).filter((item) => item.length > 0);
|
|
207
|
+
const createdAt = input.createdAt ?? new Date();
|
|
208
|
+
const sourceStage = input.sourceStage?.trim() || "unknown";
|
|
209
|
+
const sourceArtifact = input.sourceArtifact?.trim() || "unknown";
|
|
210
|
+
return `---
|
|
211
|
+
title: ${input.title.trim()}
|
|
212
|
+
created_at: ${createdAt.toISOString()}
|
|
213
|
+
source_stage: ${sourceStage}
|
|
214
|
+
source_artifact: ${sourceArtifact}
|
|
215
|
+
trigger_when:
|
|
216
|
+
${triggerWhen.length > 0 ? triggerWhen.map((trigger) => ` - ${trigger}`).join("\n") : " - <trigger token>"}
|
|
217
|
+
hypothesis: ${input.hypothesis.trim()}
|
|
218
|
+
action: ${input.action.trim()}
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
# ${input.title.trim()}
|
|
222
|
+
|
|
223
|
+
## Why capture this seed
|
|
224
|
+
${input.hypothesis.trim()}
|
|
225
|
+
|
|
226
|
+
## Trigger when
|
|
227
|
+
${triggerWhen.length > 0 ? triggerWhen.map((trigger) => `- ${trigger}`).join("\n") : "- <trigger token>"}
|
|
228
|
+
|
|
229
|
+
## Suggested action
|
|
230
|
+
${input.action.trim()}
|
|
231
|
+
|
|
232
|
+
## Notes
|
|
233
|
+
- Expected payoff:
|
|
234
|
+
- Risks:
|
|
235
|
+
`;
|
|
236
|
+
}
|
package/dist/content/skills.js
CHANGED
|
@@ -82,6 +82,18 @@ function reviewSectionsBlock(sectionsInput) {
|
|
|
82
82
|
${sections}
|
|
83
83
|
`;
|
|
84
84
|
}
|
|
85
|
+
function reviewLoopBlock(reviewLoop) {
|
|
86
|
+
if (!reviewLoop)
|
|
87
|
+
return "";
|
|
88
|
+
const checklist = reviewLoop.checklist.map((item) => `- \`${item}\``).join("\n");
|
|
89
|
+
return `## Outside Voice Review Loop
|
|
90
|
+
- Stage: \`${reviewLoop.stage}\`
|
|
91
|
+
- Target score: \`${reviewLoop.targetScore}\`
|
|
92
|
+
- Max iterations: \`${reviewLoop.maxIterations}\`
|
|
93
|
+
- Checklist dimensions:
|
|
94
|
+
${checklist}
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
85
97
|
function verificationBlock(stage) {
|
|
86
98
|
if (!VERIFICATION_STAGES.includes(stage))
|
|
87
99
|
return "";
|
|
@@ -297,6 +309,59 @@ function normalizedGuidanceKey(value) {
|
|
|
297
309
|
.trim()
|
|
298
310
|
.toLowerCase();
|
|
299
311
|
}
|
|
312
|
+
function mermaidNodeLabel(raw, index) {
|
|
313
|
+
const stripped = raw
|
|
314
|
+
.replace(/`[^`]+`/gu, "")
|
|
315
|
+
.replace(/\*\*/gu, "")
|
|
316
|
+
.replace(/[*_]/gu, "")
|
|
317
|
+
.replace(/\[[^\]]*\]\([^)]*\)/gu, "")
|
|
318
|
+
.split(/[—:.;]/u)[0]
|
|
319
|
+
?.trim() ?? "";
|
|
320
|
+
const words = stripped.split(/\s+/u).filter((word) => word.length > 0);
|
|
321
|
+
const short = words.slice(0, 4).join(" ");
|
|
322
|
+
const label = short.length === 0
|
|
323
|
+
? `Step ${index + 1}`
|
|
324
|
+
: short.replace(/["`]/gu, "");
|
|
325
|
+
return label.length > 48 ? `${label.slice(0, 45)}...` : label;
|
|
326
|
+
}
|
|
327
|
+
const MERMAID_PROCESS_MAX_NODES = 10;
|
|
328
|
+
function renderProcessFlowMermaid(executionModel) {
|
|
329
|
+
if (executionModel.processFlow && executionModel.processFlow.trim().length > 0) {
|
|
330
|
+
return `\`\`\`mermaid\n${executionModel.processFlow.trim()}\n\`\`\``;
|
|
331
|
+
}
|
|
332
|
+
const source = executionModel.process.length > 0
|
|
333
|
+
? executionModel.process
|
|
334
|
+
: executionModel.checklist;
|
|
335
|
+
if (source.length === 0) {
|
|
336
|
+
return "";
|
|
337
|
+
}
|
|
338
|
+
const limited = source.slice(0, MERMAID_PROCESS_MAX_NODES);
|
|
339
|
+
const nodes = limited.map((item, index) => ({
|
|
340
|
+
id: `S${index + 1}`,
|
|
341
|
+
label: mermaidNodeLabel(item, index)
|
|
342
|
+
}));
|
|
343
|
+
const lines = ["flowchart TD"];
|
|
344
|
+
for (const node of nodes) {
|
|
345
|
+
lines.push(` ${node.id}["${node.label}"]`);
|
|
346
|
+
}
|
|
347
|
+
for (let i = 0; i < nodes.length - 1; i += 1) {
|
|
348
|
+
lines.push(` ${nodes[i].id} --> ${nodes[i + 1].id}`);
|
|
349
|
+
}
|
|
350
|
+
if (source.length > MERMAID_PROCESS_MAX_NODES) {
|
|
351
|
+
lines.push(` S${nodes.length} --> More["...see full Checklist"]`);
|
|
352
|
+
}
|
|
353
|
+
return `\`\`\`mermaid\n${lines.join("\n")}\n\`\`\``;
|
|
354
|
+
}
|
|
355
|
+
function renderPlatformNotesBlock(notes) {
|
|
356
|
+
if (!notes || notes.length === 0) {
|
|
357
|
+
return "";
|
|
358
|
+
}
|
|
359
|
+
const body = notes.map((item) => `- ${item}`).join("\n");
|
|
360
|
+
return `## Platform Notes
|
|
361
|
+
${body}
|
|
362
|
+
|
|
363
|
+
`;
|
|
364
|
+
}
|
|
300
365
|
function dedupeGuidance(items, blockedBy) {
|
|
301
366
|
const blocked = new Set(blockedBy
|
|
302
367
|
.map((item) => normalizedGuidanceKey(item))
|
|
@@ -333,8 +398,10 @@ export function stageSkillMarkdown(stage, track = "standard") {
|
|
|
333
398
|
.map((item, i) => `${i + 1}. ${item}`)
|
|
334
399
|
.join("\n");
|
|
335
400
|
const interactionFocus = dedupeGuidance(executionModel.interactionProtocol, [...executionModel.checklist, ...executionModel.process]).slice(0, 5);
|
|
336
|
-
const
|
|
401
|
+
const processFlowMermaid = renderProcessFlowMermaid(executionModel);
|
|
402
|
+
const platformNotesBlock = renderPlatformNotesBlock(executionModel.platformNotes);
|
|
337
403
|
const stageRefs = stageSpecificSeeAlso(stage);
|
|
404
|
+
const reviewLoopSection = reviewLoopBlock(reviewLens.reviewLoop);
|
|
338
405
|
const mandatoryDelegationSummary = mandatoryDelegations.length > 0
|
|
339
406
|
? mandatoryDelegations.map((name) => `\`${name}\``).join(", ")
|
|
340
407
|
: "none";
|
|
@@ -373,7 +440,10 @@ ${philosophy.hardGate}
|
|
|
373
440
|
${mergedAntiPatterns(philosophy, executionModel)}
|
|
374
441
|
|
|
375
442
|
## Process
|
|
376
|
-
|
|
443
|
+
|
|
444
|
+
This is the stage **state machine** — the canonical ordered flow. For every detailed step, gate, and wording, follow the Checklist below; this diagram is the map, not the territory.
|
|
445
|
+
|
|
446
|
+
${processFlowMermaid.length > 0 ? processFlowMermaid : "```mermaid\nflowchart TD\n S1[\"Execute Checklist\"] --> S2[\"Satisfy required gates\"] --> S3[\"Verify before closeout\"]\n```"}
|
|
377
447
|
|
|
378
448
|
## Inputs
|
|
379
449
|
${executionModel.inputs.length > 0 ? executionModel.inputs.map((item) => `- ${item}`).join("\n") : "- (first stage — no required inputs)"}
|
|
@@ -381,7 +451,7 @@ ${executionModel.inputs.length > 0 ? executionModel.inputs.map((item) => `- ${it
|
|
|
381
451
|
## Required Context
|
|
382
452
|
${executionModel.requiredContext.length > 0 ? executionModel.requiredContext.map((item) => `- ${item}`).join("\n") : "- None beyond this skill"}
|
|
383
453
|
|
|
384
|
-
${contextLoadingBlock(artifactRules.crossStageTrace)}
|
|
454
|
+
${platformNotesBlock}${contextLoadingBlock(artifactRules.crossStageTrace)}
|
|
385
455
|
${autoSubagentDispatchBlock(stage, track)}
|
|
386
456
|
${researchPlaybooksBlock(executionModel.researchPlaybooks ?? [])}
|
|
387
457
|
|
|
@@ -394,6 +464,9 @@ ${checklistItems}
|
|
|
394
464
|
${stageExamples(stage)}
|
|
395
465
|
|
|
396
466
|
## Interaction Protocol
|
|
467
|
+
|
|
468
|
+
These are **rules for HOW you interact with the user** during this stage — tone, question shape, decision gating. Ordered steps of *what to do* live in the Checklist; do not treat these as an alternative sequence.
|
|
469
|
+
|
|
397
470
|
${interactionFocus.length > 0 ? interactionFocus.map((item, i) => `${i + 1}. ${item}`).join("\n") : "- Keep communication concise and decision-focused; rely on the Checklist for execution order."}
|
|
398
471
|
|
|
399
472
|
Decision protocol reference: \`${DECISION_PROTOCOL_PATH}\`
|
|
@@ -418,7 +491,7 @@ ${crossStageTraceBlock(artifactRules.crossStageTrace)}
|
|
|
418
491
|
${artifactValidationBlock(artifactRules.artifactValidation)}
|
|
419
492
|
|
|
420
493
|
## Review Lens
|
|
421
|
-
## Outputs
|
|
494
|
+
${reviewLoopSection ? `${reviewLoopSection}\n` : ""}## Outputs
|
|
422
495
|
${reviewLens.outputs.map((item) => `- ${item}`).join("\n")}
|
|
423
496
|
|
|
424
497
|
${reviewSectionsBlock(reviewLens.reviewSections)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FlowStage, FlowTrack, TransitionRule } from "../types.js";
|
|
2
2
|
import type { StageComplexityTier, StageAutoSubagentDispatch, StageSchema } from "./stages/schema-types.js";
|
|
3
|
-
export type { ArtifactValidation, CrossStageTrace, ReviewSection, StageComplexityTier, StageExecutionModel, StagePhilosophy, StageArtifactRules, StageReviewLens, StageAutoSubagentDispatch, StageGate, StageSchemaLegacyInput, StageSchema, StageSchemaInput, StageSchemaV2Input } from "./stages/schema-types.js";
|
|
3
|
+
export type { ArtifactValidation, CrossStageTrace, ReviewSection, StageComplexityTier, StageExecutionModel, StagePhilosophy, StageArtifactRules, StageReviewLoop, StageReviewLens, StageAutoSubagentDispatch, StageGate, StageSchemaLegacyInput, StageSchema, StageSchemaInput, StageSchemaV2Input } from "./stages/schema-types.js";
|
|
4
4
|
export declare const SKILL_ENVELOPE_KINDS: readonly ["stage-output", "gate-result", "delegation-record"];
|
|
5
5
|
export type SkillEnvelopeKind = (typeof SKILL_ENVELOPE_KINDS)[number];
|
|
6
6
|
export interface SkillEnvelope {
|
|
@@ -145,13 +145,23 @@ const REQUIRED_GATE_IDS = {
|
|
|
145
145
|
]
|
|
146
146
|
};
|
|
147
147
|
const REQUIRED_ARTIFACT_SECTIONS = {
|
|
148
|
-
brainstorm: [
|
|
148
|
+
brainstorm: [
|
|
149
|
+
"Context",
|
|
150
|
+
"Problem",
|
|
151
|
+
"Approach Tier",
|
|
152
|
+
"Approaches",
|
|
153
|
+
"Approach Reaction",
|
|
154
|
+
"Selected Direction"
|
|
155
|
+
],
|
|
149
156
|
scope: ["Scope Mode", "In Scope / Out of Scope", "Completion Dashboard", "Scope Summary"],
|
|
150
157
|
design: [
|
|
151
158
|
"Research Fleet Synthesis",
|
|
152
159
|
"Architecture Boundaries",
|
|
153
160
|
"Architecture Diagram",
|
|
154
161
|
"Failure Mode Table",
|
|
162
|
+
"Security & Threat Model",
|
|
163
|
+
"Observability & Debuggability",
|
|
164
|
+
"Deployment & Rollout",
|
|
155
165
|
"Completion Dashboard"
|
|
156
166
|
],
|
|
157
167
|
spec: ["Acceptance Criteria", "Edge Cases", "Testability Map", "Approval"],
|
|
@@ -214,6 +224,8 @@ function normalizeStageSchemaInput(value) {
|
|
|
214
224
|
whenNotToUse: value.philosophy.whenNotToUse,
|
|
215
225
|
interactionProtocol: value.executionModel.interactionProtocol,
|
|
216
226
|
process: value.executionModel.process,
|
|
227
|
+
processFlow: value.executionModel.processFlow,
|
|
228
|
+
platformNotes: value.executionModel.platformNotes,
|
|
217
229
|
requiredGates: value.executionModel.requiredGates,
|
|
218
230
|
requiredEvidence: value.executionModel.requiredEvidence,
|
|
219
231
|
inputs: value.executionModel.inputs,
|
|
@@ -227,6 +239,7 @@ function normalizeStageSchemaInput(value) {
|
|
|
227
239
|
next: value.next,
|
|
228
240
|
checklist: value.executionModel.checklist,
|
|
229
241
|
reviewSections: value.reviewLens.reviewSections,
|
|
242
|
+
reviewLoop: value.reviewLens.reviewLoop,
|
|
230
243
|
completionStatus: value.artifactRules.completionStatus,
|
|
231
244
|
crossStageTrace: value.artifactRules.crossStageTrace,
|
|
232
245
|
artifactValidation: value.artifactRules.artifactValidation,
|
|
@@ -434,6 +447,8 @@ export function stageSchema(stage, track = "standard") {
|
|
|
434
447
|
const executionModel = {
|
|
435
448
|
interactionProtocol: base.interactionProtocol,
|
|
436
449
|
process: base.process,
|
|
450
|
+
processFlow: base.processFlow,
|
|
451
|
+
platformNotes: base.platformNotes,
|
|
437
452
|
checklist: base.checklist,
|
|
438
453
|
requiredGates: tieredGates,
|
|
439
454
|
requiredEvidence: base.requiredEvidence,
|
|
@@ -453,7 +468,8 @@ export function stageSchema(stage, track = "standard") {
|
|
|
453
468
|
const reviewLens = {
|
|
454
469
|
outputs: base.outputs,
|
|
455
470
|
reviewSections: base.reviewSections,
|
|
456
|
-
mandatoryDelegations
|
|
471
|
+
mandatoryDelegations,
|
|
472
|
+
reviewLoop: base.reviewLoop
|
|
457
473
|
};
|
|
458
474
|
return {
|
|
459
475
|
...base,
|
|
@@ -44,6 +44,7 @@ export const BRAINSTORM = {
|
|
|
44
44
|
"**Propose 2-3 architecturally distinct approaches** — with real trade-offs and no recommendation yet. At least one option must be a higher-upside challenger that raises ambition vs the user's initial ask.",
|
|
45
45
|
"**Collect user reaction** — ask which approach feels closest and what concerns remain before stating your recommendation.",
|
|
46
46
|
"**Recommend only after reaction** — present final recommendation with rationale that explicitly references user feedback.",
|
|
47
|
+
"**Plant-seed shelf (optional)** — when a non-selected approach is still promising, capture it as `.cclaw/seeds/SEED-<YYYY-MM-DD>-<slug>.md` with `trigger_when`, hypothesis, and suggested action instead of losing it.",
|
|
47
48
|
"**Present design by sections** — scale each section to its complexity. Ask after each section whether it looks right so far. Cover: architecture, key components, data flow.",
|
|
48
49
|
"**Optional visual companion** — when architecture/data flow complexity is medium+ offer a compact diagram (ASCII or Mermaid) before artifact write-up.",
|
|
49
50
|
"**Write artifact** to `.cclaw/artifacts/01-brainstorm-<slug>.md`.",
|
|
@@ -74,6 +75,7 @@ export const BRAINSTORM = {
|
|
|
74
75
|
"Propose 2-3 architecturally distinct approaches with trade-offs (one must be higher-upside challenger).",
|
|
75
76
|
"Collect user reaction before giving your recommendation.",
|
|
76
77
|
"Recommend after reaction and explain how feedback changed the recommendation.",
|
|
78
|
+
"Optionally plant promising non-selected approaches into `.cclaw/seeds/SEED-<YYYY-MM-DD>-<slug>.md` with trigger_when/action notes.",
|
|
77
79
|
"Present design sections incrementally, get approval after each.",
|
|
78
80
|
"Write approved direction to `.cclaw/artifacts/01-brainstorm-<slug>.md`.",
|
|
79
81
|
"Run document-quality pass to close contradictions and weak trade-off reasoning.",
|
|
@@ -93,6 +95,7 @@ export const BRAINSTORM = {
|
|
|
93
95
|
"2-3 approaches with trade-offs are recorded, including one higher-upside challenger option.",
|
|
94
96
|
"User reaction to approaches is captured before final recommendation.",
|
|
95
97
|
"Final recommendation explicitly reflects user reaction.",
|
|
98
|
+
"When a promising option is parked, a seed file is created under `.cclaw/seeds/` and referenced in the artifact.",
|
|
96
99
|
"Approved direction and approval marker are present.",
|
|
97
100
|
"Assumptions and open questions are captured (or explicitly marked as none)."
|
|
98
101
|
],
|
|
@@ -116,6 +119,11 @@ export const BRAINSTORM = {
|
|
|
116
119
|
"required gates marked satisfied",
|
|
117
120
|
"no implementation action taken",
|
|
118
121
|
"artifact reviewed by user"
|
|
122
|
+
],
|
|
123
|
+
platformNotes: [
|
|
124
|
+
"Write artifact paths in POSIX form (`.cclaw/artifacts/01-brainstorm-<slug>.md`) even on Windows — the runtime normalizes separators. Do NOT commit Windows-style backslashes into the artifact or flow-state.",
|
|
125
|
+
"Slugify titles with lowercase ASCII letters, digits, and single dashes only — avoid spaces and case-sensitive names so the file resolves identically on case-insensitive filesystems (macOS/Windows default).",
|
|
126
|
+
"When linking to files inside the artifact, use repo-relative forward-slash paths (`src/foo/bar.ts`) so reviewers on any OS can click through."
|
|
119
127
|
]
|
|
120
128
|
},
|
|
121
129
|
artifactRules: {
|
|
@@ -130,13 +138,21 @@ export const BRAINSTORM = {
|
|
|
130
138
|
{ section: "Context", required: true, validationRule: "Must reference project state and relevant existing code or patterns." },
|
|
131
139
|
{ section: "Problem", required: true, validationRule: "Must define what we're solving, success criteria, and constraints." },
|
|
132
140
|
{ section: "Clarifying Questions", required: false, validationRule: "Must capture question, answer, and decision impact for each clarifying question." },
|
|
133
|
-
{ section: "Approach Tier", required:
|
|
134
|
-
{ section: "
|
|
135
|
-
{ section: "
|
|
136
|
-
{ section: "
|
|
141
|
+
{ section: "Approach Tier", required: true, validationRule: "Must classify depth as Lightweight/Standard/Deep and explain why." },
|
|
142
|
+
{ section: "Short-Circuit Decision", required: false, validationRule: "Must include Status/Why/Scope handoff lines when short-circuit is discussed." },
|
|
143
|
+
{ section: "Approaches", required: true, validationRule: "Must compare 2-3 architecturally distinct options with real trade-offs and include one row labeled `challenger: higher-upside`." },
|
|
144
|
+
{ section: "Approach Reaction", required: true, validationRule: "Must summarize user reaction before recommendation, including concerns that changed direction." },
|
|
145
|
+
{ section: "Selected Direction", required: true, validationRule: "Must include the selected approach, rationale tied to user reaction/feedback, and explicit approval marker." },
|
|
137
146
|
{ section: "Design", required: false, validationRule: "Must cover architecture, key components, and data flow scaled to complexity." },
|
|
138
147
|
{ section: "Visual Companion", required: false, validationRule: "If architecture/data-flow complexity is medium+, include compact ASCII/Mermaid diagram or explicitly justify omission." },
|
|
139
148
|
{ section: "Assumptions and Open Questions", required: false, validationRule: "Must capture unresolved assumptions/open questions, or explicitly state none." }
|
|
149
|
+
],
|
|
150
|
+
trivialOverrideSections: [
|
|
151
|
+
"Context",
|
|
152
|
+
"Problem",
|
|
153
|
+
"Approach Tier",
|
|
154
|
+
"Short-Circuit Decision",
|
|
155
|
+
"Selected Direction"
|
|
140
156
|
]
|
|
141
157
|
},
|
|
142
158
|
reviewLens: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { REVIEW_LOOP_CHECKLISTS } from "../review-loop.js";
|
|
1
2
|
// ---------------------------------------------------------------------------
|
|
2
3
|
// DESIGN — reference: gstack Eng review
|
|
3
4
|
// ---------------------------------------------------------------------------
|
|
@@ -44,7 +45,7 @@ export const DESIGN = {
|
|
|
44
45
|
"Codebase Investigation — Before any design decision, read the actual code in the blast radius. List every file that will be touched, its current responsibilities, and existing patterns (error handling, naming, test style). Design must conform to discovered patterns, not impose new ones without justification.",
|
|
45
46
|
"Step 0: Scope Challenge — what existing code solves sub-problems? Minimum change set? Complexity check: 8+ files or 2+ new services = complexity smell → flag for possible scope reduction.",
|
|
46
47
|
"Search Before Building — For each technical choice (library, pattern, architecture), search for existing solutions. Label findings: Layer 1 (exact match), Layer 2 (partial match, needs adaptation), Layer 3 (inspiration only), EUREKA (unexpected perfect solution). Default to existing before custom.",
|
|
47
|
-
"Architecture Review — lock component boundaries and one realistic failure scenario per new codepath. **Mandatory diagrams
|
|
48
|
+
"Architecture Review — lock component boundaries and one realistic failure scenario per new codepath. **Mandatory diagrams by tier:** Lightweight=Architecture Diagram, Standard=+Data-Flow Shadow Paths + Error Flow Diagram, Deep=+State Machine Diagram + Rollback Flowchart + Deployment Sequence Diagram.",
|
|
48
49
|
"Security & Threat Model Review — trust boundaries, authn/authz, input validation, secrets handling, data exposure risks, abuse cases, and mitigation ownership.",
|
|
49
50
|
"Code Quality Review — code organization, DRY violations, error handling patterns, over/under-engineering assessment. Include stale-diagram audit for touched files.",
|
|
50
51
|
"Test Review — diagram every new flow, data path, error path. For each: what test type covers it? Does one exist? What is the gap? Produce test plan artifact.",
|
|
@@ -52,7 +53,9 @@ export const DESIGN = {
|
|
|
52
53
|
"Observability & Debuggability Review — logging, metrics, traces, alerts, and on-call diagnosis path for each critical failure mode.",
|
|
53
54
|
"Deployment & Rollout Review — migration sequencing, flag strategy, rollback plan, compatibility window, and post-deploy verification steps.",
|
|
54
55
|
"Parallelization Strategy — If multiple independent modules, produce dependency table: which can be built in parallel? Where are conflict risks? Flag shared-state modules.",
|
|
55
|
-
"Outside Voice + Spec Review Loop — run adversarial second-opinion review, reconcile findings, and iterate up to 3 cycles or until quality score >= 0.8.",
|
|
56
|
+
"Outside Voice + Spec Review Loop — run adversarial second-opinion review, reconcile findings, and iterate up to 3 cycles or until quality score >= 0.8. When `.cclaw/config.yaml::reviewLoop.externalSecondOpinion.enabled` is true, run an additional external-model pass and explicitly resolve score/finding disagreements.",
|
|
57
|
+
"Stale Diagram Audit (opt-in) — when `.cclaw/config.yaml::optInAudits.staleDiagramAudit` is true, compare blast-radius file mtimes against diagram-marker freshness and flag stale diagrams before design lock.",
|
|
58
|
+
"Plant-seed shelf (optional) — when an unresolved/deferred design idea has upside, capture it as `.cclaw/seeds/SEED-<YYYY-MM-DD>-<slug>.md` with trigger_when and action so it can be recalled on future `/cc` starts.",
|
|
56
59
|
"Unresolved Decisions — List any design decisions that could not be resolved in this session. For each: what information is missing? Who can provide it? What is the default if no answer comes?",
|
|
57
60
|
"Distribution Check — If the plan creates new artifact types (packages, CLI tools, configs), document the build/publish story. How does it reach the user?",
|
|
58
61
|
"Deferred Items Cross-Reference — Collect every item explicitly deferred during design review. Each must appear in the Unresolved Decisions table or in the upstream scope artifact's deferred list. No deferred item may exist only in conversation — it must be written down."
|
|
@@ -86,8 +89,9 @@ export const DESIGN = {
|
|
|
86
89
|
"Add security, observability, and deployment reviews for Standard+ changes.",
|
|
87
90
|
"Run stale-diagram audit in touched files and reconcile drift.",
|
|
88
91
|
"Define test coverage strategy and performance budget.",
|
|
89
|
-
"Produce required outputs: NOT-in-scope section, What-already-exists section,
|
|
90
|
-
"
|
|
92
|
+
"Produce required outputs: NOT-in-scope section, What-already-exists section, tier-required diagrams with markers, failure mode table.",
|
|
93
|
+
"Optionally plant unresolved high-upside ideas into `.cclaw/seeds/SEED-<YYYY-MM-DD>-<slug>.md` with trigger_when/action notes.",
|
|
94
|
+
"Run outside-voice spec review loop (up to 3 iterations, quality score target >= 0.8). If configured, include external second opinion and reconcile deltas.",
|
|
91
95
|
"Produce completion dashboard: status per review section, critical/open gap counts, decision count, unresolved items.",
|
|
92
96
|
"Write design lock artifact for downstream spec/plan."
|
|
93
97
|
],
|
|
@@ -102,12 +106,15 @@ export const DESIGN = {
|
|
|
102
106
|
"Research artifact written to `.cclaw/artifacts/02a-research.md` with stack/features/architecture/pitfalls sections plus synthesis.",
|
|
103
107
|
"Artifact written to `.cclaw/artifacts/03-design-<slug>.md`.",
|
|
104
108
|
"Failure-mode table exists in Method/Exception/Rescue/UserSees format.",
|
|
105
|
-
"
|
|
109
|
+
"Tier-required diagram markers are present: architecture (all tiers), +shadow/error (Standard+), +state-machine/rollback/deployment-sequence (Deep).",
|
|
110
|
+
"When `.cclaw/config.yaml::optInAudits.staleDiagramAudit` is true, stale diagram audit finding is clear (no blast-radius file newer than diagram markers without explicit update).",
|
|
106
111
|
"Security & threat model findings are documented with mitigations.",
|
|
107
112
|
"Observability and deployment plans are explicit for critical flows.",
|
|
108
113
|
"Outside-voice findings and dispositions are recorded (accept/reject/defer).",
|
|
109
114
|
"Spec review loop summary includes iteration count and quality score trajectory.",
|
|
115
|
+
"When `.cclaw/config.yaml::reviewLoop.externalSecondOpinion.enabled` is true, external second-opinion disposition is captured.",
|
|
110
116
|
"Test strategy includes unit/integration/e2e expectations.",
|
|
117
|
+
"When a high-upside idea is deferred, a seed file is created under `.cclaw/seeds/` and referenced in the artifact.",
|
|
111
118
|
"NOT-in-scope section produced.",
|
|
112
119
|
"What-already-exists section produced.",
|
|
113
120
|
"Completion dashboard lists review section status, critical/open gap counts, decision count, and unresolved items (or 'None')."
|
|
@@ -135,6 +142,11 @@ export const DESIGN = {
|
|
|
135
142
|
"required gates marked satisfied",
|
|
136
143
|
"completion dashboard present with all review-section statuses",
|
|
137
144
|
"artifact complete for spec handoff"
|
|
145
|
+
],
|
|
146
|
+
platformNotes: [
|
|
147
|
+
"Architecture diagrams (ASCII, Mermaid) must use plain ASCII punctuation — avoid smart quotes and em-dashes that render differently across Windows CMD (cp1252), macOS Terminal (UTF-8), and Linux consoles.",
|
|
148
|
+
"When referencing build or runtime tools in the design, name them by binary (`node`, `python`, `go`) rather than by IDE-specific run configurations (`npm: start (WebStorm)`, `launch.json:Debug`) so the design stays OS-agnostic.",
|
|
149
|
+
"File system layouts drawn in the artifact use forward slashes; explicitly note when a platform-specific path style is required (e.g. Windows long-path `\\\\?\\` prefix, macOS bundle `.app/Contents/MacOS/`)."
|
|
138
150
|
]
|
|
139
151
|
},
|
|
140
152
|
artifactRules: {
|
|
@@ -154,13 +166,23 @@ export const DESIGN = {
|
|
|
154
166
|
{ section: "Codebase Investigation", required: false, validationRule: "Must list blast-radius files with current responsibilities and discovered patterns." },
|
|
155
167
|
{ section: "Search Before Building", required: false, validationRule: "For each technical choice: Layer 1 (exact match), Layer 2 (partial match), Layer 3 (inspiration), EUREKA labels with reuse-first default." },
|
|
156
168
|
{ section: "Architecture Boundaries", required: true, validationRule: "Must list component boundaries with ownership." },
|
|
157
|
-
{ section: "Architecture Diagram", required: true, validationRule: "
|
|
158
|
-
{ section: "Data
|
|
169
|
+
{ section: "Architecture Diagram", required: true, validationRule: "Must include `<!-- diagram: architecture -->` marker. Diagram must label concrete nodes, label arrows, mark direction, distinguish sync/async edges, and include at least one failure/degraded edge." },
|
|
170
|
+
{ section: "Data-Flow Shadow Paths", required: false, validationRule: "Standard/Deep: include `<!-- diagram: data-flow-shadow-paths -->` marker and path table with trigger plus fallback/degrade behavior." },
|
|
171
|
+
{ section: "Error Flow Diagram", required: false, validationRule: "Standard/Deep: include `<!-- diagram: error-flow -->` marker and failure-detection -> rescue -> user-visible outcome flow." },
|
|
172
|
+
{ section: "State Machine Diagram", required: false, validationRule: "Deep: include `<!-- diagram: state-machine -->` marker and state transitions for critical flow lifecycle." },
|
|
173
|
+
{ section: "Rollback Flowchart", required: false, validationRule: "Deep: include `<!-- diagram: rollback-flowchart -->` marker with trigger -> rollback actions -> verification." },
|
|
174
|
+
{ section: "Deployment Sequence Diagram", required: false, validationRule: "Deep: include `<!-- diagram: deployment-sequence -->` marker with rollout order and guard checks." },
|
|
175
|
+
{ section: "Data Flow", required: false, validationRule: "Must include happy path, nil input, empty input, upstream error paths, plus Interaction Edge Case matrix rows for: double-click, nav-away-mid-request, 10K-result dataset, background-job abandonment, zombie connection. Each row must declare handled yes/no and deferred item when not handled." },
|
|
176
|
+
{ section: "Stale Diagram Audit", required: false, validationRule: "When `.cclaw/config.yaml::optInAudits.staleDiagramAudit` is true: blast-radius files from Codebase Investigation must not be newer than the current design diagram-marker baseline unless explicitly refreshed." },
|
|
159
177
|
{ section: "Failure Mode Table", required: true, validationRule: "Use Method/Exception/Rescue/UserSees columns and treat silent user impact without rescue as critical." },
|
|
160
|
-
{ section: "Security & Threat Model", required:
|
|
178
|
+
{ section: "Security & Threat Model", required: true, validationRule: "Must list trust boundaries, abuse/failure scenarios, mitigations, and residual risks." },
|
|
161
179
|
{ section: "Test Strategy", required: false, validationRule: "Must define unit/integration/e2e expectations with coverage targets." },
|
|
162
180
|
{ section: "Performance Budget", required: false, validationRule: "For each critical path: metric name, target threshold, and measurement method." },
|
|
181
|
+
{ section: "Observability & Debuggability", required: true, validationRule: "Must define logs/metrics/traces plus alerting/debug path for critical failure modes." },
|
|
182
|
+
{ section: "Deployment & Rollout", required: true, validationRule: "Must define migration/flag strategy, rollback plan, and post-deploy verification steps." },
|
|
163
183
|
{ section: "What Already Exists", required: false, validationRule: "For each sub-problem: existing code/library found (Layer 1-3/EUREKA label), reuse decision, and adaptation needed." },
|
|
184
|
+
{ section: "Outside Voice Findings", required: false, validationRule: "List adversarial findings and disposition (accept/reject/defer) with rationale per finding." },
|
|
185
|
+
{ section: "Spec Review Loop", required: false, validationRule: "Record iteration table (max 3) with quality score per iteration, stop reason, and unresolved concerns." },
|
|
164
186
|
{ section: "NOT in scope", required: false, validationRule: "Work considered and explicitly deferred with one-line rationale." },
|
|
165
187
|
{ section: "Parallelization Strategy", required: false, validationRule: "If multi-module: dependency table, parallel lanes, conflict flags." },
|
|
166
188
|
{ section: "Unresolved Decisions", required: false, validationRule: "If any: what info is missing, who provides it, default if unanswered." },
|
|
@@ -178,6 +200,12 @@ export const DESIGN = {
|
|
|
178
200
|
"What-already-exists section",
|
|
179
201
|
"design completion dashboard"
|
|
180
202
|
],
|
|
203
|
+
reviewLoop: {
|
|
204
|
+
stage: "design",
|
|
205
|
+
checklist: REVIEW_LOOP_CHECKLISTS.design.map((dimension) => dimension.id),
|
|
206
|
+
maxIterations: 3,
|
|
207
|
+
targetScore: 0.8
|
|
208
|
+
},
|
|
181
209
|
reviewSections: [
|
|
182
210
|
{
|
|
183
211
|
title: "Architecture Review",
|