cclaw-cli 0.35.0 → 0.37.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/config.js CHANGED
@@ -21,8 +21,11 @@ const ALLOWED_CONFIG_KEYS = new Set([
21
21
  "gitHookGuards",
22
22
  "defaultTrack",
23
23
  "languageRulePacks",
24
- "trackHeuristics"
24
+ "trackHeuristics",
25
+ "sliceReview"
25
26
  ]);
27
+ const DEFAULT_SLICE_REVIEW_THRESHOLD = 5;
28
+ const DEFAULT_SLICE_REVIEW_TRACKS = ["standard"];
26
29
  function configFixExample() {
27
30
  return `harnesses:
28
31
  - claude
@@ -210,6 +213,41 @@ export async function readConfig(projectRoot) {
210
213
  tracks
211
214
  };
212
215
  }
216
+ const sliceReviewRaw = parsed.sliceReview;
217
+ let sliceReview = undefined;
218
+ if (Object.prototype.hasOwnProperty.call(parsed, "sliceReview")) {
219
+ if (!isRecord(sliceReviewRaw)) {
220
+ throw configValidationError(fullPath, `"sliceReview" must be an object`);
221
+ }
222
+ const enabledRaw = sliceReviewRaw.enabled;
223
+ if (enabledRaw !== undefined && typeof enabledRaw !== "boolean") {
224
+ throw configValidationError(fullPath, `"sliceReview.enabled" must be a boolean`);
225
+ }
226
+ const thresholdRaw = sliceReviewRaw.filesChangedThreshold;
227
+ if (thresholdRaw !== undefined &&
228
+ (typeof thresholdRaw !== "number" || !Number.isInteger(thresholdRaw) || thresholdRaw < 1)) {
229
+ throw configValidationError(fullPath, `"sliceReview.filesChangedThreshold" must be a positive integer`);
230
+ }
231
+ const touchTriggers = validateStringArray(sliceReviewRaw.touchTriggers, "sliceReview.touchTriggers", fullPath);
232
+ const enforceRaw = sliceReviewRaw.enforceOnTracks;
233
+ let enforceOnTracks;
234
+ if (enforceRaw !== undefined) {
235
+ if (!Array.isArray(enforceRaw)) {
236
+ throw configValidationError(fullPath, `"sliceReview.enforceOnTracks" must be an array`);
237
+ }
238
+ const invalidTracks = enforceRaw.filter((value) => typeof value !== "string" || !FLOW_TRACK_SET.has(value));
239
+ if (invalidTracks.length > 0) {
240
+ throw configValidationError(fullPath, `"sliceReview.enforceOnTracks" must contain only: ${SUPPORTED_TRACKS_TEXT}`);
241
+ }
242
+ enforceOnTracks = [...new Set(enforceRaw)];
243
+ }
244
+ sliceReview = {
245
+ enabled: typeof enabledRaw === "boolean" ? enabledRaw : false,
246
+ filesChangedThreshold: typeof thresholdRaw === "number" ? thresholdRaw : DEFAULT_SLICE_REVIEW_THRESHOLD,
247
+ touchTriggers: touchTriggers ?? [],
248
+ enforceOnTracks: enforceOnTracks ?? DEFAULT_SLICE_REVIEW_TRACKS
249
+ };
250
+ }
213
251
  return {
214
252
  version: parsed.version ?? CCLAW_VERSION,
215
253
  flowVersion: parsed.flowVersion ?? FLOW_VERSION,
@@ -220,7 +258,8 @@ export async function readConfig(projectRoot) {
220
258
  gitHookGuards,
221
259
  defaultTrack,
222
260
  languageRulePacks,
223
- trackHeuristics
261
+ trackHeuristics,
262
+ sliceReview
224
263
  };
225
264
  }
226
265
  export async function writeConfig(projectRoot, config) {
@@ -35,20 +35,24 @@ the user can approve individual lifts, accept-all, or skip.
35
35
  - set \`closeout.compoundPromoted = 0\`,
36
36
  - set \`closeout.shipSubstate = "ready_to_archive"\`,
37
37
  - emit \`compound: no candidates | next: /cc-next\` and stop.
38
- 5. Otherwise, present **one** structured ask (AskUserQuestion / AskQuestion /
38
+ 5. **Drift check** each surviving candidate before presenting it (see
39
+ "Drift check" section in the skill): confirm the lift target file is
40
+ current, spot-check the repo for contradictions, demote stale clusters
41
+ into a new superseding entry instead of a lift.
42
+ 6. Otherwise, present **one** structured ask (AskUserQuestion / AskQuestion /
39
43
  plain text) summarising all candidates at once:
40
44
  - \`apply-all\` (default) — apply every listed lift,
41
45
  - \`apply-selected\` — prompt per-candidate,
42
46
  - \`skip\` — record a skip reason and advance without changes.
43
- 6. Apply approved lifts to the target file(s). Each lift also appends a
47
+ 7. Apply approved lifts to the target file(s). Each lift also appends a
44
48
  \`type: "compound"\` entry back to \`${RUNTIME_ROOT}/knowledge.jsonl\`
45
49
  summarising what was lifted.
46
- 7. Update flow-state:
50
+ 8. Update flow-state:
47
51
  - \`closeout.compoundCompletedAt = <ISO>\`,
48
52
  - \`closeout.compoundPromoted = <count>\`,
49
53
  - \`closeout.compoundSkipped = true\` if user picked skip,
50
54
  - \`closeout.shipSubstate = "ready_to_archive"\`.
51
- 8. Emit one-line summary: \`compound: promoted=<N> skipped=<bool> | next: /cc-next\`.
55
+ 9. Emit one-line summary: \`compound: promoted=<N> skipped=<bool> | next: /cc-next\`.
52
56
 
53
57
  ## Primary skill
54
58
 
@@ -83,27 +87,53 @@ empty pass is allowed and must advance \`closeout.shipSubstate\` to
83
87
  - \`closeout.compoundPromoted = 0\`,
84
88
  - \`closeout.shipSubstate = "ready_to_archive"\`,
85
89
  - announce \`compound: no candidates\` and stop.
86
- 4. Otherwise, render each candidate as:
90
+ 4. **Drift check run before presenting any candidate.** Knowledge lines
91
+ are append-only, so textual repetition alone does not prove the rule is
92
+ still true. For every cluster that survives the recurrence filter:
93
+
94
+ - **Read the lift target.** Open the rule/protocol/skill file you would
95
+ edit. If the current contents already encode a stronger version of
96
+ the cluster's \`action\`, drop the candidate (nothing to lift).
97
+ - **Grep for contradictions.** Run a quick repo search on the cluster's
98
+ \`trigger\` keywords. If recent code or docs contradict the cluster,
99
+ treat the cluster as stale.
100
+ - **Check age.** Inspect \`last_seen_ts\` across the cluster's lines. If
101
+ every contributing line is older than ~90 days with no fresh
102
+ observation, treat the cluster as stale.
103
+ - **Handle stale clusters correctly.** Do **not** silently skip them.
104
+ Append a new superseding \`type: "lesson"\` line to
105
+ \`.cclaw/knowledge.jsonl\` whose \`trigger\` explicitly references the
106
+ old pattern (e.g. \`"when previous rule about X no longer holds: ..."\`)
107
+ and whose \`action\` documents the replacement or archive reason.
108
+ Then drop the candidate from the lift list.
109
+ - **Cite line IDs.** Every surviving candidate must list the concrete
110
+ knowledge line indices (1-based) that back it, not just a
111
+ summary string. This is what makes the lift auditable.
112
+ - Optionally invoke the \`knowledge-curation\` utility skill's
113
+ stale/duplicate/supersede heuristics if you want a second pass.
114
+
115
+ 5. Otherwise, render each candidate as:
87
116
 
88
117
  \`\`\`
89
118
  Candidate: <short title>
90
- Evidence: <knowledge refs>
119
+ Evidence: <knowledge line-ids>
120
+ Freshness: <newest last_seen_ts among evidence lines>
91
121
  Lift target: <rule/protocol/skill file>
92
122
  Change type: <add/update/remove>
93
123
  Expected benefit: <what regressions this prevents>
94
124
  \`\`\`
95
125
 
96
- 5. Present **one** structured question with three options:
126
+ 6. Present **one** structured question with three options:
97
127
  - \`apply-all\` (default) — apply every candidate,
98
128
  - \`apply-selected\` — prompt per-candidate approval next,
99
129
  - \`skip\` — record a skip reason and advance.
100
130
 
101
- 6. For approved candidates:
131
+ 7. For approved candidates:
102
132
  - edit the target file(s) with the lift,
103
133
  - append a \`type: "compound"\` entry to \`.cclaw/knowledge.jsonl\`
104
- describing what was promoted.
134
+ describing what was promoted, including the source line IDs.
105
135
 
106
- 7. Update flow-state \`closeout\`:
136
+ 8. Update flow-state \`closeout\`:
107
137
  - \`compoundCompletedAt\`,
108
138
  - \`compoundPromoted\` (count),
109
139
  - \`compoundSkipped\` (boolean) + \`compoundSkipReason\` when applicable,
@@ -122,6 +152,9 @@ closeout chain's perspective.
122
152
  - \`closeout.compoundCompletedAt\` is set.
123
153
  - \`closeout.shipSubstate === "ready_to_archive"\`.
124
154
  - If lifts were applied, the target files show the edit and at least one
125
- new \`compound\` line exists in \`.cclaw/knowledge.jsonl\`.
155
+ new \`compound\` line exists in \`.cclaw/knowledge.jsonl\`, and the new
156
+ line references the source knowledge line IDs.
157
+ - If drift check demoted any cluster, a new superseding \`lesson\` line
158
+ exists on the same run documenting the replacement.
126
159
  `;
127
160
  }
@@ -25,6 +25,7 @@ export const PLAN = {
25
25
  "Group tasks into dependency batches — batch N+1 cannot start until batch N has verification evidence.",
26
26
  "Slice into vertical tasks — each task targets 2-5 minutes, produces one testable outcome, and touches one coherent area.",
27
27
  "Attach verification — every task has an acceptance criterion mapping and a concrete verification command.",
28
+ "Annotate slice-review metadata — if `.cclaw/config.yaml::sliceReview.enabled` is true, every task row additionally carries `touchCount` (rough number of files expected to change) and `touchPaths` (glob hints, e.g. `migrations/**`, `src/auth/**`). A task may set `highRisk: true` to force a review pass regardless of thresholds. These fields feed the TDD stage's Per-Slice Review checkpoint; when `sliceReview` is disabled they are optional.",
28
29
  "Map scope Locked Decisions — every D-XX from scope is referenced by at least one plan task (or explicitly marked deferred with reason).",
29
30
  "Run anti-placeholder + anti-scope-reduction scans — block `TODO/TBD/...` and phrasing like `v1`, `for now`, `later` for locked boundaries.",
30
31
  "Define checkpoints — mark points where progress should be validated before continuing.",
@@ -143,7 +144,7 @@ export const PLAN = {
143
144
  artifactValidation: [
144
145
  { section: "Dependency Graph", required: true, validationRule: "Ordering and parallel opportunities explicit. No circular dependencies." },
145
146
  { section: "Dependency Batches", required: true, validationRule: "Every task belongs to a batch. Each batch has an exit gate and dependency statement." },
146
- { section: "Task List", required: true, validationRule: "Each task row includes ID, description, acceptance criterion, verification command, and effort estimate (S/M/L). Every task must also carry a minutes estimate within the 2-5 minute budget." },
147
+ { section: "Task List", required: true, validationRule: "Each task row includes ID, description, acceptance criterion, verification command, and effort estimate (S/M/L). Every task must also carry a minutes estimate within the 2-5 minute budget. When the sliceReview feature is enabled in the cclaw config, each task row additionally declares touchCount, touchPaths, and an optional highRisk flag so the TDD stage can decide whether a Per-Slice Review pass is required." },
147
148
  { section: "Acceptance Mapping", required: true, validationRule: "Every spec criterion is covered by at least one task." },
148
149
  { section: "Locked Decision Coverage", required: false, validationRule: "Every locked decision ID (D-XX) from scope is listed with linked task IDs or explicit defer rationale." },
149
150
  { section: "Risk Assessment", required: false, validationRule: "If present: per-task or per-batch risk identification with likelihood, impact, and mitigation strategy." },
@@ -30,6 +30,7 @@ export const TDD = {
30
30
  "REFACTOR: Improve code quality — without changing behavior. Document what you changed and why.",
31
31
  "Record evidence — capture RED failure, GREEN output, and REFACTOR notes in the TDD artifact.",
32
32
  "Annotate traceability — link to plan task ID and spec criterion.",
33
+ "Per-Slice Review (conditional) — if `.cclaw/config.yaml::sliceReview.enabled` is true and the slice meets any trigger (touchCount >= filesChangedThreshold, touchPaths match touchTriggers, or highRisk=true), append a `## Per-Slice Review` entry for this slice before moving on (see the dedicated section below).",
33
34
  "Repeat for each slice — return to step 1 for the next plan slice."
34
35
  ],
35
36
  interactionProtocol: [
@@ -41,7 +42,8 @@ export const TDD = {
41
42
  "Run full suite, not partial checks, for GREEN validation.",
42
43
  "Refactor without changing behavior and document rationale (REFACTOR).",
43
44
  "Stop if regressions appear and fix before proceeding.",
44
- "If a test passes unexpectedly, investigate: does the behavior already exist, or is the test wrong?"
45
+ "If a test passes unexpectedly, investigate: does the behavior already exist, or is the test wrong?",
46
+ "**Per-Slice Review checkpoint (conditional, opt-in).** When `.cclaw/config.yaml::sliceReview.enabled` is true, check every slice against the triggers before declaring it DONE. Triggers: `touchCount >= filesChangedThreshold`, any `touchPaths` match a `touchTriggers` glob, or the plan row declares `highRisk: true`. On a trigger, run two passes on the slice alone — (1) Spec-Compliance: trace RED/GREEN/REFACTOR evidence back to its plan task + spec criterion, noting edge cases the tests skip; (2) Quality: diff-scan for naming, error handling, dead code, simpler alternatives. Record both under `## Per-Slice Review` in `06-tdd.md`, naming the trigger that fired. Dispatch the `reviewer` subagent natively when available (log `fulfillmentMode: \"isolated\"`); otherwise fulfil via in-session role switch (`fulfillmentMode: \"role-switch\"`). Never fabricate an isolated pass from memory. Tracks outside `sliceReview.enforceOnTracks` still emit the section; doctor only escalates missed reviews on enforced tracks."
45
47
  ],
46
48
  process: [
47
49
  "Select slice and map to acceptance criterion.",
@@ -51,7 +53,7 @@ export const TDD = {
51
53
  "Run full tests and build checks.",
52
54
  "Perform refactor pass preserving behavior.",
53
55
  "Record RED, GREEN, and REFACTOR evidence in artifact.",
54
- "Annotate traceability to plan task and spec criterion."
56
+ "Annotate traceability to plan task and spec criterion; on `sliceReview` triggers, append a Per-Slice Review entry before closing the slice."
55
57
  ],
56
58
  requiredGates: [
57
59
  { id: "tdd_red_test_written", description: "Failing tests exist before implementation changes." },
@@ -149,6 +151,15 @@ export const TDD = {
149
151
  ],
150
152
  stopGate: false
151
153
  },
154
+ {
155
+ title: "Per-Slice Review Audit (conditional)",
156
+ evaluationPoints: [
157
+ "When `.cclaw/config.yaml::sliceReview.enabled` is true: does every triggered slice (touchCount >= threshold, touchPaths match, or highRisk=true) carry a Per-Slice Review entry with BOTH a Spec-Compliance pass (plan task <-> spec criterion + edge-case notes) AND a Quality pass (diff-level naming/errors/dead code/simpler alternatives)?",
158
+ "Is the delegation `fulfillmentMode` recorded (`isolated` for a dispatched reviewer subagent, `role-switch` for an in-session pass) and does it match an entry in `.cclaw/state/delegation-log.json`?",
159
+ "On tracks listed in `sliceReview.enforceOnTracks`, are there zero missed triggered slices (doctor also surfaces this as a warning)?"
160
+ ],
161
+ stopGate: false
162
+ },
152
163
  {
153
164
  title: "State-over-Interaction + Beyoncé Coverage",
154
165
  evaluationPoints: [
@@ -177,7 +188,8 @@ export const TDD = {
177
188
  { section: "Verification Ladder", required: false, validationRule: "If present: per-slice verification tier (static, command, behavioral, human) with evidence for highest tier reached." },
178
189
  { section: "Coverage Targets", required: false, validationRule: "If present: per-module or per-code-type coverage thresholds with current values and measurement commands." },
179
190
  { section: "Test Pyramid Shape", required: false, validationRule: "If present: per-slice count of Small/Medium/Large tests added, to let reviewers verify the suite is not drifting top-heavy." },
180
- { section: "Prove-It Reproduction", required: false, validationRule: "Required for bug-fix slices: original failing reproduction test (RED without fix), passing output with fix (GREEN), and a note confirming the test fails again if the fix is reverted." }
191
+ { section: "Prove-It Reproduction", required: false, validationRule: "Required for bug-fix slices: original failing reproduction test (RED without fix), passing output with fix (GREEN), and a note confirming the test fails again if the fix is reverted." },
192
+ { section: "Per-Slice Review", required: false, validationRule: "When `.cclaw/config.yaml::sliceReview.enabled` is true: per triggered slice, a two-part record — Spec-Compliance (slice <-> plan task <-> spec criterion trace plus edge-case notes) and Quality (diff-focused review of naming, error handling, dead code, simpler alternatives). Each entry names the trigger (touchCount, touchPaths glob, or highRisk) and the delegation fulfillmentMode (`isolated` when a reviewer subagent was dispatched natively; `role-switch` when fulfilled in-session). Slices that did not meet any trigger may list `not triggered` instead of a full pass." }
181
193
  ],
182
194
  batchExecutionAllowed: true
183
195
  };
package/dist/doctor.js CHANGED
@@ -873,7 +873,13 @@ export async function doctorChecks(projectRoot, options = {}) {
873
873
  let missingSchemaV2Fields = 0;
874
874
  let parsedKnowledgeLines = 0;
875
875
  let lowConfidenceLines = 0;
876
+ let staleRawEntries = 0;
876
877
  const triggerActionCounts = new Map();
878
+ // Stale threshold for raw entries: ~90 days with no re-observation.
879
+ // Chosen to match the compound drift checklist language; anything newer is
880
+ // recent enough to trust, anything older deserves a curate/supersede pass.
881
+ const STALE_RAW_THRESHOLD_MS = 90 * 24 * 60 * 60 * 1000;
882
+ const now = Date.now();
877
883
  const requiredV2Fields = [
878
884
  "type",
879
885
  "trigger",
@@ -919,6 +925,14 @@ export async function doctorChecks(projectRoot, options = {}) {
919
925
  if (missing) {
920
926
  missingSchemaV2Fields += 1;
921
927
  }
928
+ const maturity = typeof parsed.maturity === "string" ? parsed.maturity.toLowerCase() : "";
929
+ const lastSeenRaw = typeof parsed.last_seen_ts === "string" ? parsed.last_seen_ts : "";
930
+ if (maturity === "raw" && lastSeenRaw.length > 0) {
931
+ const lastSeenMs = Date.parse(lastSeenRaw);
932
+ if (Number.isFinite(lastSeenMs) && now - lastSeenMs > STALE_RAW_THRESHOLD_MS) {
933
+ staleRawEntries += 1;
934
+ }
935
+ }
922
936
  }
923
937
  catch {
924
938
  malformedKnowledgeLines += 1;
@@ -962,6 +976,15 @@ export async function doctorChecks(projectRoot, options = {}) {
962
976
  ? "no high-frequency repeated trigger/action clusters detected"
963
977
  : `warning: ${repeatedClusters.length} repeated learning cluster(s) detected (>=3 repeats). Consider /cc-ops compound to lift them into rules/skills.`
964
978
  });
979
+ checks.push({
980
+ name: "warning:knowledge:stale_raw_entries",
981
+ ok: true,
982
+ details: parsedKnowledgeLines === 0
983
+ ? "knowledge.jsonl is empty"
984
+ : staleRawEntries === 0
985
+ ? `no raw knowledge entries older than 90 days`
986
+ : `warning: ${staleRawEntries} raw knowledge entry(ies) have last_seen_ts older than 90 days. Run /cc-learn curate or append a superseding entry before the next /cc-ops compound pass.`
987
+ });
965
988
  }
966
989
  checks.push({
967
990
  name: "state:checkpoint_exists",
@@ -1382,6 +1405,37 @@ export async function doctorChecks(projectRoot, options = {}) {
1382
1405
  ? "all test slices map to acceptance-linked tasks"
1383
1406
  : `orphaned test slices: ${trace.orphanedTests.join(", ")}`
1384
1407
  });
1408
+ // Slice-review warning (opt-in via config.sliceReview.enabled).
1409
+ // Fires when:
1410
+ // - sliceReview.enabled is true
1411
+ // - current track is listed in sliceReview.enforceOnTracks
1412
+ // - 06-tdd.md exists (so the slice loop actually started)
1413
+ // - artifact contains at least one slice marker (look for the tdd
1414
+ // "Acceptance Mapping" or any `### Slice` heading) AND the Per-Slice
1415
+ // Review heading is absent
1416
+ // Non-blocking — warnings guide the user toward adding the review
1417
+ // section without failing doctor.
1418
+ const sliceReviewConfig = parsedConfig?.sliceReview;
1419
+ const sliceReviewEnabled = sliceReviewConfig?.enabled === true;
1420
+ const sliceReviewEnforcedTracks = sliceReviewConfig?.enforceOnTracks ?? ["standard"];
1421
+ const sliceReviewEnforcedHere = sliceReviewEnabled && sliceReviewEnforcedTracks.includes(activeTrack);
1422
+ if (sliceReviewEnforcedHere && tddExists) {
1423
+ const tddMarkdown = await fs.readFile(path.join(artifactsDir, "06-tdd.md"), "utf8");
1424
+ const hasSliceSignal = /^###\s+Slice\b/im.test(tddMarkdown)
1425
+ || /^##\s+Acceptance Mapping\b/im.test(tddMarkdown)
1426
+ || /^##\s+RED\b/im.test(tddMarkdown);
1427
+ const hasReviewHeading = /^##\s+Per-Slice Review\b/im.test(tddMarkdown);
1428
+ const missing = hasSliceSignal && !hasReviewHeading;
1429
+ checks.push({
1430
+ name: "warning:slice_review:missing_section",
1431
+ ok: !missing,
1432
+ details: missing
1433
+ ? `warning: sliceReview is enabled for track "${activeTrack}" and 06-tdd.md contains slice evidence but no "## Per-Slice Review" section. Add a Per-Slice Review entry for every triggered slice (touchCount >= ${sliceReviewConfig?.filesChangedThreshold ?? 5}, touchPaths match, or highRisk=true), or record "not triggered" explicitly.`
1434
+ : hasReviewHeading
1435
+ ? `sliceReview section present in 06-tdd.md (track "${activeTrack}")`
1436
+ : `sliceReview enabled but no slice evidence yet in 06-tdd.md (track "${activeTrack}")`
1437
+ });
1438
+ }
1385
1439
  const gateEvidence = await verifyCurrentStageGateEvidence(projectRoot, flowState);
1386
1440
  checks.push({
1387
1441
  name: "gates:evidence:current_stage",
package/dist/install.d.ts CHANGED
@@ -11,7 +11,8 @@ export declare function syncCclaw(projectRoot: string): Promise<void>;
11
11
  * artifacts, state, or custom config keys. Only the `version` + `flowVersion`
12
12
  * stamps are rewritten so the on-disk config reflects the installed CLI;
13
13
  * `promptGuardMode`, `tddEnforcement`, `gitHookGuards`, `languageRulePacks`,
14
- * and `trackHeuristics` are preserved verbatim from the existing config.
14
+ * `trackHeuristics`, and `sliceReview` are preserved verbatim from the
15
+ * existing config.
15
16
  *
16
17
  * For an explicit reset, run `cclaw-cli uninstall && cclaw-cli init`
17
18
  * (after optionally archiving the current run via `/cc-ops archive`).
package/dist/install.js CHANGED
@@ -1139,7 +1139,8 @@ export async function syncCclaw(projectRoot) {
1139
1139
  * artifacts, state, or custom config keys. Only the `version` + `flowVersion`
1140
1140
  * stamps are rewritten so the on-disk config reflects the installed CLI;
1141
1141
  * `promptGuardMode`, `tddEnforcement`, `gitHookGuards`, `languageRulePacks`,
1142
- * and `trackHeuristics` are preserved verbatim from the existing config.
1142
+ * `trackHeuristics`, and `sliceReview` are preserved verbatim from the
1143
+ * existing config.
1143
1144
  *
1144
1145
  * For an explicit reset, run `cclaw-cli uninstall && cclaw-cli init`
1145
1146
  * (after optionally archiving the current run via `/cc-ops archive`).
package/dist/types.d.ts CHANGED
@@ -41,6 +41,35 @@ export interface TrackHeuristicsConfig {
41
41
  /** Per-track matching rules. */
42
42
  tracks?: Partial<Record<FlowTrack, TrackHeuristicRule>>;
43
43
  }
44
+ /**
45
+ * Opt-in plan-slice review heuristic.
46
+ *
47
+ * When enabled, the TDD stage skill is instructed to insert a
48
+ * `## Per-Slice Review` section into `06-tdd.md` for every plan slice
49
+ * whose estimated `touchCount` meets `filesChangedThreshold`, whose
50
+ * `touchPaths` match any `touchTriggers` glob, or whose plan row is
51
+ * flagged `highRisk: true`. The section records a short spec-compliance
52
+ * pass plus a short quality pass (delegated to the `reviewer` subagent
53
+ * when the harness supports native dispatch, otherwise fulfilled via
54
+ * an explicit in-session role switch with evidence).
55
+ *
56
+ * Track gating: `enforceOnTracks` lists the tracks where the doctor
57
+ * check escalates to a warning. Tracks outside this list still see
58
+ * the skill prose but leave the decision to the user.
59
+ *
60
+ * All fields optional; sensible defaults: disabled, threshold 5, no
61
+ * touch triggers, `enforceOnTracks: ["standard"]`.
62
+ */
63
+ export interface SliceReviewConfig {
64
+ /** Turn the heuristic on (disabled by default). */
65
+ enabled?: boolean;
66
+ /** Minimum estimated touchCount for a slice to be eligible. */
67
+ filesChangedThreshold?: number;
68
+ /** Glob hints; any plan-task touchPath match triggers review. */
69
+ touchTriggers?: string[];
70
+ /** Tracks on which missed reviews escalate to a doctor warning. */
71
+ enforceOnTracks?: FlowTrack[];
72
+ }
44
73
  export interface VibyConfig {
45
74
  version: string;
46
75
  flowVersion: string;
@@ -67,6 +96,14 @@ export interface VibyConfig {
67
96
  * If omitted, cclaw uses built-in defaults.
68
97
  */
69
98
  trackHeuristics?: TrackHeuristicsConfig;
99
+ /**
100
+ * Opt-in per-slice review heuristic. When enabled, the TDD skill
101
+ * requires a `## Per-Slice Review` section in `06-tdd.md` for slices
102
+ * that exceed `filesChangedThreshold` or match `touchTriggers`.
103
+ * Keeps obra's "fresh subagent + spec-then-quality review per task"
104
+ * discipline tractable without forcing it on tiny quick-track fixes.
105
+ */
106
+ sliceReview?: SliceReviewConfig;
70
107
  }
71
108
  export interface TransitionRule {
72
109
  from: FlowStage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.35.0",
3
+ "version": "0.37.0",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {