cclaw-cli 0.46.10 → 0.46.12

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.
@@ -114,7 +114,8 @@ function tokensFromRule(rule) {
114
114
  "FINALIZE_MERGE_LOCAL",
115
115
  "FINALIZE_OPEN_PR",
116
116
  "FINALIZE_KEEP_BRANCH",
117
- "FINALIZE_DISCARD_BRANCH"
117
+ "FINALIZE_DISCARD_BRANCH",
118
+ "FINALIZE_NO_VCS"
118
119
  ];
119
120
  }
120
121
  if (/final verdict/iu.test(rule)) {
@@ -186,6 +187,9 @@ function getMarkdownTableRows(sectionBody) {
186
187
  const DIAGRAM_ARROW_PATTERN = /(?:<--?>|<?==?>|--?>|->>|=>|-\.->|→|⟶|↦)/u;
187
188
  const DIAGRAM_FAILURE_EDGE_PATTERN = /\b(fail(?:ed|ure)?|error|timeout|fallback|degrad(?:e|ed|ation)|retry|backoff|circuit|unavailable|recover(?:y)?|rescue|mitigat(?:e|ion)|rollback|exception|abort|dead[\s-]?letter|dlq)\b/iu;
188
189
  const DIAGRAM_GENERIC_NODE_PATTERN = /\b(service|component|module|system)\s*(?:[A-Z0-9])?\b/iu;
190
+ const TEST_COMMAND_MARKER_PATTERN = /\b(?:npm|pnpm|yarn|bun|vitest|jest|pytest|go test|cargo test|mvn test|gradle test|dotnet test)\b/iu;
191
+ const RED_FAILURE_MARKER_PATTERN = /\b(?:fail|failed|failing|assertionerror|cannot find|exception|error|exit code\s*[:=]?\s*[1-9])\b/iu;
192
+ const GREEN_SUCCESS_MARKER_PATTERN = /\b(?:pass|passed|green|ok|0 failed|exit code\s*[:=]?\s*0)\b/iu;
189
193
  function diagramEdgeLines(sectionBody) {
190
194
  return sectionBody
191
195
  .split(/\r?\n/)
@@ -219,6 +223,80 @@ function hasSyncDiagramEdge(lines) {
219
223
  return !/-\.->|-->>|~~>/u.test(line);
220
224
  });
221
225
  }
226
+ function validateTddRedEvidence(sectionBody) {
227
+ const meaningful = meaningfulLineCount(sectionBody);
228
+ if (meaningful < 2) {
229
+ return {
230
+ ok: false,
231
+ details: "RED Evidence must include at least 2 meaningful lines (command plus failing output context)."
232
+ };
233
+ }
234
+ if (!TEST_COMMAND_MARKER_PATTERN.test(sectionBody)) {
235
+ return {
236
+ ok: false,
237
+ details: "RED Evidence must include the test command that produced the failure."
238
+ };
239
+ }
240
+ if (!RED_FAILURE_MARKER_PATTERN.test(sectionBody)) {
241
+ return {
242
+ ok: false,
243
+ details: "RED Evidence must include explicit failing output markers (FAIL/FAILED/AssertionError/exit code != 0)."
244
+ };
245
+ }
246
+ return {
247
+ ok: true,
248
+ details: "RED Evidence includes command + failing output markers."
249
+ };
250
+ }
251
+ function validateTddGreenEvidence(sectionBody) {
252
+ const meaningful = meaningfulLineCount(sectionBody);
253
+ if (meaningful < 2) {
254
+ return {
255
+ ok: false,
256
+ details: "GREEN Evidence must include at least 2 meaningful lines (command and passing result)."
257
+ };
258
+ }
259
+ if (!TEST_COMMAND_MARKER_PATTERN.test(sectionBody)) {
260
+ return {
261
+ ok: false,
262
+ details: "GREEN Evidence must include the full-suite test command."
263
+ };
264
+ }
265
+ if (!GREEN_SUCCESS_MARKER_PATTERN.test(sectionBody)) {
266
+ return {
267
+ ok: false,
268
+ details: "GREEN Evidence must include explicit passing markers (PASS/PASSED/OK/exit code 0)."
269
+ };
270
+ }
271
+ return {
272
+ ok: true,
273
+ details: "GREEN Evidence includes command + passing output markers."
274
+ };
275
+ }
276
+ function validateVerificationLadder(sectionBody) {
277
+ if (!/highest tier reached/iu.test(sectionBody)) {
278
+ return {
279
+ ok: false,
280
+ details: "Verification Ladder must include a 'Highest tier reached' line."
281
+ };
282
+ }
283
+ if (!/\b(static|command|behavioral|human)\b/iu.test(sectionBody)) {
284
+ return {
285
+ ok: false,
286
+ details: "Verification Ladder must name a tier (static | command | behavioral | human)."
287
+ };
288
+ }
289
+ if (!/\b(evidence|command|sha|commit)\b/iu.test(sectionBody)) {
290
+ return {
291
+ ok: false,
292
+ details: "Verification Ladder must include evidence details (command output or commit SHA)."
293
+ };
294
+ }
295
+ return {
296
+ ok: true,
297
+ details: "Verification Ladder includes tier + evidence fields."
298
+ };
299
+ }
222
300
  const LEARNING_TYPE_SET = new Set(["rule", "pattern", "lesson", "compound"]);
223
301
  const LEARNING_CONFIDENCE_SET = new Set(["high", "medium", "low"]);
224
302
  const LEARNING_UNIVERSALITY_SET = new Set(["project", "personal", "universal"]);
@@ -594,6 +672,15 @@ function validateSectionBody(sectionBody, rule, sectionName) {
594
672
  }
595
673
  }
596
674
  const sectionNameNormalized = normalizeHeadingTitle(sectionName).toLowerCase();
675
+ if (sectionNameNormalized === "red evidence") {
676
+ return validateTddRedEvidence(sectionBody);
677
+ }
678
+ if (sectionNameNormalized === "green evidence") {
679
+ return validateTddGreenEvidence(sectionBody);
680
+ }
681
+ if (sectionNameNormalized === "verification ladder") {
682
+ return validateVerificationLadder(sectionBody);
683
+ }
597
684
  if (sectionNameNormalized === "architecture diagram") {
598
685
  const edgeLines = diagramEdgeLines(sectionBody);
599
686
  if (edgeLines.length === 0) {
@@ -29,7 +29,7 @@ export const HOOK_EVENTS_BY_HARNESS = {
29
29
  pre_tool_workflow_guard: "plugin tool.execute.before -> workflow-guard.sh",
30
30
  post_tool_context_monitor: "plugin tool.execute.after -> context-monitor.sh",
31
31
  stop_checkpoint: "plugin session.idle -> stop-checkpoint.sh",
32
- precompact_digest: "plugin session.cleared/session.resumed hooks"
32
+ precompact_digest: "plugin session.compacted -> pre-compact.sh"
33
33
  },
34
34
  codex: {
35
35
  // Codex CLI v0.114+ exposes lifecycle hooks via `.codex/hooks.json`,
@@ -46,6 +46,7 @@ This is the only progression command the user needs to drive the entire flow. St
46
46
  7. Let \`M\` = \`mandatoryDelegations\` for \`currentStage\`.
47
47
  8. If \`M\` is non-empty, inspect **\`${delegationPath}\`**. Treat as satisfied only if each mandatory agent is **completed** or **waived**.
48
48
  9. If any mandatory delegation is missing and no waiver exists: **STOP** and ask the user whether to dispatch now or waive with rationale. Do not mark gates passed while delegation is unresolved.
49
+ 10. If \`currentStage === "review"\` and \`catalog.blocked\` includes \`review_criticals_resolved\`, treat this as a hard remediation branch: recommend \`/cc-ops rewind tdd "review_blocked_by_critical"\` with the blocking finding IDs, and do not attempt to advance toward ship.
49
50
 
50
51
  ### Path A: Current stage is NOT complete (any gate unmet or delegation missing)
51
52
 
@@ -166,6 +167,8 @@ Load the current stage's skill and command contract:
166
167
 
167
168
  Execute the stage protocol. The stage skill handles interaction, STOP points, gate tracking, and stage completion via \`bash .cclaw/hooks/stage-complete.sh <stage>\` (canonical flow-state mutation path).
168
169
 
170
+ Special-case for review: if \`review_criticals_resolved\` is in \`blocked\`, route to rework instead of looping review forever — recommend \`/cc-ops rewind tdd "review_blocked_by_critical"\`.
171
+
169
172
  **Path B — stage IS complete (all gates met, all delegations done):**
170
173
 
171
174
  If \`next\` is \`done\`:
@@ -240,23 +240,12 @@ export default function cclawPlugin(ctx) {
240
240
  // the system transform hook instead.
241
241
  refreshBootstrapCache();
242
242
  }
243
+ if (eventType === "session.compacted") {
244
+ await runHookScript("pre-compact.sh", eventData ?? {});
245
+ }
243
246
  if (eventType === "session.idle") {
244
247
  await runHookScript("stop-checkpoint.sh", { loop_count: 0 });
245
248
  }
246
- if (eventType === "tool.execute.before") {
247
- const toolPayload = normalizeToolPayload(eventData, undefined);
248
- const promptOk = await runHookScript("prompt-guard.sh", toolPayload);
249
- const workflowOk = await runHookScript("workflow-guard.sh", toolPayload);
250
- if (!promptOk || !workflowOk) {
251
- throw new Error(
252
- "cclaw OpenCode guard blocked tool.execute.before (prompt/workflow guard non-zero exit)."
253
- );
254
- }
255
- }
256
- if (eventType === "tool.execute.after") {
257
- const toolPayload = normalizeToolPayload(eventData, undefined);
258
- await runHookScript("context-monitor.sh", toolPayload);
259
- }
260
249
  },
261
250
  "tool.execute.before": async (input, output) => {
262
251
  const payload = normalizeToolPayload(input, output);
@@ -17,7 +17,7 @@ export function retroCommandContract() {
17
17
 
18
18
  Auto-triggered retrospective after ship. \`/cc-next\` drafts \`${retroArtifactPath()}\`
19
19
  from run artifacts and knowledge, then asks the user exactly ONE structured
20
- question: **edit / accept / skip**. Default = accept.
20
+ question: **edit / accept / skip / rewind_for_fix**. Default = accept.
21
21
 
22
22
  This command is normally invoked indirectly by \`/cc-next\` when
23
23
  \`closeout.shipSubstate === "retro_review"\`. Invoking it directly is still
@@ -55,7 +55,8 @@ in the structured ask; there is no \`--skip\` flag.
55
55
  to a plain-text lettered list when the tool is hidden or errors):
56
56
  - \`accept\` (default) — keep the draft as-is,
57
57
  - \`edit\` — user edits \`${retroArtifactPath()}\` in-place, then re-runs \`/cc-next\`,
58
- - \`skip\` — record \`retroSkipped: true\` + one-line reason, no compound entry required.
58
+ - \`skip\` — record \`retroSkipped: true\` + one-line reason, no compound entry required,
59
+ - \`rewind_for_fix\` — route back to \`plan\` / \`tdd\` / \`review\` with a non-empty reason.
59
60
  6. On **accept**:
60
61
  - append >=1 strict-schema JSONL line to \`${knowledgePath()}\` with
61
62
  \`type: "compound"\`, \`source: "retro"\`, and \`stage: null\`,
@@ -73,7 +74,12 @@ in the structured ask; there is no \`--skip\` flag.
73
74
  - set \`retro.completedAt = <ISO>\` (marks gate satisfied for archive), and
74
75
  \`retro.compoundEntries = 0\`,
75
76
  - set \`closeout.shipSubstate = "compound_review"\`.
76
- 9. Emit a one-line summary: \`retro: accepted|edited|skipped | next: /cc-next\`.
77
+ 9. On **rewind_for_fix**:
78
+ - require \`targetStage\` in \`{ plan, tdd, review }\`,
79
+ - require a concise rationale (min 20 chars),
80
+ - instruct \`/cc-ops rewind <targetStage> "<reason>"\`,
81
+ - reset closeout progression by setting \`closeout.shipSubstate = "idle"\`.
82
+ 10. Emit a one-line summary: \`retro: accepted|edited|skipped|rewind_for_fix | next: /cc-next\`.
77
83
 
78
84
  ## Primary skill
79
85
 
@@ -83,7 +89,7 @@ in the structured ask; there is no \`--skip\` flag.
83
89
  export function retroCommandSkillMarkdown() {
84
90
  return `---
85
91
  name: ${RETRO_SKILL_NAME}
86
- description: "Auto-drafted retrospective with a single structured accept/edit/skip ask. Triggered from /cc-next when shipSubstate=retro_review."
92
+ description: "Auto-drafted retrospective with a single structured accept/edit/skip/rewind_for_fix ask. Triggered from /cc-next when shipSubstate=retro_review."
87
93
  ---
88
94
 
89
95
  # /cc-ops retro
@@ -119,6 +125,7 @@ Do not silently skip. Do not finalize without updating \`flow-state.json\`.
119
125
  > - **accept** — keep the draft and continue.
120
126
  > - **edit** — I'll edit it, then re-run \`/cc-next\`.
121
127
  > - **skip** — no retro this run (requires one-line reason).
128
+ > - **rewind_for_fix** — route back to plan/tdd/review because post-ship issues were found.
122
129
 
123
130
  4. Apply the state transition for the chosen option:
124
131
  - \`accept\` → append \`{ "type": "compound", "source": "retro", "stage": null, ... }\` line
@@ -128,11 +135,15 @@ Do not silently skip. Do not finalize without updating \`flow-state.json\`.
128
135
  - \`skip\` → set \`closeout.retroSkipped\`, \`closeout.retroSkipReason\`,
129
136
  \`closeout.retroAcceptedAt\`, \`retro.completedAt\`,
130
137
  \`retro.compoundEntries = 0\`; set \`closeout.shipSubstate = "compound_review"\`.
138
+ - \`rewind_for_fix\` → require \`targetStage ∈ {plan,tdd,review}\` and
139
+ reason (>=20 chars), then instruct \`/cc-ops rewind <targetStage> "<reason>"\`
140
+ and set \`closeout.shipSubstate = "idle"\` to restart closeout after rework.
131
141
 
132
142
  5. Print one-line completion summary:
133
143
  - \`retro gate: accepted (<N> compound entries)\`
134
144
  - \`retro gate: skipped (reason: <text>)\`
135
145
  - \`retro gate: editing (re-run /cc-next when ready)\`
146
+ - \`retro gate: rewind_for_fix (target=<stage>)\`
136
147
 
137
148
  ## Resume semantics
138
149
 
@@ -1,8 +1,8 @@
1
- import type { FlowStage } from "../types.js";
1
+ import type { FlowStage, FlowTrack } from "../types.js";
2
2
  /**
3
3
  * Long-form Batch Execution walkthrough. Rendered once into
4
4
  * \`.cclaw/references/stages/tdd-batch-walkthrough.md\` by the installer.
5
5
  */
6
6
  export declare const TDD_BATCH_WALKTHROUGH_MARKDOWN = "# TDD \u2014 Batch Execution Walkthrough\n\nDetailed RED / GREEN / REFACTOR transcript for a 3-task batch. Illustrative\nonly \u2014 do not copy the command names blindly, match them to your stack.\n\n## Batch 1 example tasks\n\n| Task ID | Description | AC | Verification |\n|---|---|---|---|\n| T-1 `[~3m]` | Add `User.emailNormalized` column | AC-1 | `npm test -- users/schema` |\n| T-2 `[~4m]` | Normalize on write in `UserRepo.save` | AC-1 | `npm test -- users/repo` |\n| T-3 `[~3m]` | Reject duplicates in `UserService.signup` | AC-2 | `npm test -- users/service` |\n\n## Execution transcript\n\n### T-1 \u2014 RED\n\n> Run: `npm test -- users/schema` \u2192 **FAIL** (missing column: `emailNormalized`). Captured the failure stack as RED evidence. No production code touched yet.\n\n### T-1 \u2014 GREEN\n\n> Added the column in the schema module. Re-ran `npm test -- users/schema` \u2192 **PASS**. Ran the full suite `npm test` \u2192 **PASS**. Captured both outputs as GREEN evidence.\n\n### T-1 \u2014 REFACTOR\n\n> Extracted the column definition into a shared `NormalizedEmail` type used by T-2/T-3. Re-ran `npm test` \u2192 **PASS**. Captured REFACTOR note: \"Extracted NormalizedEmail type to keep T-2/T-3 DRY; zero behavior change, all tests still green.\"\n\n### T-2 \u2014 RED / GREEN / REFACTOR\n\nWrite the repo test that expects normalised writes, watch it fail (RED), implement normalisation inside `UserRepo.save` only (GREEN), then refactor the normaliser out of the repo into a helper shared with T-3 (REFACTOR).\n\n### T-3 \u2014 RED / GREEN / REFACTOR\n\nWrite the service-level duplicate test that expects a rejection, watch it fail (RED), add the duplicate check in `UserService.signup` (GREEN), refactor the error message into a named constant (REFACTOR).\n\n## Batch gate check\n\nAfter T-3 REFACTOR, before declaring Batch 1 done:\n\n1. Run the full suite (`npm test`) one final time \u2192 **PASS** captured as batch-exit evidence.\n2. Verify the TDD artifact contains RED, GREEN, and REFACTOR evidence for T-1, T-2, **and** T-3. No partial batches.\n3. Only now mark Batch 1 complete. Batch 2 cannot start until this step.\n\n## When to stop mid-batch (do NOT push through)\n\n- A RED test fails for a reason you did not predict (e.g. an unrelated flaky test) \u2192 **pause**, diagnose, log an operational-self-improvement entry, and decide with the user before proceeding.\n- A GREEN step would require touching code outside the task's acceptance criterion \u2192 **pause**, the task is scoped wrong; adjust the plan or open a follow-up task.\n- The same RED failure reappears after a GREEN change \u2192 **escalate** per the 3-attempts rule; do not keep patching.\n";
7
7
  export declare function stageSkillFolder(stage: FlowStage): string;
8
- export declare function stageSkillMarkdown(stage: FlowStage): string;
8
+ export declare function stageSkillMarkdown(stage: FlowStage, track?: FlowTrack): string;
@@ -5,8 +5,8 @@ import { stageAutoSubagentDispatch, stageSchema } from "./stage-schema.js";
5
5
  const VERIFICATION_STAGES = ["tdd", "review", "ship"];
6
6
  const DECISION_PROTOCOL_PATH = `${RUNTIME_ROOT}/references/protocols/decision.md`;
7
7
  const COMPLETION_PROTOCOL_PATH = `${RUNTIME_ROOT}/references/protocols/completion.md`;
8
- function whenNotToUseBlock(stage) {
9
- const schema = stageSchema(stage);
8
+ function whenNotToUseBlock(stage, track) {
9
+ const schema = stageSchema(stage, track);
10
10
  if (schema.whenNotToUse.length === 0) {
11
11
  return "";
12
12
  }
@@ -15,8 +15,8 @@ ${schema.whenNotToUse.map((item) => `- ${item}`).join("\n")}
15
15
 
16
16
  `;
17
17
  }
18
- function contextLoadingBlock(stage) {
19
- const trace = stageSchema(stage).crossStageTrace;
18
+ function contextLoadingBlock(stage, track) {
19
+ const trace = stageSchema(stage, track).crossStageTrace;
20
20
  const readLines = trace.readsFrom.length > 0
21
21
  ? trace.readsFrom.map((value) => `- \`${value}\``).join("\n")
22
22
  : "- (first stage — no upstream artifacts)";
@@ -31,7 +31,7 @@ ${readLines}
31
31
  \`.cclaw/knowledge.jsonl\` when the digest is insufficient.
32
32
  `;
33
33
  }
34
- function autoSubagentDispatchBlock(stage) {
34
+ function autoSubagentDispatchBlock(stage, track) {
35
35
  const rules = stageAutoSubagentDispatch(stage);
36
36
  if (rules.length === 0)
37
37
  return "";
@@ -41,7 +41,7 @@ function autoSubagentDispatchBlock(stage) {
41
41
  return `| ${rule.agent} | ${rule.mode} | ${userGate} | ${rule.when} |`;
42
42
  })
43
43
  .join("\n");
44
- const mandatory = stageSchema(stage).mandatoryDelegations;
44
+ const mandatory = stageSchema(stage, track).mandatoryDelegations;
45
45
  const mandatoryList = mandatory.length > 0 ? mandatory.map((a) => `\`${a}\``).join(", ") : "none";
46
46
  const delegationLogRel = `${RUNTIME_ROOT}/state/delegation-log.json`;
47
47
  return `## Automatic Subagent Dispatch
@@ -54,8 +54,8 @@ Mandatory delegations for this stage: ${mandatoryList}.
54
54
  Record completion/waiver in \`${delegationLogRel}\` before stage completion.
55
55
  `;
56
56
  }
57
- function researchPlaybooksBlock(stage) {
58
- const playbooks = stageSchema(stage).researchPlaybooks ?? [];
57
+ function researchPlaybooksBlock(stage, track) {
58
+ const playbooks = stageSchema(stage, track).researchPlaybooks ?? [];
59
59
  if (playbooks.length === 0)
60
60
  return "";
61
61
  const rows = playbooks
@@ -70,8 +70,8 @@ and record outcomes in the stage artifact when relevant.
70
70
  ${rows}
71
71
  `;
72
72
  }
73
- function reviewSectionsBlock(stage) {
74
- const schema = stageSchema(stage);
73
+ function reviewSectionsBlock(stage, track) {
74
+ const schema = stageSchema(stage, track);
75
75
  if (schema.reviewSections.length === 0)
76
76
  return "";
77
77
  const sections = schema.reviewSections
@@ -103,8 +103,8 @@ Reference utility skill:
103
103
  \`.cclaw/skills/verification-before-completion/SKILL.md\`
104
104
  `;
105
105
  }
106
- function batchExecutionModeBlock(stage) {
107
- const schema = stageSchema(stage);
106
+ function batchExecutionModeBlock(stage, track) {
107
+ const schema = stageSchema(stage, track);
108
108
  if (!schema.batchExecutionAllowed)
109
109
  return "";
110
110
  return `## Batch Execution Mode
@@ -118,8 +118,8 @@ Detailed walkthrough:
118
118
  \`.cclaw/${STAGE_EXAMPLES_REFERENCE_DIR}/tdd-batch-walkthrough.md\`
119
119
  `;
120
120
  }
121
- function crossStageTraceBlock(stage) {
122
- const trace = stageSchema(stage).crossStageTrace;
121
+ function crossStageTraceBlock(stage, track) {
122
+ const trace = stageSchema(stage, track).crossStageTrace;
123
123
  const reads = trace.readsFrom.length > 0
124
124
  ? trace.readsFrom.map((r) => `- ${r}`).join("\n")
125
125
  : "- (first stage — no upstream artifacts)";
@@ -137,8 +137,8 @@ ${writes}
137
137
  Rule: ${trace.traceabilityRule}
138
138
  `;
139
139
  }
140
- function artifactValidationBlock(stage) {
141
- const validations = stageSchema(stage).artifactValidation;
140
+ function artifactValidationBlock(stage, track) {
141
+ const validations = stageSchema(stage, track).artifactValidation;
142
142
  if (validations.length === 0)
143
143
  return "";
144
144
  const rows = validations
@@ -206,7 +206,7 @@ function stageSpecificSeeAlso(stage) {
206
206
  };
207
207
  return refs[stage];
208
208
  }
209
- function completionParametersBlock(schema) {
209
+ function completionParametersBlock(schema, track) {
210
210
  const gateList = schema.requiredGates.map((g) => `\`${g.id}\``).join(", ");
211
211
  const mandatory = schema.mandatoryDelegations.length > 0
212
212
  ? schema.mandatoryDelegations.map((a) => `\`${a}\``).join(", ")
@@ -214,7 +214,7 @@ function completionParametersBlock(schema) {
214
214
  const nextStage = schema.next === "done" ? "done" : schema.next;
215
215
  const nextDescription = schema.next === "done"
216
216
  ? "flow complete"
217
- : stageSchema(schema.next).skillDescription;
217
+ : stageSchema(schema.next, track).skillDescription;
218
218
  return `## Completion Parameters
219
219
 
220
220
  - \`stage\`: \`${schema.stage}\`
@@ -230,8 +230,8 @@ Apply shared completion logic from:
230
230
  \`${COMPLETION_PROTOCOL_PATH}\`
231
231
  `;
232
232
  }
233
- function quickStartBlock(stage) {
234
- const schema = stageSchema(stage);
233
+ function quickStartBlock(stage, track) {
234
+ const schema = stageSchema(stage, track);
235
235
  const gatePreview = schema.requiredGates.slice(0, 3).map((g) => `\`${g.id}\``).join(", ");
236
236
  return `## Quick Start
237
237
 
@@ -297,8 +297,8 @@ After T-3 REFACTOR, before declaring Batch 1 done:
297
297
  export function stageSkillFolder(stage) {
298
298
  return stageSchema(stage).skillFolder;
299
299
  }
300
- export function stageSkillMarkdown(stage) {
301
- const schema = stageSchema(stage);
300
+ export function stageSkillMarkdown(stage, track = "standard") {
301
+ const schema = stageSchema(stage, track);
302
302
  const gateList = schema.requiredGates
303
303
  .map((g) => `- \`${g.id}\` — ${g.description}`)
304
304
  .join("\n");
@@ -324,7 +324,7 @@ If you are about to violate the Iron Law, STOP. No amount of urgency, partial pr
324
324
 
325
325
  </EXTREMELY-IMPORTANT>
326
326
 
327
- ${quickStartBlock(stage)}
327
+ ${quickStartBlock(stage, track)}
328
328
 
329
329
  ## Overview
330
330
  ${schema.purpose}
@@ -332,16 +332,16 @@ ${schema.purpose}
332
332
  ## When to Use
333
333
  ${schema.whenToUse.map((item) => `- ${item}`).join("\n")}
334
334
 
335
- ${whenNotToUseBlock(stage)}
335
+ ${whenNotToUseBlock(stage, track)}
336
336
  ## Inputs
337
337
  ${schema.inputs.length > 0 ? schema.inputs.map((item) => `- ${item}`).join("\n") : "- (first stage — no required inputs)"}
338
338
 
339
339
  ## Required Context
340
340
  ${schema.requiredContext.length > 0 ? schema.requiredContext.map((item) => `- ${item}`).join("\n") : "- None beyond this skill"}
341
341
 
342
- ${contextLoadingBlock(stage)}
343
- ${autoSubagentDispatchBlock(stage)}
344
- ${researchPlaybooksBlock(stage)}
342
+ ${contextLoadingBlock(stage, track)}
343
+ ${autoSubagentDispatchBlock(stage, track)}
344
+ ${researchPlaybooksBlock(stage, track)}
345
345
 
346
346
  ## Outputs
347
347
  ${schema.outputs.map((item) => `- ${item}`).join("\n")}
@@ -365,7 +365,7 @@ ${schema.interactionProtocol.map((item, i) => `${i + 1}. ${item}`).join("\n")}
365
365
  Shared decision/ask-user protocol:
366
366
  \`${DECISION_PROTOCOL_PATH}\`
367
367
 
368
- ${batchExecutionModeBlock(stage)}
368
+ ${batchExecutionModeBlock(stage, track)}
369
369
  ## Required Gates
370
370
  ${gateList}
371
371
 
@@ -375,10 +375,10 @@ ${evidenceList}
375
375
  ## Process
376
376
  ${schema.process.map((item, i) => `${i + 1}. ${item}`).join("\n")}
377
377
 
378
- ${reviewSectionsBlock(stage)}
378
+ ${reviewSectionsBlock(stage, track)}
379
379
  ${verificationBlock(stage)}
380
- ${crossStageTraceBlock(stage)}
381
- ${artifactValidationBlock(stage)}
380
+ ${crossStageTraceBlock(stage, track)}
381
+ ${artifactValidationBlock(stage, track)}
382
382
 
383
383
  ## Anti-Patterns & Red Flags
384
384
  ${mergedAntiPatterns(schema)}
@@ -386,7 +386,7 @@ ${mergedAntiPatterns(schema)}
386
386
  ## Verification
387
387
  ${schema.exitCriteria.map((item) => `- [ ] ${item}`).join("\n")}
388
388
 
389
- ${completionParametersBlock(schema)}
389
+ ${completionParametersBlock(schema, track)}
390
390
  ## Shared Stage Guidance
391
391
  See:
392
392
  - \`${STAGE_COMMON_GUIDANCE_REL_PATH}\`
@@ -1,13 +1,13 @@
1
- import type { FlowStage, TransitionRule } from "../types.js";
1
+ import type { FlowStage, FlowTrack, TransitionRule } from "../types.js";
2
2
  import type { StageAutoSubagentDispatch, StageSchema } from "./stages/schema-types.js";
3
3
  export type { ArtifactValidation, CrossStageTrace, ReviewSection, StageAutoSubagentDispatch, StageGate, StageSchema, StageSchemaInput } from "./stages/schema-types.js";
4
4
  /** Transition guard: agents with `mode: "mandatory"` in auto-subagent dispatch for this stage. */
5
5
  export declare function mandatoryDelegationsForStage(stage: FlowStage): string[];
6
- export declare function stageSchema(stage: FlowStage): StageSchema;
7
- export declare function orderedStageSchemas(): StageSchema[];
8
- export declare function stageGateIds(stage: FlowStage): string[];
9
- export declare function stageRecommendedGateIds(stage: FlowStage): string[];
6
+ export declare function stageSchema(stage: FlowStage, track?: FlowTrack): StageSchema;
7
+ export declare function orderedStageSchemas(track?: FlowTrack): StageSchema[];
8
+ export declare function stageGateIds(stage: FlowStage, track?: FlowTrack): string[];
9
+ export declare function stageRecommendedGateIds(stage: FlowStage, track?: FlowTrack): string[];
10
10
  export declare function nextCclawCommand(stage: FlowStage): string;
11
11
  export declare function buildTransitionRules(): TransitionRule[];
12
- export declare function stagePolicyNeedles(stage: FlowStage): string[];
12
+ export declare function stagePolicyNeedles(stage: FlowStage, track?: FlowTrack): string[];
13
13
  export declare function stageAutoSubagentDispatch(stage: FlowStage): StageAutoSubagentDispatch[];
@@ -1,16 +1,6 @@
1
1
  import { COMMAND_FILE_ORDER } from "../constants.js";
2
2
  import { BRAINSTORM, SCOPE, DESIGN, SPEC, PLAN, TDD, REVIEW, SHIP } from "./stages/index.js";
3
- // ---------------------------------------------------------------------------
4
- // NOTE: The former QUESTION_FORMAT_SPEC / ERROR_BUDGET_SPEC exports were
5
- // hoisted into `src/content/meta-skill.ts` (Shared Decision + Tool-Use
6
- // Protocol). They are no longer re-exported from here to avoid duplication
7
- // and drift. Stage skills cite the meta-skill by path instead.
8
- // ---------------------------------------------------------------------------
9
- /**
10
- * Gate tiers:
11
- * - required: blocking for stage completion.
12
- * - recommended: quality signal; unmet -> DONE_WITH_CONCERNS, not BLOCKED.
13
- */
3
+ import { tddStageForTrack } from "./stages/tdd.js";
14
4
  const REQUIRED_GATE_IDS = {
15
5
  brainstorm: [
16
6
  "brainstorm_approaches_compared",
@@ -39,17 +29,19 @@ const REQUIRED_GATE_IDS = {
39
29
  "plan_acceptance_mapped",
40
30
  "plan_wait_for_confirm"
41
31
  ],
42
- tdd: [
32
+ tdd: (track) => [
43
33
  "tdd_red_test_written",
44
34
  "tdd_green_full_suite",
45
35
  "tdd_refactor_completed",
46
- "tdd_verified_before_complete"
36
+ "tdd_verified_before_complete",
37
+ ...(track === "quick" ? [] : ["tdd_traceable_to_plan"])
47
38
  ],
48
- review: [
39
+ review: (track) => [
49
40
  "review_layer1_spec_compliance",
50
41
  "review_layer2_security",
51
42
  "review_criticals_resolved",
52
- "review_army_json_valid"
43
+ "review_army_json_valid",
44
+ ...(track === "quick" ? [] : ["review_trace_matrix_clean"])
53
45
  ],
54
46
  ship: [
55
47
  "ship_review_verdict_valid",
@@ -64,12 +56,16 @@ const REQUIRED_ARTIFACT_SECTIONS = {
64
56
  design: ["Architecture Boundaries", "Architecture Diagram", "Failure Mode Table", "Completion Dashboard"],
65
57
  spec: ["Acceptance Criteria", "Edge Cases", "Testability Map", "Approval"],
66
58
  plan: ["Task List", "Dependency Batches", "Acceptance Mapping", "WAIT_FOR_CONFIRM"],
67
- tdd: ["RED Evidence", "GREEN Evidence", "REFACTOR Notes", "Traceability"],
59
+ tdd: ["RED Evidence", "GREEN Evidence", "REFACTOR Notes", "Traceability", "Verification Ladder"],
68
60
  review: ["Layer 1 Verdict", "Review Army Contract", "Severity Summary", "Final Verdict"],
69
61
  ship: ["Preflight Results", "Release Notes", "Rollback Plan", "Finalization"]
70
62
  };
71
- function tieredStageGates(stage, gates) {
72
- const requiredSet = new Set(REQUIRED_GATE_IDS[stage]);
63
+ function resolveRequiredGateIds(stage, track) {
64
+ const raw = REQUIRED_GATE_IDS[stage];
65
+ return typeof raw === "function" ? raw(track) : raw;
66
+ }
67
+ function tieredStageGates(stage, gates, track) {
68
+ const requiredSet = new Set(resolveRequiredGateIds(stage, track));
73
69
  return gates.map((gate) => {
74
70
  return {
75
71
  ...gate,
@@ -233,9 +229,9 @@ export function mandatoryDelegationsForStage(stage) {
233
229
  .filter((d) => d.mode === "mandatory")
234
230
  .map((d) => d.agent);
235
231
  }
236
- export function stageSchema(stage) {
237
- const base = STAGE_SCHEMA_MAP[stage];
238
- const tieredGates = tieredStageGates(stage, base.requiredGates);
232
+ export function stageSchema(stage, track = "standard") {
233
+ const base = stage === "tdd" ? tddStageForTrack(track) : STAGE_SCHEMA_MAP[stage];
234
+ const tieredGates = tieredStageGates(stage, base.requiredGates, track);
239
235
  const tieredValidation = tieredArtifactValidation(stage, base.artifactValidation);
240
236
  return {
241
237
  ...base,
@@ -244,16 +240,16 @@ export function stageSchema(stage) {
244
240
  mandatoryDelegations: mandatoryDelegationsForStage(stage)
245
241
  };
246
242
  }
247
- export function orderedStageSchemas() {
248
- return COMMAND_FILE_ORDER.map((stage) => stageSchema(stage));
243
+ export function orderedStageSchemas(track = "standard") {
244
+ return COMMAND_FILE_ORDER.map((stage) => stageSchema(stage, track));
249
245
  }
250
- export function stageGateIds(stage) {
251
- return stageSchema(stage).requiredGates
246
+ export function stageGateIds(stage, track = "standard") {
247
+ return stageSchema(stage, track).requiredGates
252
248
  .filter((gate) => gate.tier === "required")
253
249
  .map((gate) => gate.id);
254
250
  }
255
- export function stageRecommendedGateIds(stage) {
256
- return stageSchema(stage).requiredGates
251
+ export function stageRecommendedGateIds(stage, track = "standard") {
252
+ return stageSchema(stage, track).requiredGates
257
253
  .filter((gate) => gate.tier === "recommended")
258
254
  .map((gate) => gate.id);
259
255
  }
@@ -273,10 +269,16 @@ export function buildTransitionRules() {
273
269
  guards: stageGateIds(schema.stage)
274
270
  });
275
271
  }
272
+ // Review can explicitly route back to TDD when the verdict is BLOCKED.
273
+ rules.push({
274
+ from: "review",
275
+ to: "tdd",
276
+ guards: ["review_verdict_blocked"]
277
+ });
276
278
  return rules;
277
279
  }
278
- export function stagePolicyNeedles(stage) {
279
- return stageSchema(stage).policyNeedles;
280
+ export function stagePolicyNeedles(stage, track = "standard") {
281
+ return stageSchema(stage, track).policyNeedles;
280
282
  }
281
283
  export function stageAutoSubagentDispatch(stage) {
282
284
  return STAGE_AUTO_SUBAGENT_DISPATCH[stage];
@@ -24,6 +24,7 @@ export const REVIEW = {
24
24
  "Change-Size Check — ~100 lines = normal. ~300 lines = consider splitting. ~1000+ lines = strongly recommend stacked PRs. Flag large diffs to the user.",
25
25
  "Adversarial Trigger Check — compute changed-line count (`git diff --shortstat <base>..HEAD`), files-touched count, and whether trust boundaries changed (auth/secrets/external inputs/permissions). If `lines > 100` OR `files > 10` OR `trust boundary changed`, **dispatch a SECOND reviewer agent with the `adversarial-review` skill loaded** and reconcile its findings into the review army (treat the conditional dispatch as mandatory whenever the trigger holds; record the trigger that fired in the dashboard).",
26
26
  "Load upstream evidence — read TDD artifact (RED + GREEN + REFACTOR), spec, and plan. Verify evidence chain is unbroken.",
27
+ "Run traceability matrix — execute `cclaw internal trace-matrix` (or equivalent helper) and confirm there are no orphaned criteria/tasks/tests before declaring ship readiness.",
27
28
  "Layer 1: Spec Compliance — check every acceptance criterion against implementation. Verdict: pass/fail per criterion.",
28
29
  "Layer 2a: Correctness — logic errors, race conditions, boundary violations, null handling.",
29
30
  "Layer 2b: Security — input validation, auth boundaries, secrets exposure, injection vectors. **Mandatory:** also load and execute the `.cclaw/skills/security-audit/SKILL.md` utility skill (proactive pattern sweep across diff + touched modules, not just the diff itself) and merge findings into the review army. The Layer 2 security pass is not complete until the audit sweep records a finding count (0 acceptable) with file:line evidence for every Critical.",
@@ -34,7 +35,8 @@ export const REVIEW = {
34
35
  "Review Army reconciliation — normalize findings into structured records, dedup by fingerprint, and mark multi-specialist confirmations.",
35
36
  "Meta-Review — Were tests actually run? Do test names match what they test? Are there real assertions?",
36
37
  "Classify findings — Critical (blocks ship), Important (should fix), Suggestion (optional improvement).",
37
- "Produce verdict — APPROVED, APPROVED_WITH_CONCERNS, or BLOCKED."
38
+ "Produce verdict — APPROVED, APPROVED_WITH_CONCERNS, or BLOCKED.",
39
+ "If verdict is BLOCKED, emit remediation route token `ROUTE_BACK_TO_TDD` and include `/cc-ops rewind tdd \"review_blocked_by_critical\"` with the blocking finding IDs."
38
40
  ],
39
41
  interactionProtocol: [
40
42
  "Run Layer 1 (spec compliance) completely before starting Layer 2.",
@@ -42,6 +44,7 @@ export const REVIEW = {
42
44
  "Classify every finding as Critical, Important, or Suggestion.",
43
45
  "For each Critical finding: use the Decision Protocol — present resolution options (A/B/C) with trade-offs, and mark one as (recommended). Do NOT use a numeric Completeness rubric; recommend the option that fully closes the finding with no carry-over risk and the smallest blast radius. If the harness's native structured-ask tool is available (`AskUserQuestion` on Claude, `AskQuestion` on Cursor, `question` on OpenCode with `permission.question: \"allow\"`, `request_user_input` on Codex in Plan/Collaboration mode), send exactly ONE question per call, validate fields against the runtime schema, and on schema error immediately fall back to a plain-text lettered list instead of retrying guessed payloads.",
44
46
  "Resolve all critical blockers before ship.",
47
+ "When verdict is BLOCKED, do not end with a passive stop: explicitly route remediation to TDD via `ROUTE_BACK_TO_TDD` and point to `/cc-ops rewind tdd` with the blocking IDs.",
45
48
  "For final verdict: use the native structured-ask tool (`AskUserQuestion` / `AskQuestion` / `question` / `request_user_input`) only if runtime schema is confirmed; otherwise collect verdict with a plain-text single-choice prompt (APPROVED / APPROVED_WITH_CONCERNS / BLOCKED).",
46
49
  "**STOP.** Do NOT proceed to ship until the user provides an explicit verdict."
47
50
  ],
@@ -53,21 +56,25 @@ export const REVIEW = {
53
56
  "Layer 2d: check architecture fit — design compliance, coupling, interfaces.",
54
57
  "Reconcile multi-agent findings into `.cclaw/artifacts/07-review-army.json` (dedup + confidence + conflict notes).",
55
58
  "Classify and prioritize all findings.",
56
- "Write review report artifact with explicit verdict."
59
+ "Write review report artifact with explicit verdict.",
60
+ "If verdict is BLOCKED, include the remediation route token `ROUTE_BACK_TO_TDD` and the rewind command payload."
57
61
  ],
58
62
  requiredGates: [
59
63
  { id: "review_layer1_spec_compliance", description: "Spec compliance check completed with per-criterion verdict." },
60
64
  { id: "review_layer2_security", description: "Security review completed." },
61
65
  { id: "review_criticals_resolved", description: "No unresolved critical blockers remain." },
62
- { id: "review_army_json_valid", description: "07-review-army.json passes schema validation (validateReviewArmy)." }
66
+ { id: "review_army_json_valid", description: "07-review-army.json passes schema validation (validateReviewArmy)." },
67
+ { id: "review_trace_matrix_clean", description: "Trace matrix has no orphaned criteria/tasks/test slices for the active run." }
63
68
  ],
64
69
  requiredEvidence: [
65
70
  "Artifact written to `.cclaw/artifacts/07-review.md`.",
66
71
  "Artifact written to `.cclaw/artifacts/07-review-army.json`.",
72
+ "Traceability matrix run recorded (no orphaned criteria/tasks/tests for enforced tracks).",
67
73
  "Layer 1 verdict captured with per-criterion pass/fail.",
68
74
  "Layer 2 sections completed with findings.",
69
75
  "Severity log includes critical/important/suggestion buckets.",
70
- "Explicit final verdict: APPROVED, APPROVED_WITH_CONCERNS, or BLOCKED."
76
+ "Explicit final verdict: APPROVED, APPROVED_WITH_CONCERNS, or BLOCKED.",
77
+ "If BLOCKED: include explicit remediation route (`ROUTE_BACK_TO_TDD`) with blocking finding IDs."
71
78
  ],
72
79
  inputs: ["implementation diff", "spec and plan artifacts", "test/build evidence"],
73
80
  requiredContext: ["spec criteria", "tdd artifact", "rulebook constraints"],
@@ -88,15 +95,9 @@ export const REVIEW = {
88
95
  "No severity classification",
89
96
  "Shipping with open criticals",
90
97
  "Batching multiple findings into one report without individual resolution",
91
- "Skipping Layer 2 sections because Layer 1 passed",
92
- "No separate Layer 1/Layer 2 outcomes",
93
- "No structured review-army reconciliation artifact",
94
- "No critical bucket",
95
- "No explicit ready/not-ready verdict",
96
- "Review sections skipped or abbreviated",
97
- "Findings not classified by severity"
98
+ "Skipping Layer 2 sections because Layer 1 passed"
98
99
  ],
99
- policyNeedles: ["Layer 1", "Layer 2", "Critical", "Review Army", "Ready to Ship", "One issue at a time"],
100
+ policyNeedles: ["Layer 1", "Layer 2", "Critical", "Review Army", "Ready to Ship", "ROUTE_BACK_TO_TDD", "One issue at a time"],
100
101
  artifactFile: "07-review.md",
101
102
  next: "ship",
102
103
  reviewSections: [
@@ -205,6 +206,8 @@ export const REVIEW = {
205
206
  { section: "Review Readiness Dashboard", required: false, validationRule: "Includes a per-pass table (Layer 1 / Layer 2 / Adversarial / Schema) with a 'Completed at' column, a Delegation log snapshot block (path .cclaw/state/delegation-log.json with required/completed/waived/pending), a Staleness signal block (commit at last review pass and current commit), and a Headline with open critical blockers + ship recommendation. At minimum, the section text must contain the substrings 'Completed at', 'delegation-log.json', 'commit at last review pass', and 'Ship recommendation'." },
206
207
  { section: "Completeness Score", required: false, validationRule: "Records AC coverage, task coverage, test-slice coverage, and adversarial-review pass status as numeric or boolean values. At minimum, a line like 'AC coverage: N/M' or 'AC coverage: 100%'." },
207
208
  { section: "Incoming Feedback Queue", required: false, validationRule: "When external review feedback exists, include a queue summary with per-item disposition (resolved / accepted-risk / rejected-with-evidence) and evidence refs." },
209
+ { section: "Trace Matrix Check", required: false, validationRule: "Records criteria/tasks/tests orphan counts (all zero on enforced tracks) with command output reference." },
210
+ { section: "Blocked Route", required: false, validationRule: "When Final Verdict is BLOCKED: includes `ROUTE_BACK_TO_TDD`, rewind target `tdd`, and blocked finding IDs." },
208
211
  { section: "Severity Summary", required: true, validationRule: "Per-severity count lines for critical, important, and suggestion buckets." },
209
212
  { section: "Final Verdict", required: true, validationRule: "Exactly one of: APPROVED, APPROVED_WITH_CONCERNS, BLOCKED." }
210
213
  ]
@@ -5,14 +5,15 @@ export const SHIP = {
5
5
  stage: "ship",
6
6
  skillFolder: "shipping-and-handoff",
7
7
  skillName: "shipping-and-handoff",
8
- skillDescription: "Release handoff stage with preflight checks, rollback readiness, and explicit finalization mode.",
9
- hardGate: "Do NOT merge, push, or finalize without a passed preflight check, written rollback plan, and exactly one explicit finalization mode selected. No exceptions for urgency.",
8
+ skillDescription: "Release handoff stage with preflight checks, rollback readiness, and explicit finalization mode for both git and non-git workflows.",
9
+ hardGate: "Do NOT merge, push, or finalize without a passed preflight check, written rollback plan, and exactly one explicit finalization mode selected. No exceptions for urgency. If no VCS is available, use FINALIZE_NO_VCS explicitly instead of inventing git steps.",
10
10
  ironLaw: "NO MERGE WITHOUT GREEN CI, A WRITTEN ROLLBACK, AND EXACTLY ONE SELECTED FINALIZATION MODE.",
11
11
  purpose: "Prepare a safe release handoff with clear rollback and branch finalization decision.",
12
12
  whenToUse: [
13
13
  "After review passes with APPROVED or APPROVED_WITH_CONCERNS verdict",
14
14
  "Before creating PR/merge/final branch action",
15
- "When release notes and rollback plan are required"
15
+ "When release notes and rollback plan are required",
16
+ "When shipping from non-git environments (docs bundles, script drops, detached artifacts)"
16
17
  ],
17
18
  whenNotToUse: [
18
19
  "Review verdict is BLOCKED or unresolved critical findings remain",
@@ -22,20 +23,21 @@ export const SHIP = {
22
23
  checklist: [
23
24
  "Validate upstream gates — verify review verdict is APPROVED or APPROVED_WITH_CONCERNS. If BLOCKED, stop immediately.",
24
25
  "Run preflight checks — tests pass, build succeeds, linter clean, type-check clean, no uncommitted changes. Every check must produce fresh output in this message.",
25
- "Merge-base detection — identify the correct base branch. Run `git merge-base HEAD <base>`. If the base has diverged significantly, flag for rebase-first.",
26
+ "Merge-base detection (git only) — identify the correct base branch. Run `git merge-base HEAD <base>`. If the base has diverged significantly, flag for rebase-first.",
26
27
  "Re-run tests on merged result — if merging locally, run the full test suite AFTER the merge, not just before. Post-merge failures are common.",
27
28
  "Generate release notes — summarize what changed, why, and what it affects. Reference spec criteria. Include: breaking changes, new dependencies, migration steps if any.",
28
29
  "Write rollback plan — trigger conditions (what tells you it is broken), rollback steps (exact commands/git operations), and verification (how to confirm rollback worked).",
29
30
  "Load utility skills — `verification-before-completion` for fresh evidence and `finishing-a-development-branch` for finalization workflow.",
30
31
  "Monitoring checklist — what should be watched after deploy? Error rates, latency, key business metrics. If no monitoring exists, flag it as a risk.",
31
- "Select finalization mode — exactly ONE enum: (A) FINALIZE_MERGE_LOCAL, (B) FINALIZE_OPEN_PR, (C) FINALIZE_KEEP_BRANCH, (D) FINALIZE_DISCARD_BRANCH. For discard: list what will be deleted, require typed confirmation.",
32
- "Execute finalization — perform the selected action. For merge: verify clean merge. For PR: include structured body (summary, test plan, rollback). For discard: verify deletion.",
33
- "Worktree cleanupif using git worktrees, clean up the worktree after merge/discard. Keep it only for 'keep branch' mode."
32
+ "Detect repository mode — if `.git/` is absent or inaccessible, lock finalization choices to FINALIZE_NO_VCS only and document manual handoff + rollback.",
33
+ "Select finalization mode exactly ONE enum: (A) FINALIZE_MERGE_LOCAL, (B) FINALIZE_OPEN_PR, (C) FINALIZE_KEEP_BRANCH, (D) FINALIZE_DISCARD_BRANCH, (E) FINALIZE_NO_VCS. For discard: list what will be deleted, require typed confirmation.",
34
+ "Execute finalizationperform the selected action. For merge: verify clean merge. For PR: include structured body (summary, test plan, rollback). For discard: verify deletion. For NO_VCS: record handoff target, artifact bundle path, and manual rollback owner.",
35
+ "Worktree cleanup — if using git worktrees, clean up the worktree after merge/discard. Keep it only for 'keep branch' mode. Skip for FINALIZE_NO_VCS."
34
36
  ],
35
37
  interactionProtocol: [
36
38
  "Run preflight checks before any release action.",
37
39
  "Document release notes and rollback plan explicitly.",
38
- "For finalization mode: use the Decision Protocol — present modes as labeled options (A/B/C/D) with consequences, and mark one as (recommended). Do NOT use a numeric Completeness rubric; recommend the mode that best addresses release blast-radius, rollback readiness, observability, and stakeholder communication — ties go to the most reversible option. If the harness's native structured-ask tool is available (`AskUserQuestion` / `AskQuestion` / `question` / `request_user_input`), send exactly ONE question per call, validate fields against the runtime schema, and on schema error immediately fall back to a plain-text lettered list instead of retrying guessed payloads.",
40
+ "For finalization mode: use the Decision Protocol — present modes as labeled options (A/B/C/D/E) with consequences, and mark one as (recommended). Do NOT use a numeric Completeness rubric; recommend the mode that best addresses release blast-radius, rollback readiness, observability, and stakeholder communication — ties go to the most reversible option. If the harness's native structured-ask tool is available (`AskUserQuestion` / `AskQuestion` / `question` / `request_user_input`), send exactly ONE question per call, validate fields against the runtime schema, and on schema error immediately fall back to a plain-text lettered list instead of retrying guessed payloads.",
39
41
  "Do not proceed if critical blockers remain from review.",
40
42
  "**STOP.** Present finalization options and wait for user selection before executing any finalization action."
41
43
  ],
@@ -43,7 +45,7 @@ export const SHIP = {
43
45
  "Validate review and test gates.",
44
46
  "Run preflight: build, test, lint, uncommitted-changes check.",
45
47
  "Generate release notes and rollback procedure.",
46
- "Choose one finalization enum: FINALIZE_MERGE_LOCAL, FINALIZE_OPEN_PR, FINALIZE_KEEP_BRANCH, or FINALIZE_DISCARD_BRANCH.",
48
+ "Choose one finalization enum: FINALIZE_MERGE_LOCAL, FINALIZE_OPEN_PR, FINALIZE_KEEP_BRANCH, FINALIZE_DISCARD_BRANCH, or FINALIZE_NO_VCS.",
47
49
  "Execute finalization action.",
48
50
  "Write ship artifact with decision, rationale, and execution result."
49
51
  ],
@@ -84,7 +86,8 @@ export const SHIP = {
84
86
  "More than one finalization mode implied",
85
87
  "No explicit preflight result",
86
88
  "Review verdict not referenced",
87
- "Finalization not executed, only planned"
89
+ "Finalization not executed, only planned",
90
+ "Selecting git-dependent finalization mode when `.git` is unavailable"
88
91
  ],
89
92
  policyNeedles: [
90
93
  "Pre-Ship Checks",
@@ -93,7 +96,8 @@ export const SHIP = {
93
96
  "FINALIZE_MERGE_LOCAL",
94
97
  "FINALIZE_OPEN_PR",
95
98
  "FINALIZE_KEEP_BRANCH",
96
- "FINALIZE_DISCARD_BRANCH"
99
+ "FINALIZE_DISCARD_BRANCH",
100
+ "FINALIZE_NO_VCS"
97
101
  ],
98
102
  artifactFile: "08-ship.md",
99
103
  next: "done",
@@ -131,7 +135,7 @@ export const SHIP = {
131
135
  { section: "Release Notes", required: true, validationRule: "What changed, why, impact. References spec criteria. Breaking changes flagged." },
132
136
  { section: "Rollback Plan", required: true, validationRule: "Trigger conditions, rollback steps (exact commands), verification steps." },
133
137
  { section: "Monitoring", required: false, validationRule: "If applicable: what metrics/logs to watch post-deploy. Risk note if no monitoring." },
134
- { section: "Finalization", required: true, validationRule: "Exactly one finalization enum token selected. Execution result documented. Worktree cleaned if applicable." },
138
+ { section: "Finalization", required: true, validationRule: "Exactly one finalization enum token selected (FINALIZE_MERGE_LOCAL | FINALIZE_OPEN_PR | FINALIZE_KEEP_BRANCH | FINALIZE_DISCARD_BRANCH | FINALIZE_NO_VCS). Execution result documented. Worktree cleaned if applicable." },
135
139
  { section: "Completion Status", required: false, validationRule: "If present: exactly one of SHIPPED, SHIPPED_WITH_EXCEPTIONS, BLOCKED. Exceptions documented when applicable." },
136
140
  { section: "Compound Step", required: false, validationRule: "Optional retrospective: at least one bullet of the form 'Insight: ... | Action: append [compound] entry to .cclaw/knowledge.jsonl', or an explicit 'No compound insight this run.' line." }
137
141
  ]
@@ -1,2 +1,4 @@
1
1
  import type { StageSchemaInput } from "./schema-types.js";
2
+ import type { FlowTrack } from "../../types.js";
2
3
  export declare const TDD: StageSchemaInput;
4
+ export declare function tddStageForTrack(track: FlowTrack): StageSchemaInput;
@@ -179,7 +179,7 @@ export const TDD = {
179
179
  { section: "GREEN Evidence", required: true, validationRule: "Full suite pass output captured." },
180
180
  { section: "REFACTOR Notes", required: true, validationRule: "What changed, why, behavior preservation confirmed." },
181
181
  { section: "Traceability", required: true, validationRule: "Plan task ID and spec criterion linked." },
182
- { section: "Verification Ladder", required: false, validationRule: "If present: per-slice verification tier (static, command, behavioral, human) with evidence for highest tier reached." },
182
+ { section: "Verification Ladder", required: true, validationRule: "Per-slice verification tier (static, command, behavioral, human) with evidence captured for the highest tier reached this turn." },
183
183
  { section: "Coverage Targets", required: false, validationRule: "If present: per-module or per-code-type coverage thresholds with current values and measurement commands." },
184
184
  { 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." },
185
185
  { 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." },
@@ -187,3 +187,65 @@ export const TDD = {
187
187
  ],
188
188
  batchExecutionAllowed: true
189
189
  };
190
+ function quickTrackText(value) {
191
+ return value
192
+ .replace(/\btask from the plan\b/giu, "acceptance criterion from the spec")
193
+ .replace(/\bplan task ID\b/giu, "acceptance criterion ID")
194
+ .replace(/\bplan task\b/giu, "acceptance criterion")
195
+ .replace(/\bplan row\b/giu, "acceptance row")
196
+ .replace(/\bplan slice\b/giu, "acceptance slice")
197
+ .replace(/\bplan artifact\b/giu, "spec artifact")
198
+ .replace(/\btraceable to plan slice\b/giu, "traceable to acceptance criterion")
199
+ .replace(/05-plan\.md/gu, "04-spec.md");
200
+ }
201
+ function tddQuickTrackVariant() {
202
+ return {
203
+ ...TDD,
204
+ skillDescription: quickTrackText(TDD.skillDescription),
205
+ hardGate: quickTrackText(TDD.hardGate),
206
+ checklist: TDD.checklist.map(quickTrackText),
207
+ interactionProtocol: TDD.interactionProtocol.map(quickTrackText),
208
+ process: TDD.process.map(quickTrackText),
209
+ requiredGates: TDD.requiredGates.map((gate) => gate.id === "tdd_traceable_to_plan"
210
+ ? { ...gate, description: "Change traceability to acceptance criterion is explicit." }
211
+ : gate),
212
+ requiredEvidence: TDD.requiredEvidence.map(quickTrackText),
213
+ inputs: TDD.inputs.map(quickTrackText),
214
+ requiredContext: ["spec artifact", "existing test patterns"],
215
+ policyNeedles: TDD.policyNeedles.map(quickTrackText),
216
+ reviewSections: TDD.reviewSections.map((section) => ({
217
+ ...section,
218
+ evaluationPoints: section.evaluationPoints.map(quickTrackText)
219
+ })),
220
+ crossStageTrace: {
221
+ ...TDD.crossStageTrace,
222
+ readsFrom: [".cclaw/artifacts/04-spec.md"],
223
+ traceabilityRule: "Every RED test traces to an acceptance criterion. Every GREEN change traces to a RED test. Evidence chain must be unbroken."
224
+ },
225
+ artifactValidation: TDD.artifactValidation.map((row) => {
226
+ if (row.section === "Acceptance Mapping") {
227
+ return {
228
+ ...row,
229
+ required: true,
230
+ validationRule: "Each RED test links to a spec acceptance criterion ID (for example AC-1)."
231
+ };
232
+ }
233
+ if (row.section === "Traceability") {
234
+ return {
235
+ ...row,
236
+ validationRule: "Acceptance criterion IDs are linked to RED/GREEN evidence."
237
+ };
238
+ }
239
+ return {
240
+ ...row,
241
+ validationRule: quickTrackText(row.validationRule)
242
+ };
243
+ })
244
+ };
245
+ }
246
+ export function tddStageForTrack(track) {
247
+ if (track === "quick") {
248
+ return tddQuickTrackVariant();
249
+ }
250
+ return TDD;
251
+ }
@@ -520,6 +520,19 @@ inputs_hash: sha256:pending
520
520
  - Adversarial review pass: true | false
521
521
  - Overall score: <0-100>
522
522
 
523
+ ## Trace Matrix Check
524
+ - Command: \`cclaw internal trace-matrix\`
525
+ - Orphaned criteria: 0
526
+ - Orphaned tasks: 0
527
+ - Orphaned tests: 0
528
+ - Evidence ref:
529
+
530
+ ## Blocked Route
531
+ - ROUTE_BACK_TO_TDD: only when Final Verdict = BLOCKED
532
+ - Target stage: tdd
533
+ - Blocking finding IDs:
534
+ - Rewind command payload:
535
+
523
536
  ## Severity Summary
524
537
  - Critical:
525
538
  - Important:
@@ -585,9 +598,11 @@ inputs_hash: sha256:pending
585
598
  - FINALIZE_OPEN_PR
586
599
  - FINALIZE_KEEP_BRANCH
587
600
  - FINALIZE_DISCARD_BRANCH
588
- - Selected label (A/B/C/D):
601
+ - FINALIZE_NO_VCS
602
+ - Selected label (A/B/C/D/E):
589
603
  - Execution result:
590
604
  - PR URL / merge commit / kept branch / discard confirmation:
605
+ - NO_VCS handoff target + artifact path (if FINALIZE_NO_VCS):
591
606
 
592
607
  ## Completion Status
593
608
  - SHIPPED | SHIPPED_WITH_EXCEPTIONS | BLOCKED
@@ -662,9 +662,11 @@ Do not merge, open PR, or discard branch until verification and rollback notes a
662
662
  - FINALIZE_OPEN_PR
663
663
  - FINALIZE_KEEP_BRANCH
664
664
  - FINALIZE_DISCARD_BRANCH
665
- 3. Execute only the chosen mode and record exact result.
666
- 4. If merge or discard happened in a feature worktree, clean the worktree.
667
- 5. Update ship artifact with release notes, rollback, and finalization evidence.
665
+ - FINALIZE_NO_VCS
666
+ 3. If \`.git\` is unavailable, use FINALIZE_NO_VCS and record manual handoff + rollback owner.
667
+ 4. Execute only the chosen mode and record exact result.
668
+ 5. If merge or discard happened in a feature worktree, clean the worktree.
669
+ 6. Update ship artifact with release notes, rollback, and finalization evidence.
668
670
 
669
671
  ## Rollback minimum
670
672
 
package/dist/doctor.js CHANGED
@@ -623,6 +623,8 @@ export async function doctorChecks(projectRoot, options = {}) {
623
623
  "session-start.sh",
624
624
  "stop-checkpoint.sh",
625
625
  "run-hook.cmd",
626
+ "stage-complete.sh",
627
+ "pre-compact.sh",
626
628
  "prompt-guard.sh",
627
629
  "workflow-guard.sh",
628
630
  "context-monitor.sh"
@@ -816,10 +818,13 @@ export async function doctorChecks(projectRoot, options = {}) {
816
818
  const codexDoc = await readHookDocument(codexHooksFile);
817
819
  const codexHooks = toObject(codexDoc?.hooks) ?? {};
818
820
  const codexSessionCmds = collectHookCommands(codexHooks.SessionStart);
821
+ const codexUserPromptCmds = collectHookCommands(codexHooks.UserPromptSubmit);
819
822
  const codexPreCmds = collectHookCommands(codexHooks.PreToolUse);
820
823
  const codexPostCmds = collectHookCommands(codexHooks.PostToolUse);
821
824
  const codexStopCmds = collectHookCommands(codexHooks.Stop);
822
825
  const codexWiringOk = codexSessionCmds.some((cmd) => cmd.includes("session-start.sh")) &&
826
+ codexUserPromptCmds.some((cmd) => cmd.includes("prompt-guard.sh")) &&
827
+ codexUserPromptCmds.some((cmd) => cmd.includes("verify-current-state --quiet")) &&
823
828
  codexPreCmds.some((cmd) => cmd.includes("prompt-guard.sh")) &&
824
829
  codexPreCmds.some((cmd) => cmd.includes("workflow-guard.sh")) &&
825
830
  codexPostCmds.some((cmd) => cmd.includes("context-monitor.sh")) &&
@@ -827,7 +832,7 @@ export async function doctorChecks(projectRoot, options = {}) {
827
832
  checks.push({
828
833
  name: "hook:wiring:codex",
829
834
  ok: codexWiringOk,
830
- details: `${codexHooksFile} must wire session-start/prompt-guard/workflow-guard/context-monitor/stop-checkpoint (PreToolUse/PostToolUse run Bash-only in Codex v0.114+)`
835
+ details: `${codexHooksFile} must wire SessionStart, UserPromptSubmit(prompt-guard + verify-current-state), PreToolUse(prompt/workflow), PostToolUse(context-monitor), and Stop(stop-checkpoint). PreToolUse/PostToolUse run Bash-only in Codex v0.114+`
831
836
  });
832
837
  // Feature flag warning: Codex ignores `.codex/hooks.json` unless the
833
838
  // user has `[features] codex_hooks = true` in `~/.codex/config.toml`.
@@ -895,6 +900,8 @@ export async function doctorChecks(projectRoot, options = {}) {
895
900
  if (configuredHarnesses.includes("opencode")) {
896
901
  const file = path.join(projectRoot, ".opencode/plugins/cclaw-plugin.mjs");
897
902
  let ok = false;
903
+ let singleHandlerPathOk = false;
904
+ let precompactHookOk = false;
898
905
  if (await exists(file)) {
899
906
  const content = await fs.readFile(file, "utf8");
900
907
  ok =
@@ -904,16 +911,36 @@ export async function doctorChecks(projectRoot, options = {}) {
904
911
  content.includes("prompt-guard.sh") &&
905
912
  content.includes("workflow-guard.sh") &&
906
913
  content.includes("context-monitor.sh") &&
914
+ content.includes("pre-compact.sh") &&
907
915
  content.includes('"session.idle"') &&
908
916
  content.includes('"session.resumed"') &&
917
+ content.includes('"session.compacted"') &&
909
918
  content.includes('"session.cleared"') &&
910
919
  content.includes('"experimental.chat.system.transform"');
920
+ singleHandlerPathOk =
921
+ !content.includes('eventType === "tool.execute.before"') &&
922
+ !content.includes('eventType === "tool.execute.after"') &&
923
+ content.includes('"tool.execute.before": async') &&
924
+ content.includes('"tool.execute.after": async');
925
+ precompactHookOk =
926
+ content.includes('eventType === "session.compacted"') &&
927
+ content.includes('runHookScript("pre-compact.sh"');
911
928
  }
912
929
  checks.push({
913
930
  name: "lifecycle:opencode:rehydration_events",
914
931
  ok,
915
932
  details: `${file} must include event lifecycle handler, tool.execute.before/after with prompt/workflow/context hooks, session.idle checkpoint, and transform rehydration`
916
933
  });
934
+ checks.push({
935
+ name: "hook:opencode:single_tool_handler_path",
936
+ ok: singleHandlerPathOk,
937
+ details: `${file} must route tool.execute.before/after through dedicated handlers exactly once (no duplicate event() branches).`
938
+ });
939
+ checks.push({
940
+ name: "hook:opencode:precompact_digest",
941
+ ok: precompactHookOk,
942
+ details: `${file} must run pre-compact.sh on session.compacted before bootstrap refresh.`
943
+ });
917
944
  const runtimeShape = await opencodePluginRuntimeShapeCheck(projectRoot);
918
945
  checks.push({
919
946
  name: "hook:opencode:runtime_shape",
@@ -59,10 +59,10 @@ export function createInitialFlowState(activeRunIdOrOptions = "active", maybeTra
59
59
  const track = options.track ?? "standard";
60
60
  const skippedStages = skippedStagesForTrack(track);
61
61
  const stageGateCatalog = {};
62
- for (const schema of orderedStageSchemas()) {
62
+ for (const schema of orderedStageSchemas(track)) {
63
63
  stageGateCatalog[schema.stage] = {
64
- required: stageGateIds(schema.stage),
65
- recommended: stageRecommendedGateIds(schema.stage),
64
+ required: stageGateIds(schema.stage, track),
65
+ recommended: stageRecommendedGateIds(schema.stage, track),
66
66
  conditional: [],
67
67
  triggered: [],
68
68
  passed: [],
@@ -5,8 +5,9 @@ import { RUNTIME_ROOT } from "./constants.js";
5
5
  import { stageSchema } from "./content/stage-schema.js";
6
6
  import { exists } from "./fs-utils.js";
7
7
  import { readFlowState, writeFlowState } from "./runs.js";
8
- async function currentStageArtifactExists(projectRoot, stage) {
9
- const artifactFile = stageSchema(stage).artifactFile;
8
+ import { buildTraceMatrix } from "./trace-matrix.js";
9
+ async function currentStageArtifactExists(projectRoot, stage, track) {
10
+ const artifactFile = stageSchema(stage, track).artifactFile;
10
11
  const candidates = [
11
12
  path.join(projectRoot, RUNTIME_ROOT, "artifacts", artifactFile),
12
13
  path.join(projectRoot, artifactFile)
@@ -34,7 +35,7 @@ function sameStringArray(a, b) {
34
35
  }
35
36
  export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
36
37
  const stage = flowState.currentStage;
37
- const schema = stageSchema(stage);
38
+ const schema = stageSchema(stage, flowState.track);
38
39
  const catalog = flowState.stageGateCatalog[stage];
39
40
  const required = schema.requiredGates
40
41
  .filter((gate) => gate.tier === "required")
@@ -92,7 +93,7 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
92
93
  issues.push(`blocked gate "${gateId}" is not defined for stage "${stage}".`);
93
94
  }
94
95
  }
95
- const artifactPresent = await currentStageArtifactExists(projectRoot, stage);
96
+ const artifactPresent = await currentStageArtifactExists(projectRoot, stage, flowState.track);
96
97
  const shouldValidateArtifact = artifactPresent || catalog.passed.length > 0 || flowState.completedStages.includes(stage);
97
98
  if (shouldValidateArtifact) {
98
99
  const lint = await lintArtifact(projectRoot, stage);
@@ -113,6 +114,23 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
113
114
  if (!verdictConsistency.ok) {
114
115
  issues.push(`review verdict inconsistency: ${verdictConsistency.errors.join("; ")}`);
115
116
  }
117
+ const traceGateRequired = schema.requiredGates.some((gate) => gate.id === "review_trace_matrix_clean" && gate.tier === "required");
118
+ if (traceGateRequired) {
119
+ const trace = await buildTraceMatrix(projectRoot);
120
+ const traceIssues = [];
121
+ if (trace.orphanedCriteria.length > 0) {
122
+ traceIssues.push(`orphaned criteria: ${trace.orphanedCriteria.join(", ")}`);
123
+ }
124
+ if (trace.orphanedTasks.length > 0) {
125
+ traceIssues.push(`orphaned tasks: ${trace.orphanedTasks.join(", ")}`);
126
+ }
127
+ if (trace.orphanedTests.length > 0) {
128
+ traceIssues.push(`orphaned tests: ${trace.orphanedTests.join(", ")}`);
129
+ }
130
+ if (traceIssues.length > 0) {
131
+ issues.push(`review trace-matrix gate blocked (review_trace_matrix_clean): ${traceIssues.join("; ")}.`);
132
+ }
133
+ }
116
134
  }
117
135
  }
118
136
  const passedSet = new Set(catalog.passed);
@@ -149,7 +167,7 @@ export function verifyCompletedStagesGateClosure(flowState) {
149
167
  const issues = [];
150
168
  const openStages = [];
151
169
  for (const stage of flowState.completedStages) {
152
- const schema = stageSchema(stage);
170
+ const schema = stageSchema(stage, flowState.track);
153
171
  const catalog = flowState.stageGateCatalog[stage];
154
172
  const required = schema.requiredGates
155
173
  .filter((gate) => gate.tier === "required")
@@ -177,10 +195,11 @@ export function verifyCompletedStagesGateClosure(flowState) {
177
195
  }
178
196
  export function reconcileCurrentStageGateCatalog(flowState) {
179
197
  const stage = flowState.currentStage;
180
- const required = stageSchema(stage).requiredGates
198
+ const schema = stageSchema(stage, flowState.track);
199
+ const required = schema.requiredGates
181
200
  .filter((gate) => gate.tier === "required")
182
201
  .map((gate) => gate.id);
183
- const recommended = stageSchema(stage).requiredGates
202
+ const recommended = schema.requiredGates
184
203
  .filter((gate) => gate.tier === "recommended")
185
204
  .map((gate) => gate.id);
186
205
  const conditional = [];
@@ -5,6 +5,7 @@
5
5
  "schemaVersion": 1,
6
6
  "requiredEvents": [
7
7
  "SessionStart",
8
+ "UserPromptSubmit",
8
9
  "PreToolUse",
9
10
  "PostToolUse",
10
11
  "Stop"
package/dist/install.js CHANGED
@@ -213,9 +213,10 @@ async function writeEvalScaffold(projectRoot) {
213
213
  }
214
214
  }
215
215
  async function writeSkills(projectRoot, config) {
216
+ const skillTrack = config?.defaultTrack ?? "standard";
216
217
  for (const stage of COMMAND_FILE_ORDER) {
217
218
  const folder = stageSkillFolder(stage);
218
- await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), stageSkillMarkdown(stage));
219
+ await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), stageSkillMarkdown(stage, skillTrack));
219
220
  // Progressive disclosure (A.2#8): materialize the full example artifact as
220
221
  // a sibling reference file. The stage skill only links to it; agents load
221
222
  // the reference on demand.
@@ -354,7 +354,7 @@ async function runAdvanceStage(projectRoot, args, io) {
354
354
  io.stderr.write(`cclaw internal advance-stage: current stage is "${flowState.currentStage}", not "${args.stage}".\n`);
355
355
  return 1;
356
356
  }
357
- const schema = stageSchema(args.stage);
357
+ const schema = stageSchema(args.stage, flowState.track);
358
358
  const requiredGateIds = schema.requiredGates
359
359
  .filter((gate) => gate.tier === "required")
360
360
  .map((gate) => gate.id);
@@ -14,6 +14,8 @@ const STATE_SNAPSHOT_EXCLUDE = new Set([
14
14
  ".flow-state.lock",
15
15
  ".delegation.lock"
16
16
  ]);
17
+ const DELEGATION_LOG_FILE = "delegation-log.json";
18
+ const TDD_CYCLE_LOG_FILE = "tdd-cycle-log.jsonl";
17
19
  function runsRoot(projectRoot) {
18
20
  return path.join(projectRoot, RUNS_DIR_REL_PATH);
19
21
  }
@@ -60,6 +62,12 @@ async function snapshotStateDirectory(projectRoot, destinationRoot) {
60
62
  }
61
63
  return copied.sort((a, b) => a.localeCompare(b));
62
64
  }
65
+ async function resetCarryoverStateFiles(projectRoot, activeRunId) {
66
+ const stateDir = stateDirPath(projectRoot);
67
+ await ensureDir(stateDir);
68
+ await writeFileSafe(path.join(stateDir, DELEGATION_LOG_FILE), `${JSON.stringify({ runId: activeRunId, entries: [] }, null, 2)}\n`);
69
+ await writeFileSafe(path.join(stateDir, TDD_CYCLE_LOG_FILE), "");
70
+ }
63
71
  function toArchiveDate(date = new Date()) {
64
72
  const yyyy = date.getFullYear().toString();
65
73
  const mm = (date.getMonth() + 1).toString().padStart(2, "0");
@@ -200,6 +208,7 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
200
208
  const snapshottedStateFiles = await snapshotStateDirectory(projectRoot, archiveStatePath);
201
209
  const resetState = createInitialFlowState();
202
210
  await writeFlowState(projectRoot, resetState, { allowReset: true });
211
+ await resetCarryoverStateFiles(projectRoot, resetState.activeRunId);
203
212
  const archivedAt = new Date().toISOString();
204
213
  const manifest = {
205
214
  version: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.46.10",
3
+ "version": "0.46.12",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {