cclaw-cli 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -19
- package/dist/content/diff-command.js +66 -20
- package/dist/content/harness-playbooks.d.ts +24 -0
- package/dist/content/harness-playbooks.js +292 -0
- package/dist/content/harnesses-doc.js +13 -3
- package/dist/content/status-command.js +89 -27
- package/dist/content/subagents.js +14 -8
- package/dist/content/tree-command.js +66 -16
- package/dist/delegation.d.ts +28 -0
- package/dist/delegation.js +47 -7
- package/dist/doctor.js +18 -2
- package/dist/harness-adapters.d.ts +40 -1
- package/dist/harness-adapters.js +24 -5
- package/dist/install.js +36 -1
- package/package.json +1 -1
|
@@ -22,6 +22,12 @@ function stageActivityPath() {
|
|
|
22
22
|
function snapshotPath() {
|
|
23
23
|
return `${RUNTIME_ROOT}/state/flow-state.snapshot.json`;
|
|
24
24
|
}
|
|
25
|
+
function harnessGapsPath() {
|
|
26
|
+
return `${RUNTIME_ROOT}/state/harness-gaps.json`;
|
|
27
|
+
}
|
|
28
|
+
function retroArtifactPath() {
|
|
29
|
+
return `${RUNTIME_ROOT}/artifacts/09-retro.md`;
|
|
30
|
+
}
|
|
25
31
|
/**
|
|
26
32
|
* Command contract for /cc-view status — a read-only snapshot command.
|
|
27
33
|
* Does not mutate state. Always safe to run.
|
|
@@ -34,7 +40,8 @@ export function statusCommandContract() {
|
|
|
34
40
|
## Purpose
|
|
35
41
|
|
|
36
42
|
**Read-only visual snapshot of the cclaw run.** Shows progress bar, current stage,
|
|
37
|
-
gate coverage, delegation status
|
|
43
|
+
gate coverage, delegation status with fulfillmentMode, closeout substate after
|
|
44
|
+
ship, harness parity fallback, stale markers, and top knowledge highlights.
|
|
38
45
|
|
|
39
46
|
This command **never mutates state**. Use it at session start to orient, or at any
|
|
40
47
|
time to answer "where are we?" without advancing the flow.
|
|
@@ -49,9 +56,10 @@ time to answer "where are we?" without advancing the flow.
|
|
|
49
56
|
## Algorithm
|
|
50
57
|
|
|
51
58
|
1. Read **\`${flowPath}\`** — capture \`track\`, \`currentStage\`, \`completedStages\`,
|
|
52
|
-
\`skippedStages\`,
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
\`skippedStages\`, \`staleStages\`, per-stage gate catalog, and **\`closeout\`**
|
|
60
|
+
(shipSubstate + retro/compound flags).
|
|
61
|
+
2. Read **\`${delegationPath}\`** — for each mandatory agent of the current stage,
|
|
62
|
+
capture \`status\`, \`fulfillmentMode\`, and whether \`evidenceRefs\` are present.
|
|
55
63
|
3. Read **\`${contextModePath()}\`** — surface \`activeMode\` (default if missing).
|
|
56
64
|
4. Compute **time in current stage** from the most recent stage-entry signal:
|
|
57
65
|
- Prefer \`${checkpointPath()}\`'s \`timestamp\` when its \`stage\` matches \`currentStage\`.
|
|
@@ -60,25 +68,37 @@ time to answer "where are we?" without advancing the flow.
|
|
|
60
68
|
- If no signal exists, render \`(unknown)\`.
|
|
61
69
|
5. Optionally read **\`${snapshotPath()}\`** to compute gate delta versus prior baseline:
|
|
62
70
|
- If missing or invalid, render \`delta: (baseline unavailable; run /cc-view diff)\`.
|
|
63
|
-
6. Read
|
|
71
|
+
6. Read **\`${harnessGapsPath()}\`** (schemaVersion 2). For every installed harness
|
|
72
|
+
capture \`tier\`, \`subagentFallback\`, and \`playbookPath\` for the harness row.
|
|
73
|
+
7. Read the top of **\`${knowledgePath()}\`** — surface up to 3 most recent entries
|
|
64
74
|
(by trailing timestamp or source marker).
|
|
65
|
-
|
|
75
|
+
8. Detect **closeout artifacts**: check whether \`${retroArtifactPath()}\` exists on
|
|
76
|
+
disk and annotate the closeout row accordingly.
|
|
77
|
+
9. Emit the visual status block described below. Do **not** load any stage skill.
|
|
66
78
|
|
|
67
79
|
## Visual markers
|
|
68
80
|
|
|
69
81
|
Default UTF markers: \`✓\` passed, \`▶\` current, \`○\` pending, \`⊘\` skipped, \`⏸\` stale, \`✗\` blocked.
|
|
70
82
|
ASCII fallback (no UTF locale): \`[x]\`, \`[>]\`, \`[ ]\`, \`[-]\`, \`[=]\`, \`[!]\`.
|
|
71
83
|
|
|
84
|
+
Delegation markers: \`✓\` completed, \`◎\` completed-no-evidence (role-switch
|
|
85
|
+
harness; **blocks stage**), \`○\` scheduled/pending, \`⊘\` waived, \`✗\` failed.
|
|
86
|
+
|
|
72
87
|
## Status Block Format
|
|
73
88
|
|
|
74
89
|
\`\`\`
|
|
75
90
|
cclaw status
|
|
76
|
-
flow:
|
|
77
|
-
stage:
|
|
78
|
-
bar:
|
|
79
|
-
gates:
|
|
80
|
-
delegations
|
|
81
|
-
|
|
91
|
+
flow: <track> · run=<runId> · feature=<feature-id>
|
|
92
|
+
stage: <stage> (<N>/<total>) · time <Xd|XhYm|Xm|unknown> · mode <activeMode>
|
|
93
|
+
bar: [✓ brainstorm] [✓ scope] [▶ design] [○ spec] [○ plan] [○ tdd] [○ review] [○ ship]
|
|
94
|
+
gates: now <passed>/<required> · blocked <count> · delta <summary or baseline-unavailable>
|
|
95
|
+
delegations (<expectedMode>):
|
|
96
|
+
- planner ✓ completed mode=<isolated|generic-dispatch|role-switch>
|
|
97
|
+
- reviewer ○ pending
|
|
98
|
+
- test-author ◎ missing-evidence (role-switch; add evidenceRefs)
|
|
99
|
+
closeout: <shipSubstate> · retro=<drafted|accepted|skipped|—> · compound=<N promoted|skipped|—>
|
|
100
|
+
harness: <id>=<tier>/<fallback>, ... · playbooks: <M>/<N>
|
|
101
|
+
stale: <list or none>
|
|
82
102
|
knowledge:
|
|
83
103
|
- <latest entry summary>
|
|
84
104
|
- <second entry summary>
|
|
@@ -86,12 +106,20 @@ cclaw status
|
|
|
86
106
|
next: /cc-next · /cc-view tree · /cc-view diff
|
|
87
107
|
\`\`\`
|
|
88
108
|
|
|
109
|
+
- Omit the \`closeout:\` row when \`currentStage !== "ship"\` and \`shipSubstate === "idle"\`.
|
|
110
|
+
- Omit \`delegations\` line when the current stage has zero mandatory delegations.
|
|
111
|
+
- Omit \`harness\` line only when \`${harnessGapsPath()}\` is missing or invalid
|
|
112
|
+
(render \`harness: (report unavailable; run cclaw upgrade)\`).
|
|
113
|
+
|
|
89
114
|
## Anti-patterns
|
|
90
115
|
|
|
91
116
|
- Inventing gate status without reading \`${flowPath}\`.
|
|
92
117
|
- Reporting delegations as satisfied when the log says \`pending\`.
|
|
118
|
+
- Treating a \`completed\` role-switch delegation without \`evidenceRefs\` as green
|
|
119
|
+
— it must surface as \`◎ missing-evidence\`.
|
|
93
120
|
- Advancing the stage from \`/cc-view status\` — progression belongs to \`/cc-next\`.
|
|
94
|
-
- Hiding
|
|
121
|
+
- Hiding the closeout substate after ship; retro/compound/archive progress must
|
|
122
|
+
be visible so \`/cc-next\` resumes at the right step.
|
|
95
123
|
|
|
96
124
|
## Primary skill
|
|
97
125
|
|
|
@@ -106,7 +134,7 @@ export function statusCommandSkillMarkdown() {
|
|
|
106
134
|
const delegationPath = delegationLogPath();
|
|
107
135
|
return `---
|
|
108
136
|
name: ${STATUS_SKILL_NAME}
|
|
109
|
-
description: "Read-only visual snapshot of the cclaw flow with progress bar, gate delta, delegations, and
|
|
137
|
+
description: "Read-only visual snapshot of the cclaw flow with progress bar, gate delta, delegations (fulfillmentMode + evidence), closeout substate, and harness parity row."
|
|
110
138
|
---
|
|
111
139
|
|
|
112
140
|
# /cc-view status — Flow Status Snapshot
|
|
@@ -114,7 +142,14 @@ description: "Read-only visual snapshot of the cclaw flow with progress bar, gat
|
|
|
114
142
|
## Overview
|
|
115
143
|
|
|
116
144
|
\`/cc-view status\` is the quickest way to answer "where are we in the flow?" without
|
|
117
|
-
advancing or mutating anything. Safe to run at any point.
|
|
145
|
+
advancing or mutating anything. Safe to run at any point. The snapshot reflects:
|
|
146
|
+
|
|
147
|
+
- progress across stages with per-stage markers,
|
|
148
|
+
- gate coverage and delta vs. baseline,
|
|
149
|
+
- mandatory delegations with **fulfillmentMode** (isolated / generic-dispatch /
|
|
150
|
+
role-switch / harness-waiver) and evidence gate,
|
|
151
|
+
- **closeout substate** after ship (retro → compound → archive),
|
|
152
|
+
- **harness parity row** (tier + fallback) for the active harness set.
|
|
118
153
|
|
|
119
154
|
## HARD-GATE
|
|
120
155
|
|
|
@@ -133,22 +168,45 @@ a read-only command. Do **not** update \`${snapshotPath()}\` here.
|
|
|
133
168
|
5. Try reading \`${snapshotPath()}\` for gate delta:
|
|
134
169
|
- If available, compare current stage \`passed\` / \`blocked\` sets against baseline.
|
|
135
170
|
- If unavailable, render \`delta: (baseline unavailable; run /cc-view diff)\`.
|
|
136
|
-
6. Read \`${
|
|
137
|
-
|
|
171
|
+
6. Read \`${harnessGapsPath()}\`:
|
|
172
|
+
- If \`schemaVersion === 2\`, for each entry render \`<harness>=<tier>/<subagentFallback>\`.
|
|
173
|
+
- Count existing \`playbookPath\` files on disk to print \`playbooks: <M>/<N>\`.
|
|
174
|
+
- If the file is missing or has an older schema, render
|
|
175
|
+
\`harness: (report unavailable; run cclaw upgrade)\`.
|
|
176
|
+
7. Read \`${RUNTIME_ROOT}/knowledge.jsonl\`. If missing or empty → knowledge highlights are \`(none recorded)\`. Parse each line as JSON and surface its \`trigger\`/\`action\`.
|
|
177
|
+
8. For each gate in \`stageGateCatalog[currentStage].required\`:
|
|
138
178
|
- Satisfied if present in \`passed\` and absent from \`blocked\`.
|
|
139
|
-
|
|
140
|
-
-
|
|
141
|
-
|
|
142
|
-
-
|
|
143
|
-
|
|
144
|
-
-
|
|
145
|
-
|
|
146
|
-
-
|
|
147
|
-
|
|
179
|
+
9. For each mandatory delegation of the current stage, evaluate:
|
|
180
|
+
- \`✓ completed\` when \`status === "completed"\` and (harness is not role-switch
|
|
181
|
+
**or** \`evidenceRefs.length >= 1\`).
|
|
182
|
+
- \`◎ missing-evidence\` when \`status === "completed"\`, harness declares
|
|
183
|
+
\`role-switch\`, and \`evidenceRefs\` is empty or absent.
|
|
184
|
+
- \`○ <status>\` for \`scheduled\` / pending.
|
|
185
|
+
- \`⊘ waived\` when \`status === "waived"\`.
|
|
186
|
+
- \`✗ failed\` when \`status === "failed"\`.
|
|
187
|
+
10. Compute **closeout row** when \`currentStage === "ship"\` or
|
|
188
|
+
\`closeout.shipSubstate !== "idle"\`:
|
|
189
|
+
- \`shipSubstate\` verbatim,
|
|
190
|
+
- \`retro=drafted|accepted|skipped|—\` derived from \`closeout.retroDraftedAt\`,
|
|
191
|
+
\`closeout.retroAcceptedAt\`, \`closeout.retroSkipped\`,
|
|
192
|
+
- \`compound=<N promoted>|skipped|—\` from
|
|
193
|
+
\`closeout.compoundPromoted\` / \`closeout.compoundSkipped\`.
|
|
194
|
+
11. Build and print the visual status block:
|
|
195
|
+
- stage header
|
|
196
|
+
- one-line progress bar with per-stage markers
|
|
197
|
+
- gate summary + delta
|
|
198
|
+
- delegation rows (per mandatory agent)
|
|
199
|
+
- closeout row (when active)
|
|
200
|
+
- harness row
|
|
201
|
+
- stale stage row
|
|
202
|
+
12. Suggest the next action:
|
|
203
|
+
- If current stage has unmet gates → \`/cc-next\` to resume.
|
|
204
|
+
- If closeout substate is non-idle → \`/cc-next\` to continue the chain.
|
|
205
|
+
- If current stage is complete → \`/cc-next\` to advance (or report "Flow complete" if terminal).
|
|
148
206
|
|
|
149
207
|
## Output Guidelines
|
|
150
208
|
|
|
151
|
-
- Keep output compact (≤
|
|
209
|
+
- Keep output compact (≤ 40 lines) — status, not narrative.
|
|
152
210
|
- Report counts, not full artifact contents.
|
|
153
211
|
- If any data source is missing or corrupt, say so explicitly rather than guessing.
|
|
154
212
|
- Include \`/cc-view tree\` for deep structure and \`/cc-view diff\` for before/after map in the final line.
|
|
@@ -157,6 +215,10 @@ a read-only command. Do **not** update \`${snapshotPath()}\` here.
|
|
|
157
215
|
|
|
158
216
|
- Rebuilding trace-matrix or running doctor from \`/cc-view status\` — those belong to dedicated tools.
|
|
159
217
|
- Treating absence of delegation log as "all delegations complete".
|
|
218
|
+
- Collapsing \`◎ missing-evidence\` into \`✓ completed\` — role-switch gaps must stay
|
|
219
|
+
visible so the stage cannot advance silently.
|
|
220
|
+
- Omitting the closeout row when \`shipSubstate !== "idle"\`; it is the only signal
|
|
221
|
+
that tells the user why \`/cc-next\` is about to run retro/compound/archive.
|
|
160
222
|
- Mutating state to "clean up" during a status check.
|
|
161
223
|
`;
|
|
162
224
|
}
|
|
@@ -43,14 +43,20 @@ Human input remains mandatory only at explicit approval gates (plan approval, us
|
|
|
43
43
|
|
|
44
44
|
### Harness routing
|
|
45
45
|
|
|
46
|
-
| Harness | Delegation tool | Structured ask
|
|
47
|
-
|
|
48
|
-
| Claude | Task
|
|
49
|
-
| Cursor |
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
| Harness | Fallback | Delegation tool | Structured ask | Parity playbook |
|
|
47
|
+
|---|---|---|---|---|
|
|
48
|
+
| Claude | \`native\` | Task (named subagent_type) | AskUserQuestion | \`.cclaw/references/harnesses/claude-playbook.md\` |
|
|
49
|
+
| Cursor | \`generic-dispatch\` | Task (generic subagent_type: explore/generalPurpose/…) | AskQuestion | \`.cclaw/references/harnesses/cursor-playbook.md\` |
|
|
50
|
+
| OpenCode | \`role-switch\` | plugin dispatch _or_ in-session role-switch | plain-text options | \`.cclaw/references/harnesses/opencode-playbook.md\` |
|
|
51
|
+
| Codex | \`role-switch\` | in-session role-switch (mandatory evidenceRefs) | plain-text options | \`.cclaw/references/harnesses/codex-playbook.md\` |
|
|
52
|
+
|
|
53
|
+
**Dispatch rules driven by \`subagentFallback\`:**
|
|
54
|
+
|
|
55
|
+
- \`native\` — use the harness's own named subagent primitive; delegation entry uses \`fulfillmentMode: "isolated"\`.
|
|
56
|
+
- \`generic-dispatch\` — map each cclaw agent onto the generic dispatcher via the harness playbook; delegation entry uses \`fulfillmentMode: "generic-dispatch"\`.
|
|
57
|
+
- \`role-switch\` — announce the role in-session, perform the work, append a delegation row with \`fulfillmentMode: "role-switch"\` and ≥1 \`evidenceRef\`. Without evidenceRefs the \`delegation:mandatory:current_stage\` check reports \`missingEvidence\` and blocks stage completion.
|
|
58
|
+
|
|
59
|
+
The only time a \`harness_limitation\` waiver fires automatically is when every installed harness declares \`subagentFallback: "waiver"\`. cclaw 0.33 no longer maps Codex onto auto-waiver — the agent must role-switch with evidence.
|
|
54
60
|
|
|
55
61
|
### Model routing
|
|
56
62
|
|
|
@@ -13,13 +13,17 @@ function artifactsPath() {
|
|
|
13
13
|
function rewindLogPath() {
|
|
14
14
|
return `${RUNTIME_ROOT}/state/rewind-log.jsonl`;
|
|
15
15
|
}
|
|
16
|
+
function harnessPlaybooksDir() {
|
|
17
|
+
return `${RUNTIME_ROOT}/references/harnesses`;
|
|
18
|
+
}
|
|
16
19
|
export function treeCommandContract() {
|
|
17
20
|
return `# /cc-view tree
|
|
18
21
|
|
|
19
22
|
## Purpose
|
|
20
23
|
|
|
21
|
-
Render a visual flow tree for quick orientation across stages, gates, delegations
|
|
22
|
-
stale markers,
|
|
24
|
+
Render a visual flow tree for quick orientation across stages, gates, delegations
|
|
25
|
+
(with fulfillmentMode), ship closeout substate, stale markers, artifact presence,
|
|
26
|
+
and per-harness playbook availability.
|
|
23
27
|
|
|
24
28
|
## HARD-GATE
|
|
25
29
|
|
|
@@ -30,13 +34,21 @@ stale markers, and artifact presence.
|
|
|
30
34
|
|
|
31
35
|
1. Read \`${flowStatePath()}\`.
|
|
32
36
|
2. Read \`${delegationLogPath()}\` (if missing, treat current-stage delegations as pending).
|
|
33
|
-
3. Detect artifact files in \`${artifactsPath()}
|
|
37
|
+
3. Detect artifact files in \`${artifactsPath()}\` (\`01-brainstorm.md\` …
|
|
38
|
+
\`08-ship.md\` plus \`09-retro.md\`).
|
|
34
39
|
4. Read rewind records from \`${rewindLogPath()}\` when present for stale-stage context.
|
|
35
|
-
5.
|
|
40
|
+
5. Inspect \`${harnessPlaybooksDir()}\` to confirm per-harness playbooks exist
|
|
41
|
+
for the installed harness set.
|
|
42
|
+
6. Render the tree using stage order from active track:
|
|
36
43
|
- stage node marker: passed/current/pending/skipped/stale
|
|
37
44
|
- gate summary: \`passed/required\`
|
|
38
|
-
- delegation summary for current stage
|
|
45
|
+
- delegation summary for current stage (each agent carries its
|
|
46
|
+
\`fulfillmentMode\` label)
|
|
39
47
|
- artifact marker per stage (exists / stale copy / missing)
|
|
48
|
+
7. When \`currentStage === "ship"\` or \`closeout.shipSubstate !== "idle"\`,
|
|
49
|
+
append a closeout sub-tree under ship with substate and retro/compound flags.
|
|
50
|
+
8. Append a final \`harnesses\` branch summarising tier + fallback +
|
|
51
|
+
playbook-present for each installed harness.
|
|
40
52
|
|
|
41
53
|
## Tree Format
|
|
42
54
|
|
|
@@ -45,12 +57,31 @@ cclaw flow tree (track=<track>, run=<runId>)
|
|
|
45
57
|
├─ [✓] brainstorm gates 6/6 artifact 01-brainstorm.md
|
|
46
58
|
├─ [✓] scope gates 5/5 artifact 02-scope.md
|
|
47
59
|
├─ [▶] design gates 2/7 artifact 03-design.md
|
|
48
|
-
│ ├─ delegations:
|
|
60
|
+
│ ├─ delegations:
|
|
61
|
+
│ │ ├─ planner ✓ completed mode=isolated
|
|
62
|
+
│ │ └─ reviewer ○ pending
|
|
49
63
|
│ └─ stale: none
|
|
50
64
|
├─ [○] spec gates - artifact missing
|
|
51
65
|
└─ [○] plan gates - artifact missing
|
|
66
|
+
|
|
67
|
+
closeout (shipSubstate=retro_review):
|
|
68
|
+
├─ retro: drafted 09-retro.md · awaiting accept/edit/skip
|
|
69
|
+
├─ compound: —
|
|
70
|
+
└─ archive: pending
|
|
71
|
+
|
|
72
|
+
harnesses:
|
|
73
|
+
├─ claude tier=tier1 fallback=native playbook ✓
|
|
74
|
+
├─ cursor tier=tier2 fallback=generic-dispatch playbook ✓
|
|
75
|
+
├─ opencode tier=tier2 fallback=role-switch playbook ✓
|
|
76
|
+
└─ codex tier=tier2 fallback=role-switch playbook ✓
|
|
52
77
|
\`\`\`
|
|
53
78
|
|
|
79
|
+
- Closeout sub-tree is **omitted** when \`currentStage !== "ship"\` and
|
|
80
|
+
\`shipSubstate === "idle"\`.
|
|
81
|
+
- Delegations sub-branch is omitted when the stage has no mandatory agents.
|
|
82
|
+
- Playbook marker is \`✗ missing\` when the file under
|
|
83
|
+
\`${harnessPlaybooksDir()}/<harness>-playbook.md\` is absent.
|
|
84
|
+
|
|
54
85
|
Use UTF markers by default, ASCII fallback when terminal cannot render UTF.
|
|
55
86
|
|
|
56
87
|
## Primary skill
|
|
@@ -61,7 +92,7 @@ Use UTF markers by default, ASCII fallback when terminal cannot render UTF.
|
|
|
61
92
|
export function treeCommandSkillMarkdown() {
|
|
62
93
|
return `---
|
|
63
94
|
name: ${TREE_SKILL_NAME}
|
|
64
|
-
description: "Render a visual flow tree for stages, gates, delegations, and
|
|
95
|
+
description: "Render a visual flow tree for stages, gates, delegations (fulfillmentMode), ship closeout substate, artifacts, and per-harness playbooks."
|
|
65
96
|
---
|
|
66
97
|
|
|
67
98
|
# /cc-view tree
|
|
@@ -72,20 +103,39 @@ Do not modify state in this command. It is a pure read/render operation.
|
|
|
72
103
|
|
|
73
104
|
## Protocol
|
|
74
105
|
|
|
75
|
-
1. Read \`${flowStatePath()}\` as source of truth.
|
|
76
|
-
2. Read \`${delegationLogPath()}\` for current-stage delegation status
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
|
|
106
|
+
1. Read \`${flowStatePath()}\` as source of truth (including \`closeout\`).
|
|
107
|
+
2. Read \`${delegationLogPath()}\` for current-stage delegation status plus
|
|
108
|
+
\`fulfillmentMode\` / \`evidenceRefs\`.
|
|
109
|
+
3. Inspect \`${artifactsPath()}\` for per-stage artifact presence and stale copies,
|
|
110
|
+
and for the retro artifact \`09-retro.md\`.
|
|
111
|
+
4. Inspect \`${harnessPlaybooksDir()}\` for \`<harness>-playbook.md\` files.
|
|
112
|
+
5. Render one compact tree:
|
|
113
|
+
- stage marker: passed/current/pending/skipped/stale,
|
|
114
|
+
- gates summary,
|
|
115
|
+
- artifact summary,
|
|
116
|
+
- delegation branch for current stage with fulfillmentMode labels,
|
|
117
|
+
6. When \`closeout.shipSubstate !== "idle"\` or \`currentStage === "ship"\`, add
|
|
118
|
+
a closeout sub-tree:
|
|
119
|
+
- \`retro:\` line derived from \`closeout.retroDraftedAt\` /
|
|
120
|
+
\`closeout.retroAcceptedAt\` / \`closeout.retroSkipped\` and artifact presence,
|
|
121
|
+
- \`compound:\` line derived from \`closeout.compoundPromoted\` /
|
|
122
|
+
\`closeout.compoundSkipped\` / \`closeout.compoundCompletedAt\`,
|
|
123
|
+
- \`archive:\` line — \`pending\` until \`shipSubstate === "ready_to_archive"\`,
|
|
124
|
+
then \`next\`; the transient \`archived\` substate surfaces only if the
|
|
125
|
+
archive step failed mid-run.
|
|
126
|
+
7. Append a \`harnesses:\` branch. For each installed harness derive the tier
|
|
127
|
+
from the harness-gaps report and mark \`playbook ✓/✗ missing\` based on
|
|
128
|
+
\`${harnessPlaybooksDir()}/<harness>-playbook.md\` existence.
|
|
129
|
+
8. If rewind records exist in \`${rewindLogPath()}\`, include latest rewind note in footer.
|
|
84
130
|
|
|
85
131
|
## Validation
|
|
86
132
|
|
|
87
133
|
- Output must mention the active \`track\` and \`currentStage\`.
|
|
88
134
|
- Exactly one stage is marked current.
|
|
89
135
|
- Missing files are reported explicitly; never guessed as complete.
|
|
136
|
+
- Delegation rows always carry a fulfillmentMode label (or \`mode=?\` when the
|
|
137
|
+
ledger entry is legacy and the mode is inferred).
|
|
138
|
+
- Closeout sub-tree is present iff ship is reached; it cannot be omitted while
|
|
139
|
+
\`shipSubstate !== "idle"\`.
|
|
90
140
|
`;
|
|
91
141
|
}
|
package/dist/delegation.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import { type SubagentFallback } from "./harness-adapters.js";
|
|
1
2
|
import type { FlowStage } from "./types.js";
|
|
2
3
|
export type DelegationMode = "mandatory" | "proactive" | "conditional";
|
|
3
4
|
export type DelegationStatus = "scheduled" | "completed" | "failed" | "waived";
|
|
5
|
+
/**
|
|
6
|
+
* How a delegation was actually fulfilled. Advisory — mirrors the harness
|
|
7
|
+
* `subagentFallback` that was in effect when the entry was recorded.
|
|
8
|
+
*
|
|
9
|
+
* - `isolated` — Claude-style isolated subagent worker.
|
|
10
|
+
* - `generic-dispatch` — Cursor-style Task dispatch mapped to a named role.
|
|
11
|
+
* - `role-switch` — performed in-session with explicit role announce.
|
|
12
|
+
* - `harness-waiver` — auto-waived due to missing dispatch capability.
|
|
13
|
+
*/
|
|
14
|
+
export type DelegationFulfillmentMode = "isolated" | "generic-dispatch" | "role-switch" | "harness-waiver";
|
|
4
15
|
export interface DelegationTokenUsage {
|
|
5
16
|
input: number;
|
|
6
17
|
output: number;
|
|
@@ -45,6 +56,12 @@ export type DelegationEntry = {
|
|
|
45
56
|
retryCount?: number;
|
|
46
57
|
/** Optional references to evidence anchors in artifacts. */
|
|
47
58
|
evidenceRefs?: string[];
|
|
59
|
+
/**
|
|
60
|
+
* Fulfillment mode this entry was executed under. Omitted on legacy rows
|
|
61
|
+
* (treated as `"isolated"` for Claude, otherwise inferred from the active
|
|
62
|
+
* harness).
|
|
63
|
+
*/
|
|
64
|
+
fulfillmentMode?: DelegationFulfillmentMode;
|
|
48
65
|
/** Schema version marker for span-compatible delegation logs. */
|
|
49
66
|
schemaVersion?: 1;
|
|
50
67
|
};
|
|
@@ -54,10 +71,21 @@ export type DelegationLedger = {
|
|
|
54
71
|
};
|
|
55
72
|
export declare function readDelegationLedger(projectRoot: string): Promise<DelegationLedger>;
|
|
56
73
|
export declare function appendDelegation(projectRoot: string, entry: DelegationEntry): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Aggregate the fulfillment mode cclaw expects for the active harness set.
|
|
76
|
+
* Priority native > generic-dispatch > role-switch > waiver — the best
|
|
77
|
+
* available mode wins so mixed installs (e.g. claude + codex) inherit the
|
|
78
|
+
* strongest guarantee.
|
|
79
|
+
*/
|
|
80
|
+
export declare function expectedFulfillmentMode(fallbacks: SubagentFallback[]): DelegationFulfillmentMode;
|
|
57
81
|
export declare function checkMandatoryDelegations(projectRoot: string, stage: FlowStage): Promise<{
|
|
58
82
|
satisfied: boolean;
|
|
59
83
|
missing: string[];
|
|
60
84
|
waived: string[];
|
|
61
85
|
autoWaived: string[];
|
|
62
86
|
staleIgnored: string[];
|
|
87
|
+
/** Delegation rows missing required evidence under a role-switch fallback. */
|
|
88
|
+
missingEvidence: string[];
|
|
89
|
+
/** Expected fulfillment mode for the active harness set. */
|
|
90
|
+
expectedMode: DelegationFulfillmentMode;
|
|
63
91
|
}>;
|
package/dist/delegation.js
CHANGED
|
@@ -54,6 +54,11 @@ function isDelegationEntry(value) {
|
|
|
54
54
|
(o.taskId === undefined || typeof o.taskId === "string") &&
|
|
55
55
|
(o.waiverReason === undefined || typeof o.waiverReason === "string") &&
|
|
56
56
|
(o.runId === undefined || typeof o.runId === "string") &&
|
|
57
|
+
(o.fulfillmentMode === undefined ||
|
|
58
|
+
o.fulfillmentMode === "isolated" ||
|
|
59
|
+
o.fulfillmentMode === "generic-dispatch" ||
|
|
60
|
+
o.fulfillmentMode === "role-switch" ||
|
|
61
|
+
o.fulfillmentMode === "harness-waiver") &&
|
|
57
62
|
(o.conditionTrigger === undefined || typeof o.conditionTrigger === "string") &&
|
|
58
63
|
(o.tokens === undefined || isDelegationTokenUsage(o.tokens)) &&
|
|
59
64
|
retryOk &&
|
|
@@ -128,6 +133,23 @@ export async function appendDelegation(projectRoot, entry) {
|
|
|
128
133
|
await writeFileSafe(filePath, `${JSON.stringify(ledger, null, 2)}\n`);
|
|
129
134
|
});
|
|
130
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Aggregate the fulfillment mode cclaw expects for the active harness set.
|
|
138
|
+
* Priority native > generic-dispatch > role-switch > waiver — the best
|
|
139
|
+
* available mode wins so mixed installs (e.g. claude + codex) inherit the
|
|
140
|
+
* strongest guarantee.
|
|
141
|
+
*/
|
|
142
|
+
export function expectedFulfillmentMode(fallbacks) {
|
|
143
|
+
if (fallbacks.length === 0)
|
|
144
|
+
return "isolated";
|
|
145
|
+
if (fallbacks.some((f) => f === "native"))
|
|
146
|
+
return "isolated";
|
|
147
|
+
if (fallbacks.some((f) => f === "generic-dispatch"))
|
|
148
|
+
return "generic-dispatch";
|
|
149
|
+
if (fallbacks.some((f) => f === "role-switch"))
|
|
150
|
+
return "role-switch";
|
|
151
|
+
return "harness-waiver";
|
|
152
|
+
}
|
|
131
153
|
export async function checkMandatoryDelegations(projectRoot, stage) {
|
|
132
154
|
const mandatory = stageSchema(stage).mandatoryDelegations;
|
|
133
155
|
const { activeRunId } = await readFlowState(projectRoot);
|
|
@@ -140,15 +162,21 @@ export async function checkMandatoryDelegations(projectRoot, stage) {
|
|
|
140
162
|
const missing = [];
|
|
141
163
|
const waived = [];
|
|
142
164
|
const autoWaived = [];
|
|
165
|
+
const missingEvidence = [];
|
|
143
166
|
const config = await readConfig(projectRoot).catch(() => null);
|
|
144
167
|
const harnesses = config?.harnesses ?? [];
|
|
145
|
-
const
|
|
146
|
-
|
|
168
|
+
const fallbacks = harnesses.map((h) => HARNESS_ADAPTERS[h].capabilities.subagentFallback);
|
|
169
|
+
const expectedMode = expectedFulfillmentMode(fallbacks);
|
|
170
|
+
const onlyWaiverFallback = harnesses.length > 0 && fallbacks.every((f) => f === "waiver");
|
|
147
171
|
for (const agent of mandatory) {
|
|
148
172
|
const rows = forRun.filter((e) => e.agent === agent);
|
|
149
|
-
const
|
|
173
|
+
const completedRows = rows.filter((e) => e.status === "completed");
|
|
174
|
+
const waivedRows = rows.filter((e) => e.status === "waived");
|
|
175
|
+
const hasCompleted = completedRows.length > 0;
|
|
176
|
+
const hasWaived = waivedRows.length > 0;
|
|
177
|
+
const ok = hasCompleted || hasWaived;
|
|
150
178
|
if (!ok) {
|
|
151
|
-
if (
|
|
179
|
+
if (onlyWaiverFallback) {
|
|
152
180
|
const existingHarnessWaiver = rows.some((e) => e.status === "waived" && e.waiverReason === "harness_limitation");
|
|
153
181
|
if (!existingHarnessWaiver) {
|
|
154
182
|
await appendDelegation(projectRoot, {
|
|
@@ -157,6 +185,7 @@ export async function checkMandatoryDelegations(projectRoot, stage) {
|
|
|
157
185
|
mode: "mandatory",
|
|
158
186
|
status: "waived",
|
|
159
187
|
waiverReason: "harness_limitation",
|
|
188
|
+
fulfillmentMode: "harness-waiver",
|
|
160
189
|
ts: new Date().toISOString(),
|
|
161
190
|
runId: activeRunId
|
|
162
191
|
});
|
|
@@ -167,16 +196,27 @@ export async function checkMandatoryDelegations(projectRoot, stage) {
|
|
|
167
196
|
else {
|
|
168
197
|
missing.push(agent);
|
|
169
198
|
}
|
|
199
|
+
continue;
|
|
170
200
|
}
|
|
171
|
-
|
|
201
|
+
if (hasWaived) {
|
|
172
202
|
waived.push(agent);
|
|
173
203
|
}
|
|
204
|
+
// Under role-switch fallback, a `completed` row is only credible if it
|
|
205
|
+
// carries at least one evidenceRef — otherwise the agent might have
|
|
206
|
+
// claimed role-switch satisfaction without showing its work.
|
|
207
|
+
if (hasCompleted &&
|
|
208
|
+
expectedMode === "role-switch" &&
|
|
209
|
+
!completedRows.some((e) => Array.isArray(e.evidenceRefs) && e.evidenceRefs.length > 0)) {
|
|
210
|
+
missingEvidence.push(agent);
|
|
211
|
+
}
|
|
174
212
|
}
|
|
175
213
|
return {
|
|
176
|
-
satisfied: missing.length === 0,
|
|
214
|
+
satisfied: missing.length === 0 && missingEvidence.length === 0,
|
|
177
215
|
missing,
|
|
178
216
|
waived,
|
|
179
217
|
autoWaived,
|
|
180
|
-
staleIgnored
|
|
218
|
+
staleIgnored,
|
|
219
|
+
missingEvidence,
|
|
220
|
+
expectedMode
|
|
181
221
|
};
|
|
182
222
|
}
|
package/dist/doctor.js
CHANGED
|
@@ -23,6 +23,7 @@ import { doctorCheckMetadata } from "./doctor-registry.js";
|
|
|
23
23
|
import { LANGUAGE_RULE_PACK_DIR, LANGUAGE_RULE_PACK_FILES, LEGACY_LANGUAGE_RULE_PACK_FOLDERS, UTILITY_SKILL_FOLDERS } from "./content/utility-skills.js";
|
|
24
24
|
import { CONTEXT_MODES, DEFAULT_CONTEXT_MODE } from "./content/contexts.js";
|
|
25
25
|
import { DOCTOR_REFERENCE_MARKDOWN } from "./content/doctor-references.js";
|
|
26
|
+
import { HARNESS_PLAYBOOKS_DIR, harnessPlaybookFileName } from "./content/harness-playbooks.js";
|
|
26
27
|
import { validateHookDocument } from "./hook-schema.js";
|
|
27
28
|
const execFileAsync = promisify(execFile);
|
|
28
29
|
async function isGitRepo(projectRoot) {
|
|
@@ -375,6 +376,12 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
375
376
|
ok: await exists(path.join(projectRoot, RUNTIME_ROOT, "references", "harnesses.md")),
|
|
376
377
|
details: `${RUNTIME_ROOT}/references/harnesses.md`
|
|
377
378
|
});
|
|
379
|
+
const playbookDir = path.join(projectRoot, RUNTIME_ROOT, ...HARNESS_PLAYBOOKS_DIR.split("/"));
|
|
380
|
+
checks.push({
|
|
381
|
+
name: "harness_ref:playbooks_index",
|
|
382
|
+
ok: await exists(path.join(playbookDir, "README.md")),
|
|
383
|
+
details: `${RUNTIME_ROOT}/${HARNESS_PLAYBOOKS_DIR}/README.md`
|
|
384
|
+
});
|
|
378
385
|
const doctorRefDir = path.join(projectRoot, RUNTIME_ROOT, "references", "doctor");
|
|
379
386
|
for (const fileName of Object.keys(DOCTOR_REFERENCE_MARKDOWN)) {
|
|
380
387
|
const refPath = path.join(doctorRefDir, fileName);
|
|
@@ -475,6 +482,12 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
475
482
|
details: shimPath
|
|
476
483
|
});
|
|
477
484
|
}
|
|
485
|
+
const playbookFile = path.join(projectRoot, RUNTIME_ROOT, ...HARNESS_PLAYBOOKS_DIR.split("/"), harnessPlaybookFileName(harness));
|
|
486
|
+
checks.push({
|
|
487
|
+
name: `harness_ref:playbook:${harness}`,
|
|
488
|
+
ok: await exists(playbookFile),
|
|
489
|
+
details: `${RUNTIME_ROOT}/${HARNESS_PLAYBOOKS_DIR}/${harnessPlaybookFileName(harness)}`
|
|
490
|
+
});
|
|
478
491
|
}
|
|
479
492
|
const agentsFile = path.join(projectRoot, "AGENTS.md");
|
|
480
493
|
let agentsBlockOk = false;
|
|
@@ -1298,12 +1311,15 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
1298
1311
|
details: `${RUNTIME_ROOT}/runs must exist for archived feature snapshots`
|
|
1299
1312
|
});
|
|
1300
1313
|
const delegation = await checkMandatoryDelegations(projectRoot, flowState.currentStage);
|
|
1314
|
+
const missingEvidenceNote = delegation.missingEvidence && delegation.missingEvidence.length > 0
|
|
1315
|
+
? ` (role-switch rows without evidenceRefs: ${delegation.missingEvidence.join(", ")})`
|
|
1316
|
+
: "";
|
|
1301
1317
|
checks.push({
|
|
1302
1318
|
name: "delegation:mandatory:current_stage",
|
|
1303
1319
|
ok: delegation.satisfied,
|
|
1304
1320
|
details: delegation.satisfied
|
|
1305
|
-
? `All mandatory delegations satisfied for stage "${flowState.currentStage}"`
|
|
1306
|
-
: `Missing mandatory delegations for stage "${flowState.currentStage}": ${delegation.missing.join(", ")}`
|
|
1321
|
+
? `All mandatory delegations satisfied for stage "${flowState.currentStage}" (mode: ${delegation.expectedMode})`
|
|
1322
|
+
: `Missing mandatory delegations for stage "${flowState.currentStage}": ${delegation.missing.join(", ")}${missingEvidenceNote}`
|
|
1307
1323
|
});
|
|
1308
1324
|
checks.push({
|
|
1309
1325
|
name: "warning:delegation:waived",
|
|
@@ -1,19 +1,58 @@
|
|
|
1
1
|
import type { HarnessId } from "./types.js";
|
|
2
2
|
export declare const CCLAW_MARKER_START = "<!-- cclaw-start -->";
|
|
3
3
|
export declare const CCLAW_MARKER_END = "<!-- cclaw-end -->";
|
|
4
|
+
export type SubagentFallback =
|
|
5
|
+
/** Harness has real, isolated subagent dispatch; no fallback needed. */
|
|
6
|
+
"native"
|
|
7
|
+
/**
|
|
8
|
+
* Harness has generic dispatch (e.g. Cursor's Task tool with
|
|
9
|
+
* `subagent_type`) but not user-defined named subagents; cclaw maps each
|
|
10
|
+
* named agent to the generic dispatcher with a structured role prompt.
|
|
11
|
+
*/
|
|
12
|
+
| "generic-dispatch"
|
|
13
|
+
/**
|
|
14
|
+
* No isolated dispatch — the agent performs the named subagent's role
|
|
15
|
+
* in-session with an explicit role announce + delegation-log entry
|
|
16
|
+
* carrying evidenceRefs. Accepted as `completed` in delegation checks.
|
|
17
|
+
*/
|
|
18
|
+
| "role-switch"
|
|
19
|
+
/**
|
|
20
|
+
* No meaningful fallback — mandatory delegations can only be waived
|
|
21
|
+
* under `waiverReason: "harness_limitation"`.
|
|
22
|
+
*/
|
|
23
|
+
| "waiver";
|
|
4
24
|
export interface HarnessAdapter {
|
|
5
25
|
id: HarnessId;
|
|
6
26
|
commandDir: string;
|
|
7
27
|
capabilities: {
|
|
8
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Level of native subagent dispatch:
|
|
30
|
+
* - `full` — isolated workers + user-defined named subagents (Claude).
|
|
31
|
+
* - `generic` — generic dispatcher (Task) without named agents (Cursor).
|
|
32
|
+
* - `partial` — plugin-based dispatch, not a first-class primitive
|
|
33
|
+
* (OpenCode).
|
|
34
|
+
* - `none` — no dispatch primitive at all (Codex).
|
|
35
|
+
*/
|
|
36
|
+
nativeSubagentDispatch: "full" | "generic" | "partial" | "none";
|
|
9
37
|
hookSurface: "full" | "plugin" | "limited" | "none";
|
|
10
38
|
structuredAsk: "AskUserQuestion" | "AskQuestion" | "plain-text";
|
|
39
|
+
/**
|
|
40
|
+
* Declared fallback pattern used when the harness cannot satisfy a
|
|
41
|
+
* mandatory delegation natively. Drives `checkMandatoryDelegations`
|
|
42
|
+
* and the generated playbook per harness.
|
|
43
|
+
*/
|
|
44
|
+
subagentFallback: SubagentFallback;
|
|
11
45
|
};
|
|
12
46
|
}
|
|
13
47
|
export declare function harnessShimFileNames(): string[];
|
|
14
48
|
export declare const HARNESS_ADAPTERS: Record<HarnessId, HarnessAdapter>;
|
|
15
49
|
export type HarnessTier = "tier1" | "tier2" | "tier3";
|
|
16
50
|
export declare function harnessTier(harnessId: HarnessId): HarnessTier;
|
|
51
|
+
/**
|
|
52
|
+
* Harness IDs ordered from best (tier1) to least-capable. Stable sort — same
|
|
53
|
+
* tier preserves declaration order.
|
|
54
|
+
*/
|
|
55
|
+
export declare function harnessesByTier(): HarnessId[];
|
|
17
56
|
/** Removes the cclaw AGENTS.md block. */
|
|
18
57
|
export declare function stripCclawBlock(content: string): string;
|
|
19
58
|
export declare function removeCclawFromAgentsMd(projectRoot: string): Promise<void>;
|