cclaw-cli 0.31.0 → 0.32.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 +41 -22
- package/dist/content/archive-command.js +58 -32
- package/dist/content/compound-command.js +79 -24
- package/dist/content/next-command.js +40 -8
- package/dist/content/protocols.js +39 -8
- package/dist/content/retro-command.js +104 -32
- package/dist/flow-state.d.ts +31 -0
- package/dist/flow-state.js +37 -1
- package/dist/runs.js +39 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -200,8 +200,9 @@ cclaw has eight stages, but a single prompt rarely needs all of them.
|
|
|
200
200
|
| **standard** _(default)_ | all 8 stages | `new feature`, `refactor`, `migration`, `platform`, `schema`, `architecture` |
|
|
201
201
|
|
|
202
202
|
Each stage produces a dated artifact under `.cclaw/artifacts/`:
|
|
203
|
-
`00-idea.md` (seed)
|
|
204
|
-
|
|
203
|
+
`00-idea.md` (seed), `01-brainstorm.md` through `08-ship.md`, and
|
|
204
|
+
`09-retro.md` at automatic closeout (see
|
|
205
|
+
[Ship and closeout](#ship-and-closeout--automatic-resumable)).
|
|
205
206
|
|
|
206
207
|
### Track heuristics are configurable
|
|
207
208
|
|
|
@@ -254,8 +255,12 @@ it into ceremony:
|
|
|
254
255
|
in a single place (`.cclaw/contexts/`), so every skill speaks the same
|
|
255
256
|
dialect.
|
|
256
257
|
- **Knowledge capture throughout the flow.** Every stage completion
|
|
257
|
-
protocol
|
|
258
|
-
|
|
258
|
+
protocol emits typed entries (`rule` / `pattern` / `lesson`) to
|
|
259
|
+
`.cclaw/knowledge.jsonl` as the flow progresses — not only at retro.
|
|
260
|
+
Retro itself adds a `compound` entry, and the automatic compound pass
|
|
261
|
+
after ship promotes recurring entries (≥ 3) into first-class
|
|
262
|
+
rules/protocols/skills so the **next** run is easier. Strict JSONL
|
|
263
|
+
schema keeps the whole thing machine-queryable.
|
|
259
264
|
- **Automatic integrity checks.** Runtime health is verified on every
|
|
260
265
|
stage transition — no command you need to remember to run.
|
|
261
266
|
|
|
@@ -278,24 +283,38 @@ native subagent dispatch, such as Codex — see
|
|
|
278
283
|
|
|
279
284
|
---
|
|
280
285
|
|
|
281
|
-
## Ship and closeout
|
|
282
|
-
|
|
283
|
-
Shipping writes `08-ship.md`
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
286
|
+
## Ship and closeout — automatic, resumable
|
|
287
|
+
|
|
288
|
+
Shipping writes `08-ship.md`. `/cc-next` then automatically walks the
|
|
289
|
+
feature through a deterministic three-step closeout without extra
|
|
290
|
+
commands from you:
|
|
291
|
+
|
|
292
|
+
1. **Retro (`09-retro.md`).** cclaw drafts a retrospective from your
|
|
293
|
+
stage artifacts, the delegation log, and the knowledge entries
|
|
294
|
+
recorded during the run. It then asks exactly **one** structured
|
|
295
|
+
question:
|
|
296
|
+
- **accept** *(default)* — keep the draft, record one `compound`
|
|
297
|
+
knowledge entry, advance.
|
|
298
|
+
- **edit** — you edit `09-retro.md` in place, then `/cc-next` again.
|
|
299
|
+
- **skip** — record a one-line reason, continue (archive will
|
|
300
|
+
surface the skip in the run manifest).
|
|
301
|
+
2. **Compound pass.** If the knowledge store has clusters recurring 3+
|
|
302
|
+
times, cclaw proposes concrete lifts into rules/protocols/skills and
|
|
303
|
+
asks once: apply-all / apply-selected / skip. An empty pass advances
|
|
304
|
+
silently.
|
|
305
|
+
3. **Archive.** Moves artifacts into `.cclaw/runs/YYYY-MM-DD-<slug>/`,
|
|
306
|
+
snapshots `state/`, writes a manifest, and resets `flow-state.json`
|
|
307
|
+
to the track's initial stage.
|
|
308
|
+
|
|
309
|
+
The chain is driven by `closeout.shipSubstate` inside `flow-state.json`
|
|
310
|
+
(`retro_review` → `compound_review` → `ready_to_archive` → `archived`).
|
|
311
|
+
If your session dies mid-closeout, a new `/cc-next` resumes at the
|
|
312
|
+
exact step — retro drafts are not regenerated and no structured ask is
|
|
313
|
+
repeated silently.
|
|
314
|
+
|
|
315
|
+
You can still invoke each step manually (`/cc-ops retro`, `/cc-ops
|
|
316
|
+
compound`, `/cc-ops archive`), but for the default path you do not need
|
|
317
|
+
to: `/cc-next` is the only command.
|
|
299
318
|
|
|
300
319
|
---
|
|
301
320
|
|
|
@@ -15,35 +15,46 @@ export function archiveCommandContract() {
|
|
|
15
15
|
|
|
16
16
|
## Purpose
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Finalize the active cclaw run: move artifacts to \`${runsPath()}/<archive-id>\`,
|
|
19
|
+
snapshot state, write a manifest, and reset runtime for the next run.
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
Auto-triggered by \`/cc-next\` when \`closeout.shipSubstate === "ready_to_archive"\`.
|
|
22
|
+
Direct invocation from a harness command is supported but rarely needed.
|
|
22
23
|
|
|
23
24
|
## HARD-GATE
|
|
24
25
|
|
|
25
|
-
- Do not archive
|
|
26
|
-
- Do not
|
|
27
|
-
|
|
26
|
+
- Do not archive with \`closeout.shipSubstate !== "ready_to_archive"\`.
|
|
27
|
+
- Do not archive a shipped run when \`retro.completedAt\` is missing and
|
|
28
|
+
\`closeout.retroSkipped !== true\`.
|
|
29
|
+
- Never hand-move files between \`${activeArtifactsPath()}\` and \`${runsPath()}\`.
|
|
30
|
+
Always run the archive runtime command so the snapshot+manifest stay
|
|
31
|
+
atomic.
|
|
28
32
|
|
|
29
33
|
## Inputs
|
|
30
34
|
|
|
31
|
-
\`/cc-ops archive [--name=<slug>]
|
|
35
|
+
\`/cc-ops archive [--name=<slug>]\`
|
|
36
|
+
|
|
37
|
+
(Legacy flags \`--skip-retro --retro-reason=<text>\` still exist for CLI
|
|
38
|
+
invocations; in-harness the skip path is driven by \`closeout.retroSkipped\`
|
|
39
|
+
set during retro.)
|
|
32
40
|
|
|
33
41
|
## Algorithm
|
|
34
42
|
|
|
35
43
|
1. Read \`${flowStatePath()}\`.
|
|
36
|
-
2.
|
|
37
|
-
|
|
44
|
+
2. Verify \`closeout.shipSubstate === "ready_to_archive"\`. If not, report
|
|
45
|
+
\`closeout not ready (state=<substate>) | run: /cc-next\` and stop.
|
|
38
46
|
3. Build archive command:
|
|
39
|
-
- base: \`npx cclaw archive
|
|
40
|
-
- optional: \`--name=<slug
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
- base: \`npx cclaw archive\`,
|
|
48
|
+
- optional: \`--name=<slug>\`,
|
|
49
|
+
- legacy override: \`--skip-retro --retro-reason=<text>\` (only when user
|
|
50
|
+
explicitly wants the CLI skip path).
|
|
51
|
+
4. Execute the archive command in project root.
|
|
52
|
+
5. On success, flow-state is reset to the initial stage for the default
|
|
53
|
+
track; \`closeout.shipSubstate\` returns to \`"idle"\` on reset.
|
|
54
|
+
6. Surface:
|
|
44
55
|
- archive id/path,
|
|
45
|
-
- reset stage
|
|
46
|
-
- knowledge curation hint when
|
|
56
|
+
- reset stage,
|
|
57
|
+
- knowledge curation hint when \`activeEntryCount >= softThreshold\`.
|
|
47
58
|
|
|
48
59
|
## Output format
|
|
49
60
|
|
|
@@ -63,36 +74,51 @@ cclaw archive
|
|
|
63
74
|
export function archiveCommandSkillMarkdown() {
|
|
64
75
|
return `---
|
|
65
76
|
name: ${ARCHIVE_SKILL_NAME}
|
|
66
|
-
description: "
|
|
77
|
+
description: "Finalize the active cclaw run. Auto-triggered by /cc-next when shipSubstate=ready_to_archive."
|
|
67
78
|
---
|
|
68
79
|
|
|
69
80
|
# /cc-ops archive
|
|
70
81
|
|
|
71
82
|
## HARD-GATE
|
|
72
83
|
|
|
73
|
-
Never simulate archive by hand-editing runtime files. Always execute the
|
|
74
|
-
runtime command so state snapshots and manifest generation stay
|
|
84
|
+
Never simulate archive by hand-editing runtime files. Always execute the
|
|
85
|
+
archive runtime command so state snapshots and manifest generation stay
|
|
86
|
+
atomic. Never bypass the substate check — if retro/compound haven't
|
|
87
|
+
advanced the substate to \`ready_to_archive\`, stop and surface the
|
|
88
|
+
mismatch.
|
|
75
89
|
|
|
76
90
|
## Protocol
|
|
77
91
|
|
|
78
92
|
1. Read \`${flowStatePath()}\`:
|
|
79
|
-
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
- if \`closeout.shipSubstate !== "ready_to_archive"\`, stop and route
|
|
94
|
+
the user back to \`/cc-next\` (it will resume at the correct step),
|
|
95
|
+
- sanity-check: \`completedStages\` must include \`"ship"\`,
|
|
96
|
+
- sanity-check: \`retro.completedAt\` is set **or**
|
|
97
|
+
\`closeout.retroSkipped === true\` with a reason.
|
|
98
|
+
2. Build shell command:
|
|
99
|
+
- \`npx cclaw archive\`,
|
|
100
|
+
- append \`--name=<slug>\` when provided,
|
|
101
|
+
- append legacy \`--skip-retro --retro-reason=<text>\` only when the user
|
|
102
|
+
explicitly requests the CLI skip path (normally not needed — skip is
|
|
103
|
+
captured in \`closeout\` during retro).
|
|
104
|
+
3. Run command from repo root.
|
|
105
|
+
4. Relay key lines from output:
|
|
106
|
+
- archive destination under \`${runsPath()}\`,
|
|
107
|
+
- flow reset confirmation,
|
|
108
|
+
- knowledge curation recommendation if \`activeEntryCount >= 50\`.
|
|
109
|
+
|
|
110
|
+
## Resume semantics
|
|
111
|
+
|
|
112
|
+
Archive is idempotent on a per-run basis. If a previous session ran
|
|
113
|
+
archive successfully, the active artifacts directory is empty and
|
|
114
|
+
\`closeout.shipSubstate\` is \`"idle"\`; \`/cc-next\` will simply report
|
|
115
|
+
"Flow complete" or prompt for a new \`/cc\` input.
|
|
91
116
|
|
|
92
117
|
## Validation
|
|
93
118
|
|
|
94
|
-
- \`${runsPath()}\` contains a new archive folder.
|
|
119
|
+
- \`${runsPath()}\` contains a new archive folder for this run.
|
|
95
120
|
- \`${activeArtifactsPath()}\` is reset for the next run.
|
|
96
121
|
- \`${flowStatePath()}\` is valid JSON and points to the initial stage.
|
|
122
|
+
- \`closeout.shipSubstate === "idle"\` after reset.
|
|
97
123
|
`;
|
|
98
124
|
}
|
|
@@ -6,29 +6,49 @@ export function compoundCommandContract() {
|
|
|
6
6
|
|
|
7
7
|
## Purpose
|
|
8
8
|
|
|
9
|
-
Lift repeated lessons into durable
|
|
10
|
-
so the next run is easier and safer.
|
|
9
|
+
Lift repeated lessons from \`${RUNTIME_ROOT}/knowledge.jsonl\` into durable
|
|
10
|
+
project assets (rules, protocols, skills) so the next run is easier and safer.
|
|
11
|
+
|
|
12
|
+
Auto-triggered by \`/cc-next\` when \`closeout.shipSubstate === "compound_review"\`.
|
|
13
|
+
Direct invocation is supported but rarely needed.
|
|
11
14
|
|
|
12
15
|
## HARD-GATE
|
|
13
16
|
|
|
14
|
-
- Do not mutate rules/skills without explicit user approval.
|
|
15
|
-
- Every proposal must cite concrete knowledge evidence (line
|
|
17
|
+
- Do not mutate rules/skills/protocols without explicit user approval.
|
|
18
|
+
- Every proposal must cite concrete knowledge evidence (line refs or IDs).
|
|
16
19
|
- Keep scope focused: one compound change set per run.
|
|
20
|
+
- Do not block the archive step if no clusters qualify — record an empty
|
|
21
|
+
compound pass and advance.
|
|
22
|
+
|
|
23
|
+
## Inputs
|
|
24
|
+
|
|
25
|
+
\`/cc-ops compound\` (no flags). The structured ask presents candidates;
|
|
26
|
+
the user can approve individual lifts, accept-all, or skip.
|
|
17
27
|
|
|
18
28
|
## Algorithm
|
|
19
29
|
|
|
20
|
-
1. Read \`${RUNTIME_ROOT}/knowledge.jsonl
|
|
21
|
-
2. Cluster
|
|
22
|
-
3.
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
1. Read \`${RUNTIME_ROOT}/knowledge.jsonl\` (strict JSONL, one entry per line).
|
|
31
|
+
2. Cluster entries by \`trigger\` + \`action\` similarity.
|
|
32
|
+
3. Filter candidates whose recurrence count >= 3.
|
|
33
|
+
4. If **no candidates** exist:
|
|
34
|
+
- set \`closeout.compoundCompletedAt = <ISO>\`,
|
|
35
|
+
- set \`closeout.compoundPromoted = 0\`,
|
|
36
|
+
- set \`closeout.shipSubstate = "ready_to_archive"\`,
|
|
37
|
+
- emit \`compound: no candidates | next: /cc-next\` and stop.
|
|
38
|
+
5. Otherwise, present **one** structured ask (AskUserQuestion / AskQuestion /
|
|
39
|
+
plain text) summarising all candidates at once:
|
|
40
|
+
- \`apply-all\` (default) — apply every listed lift,
|
|
41
|
+
- \`apply-selected\` — prompt per-candidate,
|
|
42
|
+
- \`skip\` — record a skip reason and advance without changes.
|
|
43
|
+
6. Apply approved lifts to the target file(s). Each lift also appends a
|
|
44
|
+
\`type: "compound"\` entry back to \`${RUNTIME_ROOT}/knowledge.jsonl\`
|
|
45
|
+
summarising what was lifted.
|
|
46
|
+
7. Update flow-state:
|
|
47
|
+
- \`closeout.compoundCompletedAt = <ISO>\`,
|
|
48
|
+
- \`closeout.compoundPromoted = <count>\`,
|
|
49
|
+
- \`closeout.compoundSkipped = true\` if user picked skip,
|
|
50
|
+
- \`closeout.shipSubstate = "ready_to_archive"\`.
|
|
51
|
+
8. Emit one-line summary: \`compound: promoted=<N> skipped=<bool> | next: /cc-next\`.
|
|
32
52
|
|
|
33
53
|
## Primary skill
|
|
34
54
|
|
|
@@ -38,7 +58,7 @@ so the next run is easier and safer.
|
|
|
38
58
|
export function compoundCommandSkillMarkdown() {
|
|
39
59
|
return `---
|
|
40
60
|
name: ${COMPOUND_SKILL_NAME}
|
|
41
|
-
description: "
|
|
61
|
+
description: "Lift repeated learnings into durable rules/protocols/skills. Auto-triggered after retro accept."
|
|
42
62
|
---
|
|
43
63
|
|
|
44
64
|
# /cc-ops compound
|
|
@@ -49,13 +69,21 @@ description: "Compound mode: convert repeated learnings into durable rules/proto
|
|
|
49
69
|
|
|
50
70
|
## HARD-GATE
|
|
51
71
|
|
|
52
|
-
No silent codification. Every lift requires explicit user approval.
|
|
72
|
+
No silent codification. Every lift requires explicit user approval. An
|
|
73
|
+
empty pass is allowed and must advance \`closeout.shipSubstate\` to
|
|
74
|
+
\`"ready_to_archive"\`.
|
|
53
75
|
|
|
54
76
|
## Protocol
|
|
55
77
|
|
|
56
|
-
1. Parse \`.cclaw/knowledge.jsonl\` and group repeated lessons
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
1. Parse \`.cclaw/knowledge.jsonl\` and group repeated lessons by
|
|
79
|
+
trigger+action similarity.
|
|
80
|
+
2. Keep only candidates with recurrence >= 3 and an actionable lift path.
|
|
81
|
+
3. If none qualify, record an empty pass:
|
|
82
|
+
- \`closeout.compoundCompletedAt = <ISO>\`,
|
|
83
|
+
- \`closeout.compoundPromoted = 0\`,
|
|
84
|
+
- \`closeout.shipSubstate = "ready_to_archive"\`,
|
|
85
|
+
- announce \`compound: no candidates\` and stop.
|
|
86
|
+
4. Otherwise, render each candidate as:
|
|
59
87
|
|
|
60
88
|
\`\`\`
|
|
61
89
|
Candidate: <short title>
|
|
@@ -65,8 +93,35 @@ Change type: <add/update/remove>
|
|
|
65
93
|
Expected benefit: <what regressions this prevents>
|
|
66
94
|
\`\`\`
|
|
67
95
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
96
|
+
5. Present **one** structured question with three options:
|
|
97
|
+
- \`apply-all\` (default) — apply every candidate,
|
|
98
|
+
- \`apply-selected\` — prompt per-candidate approval next,
|
|
99
|
+
- \`skip\` — record a skip reason and advance.
|
|
100
|
+
|
|
101
|
+
6. For approved candidates:
|
|
102
|
+
- edit the target file(s) with the lift,
|
|
103
|
+
- append a \`type: "compound"\` entry to \`.cclaw/knowledge.jsonl\`
|
|
104
|
+
describing what was promoted.
|
|
105
|
+
|
|
106
|
+
7. Update flow-state \`closeout\`:
|
|
107
|
+
- \`compoundCompletedAt\`,
|
|
108
|
+
- \`compoundPromoted\` (count),
|
|
109
|
+
- \`compoundSkipped\` (boolean) + \`compoundSkipReason\` when applicable,
|
|
110
|
+
- \`shipSubstate = "ready_to_archive"\`.
|
|
111
|
+
|
|
112
|
+
## Resume semantics
|
|
113
|
+
|
|
114
|
+
A new session with \`shipSubstate === "compound_review"\` re-runs the scan
|
|
115
|
+
and re-asks the structured question. If the user already applied lifts in
|
|
116
|
+
a previous session but the state file was not updated, they should pick
|
|
117
|
+
\`skip\` with reason \`already-applied\` — compound is idempotent from the
|
|
118
|
+
closeout chain's perspective.
|
|
119
|
+
|
|
120
|
+
## Validation
|
|
121
|
+
|
|
122
|
+
- \`closeout.compoundCompletedAt\` is set.
|
|
123
|
+
- \`closeout.shipSubstate === "ready_to_archive"\`.
|
|
124
|
+
- If lifts were applied, the target files show the edit and at least one
|
|
125
|
+
new \`compound\` line exists in \`.cclaw/knowledge.jsonl\`.
|
|
71
126
|
`;
|
|
72
127
|
}
|
|
@@ -33,7 +33,7 @@ This is the only progression command the user needs to drive the entire flow. St
|
|
|
33
33
|
|
|
34
34
|
- **Do not** invent gate completion: use only \`${flowPath}\` plus observable evidence in repo artifacts.
|
|
35
35
|
- **Do not** skip stages: advance only from \`currentStage\` to its configured successor.
|
|
36
|
-
-
|
|
36
|
+
- After ship completes, the closeout chain **retro -> compound -> archive** runs automatically, driven by \`closeout.shipSubstate\`. Do not ask the user to type those commands manually — follow the substate switch in Path B below.
|
|
37
37
|
|
|
38
38
|
## Algorithm (mandatory)
|
|
39
39
|
|
|
@@ -55,9 +55,24 @@ This is the only progression command the user needs to drive the entire flow. St
|
|
|
55
55
|
### Path B: Current stage IS complete (all gates passed, all delegations satisfied)
|
|
56
56
|
|
|
57
57
|
→ If current stage's \`next\` is **\`done\`**:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-
|
|
58
|
+
|
|
59
|
+
When \`currentStage === "ship"\`, route by **\`closeout.shipSubstate\`**:
|
|
60
|
+
- \`"idle"\` or missing -> set \`closeout.shipSubstate = "retro_review"\`, then
|
|
61
|
+
load \`${RUNTIME_ROOT}/commands/retro.md\` + \`${RUNTIME_ROOT}/skills/flow-retro/SKILL.md\`
|
|
62
|
+
and execute the retro protocol (draft + one structured accept/edit/skip ask).
|
|
63
|
+
- \`"retro_review"\` -> continue the retro protocol (re-ask the structured
|
|
64
|
+
question; the draft already exists — do not regenerate it).
|
|
65
|
+
- \`"compound_review"\` -> load \`${RUNTIME_ROOT}/commands/compound.md\` +
|
|
66
|
+
\`${RUNTIME_ROOT}/skills/flow-compound/SKILL.md\`, execute the compound
|
|
67
|
+
scan, ask user **one** structured question (apply / skip) per candidate
|
|
68
|
+
cluster or a single accept-all / skip choice, and advance substate on
|
|
69
|
+
completion or skip.
|
|
70
|
+
- \`"ready_to_archive"\` -> load \`${RUNTIME_ROOT}/commands/archive.md\` +
|
|
71
|
+
\`${RUNTIME_ROOT}/skills/flow-archive/SKILL.md\`, run archive, reset state.
|
|
72
|
+
- \`"archived"\` (transient) -> report "run archived" and stop.
|
|
73
|
+
|
|
74
|
+
Otherwise report **"Flow complete. All stages finished."** and stop.
|
|
75
|
+
|
|
61
76
|
→ Otherwise: load **\`${RUNTIME_ROOT}/skills/<skillFolder>/SKILL.md\`** and **\`${RUNTIME_ROOT}/commands/<nextStage>.md\`** for the successor stage. Execute that stage's protocol.
|
|
62
77
|
|
|
63
78
|
### Track-aware successor resolution
|
|
@@ -74,6 +89,9 @@ This is the only progression command the user needs to drive the entire flow. St
|
|
|
74
89
|
\`/cc-next\` in a **new session** = resume from where you left off:
|
|
75
90
|
- Flow-state records \`currentStage\` and which gates have passed.
|
|
76
91
|
- The stage skill reads upstream artifacts and picks up context.
|
|
92
|
+
- \`closeout.shipSubstate\` carries the post-ship substate, so a crashed
|
|
93
|
+
session during retro/compound/archive resumes at the exact step without
|
|
94
|
+
regenerating the retro draft.
|
|
77
95
|
- No special resume command needed — \`/cc-next\` IS the resume command.
|
|
78
96
|
|
|
79
97
|
## Primary skill
|
|
@@ -149,11 +167,25 @@ Execute the stage protocol. The stage skill handles interaction, STOP points, ga
|
|
|
149
167
|
|
|
150
168
|
If \`next\` is \`done\`:
|
|
151
169
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
170
|
+
When \`currentStage\` is \`ship\`, automatically drive the **closeout chain**
|
|
171
|
+
by inspecting \`closeout.shipSubstate\`:
|
|
172
|
+
|
|
173
|
+
| shipSubstate | Action |
|
|
174
|
+
|-----------------------|-----------------------------------------------------|
|
|
175
|
+
| \`idle\` / missing | Flip to \`retro_review\` and start retro protocol |
|
|
176
|
+
| \`retro_review\` | Continue retro protocol (re-ask accept/edit/skip) |
|
|
177
|
+
| \`compound_review\` | Run compound scan with a single approve/skip ask |
|
|
178
|
+
| \`ready_to_archive\` | Run archive skill; reset flow-state on success |
|
|
179
|
+
| \`archived\` | Report "run archived"; stop |
|
|
180
|
+
|
|
181
|
+
Each step owns its own state transition. \`/cc-next\` never shells out to
|
|
182
|
+
\`cclaw doctor\` or \`cclaw archive\` automatically — it loads the matching
|
|
183
|
+
skill and command contract and executes the protocol in-session.
|
|
184
|
+
|
|
185
|
+
Otherwise report **"Flow complete. All stages finished."** and stop.
|
|
155
186
|
|
|
156
|
-
Otherwise load the next stage's skill and command
|
|
187
|
+
Otherwise (non-terminal \`next\`): load the next stage's skill and command
|
|
188
|
+
contract, begin execution.
|
|
157
189
|
|
|
158
190
|
## Stage order
|
|
159
191
|
|
|
@@ -46,20 +46,47 @@ Shared closeout sequence applied by every stage skill.
|
|
|
46
46
|
- update \`guardEvidence\`.
|
|
47
47
|
3. Persist stage artifact under \`.cclaw/artifacts/\`.
|
|
48
48
|
4. Run \`npx cclaw doctor\` and resolve failures.
|
|
49
|
-
5. Capture
|
|
50
|
-
|
|
51
|
-
decisions, patterns, or lessons,
|
|
52
|
-
- use \`type=rule|pattern|lesson\` (\`compound\` stays retro-focused).
|
|
49
|
+
5. **Capture through-flow learnings** — see the policy below. Knowledge
|
|
50
|
+
accrues continuously across stages, not just at retro.
|
|
53
51
|
6. Notify user with stage completion and next action (\`/cc-next\`).
|
|
54
52
|
7. Stop; do not auto-run the next stage unless user asks.
|
|
55
53
|
|
|
54
|
+
## Through-flow knowledge capture
|
|
55
|
+
|
|
56
|
+
Knowledge is recorded **throughout the run**, not saved up for retro.
|
|
57
|
+
Each stage contributes a different kind of insight:
|
|
58
|
+
|
|
59
|
+
| Stage | Typical \`type\` | What to capture |
|
|
60
|
+
|-------------|-----------------|-------------------------------------------------------|
|
|
61
|
+
| brainstorm | \`lesson\` | rejected framings and why (only when non-obvious) |
|
|
62
|
+
| scope | \`rule\` | explicit out-of-scope boundaries worth remembering |
|
|
63
|
+
| design | \`pattern\` | architectural trade-offs and their rationale |
|
|
64
|
+
| spec | \`rule\` | non-negotiable acceptance criteria shape |
|
|
65
|
+
| plan | \`pattern\` | effective decomposition / risk-ordering heuristics |
|
|
66
|
+
| tdd | \`pattern\` | red→green→refactor cycle lessons, test-design notes |
|
|
67
|
+
| review | \`lesson\` | recurring defects / blockers caught in this codebase |
|
|
68
|
+
| ship | \`lesson\` | rollback triggers, preflight gotchas |
|
|
69
|
+
| retro | \`compound\` | process accelerators for the **next** run |
|
|
70
|
+
|
|
71
|
+
Rules:
|
|
72
|
+
|
|
73
|
+
- Append 1–3 strict-schema JSONL lines to \`.cclaw/knowledge.jsonl\` per
|
|
74
|
+
stage when that stage produced non-obvious decisions, patterns, or
|
|
75
|
+
lessons. Obvious restatements of the checklist do not count.
|
|
76
|
+
- Use \`type=rule|pattern|lesson\` during stages; reserve \`type=compound\`
|
|
77
|
+
for the retro step so the retro vs. through-flow signal stays
|
|
78
|
+
distinguishable.
|
|
79
|
+
- Set \`origin_stage\` to the stage that emitted the entry and
|
|
80
|
+
\`origin_feature\` to the active feature slug.
|
|
81
|
+
|
|
56
82
|
## Automatic learning capture policy
|
|
57
83
|
|
|
58
84
|
- \`standard\` / \`medium\` tracks: required for \`design\`, \`tdd\`, and \`review\`;
|
|
59
85
|
recommended for other stages.
|
|
60
86
|
- \`quick\` track: recommended only (avoid overhead for tiny fixes).
|
|
61
87
|
- "No learning captured" is acceptable only when explicitly justified (e.g. pure
|
|
62
|
-
mechanical change, no new trade-offs).
|
|
88
|
+
mechanical change, no new trade-offs). Record the justification in the
|
|
89
|
+
stage artifact, not in knowledge.jsonl.
|
|
63
90
|
|
|
64
91
|
## Resume protocol
|
|
65
92
|
|
|
@@ -108,9 +135,13 @@ No release shortcuts:
|
|
|
108
135
|
|
|
109
136
|
## 6) Compound, Don't Repeat
|
|
110
137
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
138
|
+
Knowledge is recorded **throughout** the run, not saved for the retro.
|
|
139
|
+
When a reusable lesson appears in design, plan, tdd, or review, append one
|
|
140
|
+
strict-schema JSONL entry to \`.cclaw/knowledge.jsonl\` using
|
|
141
|
+
\`type=rule|pattern|lesson\`. Reserve \`type=compound\` for post-ship retro.
|
|
142
|
+
Repeated lessons (frequency ≥ 3) are lifted into stable
|
|
143
|
+
rules/protocols/skills during the automatic compound pass so the same
|
|
144
|
+
class of mistake gets harder to repeat.
|
|
114
145
|
|
|
115
146
|
## Turn Announce Discipline
|
|
116
147
|
|
|
@@ -15,28 +15,62 @@ export function retroCommandContract() {
|
|
|
15
15
|
|
|
16
16
|
## Purpose
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Auto-triggered retrospective after ship. \`/cc-next\` drafts \`${retroArtifactPath()}\`
|
|
19
|
+
from run artifacts and knowledge, then asks the user exactly ONE structured
|
|
20
|
+
question: **edit / accept / skip**. Default = accept.
|
|
21
|
+
|
|
22
|
+
This command is normally invoked indirectly by \`/cc-next\` when
|
|
23
|
+
\`closeout.shipSubstate === "retro_review"\`. Invoking it directly is still
|
|
24
|
+
supported for manual re-runs.
|
|
19
25
|
|
|
20
26
|
## HARD-GATE
|
|
21
27
|
|
|
22
|
-
- Do not
|
|
23
|
-
|
|
28
|
+
- Do not finalize retro without \`${retroArtifactPath()}\` on disk (or an explicit
|
|
29
|
+
\`retroSkipped: true\` in closeout with a one-line reason).
|
|
30
|
+
- Do not finalize without appending **at least one** \`type=compound\` entry to
|
|
31
|
+
\`${knowledgePath()}\` (skipped runs set \`compoundEntries: 0\` instead).
|
|
32
|
+
- Never advance to compound/archive with \`shipSubstate\` still at
|
|
33
|
+
\`"retro_review"\`.
|
|
34
|
+
|
|
35
|
+
## Inputs
|
|
36
|
+
|
|
37
|
+
\`/cc-ops retro\` (no flags). If the user wants to skip, they answer **skip**
|
|
38
|
+
in the structured ask; there is no \`--skip\` flag.
|
|
24
39
|
|
|
25
40
|
## Algorithm
|
|
26
41
|
|
|
27
|
-
1. Read \`${flowStatePath()}\`; confirm
|
|
28
|
-
2.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
- \`
|
|
34
|
-
-
|
|
35
|
-
4. Update flow-state
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
- \`
|
|
39
|
-
|
|
42
|
+
1. Read \`${flowStatePath()}\`; confirm \`completedStages\` contains \`"ship"\`.
|
|
43
|
+
2. If \`closeout.shipSubstate !== "retro_review"\`, and \`retro.completedAt\`
|
|
44
|
+
is already set, report "retro already complete" and stop.
|
|
45
|
+
3. Draft \`${retroArtifactPath()}\` from available evidence:
|
|
46
|
+
- scan \`.cclaw/artifacts/01..08-*.md\` for decisions, blockers, rewinds,
|
|
47
|
+
- scan \`.cclaw/state/delegation-log.json\` for subagent outcomes,
|
|
48
|
+
- scan \`${knowledgePath()}\` for entries recorded during this run,
|
|
49
|
+
- structure the draft as: Outcomes / Slowed / Accelerated / Repeatable rule.
|
|
50
|
+
4. Update \`closeout.retroDraftedAt = <ISO>\` in flow-state.
|
|
51
|
+
5. Present **one** structured ask (AskUserQuestion on Claude, AskQuestion on
|
|
52
|
+
Cursor, plain-text options elsewhere):
|
|
53
|
+
- \`accept\` (default) — keep the draft as-is,
|
|
54
|
+
- \`edit\` — user edits \`${retroArtifactPath()}\` in-place, then re-runs \`/cc-next\`,
|
|
55
|
+
- \`skip\` — record \`retroSkipped: true\` + one-line reason, no compound entry required.
|
|
56
|
+
6. On **accept**:
|
|
57
|
+
- append >=1 strict-schema JSONL line to \`${knowledgePath()}\` with
|
|
58
|
+
\`type: "compound"\` and \`stage: "retro"\`,
|
|
59
|
+
- set \`retro.required = true\`, \`retro.completedAt = <ISO>\`,
|
|
60
|
+
\`retro.compoundEntries = <count>\`,
|
|
61
|
+
- set \`closeout.retroAcceptedAt = <ISO>\`,
|
|
62
|
+
- set \`closeout.shipSubstate = "compound_review"\`.
|
|
63
|
+
7. On **edit**:
|
|
64
|
+
- leave \`shipSubstate = "retro_review"\`,
|
|
65
|
+
- tell user to edit \`${retroArtifactPath()}\` and run \`/cc-next\` again.
|
|
66
|
+
8. On **skip**:
|
|
67
|
+
- require a one-line reason; if empty, re-ask once then escalate,
|
|
68
|
+
- set \`closeout.retroSkipped = true\`, \`closeout.retroSkipReason = <text>\`,
|
|
69
|
+
\`closeout.retroAcceptedAt = <ISO>\`,
|
|
70
|
+
- set \`retro.completedAt = <ISO>\` (marks gate satisfied for archive), and
|
|
71
|
+
\`retro.compoundEntries = 0\`,
|
|
72
|
+
- set \`closeout.shipSubstate = "compound_review"\`.
|
|
73
|
+
9. Emit a one-line summary: \`retro: accepted|edited|skipped | next: /cc-next\`.
|
|
40
74
|
|
|
41
75
|
## Primary skill
|
|
42
76
|
|
|
@@ -46,33 +80,71 @@ Mandatory retrospective gate before archive once ship is complete.
|
|
|
46
80
|
export function retroCommandSkillMarkdown() {
|
|
47
81
|
return `---
|
|
48
82
|
name: ${RETRO_SKILL_NAME}
|
|
49
|
-
description: "
|
|
83
|
+
description: "Auto-drafted retrospective with a single structured accept/edit/skip ask. Triggered from /cc-next when shipSubstate=retro_review."
|
|
50
84
|
---
|
|
51
85
|
|
|
52
86
|
# /cc-ops retro
|
|
53
87
|
|
|
54
88
|
## HARD-GATE
|
|
55
89
|
|
|
56
|
-
Archive
|
|
90
|
+
Archive stays blocked until one of:
|
|
91
|
+
- retro artifact exists **and** one compound knowledge entry was appended, OR
|
|
92
|
+
- retro was explicitly skipped with a one-line reason recorded in closeout.
|
|
93
|
+
|
|
94
|
+
Do not silently skip. Do not finalize without updating \`flow-state.json\`.
|
|
57
95
|
|
|
58
96
|
## Protocol
|
|
59
97
|
|
|
60
|
-
1. Confirm ship completion
|
|
61
|
-
2.
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
|
|
98
|
+
1. Confirm ship completion by reading \`${flowStatePath()}\`.
|
|
99
|
+
2. If retro draft does not yet exist, synthesise \`${retroArtifactPath()}\` using:
|
|
100
|
+
- all \`.cclaw/artifacts/*-*.md\` from the active run (stages 01–08),
|
|
101
|
+
- \`.cclaw/state/delegation-log.json\` entries,
|
|
102
|
+
- \`${knowledgePath()}\` entries written during this run.
|
|
103
|
+
Draft sections:
|
|
104
|
+
- **Outcomes** — what was actually shipped.
|
|
105
|
+
- **Slowed** — concrete friction points (cite artifact line or delegation id).
|
|
106
|
+
- **Accelerated** — patterns/decisions that worked and are worth keeping.
|
|
107
|
+
- **Repeatable rule** — one candidate rule/pattern for next run.
|
|
108
|
+
Record \`closeout.retroDraftedAt\`.
|
|
109
|
+
3. Ask the user **one** structured question via the harness question tool
|
|
110
|
+
(AskUserQuestion / AskQuestion / plain text fallback):
|
|
111
|
+
|
|
112
|
+
> Retro draft ready at \`${retroArtifactPath()}\`. How do you want to
|
|
113
|
+
> proceed? (default: accept)
|
|
114
|
+
>
|
|
115
|
+
> - **accept** — keep the draft and continue.
|
|
116
|
+
> - **edit** — I'll edit it, then re-run \`/cc-next\`.
|
|
117
|
+
> - **skip** — no retro this run (requires one-line reason).
|
|
118
|
+
|
|
119
|
+
4. Apply the state transition for the chosen option:
|
|
120
|
+
- \`accept\` → append \`{ "type": "compound", "stage": "retro", ... }\` line
|
|
121
|
+
to \`${knowledgePath()}\`; set \`retro.completedAt\`, \`retro.compoundEntries\`,
|
|
122
|
+
\`closeout.retroAcceptedAt\`; set \`closeout.shipSubstate = "compound_review"\`.
|
|
123
|
+
- \`edit\` → leave \`shipSubstate = "retro_review"\`; announce resume path.
|
|
124
|
+
- \`skip\` → set \`closeout.retroSkipped\`, \`closeout.retroSkipReason\`,
|
|
125
|
+
\`closeout.retroAcceptedAt\`, \`retro.completedAt\`,
|
|
126
|
+
\`retro.compoundEntries = 0\`; set \`closeout.shipSubstate = "compound_review"\`.
|
|
127
|
+
|
|
128
|
+
5. Print one-line completion summary:
|
|
129
|
+
- \`retro gate: accepted (<N> compound entries)\`
|
|
130
|
+
- \`retro gate: skipped (reason: <text>)\`
|
|
131
|
+
- \`retro gate: editing (re-run /cc-next when ready)\`
|
|
132
|
+
|
|
133
|
+
## Resume semantics
|
|
134
|
+
|
|
135
|
+
A new session with \`closeout.shipSubstate === "retro_review"\` resumes
|
|
136
|
+
exactly here. If \`closeout.retroDraftedAt\` is present but
|
|
137
|
+
\`retroAcceptedAt\` is missing, re-ask the same structured question without
|
|
138
|
+
regenerating the draft.
|
|
71
139
|
|
|
72
140
|
## Validation
|
|
73
141
|
|
|
74
|
-
- \`${retroArtifactPath()}\` exists and is non-empty
|
|
75
|
-
|
|
76
|
-
- \`
|
|
142
|
+
- \`${retroArtifactPath()}\` exists and is non-empty, **or**
|
|
143
|
+
\`closeout.retroSkipped === true\` with a non-empty reason.
|
|
144
|
+
- When accepted: \`${knowledgePath()}\` gained a valid \`compound\` line
|
|
145
|
+
and \`retro.compoundEntries > 0\`.
|
|
146
|
+
- \`retro.completedAt\` is set.
|
|
147
|
+
- \`closeout.shipSubstate\` is \`"compound_review"\` (or still
|
|
148
|
+
\`"retro_review"\` when user picked \`edit\`).
|
|
77
149
|
`;
|
|
78
150
|
}
|
package/dist/flow-state.d.ts
CHANGED
|
@@ -28,6 +28,35 @@ export interface RetroState {
|
|
|
28
28
|
completedAt?: string;
|
|
29
29
|
compoundEntries: number;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Ship closeout substate machine.
|
|
33
|
+
*
|
|
34
|
+
* After ship completes, cclaw auto-chains retro → compound → archive.
|
|
35
|
+
* Each step is interruptible: `/cc-next` reads `shipSubstate` and resumes
|
|
36
|
+
* from the correct step even across sessions.
|
|
37
|
+
*
|
|
38
|
+
* - `idle` — ship not complete, or closeout not yet started.
|
|
39
|
+
* - `retro_review` — 09-retro.md draft exists; awaiting user edit/accept/skip.
|
|
40
|
+
* - `compound_review` — retro accepted; compound pass awaiting execution
|
|
41
|
+
* (or user skip).
|
|
42
|
+
* - `ready_to_archive` — retro + compound done; archive is the next
|
|
43
|
+
* automatic step.
|
|
44
|
+
* - `archived` — archive completed in this session (transient — archive
|
|
45
|
+
* resets flow-state so this value does not persist between runs).
|
|
46
|
+
*/
|
|
47
|
+
export declare const SHIP_SUBSTATES: readonly ["idle", "retro_review", "compound_review", "ready_to_archive", "archived"];
|
|
48
|
+
export type ShipSubstate = (typeof SHIP_SUBSTATES)[number];
|
|
49
|
+
export interface CloseoutState {
|
|
50
|
+
shipSubstate: ShipSubstate;
|
|
51
|
+
retroDraftedAt?: string;
|
|
52
|
+
retroAcceptedAt?: string;
|
|
53
|
+
retroSkipped?: boolean;
|
|
54
|
+
retroSkipReason?: string;
|
|
55
|
+
compoundCompletedAt?: string;
|
|
56
|
+
compoundSkipped?: boolean;
|
|
57
|
+
compoundPromoted: number;
|
|
58
|
+
}
|
|
59
|
+
export declare function createInitialCloseoutState(): CloseoutState;
|
|
31
60
|
export interface FlowState {
|
|
32
61
|
activeRunId: string;
|
|
33
62
|
currentStage: FlowStage;
|
|
@@ -44,6 +73,8 @@ export interface FlowState {
|
|
|
44
73
|
rewinds: RewindRecord[];
|
|
45
74
|
/** Mandatory retrospective gate status before archive. */
|
|
46
75
|
retro: RetroState;
|
|
76
|
+
/** Ship → retro → compound → archive substate for resumable closeout. */
|
|
77
|
+
closeout: CloseoutState;
|
|
47
78
|
}
|
|
48
79
|
export interface InitialFlowStateOptions {
|
|
49
80
|
activeRunId?: string;
|
package/dist/flow-state.js
CHANGED
|
@@ -2,6 +2,41 @@ import { COMMAND_FILE_ORDER } from "./constants.js";
|
|
|
2
2
|
import { buildTransitionRules, orderedStageSchemas, stageConditionalGateIds, stageGateIds, stageRecommendedGateIds } from "./content/stage-schema.js";
|
|
3
3
|
import { FLOW_STAGES, FLOW_TRACKS, TRACK_STAGES } from "./types.js";
|
|
4
4
|
export const TRANSITION_RULES = buildTransitionRules();
|
|
5
|
+
/**
|
|
6
|
+
* Ship closeout substate machine.
|
|
7
|
+
*
|
|
8
|
+
* After ship completes, cclaw auto-chains retro → compound → archive.
|
|
9
|
+
* Each step is interruptible: `/cc-next` reads `shipSubstate` and resumes
|
|
10
|
+
* from the correct step even across sessions.
|
|
11
|
+
*
|
|
12
|
+
* - `idle` — ship not complete, or closeout not yet started.
|
|
13
|
+
* - `retro_review` — 09-retro.md draft exists; awaiting user edit/accept/skip.
|
|
14
|
+
* - `compound_review` — retro accepted; compound pass awaiting execution
|
|
15
|
+
* (or user skip).
|
|
16
|
+
* - `ready_to_archive` — retro + compound done; archive is the next
|
|
17
|
+
* automatic step.
|
|
18
|
+
* - `archived` — archive completed in this session (transient — archive
|
|
19
|
+
* resets flow-state so this value does not persist between runs).
|
|
20
|
+
*/
|
|
21
|
+
export const SHIP_SUBSTATES = [
|
|
22
|
+
"idle",
|
|
23
|
+
"retro_review",
|
|
24
|
+
"compound_review",
|
|
25
|
+
"ready_to_archive",
|
|
26
|
+
"archived"
|
|
27
|
+
];
|
|
28
|
+
export function createInitialCloseoutState() {
|
|
29
|
+
return {
|
|
30
|
+
shipSubstate: "idle",
|
|
31
|
+
retroDraftedAt: undefined,
|
|
32
|
+
retroAcceptedAt: undefined,
|
|
33
|
+
retroSkipped: undefined,
|
|
34
|
+
retroSkipReason: undefined,
|
|
35
|
+
compoundCompletedAt: undefined,
|
|
36
|
+
compoundSkipped: undefined,
|
|
37
|
+
compoundPromoted: 0
|
|
38
|
+
};
|
|
39
|
+
}
|
|
5
40
|
export function isFlowTrack(value) {
|
|
6
41
|
return typeof value === "string" && FLOW_TRACKS.includes(value);
|
|
7
42
|
}
|
|
@@ -48,7 +83,8 @@ export function createInitialFlowState(activeRunIdOrOptions = "active", maybeTra
|
|
|
48
83
|
required: false,
|
|
49
84
|
completedAt: undefined,
|
|
50
85
|
compoundEntries: 0
|
|
51
|
-
}
|
|
86
|
+
},
|
|
87
|
+
closeout: createInitialCloseoutState()
|
|
52
88
|
};
|
|
53
89
|
}
|
|
54
90
|
export function canTransition(from, to) {
|
package/dist/runs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { COMMAND_FILE_ORDER, RUNTIME_ROOT } from "./constants.js";
|
|
4
|
-
import { canTransition, createInitialFlowState, isFlowTrack, skippedStagesForTrack } from "./flow-state.js";
|
|
4
|
+
import { canTransition, createInitialCloseoutState, createInitialFlowState, isFlowTrack, skippedStagesForTrack, SHIP_SUBSTATES } from "./flow-state.js";
|
|
5
5
|
import { ensureFeatureSystem, readActiveFeature, syncActiveFeatureSnapshot } from "./feature-system.js";
|
|
6
6
|
import { ensureDir, exists, withDirectoryLock, writeFileSafe } from "./fs-utils.js";
|
|
7
7
|
export class InvalidStageTransitionError extends Error {
|
|
@@ -272,6 +272,37 @@ function sanitizeRetroState(value) {
|
|
|
272
272
|
compoundEntries
|
|
273
273
|
};
|
|
274
274
|
}
|
|
275
|
+
function isShipSubstate(value) {
|
|
276
|
+
return typeof value === "string" && SHIP_SUBSTATES.includes(value);
|
|
277
|
+
}
|
|
278
|
+
function sanitizeCloseoutState(value) {
|
|
279
|
+
const fallback = createInitialCloseoutState();
|
|
280
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
281
|
+
return fallback;
|
|
282
|
+
}
|
|
283
|
+
const typed = value;
|
|
284
|
+
const shipSubstate = isShipSubstate(typed.shipSubstate) ? typed.shipSubstate : fallback.shipSubstate;
|
|
285
|
+
const retroDraftedAt = typeof typed.retroDraftedAt === "string" ? typed.retroDraftedAt : undefined;
|
|
286
|
+
const retroAcceptedAt = typeof typed.retroAcceptedAt === "string" ? typed.retroAcceptedAt : undefined;
|
|
287
|
+
const retroSkipped = typeof typed.retroSkipped === "boolean" ? typed.retroSkipped : undefined;
|
|
288
|
+
const retroSkipReason = typeof typed.retroSkipReason === "string" ? typed.retroSkipReason : undefined;
|
|
289
|
+
const compoundCompletedAt = typeof typed.compoundCompletedAt === "string" ? typed.compoundCompletedAt : undefined;
|
|
290
|
+
const compoundSkipped = typeof typed.compoundSkipped === "boolean" ? typed.compoundSkipped : undefined;
|
|
291
|
+
const promotedRaw = typed.compoundPromoted;
|
|
292
|
+
const compoundPromoted = typeof promotedRaw === "number" && Number.isFinite(promotedRaw) && promotedRaw >= 0
|
|
293
|
+
? Math.floor(promotedRaw)
|
|
294
|
+
: 0;
|
|
295
|
+
return {
|
|
296
|
+
shipSubstate,
|
|
297
|
+
retroDraftedAt,
|
|
298
|
+
retroAcceptedAt,
|
|
299
|
+
retroSkipped,
|
|
300
|
+
retroSkipReason,
|
|
301
|
+
compoundCompletedAt,
|
|
302
|
+
compoundSkipped,
|
|
303
|
+
compoundPromoted
|
|
304
|
+
};
|
|
305
|
+
}
|
|
275
306
|
function coerceFlowState(parsed) {
|
|
276
307
|
const track = coerceTrack(parsed.track);
|
|
277
308
|
const next = createInitialFlowState("active", track);
|
|
@@ -289,7 +320,8 @@ function coerceFlowState(parsed) {
|
|
|
289
320
|
skippedStages: sanitizeSkippedStages(parsed.skippedStages, track),
|
|
290
321
|
staleStages: sanitizeStaleStages(parsed.staleStages),
|
|
291
322
|
rewinds: sanitizeRewinds(parsed.rewinds),
|
|
292
|
-
retro: sanitizeRetroState(parsed.retro)
|
|
323
|
+
retro: sanitizeRetroState(parsed.retro),
|
|
324
|
+
closeout: sanitizeCloseoutState(parsed.closeout)
|
|
293
325
|
};
|
|
294
326
|
}
|
|
295
327
|
function toArchiveDate(date = new Date()) {
|
|
@@ -536,9 +568,12 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
|
|
|
536
568
|
if (skipRetro && (!skipRetroReason || skipRetroReason.length === 0)) {
|
|
537
569
|
throw new Error("archive --skip-retro requires --retro-reason=<text>.");
|
|
538
570
|
}
|
|
539
|
-
|
|
571
|
+
const retroSkippedInCloseout = sourceState.closeout.retroSkipped === true &&
|
|
572
|
+
typeof sourceState.closeout.retroSkipReason === "string" &&
|
|
573
|
+
sourceState.closeout.retroSkipReason.trim().length > 0;
|
|
574
|
+
if (retroGate.required && !retroGate.completed && !skipRetro && !retroSkippedInCloseout) {
|
|
540
575
|
throw new Error("Archive blocked: retro gate is required after ship completion. " +
|
|
541
|
-
"Run /cc-
|
|
576
|
+
"Run /cc-next (auto-runs retro) or, for CLI-only flows, re-run `cclaw archive --skip-retro --retro-reason=<text>`.");
|
|
542
577
|
}
|
|
543
578
|
if (retroGate.completed) {
|
|
544
579
|
const completedAt = sourceState.retro.completedAt ?? new Date().toISOString();
|