cclaw-cli 6.9.0 → 6.11.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/plan.js +37 -0
- package/dist/artifact-linter/shared.d.ts +51 -2
- package/dist/artifact-linter/shared.js +52 -4
- package/dist/artifact-linter/tdd.d.ts +42 -1
- package/dist/artifact-linter/tdd.js +443 -31
- package/dist/artifact-linter.js +93 -2
- package/dist/content/core-agents.d.ts +14 -0
- package/dist/content/core-agents.js +35 -0
- package/dist/content/examples.js +9 -9
- package/dist/content/hooks.js +202 -4
- package/dist/content/reference-patterns.js +2 -2
- package/dist/content/skills.js +1 -1
- package/dist/content/stage-schema.js +0 -3
- package/dist/content/stages/tdd.js +22 -23
- package/dist/content/subagents.js +12 -3
- package/dist/content/templates.d.ts +6 -0
- package/dist/content/templates.js +42 -34
- package/dist/delegation.d.ts +119 -0
- package/dist/delegation.js +171 -10
- package/dist/install.js +9 -0
- package/dist/internal/advance-stage.js +15 -3
- package/dist/internal/plan-split-waves.d.ts +66 -0
- package/dist/internal/plan-split-waves.js +249 -0
- package/package.json +1 -1
|
@@ -3,6 +3,8 @@ import { resolveArtifactPath as resolveStageArtifactPath } from "../artifact-pat
|
|
|
3
3
|
import { exists } from "../fs-utils.js";
|
|
4
4
|
import { FORBIDDEN_PLACEHOLDER_TOKENS, CONFIDENCE_FINDING_REGEX_SOURCE } from "../content/skills.js";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { PLAN_SPLIT_SMALL_PLAN_THRESHOLD, parseImplementationUnits } from "../internal/plan-split-waves.js";
|
|
6
8
|
export async function lintPlanStage(ctx) {
|
|
7
9
|
const { projectRoot, track, raw, absFile, sections, findings, parsedFrontmatter, brainstormShortCircuitBody, brainstormShortCircuitActivated, staleDiagramAuditEnabled, isTrivialOverride } = ctx;
|
|
8
10
|
evaluateInvestigationTrace(ctx, "Implementation Units");
|
|
@@ -114,6 +116,41 @@ export async function lintPlanStage(ctx) {
|
|
|
114
116
|
? "No forbidden placeholder tokens detected outside the rule section."
|
|
115
117
|
: `Detected forbidden token(s) elsewhere in plan: ${filteredPlanHits.join(", ")}.`
|
|
116
118
|
});
|
|
119
|
+
// v6.10.0 (P4) — advisory `plan_too_large_no_waves`. Fires when a
|
|
120
|
+
// standard-track plan has more than the wave-split threshold of
|
|
121
|
+
// implementation units AND the wave-plans/ directory is empty.
|
|
122
|
+
// Linter advisories never block stage-complete (`required: false`),
|
|
123
|
+
// so the agent gets a nudge to run `cclaw-cli internal plan-split-waves`
|
|
124
|
+
// without the plan stage failing.
|
|
125
|
+
try {
|
|
126
|
+
const planUnits = parseImplementationUnits(raw);
|
|
127
|
+
if (planUnits.length > PLAN_SPLIT_SMALL_PLAN_THRESHOLD) {
|
|
128
|
+
const artifactsDir = path.dirname(absFile);
|
|
129
|
+
const wavePlansDir = path.join(artifactsDir, "wave-plans");
|
|
130
|
+
let wavePlansHasContent = false;
|
|
131
|
+
try {
|
|
132
|
+
const dirEntries = await fs.readdir(wavePlansDir);
|
|
133
|
+
wavePlansHasContent = dirEntries.some((name) => /^wave-\d+\.md$/u.test(name));
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
wavePlansHasContent = false;
|
|
137
|
+
}
|
|
138
|
+
if (!wavePlansHasContent) {
|
|
139
|
+
findings.push({
|
|
140
|
+
section: "plan_too_large_no_waves",
|
|
141
|
+
required: false,
|
|
142
|
+
rule: "Plans with > 50 implementation units benefit from being split into manageable waves via `cclaw-cli internal plan-split-waves`.",
|
|
143
|
+
found: false,
|
|
144
|
+
details: `Plan has ${planUnits.length} implementation unit(s) (threshold ${PLAN_SPLIT_SMALL_PLAN_THRESHOLD}) and no wave-plans/ directory yet. ` +
|
|
145
|
+
"Run `cclaw-cli internal plan-split-waves` to break this plan into manageable waves; the linter is advisory only and will not block stage-complete."
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Parser errors should never block the linter — the advisory is
|
|
152
|
+
// purely a nudge.
|
|
153
|
+
}
|
|
117
154
|
const handoffBody = sectionBodyByName(sections, "Execution Handoff");
|
|
118
155
|
if (handoffBody !== null) {
|
|
119
156
|
const ok = /(subagent-driven|inline executor)/iu.test(handoffBody);
|
|
@@ -395,11 +395,49 @@ export interface ArchitectureDiagramValidationResult {
|
|
|
395
395
|
* mentioning external-dependency keywords).
|
|
396
396
|
*/
|
|
397
397
|
export declare function validateArchitectureDiagram(sectionBody: string, context?: ArchitectureDiagramValidationContext): ArchitectureDiagramValidationResult;
|
|
398
|
-
|
|
398
|
+
/**
|
|
399
|
+
* v6.10.0 (T3) — pointer-mode evidence acceptance. RED/GREEN sections may
|
|
400
|
+
* substitute pasted stdout with a single line of the form
|
|
401
|
+
* `Evidence: <relative-or-abs-path>` or `Evidence: spanId:<id>`. The
|
|
402
|
+
* validator alone cannot reach the filesystem or delegation ledger
|
|
403
|
+
* synchronously, so the lint pipeline pre-resolves pointers and then
|
|
404
|
+
* passes booleans through these option flags.
|
|
405
|
+
*/
|
|
406
|
+
export interface TddEvidencePointerOptions {
|
|
407
|
+
/**
|
|
408
|
+
* True when the section body has at least one `Evidence:` pointer line
|
|
409
|
+
* AND the pointer resolved to either an existing file or a known
|
|
410
|
+
* delegation spanId. The validator then short-circuits without
|
|
411
|
+
* requiring pasted stdout markers.
|
|
412
|
+
*/
|
|
413
|
+
pointerSatisfied?: boolean;
|
|
414
|
+
/**
|
|
415
|
+
* v6.11.0 (D5) — true when `delegation-events.jsonl` carries at least
|
|
416
|
+
* one slice-tagged event for the current run with the matching phase
|
|
417
|
+
* (`phase=red` for RED, `phase=green` for GREEN) and a non-empty
|
|
418
|
+
* `evidenceRefs` array. Phase events are the new source of truth in
|
|
419
|
+
* v6.11.0, so the markdown evidence block is auto-satisfied without
|
|
420
|
+
* requiring hand-pasted stdout content.
|
|
421
|
+
*/
|
|
422
|
+
phaseEventsSatisfied?: boolean;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Sync helper that scans for `Evidence:` lines in a section body and
|
|
426
|
+
* returns the trimmed value of each. Used by the lint pipeline to
|
|
427
|
+
* pre-resolve pointers (filesystem path-existence or delegation ledger
|
|
428
|
+
* spanId match) before invoking the validators.
|
|
429
|
+
*
|
|
430
|
+
* Recognised forms:
|
|
431
|
+
* Evidence: <path>
|
|
432
|
+
* Evidence: spanId:<id>
|
|
433
|
+
* - Evidence: <path>
|
|
434
|
+
*/
|
|
435
|
+
export declare function extractEvidencePointers(sectionBody: string): string[];
|
|
436
|
+
export declare function validateTddRedEvidence(sectionBody: string, opts?: TddEvidencePointerOptions): {
|
|
399
437
|
ok: boolean;
|
|
400
438
|
details: string;
|
|
401
439
|
};
|
|
402
|
-
export declare function validateTddGreenEvidence(sectionBody: string): {
|
|
440
|
+
export declare function validateTddGreenEvidence(sectionBody: string, opts?: TddEvidencePointerOptions): {
|
|
403
441
|
ok: boolean;
|
|
404
442
|
details: string;
|
|
405
443
|
};
|
|
@@ -543,6 +581,17 @@ export interface ValidateSectionBodyContext {
|
|
|
543
581
|
* in the Architecture Diagram body.
|
|
544
582
|
*/
|
|
545
583
|
liteTier?: boolean;
|
|
584
|
+
/**
|
|
585
|
+
* v6.10.0 (T3) — pre-resolved RED/GREEN Evidence pointer state. The
|
|
586
|
+
* artifact linter resolves `Evidence: <path|spanId:...>` lines and
|
|
587
|
+
* inspects the TDD slice sidecar before invoking
|
|
588
|
+
* `validateSectionBody`; the resulting booleans here let the
|
|
589
|
+
* validator short-circuit without re-doing async work.
|
|
590
|
+
*/
|
|
591
|
+
tddEvidence?: {
|
|
592
|
+
red?: TddEvidencePointerOptions;
|
|
593
|
+
green?: TddEvidencePointerOptions;
|
|
594
|
+
};
|
|
546
595
|
}
|
|
547
596
|
export declare function validateSectionBody(sectionBody: string, rule: string, sectionName: string, context?: ValidateSectionBodyContext): {
|
|
548
597
|
ok: boolean;
|
|
@@ -1417,7 +1417,43 @@ function shouldEnforceFailureEdge(diagramBody, context) {
|
|
|
1417
1417
|
return true;
|
|
1418
1418
|
return false;
|
|
1419
1419
|
}
|
|
1420
|
-
|
|
1420
|
+
/**
|
|
1421
|
+
* Sync helper that scans for `Evidence:` lines in a section body and
|
|
1422
|
+
* returns the trimmed value of each. Used by the lint pipeline to
|
|
1423
|
+
* pre-resolve pointers (filesystem path-existence or delegation ledger
|
|
1424
|
+
* spanId match) before invoking the validators.
|
|
1425
|
+
*
|
|
1426
|
+
* Recognised forms:
|
|
1427
|
+
* Evidence: <path>
|
|
1428
|
+
* Evidence: spanId:<id>
|
|
1429
|
+
* - Evidence: <path>
|
|
1430
|
+
*/
|
|
1431
|
+
export function extractEvidencePointers(sectionBody) {
|
|
1432
|
+
const pointers = [];
|
|
1433
|
+
const pattern = /^\s*-?\s*evidence\s*:\s*(.+?)\s*$/imu;
|
|
1434
|
+
for (const line of sectionBody.split(/\r?\n/u)) {
|
|
1435
|
+
const match = pattern.exec(line);
|
|
1436
|
+
if (match && match[1] !== undefined) {
|
|
1437
|
+
const value = match[1].trim();
|
|
1438
|
+
if (value.length > 0)
|
|
1439
|
+
pointers.push(value);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return pointers;
|
|
1443
|
+
}
|
|
1444
|
+
export function validateTddRedEvidence(sectionBody, opts = {}) {
|
|
1445
|
+
if (opts.phaseEventsSatisfied) {
|
|
1446
|
+
return {
|
|
1447
|
+
ok: true,
|
|
1448
|
+
details: "RED Evidence auto-satisfied: delegation-events.jsonl carries a phase=red row with non-empty evidenceRefs for the active run."
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
if (opts.pointerSatisfied) {
|
|
1452
|
+
return {
|
|
1453
|
+
ok: true,
|
|
1454
|
+
details: "RED Evidence satisfied via `Evidence: <path|spanId:...>` pointer (resolved to an existing artifact or delegation span)."
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1421
1457
|
const meaningful = meaningfulLineCount(sectionBody);
|
|
1422
1458
|
if (meaningful < 2) {
|
|
1423
1459
|
return {
|
|
@@ -1442,7 +1478,19 @@ export function validateTddRedEvidence(sectionBody) {
|
|
|
1442
1478
|
details: "RED Evidence includes command + failing output markers."
|
|
1443
1479
|
};
|
|
1444
1480
|
}
|
|
1445
|
-
export function validateTddGreenEvidence(sectionBody) {
|
|
1481
|
+
export function validateTddGreenEvidence(sectionBody, opts = {}) {
|
|
1482
|
+
if (opts.phaseEventsSatisfied) {
|
|
1483
|
+
return {
|
|
1484
|
+
ok: true,
|
|
1485
|
+
details: "GREEN Evidence auto-satisfied: delegation-events.jsonl carries a phase=green row with non-empty evidenceRefs for the active run."
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
if (opts.pointerSatisfied) {
|
|
1489
|
+
return {
|
|
1490
|
+
ok: true,
|
|
1491
|
+
details: "GREEN Evidence satisfied via `Evidence: <path|spanId:...>` pointer (resolved to an existing artifact or delegation span)."
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1446
1494
|
const meaningful = meaningfulLineCount(sectionBody);
|
|
1447
1495
|
if (meaningful < 2) {
|
|
1448
1496
|
return {
|
|
@@ -2058,10 +2106,10 @@ export function validateSectionBody(sectionBody, rule, sectionName, context = {}
|
|
|
2058
2106
|
}
|
|
2059
2107
|
const sectionNameNormalized = normalizeHeadingTitle(sectionName).toLowerCase();
|
|
2060
2108
|
if (sectionNameNormalized === "red evidence") {
|
|
2061
|
-
return validateTddRedEvidence(sectionBody);
|
|
2109
|
+
return validateTddRedEvidence(sectionBody, context.tddEvidence?.red ?? {});
|
|
2062
2110
|
}
|
|
2063
2111
|
if (sectionNameNormalized === "green evidence") {
|
|
2064
|
-
return validateTddGreenEvidence(sectionBody);
|
|
2112
|
+
return validateTddGreenEvidence(sectionBody, context.tddEvidence?.green ?? {});
|
|
2065
2113
|
}
|
|
2066
2114
|
if (sectionNameNormalized === "verification ladder") {
|
|
2067
2115
|
return validateVerificationLadder(sectionBody);
|
|
@@ -1,13 +1,54 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { DelegationEntry } from "../delegation.js";
|
|
2
|
+
import { type LintFinding, type StageLintContext } from "./shared.js";
|
|
3
|
+
/**
|
|
4
|
+
* v6.11.0 — TDD stage linter.
|
|
5
|
+
*
|
|
6
|
+
* Source-of-truth ladder, in order of precedence:
|
|
7
|
+
*
|
|
8
|
+
* 1. **Phase events** in `delegation-events.jsonl` for the active run
|
|
9
|
+
* (`stage=tdd`, `sliceId=S-N`, `phase=red|green|refactor|refactor-deferred|doc`).
|
|
10
|
+
* When at least one slice carries any phase event, the linter
|
|
11
|
+
* auto-derives Watched-RED / Vertical Slice Cycle from the events
|
|
12
|
+
* and writes a rendered summary block between auto-render markers
|
|
13
|
+
* in `06-tdd.md`. Markdown table content is no longer required.
|
|
14
|
+
* 2. **Legacy markdown tables** (Watched-RED Proof + Vertical Slice
|
|
15
|
+
* Cycle) — used as a fallback when the events ledger has no slice
|
|
16
|
+
* phase rows for the active run. Existing v6.10 and earlier
|
|
17
|
+
* artifacts continue to validate via this path.
|
|
18
|
+
* 3. **Sharded slice files** under `<artifacts-dir>/tdd-slices/S-*.md`.
|
|
19
|
+
* Per-slice prose lives there. The main `06-tdd.md` is auto-indexed
|
|
20
|
+
* via `## Slices Index`.
|
|
21
|
+
*/
|
|
2
22
|
export declare function lintTddStage(ctx: StageLintContext): Promise<void>;
|
|
23
|
+
interface SliceFileInfo {
|
|
24
|
+
sliceId: string;
|
|
25
|
+
absPath: string;
|
|
26
|
+
}
|
|
3
27
|
interface ParsedSliceCycleResult {
|
|
4
28
|
ok: boolean;
|
|
5
29
|
details: string;
|
|
6
30
|
}
|
|
31
|
+
interface ExpandedSliceCycleResult extends ParsedSliceCycleResult {
|
|
32
|
+
findings: LintFinding[];
|
|
33
|
+
}
|
|
34
|
+
export declare function evaluateEventsWatchedRed(slices: Map<string, DelegationEntry[]>): ParsedSliceCycleResult;
|
|
35
|
+
export declare function evaluateEventsSliceCycle(slices: Map<string, DelegationEntry[]>): ExpandedSliceCycleResult;
|
|
36
|
+
interface DocCoverageResult {
|
|
37
|
+
missing: string[];
|
|
38
|
+
}
|
|
39
|
+
export declare function evaluateSliceDocumenterCoverage(slices: Map<string, DelegationEntry[]>): DocCoverageResult;
|
|
7
40
|
export declare function parseVerticalSliceCycle(body: string): ParsedSliceCycleResult;
|
|
8
41
|
interface VerificationLadderResult {
|
|
9
42
|
ok: boolean;
|
|
10
43
|
details: string;
|
|
11
44
|
}
|
|
12
45
|
export declare function evaluateVerificationLadder(body: string | null): VerificationLadderResult;
|
|
46
|
+
interface RenderInput {
|
|
47
|
+
mainArtifactPath: string;
|
|
48
|
+
slicesByEvents: Map<string, DelegationEntry[]>;
|
|
49
|
+
sliceFiles: SliceFileInfo[];
|
|
50
|
+
renderSummary?: boolean;
|
|
51
|
+
renderIndex?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export declare function renderTddSliceSummary(input: RenderInput): Promise<void>;
|
|
13
54
|
export {};
|