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.
Files changed (36) hide show
  1. package/dist/artifact-linter.js +609 -10
  2. package/dist/config.d.ts +1 -1
  3. package/dist/config.js +82 -4
  4. package/dist/content/examples.js +23 -6
  5. package/dist/content/hook-inline-snippets.d.ts +80 -0
  6. package/dist/content/hook-inline-snippets.js +270 -0
  7. package/dist/content/ideate-command.d.ts +6 -2
  8. package/dist/content/ideate-command.js +43 -16
  9. package/dist/content/ideate-frames.d.ts +31 -0
  10. package/dist/content/ideate-frames.js +140 -0
  11. package/dist/content/ideate-ranking.d.ts +25 -0
  12. package/dist/content/ideate-ranking.js +65 -0
  13. package/dist/content/node-hooks.js +9 -197
  14. package/dist/content/review-loop.d.ts +192 -0
  15. package/dist/content/review-loop.js +689 -0
  16. package/dist/content/seed-shelf.d.ts +36 -0
  17. package/dist/content/seed-shelf.js +236 -0
  18. package/dist/content/skills.js +77 -4
  19. package/dist/content/stage-schema.d.ts +1 -1
  20. package/dist/content/stage-schema.js +18 -2
  21. package/dist/content/stages/brainstorm.js +20 -4
  22. package/dist/content/stages/design.js +36 -8
  23. package/dist/content/stages/plan.js +5 -0
  24. package/dist/content/stages/review.js +5 -0
  25. package/dist/content/stages/schema-types.d.ts +29 -0
  26. package/dist/content/stages/scope.js +22 -6
  27. package/dist/content/stages/ship.js +6 -0
  28. package/dist/content/stages/spec.js +6 -0
  29. package/dist/content/stages/tdd.js +6 -0
  30. package/dist/content/start-command.js +24 -18
  31. package/dist/content/templates.js +108 -4
  32. package/dist/internal/advance-stage.js +143 -1
  33. package/dist/trace-matrix.d.ts +14 -0
  34. package/dist/trace-matrix.js +55 -1
  35. package/dist/types.d.ts +27 -0
  36. 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
+ }
@@ -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 processSummary = dedupeGuidance(executionModel.process, executionModel.checklist).slice(0, 5);
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
- ${processSummary.length > 0 ? processSummary.map((item, i) => `${i + 1}. ${item}`).join("\n") : "1. Execute the Checklist in order.\n2. Satisfy every required gate.\n3. Complete verification before stage closeout."}
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: ["Context", "Problem", "Approaches", "Selected Direction"],
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: false, validationRule: "Must classify depth as Lightweight/Standard/Deep and explain why." },
134
- { section: "Approaches", required: true, validationRule: "Must compare 2-3 architecturally distinct options with real trade-offs and include one higher-upside challenger option." },
135
- { section: "Approach Reaction", required: false, validationRule: "Must summarize user reaction before recommendation, including concerns that changed direction." },
136
- { section: "Selected Direction", required: true, validationRule: "Must include the selected approach, rationale tied to user reaction, and explicit approval marker." },
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:** architecture for all tiers; Standard/Deep adds Data-Flow Shadow Paths and Error Flow.",
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, architecture + shadow/error diagrams, failure mode table.",
90
- "Run outside-voice spec review loop (up to 3 iterations, quality score target >= 0.8).",
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
- "Data-flow shadow and error-flow diagrams are present for Standard+ complexity.",
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: "At least one diagram (ASCII, Mermaid, or image) showing component boundaries and data flow direction. Diagram must: (1) label every node with a concrete component name (no generic 'Service A/B'), (2) label every arrow with the action or message (no unlabeled arrows), (3) mark direction of data flow explicitly, (4) distinguish synchronous from asynchronous edges (e.g. solid vs dashed, or `sync:` / `async:` prefix), (5) include at least one failure/degraded edge line that contains an arrow plus a failure keyword (`timeout`, `error`, `fallback`, `degraded`, `retry`, etc.). Standard/Deep complexity must also include `Data-Flow Shadow Paths` and `Error Flow Diagram` sections." },
158
- { section: "Data Flow", required: false, validationRule: "Must include happy path, nil input, empty input, upstream error paths, plus interaction edge-case matrix (double-click, navigate-away, stale-state, large-result, background-job abandonment)." },
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: false, validationRule: "Must list trust boundaries, abuse/failure scenarios, mitigations, and residual risks." },
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",