cclaw-cli 0.47.0 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -174,7 +174,7 @@ inside `/cc-ops` subcommands.
174
174
  |---|---|
175
175
  | **`/cc <idea>`** | Classify the task, discover origin docs (`docs/prd/**`, ADRs, root `PRD.md`, …), sniff the stack, recommend a track, then start the first stage of that track. `/cc` without arguments resumes the current flow. |
176
176
  | **`/cc-next`** | The one progression primitive. Reads `flow-state.json`, checks gates + mandatory subagent delegations, and either resumes the current stage or advances to the next. `/cc-next` in a new session is how you **resume**. |
177
- | **`/cc-ideate`** | Repository improvement discovery. Scans for TODOs, flaky tests, oversized modules, docs drift, and recurring knowledge-store lessons, **persists the ranked backlog** to `.cclaw/artifacts/ideation-<date>-<slug>.md`, and ends with a concrete handoff: launch `/cc` on the selected candidate in the same session, save-and-close, or discard. Resume check on next run reuses any ideation artifact younger than 30 days. Never mutates `flow-state.json`. |
177
+ | **`/cc-ideate`** | Repository improvement ideate mode. Scans for TODOs, flaky tests, oversized modules, docs drift, and recurring knowledge-store lessons, **persists the ranked backlog** to `.cclaw/artifacts/ideate-<date>-<slug>.md`, and ends with a concrete handoff: launch `/cc` on the selected candidate in the same session, save-and-close, or discard. Resume check on next run reuses any ideate artifact younger than 30 days. Never mutates `flow-state.json`. |
178
178
  | **`/cc-view`** | Read-only flow visibility. `/cc-view status` (default) shows stage progress, mandatory delegations with their fulfillment mode (isolated / generic-dispatch / role-switch), the ship closeout substate (retro → compound → archive), and the active harness parity row. `/cc-view tree` renders the same picture as a tree with a closeout sub-branch under ship and a per-harness playbook summary. `/cc-view diff` shows stage/gate/closeout/delegation deltas since the last run. Never mutates state (except diff's snapshot baseline). |
179
179
 
180
180
  > Power-user surface: `/cc-ops` is an operational router for manual
@@ -458,6 +458,8 @@ CCLAW_EVAL_MODEL=glm-5.1 # default
458
458
 
459
459
  Full details, corpus format, and the eval contract live in
460
460
  [`docs/evals.md`](./docs/evals.md).
461
+ Mutation-testing setup lives in `stryker.config.mjs` and
462
+ `.github/workflows/mutation.yml` (manual + weekly run).
461
463
 
462
464
  ---
463
465
 
@@ -58,9 +58,16 @@ export interface ReviewVerdictConsistencyResult {
58
58
  openCriticalCount: number;
59
59
  shipBlockerCount: number;
60
60
  }
61
+ export interface ReviewSecurityNoChangeAttestationResult {
62
+ ok: boolean;
63
+ errors: string[];
64
+ hasSecurityFinding: boolean;
65
+ hasNoChangeAttestation: boolean;
66
+ }
61
67
  /**
62
68
  * Ensure the narrative verdict in 07-review.md is consistent with the
63
69
  * structured review-army reconciliation. A review cannot declare
64
70
  * APPROVED while open Critical findings or shipBlockers remain.
65
71
  */
66
72
  export declare function checkReviewVerdictConsistency(projectRoot: string): Promise<ReviewVerdictConsistencyResult>;
73
+ export declare function checkReviewSecurityNoChangeAttestation(projectRoot: string): Promise<ReviewSecurityNoChangeAttestationResult>;
@@ -1324,3 +1324,46 @@ export async function checkReviewVerdictConsistency(projectRoot) {
1324
1324
  shipBlockerCount
1325
1325
  };
1326
1326
  }
1327
+ export async function checkReviewSecurityNoChangeAttestation(projectRoot) {
1328
+ const reviewMdPath = path.join(projectRoot, RUNTIME_ROOT, "artifacts", "07-review.md");
1329
+ if (!(await exists(reviewMdPath))) {
1330
+ return {
1331
+ ok: true,
1332
+ errors: [],
1333
+ hasSecurityFinding: false,
1334
+ hasNoChangeAttestation: false
1335
+ };
1336
+ }
1337
+ const errors = [];
1338
+ const raw = await fs.readFile(reviewMdPath, "utf8");
1339
+ const sections = extractH2Sections(raw);
1340
+ const securityBody = sectionBodyByName(sections, "Layer 2 Security")
1341
+ ?? sectionBodyByName(sections, "Layer 2b: Security")
1342
+ ?? sectionBodyByName(sections, "Layer 2 Findings");
1343
+ if (!securityBody) {
1344
+ errors.push('07-review.md is missing a Layer 2 security section.');
1345
+ return {
1346
+ ok: false,
1347
+ errors,
1348
+ hasSecurityFinding: false,
1349
+ hasNoChangeAttestation: false
1350
+ };
1351
+ }
1352
+ const securityTableRowPattern = /^\|\s*[^|\n]+\|\s*[^|\n]+\|\s*security\s*\|\s*[^|\n]+\|\s*[^|\n]+\|/imu;
1353
+ const securityBulletPattern = /^[*-]\s+.*\b(?:security|auth|injection|secret|credential|permission)\b/imu;
1354
+ const hasSecurityFinding = securityTableRowPattern.test(securityBody) || securityBulletPattern.test(securityBody);
1355
+ const attestationMatch = /NO_CHANGE_ATTESTATION\s*:\s*(.*)/iu.exec(securityBody);
1356
+ const hasNoChangeAttestation = Boolean(attestationMatch && attestationMatch[1]?.trim().length > 0);
1357
+ if (attestationMatch && attestationMatch[1]?.trim().length === 0) {
1358
+ errors.push("NO_CHANGE_ATTESTATION must include a non-empty rationale.");
1359
+ }
1360
+ if (!hasSecurityFinding && !hasNoChangeAttestation) {
1361
+ errors.push("Layer 2 security evidence missing: include at least one security finding or `NO_CHANGE_ATTESTATION: <reason>`.");
1362
+ }
1363
+ return {
1364
+ ok: errors.length === 0,
1365
+ errors,
1366
+ hasSecurityFinding,
1367
+ hasNoChangeAttestation
1368
+ };
1369
+ }
package/dist/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { FlowTrack, HarnessId, LanguageRulePack, VibyConfig } from "./types.js";
1
+ import type { CclawConfig, FlowTrack, HarnessId, LanguageRulePack } from "./types.js";
2
2
  export declare function configPath(projectRoot: string): string;
3
3
  /**
4
4
  * Default test-path patterns used by workflow-guard.sh to classify TDD writes.
@@ -20,7 +20,7 @@ export declare const DEFAULT_COMPOUND_RECURRENCE_THRESHOLD = 3;
20
20
  * regardless of whether the user wrote `strictness`, the legacy keys, both,
21
21
  * or neither.
22
22
  */
23
- export declare function createDefaultConfig(harnesses?: HarnessId[], defaultTrack?: FlowTrack): VibyConfig;
23
+ export declare function createDefaultConfig(harnesses?: HarnessId[], defaultTrack?: FlowTrack): CclawConfig;
24
24
  /**
25
25
  * Probe common project-root manifests to infer which language rule packs the
26
26
  * user would reasonably want. Pure-functional best-effort: any filesystem
@@ -31,9 +31,9 @@ export declare function createDefaultConfig(harnesses?: HarnessId[], defaultTrac
31
31
  * never surprise a user who intentionally cleared the list.
32
32
  */
33
33
  export declare function detectLanguageRulePacks(projectRoot: string): Promise<LanguageRulePack[]>;
34
- export declare function readConfig(projectRoot: string): Promise<VibyConfig>;
34
+ export declare function readConfig(projectRoot: string): Promise<CclawConfig>;
35
35
  /**
36
- * Fields that live on the populated runtime `VibyConfig` but are considered
36
+ * Fields that live on the populated runtime `CclawConfig` but are considered
37
37
  * "advanced" — we keep them in the in-memory object so downstream callers
38
38
  * don't have to branch, but we do **not** write them to `config.yaml` unless
39
39
  * the user set them explicitly. Keeps the default template small and honest:
@@ -43,7 +43,7 @@ type AdvancedConfigKey = "promptGuardMode" | "tddEnforcement" | "tddTestGlobs" |
43
43
  /**
44
44
  * Options controlling the serialisation shape of `config.yaml`.
45
45
  *
46
- * - `"full"` (default): write every field on the `VibyConfig` object that
46
+ * - `"full"` (default): write every field on the `CclawConfig` object that
47
47
  * isn't `undefined`. Preserves existing shapes and keeps legacy callers
48
48
  * working without migration.
49
49
  * - `"minimal"`: write only the user-facing knobs (`MINIMAL_CONFIG_KEYS`)
@@ -60,7 +60,7 @@ export interface WriteConfigOptions {
60
60
  mode?: "full" | "minimal";
61
61
  advancedKeysPresent?: ReadonlySet<AdvancedConfigKey>;
62
62
  }
63
- export declare function writeConfig(projectRoot: string, config: VibyConfig, options?: WriteConfigOptions): Promise<void>;
63
+ export declare function writeConfig(projectRoot: string, config: CclawConfig, options?: WriteConfigOptions): Promise<void>;
64
64
  /**
65
65
  * Enumerate which advanced keys are currently set in the on-disk config.
66
66
  * Used by `cclaw upgrade` to preserve the user's existing shape — if they
package/dist/config.js CHANGED
@@ -44,6 +44,22 @@ const MINIMAL_CONFIG_KEYS = [
44
44
  ];
45
45
  const DEFAULT_SLICE_REVIEW_THRESHOLD = 5;
46
46
  const DEFAULT_SLICE_REVIEW_TRACKS = ["standard"];
47
+ const emittedConfigWarnings = new Set();
48
+ function emitConfigWarningOnce(code, message) {
49
+ const key = `${code}:${message}`;
50
+ if (emittedConfigWarnings.has(key)) {
51
+ return;
52
+ }
53
+ emittedConfigWarnings.add(key);
54
+ process.emitWarning(message, { code });
55
+ }
56
+ function sameStringArray(a, b) {
57
+ if (!a || !b)
58
+ return false;
59
+ if (a.length !== b.length)
60
+ return false;
61
+ return a.every((value, index) => value === b[index]);
62
+ }
47
63
  function configFixExample() {
48
64
  return `harnesses:
49
65
  - claude
@@ -244,6 +260,12 @@ export async function readConfig(projectRoot) {
244
260
  explicitTddTestPathPatterns = validateStringArray(tddRaw.testPathPatterns, "tdd.testPathPatterns", fullPath);
245
261
  explicitTddProductionPathPatterns = validateStringArray(tddRaw.productionPathPatterns, "tdd.productionPathPatterns", fullPath);
246
262
  }
263
+ if (tddTestGlobsRaw !== undefined &&
264
+ explicitTddTestPathPatterns !== undefined &&
265
+ !sameStringArray(tddTestGlobs, explicitTddTestPathPatterns)) {
266
+ emitConfigWarningOnce("CCLAW_CONFIG_DEPRECATED_TDD_TEST_GLOBS", `[cclaw] Both "tddTestGlobs" (deprecated) and "tdd.testPathPatterns" are set in ${fullPath}. ` +
267
+ `Using "tdd.testPathPatterns".`);
268
+ }
247
269
  const resolvedTddTestPathPatterns = [
248
270
  ...(explicitTddTestPathPatterns ?? tddTestGlobs ?? DEFAULT_TDD_TEST_PATH_PATTERNS)
249
271
  ];
@@ -15,7 +15,16 @@ export declare const EVALS_CONFIG_PATH = ".cclaw/evals/config.yaml";
15
15
  export declare const EVALS_DIRS: readonly [".cclaw/evals", ".cclaw/evals/corpus", ".cclaw/evals/rubrics", ".cclaw/evals/baselines", ".cclaw/evals/reports"];
16
16
  export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/contexts", ".cclaw/templates", ".cclaw/artifacts", ".cclaw/worktrees", ".cclaw/state", ".cclaw/runs", ".cclaw/rules", ".cclaw/adapters", ".cclaw/agents", ".cclaw/hooks", ".cclaw/custom-skills", ".cclaw/evals", ".cclaw/evals/corpus", ".cclaw/evals/rubrics", ".cclaw/evals/baselines", ".cclaw/evals/reports"];
17
17
  export declare const REQUIRED_GITIGNORE_PATTERNS: readonly ["# cclaw generated artifacts", ".cclaw/", "# cclaw evals: user-owned, track in git", "!.cclaw/evals/", "!.cclaw/evals/config.yaml", "!.cclaw/evals/corpus/", "!.cclaw/evals/corpus/**", "!.cclaw/evals/rubrics/", "!.cclaw/evals/rubrics/**", "!.cclaw/evals/baselines/", "!.cclaw/evals/baselines/**", ".claude/commands/cc-*.md", ".claude/commands/cc.md", ".cursor/commands/cc-*.md", ".cursor/commands/cc.md", ".opencode/commands/cc-*.md", ".opencode/commands/cc.md", ".agents/skills/cc/SKILL.md", ".agents/skills/cc-*/SKILL.md", ".claude/hooks/hooks.json", ".cursor/hooks.json", ".codex/hooks.json", ".opencode/plugins/cclaw-plugin.mjs", ".cursor/rules/cclaw-workflow.mdc"];
18
- export declare const COMMAND_FILE_ORDER: FlowStage[];
18
+ /**
19
+ * Canonical stage -> skill folder mapping.
20
+ *
21
+ * Intentional divergence from stage ids:
22
+ * - stage ids stay short and flow-oriented (`spec`, `tdd`, `ship`)
23
+ * - skill folders stay descriptive and user-facing for `.cclaw/skills/*`.
24
+ *
25
+ * Keep this map as the single source of truth for generated skill paths.
26
+ */
27
+ export declare const STAGE_TO_SKILL_FOLDER: Record<FlowStage, string>;
19
28
  export declare const UTILITY_COMMANDS: readonly ["learn", "next", "ideate", "view", "status", "tree", "diff", "ops", "feature", "tdd-log", "retro", "compound", "archive", "rewind"];
20
29
  export declare const SUBAGENT_SKILL_FOLDERS: readonly ["subagent-dev", "parallel-dispatch"];
21
30
  export type UtilityCommand = (typeof UTILITY_COMMANDS)[number];
package/dist/constants.js CHANGED
@@ -103,16 +103,25 @@ export const REQUIRED_GITIGNORE_PATTERNS = [
103
103
  ".opencode/plugins/cclaw-plugin.mjs",
104
104
  ".cursor/rules/cclaw-workflow.mdc"
105
105
  ];
106
- export const COMMAND_FILE_ORDER = [
107
- "brainstorm",
108
- "scope",
109
- "design",
110
- "spec",
111
- "plan",
112
- "tdd",
113
- "review",
114
- "ship"
115
- ];
106
+ /**
107
+ * Canonical stage -> skill folder mapping.
108
+ *
109
+ * Intentional divergence from stage ids:
110
+ * - stage ids stay short and flow-oriented (`spec`, `tdd`, `ship`)
111
+ * - skill folders stay descriptive and user-facing for `.cclaw/skills/*`.
112
+ *
113
+ * Keep this map as the single source of truth for generated skill paths.
114
+ */
115
+ export const STAGE_TO_SKILL_FOLDER = {
116
+ brainstorm: "brainstorming",
117
+ scope: "scope-shaping",
118
+ design: "engineering-design-lock",
119
+ spec: "specification-authoring",
120
+ plan: "planning-and-task-breakdown",
121
+ tdd: "test-driven-development",
122
+ review: "two-layer-review",
123
+ ship: "shipping-and-handoff"
124
+ };
116
125
  export const UTILITY_COMMANDS = [
117
126
  "learn",
118
127
  "next",
@@ -1,2 +1,2 @@
1
1
  import type { FlowStage } from "../types.js";
2
- export declare function commandContract(stage: FlowStage): string;
2
+ export declare function stageCommandContract(stage: FlowStage): string;
@@ -1,6 +1,6 @@
1
1
  import { stageSchema } from "./stage-schema.js";
2
2
  import { stageSkillFolder } from "./skills.js";
3
- export function commandContract(stage) {
3
+ export function stageCommandContract(stage) {
4
4
  const schema = stageSchema(stage);
5
5
  const skillPath = `.cclaw/skills/${stageSkillFolder(stage)}/SKILL.md`;
6
6
  const reads = schema.crossStageTrace.readsFrom;
@@ -1,4 +1,5 @@
1
1
  import { HARNESS_ADAPTERS, harnessTier } from "../harness-adapters.js";
2
+ import { STAGE_TO_SKILL_FOLDER } from "../constants.js";
2
3
  import { HOOK_EVENTS_BY_HARNESS, HOOK_SEMANTIC_EVENTS } from "./hook-events.js";
3
4
  import { HARNESS_PLAYBOOKS_DIR, harnessPlaybookFileName } from "./harness-playbooks.js";
4
5
  import { HARNESS_TOOL_REFS_DIR } from "./harness-tool-refs.js";
@@ -23,6 +24,15 @@ function tierDescription(tier) {
23
24
  }
24
25
  export function harnessIntegrationDocMarkdown() {
25
26
  const harnesses = Object.keys(HARNESS_ADAPTERS);
27
+ const stageSkillRows = Object.entries(STAGE_TO_SKILL_FOLDER)
28
+ .map(([stage, skillFolder]) => `| \`${stage}\` | \`${skillFolder}\` |`)
29
+ .join("\n");
30
+ const hookCasingRows = [
31
+ "| Claude Code | `claude` | PascalCase (`SessionStart`, `PreToolUse`) |",
32
+ "| Cursor | `cursor` | camelCase (`sessionStart`, `preToolUse`) |",
33
+ "| OpenCode | `opencode` | camelCase (`sessionStart`, `preToolUse`) |",
34
+ "| OpenAI Codex | `codex` | PascalCase (`SessionStart`, `PreToolUse`) |"
35
+ ].join("\n");
26
36
  const capabilityRows = harnesses
27
37
  .map((harness) => {
28
38
  const adapter = HARNESS_ADAPTERS[harness];
@@ -75,6 +85,17 @@ Design-stage research fleet uses the same parity model:
75
85
  |---|---|---|---|---|
76
86
  ${hookRows}
77
87
 
88
+ ## Hook event casing
89
+
90
+ Hook keys are intentionally harness-native and must not be normalized:
91
+
92
+ | Harness | ID | Event key casing |
93
+ |---|---|---|
94
+ ${hookCasingRows}
95
+
96
+ Use the exact event names from each harness schema. Treating all hooks as one
97
+ shared casing silently breaks generated wiring.
98
+
78
99
  ## Interpretation
79
100
 
80
101
  - \`tier1\`: full native delegation + structured asks + full hook surface.
@@ -91,7 +112,7 @@ All harnesses receive the same utility commands:
91
112
 
92
113
  - \`/cc\` - flow entry and resume
93
114
  - \`/cc-next\` - stage progression
94
- - \`/cc-ideate\` - discovery mode for ranked repo-improvement backlog
115
+ - \`/cc-ideate\` - ideate mode for ranked repo-improvement backlog
95
116
  - \`/cc-view\` - read-only router for status/tree/diff
96
117
  - \`/cc-ops\` - operations router for feature/tdd-log/retro/compound/archive/rewind
97
118
 
@@ -112,6 +133,16 @@ Operations subcommands:
112
133
  Stage order remains canonical:
113
134
  \`brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship\`
114
135
 
136
+ ## Stage -> skill folder mapping
137
+
138
+ | Stage | Skill folder |
139
+ |---|---|
140
+ ${stageSkillRows}
141
+
142
+ This map is generated from \`src/constants.ts::STAGE_TO_SKILL_FOLDER\` so
143
+ skill-path naming stays explicit and stable even when stage ids differ from
144
+ folder names.
145
+
115
146
  ## Install surfaces
116
147
 
117
148
  Always generated:
@@ -95,9 +95,9 @@ generic dispatcher with a strict role prompt.
95
95
  ## Dispatch pattern
96
96
 
97
97
  1. Pick the mapped \`subagent_type\` from the table above.
98
- 2. Build the \`prompt\` from the cclaw agent contract in
98
+ 2. Build the \`prompt\` from the cclaw agent role brief in
99
99
  \`.cclaw/agents/<agent>.md\`, prefaced with a single line naming the
100
- cclaw role (\`You are the cclaw <agent>. Follow the contract below.\`).
100
+ cclaw role (\`You are the cclaw <agent>. Follow the role brief below.\`).
101
101
  3. Set \`readonly: true\` when the table says yes — Cursor enforces it.
102
102
  4. Before dispatch, append a delegation row:
103
103
 
@@ -142,7 +142,7 @@ description: "OpenCode has plugin-based dispatch hooks and a native structured-a
142
142
  **Fallback: role-switch.** OpenCode exposes tool/session event hooks via a
143
143
  plugin but does not provide an isolated subagent worker. cclaw closes the
144
144
  delegation gate by role-switching inside the same session: the agent
145
- announces the role, performs the work against the contract, and records
145
+ announces the role, performs the work against the role brief, and records
146
146
  evidence.
147
147
 
148
148
  **Structured ask: native \`question\` tool.** OpenCode ships a first-class
@@ -168,7 +168,7 @@ artifact decision log. Full mapping:
168
168
  > Acting as cclaw **<agent>** per \`.cclaw/agents/<agent>.md\`. No other
169
169
  > role may be assumed until the delegation row is closed.
170
170
 
171
- 2. Execute the role's contract. Do NOT interleave other roles' work.
171
+ 2. Execute the role's brief. Do NOT interleave other roles' work.
172
172
  3. Write the result into the stage artifact (e.g. TDD work lands in
173
173
  \`.cclaw/artifacts/06-tdd.md\`).
174
174
  4. Append a delegation row:
@@ -2,13 +2,13 @@ import { RUNTIME_ROOT } from "../constants.js";
2
2
  const IDEATE_SKILL_FOLDER = "flow-ideate";
3
3
  const IDEATE_SKILL_NAME = "flow-ideate";
4
4
  /**
5
- * Directory + filename convention for ideation artifacts. These are separate
5
+ * Directory + filename convention for ideate artifacts. These are separate
6
6
  * from stage artifacts (00-..08-*.md) because `/cc-ideate` runs outside the
7
7
  * critical-path flow state machine and must not collide with stage numbering.
8
8
  */
9
- const IDEATION_ARTIFACT_GLOB = ".cclaw/artifacts/ideation-*.md";
10
- const IDEATION_ARTIFACT_PATTERN = ".cclaw/artifacts/ideation-<YYYY-MM-DD-slug>.md";
11
- const IDEATION_RESUME_WINDOW_DAYS = 30;
9
+ const IDEATE_ARTIFACT_GLOB = ".cclaw/artifacts/ideate-*.md";
10
+ const IDEATE_ARTIFACT_PATTERN = ".cclaw/artifacts/ideate-<YYYY-MM-DD-slug>.md";
11
+ const IDEATE_RESUME_WINDOW_DAYS = 30;
12
12
  /**
13
13
  * Structured-ask tool list reused across cclaw skills. Kept inline here (small
14
14
  * enough) to avoid cross-module coupling; larger stage skills cite the shared
@@ -23,25 +23,25 @@ export function ideateCommandContract() {
23
23
 
24
24
  ## Purpose
25
25
 
26
- Repository-improvement discovery mode. Generate a ranked backlog of
26
+ Repository-improvement ideate mode. Generate a ranked backlog of
27
27
  high-value improvements, persist it as an artifact on disk, and end with
28
28
  an explicit handoff — either launch \`/cc\` on a chosen candidate in the
29
29
  same session, or save/discard the backlog.
30
30
 
31
31
  ## HARD-GATE
32
32
 
33
- - Discovery mode only. Never mutate \`.cclaw/state/flow-state.json\`.
33
+ - Ideate mode only. Never mutate \`.cclaw/state/flow-state.json\`.
34
34
  - Every recommendation cites evidence from the current repository
35
35
  (file path, command output, or knowledge-store entry id).
36
36
  - Always write a persisted artifact to
37
- \`${IDEATION_ARTIFACT_PATTERN}\`. Chat-only output is not acceptable —
37
+ \`${IDEATE_ARTIFACT_PATTERN}\`. Chat-only output is not acceptable —
38
38
  the next session must be able to resume.
39
39
  - Always end with a structured handoff prompt, not an open question.
40
40
 
41
41
  ## Algorithm
42
42
 
43
- 1. **Resume check.** Glob \`${IDEATION_ARTIFACT_GLOB}\`. If any artifact
44
- has been modified within the last ${IDEATION_RESUME_WINDOW_DAYS} days,
43
+ 1. **Resume check.** Glob \`${IDEATE_ARTIFACT_GLOB}\`. If any artifact
44
+ has been modified within the last ${IDEATE_RESUME_WINDOW_DAYS} days,
45
45
  offer the user: continue that backlog, start fresh, or cancel.
46
46
  2. **Scan repo signals:**
47
47
  - open TODO/FIXME/XXX/HACK notes,
@@ -54,7 +54,7 @@ same session, or save/discard the backlog.
54
54
  per candidate.
55
55
  4. **Rank by impact/effort**, recommend the top item.
56
56
  5. **Write the artifact** at
57
- \`${IDEATION_ARTIFACT_PATTERN}\` using the schema in the skill.
57
+ \`${IDEATE_ARTIFACT_PATTERN}\` using the schema in the skill.
58
58
  6. **Present the handoff prompt** with four concrete options — not A/B/C
59
59
  letters. Default = "Start /cc on the top recommendation".
60
60
 
@@ -66,7 +66,7 @@ same session, or save/discard the backlog.
66
66
  export function ideateCommandSkillMarkdown() {
67
67
  return `---
68
68
  name: ${IDEATE_SKILL_NAME}
69
- description: "Repository ideation mode: detect and rank high-leverage improvements, persist a backlog artifact, and hand off to /cc or save/discard."
69
+ description: "Repository ideate mode: detect and rank high-leverage improvements, persist a backlog artifact, and hand off to /cc or save/discard."
70
70
  ---
71
71
 
72
72
  # /cc-ideate
@@ -75,12 +75,12 @@ description: "Repository ideation mode: detect and rank high-leverage improvemen
75
75
 
76
76
  "Using flow-ideate to identify highest-leverage improvements in this
77
77
  repository. Will persist a ranked backlog to
78
- \`${IDEATION_ARTIFACT_PATTERN}\` and end with an explicit handoff."
78
+ \`${IDEATE_ARTIFACT_PATTERN}\` and end with an explicit handoff."
79
79
 
80
80
  ## HARD-GATE
81
81
 
82
82
  - Do not start coding in ideate mode.
83
- - Do not mutate \`.cclaw/state/flow-state.json\` — ideation sits outside
83
+ - Do not mutate \`.cclaw/state/flow-state.json\` — ideate mode sits outside
84
84
  the critical-path flow.
85
85
  - Always produce the artifact file on disk before presenting the handoff.
86
86
  - Always end with a structured handoff that names the concrete follow-up
@@ -91,12 +91,12 @@ repository. Will persist a ranked backlog to
91
91
  ### Phase 0 — Resume check
92
92
 
93
93
  1. Use the harness's file-glob tool (\`Glob\` pattern
94
- \`${IDEATION_ARTIFACT_GLOB}\` or equivalent \`ls\`/\`find\`).
95
- 2. Filter to files modified within the last ${IDEATION_RESUME_WINDOW_DAYS} days.
94
+ \`${IDEATE_ARTIFACT_GLOB}\` or equivalent \`ls\`/\`find\`).
95
+ 2. Filter to files modified within the last ${IDEATE_RESUME_WINDOW_DAYS} days.
96
96
  3. If one or more match, present **one** structured ask using the
97
97
  harness's native tool (${STRUCTURED_ASK_TOOLS}) with options:
98
98
  - **Continue the existing backlog** — read the most-recent
99
- ideation-*.md and work from its candidate list; skip re-scanning.
99
+ ideate-*.md and work from its candidate list; skip re-scanning.
100
100
  - **Start a fresh scan** — proceed to Phase 1; the old artifact stays
101
101
  on disk for history.
102
102
  - **Cancel** — stop; do not scan or write anything.
@@ -137,10 +137,10 @@ Aim for 5–10 candidates. Do not invent candidates without evidence.
137
137
  1. Sort by impact/effort ratio; break ties with confidence.
138
138
  2. Compute the artifact filename:
139
139
  - \`slug\` = first 3–5 words of the top recommendation, lowercase,
140
- non-alphanumeric collapsed to \`-\`, trimmed. When ideation is
140
+ non-alphanumeric collapsed to \`-\`, trimmed. When ideate mode is
141
141
  focus-hinted (user passed an argument), use the focus hint instead.
142
142
  - \`date\` = today in \`YYYY-MM-DD\` (local time).
143
- - Path = \`.cclaw/artifacts/ideation-<date>-<slug>.md\`.
143
+ - Path = \`.cclaw/artifacts/ideate-<date>-<slug>.md\`.
144
144
  3. Use the harness's write-file tool (\`Write\`, \`apply_patch\`, or shell
145
145
  \`cat <<EOF > path\`) to create the artifact with this schema:
146
146
 
@@ -195,7 +195,7 @@ lettered list with the same four labels. Do not invent extra options.
195
195
  - **Start /cc on I-1** or **different candidate:** announce
196
196
  "Handing off to /cc <phrase>" and load the \`using-cclaw\` router
197
197
  skill. From there, the normal \`/cc\` classification and stage flow
198
- takes over. Do not produce a second artifact; the ideation file is
198
+ takes over. Do not produce a second artifact; the ideate file is
199
199
  preserved as the origin document for this run.
200
200
  - **Save and close:** reply with the artifact path and stop.
201
201
  - **Discard:** delete the artifact file, confirm deletion, stop.
@@ -1,4 +1,4 @@
1
- import { RUNTIME_ROOT } from "../constants.js";
1
+ import { RUNTIME_ROOT, STAGE_TO_SKILL_FOLDER } from "../constants.js";
2
2
  import { STAGE_EXAMPLES_REFERENCE_DIR, stageDomainExamples, stageExamples, stageGoodBadExamples } 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";
@@ -295,7 +295,7 @@ After T-3 REFACTOR, before declaring Batch 1 done:
295
295
  - The same RED failure reappears after a GREEN change → **escalate** per the 3-attempts rule; do not keep patching.
296
296
  `;
297
297
  export function stageSkillFolder(stage) {
298
- return stageSchema(stage).skillFolder;
298
+ return STAGE_TO_SKILL_FOLDER[stage];
299
299
  }
300
300
  export function stageSkillMarkdown(stage, track = "standard") {
301
301
  const schema = stageSchema(stage, track);
@@ -1,7 +1,17 @@
1
- import { COMMAND_FILE_ORDER } from "../constants.js";
2
- import { FLOW_TRACKS, TRACK_STAGES } from "../types.js";
1
+ import { FLOW_STAGES, FLOW_TRACKS, TRACK_STAGES } from "../types.js";
2
+ import { STAGE_TO_SKILL_FOLDER } from "../constants.js";
3
3
  import { BRAINSTORM, SCOPE, DESIGN, SPEC, PLAN, TDD, REVIEW, SHIP } from "./stages/index.js";
4
4
  import { tddStageForTrack } from "./stages/tdd.js";
5
+ const ARTIFACT_STAGE_BY_PATH = {
6
+ ".cclaw/artifacts/01-brainstorm.md": "brainstorm",
7
+ ".cclaw/artifacts/02-scope.md": "scope",
8
+ ".cclaw/artifacts/03-design.md": "design",
9
+ ".cclaw/artifacts/04-spec.md": "spec",
10
+ ".cclaw/artifacts/05-plan.md": "plan",
11
+ ".cclaw/artifacts/06-tdd.md": "tdd",
12
+ ".cclaw/artifacts/07-review.md": "review",
13
+ ".cclaw/artifacts/08-ship.md": "ship"
14
+ };
5
15
  const REQUIRED_GATE_IDS = {
6
16
  brainstorm: [
7
17
  "brainstorm_approaches_compared",
@@ -92,6 +102,16 @@ function tieredArtifactValidation(stage, rows) {
92
102
  };
93
103
  });
94
104
  }
105
+ function readsFromForTrack(readsFrom, track) {
106
+ const stageSet = new Set(TRACK_STAGES[track]);
107
+ return readsFrom.filter((artifactPath) => {
108
+ const stage = ARTIFACT_STAGE_BY_PATH[artifactPath];
109
+ if (!stage) {
110
+ return true;
111
+ }
112
+ return stageSet.has(stage);
113
+ });
114
+ }
95
115
  // ---------------------------------------------------------------------------
96
116
  // Stage map and accessors
97
117
  // ---------------------------------------------------------------------------
@@ -199,8 +219,8 @@ const STAGE_AUTO_SUBAGENT_DISPATCH = {
199
219
  },
200
220
  {
201
221
  agent: "reviewer",
202
- mode: "proactive",
203
- when: "When the diff exceeds 100 changed lines, touches more than 10 files, or modifies trust boundaries — dispatch a SECOND, independent reviewer with the adversarial-review skill loaded so the review army has at least two voices on a high-blast-radius change.",
222
+ mode: "mandatory",
223
+ when: "Mandatory when the diff exceeds 100 changed lines, touches more than 10 files, or modifies trust boundaries — dispatch a SECOND, independent reviewer with the adversarial-review skill loaded so the review army has at least two voices on a high-blast-radius change.",
204
224
  purpose: "Adversarial second-opinion review on large or trust-sensitive diffs. The second reviewer treats the implementation as hostile and tries to break it (hostile-user, future-maintainer, competitor lenses) instead of sympathetically explaining it.",
205
225
  requiresUserGate: false,
206
226
  skill: "adversarial-review"
@@ -233,23 +253,29 @@ const STAGE_AUTO_SUBAGENT_DISPATCH = {
233
253
  };
234
254
  /** Transition guard: agents with `mode: "mandatory"` in auto-subagent dispatch for this stage. */
235
255
  export function mandatoryDelegationsForStage(stage) {
236
- return STAGE_AUTO_SUBAGENT_DISPATCH[stage]
237
- .filter((d) => d.mode === "mandatory")
238
- .map((d) => d.agent);
256
+ return [...new Set(STAGE_AUTO_SUBAGENT_DISPATCH[stage]
257
+ .filter((d) => d.mode === "mandatory")
258
+ .map((d) => d.agent))];
239
259
  }
240
260
  export function stageSchema(stage, track = "standard") {
241
261
  const base = stage === "tdd" ? tddStageForTrack(track) : STAGE_SCHEMA_MAP[stage];
242
262
  const tieredGates = tieredStageGates(stage, base.requiredGates, track);
243
263
  const tieredValidation = tieredArtifactValidation(stage, base.artifactValidation);
264
+ const crossStageTrace = {
265
+ ...base.crossStageTrace,
266
+ readsFrom: readsFromForTrack(base.crossStageTrace.readsFrom, track)
267
+ };
244
268
  return {
245
269
  ...base,
270
+ skillFolder: STAGE_TO_SKILL_FOLDER[stage],
271
+ crossStageTrace,
246
272
  requiredGates: tieredGates,
247
273
  artifactValidation: tieredValidation,
248
274
  mandatoryDelegations: mandatoryDelegationsForStage(stage)
249
275
  };
250
276
  }
251
277
  export function orderedStageSchemas(track = "standard") {
252
- return COMMAND_FILE_ORDER.map((stage) => stageSchema(stage, track));
278
+ return FLOW_STAGES.map((stage) => stageSchema(stage, track));
253
279
  }
254
280
  export function stageGateIds(stage, track = "standard") {
255
281
  return stageSchema(stage, track).requiredGates
@@ -10,7 +10,7 @@ export const DESIGN = {
10
10
  ironLaw: "NO DESIGN DECISION WITHOUT A LABELED DIAGRAM, A REJECTED ALTERNATIVE, AND A NAMED FAILURE MODE.",
11
11
  purpose: "Lock architecture, data flow, failure modes, and test/performance expectations through rigorous interactive review.",
12
12
  whenToUse: [
13
- "After scope contract approval",
13
+ "After scope agreement approval",
14
14
  "Before writing final spec and execution plan",
15
15
  "When architecture risks need explicit treatment"
16
16
  ],
@@ -79,7 +79,7 @@ export const DESIGN = {
79
79
  "What-already-exists section produced.",
80
80
  "Completion dashboard lists every review section status, decision count, and unresolved items (or 'None')."
81
81
  ],
82
- inputs: ["scope contract", "system constraints", "non-functional requirements"],
82
+ inputs: ["scope agreement artifact", "system constraints", "non-functional requirements"],
83
83
  requiredContext: [
84
84
  "parallel research synthesis from `.cclaw/artifacts/02a-research.md`",
85
85
  "existing architecture and boundaries",
@@ -201,7 +201,7 @@ export const REVIEW = {
201
201
  },
202
202
  artifactValidation: [
203
203
  { section: "Layer 1 Verdict", required: true, validationRule: "Per-criterion pass/fail with references." },
204
- { section: "Layer 2 Findings", required: false, validationRule: "Each finding has severity, description, and resolution status." },
204
+ { section: "Layer 2 Findings", required: false, validationRule: "Each finding has severity, description, and resolution status. Security coverage must include either explicit security findings or `NO_CHANGE_ATTESTATION: <reason>` when no security-relevant changes were found." },
205
205
  { section: "Review Army Contract", required: true, validationRule: "Structured findings include id/severity/confidence/fingerprint/reportedBy/status with dedup reconciliation summary." },
206
206
  { section: "Review Readiness Dashboard", required: false, validationRule: "Includes a per-pass table (Layer 1 / Layer 2 / Adversarial / Schema) with a 'Completed at' column, a Delegation log snapshot block (path .cclaw/state/delegation-log.json with required/completed/waived/pending), a Staleness signal block (commit at last review pass and current commit), and a Headline with open critical blockers + ship recommendation. At minimum, the section text must contain the substrings 'Completed at', 'delegation-log.json', 'commit at last review pass', and 'Ship recommendation'." },
207
207
  { section: "Completeness Score", required: false, validationRule: "Records AC coverage, task coverage, test-slice coverage, and adversarial-review pass status as numeric or boolean values. At minimum, a line like 'AC coverage: N/M' or 'AC coverage: 100%'." },
@@ -100,6 +100,8 @@ export const SHIP = {
100
100
  "FINALIZE_NO_VCS"
101
101
  ],
102
102
  artifactFile: "08-ship.md",
103
+ // `done` exits the stage pipeline. Archive semantics are handled by the
104
+ // closeout substate machine (`idle` -> ... -> `archived`) in flow-state.
103
105
  next: "done",
104
106
  reviewSections: [
105
107
  {
@@ -63,7 +63,8 @@ export const TDD = {
63
63
  { id: "tdd_green_full_suite", description: "Full relevant suite passes in GREEN state." },
64
64
  { id: "tdd_refactor_completed", description: "Refactor pass completed with behavior preservation verified." },
65
65
  { id: "tdd_verified_before_complete", description: "Fresh verification evidence includes test command, commit SHA, and explicit pass/fail status." },
66
- { id: "tdd_traceable_to_plan", description: "Change traceability to plan slice is explicit." }
66
+ { id: "tdd_traceable_to_plan", description: "Change traceability to plan slice is explicit." },
67
+ { id: "tdd_docs_drift_check", description: "When public API/config/CLI surfaces change, docs drift is addressed via a completed doc-updater pass." }
67
68
  ],
68
69
  requiredEvidence: [
69
70
  "Artifact updated at `.cclaw/artifacts/06-tdd.md` with RED, GREEN, and REFACTOR sections.",
@@ -206,9 +207,12 @@ function tddQuickTrackVariant() {
206
207
  checklist: TDD.checklist.map(quickTrackText),
207
208
  interactionProtocol: TDD.interactionProtocol.map(quickTrackText),
208
209
  process: TDD.process.map(quickTrackText),
209
- requiredGates: TDD.requiredGates.map((gate) => gate.id === "tdd_traceable_to_plan"
210
- ? { ...gate, description: "Change traceability to acceptance criterion is explicit." }
211
- : gate),
210
+ requiredGates: TDD.requiredGates
211
+ .filter((gate) => gate.id !== "tdd_traceable_to_plan")
212
+ .map((gate) => ({
213
+ ...gate,
214
+ description: quickTrackText(gate.description)
215
+ })),
212
216
  requiredEvidence: TDD.requiredEvidence.map(quickTrackText),
213
217
  inputs: TDD.inputs.map(quickTrackText),
214
218
  requiredContext: ["spec artifact", "existing test patterns"],