cclaw-cli 7.6.0 → 7.7.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.
@@ -4,6 +4,8 @@ import { RUNTIME_ROOT } from "../constants.js";
4
4
  import { readDelegationEvents, readDelegationLedger } from "../delegation.js";
5
5
  import { readFlowState } from "../runs.js";
6
6
  import { DEFAULT_SLICE_STREAM_REL_PATH, readEventStreamFile } from "../streaming/event-stream.js";
7
+ import { readConfig, resolveExecutionStrictness, resolveExecutionTopology, resolveMaxBuilders } from "../config.js";
8
+ import { routeExecutionTopology } from "../execution-topology.js";
7
9
  import { mergeParallelWaveDefinitions, parseParallelExecutionPlanWaves, parseWavePlanDirectory } from "./plan-split-waves.js";
8
10
  import { compareSliceIds, parseSliceId } from "../util/slice-id.js";
9
11
  const PARALLEL_EXEC_MANAGED_START = "<!-- parallel-exec-managed-start -->";
@@ -82,7 +84,11 @@ function parseManagedWaveClaimedPaths(planMarkdown) {
82
84
  if (cells.length === 0)
83
85
  continue;
84
86
  const first = cells[0].toLowerCase();
85
- if (first === "sliceid" || first === "slice id") {
87
+ if (first === "sliceid" ||
88
+ first === "slice id" ||
89
+ first === "unitid" ||
90
+ first === "unit id" ||
91
+ first === "unit") {
86
92
  headerIdx = new Map();
87
93
  for (let i = 0; i < cells.length; i += 1) {
88
94
  const key = cells[i].toLowerCase().replace(/[^a-z0-9]/gu, "");
@@ -95,10 +101,12 @@ function parseManagedWaveClaimedPaths(planMarkdown) {
95
101
  if (cells.every((cell) => /^:?-{3,}:?$/u.test(cell))) {
96
102
  continue;
97
103
  }
98
- const parsedSlice = parseSliceId(cells[0]);
99
- if (!parsedSlice)
104
+ const firstCell = (cells[0] ?? "").replace(/^`|`$/gu, "").trim();
105
+ const parsedSlice = parseSliceId(firstCell);
106
+ const parsedUnit = /^U-(\d+(?:[a-z][a-z0-9]*)?)$/iu.exec(firstCell);
107
+ if (!parsedSlice && !parsedUnit)
100
108
  continue;
101
- const sliceId = parsedSlice.id;
109
+ const sliceId = parsedSlice?.id ?? `S-${parsedUnit[1].toLowerCase()}`;
102
110
  const pathsIdx = headerIdx.get("claimedpaths");
103
111
  const rawPaths = pathsIdx !== undefined ? (cells[pathsIdx] ?? "") : "";
104
112
  const claimedPaths = rawPaths.length === 0
@@ -175,7 +183,10 @@ export async function runWaveStatus(projectRoot, options = {}) {
175
183
  waveId: null,
176
184
  readyToDispatch: [],
177
185
  pathConflicts: [],
178
- mode: "none"
186
+ mode: "none",
187
+ topology: "none",
188
+ topologyReason: "wave plan could not be parsed",
189
+ maxBuilders: 0
179
190
  },
180
191
  warnings: [
181
192
  `wave_plan_parse_error: ${err instanceof Error ? err.message : String(err)}`
@@ -203,7 +214,10 @@ export async function runWaveStatus(projectRoot, options = {}) {
203
214
  waveId: null,
204
215
  readyToDispatch: [],
205
216
  pathConflicts: [],
206
- mode: "none"
217
+ mode: "none",
218
+ topology: "none",
219
+ topologyReason: "wave plan sources conflict",
220
+ maxBuilders: 0
207
221
  },
208
222
  warnings: [
209
223
  `wave_plan_merge_conflict: ${err instanceof Error ? err.message : String(err)}`
@@ -331,12 +345,19 @@ export async function runWaveStatus(projectRoot, options = {}) {
331
345
  warnings.push("wave_plan_managed_block_missing: <!-- parallel-exec-managed-start --> block not found in 05-plan.md and wave-plans/ has no parseable wave files.");
332
346
  }
333
347
  let nextDispatch;
348
+ const config = await readConfig(projectRoot).catch(() => null);
349
+ const configuredTopology = resolveExecutionTopology(config);
350
+ const strictness = resolveExecutionStrictness(config);
351
+ const maxBuilders = resolveMaxBuilders(config);
334
352
  if (firstOpenWave === null) {
335
353
  nextDispatch = {
336
354
  waveId: null,
337
355
  readyToDispatch: [],
338
356
  pathConflicts: [],
339
- mode: "none"
357
+ mode: "none",
358
+ topology: "none",
359
+ topologyReason: "no open wave has ready units",
360
+ maxBuilders
340
361
  };
341
362
  }
342
363
  else {
@@ -350,11 +371,28 @@ export async function runWaveStatus(projectRoot, options = {}) {
350
371
  : readyToDispatch.length === 1
351
372
  ? "single-slice"
352
373
  : "none";
374
+ const topologyDecision = mode === "none"
375
+ ? null
376
+ : routeExecutionTopology({
377
+ configuredTopology,
378
+ strictness,
379
+ maxBuilders,
380
+ shape: {
381
+ unitCount: readyToDispatch.length,
382
+ independentUnitCount: conflicts.length > 0 ? 0 : readyToDispatch.length,
383
+ substantialUnitCount: readyToDispatch.length,
384
+ hasPathConflicts: conflicts.length > 0,
385
+ inlineSafe: false
386
+ }
387
+ });
353
388
  nextDispatch = {
354
389
  waveId: firstOpenWave.waveId,
355
390
  readyToDispatch,
356
391
  pathConflicts: conflicts,
357
- mode
392
+ mode,
393
+ topology: topologyDecision?.topology ?? "none",
394
+ topologyReason: topologyDecision?.reason ?? "no ready units",
395
+ maxBuilders: topologyDecision?.maxBuilders ?? maxBuilders
358
396
  };
359
397
  }
360
398
  return {
@@ -382,6 +420,7 @@ function formatHumanReport(report) {
382
420
  }
383
421
  lines.push(`nextDispatch: wave=${report.nextDispatch.waveId ?? "(none)"} ` +
384
422
  `mode=${report.nextDispatch.mode} ` +
423
+ `topology=${report.nextDispatch.topology} ` +
385
424
  `ready=[${report.nextDispatch.readyToDispatch.join(",")}]`);
386
425
  if (report.warnings.length > 0) {
387
426
  lines.push("warnings:");
package/dist/types.d.ts CHANGED
@@ -163,6 +163,10 @@ export interface ReviewLoopConfig {
163
163
  export type VcsMode = "git-with-remote" | "git-local-only" | "none";
164
164
  export type TddCommitMode = "managed-per-slice" | "agent-required" | "checkpoint-only" | "off";
165
165
  export type TddIsolationMode = "worktree" | "in-place" | "auto";
166
+ export type ExecutionTopology = "auto" | "inline" | "single-builder" | "parallel-builders" | "strict-micro";
167
+ export type ExecutionStrictnessProfile = "fast" | "balanced" | "strict";
168
+ export type PlanSliceGranularity = "feature-atomic" | "strict-micro";
169
+ export type PlanMicroTaskPolicy = "advisory" | "strict";
166
170
  /**
167
171
  * 7.6.0 — what slice-commit does when a manifest in the slice's
168
172
  * claim drifts its corresponding lockfile twin (e.g. Cargo.toml +
@@ -206,11 +210,52 @@ export interface TddConfig {
206
210
  */
207
211
  lockfileTwinPolicy?: LockfileTwinPolicy;
208
212
  }
213
+ export interface ExecutionConfig {
214
+ /**
215
+ * 7.7.0 — adaptive execution topology for implementation units.
216
+ *
217
+ * - `auto` (default): choose the cheapest safe route from plan shape.
218
+ * - `inline`: controller may execute a low-risk unit inline while still
219
+ * recording RED/GREEN/REFACTOR evidence.
220
+ * - `single-builder`: one slice-builder owns a feature-atomic unit.
221
+ * - `parallel-builders`: fan out independent substantial units only.
222
+ * - `strict-micro`: preserve the pre-7.7 posture where each tiny task is
223
+ * its own schedulable slice.
224
+ */
225
+ topology?: ExecutionTopology;
226
+ /**
227
+ * 7.7.0 — default calibration for topology and plan-shape advisories.
228
+ * `balanced` is the default: prefer feature-atomic units with internal
229
+ * 2-5 minute TDD steps, warning on microtask-only plans without blocking.
230
+ */
231
+ strictness?: ExecutionStrictnessProfile;
232
+ /**
233
+ * 7.7.0 — upper bound for simultaneous slice-builder workers when the
234
+ * router selects `parallel-builders`.
235
+ */
236
+ maxBuilders?: number;
237
+ }
238
+ export interface PlanConfig {
239
+ /**
240
+ * 7.7.0 — default schedulable surface for plan authoring.
241
+ * - feature-atomic: U-* slices contain internal 2-5 minute TDD steps.
242
+ * - strict-micro: one tiny task can remain one schedulable slice.
243
+ */
244
+ sliceGranularity?: PlanSliceGranularity;
245
+ /**
246
+ * 7.7.0 — how strongly the plan linter reacts to microtask-only plans.
247
+ * `advisory` warns in fast/balanced execution; `strict` treats strict
248
+ * microtask planning as intentional.
249
+ */
250
+ microTaskPolicy?: PlanMicroTaskPolicy;
251
+ }
209
252
  export interface CclawConfig {
210
253
  version: string;
211
254
  flowVersion: string;
212
255
  harnesses: HarnessId[];
213
256
  tdd?: TddConfig;
257
+ execution?: ExecutionConfig;
258
+ plan?: PlanConfig;
214
259
  }
215
260
  export interface TransitionRule {
216
261
  from: FlowStage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "7.6.0",
3
+ "version": "7.7.0",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {