cclaw-cli 0.51.21 → 0.51.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -13
- package/dist/config.d.ts +8 -1
- package/dist/config.js +9 -6
- package/dist/content/examples.js +2 -2
- package/dist/content/hook-manifest.d.ts +2 -4
- package/dist/content/hook-manifest.js +5 -7
- package/dist/content/learnings.js +5 -2
- package/dist/content/meta-skill.d.ts +1 -0
- package/dist/content/meta-skill.js +16 -9
- package/dist/content/next-command.js +2 -2
- package/dist/content/node-hooks.js +14 -4
- package/dist/content/review-loop.js +15 -5
- package/dist/content/review-prompts.js +1 -1
- package/dist/content/skills.js +16 -11
- package/dist/content/stage-command.d.ts +2 -0
- package/dist/content/stage-command.js +17 -0
- package/dist/content/stage-schema.js +1 -0
- package/dist/content/stages/brainstorm.js +3 -3
- package/dist/content/stages/design.js +18 -17
- package/dist/content/stages/plan.js +2 -1
- package/dist/content/stages/review.js +15 -15
- package/dist/content/stages/scope.js +14 -14
- package/dist/content/stages/spec.js +7 -5
- package/dist/content/stages/tdd.js +11 -4
- package/dist/content/start-command.d.ts +4 -3
- package/dist/content/start-command.js +21 -17
- package/dist/content/subagents.js +14 -4
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +49 -29
- package/dist/content/track-render-context.js +7 -0
- package/dist/content/view-command.js +3 -1
- package/dist/delegation.d.ts +2 -2
- package/dist/delegation.js +40 -13
- package/dist/doctor-registry.js +1 -1
- package/dist/doctor.js +222 -34
- package/dist/gate-evidence.js +19 -7
- package/dist/harness-adapters.d.ts +14 -11
- package/dist/harness-adapters.js +154 -22
- package/dist/install.js +116 -28
- package/dist/internal/advance-stage.js +90 -11
- package/dist/knowledge-store.d.ts +4 -1
- package/dist/knowledge-store.js +24 -14
- package/dist/retro-gate.d.ts +1 -0
- package/dist/retro-gate.js +9 -9
- package/dist/run-archive.js +19 -1
- package/dist/run-persistence.js +6 -2
- package/dist/tdd-cycle.js +6 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -344,8 +344,9 @@ The `tdd` stage is not prose guidance. It requires:
|
|
|
344
344
|
- optional **REFACTOR** pass with coverage preservation
|
|
345
345
|
|
|
346
346
|
`/cc-next` will not advance past `tdd` until the delegation log shows the
|
|
347
|
-
subagent as `completed
|
|
348
|
-
|
|
347
|
+
subagent as `completed`. Codex and OpenCode use generated native subagents
|
|
348
|
+
by default; a role-switch row is only a degraded fallback and must include
|
|
349
|
+
`evidenceRefs` — see [Harness support](#harness-support).
|
|
349
350
|
|
|
350
351
|
---
|
|
351
352
|
|
|
@@ -391,25 +392,25 @@ closes every real gap with a documented fallback — not a silent waiver.
|
|
|
391
392
|
|---|---|---|---|---|
|
|
392
393
|
| Claude Code | full (named subagents) | `native` | full | `AskUserQuestion` |
|
|
393
394
|
| Cursor | generic Task dispatcher | `generic-dispatch` | full | `AskQuestion` |
|
|
394
|
-
| OpenCode |
|
|
395
|
-
| OpenAI Codex |
|
|
395
|
+
| OpenCode | native subagents (`.opencode/agents`) | `native` | plugin | `question` (permission-gated; `permission.question: "allow"`) |
|
|
396
|
+
| OpenAI Codex | native parallel subagents (`.codex/agents`) | `native` | limited (Bash-only `PreToolUse`/`PostToolUse`; requires `codex_hooks` feature flag) | `request_user_input` (experimental; Plan / Collaboration mode) |
|
|
396
397
|
|
|
397
398
|
What the fallbacks mean:
|
|
398
399
|
|
|
399
|
-
- `native` — Claude
|
|
400
|
-
workers; cclaw records them with `fulfillmentMode: "isolated"`.
|
|
400
|
+
- `native` — Claude, OpenCode, and Codex run mandatory delegations in
|
|
401
|
+
isolated subagent workers; cclaw records them with `fulfillmentMode: "isolated"`.
|
|
401
402
|
- `generic-dispatch` — Cursor has a real Task tool with a fixed
|
|
402
403
|
vocabulary of `subagent_type`s (`explore`, `generalPurpose`, …).
|
|
403
404
|
cclaw maps each named agent (planner / reviewer / test-author /
|
|
404
405
|
security-reviewer / doc-updater) onto the generic dispatcher with a
|
|
405
406
|
structured role prompt.
|
|
406
|
-
- `role-switch` —
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
407
|
+
- `role-switch` — degraded fallback only when the active runtime cannot
|
|
408
|
+
expose its declared dispatch surface. The agent announces
|
|
409
|
+
`## cclaw role-switch: <stage>/<agent> (<mode>)`, loads
|
|
410
|
+
`.cclaw/agents/<agent>.md`, writes outputs to the stage artifact, and
|
|
411
|
+
records a delegation row with `fulfillmentMode: "role-switch"` plus at
|
|
412
|
+
least one `evidenceRef`. A role-switch `completed` row without
|
|
413
|
+
evidenceRefs is classified as `missingEvidence` by `cclaw doctor`.
|
|
413
414
|
- `waiver` — reserved. Only fires auto-waivers if every installed
|
|
414
415
|
harness declares it. Currently unused — v0.33 removed the old
|
|
415
416
|
Codex-only auto-waiver path.
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { CclawConfig, FlowTrack, HarnessId, LanguageRulePack } from "./types.js";
|
|
2
|
+
export interface ConfigWarningState {
|
|
3
|
+
emitted: Set<string>;
|
|
4
|
+
}
|
|
5
|
+
export interface ReadConfigOptions {
|
|
6
|
+
warningState?: ConfigWarningState;
|
|
7
|
+
}
|
|
8
|
+
export declare function createConfigWarningState(): ConfigWarningState;
|
|
2
9
|
export declare class InvalidConfigError extends Error {
|
|
3
10
|
constructor(message: string);
|
|
4
11
|
}
|
|
@@ -34,7 +41,7 @@ export declare function createDefaultConfig(harnesses?: HarnessId[], defaultTrac
|
|
|
34
41
|
* never surprise a user who intentionally cleared the list.
|
|
35
42
|
*/
|
|
36
43
|
export declare function detectLanguageRulePacks(projectRoot: string): Promise<LanguageRulePack[]>;
|
|
37
|
-
export declare function readConfig(projectRoot: string): Promise<CclawConfig>;
|
|
44
|
+
export declare function readConfig(projectRoot: string, options?: ReadConfigOptions): Promise<CclawConfig>;
|
|
38
45
|
/**
|
|
39
46
|
* Fields that live on the populated runtime `CclawConfig` but are considered
|
|
40
47
|
* "advanced" — we keep them in the in-memory object so downstream callers
|
package/dist/config.js
CHANGED
|
@@ -56,13 +56,15 @@ const MINIMAL_CONFIG_KEYS = [
|
|
|
56
56
|
];
|
|
57
57
|
const DEFAULT_SLICE_REVIEW_THRESHOLD = 5;
|
|
58
58
|
const DEFAULT_SLICE_REVIEW_TRACKS = ["standard"];
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
export function createConfigWarningState() {
|
|
60
|
+
return { emitted: new Set() };
|
|
61
|
+
}
|
|
62
|
+
function emitConfigWarningOnce(warningState, code, message) {
|
|
61
63
|
const key = `${code}:${message}`;
|
|
62
|
-
if (
|
|
64
|
+
if (warningState.emitted.has(key)) {
|
|
63
65
|
return;
|
|
64
66
|
}
|
|
65
|
-
|
|
67
|
+
warningState.emitted.add(key);
|
|
66
68
|
process.emitWarning(message, { code });
|
|
67
69
|
}
|
|
68
70
|
function sameStringArray(a, b) {
|
|
@@ -196,7 +198,8 @@ export async function detectLanguageRulePacks(projectRoot) {
|
|
|
196
198
|
}
|
|
197
199
|
return [...new Set(detected)];
|
|
198
200
|
}
|
|
199
|
-
export async function readConfig(projectRoot) {
|
|
201
|
+
export async function readConfig(projectRoot, options = {}) {
|
|
202
|
+
const warningState = options.warningState ?? createConfigWarningState();
|
|
200
203
|
const fullPath = configPath(projectRoot);
|
|
201
204
|
if (!(await exists(fullPath))) {
|
|
202
205
|
return createDefaultConfig();
|
|
@@ -269,7 +272,7 @@ export async function readConfig(projectRoot) {
|
|
|
269
272
|
if (tddTestGlobsRaw !== undefined &&
|
|
270
273
|
explicitTddTestPathPatterns !== undefined &&
|
|
271
274
|
!sameStringArray(tddTestGlobs, explicitTddTestPathPatterns)) {
|
|
272
|
-
emitConfigWarningOnce("CCLAW_CONFIG_DEPRECATED_TDD_TEST_GLOBS", `[cclaw] Both "tddTestGlobs" (deprecated) and "tdd.testPathPatterns" are set in ${fullPath}. ` +
|
|
275
|
+
emitConfigWarningOnce(warningState, "CCLAW_CONFIG_DEPRECATED_TDD_TEST_GLOBS", `[cclaw] Both "tddTestGlobs" (deprecated) and "tdd.testPathPatterns" are set in ${fullPath}. ` +
|
|
273
276
|
`Using "tdd.testPathPatterns".`);
|
|
274
277
|
}
|
|
275
278
|
const resolvedTddTestPathPatterns = [
|
package/dist/content/examples.js
CHANGED
|
@@ -726,8 +726,8 @@ const DOMAIN_LABELS = {
|
|
|
726
726
|
export const RESEARCH_FLEET_USAGE_EXAMPLE = [
|
|
727
727
|
"Before drafting `03-design.md`, run `research/research-fleet.md` once and",
|
|
728
728
|
"capture all four lenses in `.cclaw/artifacts/02a-research.md`.",
|
|
729
|
-
"Dispatch semantics by harness: Claude/
|
|
730
|
-
"
|
|
729
|
+
"Dispatch semantics by harness: Claude/OpenCode/Codex = native subagents;",
|
|
730
|
+
"Cursor = generic-dispatch Task mapping; role-switch only as degraded fallback.",
|
|
731
731
|
"Design must include a `Research Fleet Synthesis` section that maps each",
|
|
732
732
|
"lens to concrete architecture decisions and risks."
|
|
733
733
|
].join(" ");
|
|
@@ -45,14 +45,12 @@ export interface HookHandlerSpec {
|
|
|
45
45
|
description: string;
|
|
46
46
|
/**
|
|
47
47
|
* Semantic event id used by `HOOK_EVENTS_BY_HARNESS` / docs.
|
|
48
|
-
* `null` means this handler contributes no semantic coverage row
|
|
49
|
-
* (e.g. `verify-current-state` on codex is a supplementary guard,
|
|
50
|
-
* not a top-level semantic event).
|
|
48
|
+
* `null` means this handler contributes no semantic coverage row.
|
|
51
49
|
*/
|
|
52
50
|
semantic: HookSemanticEvent | null;
|
|
53
51
|
bindings: Partial<Record<HookManifestHarness, HookBinding[]>>;
|
|
54
52
|
}
|
|
55
|
-
export declare const HOOK_SEMANTIC_EVENTS: readonly ["session_rehydrate", "pre_tool_prompt_guard", "pre_tool_workflow_guard", "post_tool_context_monitor", "stop_handoff", "precompact_compat"];
|
|
53
|
+
export declare const HOOK_SEMANTIC_EVENTS: readonly ["session_rehydrate", "pre_tool_prompt_guard", "pre_tool_workflow_guard", "post_tool_context_monitor", "stop_handoff", "precompact_compat", "strict_state_verify"];
|
|
56
54
|
export type HookSemanticEvent = (typeof HOOK_SEMANTIC_EVENTS)[number];
|
|
57
55
|
export declare const HOOK_MANIFEST: readonly HookHandlerSpec[];
|
|
58
56
|
export interface EventGroup {
|
|
@@ -35,7 +35,8 @@ export const HOOK_SEMANTIC_EVENTS = [
|
|
|
35
35
|
"pre_tool_workflow_guard",
|
|
36
36
|
"post_tool_context_monitor",
|
|
37
37
|
"stop_handoff",
|
|
38
|
-
"precompact_compat"
|
|
38
|
+
"precompact_compat",
|
|
39
|
+
"strict_state_verify"
|
|
39
40
|
];
|
|
40
41
|
export const HOOK_MANIFEST = [
|
|
41
42
|
{
|
|
@@ -73,10 +74,7 @@ export const HOOK_MANIFEST = [
|
|
|
73
74
|
bindings: {
|
|
74
75
|
claude: [{ event: "PreToolUse", matcher: "Write|Edit|MultiEdit|NotebookEdit|Bash" }],
|
|
75
76
|
cursor: [{ event: "preToolUse", matcher: "*" }],
|
|
76
|
-
codex: [
|
|
77
|
-
{ event: "UserPromptSubmit" },
|
|
78
|
-
{ event: "PreToolUse", matcher: "Bash|bash" }
|
|
79
|
-
]
|
|
77
|
+
codex: [{ event: "PreToolUse", matcher: "Bash|bash" }]
|
|
80
78
|
}
|
|
81
79
|
},
|
|
82
80
|
{
|
|
@@ -112,8 +110,8 @@ export const HOOK_MANIFEST = [
|
|
|
112
110
|
},
|
|
113
111
|
{
|
|
114
112
|
handler: "verify-current-state",
|
|
115
|
-
description: "Supplementary
|
|
116
|
-
semantic:
|
|
113
|
+
description: "Supplementary Codex strict-mode guard that runs on UserPromptSubmit to assert the live state matches the flow.",
|
|
114
|
+
semantic: "strict_state_verify",
|
|
117
115
|
bindings: {
|
|
118
116
|
codex: [{ event: "UserPromptSubmit" }]
|
|
119
117
|
}
|
|
@@ -78,7 +78,7 @@ Do not invent alternate stores (no markdown mirror, no SQLite, no per-stage file
|
|
|
78
78
|
|
|
79
79
|
Exactly one JSON object per line. Required fields must appear in the order:
|
|
80
80
|
\`type, trigger, action, confidence, domain, stage, origin_stage, origin_run, frequency, universality, maturity, created, first_seen_ts, last_seen_ts, project\`.
|
|
81
|
-
Optional fields \`source\` and \`
|
|
81
|
+
Optional fields \`source\`, \`severity\`, \`supersedes\`, and \`superseded_by\` may be appended after \`project\`.
|
|
82
82
|
|
|
83
83
|
\`\`\`json
|
|
84
84
|
{"type":"pattern","trigger":"when reviewing external payloads","action":"parse through zod before touching service layer","confidence":"high","domain":"api","stage":"review","origin_stage":"review","origin_run":"payload-hardening","frequency":1,"universality":"project","maturity":"raw","created":"2026-04-14T12:00:00Z","first_seen_ts":"2026-04-14T12:00:00Z","last_seen_ts":"2026-04-14T12:00:00Z","project":"cclaw"}
|
|
@@ -103,12 +103,15 @@ Optional fields \`source\` and \`severity\` may be appended after \`project\`.
|
|
|
103
103
|
| \`project\` | string \\| null | yes | Repo or scope name. Use \`null\` when the entry crosses projects. |
|
|
104
104
|
| \`source\` | \`"stage" \\| "retro" \\| "compound" \\| "ideate" \\| "manual" \\| null\` | no | Origin channel for the entry when known. |
|
|
105
105
|
| \`severity\` | \`"critical" \\| "important" \\| "suggestion"\` | no | Priority signal for compound lifts; \`critical\` enables single-hit override in compound readiness analysis. |
|
|
106
|
+
| \`supersedes\` | string[] | no | Non-empty IDs/slugs of older entries this entry refreshes. Use only for clear replacements discovered during compound closeout or curation. |
|
|
107
|
+
| \`superseded_by\` | string | no | Non-empty ID/slug of the newer entry that refreshes this one. Use only when marking stale guidance as replaced. |
|
|
106
108
|
|
|
107
109
|
Rules:
|
|
108
110
|
- No other fields beyond the table above. Extra keys are forbidden and MUST be rejected by any writer.
|
|
109
111
|
- Every required-null field must be emitted explicitly as \`null\` (not omitted). This keeps the file grep-friendly.
|
|
110
112
|
- Append-only: never rewrite or delete a historical line. Corrections are new
|
|
111
|
-
entries whose \`trigger\` clearly supersedes the earlier one
|
|
113
|
+
entries whose \`trigger\` clearly supersedes the earlier one; add \`supersedes\` /
|
|
114
|
+
\`superseded_by\` only when the replacement relationship is clear.
|
|
112
115
|
- Keep each entry one line. No pretty-printing. No trailing commas.
|
|
113
116
|
|
|
114
117
|
## Curation policy (target: ≤ 50 active entries)
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { conversationLanguagePolicyMarkdown } from "./language-policy.js";
|
|
2
2
|
import { CLOSEOUT_CHAIN, closeoutChainInline, closeoutFlowMapSentence, closeoutProtocolBehaviorSentence } from "./closeout-guidance.js";
|
|
3
3
|
export const META_SKILL_NAME = "using-cclaw";
|
|
4
|
+
export const META_SKILL_GENERATED_HELPER_SKILLS = [
|
|
5
|
+
"subagent-dev",
|
|
6
|
+
"parallel-dispatch",
|
|
7
|
+
"session",
|
|
8
|
+
"iron-laws"
|
|
9
|
+
];
|
|
10
|
+
function generatedHelperSkillList() {
|
|
11
|
+
return META_SKILL_GENERATED_HELPER_SKILLS.map((name) => `\`${name}\``).join(", ");
|
|
12
|
+
}
|
|
4
13
|
export function usingCclawSkillMarkdown() {
|
|
5
14
|
return `---
|
|
6
15
|
name: using-cclaw
|
|
@@ -14,7 +23,7 @@ description: "Routing brain for cclaw. Decide whether to start/resume a stage, a
|
|
|
14
23
|
1. User message in current turn.
|
|
15
24
|
2. Active stage skill and command contract.
|
|
16
25
|
3. This routing file.
|
|
17
|
-
4.
|
|
26
|
+
4. Generated cclaw helper skills, research playbooks, and enabled rule packs.
|
|
18
27
|
5. Default model behavior.
|
|
19
28
|
|
|
20
29
|
If the user explicitly overrides a stage rule, record it in the artifact.
|
|
@@ -25,9 +34,7 @@ ${conversationLanguagePolicyMarkdown()}
|
|
|
25
34
|
If \`.cclaw/state/flow-state.json\` exists and \`currentStage\` is set,
|
|
26
35
|
load the matching stage SKILL before producing **substantive** work
|
|
27
36
|
(artifact edits, code, structured clarifying questions). Do not improvise
|
|
28
|
-
from memory.
|
|
29
|
-
triggers it (security, performance, debugging, docs, finishing-a-branch,
|
|
30
|
-
verification-before-completion).
|
|
37
|
+
from memory. Load only generated helper surfaces that actually exist in this install: ${generatedHelperSkillList()}, research playbooks, review prompts, or enabled language rule packs under \`.cclaw/rules/lang/\`. Do not invent helper-skill names beyond those generated surfaces.
|
|
31
38
|
|
|
32
39
|
Substantive vs. non-substantive:
|
|
33
40
|
|
|
@@ -70,7 +77,7 @@ Task arrives
|
|
|
70
77
|
| Class | Route |
|
|
71
78
|
|---|---|
|
|
72
79
|
| non-trivial software work | \`/cc <idea>\` |
|
|
73
|
-
| trivial software fix | \`/cc <idea>\` (quick
|
|
80
|
+
| trivial software fix | \`/cc <idea>\` (quick track) |
|
|
74
81
|
| bugfix with clear repro | \`/cc <idea>\` and enforce RED-first in tdd |
|
|
75
82
|
| pure question / conversation | answer directly |
|
|
76
83
|
| non-software work | answer directly |
|
|
@@ -115,10 +122,10 @@ Use the current stage skill plus \`.cclaw/state/flow-state.json\` for orientatio
|
|
|
115
122
|
|
|
116
123
|
Use built-in judgment only when triggered by the current task:
|
|
117
124
|
|
|
118
|
-
-
|
|
119
|
-
-
|
|
120
|
-
-
|
|
121
|
-
- iron-laws as policy arbitration when instructions conflict
|
|
125
|
+
- generated subagent context skills for mandatory review/delegation contracts
|
|
126
|
+
- research playbooks and review prompts when a stage explicitly calls for them
|
|
127
|
+
- inline verification and ship/finalization sections in the active stage skill
|
|
128
|
+
- \`iron-laws\` as policy arbitration when instructions conflict
|
|
122
129
|
- language rule packs from \`.cclaw/config.yaml\` when enabled
|
|
123
130
|
|
|
124
131
|
## Protocol Behavior
|
|
@@ -112,7 +112,7 @@ ${ralphLoopContractSnippet()}
|
|
|
112
112
|
- If \`track === "quick"\`, the critical path is **spec → tdd → review → ship**. When advancing, skip any stage listed in \`skippedStages\` — i.e. after the current stage completes, pick the next stage that is NOT in \`skippedStages\`.
|
|
113
113
|
- If \`track === "medium"\`, the critical path is **brainstorm → spec → plan → tdd → review → ship**. Scope and design are intentionally skipped unless the run is reclassified to standard.
|
|
114
114
|
- If \`track === "standard"\`, advance through all 8 stages in their natural order.
|
|
115
|
-
- Never reintroduce a skipped stage mid-run. If
|
|
115
|
+
- Never manually reintroduce a skipped stage mid-run. If evidence shows the track was wrong, stop and use the managed start-flow helper with \`--reclassify\`; only that managed reclassification may add upstream stages back into the active track.
|
|
116
116
|
|
|
117
117
|
## Resume Semantics
|
|
118
118
|
|
|
@@ -130,7 +130,7 @@ When orchestrated by another skill/subagent, emit exactly one JSON envelope and
|
|
|
130
130
|
no narrative text:
|
|
131
131
|
|
|
132
132
|
\`\`\`json
|
|
133
|
-
{"version":"1","kind":"gate-result","stage":"
|
|
133
|
+
{"version":"1","kind":"gate-result","stage":"<currentStage>","payload":{"command":"/cc-next","decision":"resume_or_advance","nextStage":"<nextStage>"},"emittedAt":"<ISO-8601>"}
|
|
134
134
|
\`\`\`
|
|
135
135
|
|
|
136
136
|
Validate envelopes with:
|
|
@@ -1117,12 +1117,21 @@ async function handleStopHandoff(runtime) {
|
|
|
1117
1117
|
return 1;
|
|
1118
1118
|
}
|
|
1119
1119
|
|
|
1120
|
+
const closeoutObj = toObject(state.raw.closeout) || {};
|
|
1121
|
+
const shipSubstate = typeof closeoutObj.shipSubstate === "string" ? closeoutObj.shipSubstate : "idle";
|
|
1122
|
+
const closeoutContext =
|
|
1123
|
+
state.currentStage === "ship" || shipSubstate !== "idle"
|
|
1124
|
+
? " closeout.shipSubstate=" + shipSubstate + "; closeout chain=retro -> compound -> archive; continue closeout with /cc-next."
|
|
1125
|
+
: "";
|
|
1126
|
+
|
|
1120
1127
|
const message =
|
|
1121
1128
|
"Cclaw: session ending (stage=" +
|
|
1122
1129
|
state.currentStage +
|
|
1123
1130
|
", run=" +
|
|
1124
1131
|
state.activeRunId +
|
|
1125
|
-
").
|
|
1132
|
+
")." +
|
|
1133
|
+
closeoutContext +
|
|
1134
|
+
" Active artifacts stay in " +
|
|
1126
1135
|
RUNTIME_ROOT +
|
|
1127
1136
|
"/artifacts until archive. Before stopping: (1) confirm flow-state reflects reality, (2) ensure artifact changes match current intent, (3) if you discovered a non-obvious rule/pattern during stage work, add it to the current artifact ## Learnings section so stage-complete can harvest it, (4) commit or revert pending changes.";
|
|
1128
1137
|
|
|
@@ -1627,7 +1636,7 @@ async function handleVerifyCurrentState(runtime) {
|
|
|
1627
1636
|
process.stderr.write(result.stderr.trim().length > 0
|
|
1628
1637
|
? result.stderr
|
|
1629
1638
|
: "[cclaw] hook: local Node runtime entrypoint is required for verify-current-state\\n");
|
|
1630
|
-
return 1;
|
|
1639
|
+
return mode === "strict" ? 1 : 0;
|
|
1631
1640
|
}
|
|
1632
1641
|
if (mode === "strict") {
|
|
1633
1642
|
if (result.code !== 0) {
|
|
@@ -1650,9 +1659,10 @@ async function handleVerifyCurrentState(runtime) {
|
|
|
1650
1659
|
function normalizeHookName(rawName) {
|
|
1651
1660
|
const value = normalizeText(rawName).toLowerCase();
|
|
1652
1661
|
if (value === "session-start") return "session-start";
|
|
1653
|
-
if (value === "stop-handoff") return "stop-handoff";
|
|
1662
|
+
if (value === "stop-handoff" || value === "stop") return "stop-handoff";
|
|
1654
1663
|
if (value === "stop-checkpoint") return "stop-handoff";
|
|
1655
|
-
if (value === "pre-compact") return "pre-compact";
|
|
1664
|
+
if (value === "pre-compact" || value === "precompact") return "pre-compact";
|
|
1665
|
+
if (value === "session-rehydrate") return "session-start";
|
|
1656
1666
|
if (value === "prompt-guard") return "prompt-guard";
|
|
1657
1667
|
if (value === "workflow-guard") return "workflow-guard";
|
|
1658
1668
|
if (value === "context-monitor") return "context-monitor";
|
|
@@ -456,6 +456,9 @@ export function buildReviewLoopEnvelope(args) {
|
|
|
456
456
|
function formatScore(value) {
|
|
457
457
|
return clampScore(value).toFixed(3);
|
|
458
458
|
}
|
|
459
|
+
function reviewLoopHeading(stage) {
|
|
460
|
+
return stage === "scope" ? "Scope Outside Voice Loop" : "Design Outside Voice Loop";
|
|
461
|
+
}
|
|
459
462
|
function finalEnvelopeScore(envelope) {
|
|
460
463
|
if (envelope.iterations.length === 0)
|
|
461
464
|
return 0;
|
|
@@ -486,7 +489,8 @@ export function renderReviewLoopSummarySection(envelope) {
|
|
|
486
489
|
})
|
|
487
490
|
.join("\n")
|
|
488
491
|
: "| 0 | 0.000 | 0 |";
|
|
489
|
-
|
|
492
|
+
const heading = reviewLoopHeading(envelope.stage);
|
|
493
|
+
return `## ${heading}
|
|
490
494
|
| Iteration | Quality Score | Findings |
|
|
491
495
|
|---|---|---|
|
|
492
496
|
${rows}
|
|
@@ -498,9 +502,14 @@ ${rows}
|
|
|
498
502
|
export function upsertReviewLoopSummary(markdown, envelope) {
|
|
499
503
|
const withHeader = upsertReviewLoopHeader(markdown, envelope);
|
|
500
504
|
const section = renderReviewLoopSummarySection(envelope);
|
|
501
|
-
const
|
|
502
|
-
const match =
|
|
503
|
-
|
|
505
|
+
const headingCandidates = [reviewLoopHeading(envelope.stage), "Spec Review Loop"];
|
|
506
|
+
const match = headingCandidates
|
|
507
|
+
.map((heading) => {
|
|
508
|
+
const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
509
|
+
return new RegExp(`^##\\s+${escapedHeading}\\s*$`, "m").exec(withHeader);
|
|
510
|
+
})
|
|
511
|
+
.find((candidate) => candidate !== null && candidate.index >= 0);
|
|
512
|
+
if (!match) {
|
|
504
513
|
const needsBreak = withHeader.endsWith("\n") ? "" : "\n";
|
|
505
514
|
return `${withHeader}${needsBreak}\n${section}\n`;
|
|
506
515
|
}
|
|
@@ -613,7 +622,8 @@ function parseHeaderMeta(markdown) {
|
|
|
613
622
|
};
|
|
614
623
|
}
|
|
615
624
|
export function extractReviewLoopEnvelopeFromArtifact(markdown, stage, artifactPath) {
|
|
616
|
-
const sectionBody = extractH2Section(markdown,
|
|
625
|
+
const sectionBody = extractH2Section(markdown, reviewLoopHeading(stage))
|
|
626
|
+
?? extractH2Section(markdown, "Spec Review Loop");
|
|
617
627
|
if (!sectionBody)
|
|
618
628
|
return null;
|
|
619
629
|
const iterations = parseIterationsTable(sectionBody);
|
|
@@ -57,7 +57,7 @@ value. Do not nitpick wording.
|
|
|
57
57
|
|
|
58
58
|
## Output
|
|
59
59
|
|
|
60
|
-
Record in \`## Outside Voice Findings\` or
|
|
60
|
+
Record in \`## Outside Voice Findings\` or the stage-specific outside voice loop section:
|
|
61
61
|
|
|
62
62
|
\`\`\`markdown
|
|
63
63
|
| ID | Dimension | Finding | Disposition | Rationale |
|
package/dist/content/skills.js
CHANGED
|
@@ -61,23 +61,24 @@ function autoSubagentDispatchBlock(stage, track) {
|
|
|
61
61
|
const rules = stageAutoSubagentDispatch(stage);
|
|
62
62
|
if (rules.length === 0)
|
|
63
63
|
return "";
|
|
64
|
+
const schema = stageSchema(stage, track);
|
|
64
65
|
const rows = rules
|
|
65
66
|
.map((rule) => {
|
|
66
67
|
const userGate = rule.requiresUserGate ? "required" : "not required";
|
|
67
|
-
return `| ${rule.agent} | ${rule.mode} | ${userGate} | ${rule.when} |`;
|
|
68
|
+
return `| ${rule.agent} | ${rule.mode} | ${userGate} | ${rule.when} | ${rule.purpose} |`;
|
|
68
69
|
})
|
|
69
70
|
.join("\n");
|
|
70
|
-
const mandatory =
|
|
71
|
+
const mandatory = schema.mandatoryDelegations;
|
|
71
72
|
const mandatoryList = mandatory.length > 0 ? mandatory.map((a) => `\`${a}\``).join(", ") : "none";
|
|
72
73
|
const delegationLogRel = `${RUNTIME_ROOT}/state/delegation-log.json`;
|
|
74
|
+
const artifactRef = `${RUNTIME_ROOT}/artifacts/${schema.artifactRules.artifactFile}`;
|
|
73
75
|
return `## Automatic Subagent Dispatch
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|---|---|---|---|
|
|
76
|
+
| Agent | Mode | User Gate | Trigger | Purpose |
|
|
77
|
+
|---|---|---|---|---|
|
|
77
78
|
${rows}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
Mandatory: ${mandatoryList}. Record completion/waiver in \`${delegationLogRel}\` before completion.
|
|
80
|
+
### Harness Dispatch Contract
|
|
81
|
+
Use true harness dispatch: Claude native Task, Cursor generic dispatch, OpenCode \`.opencode/agents/<agent>.md\`, Codex \`.codex/agents/<agent>.toml\`. Run independent read-only/review agents in parallel where safe, write evidence into \`${artifactRef}\`, then append \`${delegationLogRel}\` rows with matching \`fulfillmentMode: "isolated"\` or \`"generic-dispatch"\`. Do not collapse OpenCode or Codex to role-switch by default; role-switch is degraded fallback and must carry non-empty \`evidenceRefs\`. Missing evidence blocks completion.
|
|
81
82
|
`;
|
|
82
83
|
}
|
|
83
84
|
function researchPlaybooksBlock(playbooks) {
|
|
@@ -160,15 +161,18 @@ function batchExecutionModeBlock(stage, track) {
|
|
|
160
161
|
const schema = stageSchema(stage, track);
|
|
161
162
|
if (!schema.batchExecutionAllowed)
|
|
162
163
|
return "";
|
|
164
|
+
const orderingGuidance = track === "quick"
|
|
165
|
+
? "Use spec acceptance items / bug reproduction slices for ordering."
|
|
166
|
+
: "Use plan slices for ordering.";
|
|
163
167
|
return `## Batch Execution Mode
|
|
164
168
|
|
|
165
169
|
Execute the current dependency batch task-by-task (RED -> GREEN -> REFACTOR).
|
|
166
170
|
Stop on BLOCKED status or when user input is required.
|
|
167
|
-
Apply concise turn announces: one announce per batch boundary (or when risk/
|
|
171
|
+
Apply concise turn announces: one announce per batch boundary (or when risk/source
|
|
168
172
|
changes materially), then execute tasks without repetitive boilerplate.
|
|
169
173
|
|
|
170
174
|
Detailed walkthrough:
|
|
171
|
-
|
|
175
|
+
${orderingGuidance} Keep RED -> GREEN -> REFACTOR evidence in the TDD artifact.
|
|
172
176
|
`;
|
|
173
177
|
}
|
|
174
178
|
function crossStageTraceBlock(trace) {
|
|
@@ -386,6 +390,7 @@ ${philosophy.purpose}
|
|
|
386
390
|
|
|
387
391
|
## Complexity Tier
|
|
388
392
|
- Active tier: \`${schema.complexityTier}\`
|
|
393
|
+
- Scale-to-complexity rule: execute required gates and artifact sections, but keep optional/deep sections compact unless risk, novelty, or configuration triggers them. Do not mechanically expand lightweight work into a strategy workshop.
|
|
389
394
|
- Mandatory delegations at this tier: ${mandatoryDelegationSummary}
|
|
390
395
|
- Track render context: \`${trackContext.track}\` (${trackContext.usesPlanTerminology ? "plan-first wording" : "acceptance-first wording"})
|
|
391
396
|
|
|
@@ -452,7 +457,7 @@ ${reviewLens.outputs.map((item) => `- ${item}`).join("\n")}
|
|
|
452
457
|
${reviewSectionsBlock(reviewLens.reviewSections)}
|
|
453
458
|
|
|
454
459
|
## Shared Stage Guidance
|
|
455
|
-
-
|
|
460
|
+
- At STOP/closeout points, offer the shared handoff choices only when a user decision is needed.
|
|
456
461
|
- Carry upstream decisions forward explicitly; record drift instead of silently changing direction.
|
|
457
462
|
- Before closeout, fill \`## Learnings\` with \`- None this stage.\` or 1-3 strict JSON bullets.
|
|
458
463
|
- Keep decisions explicit: context, options, chosen option, rationale, risk, and rollback.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
import { stageSkillFolder } from "./skills.js";
|
|
3
|
+
export function stageCommandShimMarkdown(stage) {
|
|
4
|
+
const skillPath = `${RUNTIME_ROOT}/skills/${stageSkillFolder(stage)}/SKILL.md`;
|
|
5
|
+
return `# /cc-${stage}
|
|
6
|
+
|
|
7
|
+
This is a thin compatibility shim for the \`${stage}\` flow stage.
|
|
8
|
+
|
|
9
|
+
Load and follow the authoritative stage skill:
|
|
10
|
+
|
|
11
|
+
- \`${skillPath}\`
|
|
12
|
+
|
|
13
|
+
Normal stage resume and advancement uses \`/cc-next\`. Use \`/cc-next\` to read
|
|
14
|
+
\`.cclaw/state/flow-state.json\`, select the active stage, and advance only after
|
|
15
|
+
that stage's gates pass. Do not duplicate the stage protocol here.
|
|
16
|
+
`;
|
|
17
|
+
}
|
|
@@ -227,6 +227,7 @@ const REQUIRED_GATE_IDS = {
|
|
|
227
227
|
review: (track) => [
|
|
228
228
|
"review_layer1_spec_compliance",
|
|
229
229
|
"review_layer2_security",
|
|
230
|
+
"review_layer_coverage_complete",
|
|
230
231
|
"review_criticals_resolved",
|
|
231
232
|
"review_army_json_valid",
|
|
232
233
|
...(track === "quick" ? [] : ["review_trace_matrix_clean"])
|
|
@@ -40,10 +40,10 @@ export const BRAINSTORM = {
|
|
|
40
40
|
"**Classify depth and scope** — pick Lightweight / Standard / Deep; decompose independent subsystems before deeper work.",
|
|
41
41
|
"**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.",
|
|
42
42
|
"**Reframe with How Might We** — write a single `How Might We …?` line that names the user, the desired outcome, and the constraint. This is the altitude check before approaches.",
|
|
43
|
-
"**Sharpening questions (3-5)** — capture decision-changing question/answer pairs in the `Sharpening Questions` table with the actual decision impact;
|
|
43
|
+
"**Sharpening questions (3-5)** — capture decision-changing question/answer pairs in the `Sharpening Questions` table with the actual decision impact; only non-critical preference/default assumptions may continue. STOP and ask on scope, architecture, security, data loss, public API, migration, auth/pricing, or user-approval uncertainty.",
|
|
44
44
|
"**Use compact discovery for simple apps** — for concrete low-risk asks (todo app, landing page, local widget), do one context pass, compare one baseline and one challenger, then ask for one explicit approval; do not drag the user through a full workshop.",
|
|
45
45
|
"**Short-circuit concrete asks** — for unambiguous implementation-only requests, write a compact brainstorm stub (context, problem, approved intent, constraints, assumptions) and ask for one explicit approval.",
|
|
46
|
-
"**Ask only decision-changing questions** — one at a time; if answers would not change approach, state the assumption and continue.",
|
|
46
|
+
"**Ask only decision-changing questions** — one at a time; if answers would not change approach and are non-critical preference/default assumptions, state the assumption and continue; STOP on scope, architecture, security, data loss, public API, migration, auth/pricing, or user approval uncertainty.",
|
|
47
47
|
"**Compare 2-3 distinct approaches with stable Role/Upside columns** — Role values are `baseline` | `challenger` | `wild-card`; Upside is `low` | `modest` | `high` | `higher`; include real trade-offs and reuse notes; include exactly one challenger with explicit `high` or `higher` upside.",
|
|
48
48
|
"**Collect reaction before recommending** — ask which option feels closest and what concern remains, then recommend based on that reaction.",
|
|
49
49
|
"**Write the `Not Doing` list** — name 3-5 things this brainstorm explicitly is not committing to (vs. deferred). This protects scope from silent enlargement and the next stage from rework.",
|
|
@@ -55,7 +55,7 @@ export const BRAINSTORM = {
|
|
|
55
55
|
"Start from observed project context; if the idea is vague, first narrow the project type with **one** structured question, then keep going.",
|
|
56
56
|
"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.",
|
|
57
57
|
"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.",
|
|
58
|
-
"
|
|
58
|
+
"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.",
|
|
59
59
|
"For simple greenfield web apps, 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).",
|
|
60
60
|
"Show approaches before the recommendation; include a higher-upside challenger and gather reaction first.",
|
|
61
61
|
"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.",
|