cclaw-cli 6.10.0 → 6.12.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/dist/artifact-linter/shared.d.ts +7 -4
- package/dist/artifact-linter/shared.js +4 -4
- package/dist/artifact-linter/tdd.d.ts +67 -17
- package/dist/artifact-linter/tdd.js +663 -131
- package/dist/artifact-linter.js +46 -40
- package/dist/content/core-agents.d.ts +14 -0
- package/dist/content/core-agents.js +36 -1
- package/dist/content/examples.js +5 -5
- package/dist/content/hooks.js +68 -3
- package/dist/content/skills.d.ts +10 -0
- package/dist/content/skills.js +60 -2
- package/dist/content/stage-schema.js +13 -7
- package/dist/content/stages/schema-types.d.ts +1 -1
- package/dist/content/stages/tdd.js +21 -20
- package/dist/content/subagents.js +10 -9
- package/dist/content/templates.d.ts +6 -0
- package/dist/content/templates.js +38 -20
- package/dist/delegation.d.ts +27 -0
- package/dist/delegation.js +13 -1
- package/dist/flow-state.d.ts +15 -0
- package/dist/install.js +118 -0
- package/dist/internal/advance-stage.js +2 -6
- package/dist/run-persistence.js +13 -0
- package/package.json +1 -1
- package/dist/tdd-slices.d.ts +0 -90
- package/dist/tdd-slices.js +0 -375
package/dist/artifact-linter.js
CHANGED
|
@@ -5,7 +5,6 @@ import { exists } from "./fs-utils.js";
|
|
|
5
5
|
import { stageSchema } from "./content/stage-schema.js";
|
|
6
6
|
import { readFlowState } from "./run-persistence.js";
|
|
7
7
|
import { duplicateH2Headings, extractEvidencePointers, extractH2Sections, extractRequirementIdsFromMarkdown, isShortCircuitActivated, normalizeHeadingTitle, parseFrontmatter, parseLearningsSection, sectionBodyByAnyName, sectionBodyByHeadingPrefix, sectionBodyByName, validateSectionBody, formatLearningsErrorsBullets } from "./artifact-linter/shared.js";
|
|
8
|
-
import { foldTddSliceLedger, readTddSliceLedger } from "./tdd-slices.js";
|
|
9
8
|
import { shouldDemoteArtifactValidationByTrack } from "./content/stage-schema.js";
|
|
10
9
|
import { readDelegationLedger, recordArtifactValidationDemotedByTrack } from "./delegation.js";
|
|
11
10
|
import { classifyAndPersistFindings } from "./artifact-linter/findings-dedup.js";
|
|
@@ -147,11 +146,12 @@ export async function lintArtifact(projectRoot, stage, track = "standard", optio
|
|
|
147
146
|
}
|
|
148
147
|
}
|
|
149
148
|
const liteTierForValidators = shouldDemoteArtifactValidationByTrack(track, taskClass);
|
|
150
|
-
// v6.
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
149
|
+
// v6.11.0 (D5) — pre-resolve RED/GREEN Evidence pointers AND
|
|
150
|
+
// delegation phase events so `validateSectionBody` (sync) can
|
|
151
|
+
// short-circuit. The Evidence: pointer mode (v6.10.0 T3) stays as a
|
|
152
|
+
// fallback alongside legacy markdown content; phase events with a
|
|
153
|
+
// `phase=red`/`phase=green` row plus non-empty evidenceRefs auto-pass
|
|
154
|
+
// the corresponding markdown validator.
|
|
155
155
|
const tddEvidenceContext = stage === "tdd"
|
|
156
156
|
? await resolveTddEvidencePointerContext({
|
|
157
157
|
projectRoot,
|
|
@@ -435,14 +435,21 @@ const ARTIFACT_VALIDATION_LITE_DEMOTE_SECTIONS = new Set([
|
|
|
435
435
|
"Product Discovery Delegation (Strategist Mode)"
|
|
436
436
|
]);
|
|
437
437
|
/**
|
|
438
|
-
* v6.
|
|
439
|
-
* auto-satisfy state for the TDD stage's RED/GREEN
|
|
440
|
-
* `validateSectionBody` (sync) can short-circuit.
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
438
|
+
* v6.11.0 (D5) — pre-resolve `Evidence:` pointers and delegation
|
|
439
|
+
* phase-event auto-satisfy state for the TDD stage's RED/GREEN
|
|
440
|
+
* Evidence rows so `validateSectionBody` (sync) can short-circuit.
|
|
441
|
+
*
|
|
442
|
+
* - `<path>` pointer is satisfied when the path exists on disk relative
|
|
443
|
+
* to the project root.
|
|
444
|
+
* - `spanId:<id>` pointer is satisfied when any delegation ledger row
|
|
445
|
+
* carries that span id.
|
|
446
|
+
* - Phase-event auto-satisfy fires when `delegation-events.jsonl`
|
|
447
|
+
* carries at least one slice-tagged event for the active run with
|
|
448
|
+
* `phase=red`/`phase=green` and non-empty `evidenceRefs`. This is the
|
|
449
|
+
* v6.11.0 replacement for the v6.10.0 sidecar auto-satisfy hook —
|
|
450
|
+
* slice events are now the source of truth, the RED/GREEN markdown
|
|
451
|
+
* tables are auto-rendered from them, and the validators MUST NOT
|
|
452
|
+
* demand pasted stdout when the events already prove RED/GREEN.
|
|
446
453
|
*/
|
|
447
454
|
async function resolveTddEvidencePointerContext(input) {
|
|
448
455
|
const { projectRoot, sections } = input;
|
|
@@ -451,16 +458,29 @@ async function resolveTddEvidencePointerContext(input) {
|
|
|
451
458
|
const redPointers = extractEvidencePointers(redSection);
|
|
452
459
|
const greenPointers = extractEvidencePointers(greenSection);
|
|
453
460
|
let knownSpanIds = new Set();
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
461
|
+
let phaseEventsAutoSatisfy = { red: false, green: false };
|
|
462
|
+
try {
|
|
463
|
+
const ledger = await readDelegationLedger(projectRoot);
|
|
464
|
+
knownSpanIds = new Set(ledger.entries
|
|
465
|
+
.map((entry) => entry.spanId)
|
|
466
|
+
.filter((id) => typeof id === "string" && id.length > 0));
|
|
467
|
+
const runId = ledger.runId;
|
|
468
|
+
const slicePhaseRows = ledger.entries.filter((entry) => entry.runId === runId &&
|
|
469
|
+
entry.stage === "tdd" &&
|
|
470
|
+
typeof entry.sliceId === "string" &&
|
|
471
|
+
entry.sliceId.length > 0 &&
|
|
472
|
+
typeof entry.phase === "string");
|
|
473
|
+
const redOk = slicePhaseRows.some((entry) => entry.phase === "red" &&
|
|
474
|
+
Array.isArray(entry.evidenceRefs) &&
|
|
475
|
+
entry.evidenceRefs.some((ref) => typeof ref === "string" && ref.trim().length > 0));
|
|
476
|
+
const greenOk = slicePhaseRows.some((entry) => entry.phase === "green" &&
|
|
477
|
+
Array.isArray(entry.evidenceRefs) &&
|
|
478
|
+
entry.evidenceRefs.some((ref) => typeof ref === "string" && ref.trim().length > 0));
|
|
479
|
+
phaseEventsAutoSatisfy = { red: redOk, green: greenOk };
|
|
480
|
+
}
|
|
481
|
+
catch {
|
|
482
|
+
knownSpanIds = new Set();
|
|
483
|
+
phaseEventsAutoSatisfy = { red: false, green: false };
|
|
464
484
|
}
|
|
465
485
|
async function pointerResolves(value) {
|
|
466
486
|
const trimmed = value.replace(/[`*_]/gu, "").trim();
|
|
@@ -480,28 +500,14 @@ async function resolveTddEvidencePointerContext(input) {
|
|
|
480
500
|
}
|
|
481
501
|
return false;
|
|
482
502
|
}
|
|
483
|
-
let redSidecarAutoSatisfy = false;
|
|
484
|
-
let greenSidecarAutoSatisfy = false;
|
|
485
|
-
try {
|
|
486
|
-
const sidecar = await readTddSliceLedger(projectRoot);
|
|
487
|
-
if (sidecar.entries.length > 0) {
|
|
488
|
-
const folded = foldTddSliceLedger(sidecar.entries);
|
|
489
|
-
redSidecarAutoSatisfy = folded.some((entry) => typeof entry.redOutputRef === "string" && entry.redOutputRef.length > 0);
|
|
490
|
-
greenSidecarAutoSatisfy = folded.some((entry) => typeof entry.greenOutputRef === "string" && entry.greenOutputRef.length > 0);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
catch {
|
|
494
|
-
redSidecarAutoSatisfy = false;
|
|
495
|
-
greenSidecarAutoSatisfy = false;
|
|
496
|
-
}
|
|
497
503
|
return {
|
|
498
504
|
red: {
|
|
499
505
|
pointerSatisfied: await anyResolved(redPointers),
|
|
500
|
-
|
|
506
|
+
phaseEventsSatisfied: phaseEventsAutoSatisfy.red
|
|
501
507
|
},
|
|
502
508
|
green: {
|
|
503
509
|
pointerSatisfied: await anyResolved(greenPointers),
|
|
504
|
-
|
|
510
|
+
phaseEventsSatisfied: phaseEventsAutoSatisfy.green
|
|
505
511
|
}
|
|
506
512
|
};
|
|
507
513
|
}
|
|
@@ -207,6 +207,20 @@ export declare const CCLAW_AGENTS: readonly [{
|
|
|
207
207
|
readonly relatedStages: ["tdd"];
|
|
208
208
|
readonly returnSchema: AgentReturnSchema;
|
|
209
209
|
readonly body: string;
|
|
210
|
+
}, {
|
|
211
|
+
readonly name: "slice-documenter";
|
|
212
|
+
readonly description: "MANDATORY in PARALLEL with slice-implementer for every TDD slice (regardless of discoveryMode, v6.12.0 Phase R). Writes per-slice prose summary to `<artifacts-dir>/tdd-slices/S-<id>.md`. Does NOT implement, does NOT write tests. Linter rule `tdd_slice_documenter_missing` blocks the gate when a `phase=doc` event is missing for a green slice.";
|
|
213
|
+
readonly tools: ["Read", "Write", "Edit", "Grep", "Glob"];
|
|
214
|
+
readonly model: "fast";
|
|
215
|
+
readonly activation: "mandatory";
|
|
216
|
+
readonly relatedStages: ["tdd"];
|
|
217
|
+
readonly returnSchema: {
|
|
218
|
+
readonly statusField: "status";
|
|
219
|
+
readonly allowedStatuses: ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"];
|
|
220
|
+
readonly requiredFields: ["status", "summaryMd", "learnings", "evidenceRefs", "blockers"];
|
|
221
|
+
readonly evidenceFields: ["summaryMd", "evidenceRefs"];
|
|
222
|
+
};
|
|
223
|
+
readonly body: string;
|
|
210
224
|
}, {
|
|
211
225
|
readonly name: "fixer";
|
|
212
226
|
readonly description: "ON-DEMAND fresh worker after review FAIL/PARTIAL evidence. Must fix only the cited criterion within explicit allowed files.";
|
|
@@ -544,6 +544,41 @@ export const CCLAW_AGENTS = [
|
|
|
544
544
|
"**Role boundary:** do not broaden scope, do not review your own work as final approval, and do not spawn subagents."
|
|
545
545
|
].join("\n")
|
|
546
546
|
},
|
|
547
|
+
{
|
|
548
|
+
name: "slice-documenter",
|
|
549
|
+
description: "MANDATORY in PARALLEL with slice-implementer for every TDD slice (regardless of discoveryMode, v6.12.0 Phase R). Writes per-slice prose summary to `<artifacts-dir>/tdd-slices/S-<id>.md`. Does NOT implement, does NOT write tests. Linter rule `tdd_slice_documenter_missing` blocks the gate when a `phase=doc` event is missing for a green slice.",
|
|
550
|
+
tools: ["Read", "Write", "Edit", "Grep", "Glob"],
|
|
551
|
+
model: "fast",
|
|
552
|
+
activation: "mandatory",
|
|
553
|
+
relatedStages: ["tdd"],
|
|
554
|
+
returnSchema: {
|
|
555
|
+
statusField: "status",
|
|
556
|
+
allowedStatuses: ["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"],
|
|
557
|
+
requiredFields: ["status", "summaryMd", "learnings", "evidenceRefs", "blockers"],
|
|
558
|
+
evidenceFields: ["summaryMd", "evidenceRefs"]
|
|
559
|
+
},
|
|
560
|
+
body: [
|
|
561
|
+
"You are a **slice-documenter** dispatched in PARALLEL with `slice-implementer` for the same slice.",
|
|
562
|
+
"",
|
|
563
|
+
"**Mission:** capture per-slice prose summary while production code is being written.",
|
|
564
|
+
"Because your only `claimedPath` is `<artifacts-dir>/tdd-slices/S-<id>.md` and the implementer's `claimedPaths` are production code, the file-overlap scheduler auto-allows the parallel dispatch.",
|
|
565
|
+
"",
|
|
566
|
+
"When invoked:",
|
|
567
|
+
"1. Read the active plan unit, acceptance criterion, and the failing RED test for this slice.",
|
|
568
|
+
"2. Write a thin per-slice file at `<artifacts-dir>/tdd-slices/S-<id>.md` with the headings:",
|
|
569
|
+
" - `# Slice S-<id>`",
|
|
570
|
+
" - `## Plan unit` (T-... pointer)",
|
|
571
|
+
" - `## Acceptance criteria` (AC-... ids)",
|
|
572
|
+
" - `## Why this slice`",
|
|
573
|
+
" - `## What was tested`",
|
|
574
|
+
" - `## What was implemented`",
|
|
575
|
+
" - `## REFACTOR notes`",
|
|
576
|
+
" - `## Learnings`",
|
|
577
|
+
"3. Return JSON: `{ status, summaryMd, learnings: string[], evidenceRefs: [\"<artifacts-dir>/tdd-slices/S-<id>.md\"], blockers: [] }`.",
|
|
578
|
+
"",
|
|
579
|
+
"**Forbidden:** edit `06-tdd.md`, test files, or production code. Edit ONLY your slice file."
|
|
580
|
+
].join("\n")
|
|
581
|
+
},
|
|
547
582
|
{
|
|
548
583
|
name: "fixer",
|
|
549
584
|
description: "ON-DEMAND fresh worker after review FAIL/PARTIAL evidence. Must fix only the cited criterion within explicit allowed files.",
|
|
@@ -694,7 +729,7 @@ ${(() => {
|
|
|
694
729
|
const mode = activationModeSummary();
|
|
695
730
|
return `- **Mandatory:** ${mode.mandatory}.
|
|
696
731
|
- **Proactive:** ${mode.proactive}.
|
|
697
|
-
- **On-demand:**
|
|
732
|
+
- **On-demand:** fixer. Research playbooks are in-thread procedures.`;
|
|
698
733
|
})()}
|
|
699
734
|
|
|
700
735
|
### Cost-aware routing
|
package/dist/content/examples.js
CHANGED
|
@@ -36,10 +36,10 @@ export const BEHAVIOR_ANCHORS = [
|
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
stage: "tdd",
|
|
39
|
-
section: "
|
|
40
|
-
bad: "
|
|
41
|
-
good: "
|
|
42
|
-
ruleHint: "RED
|
|
39
|
+
section: "Vertical Slice Cycle",
|
|
40
|
+
bad: "Controller writes the failing test, the GREEN fix, AND per-slice prose into `06-tdd.md` itself, then hand-edits Watched-RED / Vertical Slice Cycle tables. `phase=red`/`green`/`doc` events missing; `tdd_slice_implementer_missing` and `tdd_slice_documenter_missing` block the gate.",
|
|
41
|
+
good: "Per slice: (1) `Task(\"test-author --slice S-1 --phase red\")`. Verify the event. (2) ONE message, TWO Tasks — `slice-implementer --phase green` AND `slice-documenter --phase doc`. (3) `slice-implementer --phase refactor` (or `refactor-deferred`). Linter auto-renders Vertical Slice Cycle from events.",
|
|
42
|
+
ruleHint: "Per-Slice Ritual (v6.12.0+): RED → verify → GREEN+DOC fan-out (one message, two Tasks) → REFACTOR. Controller never writes GREEN code or per-slice prose. Mandatory regardless of `discoveryMode`."
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
stage: "review",
|
|
@@ -252,7 +252,7 @@ Plan is ready to execute after user confirmation.
|
|
|
252
252
|
| Slice | Source ID | AC ID | Expected behavior | RED-link |
|
|
253
253
|
| --- | --- | --- | --- | --- |
|
|
254
254
|
| S-1 | SRC-1 | AC-1 | feed window honors 30d cap | spanId:tdd-feed-window-red |
|
|
255
|
-
| S-2 | SRC-2 | AC-3 | degraded banner appears on disconnect |
|
|
255
|
+
| S-2 | SRC-2 | AC-3 | degraded banner appears on disconnect | spanId:tdd-banner-red (auto-derived from delegation-events.jsonl) |
|
|
256
256
|
|
|
257
257
|
## GREEN
|
|
258
258
|
|
package/dist/content/hooks.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { RUNTIME_ROOT } from "../constants.js";
|
|
5
|
-
import { DELEGATION_DISPATCH_SURFACES, DELEGATION_DISPATCH_SURFACE_PATH_PREFIXES } from "../delegation.js";
|
|
5
|
+
import { DELEGATION_DISPATCH_SURFACES, DELEGATION_DISPATCH_SURFACE_PATH_PREFIXES, DELEGATION_PHASES } from "../delegation.js";
|
|
6
6
|
function resolveCliRuntimeForGeneratedHook() {
|
|
7
7
|
const here = fileURLToPath(import.meta.url);
|
|
8
8
|
// Vitest runs init/sync from src/ and expects helpers to execute the same
|
|
@@ -210,6 +210,8 @@ const TERMINAL = new Set(["completed", "failed", "waived", "stale"]);
|
|
|
210
210
|
const VALID_DISPATCH_SURFACES = ${JSON.stringify([...DELEGATION_DISPATCH_SURFACES])};
|
|
211
211
|
const VALID_DISPATCH_SURFACES_SET = new Set(VALID_DISPATCH_SURFACES);
|
|
212
212
|
const SURFACE_PATH_PREFIXES = ${JSON.stringify(DELEGATION_DISPATCH_SURFACE_PATH_PREFIXES)};
|
|
213
|
+
const VALID_DELEGATION_PHASES = ${JSON.stringify([...DELEGATION_PHASES])};
|
|
214
|
+
const VALID_DELEGATION_PHASES_SET = new Set(VALID_DELEGATION_PHASES);
|
|
213
215
|
const LEDGER_SCHEMA_VERSION = 3;
|
|
214
216
|
const FLOW_STATE_GUARD_REL_PATH = RUNTIME_ROOT + "/.flow-state.guard.json";
|
|
215
217
|
|
|
@@ -343,6 +345,11 @@ function usage() {
|
|
|
343
345
|
"TDD parallel scheduler (v6.10.0):",
|
|
344
346
|
" --paths=<a,b,c> repo-relative paths the slice-implementer will edit; disjoint sets auto-promote to allowParallel, overlap throws DispatchOverlapError",
|
|
345
347
|
" --override-cap=<int> raise the slice-implementer fan-out cap once for this dispatch (default cap " + String(5) + ", env CCLAW_MAX_PARALLEL_SLICE_IMPLEMENTERS overrides globally)",
|
|
348
|
+
"",
|
|
349
|
+
"TDD slice phase tagging (v6.11.0):",
|
|
350
|
+
" --slice=<id> TDD slice identifier (e.g. S-1) used by the linter to auto-derive the Watched-RED + Vertical Slice Cycle tables.",
|
|
351
|
+
" --phase=<phase> one of " + VALID_DELEGATION_PHASES.join(", ") + ". Pair with --slice to record a TDD slice phase event.",
|
|
352
|
+
" --refactor-rationale=<t> required when --phase=refactor-deferred unless --evidence-ref carries the rationale text.",
|
|
346
353
|
""
|
|
347
354
|
].join("\\n") + "\\n");
|
|
348
355
|
}
|
|
@@ -451,6 +458,32 @@ function buildRow(args, status, runId, now, options) {
|
|
|
451
458
|
.split(",")
|
|
452
459
|
.map((value) => value.trim())
|
|
453
460
|
.filter((value) => value.length > 0);
|
|
461
|
+
// v6.11.0 (D1+D2): TDD slice tagging via --slice / --phase. Phase
|
|
462
|
+
// must be one of the canonical enum values; the inline validator
|
|
463
|
+
// rejects unknown phases before the row hits the ledger.
|
|
464
|
+
const sliceId =
|
|
465
|
+
typeof args.slice === "string" && args.slice.trim().length > 0
|
|
466
|
+
? args.slice.trim()
|
|
467
|
+
: undefined;
|
|
468
|
+
const phase =
|
|
469
|
+
typeof args.phase === "string" && args.phase.trim().length > 0
|
|
470
|
+
? args.phase.trim()
|
|
471
|
+
: undefined;
|
|
472
|
+
// v6.11.0 (D2): when --refactor-rationale is supplied it is folded
|
|
473
|
+
// into evidenceRefs[0] so the linter (which reads evidenceRefs only)
|
|
474
|
+
// can surface the rationale without touching new fields. The user
|
|
475
|
+
// may also pass --evidence-ref containing the rationale text.
|
|
476
|
+
let resolvedEvidenceRefs = normalizeEvidenceRefs(args);
|
|
477
|
+
if (
|
|
478
|
+
phase === "refactor-deferred" &&
|
|
479
|
+
typeof args["refactor-rationale"] === "string" &&
|
|
480
|
+
args["refactor-rationale"].trim().length > 0
|
|
481
|
+
) {
|
|
482
|
+
const rationale = args["refactor-rationale"].trim();
|
|
483
|
+
if (!resolvedEvidenceRefs.includes(rationale)) {
|
|
484
|
+
resolvedEvidenceRefs = [rationale, ...resolvedEvidenceRefs];
|
|
485
|
+
}
|
|
486
|
+
}
|
|
454
487
|
return {
|
|
455
488
|
stage: args.stage,
|
|
456
489
|
agent: args.agent,
|
|
@@ -463,7 +496,7 @@ function buildRow(args, status, runId, now, options) {
|
|
|
463
496
|
agentDefinitionPath: args["agent-definition-path"],
|
|
464
497
|
fulfillmentMode,
|
|
465
498
|
waiverReason: args["waiver-reason"],
|
|
466
|
-
evidenceRefs:
|
|
499
|
+
evidenceRefs: resolvedEvidenceRefs,
|
|
467
500
|
runId,
|
|
468
501
|
startTs,
|
|
469
502
|
ts: now,
|
|
@@ -473,7 +506,9 @@ function buildRow(args, status, runId, now, options) {
|
|
|
473
506
|
endTs: TERMINAL.has(status) ? now : undefined,
|
|
474
507
|
schemaVersion: LEDGER_SCHEMA_VERSION,
|
|
475
508
|
allowParallel: args["allow-parallel"] === true ? true : undefined,
|
|
476
|
-
claimedPaths: claimedPaths.length > 0 ? claimedPaths : undefined
|
|
509
|
+
claimedPaths: claimedPaths.length > 0 ? claimedPaths : undefined,
|
|
510
|
+
sliceId,
|
|
511
|
+
phase
|
|
477
512
|
};
|
|
478
513
|
}
|
|
479
514
|
|
|
@@ -1014,6 +1049,36 @@ async function main() {
|
|
|
1014
1049
|
return;
|
|
1015
1050
|
}
|
|
1016
1051
|
|
|
1052
|
+
// v6.11.0 (D2) — TDD slice phase tagging validation. --phase is
|
|
1053
|
+
// strictly enum-bound; --slice must be a non-empty string when
|
|
1054
|
+
// provided; --phase=refactor-deferred requires either an explicit
|
|
1055
|
+
// --refactor-rationale or an --evidence-ref with rationale text so
|
|
1056
|
+
// the linter has something to render.
|
|
1057
|
+
if (args.phase !== undefined && !VALID_DELEGATION_PHASES_SET.has(args.phase)) {
|
|
1058
|
+
problems.push("invalid --phase (allowed: " + VALID_DELEGATION_PHASES.join(", ") + ")");
|
|
1059
|
+
emitProblems(problems, json, 2);
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
if (args.slice !== undefined && (typeof args.slice !== "string" || args.slice.trim().length === 0)) {
|
|
1063
|
+
problems.push("--slice requires a non-empty value");
|
|
1064
|
+
emitProblems(problems, json, 2);
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
if (args.phase === "refactor-deferred") {
|
|
1068
|
+
const rationaleProvided =
|
|
1069
|
+
typeof args["refactor-rationale"] === "string" && args["refactor-rationale"].trim().length > 0;
|
|
1070
|
+
const evidenceProvided =
|
|
1071
|
+
(typeof args["evidence-ref"] === "string" && args["evidence-ref"].trim().length > 0) ||
|
|
1072
|
+
(Array.isArray(args["evidence-refs"]) && args["evidence-refs"].some(
|
|
1073
|
+
(ref) => typeof ref === "string" && ref.trim().length > 0
|
|
1074
|
+
));
|
|
1075
|
+
if (!rationaleProvided && !evidenceProvided) {
|
|
1076
|
+
problems.push("--phase=refactor-deferred requires --refactor-rationale=<text> or --evidence-ref=<text>");
|
|
1077
|
+
emitProblems(problems, json, 2);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1017
1082
|
if (args.status === "completed" && args["dispatch-surface"] !== "role-switch") {
|
|
1018
1083
|
for (const key of ["dispatch-id", "dispatch-surface", "agent-definition-path"]) {
|
|
1019
1084
|
if (!args[key]) problems.push("completed isolated/generic status requires --" + key);
|
package/dist/content/skills.d.ts
CHANGED
|
@@ -18,6 +18,16 @@ export declare function watchedFailProofBlock(): string;
|
|
|
18
18
|
export declare const INVESTIGATION_DISCIPLINE_STAGES: ReadonlySet<FlowStage>;
|
|
19
19
|
export declare function investigationDisciplineBlock(): string;
|
|
20
20
|
export declare function behaviorAnchorBlock(stage: FlowStage): string;
|
|
21
|
+
/**
|
|
22
|
+
* v6.12.0 Phase Ritual + Phase W — TDD-only top-of-skill sections that
|
|
23
|
+
* sit immediately after the `<EXTREMELY-IMPORTANT>` Iron Law block and
|
|
24
|
+
* before `## Quick Start`. They establish the per-slice three-dispatch
|
|
25
|
+
* ritual + wave batch mode in imperative voice with literal commands so
|
|
26
|
+
* pattern-matching on read works in our favor.
|
|
27
|
+
*
|
|
28
|
+
* Empty for non-TDD stages.
|
|
29
|
+
*/
|
|
30
|
+
export declare function tddTopOfSkillBlock(stage: FlowStage): string;
|
|
21
31
|
export declare function stageSkillFolder(stage: FlowStage): string;
|
|
22
32
|
export declare function stageSkillMarkdown(stage: FlowStage, track?: FlowTrack): string;
|
|
23
33
|
export declare function executingWavesSkillMarkdown(): string;
|
package/dist/content/skills.js
CHANGED
|
@@ -102,7 +102,7 @@ Any "the failure is real" claim (failing test, broken build, regression catch, d
|
|
|
102
102
|
|
|
103
103
|
\`proof: <iso-ts> | <observed snippet — first 200 chars> | source: <command or log path>\`
|
|
104
104
|
|
|
105
|
-
For TDD specifically, this is the watched-RED proof and is required per new test before \`stage-complete\` accepts the stage. From v6.
|
|
105
|
+
For TDD specifically, this is the watched-RED proof and is required per new test before \`stage-complete\` accepts the stage. From v6.12.0 onward, every slice on every TDD run dispatches three roles in this exact order: (1) \`test-author --slice S-<id> --phase red\`, (2) ONE message with TWO concurrent Task calls — \`slice-implementer --slice S-<id> --phase green --paths <production paths>\` AND \`slice-documenter --slice S-<id> --phase doc --paths <artifacts-dir>/tdd-slices/S-<id>.md\`, (3) \`slice-implementer --phase refactor\` or \`--phase refactor-deferred --refactor-rationale "<why>"\`. The linter auto-derives the \`Watched-RED Proof\` and \`Vertical Slice Cycle\` tables in \`06-tdd.md\` from \`.cclaw/state/delegation-events.jsonl\`. Do NOT hand-edit those tables. \`slice-implementer\` and \`slice-documenter\` are mandatory regardless of \`discoveryMode\` (v6.12.0 Phase R/M); the controller MUST NOT write GREEN production code or per-slice prose itself. v6.10.0 sidecar (\`06-tdd-slices.jsonl\`) is removed; \`cclaw-cli sync\` cleans the file from existing installs.
|
|
106
106
|
`;
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
@@ -168,6 +168,64 @@ function whenNotToUseBlock(items) {
|
|
|
168
168
|
return `## When Not to Use
|
|
169
169
|
${items.map((item) => `- ${item}`).join("\n")}
|
|
170
170
|
|
|
171
|
+
`;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* v6.12.0 Phase Ritual + Phase W — TDD-only top-of-skill sections that
|
|
175
|
+
* sit immediately after the `<EXTREMELY-IMPORTANT>` Iron Law block and
|
|
176
|
+
* before `## Quick Start`. They establish the per-slice three-dispatch
|
|
177
|
+
* ritual + wave batch mode in imperative voice with literal commands so
|
|
178
|
+
* pattern-matching on read works in our favor.
|
|
179
|
+
*
|
|
180
|
+
* Empty for non-TDD stages.
|
|
181
|
+
*/
|
|
182
|
+
export function tddTopOfSkillBlock(stage) {
|
|
183
|
+
if (stage !== "tdd")
|
|
184
|
+
return "";
|
|
185
|
+
return `## Per-Slice Ritual (v6.12.0+)
|
|
186
|
+
|
|
187
|
+
ONE slice = THREE dispatches, in this order. Do not skip, do not collapse.
|
|
188
|
+
|
|
189
|
+
1. **RED** — \`Task("test-author --slice S-<id> --phase red")\`.
|
|
190
|
+
2. **Verify RED** — wait for the \`phase=red\` event in \`.cclaw/state/delegation-events.jsonl\` with non-empty \`evidenceRefs\`. No production edits.
|
|
191
|
+
3. **GREEN+DOC fan-out** — ONE message, TWO concurrent Tasks:
|
|
192
|
+
\`\`\`
|
|
193
|
+
Task("slice-implementer --slice S-<id> --phase green --paths <prod paths>")
|
|
194
|
+
Task("slice-documenter --slice S-<id> --phase doc --paths <artifacts-dir>/tdd-slices/S-<id>.md")
|
|
195
|
+
\`\`\`
|
|
196
|
+
The file-overlap scheduler auto-allows parallel dispatch because \`claimedPaths\` are disjoint. Fire BOTH calls in the same message — never serialize independent work.
|
|
197
|
+
4. **REFACTOR** — \`Task("slice-implementer --slice S-<id> --phase refactor")\` OR \`--phase refactor-deferred --refactor-rationale '<why>'\`.
|
|
198
|
+
|
|
199
|
+
**FORBIDDEN:**
|
|
200
|
+
- Controller writing GREEN production code. ALL GREEN goes through \`slice-implementer\` — linter rule \`tdd_slice_implementer_missing\` blocks the gate.
|
|
201
|
+
- Controller writing per-slice prose into legacy \`06-tdd.md\` sections (Test Discovery / RED Evidence / GREEN Evidence / Watched-RED Proof / Vertical Slice Cycle / Per-Slice Review / Failure Analysis / Acceptance Mapping). \`slice-documenter\` owns \`tdd-slices/S-<id>.md\` — \`tdd_slice_documenter_missing\` blocks the gate.
|
|
202
|
+
- Hand-editing auto-render blocks between \`auto-start: tdd-slice-summary\` / \`auto-start: slices-index\` markers — overwritten every lint.
|
|
203
|
+
|
|
204
|
+
Delegation-record signature: \`node .cclaw/hooks/delegation-record.mjs --stage=tdd --agent=<agent> --mode=mandatory --status=<...> --span-id=<id> --dispatch-id=<id> --dispatch-surface=<surface> --agent-definition-path=<path> --slice=S-<id> --phase=<red|green|refactor|refactor-deferred|doc> [--paths=<csv>] [--refactor-rationale=<why>] [--ack-ts=<iso>] [--evidence-ref=<ref>] --json\`.
|
|
205
|
+
|
|
206
|
+
## Wave Batch Mode (v6.12.0+)
|
|
207
|
+
|
|
208
|
+
Trigger: any \`<artifacts-dir>/wave-plans/wave-NN.md\` exists, OR 2+ slices have disjoint \`claimedPaths\`. Cap = 5 \`slice-implementer\` lanes (10 subagents incl. paired documenters) via \`MAX_PARALLEL_SLICE_IMPLEMENTERS\`.
|
|
209
|
+
|
|
210
|
+
**Phase A — RED checkpoint** — ONE message, all test-authors:
|
|
211
|
+
\`\`\`
|
|
212
|
+
Task("test-author --slice S-1 --phase red")
|
|
213
|
+
Task("test-author --slice S-2 --phase red")
|
|
214
|
+
Task("test-author --slice S-3 --phase red")
|
|
215
|
+
\`\`\`
|
|
216
|
+
Wait for ALL Phase A REDs to land with non-empty \`evidenceRefs\` before Phase B. Linter \`tdd_red_checkpoint_violation\` (required: true) blocks any wave where a \`phase=green\` \`completedTs\` precedes the wave's last \`phase=red\` \`completedTs\`.
|
|
217
|
+
|
|
218
|
+
**Phase B — GREEN+DOC fan-out** — ONE message, paired implementer+documenter Tasks per slice:
|
|
219
|
+
\`\`\`
|
|
220
|
+
Task("slice-implementer --slice S-1 --phase green --paths <S-1 prod>")
|
|
221
|
+
Task("slice-documenter --slice S-1 --phase doc --paths <artifacts-dir>/tdd-slices/S-1.md")
|
|
222
|
+
Task("slice-implementer --slice S-2 --phase green --paths <S-2 prod>")
|
|
223
|
+
Task("slice-documenter --slice S-2 --phase doc --paths <artifacts-dir>/tdd-slices/S-2.md")
|
|
224
|
+
\`\`\`
|
|
225
|
+
Launch ALL Phase B pairs in ONE message. **Never serialize independent work.**
|
|
226
|
+
|
|
227
|
+
**Fan-in** — when 2+ \`slice-implementer\` rows complete in a wave, dispatch \`integration-overseer\` to verify cohesion contract (shared types, touchpoints, invariants, integration tests).
|
|
228
|
+
|
|
171
229
|
`;
|
|
172
230
|
}
|
|
173
231
|
function artifactTemplatePathForStage(stage) {
|
|
@@ -593,7 +651,7 @@ If you are about to violate the Iron Law, STOP. No amount of urgency, partial pr
|
|
|
593
651
|
|
|
594
652
|
</EXTREMELY-IMPORTANT>
|
|
595
653
|
|
|
596
|
-
${quickStartBlock(stage, track)}
|
|
654
|
+
${tddTopOfSkillBlock(stage)}${quickStartBlock(stage, track)}
|
|
597
655
|
|
|
598
656
|
${STAGE_LANGUAGE_POLICY_POINTER}
|
|
599
657
|
## Philosophy
|
|
@@ -117,6 +117,7 @@ function defaultReturnSchemaForAgent(agent) {
|
|
|
117
117
|
case "feasibility-reviewer":
|
|
118
118
|
return "review-return";
|
|
119
119
|
case "slice-implementer":
|
|
120
|
+
case "slice-documenter":
|
|
120
121
|
return "worker-return";
|
|
121
122
|
case "release-reviewer":
|
|
122
123
|
return "release-return";
|
|
@@ -143,7 +144,7 @@ function defaultReturnSchemaForAgent(agent) {
|
|
|
143
144
|
function dispatchClassForRow(row) {
|
|
144
145
|
if (row.dispatchClass)
|
|
145
146
|
return row.dispatchClass;
|
|
146
|
-
if (row.agent === "fixer" || row.agent === "slice-implementer")
|
|
147
|
+
if (row.agent === "fixer" || row.agent === "slice-implementer" || row.agent === "slice-documenter")
|
|
147
148
|
return "worker";
|
|
148
149
|
return row.skill?.includes("review") || row.agent === "reviewer" || row.agent === "security-reviewer" || row.agent.endsWith("-reviewer")
|
|
149
150
|
? "review-lens"
|
|
@@ -357,15 +358,12 @@ const REQUIRED_ARTIFACT_SECTIONS = {
|
|
|
357
358
|
],
|
|
358
359
|
plan: ["Task List", "Dependency Batches", "Acceptance Mapping", "Execution Posture", "WAIT_FOR_CONFIRM"],
|
|
359
360
|
tdd: [
|
|
360
|
-
"Test Discovery",
|
|
361
361
|
"System-Wide Impact Check",
|
|
362
362
|
"RED Evidence",
|
|
363
363
|
"GREEN Evidence",
|
|
364
364
|
"REFACTOR Notes",
|
|
365
365
|
"Traceability",
|
|
366
366
|
"Iron Law Acknowledgement",
|
|
367
|
-
"Watched-RED Proof",
|
|
368
|
-
"Vertical Slice Cycle",
|
|
369
367
|
"Verification Ladder"
|
|
370
368
|
],
|
|
371
369
|
review: ["Review Evidence Scope", "Changed-File Coverage", "Layer 1 Verdict", "Review Findings Contract", "Severity Summary", "Final Verdict"],
|
|
@@ -724,10 +722,18 @@ const STAGE_AUTO_SUBAGENT_DISPATCH = {
|
|
|
724
722
|
},
|
|
725
723
|
{
|
|
726
724
|
agent: "slice-implementer",
|
|
727
|
-
mode: "
|
|
725
|
+
mode: "mandatory",
|
|
726
|
+
requiredAtTier: "lightweight",
|
|
727
|
+
when: "Always for GREEN and REFACTOR phases. Controller MUST NOT write production code itself.",
|
|
728
|
+
purpose: "Implement the minimal passing slice inside explicit file boundaries and return strict worker evidence. v6.12.0 Phase M makes this dispatch mandatory; the linter rule `tdd_slice_implementer_missing` blocks the gate when GREEN was authored by anyone other than `slice-implementer`.",
|
|
729
|
+
requiresUserGate: false
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
agent: "slice-documenter",
|
|
733
|
+
mode: "mandatory",
|
|
728
734
|
requiredAtTier: "lightweight",
|
|
729
|
-
when: "
|
|
730
|
-
purpose: "
|
|
735
|
+
when: "Always in PARALLEL with `slice-implementer --phase green` for the same slice.",
|
|
736
|
+
purpose: "Write per-slice prose into `<artifacts-dir>/tdd-slices/S-<id>.md` while production code is being implemented. v6.12.0 Phase R makes this mandatory regardless of `discoveryMode`; the linter rule `tdd_slice_documenter_missing` blocks the gate when a `phase=doc` event is missing.",
|
|
731
737
|
requiresUserGate: false
|
|
732
738
|
},
|
|
733
739
|
{
|
|
@@ -20,7 +20,7 @@ export interface ArtifactValidation {
|
|
|
20
20
|
tier?: "required" | "recommended";
|
|
21
21
|
validationRule: string;
|
|
22
22
|
}
|
|
23
|
-
export type StageSubagentName = "researcher" | "architect" | "spec-validator" | "spec-document-reviewer" | "coherence-reviewer" | "scope-guardian-reviewer" | "feasibility-reviewer" | "slice-implementer" | "release-reviewer" | "planner" | "product-discovery" | "divergent-thinker" | "critic" | "reviewer" | "security-reviewer" | "integration-overseer" | "test-author" | "doc-updater" | "fixer";
|
|
23
|
+
export type StageSubagentName = "researcher" | "architect" | "spec-validator" | "spec-document-reviewer" | "coherence-reviewer" | "scope-guardian-reviewer" | "feasibility-reviewer" | "slice-implementer" | "slice-documenter" | "release-reviewer" | "planner" | "product-discovery" | "divergent-thinker" | "critic" | "reviewer" | "security-reviewer" | "integration-overseer" | "test-author" | "doc-updater" | "fixer";
|
|
24
24
|
export type StageSubagentDispatchClass = "stage-specialist" | "worker" | "review-lens";
|
|
25
25
|
export type StageSubagentReturnSchema = "planning-return" | "product-return" | "critic-return" | "review-return" | "security-return" | "tdd-return" | "docs-return" | "worker-return" | "fixer-return" | "research-return" | "architecture-return" | "spec-validation-return" | "release-return";
|
|
26
26
|
export interface StageAutoSubagentDispatch {
|