cclaw-cli 6.5.0 → 6.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.
Files changed (49) hide show
  1. package/dist/artifact-linter/brainstorm.js +2 -1
  2. package/dist/artifact-linter/design.js +2 -1
  3. package/dist/artifact-linter/findings-dedup.d.ts +56 -0
  4. package/dist/artifact-linter/findings-dedup.js +232 -0
  5. package/dist/artifact-linter/plan.js +4 -2
  6. package/dist/artifact-linter/review.js +2 -1
  7. package/dist/artifact-linter/scope.js +2 -1
  8. package/dist/artifact-linter/shared.d.ts +103 -0
  9. package/dist/artifact-linter/shared.js +177 -0
  10. package/dist/artifact-linter/tdd.js +2 -1
  11. package/dist/artifact-linter.d.ts +1 -1
  12. package/dist/artifact-linter.js +45 -3
  13. package/dist/content/examples.d.ts +32 -0
  14. package/dist/content/examples.js +74 -0
  15. package/dist/content/hooks.js +36 -1
  16. package/dist/content/node-hooks.js +43 -0
  17. package/dist/content/skills-elicitation.js +3 -6
  18. package/dist/content/skills.d.ts +10 -0
  19. package/dist/content/skills.js +44 -2
  20. package/dist/content/stages/brainstorm.js +7 -5
  21. package/dist/content/stages/design.js +3 -1
  22. package/dist/content/stages/plan.js +3 -1
  23. package/dist/content/stages/review.js +3 -1
  24. package/dist/content/stages/scope.js +5 -3
  25. package/dist/content/stages/ship.js +2 -1
  26. package/dist/content/stages/spec.js +3 -1
  27. package/dist/content/stages/tdd.js +3 -1
  28. package/dist/content/templates.d.ts +9 -0
  29. package/dist/content/templates.js +45 -2
  30. package/dist/delegation.d.ts +9 -0
  31. package/dist/delegation.js +3 -0
  32. package/dist/internal/advance-stage/advance.js +23 -1
  33. package/dist/internal/advance-stage/parsers.d.ts +8 -0
  34. package/dist/internal/advance-stage/parsers.js +7 -0
  35. package/dist/internal/advance-stage/proactive-delegation-trace.d.ts +3 -0
  36. package/dist/internal/advance-stage/proactive-delegation-trace.js +8 -1
  37. package/dist/internal/advance-stage/rewind.js +2 -2
  38. package/dist/internal/advance-stage/start-flow.js +4 -1
  39. package/dist/internal/advance-stage.js +32 -2
  40. package/dist/internal/flow-state-repair.d.ts +13 -0
  41. package/dist/internal/flow-state-repair.js +65 -0
  42. package/dist/internal/waiver-grant.d.ts +62 -0
  43. package/dist/internal/waiver-grant.js +294 -0
  44. package/dist/run-persistence.d.ts +70 -0
  45. package/dist/run-persistence.js +215 -3
  46. package/dist/runs.d.ts +1 -1
  47. package/dist/runs.js +1 -1
  48. package/dist/runtime/run-hook.mjs +43 -0
  49. package/package.json +1 -1
@@ -1,7 +1,8 @@
1
1
  import { RUNTIME_ROOT, STAGE_TO_SKILL_FOLDER } from "../constants.js";
2
2
  import { nextStage as nextStageForTrack } from "../flow-state.js";
3
3
  import { FLOW_STAGES } from "../types.js";
4
- import { stageExamples } from "./examples.js";
4
+ import { behaviorAnchorFor, stageExamples } from "./examples.js";
5
+ import { INVESTIGATION_DISCIPLINE_BLOCK } from "./templates.js";
5
6
  import { reviewStackAwareRoutes, reviewStackAwareRoutingSummary, stageAutoSubagentDispatch, stageSchema, stageTrackRenderContext } from "./stage-schema.js";
6
7
  import { referencePatternsForStage } from "./reference-patterns.js";
7
8
  import { harnessDelegationRecipes } from "../harness-adapters.js";
@@ -104,6 +105,40 @@ Any "the failure is real" claim (failing test, broken build, regression catch, d
104
105
  For TDD specifically, this is the watched-RED proof and is required per new test before \`stage-complete\` accepts the stage.
105
106
  `;
106
107
  }
108
+ /**
109
+ * Stages that perform real investigation work. The shared
110
+ * `INVESTIGATION_DISCIPLINE_BLOCK` is rendered once per stage skill in this
111
+ * set so the search → graph → narrow-read → draft ladder appears verbatim
112
+ * across the elicitation/spec/plan/tdd/review pipeline. `ship` is excluded:
113
+ * it consumes the upstream trace rather than producing one.
114
+ */
115
+ export const INVESTIGATION_DISCIPLINE_STAGES = new Set([
116
+ "brainstorm",
117
+ "scope",
118
+ "design",
119
+ "spec",
120
+ "plan",
121
+ "tdd",
122
+ "review"
123
+ ]);
124
+ export function investigationDisciplineBlock() {
125
+ return INVESTIGATION_DISCIPLINE_BLOCK;
126
+ }
127
+ export function behaviorAnchorBlock(stage) {
128
+ const anchor = behaviorAnchorFor(stage);
129
+ if (!anchor)
130
+ return "";
131
+ const ruleHint = anchor.ruleHint && anchor.ruleHint.trim().length > 0
132
+ ? `\n\nRule hint: ${anchor.ruleHint.trim()}`
133
+ : "";
134
+ return `## Behavior anchor
135
+
136
+ Anchored to artifact section: \`${anchor.section}\`.
137
+
138
+ - Bad: ${anchor.bad}
139
+ - Good: ${anchor.good}${ruleHint}
140
+ `;
141
+ }
107
142
  function crossCuttingMechanicsBlock(stage) {
108
143
  // All stages share the universal mechanics, but each stage's matching
109
144
  // linter rules decide what is mandatory vs. structural-only.
@@ -117,6 +152,13 @@ function crossCuttingMechanicsBlock(stage) {
117
152
  if (stage === "tdd" || stage === "review" || stage === "ship") {
118
153
  blocks.push(watchedFailProofBlock());
119
154
  }
155
+ if (INVESTIGATION_DISCIPLINE_STAGES.has(stage)) {
156
+ blocks.push(investigationDisciplineBlock());
157
+ }
158
+ const anchor = behaviorAnchorBlock(stage);
159
+ if (anchor.length > 0) {
160
+ blocks.push(anchor);
161
+ }
120
162
  return blocks.join("\n");
121
163
  }
122
164
  function whenNotToUseBlock(items) {
@@ -388,7 +430,7 @@ function completionParametersBlock(schema, track) {
388
430
  - \`delegation lifecycle proof\`: use the delegation helper recipe in this section with explicit lifecycle rows: \`--status=scheduled\` -> \`--status=launched\` -> \`--status=acknowledged\` -> \`--status=completed\` (completed isolated/generic requires prior ACK for the same span or \`--ack-ts=<iso>\`).
389
431
  - Fill \`## Learnings\` before closeout: either \`- None this stage.\` or JSON bullets with required keys \`type\`, \`trigger\`, \`action\`, \`confidence\` (knowledge-schema compatible).
390
432
  - If you edit any completed-stage artifact after it shipped (\`completedStageMeta\` timestamps exist), append a short \`## Amendments\` section with dated bullets (timestamp + reason) instead of overwriting the archived narrative silently — advisory linter rule \`stage_artifact_post_closure_mutation\` enforces visibility when this trail is missing.
391
- - Record mandatory delegation lifecycle in \`${RUNTIME_ROOT}/state/delegation-log.json\` and append proof events to \`${RUNTIME_ROOT}/state/delegation-events.jsonl\`; the ledger is current state, the event log is audit proof.${mandatoryAgents.length > 0 ? ` If a mandatory delegation cannot run in this harness, use \`--waive-delegation=${mandatoryAgents.join(",")} --waiver-reason="<why safe>"\` on the completion helper.` : ""} If proactive delegations were intentionally skipped, rerun only with \`--accept-proactive-waiver\` (optionally \`--accept-proactive-waiver-reason="<why safe>"\`) after explicit user approval.
433
+ - Record mandatory delegation lifecycle in \`${RUNTIME_ROOT}/state/delegation-log.json\` and append proof events to \`${RUNTIME_ROOT}/state/delegation-events.jsonl\`; the ledger is current state, the event log is audit proof.${mandatoryAgents.length > 0 ? ` If a mandatory delegation cannot run in this harness, use \`--waive-delegation=${mandatoryAgents.join(",")} --waiver-reason="<why safe>"\` on the completion helper.` : ""} If proactive delegations were intentionally skipped, first issue a short-lived waiver token with \`cclaw-cli internal waiver-grant --stage <stage> --reason "<short-slug>"\`, then rerun the completion helper with \`--accept-proactive-waiver=<token> --accept-proactive-waiver-reason="<why safe>"\` after explicit user approval. Tokens expire in 30 minutes and are single-use; bare \`--accept-proactive-waiver\` is no longer accepted.
392
434
  - Never edit raw \`flow-state.json\` to complete a stage, even in advisory mode; that bypasses validation, gate evidence, and Learnings harvest. If a helper fails, report a one-line human-readable failure plus fenced JSON diagnostics; never echo the invoking command line or apply a manual state workaround.
393
435
  - Stage completion claim requires \`stage-complete\` exit 0 in the current turn. Quote the single-line success JSON exactly as printed to stdout (for example \`{"ok":true,"command":"stage-complete",...}\` including \`completedStages\` / \`currentStage\` / \`runId\`); do not paraphrase. Do not infer success from empty stdout or from skipped retries (quiet mode always emits one JSON line on success).
394
436
  - Completion protocol: verify required gates, update the artifact, then use the completion helper with \`--evidence-json\` and \`--passed\` for every satisfied gate.
@@ -38,10 +38,10 @@ export const BRAINSTORM = {
38
38
  checklist: [
39
39
  "**ADAPTIVE ELICITATION COMES FIRST (no exceptions, no subagent dispatch before).** Load `.cclaw/skills/adaptive-elicitation/SKILL.md`. Walk the brainstorm forcing questions one-at-a-time via the harness-native question tool, append one row to `## Q&A Log` (`Turn | Question | User answer (1-line) | Decision impact`) after each user answer **and stamp the row's `Decision impact` cell with the matching `[topic:<id>]` tag** (e.g. `[topic:pain]`). Continue until every forcing-question topic id is tagged on a row OR Ralph-Loop convergence detector says no new decision-changing rows in last 2 iterations OR user records an explicit stop-signal row. Only then proceed to delegations, drafts, or analysis. The linter `qa_log_unconverged` rule will block `stage-complete` if convergence is not reached.",
40
40
  "**Explore project context** — after the elicitation loop converges, inspect existing files/docs/recent activity to refine the Discovered context section; capture matching files/patterns/seeds in `Context > Discovered context` so downstream stages don't redo discovery.",
41
- "**Brainstorm forcing questions (must be covered or explicitly waived)** — `pain: what pain are we solving`; `direct-path: what is the direct path`; `do-nothing: what happens if we do nothing`; `operator: who is the first operator/user affected`; `no-go: what no-go boundaries are non-negotiable`. Tag the matching `## Q&A Log` row's `Decision impact` cell with `[topic:<id>]` (e.g. `[topic:pain]`) so the linter can verify coverage in any natural language. Tags are MANDATORY for forcing-question rows; un-tagged rows do NOT count toward coverage.",
41
+ "**Brainstorm forcing questions (must be covered or explicitly waived)** — `pain: what pain are we solving`; `direct-path: what is the direct path`; `operator: who is the first operator/user affected`; `no-go: what no-go boundaries are non-negotiable`. Tag the matching `## Q&A Log` row's `Decision impact` cell with `[topic:<id>]` (e.g. `[topic:pain]`) so the linter can verify coverage in any natural language. Tags are MANDATORY for forcing-question rows; un-tagged rows do NOT count toward coverage. Round 6 (v6.7.0) removed the counterfactual `do-nothing` topic; the Problem Decision Record already captures `Do-nothing consequence`.",
42
42
  "**Discovery posture (flow-state `discoveryMode`)** — follow `lean` / `guided` / `deep` from the active run. Use lean for smallest safe discovery pass; guided as the default balanced pass; escalate to deep when ambiguity, architecture, external dependency, security/data risk, or explicit think-bigger requests warrant fuller option pressure and mandatory specialist coverage.",
43
43
  "**Write the Problem Decision Record** — pick a free-form `Frame type` label that names how this work is framed (examples: product, technical-maintenance, research-spike, ops-incident, infrastructure), then fill the universal Framing fields: affected user/role/operator, current state/failure mode/opportunity, desired observable outcome, evidence/signal, why now, do-nothing consequence, and non-goals.",
44
- "**Premise check (one pass)** — answer the three gstack-style questions in the artifact body: *Right problem? Direct path? What if we do nothing?* Take a position; do not hedge.",
44
+ "**Premise check (one pass)** — answer the two gstack-style questions in the artifact body: *Right problem? Direct path?* Take a position; do not hedge. Round 6 (v6.7.0): the counterfactual premise line was retired; Do-nothing consequence already lives in the Problem Decision Record.",
45
45
  "**Reframe with How Might We** — write a single `How Might We …?` line that names the user/operator, the desired outcome, and the constraint. This is the altitude check before approaches.",
46
46
  "**Run Clarity Gate** — record ambiguity score (0.00-1.00), decision boundaries, reaffirmed non-goals, and residual-risk handoff before locking recommendations. If ambiguity remains high (>0.40), ask one decision-changing question before recommending.",
47
47
  "**Sharpening question discipline** — ask one decision-changing question at a time. Do not default to 3-5 batched questions; record only questions that changed the direction or a critical stop decision.",
@@ -62,14 +62,16 @@ export const BRAINSTORM = {
62
62
  "\"If something is unclear, stop. Name what's confusing. Ask.\"",
63
63
  "Start from observed project context; if the idea is vague, first narrow the project type with **one** structured question, then keep going.",
64
64
  "Honor the run's `discoveryMode` (`lean` | `guided` | `deep`) from flow-state: lean stays fastest, guided is the default breadth, deep pulls in fuller critique and mandatory delegations when the run is classified that way.",
65
- "Lead with the premise check (right problem / direct path / what if nothing) and the `How Might We` reframing before approaches; both go in the artifact, not just the chat.",
65
+ "Lead with the premise check (right problem / direct path) and the `How Might We` reframing before approaches; both go in the artifact, not just the chat. Round 6 (v6.7.0) removed the counterfactual premise line; Do-nothing consequence still lives in the Problem Decision Record.",
66
66
  "Ask at most one question per turn, only when decision-changing; if using a structured question tool, send exactly one question object, not a multi-question form.",
67
67
  "Run the shared adaptive elicitation cycle from `.cclaw/skills/adaptive-elicitation/SKILL.md`, including stop-signal handling (RU/EN/UA), smart-skip, conditional grilling triggers, and append-only `## Q&A Log` updates.",
68
68
  "Only non-critical preference/default assumptions may continue inline. STOP and ask when uncertainty affects scope, architecture, security, data loss, public API, migration, auth/pricing, or user approval.",
69
69
  "For simple low-risk greenfield work, present a compact A/B choice with one recommended path and one higher-upside challenger; keep the artifact concise but structurally complete (Context, Premise, How Might We, Sharpening Questions, Approaches, Reaction, Selected Direction, Not Doing).",
70
70
  "Show approaches before the recommendation; include a higher-upside challenger and gather reaction first.",
71
71
  "Self-review before approval: re-read the artifact, fix contradictions/placeholders/weak trade-offs, then ask for approval. Do not ask for approval on a draft you have not re-read.",
72
- "State exactly what is being approved, then **STOP** until the user explicitly approves the artifact."
72
+ "State exactly what is being approved, then **STOP** until the user explicitly approves the artifact.",
73
+ "Investigation discipline: follow the shared `## Investigation Discipline` block (search -> graph/impact -> narrow read of 1-3 files -> draft) before any drafting or delegation; pass repo-relative paths and refs (never file bodies) in delegations.",
74
+ "Behavior anchor: see the shared `## Behavior anchor` block in this skill — the bad/good pair anchors how this stage's `Problem Decision Record` must be filled."
73
75
  ],
74
76
  process: [
75
77
  "Explore project context and align work to the run's discovery mode (lean / guided / deep).",
@@ -140,7 +142,7 @@ export const BRAINSTORM = {
140
142
  artifactValidation: [
141
143
  { section: "Context", required: true, validationRule: "Must reference project state and relevant existing code or patterns. A `Discovered context` subsection (or list) is recommended for downstream traceability." },
142
144
  { section: "Problem Decision Record", required: true, validationRule: "Must include a free-form `Frame type` label (examples only: product, technical-maintenance, research-spike, ops-incident, infrastructure) and the universal Framing fields: affected user/role/operator, current state/failure mode/opportunity, desired observable outcome, evidence/signal, why now, do-nothing consequence, non-goals. The linter checks that the section has meaningful content; the field labels themselves are the structural contract." },
143
- { section: "Premise Check", required: false, validationRule: "Recommended: explicit answers to `Right problem?`, `Direct path?`, `What if we do nothing?` — take a position, do not hedge." },
145
+ { section: "Premise Check", required: false, validationRule: "Recommended: explicit answers to `Right problem?` and `Direct path?` — take a position, do not hedge. Round 6 (v6.7.0) retired the counterfactual premise line; Do-nothing consequence already lives in the Problem Decision Record." },
144
146
  { section: "How Might We", required: false, validationRule: "Recommended: a single `How Might We …?` line naming the user, the outcome, and the binding constraint." },
145
147
  { section: "Clarity Gate", required: false, validationRule: "Recommended before recommendation lock: include ambiguity score (0.00-1.00), decision boundaries, reaffirmed non-goals, and residual-risk handoff for scope." },
146
148
  { section: "Sharpening Questions", required: false, validationRule: "Recommended only when needed: one decision-changing question per turn with explicit `Decision impact`; compact tasks may record `None - early exit` with rationale." },
@@ -71,7 +71,9 @@ export const DESIGN = {
71
71
  "Classify ambiguity before acting. Only non-critical preference/default assumptions may continue; STOP on uncertainty about scope, architecture, security, data loss, public API, migration, auth/pricing, or required user approval. Design hypotheses must name validation path, rollback trigger, and owner before they can be carried forward.",
72
72
  "Before final approval, run the critic pass, reconcile material findings, and bound retries with the review-loop policy.",
73
73
  "For baseline approval, present the full design plus exact spec handoff and **STOP** until explicit approval.",
74
- "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` runs **AFTER user approval of the design lock**, not before Q&A. Sequence is: Q&A loop -> draft design lock -> user approval -> `planner` delegation -> `stage-complete`. Legal fulfillment modes for `planner`: (a) **harness-native Task tool** — run the delegation, then record via `node .cclaw/hooks/delegation-record.mjs --stage=design --agent=planner --mode=mandatory --status=completed --span-id=<uuid> --dispatch-surface=cursor-task --agent-definition-path=<agent-md-path> --evidence-ref=<artifact#section>`; (b) **role-switch** — write planner output into the design artifact, then record with `--dispatch-surface=role-switch`; (c) **cclaw subagent helper** with `--dispatch-surface=isolated`. Run `node .cclaw/hooks/stage-complete.mjs design` from the tool layer (do not paste the command into chat); report only the resulting summary."
74
+ "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` runs **AFTER user approval of the design lock**, not before Q&A. Sequence is: Q&A loop -> draft design lock -> user approval -> `planner` delegation -> `stage-complete`. Legal fulfillment modes for `planner`: (a) **harness-native Task tool** — run the delegation, then record via `node .cclaw/hooks/delegation-record.mjs --stage=design --agent=planner --mode=mandatory --status=completed --span-id=<uuid> --dispatch-surface=cursor-task --agent-definition-path=<agent-md-path> --evidence-ref=<artifact#section>`; (b) **role-switch** — write planner output into the design artifact, then record with `--dispatch-surface=role-switch`; (c) **cclaw subagent helper** with `--dispatch-surface=isolated`. Run `node .cclaw/hooks/stage-complete.mjs design` from the tool layer (do not paste the command into chat); report only the resulting summary.",
75
+ "Investigation discipline: follow the shared `## Investigation Discipline` block before drafting architecture — populate `Codebase Investigation` from a search/graph trace and pass paths/refs (never file bodies) to investigator/critic delegations.",
76
+ "Behavior anchor: see the shared `## Behavior anchor` block in this skill — the bad/good pair anchors how `Codebase Investigation` must precede any ADR commitment."
75
77
  ],
76
78
  process: [
77
79
  "Read upstream artifacts and current design docs.",
@@ -61,7 +61,9 @@ export const PLAN = {
61
61
  "Preserve locked scope boundaries: no silent scope reduction language in task rows.",
62
62
  "Enforce WAIT_FOR_CONFIRM: present the plan summary with options (A) Approve / (B) Revise / (C) Reject.",
63
63
  "**STOP.** Do NOT proceed until user explicitly approves.",
64
- "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be marked completed or explicitly waived in `.cclaw/state/delegation-log.json`. Then close the stage via `node .cclaw/hooks/stage-complete.mjs plan` and tell the user to run `/cc`."
64
+ "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be marked completed or explicitly waived in `.cclaw/state/delegation-log.json`. Then close the stage via `node .cclaw/hooks/stage-complete.mjs plan` and tell the user to run `/cc`.",
65
+ "Investigation discipline: follow the shared `## Investigation Discipline` block — when defining `Implementation Units`, list cited paths in the `Files` and `Patterns to follow` rows instead of pasting code into chat or delegations.",
66
+ "Behavior anchor: see the shared `## Behavior anchor` block in this skill — the bad/good pair anchors how `Execution Posture` may only claim parallel-safe with disjoint units and a cited interface contract."
65
67
  ],
66
68
  process: [
67
69
  "Build dependency graph and ordered slices.",
@@ -58,7 +58,9 @@ export const REVIEW = {
58
58
  "Resolve all critical blockers before ship. If verdict is BLOCKED, do not pass `review_criticals_resolved`; pass only the remediation route gate `review_verdict_blocked` when routing back to TDD.",
59
59
  "When verdict is BLOCKED, do not end with a passive stop: explicitly route remediation to TDD via `ROUTE_BACK_TO_TDD`, point to `npx cclaw-cli internal rewind tdd` with the blocking IDs, and tell the operator to ack the stale TDD marker only after rework is complete.",
60
60
  structuredAskSingleChoiceInstruction("final verdict", "verdict (APPROVED / APPROVED_WITH_CONCERNS / BLOCKED)"),
61
- "**STOP.** Do NOT proceed to ship until the user provides an explicit verdict."
61
+ "**STOP.** Do NOT proceed to ship until the user provides an explicit verdict.",
62
+ "Investigation discipline: follow the shared `## Investigation Discipline` block — `Changed-File Coverage` and Layer 2 findings cite `path:line`; delegate `reviewer`/`security-reviewer` with paths and refs, never with pasted file contents.",
63
+ "Behavior anchor: see the shared `## Behavior anchor` block in this skill — the bad/good pair anchors that `Layer 2 Findings` surface defects, not drive-by refactors."
62
64
  ],
63
65
  process: [
64
66
  "Layer 1: check acceptance criteria and requirement coverage.",
@@ -47,9 +47,9 @@ export const SCOPE = {
47
47
  executionModel: {
48
48
  checklist: [
49
49
  "**ADAPTIVE ELICITATION COMES FIRST (no exceptions, no subagent dispatch before).** Load `.cclaw/skills/adaptive-elicitation/SKILL.md`. Walk the scope forcing questions one-at-a-time via the harness-native question tool, append one row to `## Q&A Log` (`Turn | Question | User answer (1-line) | Decision impact`) after each user answer **and stamp the row's `Decision impact` cell with the matching `[topic:<id>]` tag** (e.g. `[topic:in-out]`). Continue until every forcing-question topic id is tagged on a row OR Ralph-Loop convergence detector says no new decision-changing rows in last 2 iterations OR user records an explicit stop-signal row. Only then propose the scope contract draft, recommend a mode, or dispatch any delegations. The linter `qa_log_unconverged` rule will block `stage-complete` if convergence is not reached.",
50
- "**Scope forcing questions (must be covered or explicitly waived)** — `in-out: what is definitely in/out`; `locked-upstream: which upstream decisions are locked`; `rollback: what rollback path protects users if scope assumptions fail`; `failure-modes: what are the top failure modes we must design for`. Tag the matching `## Q&A Log` row's `Decision impact` cell with `[topic:<id>]` (e.g. `[topic:in-out]`) so the linter can verify coverage in any natural language. Tags are MANDATORY for forcing-question rows; un-tagged rows do NOT count toward coverage.",
50
+ "**Scope forcing questions (must be covered or explicitly waived)** — `in-out: what is definitely in/out`; `locked-upstream: which upstream decisions are locked`. Tag the matching `## Q&A Log` row's `Decision impact` cell with `[topic:<id>]` (e.g. `[topic:in-out]`) so the linter can verify coverage in any natural language. Tags are MANDATORY for forcing-question rows; un-tagged rows do NOT count toward coverage. Round 6 (v6.7.0) removed the counterfactual `rollback` and `failure-modes` topics from scope forcing questions; Design still owns the Failure Mode Table and rollback evidence.",
51
51
  "**Scope contract first** — read brainstorm handoff, name upstream decisions used, explicit drift, confidence, unresolved questions, and next-stage risk hints; draft the in-scope/out-of-scope/deferred/discretion contract before any design choice.",
52
- "**Premise carry-forward (do NOT re-author)** — brainstorm OWNS the premise check (right problem / direct path / what if nothing). Cite brainstorm's `## Premise Check` section in `## Upstream Handoff > Decisions carried forward`. Add a row to `## Premise Drift` only when the scope-stage Q&A surfaced NEW evidence that materially changes the brainstorm answer (e.g. new constraint, new user signal). Otherwise mark `Premise Drift: None` — do not duplicate the brainstorm premise table.",
52
+ "**Premise carry-forward (do NOT re-author)** — brainstorm OWNS the premise check (right problem / direct path). Cite brainstorm's `## Premise Check` section in `## Upstream Handoff > Decisions carried forward`. Add a row to `## Premise Drift` only when the scope-stage Q&A surfaced NEW evidence that materially changes the brainstorm answer (e.g. new constraint, new user signal). Otherwise mark `Premise Drift: None` — do not duplicate the brainstorm premise table.",
53
53
  "**Conditional 10-star boundary** — for deep/high-risk/product-strategy work, show what would make the product meaningfully better, then explicitly choose what ships now, what is deferred, and what is excluded without vague `later/for now` placeholders. Skip this for straightforward repair work and record `not needed: compact scope`.",
54
54
  "**Pick one operational mode with the user** — HOLD SCOPE preserves focus; SELECTIVE EXPANSION cherry-picks high-leverage reference ideas; SCOPE EXPANSION explores ambitious alternatives; SCOPE REDUCTION cuts to the essential wedge. Recommend one, state why and what signal would change it, then keep elicitation focused until the user either approves or asks to proceed with draft boundaries.",
55
55
  "**Product-discovery is REQUIRED for SELECTIVE / SCOPE EXPANSION (hard gate)** — If the resolved scope mode is SELECTIVE EXPANSION or SCOPE EXPANSION, run \`product-discovery\` in proactive mode **after** adaptive elicitation converges and **before** \`stage-complete\`. Do not complete this stage until the delegation ledger shows \`product-discovery\` as \`completed\` with non-empty \`evidenceRefs\` pointing at this scope artifact. HOLD SCOPE and SCOPE REDUCTION do not require this row.",
@@ -74,7 +74,9 @@ export const SCOPE = {
74
74
  "If the user says no but cannot name the change, offer concrete moves: keep scope, add one obvious adjacent capability, reduce to wedge, or re-open stack/product direction.",
75
75
  "Before final approval, record outside-voice findings and a `## Scope Outside Voice Loop` table per the Scope Outside Voice Loop policy above.",
76
76
  "**STOP.** Wait for explicit user approval of the scope mode and scope contract before writing final approval language or advancing.",
77
- "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` runs **AFTER user approval of the scope contract**, not before Q&A. Sequence is: Q&A loop -> propose contract -> user approval -> `planner` delegation -> `stage-complete`. If you delegate `planner` before the Q&A loop converges, you violate the elicitation contract and the linter will block stage-complete via `qa_log_unconverged`. Legal fulfillment modes for `planner`: (a) **harness-native Task tool** — run the delegation, then record the lifecycle row via `node .cclaw/hooks/delegation-record.mjs --stage=scope --agent=planner --mode=mandatory --status=completed --span-id=<uuid> --dispatch-surface=cursor-task --agent-definition-path=<agent-md-path> --evidence-ref=<artifact#section>` (the helper sets `fulfillmentMode: \"generic-dispatch\"` automatically); (b) **role-switch** — announce `## cclaw role-switch: scope/planner (mandatory)`, write the planner output/evidence into the scope artifact, then record the row with `--dispatch-surface=role-switch --agent-definition-path=<artifact-anchor>` (helper sets `fulfillmentMode: \"role-switch\"` automatically); (c) **cclaw subagent helper** if available, with `--dispatch-surface=isolated`. Run `node .cclaw/hooks/stage-complete.mjs scope` from the tool layer (do not paste the command into chat); report only the resulting summary."
77
+ "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` runs **AFTER user approval of the scope contract**, not before Q&A. Sequence is: Q&A loop -> propose contract -> user approval -> `planner` delegation -> `stage-complete`. If you delegate `planner` before the Q&A loop converges, you violate the elicitation contract and the linter will block stage-complete via `qa_log_unconverged`. Legal fulfillment modes for `planner`: (a) **harness-native Task tool** — run the delegation, then record the lifecycle row via `node .cclaw/hooks/delegation-record.mjs --stage=scope --agent=planner --mode=mandatory --status=completed --span-id=<uuid> --dispatch-surface=cursor-task --agent-definition-path=<agent-md-path> --evidence-ref=<artifact#section>` (the helper sets `fulfillmentMode: \"generic-dispatch\"` automatically); (b) **role-switch** — announce `## cclaw role-switch: scope/planner (mandatory)`, write the planner output/evidence into the scope artifact, then record the row with `--dispatch-surface=role-switch --agent-definition-path=<artifact-anchor>` (helper sets `fulfillmentMode: \"role-switch\"` automatically); (c) **cclaw subagent helper** if available, with `--dispatch-surface=isolated`. Run `node .cclaw/hooks/stage-complete.mjs scope` from the tool layer (do not paste the command into chat); report only the resulting summary.",
78
+ "Investigation discipline: follow the shared `## Investigation Discipline` block (search -> graph/impact -> narrow read of 1-3 files -> draft); pass repo-relative paths and refs to any delegated planner/critic instead of pasting upstream content.",
79
+ "Behavior anchor: see the shared `## Behavior anchor` block in this skill — the bad/good pair anchors how this stage's `Scope Contract` must trace each row to a recorded user signal."
78
80
  ],
79
81
  process: [
80
82
  "Run pre-scope system audit (git log/diff/stash + debt-marker scan) — scope OWNS the repo audit; design will only diff the blast radius since this scope baseline.",
@@ -59,7 +59,8 @@ export const SHIP = {
59
59
  "Document release notes and rollback plan explicitly.",
60
60
  decisionProtocolInstruction("finalization mode", "present modes as labeled options (A/B/C/D/E) with consequences, and mark one as (recommended)", "recommend the mode that best addresses release blast-radius, rollback readiness, observability, and stakeholder communication — ties go to the most reversible option"),
61
61
  "Do not proceed if critical blockers remain from review.",
62
- "**STOP.** Present finalization options and wait for user selection before executing any finalization action."
62
+ "**STOP.** Present finalization options and wait for user selection before executing any finalization action.",
63
+ "Behavior anchor: see the shared `## Behavior anchor` block in this skill — the bad/good pair anchors that `Preflight Results` cite fresh command output, exit codes, and the commit SHA from this turn."
63
64
  ],
64
65
  process: [
65
66
  "Validate review and test gates.",
@@ -54,7 +54,9 @@ export const SPEC = {
54
54
  "**Chunk acceptance criteria for review.** When presenting the spec to the user for sign-off, deliver acceptance criteria in batches of 3-5 and **pause for explicit ACK** (via Decision Protocol) before sending the next batch. Do not dump the full criteria wall in one message — small batches surface objections earlier and keep the sign-off meaningful. Full spec writeup still lands in `04-spec.md`, but the conversation itself must be digestible.",
55
55
  "Require user confirmation on the written spec. **STOP.** Do NOT proceed to plan until user approves.",
56
56
  "For each criterion, ask: what exact evidence proves this passed? If the evidence or verification command/manual step is vague, rewrite.",
57
- "When encountering ambiguity, classify it before acting: (A) ask user for missing info, (B) enumerate non-critical interpretations and pick one with justification, (C) propose hypothesis with validation path. Do NOT silently resolve ambiguity. STOP on scope, architecture, security, data loss, public API, migration, auth/pricing, or user-approval uncertainty."
57
+ "When encountering ambiguity, classify it before acting: (A) ask user for missing info, (B) enumerate non-critical interpretations and pick one with justification, (C) propose hypothesis with validation path. Do NOT silently resolve ambiguity. STOP on scope, architecture, security, data loss, public API, migration, auth/pricing, or user-approval uncertainty.",
58
+ "Investigation discipline: follow the shared `## Investigation Discipline` block — derive ACs from cited upstream paths/refs (`02-scope.md#R-2`, `03-design.md#DD-1`) instead of pasting their bodies into delegation prompts.",
59
+ "Behavior anchor: see the shared `## Behavior anchor` block in this skill — the bad/good pair anchors how each `Acceptance Criteria` row must carry an observable predicate plus the evidence path."
58
60
  ],
59
61
  process: [
60
62
  "Define measurable acceptance criteria.",
@@ -71,7 +71,9 @@ export const TDD = {
71
71
  "Use incremental RED/GREEN/REFACTOR commits when the repository workflow and working tree make that appropriate; otherwise record the checkpoint boundaries in the artifact.",
72
72
  "Stop if regressions appear and fix before proceeding.",
73
73
  "If a test passes unexpectedly, investigate: does the behavior already exist, or is the test wrong?",
74
- "**Per-Slice Review point (conditional).** 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."
74
+ "**Per-Slice Review point (conditional).** 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.",
75
+ "Investigation discipline: follow the shared `## Investigation Discipline` block — `Watched-RED Proof` and `RED Evidence` rows must cite test paths and command logs, not pasted source bodies; delegate `test-author` with paths and refs only.",
76
+ "Behavior anchor: see the shared `## Behavior anchor` block in this skill — the bad/good pair anchors how `RED Evidence` must contain a falsifiable assertion (no tautologies)."
75
77
  ],
76
78
  process: [
77
79
  "Select one vertical slice and map it to acceptance criterion(s).",
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Shared investigation discipline block (Round 5 / v6.6.0). Rendered once per
3
+ * elicitation/spec stage skill (brainstorm, scope, design, spec, plan, tdd,
4
+ * review). The block enforces a four-step ladder before drafting and a
5
+ * path-passing rule for delegations so token cost and "jumped into code"
6
+ * regressions stay bounded. Stop-trigger count and ladder-step count are
7
+ * verified by `tests/unit/investigation-discipline-block.test.ts`.
8
+ */
9
+ export declare const INVESTIGATION_DISCIPLINE_BLOCK = "## Investigation Discipline\n\nUse this ladder before drafting or delegating; do not jump straight to the editor.\n\n1. **Search** \u2014 locate the surface (file path, symbol, ref) before reading. Use `rg` / glob / graph; record the query, not the chunk.\n2. **Graph / impact** \u2014 name what the change touches (callers, callees, tests, configs) and its blast radius before opening a file.\n3. **Narrow read** \u2014 read at most 1-3 files, only the sections needed; cite paths with `:line` ranges instead of pasting bodies.\n4. **Draft** \u2014 only after the trace exists; the trace is the authority, not chat history or memory.\n\n**Path-passing in delegations.** When delegating, pass repo-relative paths and refs (e.g. `src/foo/bar.ts:42`, `D-12`, `AC-3`) \u2014 never the file body. The subagent re-reads from path; pasting content fragments breaks freshness and inflates tokens.\n\n**Stop triggers** (any one means halt and re-enter the ladder):\n\n- You are about to read more than 3 files in one pass.\n- You are about to load file content into a delegation prompt instead of paths or refs.\n- You are about to start a draft before any trace (search log, graph note, narrow-read citation) exists.\n";
1
10
  export declare const ARTIFACT_TEMPLATES: Record<string, string>;
2
11
  export declare const RULEBOOK_MARKDOWN = "# Cclaw Rulebook\n\n## MUST_ALWAYS\n- Follow flow order: brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship\n- Require explicit user confirmation after plan before TDD\n- Keep evidence artifacts in `.cclaw/artifacts/`\n- Enforce RED before GREEN in TDD\n- Run two-layer review (spec_compliance and code_quality) before ship\n- Validate all inputs before processing \u2014 never trust external data without sanitization\n- Prefer immutable data patterns and pure functions where the language supports them\n- Follow existing repo conventions, patterns, and directory structure \u2014 match the codebase\n- Verify claims with fresh evidence: \"tests pass\" requires running tests in this message\n- Use conventional commits: `type(scope): description` (feat, fix, refactor, test, docs, chore)\n\n## MUST_NEVER\n- Skip RED phase and jump directly to GREEN in TDD\n- Ship with critical review findings\n- Start implementation during /brainstorm\n- Modify generated cclaw files manually when CLI can regenerate them\n- Commit `.cclaw/` or generated shim files\n- Expose secrets, tokens, API keys, or absolute system paths in agent output\n- Duplicate existing functionality without explicit justification \u2014 search before building\n- Bypass security checks, linting hooks, or type checking to \"move faster\"\n- Claim success (\"Done,\" \"All good,\" \"Tests pass\") without running verification in this message\n- Make changes outside the blast radius of the current task without user consent\n\n## DELEGATION\nWhen a task requires specialist knowledge (security audit, performance profiling, database review),\ndelegate to a specialized agent or skill if the harness supports it. The primary agent should:\n1. Identify the specialist domain\n2. Provide focused context (relevant files, the specific concern)\n3. Evaluate the specialist output before acting on it \u2014 do not blindly apply recommendations\n";
3
12
  /**
@@ -1,4 +1,5 @@
1
1
  import { CCLAW_VERSION, SHIP_FINALIZATION_MODES } from "../constants.js";
2
+ import { renderBehaviorAnchorTemplateLine } from "./examples.js";
2
3
  import { orderedStageSchemas } from "./stage-schema.js";
3
4
  import { FLOW_STAGES } from "../types.js";
4
5
  const SHIP_FINALIZATION_ENUM_LINES = SHIP_FINALIZATION_MODES.map((mode) => ` - ${mode}`).join("\n");
@@ -17,11 +18,38 @@ const SEED_SHELF_SECTION = `## Seed Shelf Candidates (optional)
17
18
  | Seed file | Trigger when | Suggested action | Status (planted/deferred/ignored) |
18
19
  |---|---|---|---|
19
20
  | .cclaw/seeds/SEED-YYYY-MM-DD-<slug>.md | | | |`;
21
+ /**
22
+ * Shared investigation discipline block (Round 5 / v6.6.0). Rendered once per
23
+ * elicitation/spec stage skill (brainstorm, scope, design, spec, plan, tdd,
24
+ * review). The block enforces a four-step ladder before drafting and a
25
+ * path-passing rule for delegations so token cost and "jumped into code"
26
+ * regressions stay bounded. Stop-trigger count and ladder-step count are
27
+ * verified by `tests/unit/investigation-discipline-block.test.ts`.
28
+ */
29
+ export const INVESTIGATION_DISCIPLINE_BLOCK = `## Investigation Discipline
30
+
31
+ Use this ladder before drafting or delegating; do not jump straight to the editor.
32
+
33
+ 1. **Search** — locate the surface (file path, symbol, ref) before reading. Use \`rg\` / glob / graph; record the query, not the chunk.
34
+ 2. **Graph / impact** — name what the change touches (callers, callees, tests, configs) and its blast radius before opening a file.
35
+ 3. **Narrow read** — read at most 1-3 files, only the sections needed; cite paths with \`:line\` ranges instead of pasting bodies.
36
+ 4. **Draft** — only after the trace exists; the trace is the authority, not chat history or memory.
37
+
38
+ **Path-passing in delegations.** When delegating, pass repo-relative paths and refs (e.g. \`src/foo/bar.ts:42\`, \`D-12\`, \`AC-3\`) — never the file body. The subagent re-reads from path; pasting content fragments breaks freshness and inflates tokens.
39
+
40
+ **Stop triggers** (any one means halt and re-enter the ladder):
41
+
42
+ - You are about to read more than 3 files in one pass.
43
+ - You are about to load file content into a delegation prompt instead of paths or refs.
44
+ - You are about to start a draft before any trace (search log, graph note, narrow-read citation) exists.
45
+ `;
20
46
  export const ARTIFACT_TEMPLATES = {
21
47
  "01-brainstorm.md": `${artifactFrontmatter("brainstorm")}
22
48
 
23
49
  # Brainstorm Artifact
24
50
 
51
+ ${renderBehaviorAnchorTemplateLine("brainstorm")}
52
+
25
53
  ## Mode Block
26
54
  - **Mode:** STARTUP | BUILDER | ENGINEERING | OPS | RESEARCH (pick exactly one)
27
55
  - **Why this mode:** (one line; cite a concrete signal — repo state, user prompt, ownership, risk window)
@@ -61,7 +89,6 @@ export const ARTIFACT_TEMPLATES = {
61
89
  ## Premise Check
62
90
  - **Right problem?** (yes/no + one-line justification — take a position)
63
91
  - **Direct path?** (yes/no + one-line justification)
64
- - **What if we do nothing?** (concrete consequence, not "nothing happens")
65
92
 
66
93
  ## How Might We
67
94
  - *How might we …?* — one line naming the user, the desired outcome, and the binding constraint.
@@ -89,7 +116,7 @@ export const ARTIFACT_TEMPLATES = {
89
116
  | 1 | | | scope-shaping [topic:pain] |
90
117
 
91
118
  > Append-only by turn. Add one row after each user answer; do not rewrite prior rows.
92
- > **Topic tag is MANDATORY for forcing-question rows.** Stamp \`[topic:<id>]\` in the \`Decision impact\` cell so the linter can verify coverage in any natural language (RU/EN/UA/etc.). Brainstorm IDs: \`pain\`, \`direct-path\`, \`do-nothing\`, \`operator\`, \`no-go\`. Multiple tags allowed when one answer covers several topics. Stop-signal rows do NOT need a tag. Wave 24 (v6.0.0) removed the English keyword fallback.
119
+ > **Topic tag is MANDATORY for forcing-question rows.** Stamp \`[topic:<id>]\` in the \`Decision impact\` cell so the linter can verify coverage in any natural language (RU/EN/UA/etc.). Brainstorm IDs: \`pain\`, \`direct-path\`, \`operator\`, \`no-go\`. Multiple tags allowed when one answer covers several topics. Stop-signal rows do NOT need a tag. Wave 24 (v6.0.0) removed the English keyword fallback; Round 6 (v6.7.0) retired the counterfactual \`do-nothing\` topic (Do-nothing consequence stays in the Problem Decision Record).
93
120
 
94
121
  ## Approach Tier
95
122
  - Tier: lite | standard | deep
@@ -200,6 +227,8 @@ ${MARKDOWN_CODE_FENCE}
200
227
 
201
228
  # Scope Artifact
202
229
 
230
+ ${renderBehaviorAnchorTemplateLine("scope")}
231
+
203
232
  ## Upstream Handoff
204
233
  - Source artifacts: \`00-idea.md\`, \`01-brainstorm-<slug>.md\`
205
234
  - Decisions carried forward:
@@ -434,6 +463,8 @@ ${MARKDOWN_CODE_FENCE}
434
463
 
435
464
  # Design Artifact
436
465
 
466
+ ${renderBehaviorAnchorTemplateLine("design")}
467
+
437
468
  ## Compact-First Scaffold
438
469
  - Default to the compact design spine unless risk requires Standard/Deep add-ons.
439
470
  - Compact required spine: Upstream Handoff, Codebase Investigation, Engineering Lock, Architecture Boundaries, Architecture Diagram, Data Flow, Failure Mode Table, Test Strategy, Spec Handoff, and Completion Dashboard.
@@ -698,6 +729,8 @@ ${MARKDOWN_CODE_FENCE}
698
729
 
699
730
  # Specification Artifact
700
731
 
732
+ ${renderBehaviorAnchorTemplateLine("spec")}
733
+
701
734
  ## Upstream Handoff
702
735
  - Source artifacts: standard uses \`02-scope-<slug>.md\` + \`03-design-<slug>.md\`; medium uses \`01-brainstorm-<slug>.md\` when present; quick uses \`00-idea.md\` plus reproduction context.
703
736
  - Decisions carried forward:
@@ -797,6 +830,8 @@ ${MARKDOWN_CODE_FENCE}
797
830
 
798
831
  # Plan Artifact
799
832
 
833
+ ${renderBehaviorAnchorTemplateLine("plan")}
834
+
800
835
  ## Plan Header
801
836
  - **Goal:** (one sentence — what this plan delivers)
802
837
  - **Architecture:** (2-3 sentences — approach + key boundaries)
@@ -912,12 +947,14 @@ Execution rule: complete and verify each batch before starting the next batch.
912
947
  - **Inline recipe (if Inline executor):** TDD loop unit-by-unit with batch checkpoints
913
948
 
914
949
  ## Plan Quality Scan
950
+ <!-- linter-meta -->
915
951
  - Placeholder scan:
916
952
  - Scanned tokens: \`TODO\`, \`TBD\`, \`FIXME\`, \`<fill-in>\`, \`<your-*-here>\`, \`xxx\`, bare ellipsis in task rows.
917
953
  - Hits: 0 (required for WAIT_FOR_CONFIRM to resolve).
918
954
  - Scope reduction language scan:
919
955
  - Scanned phrases: \`v1\`, \`for now\`, \`later\`, \`temporary\`, \`placeholder\`, \`mock for now\`, \`hardcoded for now\`, \`will improve later\`.
920
956
  - Hits: 0 (required when Locked Decisions section is non-empty; reference D-XX IDs from scope).
957
+ <!-- /linter-meta -->
921
958
 
922
959
  ## WAIT_FOR_CONFIRM
923
960
  - Status: pending
@@ -930,6 +967,8 @@ Execution rule: complete and verify each batch before starting the next batch.
930
967
 
931
968
  # TDD Artifact
932
969
 
970
+ ${renderBehaviorAnchorTemplateLine("tdd")}
971
+
933
972
  ## Upstream Handoff
934
973
  - Source artifacts: \`04-spec.md\` plus the active track's upstream source item (plan slice on standard/medium, spec acceptance item or bug reproduction slice on quick).
935
974
  - Decisions carried forward:
@@ -1126,6 +1165,8 @@ Execution rule: complete and verify each batch before starting the next batch.
1126
1165
 
1127
1166
  # Review Artifact
1128
1167
 
1168
+ ${renderBehaviorAnchorTemplateLine("review")}
1169
+
1129
1170
  ## Upstream Handoff
1130
1171
  - Source artifacts: \`04-spec.md\`, \`06-tdd.md\`, plus the active track's upstream source item when available.
1131
1172
  - Decisions carried forward:
@@ -1298,6 +1339,8 @@ Execution rule: complete and verify each batch before starting the next batch.
1298
1339
 
1299
1340
  # Ship Artifact
1300
1341
 
1342
+ ${renderBehaviorAnchorTemplateLine("ship")}
1343
+
1301
1344
  ## Upstream Handoff
1302
1345
  - Source artifacts: \`06-tdd.md\`, \`07-review.md\`
1303
1346
  - Decisions carried forward:
@@ -60,6 +60,15 @@ export type DelegationEntry = {
60
60
  taskId?: string;
61
61
  waiverReason?: string;
62
62
  acceptedBy?: DelegationWaiverAcceptedBy;
63
+ /**
64
+ * Waiver approval token captured from `cclaw-cli internal waiver-grant`.
65
+ * Present on waiver rows written after v6.7.0. Legacy waiver rows omit
66
+ * these fields and are surfaced as the advisory linter finding
67
+ * `waiver_legacy_provenance`.
68
+ */
69
+ approvalToken?: string;
70
+ approvalReason?: string;
71
+ approvalIssuedAt?: string;
63
72
  ts?: string;
64
73
  /**
65
74
  * Run id the entry belongs to. Older ledgers written before 0.5.17 may omit this;
@@ -199,6 +199,9 @@ function isDelegationEntry(value) {
199
199
  (o.taskId === undefined || typeof o.taskId === "string") &&
200
200
  (o.waiverReason === undefined || typeof o.waiverReason === "string") &&
201
201
  (o.acceptedBy === undefined || o.acceptedBy === "user-flag") &&
202
+ (o.approvalToken === undefined || typeof o.approvalToken === "string") &&
203
+ (o.approvalReason === undefined || typeof o.approvalReason === "string") &&
204
+ (o.approvalIssuedAt === undefined || typeof o.approvalIssuedAt === "string") &&
202
205
  waiverOk &&
203
206
  (o.runId === undefined || typeof o.runId === "string") &&
204
207
  (o.fulfillmentMode === undefined ||
@@ -12,6 +12,7 @@ import { extractReviewLoopEnvelopeFromArtifact } from "../../content/review-loop
12
12
  import { unique } from "./helpers.js";
13
13
  import { AUTO_REVIEW_LOOP_GATE_BY_STAGE, reviewLoopArtifactFixHint, reviewLoopEnvelopeExample, validateGateEvidenceShape } from "./review-loop.js";
14
14
  import { ensureProactiveDelegationTrace } from "./proactive-delegation-trace.js";
15
+ import { consumeWaiverToken } from "../waiver-grant.js";
15
16
  function resolveSuccessorTransition(stage, track, transitionTargets, satisfiedGuards, selectedTransitionGuards) {
16
17
  const natural = transitionTargets[0] ?? null;
17
18
  const specialTargets = transitionTargets.filter((target) => target !== natural);
@@ -542,9 +543,30 @@ export async function runAdvanceStage(projectRoot, args, io) {
542
543
  }
543
544
  return 1;
544
545
  }
546
+ let approvalRecord = null;
547
+ if (args.acceptProactiveWaiver) {
548
+ const tokenRaw = args.acceptProactiveWaiverToken?.trim() ?? "";
549
+ if (tokenRaw.length === 0) {
550
+ io.stderr.write(`cclaw internal advance-stage: --accept-proactive-waiver now requires =<token>. Run \`cclaw-cli internal waiver-grant --stage ${args.stage} --reason "<why safe>"\` to issue one, then rerun with --accept-proactive-waiver=<token>.\n`);
551
+ return 2;
552
+ }
553
+ const consumed = await consumeWaiverToken(projectRoot, {
554
+ stage: args.stage,
555
+ token: tokenRaw,
556
+ consumedBy: "advance-stage"
557
+ });
558
+ if (!consumed.ok) {
559
+ io.stderr.write(`cclaw internal advance-stage: waiver token rejected (${consumed.reason}): ${consumed.detail}. Issue a fresh token via \`cclaw-cli internal waiver-grant --stage ${args.stage} --reason "<why safe>"\`.\n`);
560
+ return 2;
561
+ }
562
+ approvalRecord = consumed.record;
563
+ }
545
564
  const proactiveTrace = await ensureProactiveDelegationTrace(projectRoot, args.stage, {
546
565
  acceptWaiver: args.acceptProactiveWaiver,
547
566
  waiverReason: args.acceptProactiveWaiverReason,
567
+ approvalToken: approvalRecord?.token,
568
+ approvalReason: approvalRecord?.reason,
569
+ approvalIssuedAt: approvalRecord?.issuedAt,
548
570
  discoveryMode: flowState.discoveryMode,
549
571
  repoSignals: flowState.repoSignals
550
572
  });
@@ -600,7 +622,7 @@ export async function runAdvanceStage(projectRoot, args, io) {
600
622
  currentStage: successor ?? args.stage,
601
623
  interactionHints
602
624
  };
603
- await writeFlowState(projectRoot, finalState);
625
+ await writeFlowState(projectRoot, finalState, { writerSubsystem: "advance-stage" });
604
626
  if (args.quiet) {
605
627
  io.stdout.write(`${JSON.stringify({
606
628
  ok: true,
@@ -8,6 +8,14 @@ export interface AdvanceStageArgs {
8
8
  waiverReason?: string;
9
9
  acceptProactiveWaiver: boolean;
10
10
  acceptProactiveWaiverReason?: string;
11
+ /**
12
+ * Approval token issued by `cclaw-cli internal waiver-grant`. Required
13
+ * (via `--accept-proactive-waiver=<token>`) whenever the caller asserts
14
+ * `acceptProactiveWaiver`. Legacy `--accept-proactive-waiver` without a
15
+ * token is still parsed but rejected downstream by the advance-stage
16
+ * handler so operators see the error at runtime.
17
+ */
18
+ acceptProactiveWaiverToken?: string;
11
19
  skipQuestions: boolean;
12
20
  quiet: boolean;
13
21
  json: boolean;
@@ -12,6 +12,7 @@ export function parseAdvanceStageArgs(tokens) {
12
12
  let waiverReason;
13
13
  let acceptProactiveWaiver = false;
14
14
  let acceptProactiveWaiverReason;
15
+ let acceptProactiveWaiverToken;
15
16
  let skipQuestions = false;
16
17
  let quiet = false;
17
18
  let json = false;
@@ -81,6 +82,11 @@ export function parseAdvanceStageArgs(tokens) {
81
82
  acceptProactiveWaiver = true;
82
83
  continue;
83
84
  }
85
+ if (token.startsWith("--accept-proactive-waiver=")) {
86
+ acceptProactiveWaiver = true;
87
+ acceptProactiveWaiverToken = token.slice("--accept-proactive-waiver=".length).trim();
88
+ continue;
89
+ }
84
90
  if (token === "--skip-questions") {
85
91
  skipQuestions = true;
86
92
  continue;
@@ -107,6 +113,7 @@ export function parseAdvanceStageArgs(tokens) {
107
113
  waiverReason,
108
114
  acceptProactiveWaiver,
109
115
  acceptProactiveWaiverReason,
116
+ acceptProactiveWaiverToken,
110
117
  skipQuestions,
111
118
  quiet,
112
119
  json
@@ -16,6 +16,9 @@ export interface ProactiveDelegationTraceResult {
16
16
  export declare function ensureProactiveDelegationTrace(projectRoot: string, stage: FlowStage, options: {
17
17
  acceptWaiver: boolean;
18
18
  waiverReason?: string;
19
+ approvalToken?: string;
20
+ approvalReason?: string;
21
+ approvalIssuedAt?: string;
19
22
  discoveryMode: DiscoveryMode;
20
23
  repoSignals?: RepoSignals;
21
24
  }): Promise<ProactiveDelegationTraceResult>;
@@ -31,7 +31,11 @@ export async function ensureProactiveDelegationTrace(projectRoot, stage, options
31
31
  return { missingRules: [] };
32
32
  if (!options.acceptWaiver)
33
33
  return { missingRules };
34
- const waiverReason = options.waiverReason?.trim() || "accepted via --accept-proactive-waiver";
34
+ const approvalToken = options.approvalToken?.trim();
35
+ const approvalReason = options.approvalReason?.trim();
36
+ const waiverReason = options.waiverReason?.trim() ||
37
+ approvalReason ||
38
+ "accepted via --accept-proactive-waiver";
35
39
  for (const rule of missingRules) {
36
40
  await appendDelegation(projectRoot, {
37
41
  stage,
@@ -42,6 +46,9 @@ export async function ensureProactiveDelegationTrace(projectRoot, stage, options
42
46
  acceptedBy: "user-flag",
43
47
  conditionTrigger: rule.when,
44
48
  skill: rule.skill,
49
+ ...(approvalToken ? { approvalToken } : {}),
50
+ ...(approvalReason ? { approvalReason } : {}),
51
+ ...(options.approvalIssuedAt ? { approvalIssuedAt: options.approvalIssuedAt } : {}),
45
52
  ts: new Date().toISOString()
46
53
  });
47
54
  }
@@ -40,7 +40,7 @@ export async function runRewind(projectRoot, args, io) {
40
40
  const staleStages = { ...current.staleStages };
41
41
  delete staleStages[args.targetStage];
42
42
  const nextState = { ...current, staleStages };
43
- await writeFlowState(projectRoot, nextState);
43
+ await writeFlowState(projectRoot, nextState, { writerSubsystem: "rewind-ack" });
44
44
  const payload = {
45
45
  ok: true,
46
46
  command: "rewind",
@@ -85,7 +85,7 @@ export async function runRewind(projectRoot, args, io) {
85
85
  staleStages,
86
86
  rewinds: [...current.rewinds, record]
87
87
  };
88
- await writeFlowState(projectRoot, nextState);
88
+ await writeFlowState(projectRoot, nextState, { writerSubsystem: "rewind" });
89
89
  const payload = {
90
90
  ok: true,
91
91
  command: "rewind",
@@ -209,7 +209,10 @@ export async function runStartFlow(projectRoot, args, io) {
209
209
  }
210
210
  const repoSignals = await collectRepoSignals(projectRoot);
211
211
  nextState = { ...nextState, repoSignals };
212
- await writeFlowState(projectRoot, nextState, { allowReset: true });
212
+ await writeFlowState(projectRoot, nextState, {
213
+ allowReset: true,
214
+ writerSubsystem: "start-flow"
215
+ });
213
216
  await appendIdeaArtifact(projectRoot, args, current);
214
217
  const successPayload = {
215
218
  ok: true,