cclaw-cli 7.0.2 → 7.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-linter/plan.js +79 -0
- package/dist/content/meta-skill.js +11 -1
- package/dist/content/skills.d.ts +7 -0
- package/dist/content/skills.js +48 -1
- package/dist/content/stage-schema.js +1 -0
- package/dist/content/stages/plan.js +2 -0
- package/dist/content/stages/review.js +1 -0
- package/package.json +1 -1
|
@@ -5,6 +5,37 @@ import { FORBIDDEN_PLACEHOLDER_TOKENS, CONFIDENCE_FINDING_REGEX_SOURCE } from ".
|
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { PLAN_SPLIT_SMALL_PLAN_THRESHOLD, parseImplementationUnits, parseImplementationUnitParallelFields } from "../internal/plan-split-waves.js";
|
|
8
|
+
const PARALLEL_EXEC_MANAGED_START = "<!-- parallel-exec-managed-start -->";
|
|
9
|
+
const PARALLEL_EXEC_MANAGED_END = "<!-- parallel-exec-managed-end -->";
|
|
10
|
+
const TASK_ID_PATTERN = /\bT-\d{3}[a-z]?(?:\.\d{1,3})?\b/giu;
|
|
11
|
+
/**
|
|
12
|
+
* Extract every distinct T-NNN[a-z]?(.NNN)? id from a markdown body.
|
|
13
|
+
*
|
|
14
|
+
* Used by the `plan_parallel_exec_full_coverage` linter to compute the
|
|
15
|
+
* authored task set (from `## Task List`) vs. the wave-claimed task set
|
|
16
|
+
* (from inside `<!-- parallel-exec-managed-start -->`).
|
|
17
|
+
*/
|
|
18
|
+
function extractTaskIds(body) {
|
|
19
|
+
const ids = new Set();
|
|
20
|
+
for (const match of body.matchAll(TASK_ID_PATTERN)) {
|
|
21
|
+
ids.add(match[0]);
|
|
22
|
+
}
|
|
23
|
+
return ids;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Return the body between the parallel-exec managed comment markers, or
|
|
27
|
+
* an empty string if the block is absent. The TDD wave parser uses the
|
|
28
|
+
* same delimiters; keeping the regex local avoids cross-package import
|
|
29
|
+
* cycles in the linter.
|
|
30
|
+
*/
|
|
31
|
+
function extractParallelExecManagedBody(planMarkdown) {
|
|
32
|
+
const startIdx = planMarkdown.indexOf(PARALLEL_EXEC_MANAGED_START);
|
|
33
|
+
const endIdx = planMarkdown.indexOf(PARALLEL_EXEC_MANAGED_END);
|
|
34
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) {
|
|
35
|
+
return "";
|
|
36
|
+
}
|
|
37
|
+
return planMarkdown.slice(startIdx + PARALLEL_EXEC_MANAGED_START.length, endIdx);
|
|
38
|
+
}
|
|
8
39
|
export async function lintPlanStage(ctx) {
|
|
9
40
|
const { projectRoot, track, raw, absFile, sections, findings, parsedFrontmatter, brainstormShortCircuitBody, brainstormShortCircuitActivated, staleDiagramAuditEnabled, isTrivialOverride } = ctx;
|
|
10
41
|
evaluateInvestigationTrace(ctx, "Implementation Units");
|
|
@@ -277,4 +308,52 @@ export async function lintPlanStage(ctx) {
|
|
|
277
308
|
: "Parallel-ready units detected or plan is single-unit."
|
|
278
309
|
});
|
|
279
310
|
}
|
|
311
|
+
// plan_parallel_exec_full_coverage: every T-NNN task listed in the
|
|
312
|
+
// plan's Task List must be assigned to a slice inside the
|
|
313
|
+
// <!-- parallel-exec-managed-start --> block. Without this, TDD
|
|
314
|
+
// cannot fan out work the plan never authored as waves; the previous
|
|
315
|
+
// failure mode was `stage-complete tdd` succeeding when only the
|
|
316
|
+
// first batch of tasks had been wave-assigned.
|
|
317
|
+
//
|
|
318
|
+
// Spike rows (`S-N`) live in the same Task List but are excluded
|
|
319
|
+
// because they are wall-clock spikes that produce evidence files
|
|
320
|
+
// and are not part of the regular slice fan-out. A task is also
|
|
321
|
+
// excluded when it appears under a `## Deferred Tasks` (or
|
|
322
|
+
// `## Backlog`) heading inside the plan with an explicit reason.
|
|
323
|
+
if (strictPlanGuards) {
|
|
324
|
+
const taskListSection = sectionBodyByName(sections, "Task List") ?? "";
|
|
325
|
+
const authoredTaskIds = extractTaskIds(taskListSection);
|
|
326
|
+
// Collect deferred / backlog task ids so they don't trigger the
|
|
327
|
+
// "uncovered" finding. Both heading variants are accepted.
|
|
328
|
+
const deferredBody = (sectionBodyByName(sections, "Deferred Tasks") ?? "") +
|
|
329
|
+
"\n" +
|
|
330
|
+
(sectionBodyByName(sections, "Backlog") ?? "");
|
|
331
|
+
const deferredIds = extractTaskIds(deferredBody);
|
|
332
|
+
const parallelExecBody = extractParallelExecManagedBody(raw);
|
|
333
|
+
const claimedIds = extractTaskIds(parallelExecBody);
|
|
334
|
+
const uncovered = [];
|
|
335
|
+
for (const id of authoredTaskIds) {
|
|
336
|
+
if (claimedIds.has(id))
|
|
337
|
+
continue;
|
|
338
|
+
if (deferredIds.has(id))
|
|
339
|
+
continue;
|
|
340
|
+
uncovered.push(id);
|
|
341
|
+
}
|
|
342
|
+
uncovered.sort();
|
|
343
|
+
const blockPresent = parallelExecBody.length > 0;
|
|
344
|
+
const taskListPresent = authoredTaskIds.size > 0;
|
|
345
|
+
findings.push({
|
|
346
|
+
section: "plan_parallel_exec_full_coverage",
|
|
347
|
+
required: taskListPresent,
|
|
348
|
+
rule: "Every T-NNN task in `## Task List` must be assigned to at least one slice inside the `<!-- parallel-exec-managed-start -->` block (or moved to an explicit `## Deferred Tasks` / `## Backlog` section). TDD cannot fan out waves the plan never authored.",
|
|
349
|
+
found: taskListPresent && blockPresent && uncovered.length === 0,
|
|
350
|
+
details: !taskListPresent
|
|
351
|
+
? "Task List section is empty or missing T-NNN ids; full-coverage check skipped."
|
|
352
|
+
: !blockPresent
|
|
353
|
+
? "`<!-- parallel-exec-managed-start -->` block is missing or empty. Author the Parallel Execution Plan with W-02..W-N covering every task before plan-final-approval."
|
|
354
|
+
: uncovered.length === 0
|
|
355
|
+
? `Parallel Execution Plan covers all ${authoredTaskIds.size} authored task id(s); ${deferredIds.size} task id(s) are explicitly deferred.`
|
|
356
|
+
: `Uncovered task id(s) — author waves for: ${uncovered.slice(0, 25).join(", ")}${uncovered.length > 25 ? `, … (${uncovered.length - 25} more)` : ""}. Either add slices for them inside <!-- parallel-exec-managed-start --> or move them under \`## Deferred Tasks\` with a reason.`
|
|
357
|
+
});
|
|
358
|
+
}
|
|
280
359
|
}
|
|
@@ -57,6 +57,16 @@ If you think any of these, stop and follow the routing flow:
|
|
|
57
57
|
- "I can answer from memory without loading the active stage skill." -> No. Load the skill first.
|
|
58
58
|
- "Hook guard warned, but I can ignore it." -> No. Resolve the warning before continuing.
|
|
59
59
|
- "I'll edit \`.cclaw/state\` directly to move faster." -> No. Use managed commands only.
|
|
60
|
+
- "I'll just do the worker's job inline so we move faster." -> No. See the Controller dispatch discipline below.
|
|
61
|
+
|
|
62
|
+
## Controller dispatch discipline (applies to every stage)
|
|
63
|
+
|
|
64
|
+
cclaw stages have **mandatory delegations** (TDD: \`slice-builder\`; review: \`reviewer\` + \`security-reviewer\`; design: \`architect\`; scope: \`planner\`; etc.). The controller is the **orchestrator**, not the worker. When a stage declares a mandatory delegation:
|
|
65
|
+
|
|
66
|
+
- **Dispatch via the harness Task tool.** Do NOT write the worker's output (slice code, review findings, architect notes) into the artifact yourself as a substitute for delegating. Editing \`06-tdd.md\` slice cards, \`07-review.md\` findings, or any other "result of mandatory worker" content inline in the controller chat is a protocol violation.
|
|
67
|
+
- **Parallel by default when paths/lenses are independent.** TDD wave-fanout (disjoint \`claimedPaths\`) and review-army (independent reviewer lenses) MUST emit all parallel \`Task\` calls in a SINGLE controller message — not sequentially over multiple turns. The controller waits for all spans to return before reconciling.
|
|
68
|
+
- **Record lifecycle on the same span** via \`delegation-record --status=scheduled|launched|acknowledged|completed\`; the worker emits its own \`--phase=…\` and evidence rows. A \`completed\` row without a matching ACK or dispatch surface is a forgery.
|
|
69
|
+
- **Auto-advance when stage-complete returns ok.** When the helper reports a new \`currentStage\`, immediately load the next stage skill and continue. Announce \`Stage <prev> complete → entering <next>. Continuing.\` Do NOT pause for the user to retype \`/cc\` or say \"продолжай\" — that pause is the failure mode 7.0.2 explicitly removed. The only legitimate stop is a real blocker (missing user input, ambiguous decision, hook fail).
|
|
60
70
|
|
|
61
71
|
## Routing flow
|
|
62
72
|
|
|
@@ -140,7 +150,7 @@ Use session-injected knowledge digest first. Only stream full
|
|
|
140
150
|
|
|
141
151
|
- Do not skip stages silently.
|
|
142
152
|
- Do not claim gate completion without evidence.
|
|
143
|
-
-
|
|
153
|
+
- DO auto-advance to the next stage after \`stage-complete\` returns ok (see Controller dispatch discipline). The user does not need to retype \`/cc\`.
|
|
144
154
|
- Escalate after repeated failures (see decision protocol).
|
|
145
155
|
`;
|
|
146
156
|
}
|
package/dist/content/skills.d.ts
CHANGED
|
@@ -26,6 +26,13 @@ export declare function behaviorAnchorBlock(stage: FlowStage): string;
|
|
|
26
26
|
* Empty for non-TDD stages.
|
|
27
27
|
*/
|
|
28
28
|
export declare function tddTopOfSkillBlock(stage: FlowStage): string;
|
|
29
|
+
/**
|
|
30
|
+
* Review-only prelude: mandates parallel reviewer / security-reviewer dispatch
|
|
31
|
+
* via harness Task and forbids inline authoring of findings.
|
|
32
|
+
*
|
|
33
|
+
* Empty for non-review stages.
|
|
34
|
+
*/
|
|
35
|
+
export declare function reviewTopOfSkillBlock(stage: FlowStage): string;
|
|
29
36
|
export declare function stageSkillFolder(stage: FlowStage): string;
|
|
30
37
|
export declare function stageSkillMarkdown(stage: FlowStage, track?: FlowTrack, _packageVersion?: string | null): string;
|
|
31
38
|
export declare function executingWavesSkillMarkdown(): string;
|
package/dist/content/skills.js
CHANGED
|
@@ -217,6 +217,53 @@ The output names: \`waves[]\` (closed/open), \`nextDispatch.waveId\`, \`nextDisp
|
|
|
217
217
|
|
|
218
218
|
Wave resume: reuse \`wave-status\` outputs and parallelize unfinished members instead of restarting finished slices.
|
|
219
219
|
|
|
220
|
+
---
|
|
221
|
+
`;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Review-only prelude: mandates parallel reviewer / security-reviewer dispatch
|
|
225
|
+
* via harness Task and forbids inline authoring of findings.
|
|
226
|
+
*
|
|
227
|
+
* Empty for non-review stages.
|
|
228
|
+
*/
|
|
229
|
+
export function reviewTopOfSkillBlock(stage) {
|
|
230
|
+
if (stage !== "review")
|
|
231
|
+
return "";
|
|
232
|
+
return `## Review orchestration primer
|
|
233
|
+
|
|
234
|
+
**MANDATE — controller never authors findings inline.** In review the controller orchestrates; \`reviewer\` (functional/spec/correctness/architecture/perf/observability) and \`security-reviewer\` (security sweep + dependency/version audit) are the **mandatory delegated workers** that produce findings, lens coverage, and the verdict input. Typing \`## Layer 1 Findings\`, \`## Layer 2 Findings\`, \`## Lens Coverage\`, or \`## Final Verdict\` content directly into \`07-review.md\` in the controller chat is a protocol violation. The controller writes ONLY the reconciled multi-specialist verdict block AFTER all reviewer Tasks return.
|
|
235
|
+
|
|
236
|
+
**Step 1 — Diff scope (always first):**
|
|
237
|
+
\`git diff --stat <base>...HEAD\` and \`git diff --name-only <base>...HEAD\`.
|
|
238
|
+
If the diff is empty, exit early with APPROVED (no changes to review).
|
|
239
|
+
|
|
240
|
+
**Step 2 — Dispatch the review army in PARALLEL (single controller message):**
|
|
241
|
+
|
|
242
|
+
| Lens | Worker | Mandatory? |
|
|
243
|
+
|--------------------------|-----------------------|------------|
|
|
244
|
+
| Spec compliance / Layer 1 | \`reviewer\` | yes |
|
|
245
|
+
| Layer 2 cross-slice / correctness / observability | \`reviewer\` | yes |
|
|
246
|
+
| Security sweep + dep/version audit | \`security-reviewer\` | yes (or \`NO_SECURITY_IMPACT\` attestation) |
|
|
247
|
+
| Adversarial second opinion | \`reviewer\` (adversarial framing) | only if trust boundaries moved OR diff is large+high-risk |
|
|
248
|
+
|
|
249
|
+
Emit ONE \`Task\` per lens in a single controller message. For each lens:
|
|
250
|
+
|
|
251
|
+
1. Append \`delegation-record --status=scheduled\` for the lens span (one row per lens; reuse the same \`spanId\` for the lens lifecycle).
|
|
252
|
+
2. Append \`delegation-record --status=launched\` immediately after.
|
|
253
|
+
3. Issue the harness Task call: \`Task(subagent_type=<harness reviewer/security-reviewer mapping>, description="<lens> review", prompt="<diff range, files, AC ids, upstream artifacts (spec, design, tdd Per-Slice Reviews), expected output schema for 07-review-army.json>")\`.
|
|
254
|
+
4. The reviewer span ACKs locally and writes its findings/lens coverage to \`07-review-army.json\` (and the structured findings table in \`07-review.md\`) on its own — including \`NO_SECURITY_IMPACT\` rationale if a security pass yields zero findings.
|
|
255
|
+
5. The controller waits for ALL lens spans to return before reconciling.
|
|
256
|
+
|
|
257
|
+
**Step 3 — Reconcile and verdict:** after all lens spans complete:
|
|
258
|
+
|
|
259
|
+
1. Run \`validateReviewArmy\` (helper or linter) on \`07-review-army.json\`.
|
|
260
|
+
2. Dedup by fingerprint, mark multi-specialist confirmations.
|
|
261
|
+
3. Confirm acceptance criteria coverage and Pre-Critic / Lens Coverage / Anti-sycophancy fields are present (linter requires them).
|
|
262
|
+
4. Compute the final verdict: APPROVED, APPROVED_WITH_CONCERNS, or BLOCKED.
|
|
263
|
+
5. If BLOCKED, emit \`ROUTE_BACK_TO_TDD\` with the blocking finding ids and the managed \`npx cclaw-cli internal rewind tdd\` command. Do NOT silently stop.
|
|
264
|
+
|
|
265
|
+
**Step 4 — Auto-advance after stage-complete:** when \`stage-complete review\` returns \`ok\` with a new \`currentStage\` (typically \`ship\`), immediately load the next stage skill and continue. Announce \"Stage review complete → entering <next>. Continuing.\" and proceed without waiting for the user to retype \`/cc\`.
|
|
266
|
+
|
|
220
267
|
---
|
|
221
268
|
`;
|
|
222
269
|
}
|
|
@@ -643,7 +690,7 @@ If you are about to violate the Iron Law, STOP. No amount of urgency, partial pr
|
|
|
643
690
|
|
|
644
691
|
</EXTREMELY-IMPORTANT>
|
|
645
692
|
|
|
646
|
-
${renderTrackTerminology(tddTopOfSkillBlock(stage), trackContext)}${quickStartBlock(stage, track)}
|
|
693
|
+
${renderTrackTerminology(tddTopOfSkillBlock(stage) + reviewTopOfSkillBlock(stage), trackContext)}${quickStartBlock(stage, track)}
|
|
647
694
|
|
|
648
695
|
${STAGE_LANGUAGE_POLICY_POINTER}
|
|
649
696
|
## Philosophy
|
|
@@ -51,6 +51,7 @@ export const PLAN = {
|
|
|
51
51
|
"Run anti-placeholder + anti-scope-reduction scans — block `TODO/TBD/...` and phrasing like `v1`, `for now`, `later` for locked boundaries.",
|
|
52
52
|
"Define validation points — mark where progress must be checked before continuing, with concrete command and expected evidence.",
|
|
53
53
|
"Define execution posture — record whether execution should be sequential, dependency-batched, parallel-safe, or blocked; include risk triggers and RED/GREEN/REFACTOR checkpoint/commit expectations when the repo workflow supports them. This fulfills the `plan_execution_posture_recorded` gate.",
|
|
54
|
+
"**Author the FULL Parallel Execution Plan.** Inside the `<!-- parallel-exec-managed-start -->` block, enumerate ALL waves W-02..W-N covering EVERY T-NNN task in `## Task List` — no `we'll author waves later`, `next batch only`, or open-ended Backlog handwave is acceptable. Each task gets a slice with `sliceId | taskId | dependsOn | claimedPaths | parallelizable | riskTier | lane`. Spike rows (`S-N`) and tasks marked `deferred` in an explicit `Deferred:` column may be omitted, but every other T-NNN must be claimed. This fulfills the `plan_parallel_exec_full_coverage` gate. The TDD stage downstream is a pure consumer of these waves — if the plan does not author them, TDD cannot fan out that work.",
|
|
54
55
|
"WAIT_FOR_CONFIRM — write plan artifact and explicitly pause. **STOP.** Do NOT proceed until user confirms. Then close the stage with `node .cclaw/hooks/stage-complete.mjs plan` and tell user to run `/cc`."
|
|
55
56
|
],
|
|
56
57
|
interactionProtocol: [
|
|
@@ -80,6 +81,7 @@ export const PLAN = {
|
|
|
80
81
|
{ id: "plan_dependency_batches_defined", description: "Tasks are grouped into executable batches with gate checks and execution posture." },
|
|
81
82
|
{ id: "plan_acceptance_mapped", description: "Each task maps to a spec acceptance criterion." },
|
|
82
83
|
{ id: "plan_execution_posture_recorded", description: "Execution posture is recorded before implementation handoff." },
|
|
84
|
+
{ id: "plan_parallel_exec_full_coverage", description: "Every T-NNN task in `## Task List` (other than spikes/explicitly-deferred) is assigned to at least one slice inside the `<!-- parallel-exec-managed-start -->` block; TDD cannot fan out work that the plan never authored as waves." },
|
|
83
85
|
{ id: "plan_wait_for_confirm", description: "Execution blocked until explicit user confirmation." }
|
|
84
86
|
],
|
|
85
87
|
requiredEvidence: [
|
|
@@ -32,6 +32,7 @@ export const REVIEW = {
|
|
|
32
32
|
},
|
|
33
33
|
executionModel: {
|
|
34
34
|
checklist: [
|
|
35
|
+
"**MANDATE — controller never authors review findings.** The controller orchestrates; `reviewer` and `security-reviewer` are mandatory delegated workers that produce the actual findings, lens coverage, and verdict input. **Dispatch them in parallel as harness Task subagents in a single controller message** — one Task per lens. Do NOT type `## Layer 1 Findings`, `## Layer 2 Findings`, `## Lens Coverage`, or `## Final Verdict` content into `07-review.md` yourself as a substitute for delegating. The controller's only writes to the review artifact are: structural scaffolding (section headings if the template did not pre-render them) and the reconciled multi-specialist verdict block AFTER all reviewer Tasks return.",
|
|
35
36
|
"**Boundary with TDD (do NOT re-classify slice findings).** `tdd.Per-Slice Review` OWNS severity-classified findings WITHIN a single slice (correctness, edge cases, regression for that slice). `review` OWNS whole-diff Layer 1 (spec compliance) plus Layer 2 (cross-slice integration findings, security sweep, dependency/version audit, observability). When the same finding ID appears in both `06-tdd.md > Per-Slice Review` and `07-review.md` / `07-review-army.json`, the severity/disposition MUST match — the cross-artifact-duplication linter blocks otherwise.",
|
|
36
37
|
"Diff Scope — Run `git diff` against base branch. If no diff, exit early with APPROVED (no changes to review). Scope the review to changed files unless blast-radius analysis requires wider inspection.",
|
|
37
38
|
"Change-Size Check — ~100 lines = normal. ~300 lines = consider splitting. ~1000+ lines = strongly recommend stacked PRs. Flag large diffs to the user.",
|