cclaw-cli 0.48.30 → 0.48.32

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.
@@ -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
+ }
@@ -1,22 +1,20 @@
1
1
  import { RUNTIME_ROOT, STAGE_TO_SKILL_FOLDER } from "../constants.js";
2
- import { STAGE_EXAMPLES_REFERENCE_DIR, stageDomainExamples, stageExamples, stageGoodBadExamples } from "./examples.js";
2
+ import { STAGE_EXAMPLES_REFERENCE_DIR, stageExamples } from "./examples.js";
3
3
  import { STAGE_COMMON_GUIDANCE_REL_PATH } from "./stage-common-guidance.js";
4
4
  import { stageAutoSubagentDispatch, stageSchema } from "./stage-schema.js";
5
5
  const VERIFICATION_STAGES = ["tdd", "review", "ship"];
6
6
  const DECISION_PROTOCOL_PATH = `${RUNTIME_ROOT}/references/protocols/decision.md`;
7
7
  const COMPLETION_PROTOCOL_PATH = `${RUNTIME_ROOT}/references/protocols/completion.md`;
8
- function whenNotToUseBlock(stage, track) {
9
- const schema = stageSchema(stage, track);
10
- if (schema.whenNotToUse.length === 0) {
8
+ function whenNotToUseBlock(items) {
9
+ if (items.length === 0) {
11
10
  return "";
12
11
  }
13
12
  return `## When Not to Use
14
- ${schema.whenNotToUse.map((item) => `- ${item}`).join("\n")}
13
+ ${items.map((item) => `- ${item}`).join("\n")}
15
14
 
16
15
  `;
17
16
  }
18
- function contextLoadingBlock(stage, track) {
19
- const trace = stageSchema(stage, track).crossStageTrace;
17
+ function contextLoadingBlock(trace) {
20
18
  const readLines = trace.readsFrom.length > 0
21
19
  ? trace.readsFrom.map((value) => `- \`${value}\``).join("\n")
22
20
  : "- (first stage — no upstream artifacts)";
@@ -54,8 +52,7 @@ Mandatory delegations for this stage: ${mandatoryList}.
54
52
  Record completion/waiver in \`${delegationLogRel}\` before stage completion.
55
53
  `;
56
54
  }
57
- function researchPlaybooksBlock(stage, track) {
58
- const playbooks = stageSchema(stage, track).researchPlaybooks ?? [];
55
+ function researchPlaybooksBlock(playbooks) {
59
56
  if (playbooks.length === 0)
60
57
  return "";
61
58
  const rows = playbooks
@@ -70,11 +67,10 @@ and record outcomes in the stage artifact when relevant.
70
67
  ${rows}
71
68
  `;
72
69
  }
73
- function reviewSectionsBlock(stage, track) {
74
- const schema = stageSchema(stage, track);
75
- if (schema.reviewSections.length === 0)
70
+ function reviewSectionsBlock(sectionsInput) {
71
+ if (sectionsInput.length === 0)
76
72
  return "";
77
- const sections = schema.reviewSections
73
+ const sections = sectionsInput
78
74
  .map((sec) => {
79
75
  const points = sec.evaluationPoints.map((p) => `- ${p}`).join("\n");
80
76
  const title = sec.stopGate ? `${sec.title} (STOP gate)` : sec.title;
@@ -86,6 +82,18 @@ function reviewSectionsBlock(stage, track) {
86
82
  ${sections}
87
83
  `;
88
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
+ }
89
97
  function verificationBlock(stage) {
90
98
  if (!VERIFICATION_STAGES.includes(stage))
91
99
  return "";
@@ -116,8 +124,7 @@ Detailed walkthrough:
116
124
  \`.cclaw/${STAGE_EXAMPLES_REFERENCE_DIR}/tdd-batch-walkthrough.md\`
117
125
  `;
118
126
  }
119
- function crossStageTraceBlock(stage, track) {
120
- const trace = stageSchema(stage, track).crossStageTrace;
127
+ function crossStageTraceBlock(trace) {
121
128
  const reads = trace.readsFrom.length > 0
122
129
  ? trace.readsFrom.map((r) => `- ${r}`).join("\n")
123
130
  : "- (first stage — no upstream artifacts)";
@@ -135,8 +142,7 @@ ${writes}
135
142
  Rule: ${trace.traceabilityRule}
136
143
  `;
137
144
  }
138
- function artifactValidationBlock(stage, track) {
139
- const validations = stageSchema(stage, track).artifactValidation;
145
+ function artifactValidationBlock(validations) {
140
146
  if (validations.length === 0)
141
147
  return "";
142
148
  const rows = validations
@@ -152,10 +158,10 @@ function artifactValidationBlock(stage, track) {
152
158
  ${rows}
153
159
  `;
154
160
  }
155
- function mergedAntiPatterns(schema) {
161
+ function mergedAntiPatterns(philosophy, execution) {
156
162
  const merged = [];
157
163
  const seen = new Set();
158
- for (const item of [...schema.commonRationalizations, ...schema.blockers]) {
164
+ for (const item of [...philosophy.commonRationalizations, ...execution.blockers]) {
159
165
  const key = item.trim().toLowerCase();
160
166
  if (seen.has(key))
161
167
  continue;
@@ -205,9 +211,9 @@ function stageSpecificSeeAlso(stage) {
205
211
  return refs[stage];
206
212
  }
207
213
  function completionParametersBlock(schema, track) {
208
- const gateList = schema.requiredGates.map((g) => `\`${g.id}\``).join(", ");
209
- const mandatory = schema.mandatoryDelegations.length > 0
210
- ? schema.mandatoryDelegations.map((a) => `\`${a}\``).join(", ")
214
+ const gateList = schema.executionModel.requiredGates.map((g) => `\`${g.id}\``).join(", ");
215
+ const mandatory = schema.reviewLens.mandatoryDelegations.length > 0
216
+ ? schema.reviewLens.mandatoryDelegations.map((a) => `\`${a}\``).join(", ")
211
217
  : "none";
212
218
  const nextStage = schema.next === "done" ? "done" : schema.next;
213
219
  const nextDescription = schema.next === "done"
@@ -218,25 +224,24 @@ function completionParametersBlock(schema, track) {
218
224
  - \`stage\`: \`${schema.stage}\`
219
225
  - \`next\`: \`${nextStage}\` (${nextDescription})
220
226
  - \`gates\`: ${gateList}
221
- - \`artifact\`: \`${RUNTIME_ROOT}/artifacts/${schema.artifactFile}\`
227
+ - \`artifact\`: \`${RUNTIME_ROOT}/artifacts/${schema.artifactRules.artifactFile}\`
222
228
  - \`mandatory delegations\`: ${mandatory}
223
229
  - \`completion helper\`: \`node .cclaw/hooks/stage-complete.mjs ${schema.stage}\`
224
230
  - Fill \`## Learnings\` before closeout: either \`- None this stage.\` or JSON bullets with required keys \`type\`, \`trigger\`, \`action\`, \`confidence\` (knowledge-schema compatible).
225
231
  - Record mandatory delegation completion/waiver in \`${RUNTIME_ROOT}/state/delegation-log.json\` with rationale as needed.
226
232
  - Use the completion helper instead of raw \`flow-state.json\` edits (legacy direct edits trigger workflow-guard warnings or strict-mode blocks).
227
- Apply shared completion logic from:
228
- \`${COMPLETION_PROTOCOL_PATH}\`
233
+ - Completion protocol reference: \`${COMPLETION_PROTOCOL_PATH}\`
229
234
  `;
230
235
  }
231
236
  function quickStartBlock(stage, track) {
232
237
  const schema = stageSchema(stage, track);
233
- const gatePreview = schema.requiredGates.slice(0, 3).map((g) => `\`${g.id}\``).join(", ");
238
+ const gatePreview = schema.executionModel.requiredGates.slice(0, 3).map((g) => `\`${g.id}\``).join(", ");
234
239
  return `## Quick Start
235
240
 
236
- 1. Announce at start: "Using \`${schema.skillName}\` to ${schema.purpose}".
241
+ 1. Announce at start: "Using \`${schema.skillName}\` to ${schema.philosophy.purpose}".
237
242
  2. Obey HARD-GATE and Iron Law.
238
- 3. Execute checklist in order and persist \`${RUNTIME_ROOT}/artifacts/${schema.artifactFile}\`.
239
- 4. Satisfy gates (${gatePreview}${schema.requiredGates.length > 3 ? ` +${schema.requiredGates.length - 3}` : ""}).
243
+ 3. Execute checklist in order and persist \`${RUNTIME_ROOT}/artifacts/${schema.artifactRules.artifactFile}\`.
244
+ 4. Satisfy gates (${gatePreview}${schema.executionModel.requiredGates.length > 3 ? ` +${schema.executionModel.requiredGates.length - 3}` : ""}).
240
245
  `;
241
246
  }
242
247
  /**
@@ -325,21 +330,27 @@ function dedupeGuidance(items, blockedBy) {
325
330
  }
326
331
  export function stageSkillMarkdown(stage, track = "standard") {
327
332
  const schema = stageSchema(stage, track);
328
- const gateList = schema.requiredGates
333
+ const philosophy = schema.philosophy;
334
+ const executionModel = schema.executionModel;
335
+ const artifactRules = schema.artifactRules;
336
+ const reviewLens = schema.reviewLens;
337
+ const mandatoryDelegations = reviewLens.mandatoryDelegations;
338
+ const gateList = executionModel.requiredGates
329
339
  .map((g) => `- \`${g.id}\` — ${g.description}`)
330
340
  .join("\n");
331
- const evidenceList = schema.requiredEvidence
341
+ const evidenceList = executionModel.requiredEvidence
332
342
  .map((e) => `- [ ] ${e}`)
333
343
  .join("\n");
334
- const checklistItems = schema.checklist
344
+ const checklistItems = executionModel.checklist
335
345
  .map((item, i) => `${i + 1}. ${item}`)
336
346
  .join("\n");
337
- const interactionFocus = dedupeGuidance(schema.interactionProtocol, [...schema.checklist, ...schema.process]).slice(0, 5);
338
- const processSummary = dedupeGuidance(schema.process, schema.checklist).slice(0, 5);
339
- const processNote = schema.process.length > processSummary.length
340
- ? `- Follow the Checklist above for remaining execution detail (+${schema.process.length - processSummary.length} condensed step${schema.process.length - processSummary.length === 1 ? "" : "s"}).`
341
- : "";
347
+ const interactionFocus = dedupeGuidance(executionModel.interactionProtocol, [...executionModel.checklist, ...executionModel.process]).slice(0, 5);
348
+ const processSummary = dedupeGuidance(executionModel.process, executionModel.checklist).slice(0, 5);
342
349
  const stageRefs = stageSpecificSeeAlso(stage);
350
+ const reviewLoopSection = reviewLoopBlock(reviewLens.reviewLoop);
351
+ const mandatoryDelegationSummary = mandatoryDelegations.length > 0
352
+ ? mandatoryDelegations.map((name) => `\`${name}\``).join(", ")
353
+ : "none";
343
354
  return `---
344
355
  name: ${schema.skillName}
345
356
  description: "${schema.skillDescription}"
@@ -349,7 +360,7 @@ description: "${schema.skillDescription}"
349
360
 
350
361
  <EXTREMELY-IMPORTANT>
351
362
 
352
- **IRON LAW — ${stage.toUpperCase()}:** ${schema.ironLaw}
363
+ **IRON LAW — ${stage.toUpperCase()}:** ${philosophy.ironLaw}
353
364
 
354
365
  If you are about to violate the Iron Law, STOP. No amount of urgency, partial progress, or clever reinterpretation overrides it. Escalate via the Decision Protocol or abandon the stage.
355
366
 
@@ -357,28 +368,35 @@ If you are about to violate the Iron Law, STOP. No amount of urgency, partial pr
357
368
 
358
369
  ${quickStartBlock(stage, track)}
359
370
 
360
- ## Overview
361
- ${schema.purpose}
371
+ ## Philosophy
372
+ ${philosophy.purpose}
373
+
374
+ ## Complexity Tier
375
+ - Active tier: \`${schema.complexityTier}\`
376
+ - Mandatory delegations at this tier: ${mandatoryDelegationSummary}
362
377
 
363
378
  ## When to Use
364
- ${schema.whenToUse.map((item) => `- ${item}`).join("\n")}
379
+ ${philosophy.whenToUse.map((item) => `- ${item}`).join("\n")}
380
+
381
+ ${whenNotToUseBlock(philosophy.whenNotToUse)}
382
+ ## HARD-GATE
383
+ ${philosophy.hardGate}
384
+
385
+ ## Anti-Patterns & Red Flags
386
+ ${mergedAntiPatterns(philosophy, executionModel)}
387
+
388
+ ## Process
389
+ ${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."}
365
390
 
366
- ${whenNotToUseBlock(stage, track)}
367
391
  ## Inputs
368
- ${schema.inputs.length > 0 ? schema.inputs.map((item) => `- ${item}`).join("\n") : "- (first stage — no required inputs)"}
392
+ ${executionModel.inputs.length > 0 ? executionModel.inputs.map((item) => `- ${item}`).join("\n") : "- (first stage — no required inputs)"}
369
393
 
370
394
  ## Required Context
371
- ${schema.requiredContext.length > 0 ? schema.requiredContext.map((item) => `- ${item}`).join("\n") : "- None beyond this skill"}
395
+ ${executionModel.requiredContext.length > 0 ? executionModel.requiredContext.map((item) => `- ${item}`).join("\n") : "- None beyond this skill"}
372
396
 
373
- ${contextLoadingBlock(stage, track)}
397
+ ${contextLoadingBlock(artifactRules.crossStageTrace)}
374
398
  ${autoSubagentDispatchBlock(stage, track)}
375
- ${researchPlaybooksBlock(stage, track)}
376
-
377
- ## Outputs
378
- ${schema.outputs.map((item) => `- ${item}`).join("\n")}
379
-
380
- ## HARD-GATE
381
- ${schema.hardGate}
399
+ ${researchPlaybooksBlock(executionModel.researchPlaybooks ?? [])}
382
400
 
383
401
  ## Checklist
384
402
 
@@ -386,15 +404,12 @@ You MUST complete these steps in order:
386
404
 
387
405
  ${checklistItems}
388
406
 
389
- ${stageGoodBadExamples(stage)}
390
- ${stageDomainExamples(stage)}
391
407
  ${stageExamples(stage)}
392
408
 
393
409
  ## Interaction Protocol
394
410
  ${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."}
395
411
 
396
- Shared decision/ask-user protocol:
397
- \`${DECISION_PROTOCOL_PATH}\`
412
+ Decision protocol reference: \`${DECISION_PROTOCOL_PATH}\`
398
413
 
399
414
  ${batchExecutionModeBlock(stage, track)}
400
415
  ## Required Gates
@@ -403,22 +418,24 @@ ${gateList}
403
418
  ## Required Evidence
404
419
  ${evidenceList}
405
420
 
406
- ## Process
407
- ${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."}
408
- ${processNote.length > 0 ? `\n${processNote}` : ""}
409
-
410
- ${reviewSectionsBlock(stage, track)}
411
421
  ${verificationBlock(stage)}
412
- ${crossStageTraceBlock(stage, track)}
413
- ${artifactValidationBlock(stage, track)}
414
-
415
- ## Anti-Patterns & Red Flags
416
- ${mergedAntiPatterns(schema)}
417
422
 
418
423
  ## Verification
419
- ${schema.exitCriteria.map((item) => `- [ ] ${item}`).join("\n")}
424
+ ${executionModel.exitCriteria.map((item) => `- [ ] ${item}`).join("\n")}
420
425
 
421
426
  ${completionParametersBlock(schema, track)}
427
+ ## Artifact Rules
428
+ - Artifact target: \`${RUNTIME_ROOT}/artifacts/${artifactRules.artifactFile}\`
429
+
430
+ ${crossStageTraceBlock(artifactRules.crossStageTrace)}
431
+ ${artifactValidationBlock(artifactRules.artifactValidation)}
432
+
433
+ ## Review Lens
434
+ ${reviewLoopSection ? `${reviewLoopSection}\n` : ""}## Outputs
435
+ ${reviewLens.outputs.map((item) => `- ${item}`).join("\n")}
436
+
437
+ ${reviewSectionsBlock(reviewLens.reviewSections)}
438
+
422
439
  ## Shared Stage Guidance
423
440
  See:
424
441
  - \`${STAGE_COMMON_GUIDANCE_REL_PATH}\`
@@ -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"],
@@ -227,6 +237,7 @@ function normalizeStageSchemaInput(value) {
227
237
  next: value.next,
228
238
  checklist: value.executionModel.checklist,
229
239
  reviewSections: value.reviewLens.reviewSections,
240
+ reviewLoop: value.reviewLens.reviewLoop,
230
241
  completionStatus: value.artifactRules.completionStatus,
231
242
  crossStageTrace: value.artifactRules.crossStageTrace,
232
243
  artifactValidation: value.artifactRules.artifactValidation,
@@ -453,7 +464,8 @@ export function stageSchema(stage, track = "standard") {
453
464
  const reviewLens = {
454
465
  outputs: base.outputs,
455
466
  reviewSections: base.reviewSections,
456
- mandatoryDelegations
467
+ mandatoryDelegations,
468
+ reviewLoop: base.reviewLoop
457
469
  };
458
470
  return {
459
471
  ...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
  ],
@@ -130,13 +133,21 @@ export const BRAINSTORM = {
130
133
  { section: "Context", required: true, validationRule: "Must reference project state and relevant existing code or patterns." },
131
134
  { section: "Problem", required: true, validationRule: "Must define what we're solving, success criteria, and constraints." },
132
135
  { 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." },
136
+ { section: "Approach Tier", required: true, validationRule: "Must classify depth as Lightweight/Standard/Deep and explain why." },
137
+ { section: "Short-Circuit Decision", required: false, validationRule: "Must include Status/Why/Scope handoff lines when short-circuit is discussed." },
138
+ { section: "Approaches", required: true, validationRule: "Must compare 2-3 architecturally distinct options with real trade-offs and include one row labeled `challenger: higher-upside`." },
139
+ { section: "Approach Reaction", required: true, validationRule: "Must summarize user reaction before recommendation, including concerns that changed direction." },
140
+ { section: "Selected Direction", required: true, validationRule: "Must include the selected approach, rationale tied to user reaction/feedback, and explicit approval marker." },
137
141
  { section: "Design", required: false, validationRule: "Must cover architecture, key components, and data flow scaled to complexity." },
138
142
  { section: "Visual Companion", required: false, validationRule: "If architecture/data-flow complexity is medium+, include compact ASCII/Mermaid diagram or explicitly justify omission." },
139
143
  { section: "Assumptions and Open Questions", required: false, validationRule: "Must capture unresolved assumptions/open questions, or explicitly state none." }
144
+ ],
145
+ trivialOverrideSections: [
146
+ "Context",
147
+ "Problem",
148
+ "Approach Tier",
149
+ "Short-Circuit Decision",
150
+ "Selected Direction"
140
151
  ]
141
152
  },
142
153
  reviewLens: {