cc-devflow 4.5.8 → 4.5.10
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/.claude/skills/cc-act/CHANGELOG.md +33 -0
- package/.claude/skills/cc-act/PLAYBOOK.md +9 -4
- package/.claude/skills/cc-act/SKILL.md +73 -12
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +30 -0
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +29 -0
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +103 -0
- package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +61 -5
- package/.claude/skills/cc-act/references/closure-contract.md +4 -1
- package/.claude/skills/cc-act/references/git-commit-guidelines.md +342 -37
- package/.claude/skills/cc-act/scripts/cc-act-common.sh +29 -1
- package/.claude/skills/cc-act/scripts/render-pr-brief.sh +164 -0
- package/.claude/skills/cc-act/scripts/sync-act-docs.sh +1 -1
- package/.claude/skills/cc-check/CHANGELOG.md +17 -0
- package/.claude/skills/cc-check/PLAYBOOK.md +1 -0
- package/.claude/skills/cc-check/SKILL.md +9 -5
- package/.claude/skills/cc-check/references/review-contract.md +7 -0
- package/.claude/skills/cc-check/scripts/render-report-card.js +6 -1
- package/.claude/skills/cc-dev/CHANGELOG.md +5 -0
- package/.claude/skills/cc-dev/SKILL.md +26 -1
- package/.claude/skills/cc-do/CHANGELOG.md +23 -0
- package/.claude/skills/cc-do/PLAYBOOK.md +7 -7
- package/.claude/skills/cc-do/SKILL.md +49 -45
- package/.claude/skills/cc-do/references/execution-recovery.md +18 -13
- package/.claude/skills/cc-do/scripts/build-task-context.sh +13 -22
- package/.claude/skills/cc-do/scripts/mark-task-complete.sh +0 -6
- package/.claude/skills/cc-do/scripts/record-review-decision.sh +4 -5
- package/.claude/skills/cc-do/scripts/recover-workflow.sh +9 -11
- package/.claude/skills/cc-do/scripts/verify-task-gates.sh +12 -10
- package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +7 -29
- package/.claude/skills/cc-investigate/CHANGELOG.md +34 -0
- package/.claude/skills/cc-investigate/PLAYBOOK.md +21 -5
- package/.claude/skills/cc-investigate/SKILL.md +97 -40
- package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +66 -4
- package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +30 -59
- package/.claude/skills/cc-investigate/assets/{ANALYSIS_TEMPLATE.md → legacy/ANALYSIS_TEMPLATE.md} +48 -0
- package/.claude/skills/cc-investigate/references/investigation-contract.md +16 -2
- package/.claude/skills/cc-investigate/scripts/bootstrap-analysis.sh +1 -1
- package/.claude/skills/cc-next/CHANGELOG.md +6 -0
- package/.claude/skills/cc-next/PLAYBOOK.md +26 -4
- package/.claude/skills/cc-next/SKILL.md +39 -4
- package/.claude/skills/cc-plan/CHANGELOG.md +38 -0
- package/.claude/skills/cc-plan/PLAYBOOK.md +60 -53
- package/.claude/skills/cc-plan/SKILL.md +164 -87
- package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +101 -9
- package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +58 -229
- package/.claude/skills/cc-plan/assets/{DESIGN_TEMPLATE.md → legacy/DESIGN_TEMPLATE.md} +68 -0
- package/.claude/skills/cc-plan/assets/{TINY_DESIGN_TEMPLATE.md → legacy/TINY_DESIGN_TEMPLATE.md} +47 -1
- package/.claude/skills/cc-plan/references/planning-contract.md +48 -33
- package/.claude/skills/cc-review/CHANGELOG.md +6 -0
- package/.claude/skills/cc-review/PLAYBOOK.md +9 -11
- package/.claude/skills/cc-review/SKILL.md +37 -61
- package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +1 -1
- package/.claude/skills/cc-review/references/implementation-review-branch.md +5 -5
- package/.claude/skills/cc-review/references/plan-review-branch.md +1 -1
- package/.claude/skills/cc-review/references/review-methods.md +4 -4
- package/.claude/skills/cc-review/scripts/collect-review-context.sh +14 -7
- package/.claude/skills/cc-roadmap/CHANGELOG.md +6 -0
- package/.claude/skills/cc-roadmap/PLAYBOOK.md +30 -0
- package/.claude/skills/cc-roadmap/SKILL.md +45 -8
- package/.claude/skills/cc-roadmap/assets/BACKLOG_TEMPLATE.md +8 -0
- package/.claude/skills/cc-roadmap/assets/ROADMAP_TEMPLATE.md +22 -0
- package/.claude/skills/cc-roadmap/assets/TRACKING_TEMPLATE.json +32 -1
- package/.claude/skills/cc-roadmap/references/roadmap-dialogue.md +14 -14
- package/CHANGELOG.md +28 -0
- package/CONTRIBUTING.md +40 -4
- package/CONTRIBUTING.zh-CN.md +40 -4
- package/README.md +57 -43
- package/README.zh-CN.md +57 -43
- package/bin/cc-devflow-cli.js +293 -36
- package/docs/examples/START-HERE.md +5 -4
- package/docs/examples/example-bindings.json +10 -10
- package/docs/examples/full-design-blocked/BACKLOG.md +1 -1
- package/docs/examples/full-design-blocked/README.md +2 -2
- package/docs/examples/full-design-blocked/ROADMAP.md +1 -1
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +2 -1
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +29 -312
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/tasks.md +11 -8
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +4 -4
- package/docs/examples/full-design-blocked/roadmap.json +1 -1
- package/docs/examples/local-handoff/BACKLOG.md +1 -1
- package/docs/examples/local-handoff/README.md +2 -2
- package/docs/examples/local-handoff/ROADMAP.md +1 -1
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +2 -1
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +27 -210
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/tasks.md +9 -6
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +1 -1
- package/docs/examples/local-handoff/roadmap.json +1 -1
- package/docs/examples/pdca-loop/BACKLOG.md +1 -1
- package/docs/examples/pdca-loop/README.md +2 -2
- package/docs/examples/pdca-loop/ROADMAP.md +1 -1
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +65 -1
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +2 -1
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +26 -228
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/tasks.md +9 -6
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +1 -1
- package/docs/examples/pdca-loop/roadmap.json +1 -1
- package/docs/examples/scripts/check-example-bindings.sh +11 -5
- package/docs/get-shit-done-strategy-audit.md +22 -22
- package/docs/guides/artifact-contract.md +44 -0
- package/docs/guides/getting-started.md +10 -8
- package/docs/guides/getting-started.zh-CN.md +10 -8
- package/docs/guides/minimize-artifacts.md +123 -0
- package/docs/guides/project-postmortem.md +78 -0
- package/lib/compiler/__tests__/skills-registry.test.js +2 -2
- package/lib/skill-runtime/CLAUDE.md +1 -1
- package/lib/skill-runtime/__tests__/autopilot.test.js +42 -6
- package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +165 -0
- package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +2 -2
- package/lib/skill-runtime/__tests__/dispatch.test.js +8 -38
- package/lib/skill-runtime/__tests__/intent.test.js +4 -20
- package/lib/skill-runtime/__tests__/lifecycle.test.js +1 -1
- package/lib/skill-runtime/__tests__/paths.test.js +7 -1
- package/lib/skill-runtime/__tests__/planner.tdd.test.js +63 -2
- package/lib/skill-runtime/__tests__/prepare-pr.test.js +3 -16
- package/lib/skill-runtime/__tests__/query.test.js +388 -7
- package/lib/skill-runtime/__tests__/review-check-integration.test.js +148 -0
- package/lib/skill-runtime/__tests__/review-records.test.js +619 -0
- package/lib/skill-runtime/__tests__/runtime.integration.test.js +64 -23
- package/lib/skill-runtime/__tests__/schemas.test.js +76 -2
- package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +137 -0
- package/lib/skill-runtime/__tests__/task-contract.test.js +783 -0
- package/lib/skill-runtime/__tests__/verify-artifacts.test.js +203 -0
- package/lib/skill-runtime/__tests__/worker-run.test.js +4 -11
- package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +31 -0
- package/lib/skill-runtime/__tests__/workflow-context.test.js +98 -0
- package/lib/skill-runtime/artifacts.js +0 -5
- package/lib/skill-runtime/context-index.js +545 -0
- package/lib/skill-runtime/intent.js +9 -33
- package/lib/skill-runtime/lifecycle.js +1 -1
- package/lib/skill-runtime/operations/CLAUDE.md +2 -2
- package/lib/skill-runtime/operations/dispatch.js +4 -42
- package/lib/skill-runtime/operations/init.js +2 -6
- package/lib/skill-runtime/operations/janitor.js +2 -18
- package/lib/skill-runtime/operations/resume.js +21 -38
- package/lib/skill-runtime/operations/review-records.js +265 -0
- package/lib/skill-runtime/operations/snapshot.js +1 -1
- package/lib/skill-runtime/operations/task-contract.js +524 -0
- package/lib/skill-runtime/operations/worker-run.js +2 -30
- package/lib/skill-runtime/paths.js +4 -4
- package/lib/skill-runtime/planner.js +25 -13
- package/lib/skill-runtime/query-registry.js +2 -2
- package/lib/skill-runtime/query.js +16 -3
- package/lib/skill-runtime/review-records.js +123 -0
- package/lib/skill-runtime/review.js +246 -11
- package/lib/skill-runtime/schemas.js +179 -15
- package/lib/skill-runtime/store.js +0 -10
- package/lib/skill-runtime/task-contract.js +187 -0
- package/lib/skill-runtime/workflow-context.js +748 -0
- package/package.json +7 -4
|
@@ -11,10 +11,10 @@ CC-DevFlow 现在有两条入口:
|
|
|
11
11
|
- `cc-devflow init`:把整包 `.claude` 安装到你的项目里
|
|
12
12
|
- `cc-devflow adapt`:生成 Codex、Cursor、Qwen、Antigravity 等平台产物
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
核心工作流可以手动走 PDCA/IDCA Skill,也可以通过 PR harness Skill 自动推进:
|
|
15
15
|
|
|
16
16
|
```text
|
|
17
|
-
cc-roadmap
|
|
17
|
+
cc-roadmap -> cc-next -> cc-dev
|
|
18
18
|
|
|
19
19
|
PDCA: cc-plan -> [cc-review] -> cc-do -> [cc-review] -> cc-check -> cc-act
|
|
20
20
|
IDCA: cc-investigate -> [cc-review] -> cc-do -> [cc-review] -> cc-check -> cc-act
|
|
@@ -36,7 +36,7 @@ IDCA: cc-investigate -> [cc-review] -> cc-do -> [cc-review] -> cc-check -> cc-ac
|
|
|
36
36
|
npx cc-devflow init --dir /path/to/your/project
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
整包安装会带上
|
|
39
|
+
整包安装会带上 roadmap、next-work selection、autonomous dev、手动 PDCA/IDCA、可选 `cc-review`、PR review/landing,以及维护用的 `cc-spec-init` 和 `cc-simplify`。
|
|
40
40
|
|
|
41
41
|
### 单个 Skill 安装
|
|
42
42
|
|
|
@@ -87,9 +87,9 @@ find .codex/skills -mindepth 2 -maxdepth 2 -name SKILL.md | sort
|
|
|
87
87
|
|
|
88
88
|
- `cc-roadmap` 产出可编辑真相 `devflow/roadmap.json`,再生成 `devflow/ROADMAP.md` 和 deprecated `devflow/BACKLOG.md`
|
|
89
89
|
- `cc-spec-init` 产出 `devflow/specs/INDEX.md`、capability spec 和 `change-meta.json`
|
|
90
|
-
- `cc-plan` 产出 `planning/
|
|
91
|
-
- `cc-investigate` 产出 `planning/
|
|
92
|
-
- `cc-review` 产出 `
|
|
90
|
+
- `cc-plan` 产出 `planning/tasks.md#Contract Summary`、CLI 生成的 `task-manifest.json` 和 `change-meta.json`
|
|
91
|
+
- `cc-investigate` 产出 `planning/tasks.md#Root Cause Contract`、CLI 生成的 `task-manifest.json` 和 `change-meta.json`
|
|
92
|
+
- `cc-review` 产出 `review-ledger.jsonl`、可选 `review-findings.json`,Markdown 报告只在需要时按需渲染
|
|
93
93
|
- `cc-check` 产出 `report-card.json`
|
|
94
94
|
- `cc-act` 只产出一个最终 handoff 文件:`handoff/pr-brief.md`、`handoff/resume-index.md` 或 `handoff/release-note.md`
|
|
95
95
|
|
|
@@ -97,7 +97,8 @@ durable truth 分两层:
|
|
|
97
97
|
|
|
98
98
|
- `devflow/specs/`:capability 真相,保留 `INDEX.md` 与 `capabilities/*.md`
|
|
99
99
|
- 新 change 目录必须命名为 `REQ-<number>-<description>`(需求)或 `FIX-<number>-<description>`(修复);`REQ` 和 `FIX` 分别维护自己的递增编号,跨前缀同号不是冲突;并行工作树造成重复编号时,完整 change key 的描述负责区分业务内容,旧小写目录只作为历史兼容读取。
|
|
100
|
-
- `devflow/changes/<change>/`:变更真相,保留 `change-
|
|
100
|
+
- `devflow/changes/<change>/`:变更真相,保留 `change-meta.json`、`planning/tasks.md`、CLI 生成的 `task-manifest.json`、review ledger / findings 记录、debug / failed 的可选 CLI 日志、`report-card.json` 和唯一的最终 handoff 文件。不要生成任务级 `context.md`、`checkpoint.json` 或 AI 手写过程文件。
|
|
101
|
+
- 历史 `planning/design.md`、`planning/analysis.md` 和 `cc-review-*.md` 是旧 change 的可读 fallback,不再是新默认写入。
|
|
101
102
|
- worker prompt、journal、assignment、session log 统一放到 `devflow/workspaces/<change>/`,作为 ephemeral scratch。
|
|
102
103
|
|
|
103
104
|
进入实现前,planning handoff 应该先把证据写实:
|
|
@@ -153,7 +154,7 @@ npx cc-devflow adapt --cwd /path/to/your/project --platform codex
|
|
|
153
154
|
|
|
154
155
|
如果你的项目没有可选的 `.claude/commands/` 输入目录,这也是正常的;编译器仍然会生成 skills registry,并为 Codex 镜像正式分发 skill 集合。
|
|
155
156
|
|
|
156
|
-
Codex 现在会把正式分发的 skill 从 `.claude/skills/<skill>/` 镜像到 `.codex/skills/<skill
|
|
157
|
+
Codex 现在会把正式分发的 skill 从 `.claude/skills/<skill>/` 镜像到 `.codex/skills/<skill>/`。这套集合包含公开 workflow skill 和维护类 skill `cc-spec-init`、`cc-simplify`,并且镜像是纯增量的:项目里已有的自定义 Codex skill 不会被删除。
|
|
157
158
|
|
|
158
159
|
### 保持 skill 和样例同步
|
|
159
160
|
|
|
@@ -172,6 +173,7 @@ npm run verify:publish
|
|
|
172
173
|
- [CLI 与 Skill](../commands/README.zh-CN.md)
|
|
173
174
|
- [工作流详解](./workflow-guide.md)
|
|
174
175
|
- [最佳实践](./best-practices.md)
|
|
176
|
+
- [最小 Artifact 合同](./minimize-artifacts.md)
|
|
175
177
|
- [样例入口页](../examples/START-HERE.md)
|
|
176
178
|
- [简版样例列表](../examples/README.md)
|
|
177
179
|
- [项目 README](../../README.zh-CN.md)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Minimized Workflow Artifacts
|
|
2
|
+
|
|
3
|
+
This guide describes the default artifact contract for new cc-devflow changes.
|
|
4
|
+
The goal is simple: keep durable workflow truth readable, small, and measurable.
|
|
5
|
+
|
|
6
|
+
## Default Shape
|
|
7
|
+
|
|
8
|
+
Each new change keeps durable truth under `devflow/changes/<change-key>/`.
|
|
9
|
+
|
|
10
|
+
Default human-authored Markdown:
|
|
11
|
+
|
|
12
|
+
- `planning/tasks.md`
|
|
13
|
+
|
|
14
|
+
Default machine-owned records:
|
|
15
|
+
|
|
16
|
+
- `change-meta.json`
|
|
17
|
+
- `planning/task-manifest.json`
|
|
18
|
+
- `review/review-ledger.jsonl`
|
|
19
|
+
- `review/review-findings.json` when findings exist
|
|
20
|
+
- `execution/tasks/<task-id>/checkpoint.json`
|
|
21
|
+
- `review/report-card.json`
|
|
22
|
+
- one final handoff file under `handoff/`
|
|
23
|
+
|
|
24
|
+
Runtime scratch, worker prompts, journals, assignments, and session logs belong
|
|
25
|
+
under `devflow/workspaces/<change-key>/`, not beside durable change truth.
|
|
26
|
+
|
|
27
|
+
## Feature Plans
|
|
28
|
+
|
|
29
|
+
Feature and scope changes use:
|
|
30
|
+
|
|
31
|
+
- `planning/tasks.md#Contract Summary`
|
|
32
|
+
- `planning/task-manifest.json`
|
|
33
|
+
- `change-meta.json`
|
|
34
|
+
|
|
35
|
+
`Contract Summary` owns the frozen human-readable plan: user story, non-negotiable
|
|
36
|
+
constraints, decisions that must not be reopened, task slices, and verification
|
|
37
|
+
expectations. The task manifest is generated or validated by CLI tooling and owns
|
|
38
|
+
machine-readable task status.
|
|
39
|
+
|
|
40
|
+
## Bug Investigations
|
|
41
|
+
|
|
42
|
+
Bug, regression, and unexpected-behavior work uses:
|
|
43
|
+
|
|
44
|
+
- `planning/tasks.md#Root Cause Contract`
|
|
45
|
+
- `planning/task-manifest.json`
|
|
46
|
+
- `change-meta.json`
|
|
47
|
+
|
|
48
|
+
`Root Cause Contract` owns the symptom, reproduction evidence, confirmed cause,
|
|
49
|
+
rejected near-causes, repair boundary, and task handoff. `cc-do` should implement
|
|
50
|
+
from that frozen contract instead of reopening investigation during execution.
|
|
51
|
+
|
|
52
|
+
## Review Records
|
|
53
|
+
|
|
54
|
+
`cc-review` writes structured lifecycle events first:
|
|
55
|
+
|
|
56
|
+
- `review/review-ledger.jsonl`
|
|
57
|
+
- optional `review/review-findings.json`
|
|
58
|
+
- optional rendered Markdown from `cc-devflow review render`
|
|
59
|
+
|
|
60
|
+
Markdown review reports are for human reading when needed. They are not the
|
|
61
|
+
default durable review source.
|
|
62
|
+
|
|
63
|
+
Useful commands:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx cc-devflow review start --change REQ-001 --change-key REQ-001-copy-invite-link --base-sha abc123 --head-sha def456
|
|
67
|
+
npx cc-devflow review record-node --change REQ-001 --change-key REQ-001-copy-invite-link --review-id <review-id> --node-id R001 --target planning/tasks.md --status checked --coverage contract --evidence-ref "cmd:npm run verify"
|
|
68
|
+
npx cc-devflow review add-finding --change REQ-001 --change-key REQ-001-copy-invite-link --review-id <review-id> --finding-id F001 --severity important --confidence 8 --display-tier blocking --fingerprint sha256:<hash> --scope "current change" --path planning/tasks.md --evidence "finding evidence" --recommendation "repair action" --route cc-do
|
|
69
|
+
npx cc-devflow review close --change REQ-001 --change-key REQ-001-copy-invite-link --review-id <review-id> --status clean --blocking-count 0 --warning-count 0 --next cc-check
|
|
70
|
+
npx cc-devflow review render --change REQ-001 --change-key REQ-001-copy-invite-link --review-id <review-id> --output review/review-report.md
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Legacy Fallback
|
|
74
|
+
|
|
75
|
+
Older changes may still contain:
|
|
76
|
+
|
|
77
|
+
- `planning/design.md`
|
|
78
|
+
- `planning/analysis.md`
|
|
79
|
+
- `review/cc-review-plan.md`
|
|
80
|
+
- `review/cc-review-report.md`
|
|
81
|
+
- `review/cc-review-agent-results.jsonl`
|
|
82
|
+
|
|
83
|
+
Those files remain readable compatibility inputs. New changes should not write
|
|
84
|
+
them by default. When migrating old work, fold feature-plan truth into
|
|
85
|
+
`planning/tasks.md#Contract Summary` and bug-investigation truth into
|
|
86
|
+
`planning/tasks.md#Root Cause Contract`.
|
|
87
|
+
|
|
88
|
+
## Validation Gates
|
|
89
|
+
|
|
90
|
+
Validate one change:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npx cc-devflow task-contract validate --change REQ-001 --change-key REQ-001-copy-invite-link
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Validate the repository artifact contract:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm run verify:artifacts
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Measure the contract:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm run benchmark:artifacts
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The package-level verification command also includes artifact validation:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npm run verify
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Authoring Rule
|
|
115
|
+
|
|
116
|
+
Before adding a durable file under `devflow/changes/<change-key>/`, answer:
|
|
117
|
+
|
|
118
|
+
1. Which downstream skill reads it by default?
|
|
119
|
+
2. Which state does it own that no existing artifact owns?
|
|
120
|
+
3. Which command fails if it drifts?
|
|
121
|
+
|
|
122
|
+
If those answers are unclear, keep the information in `planning/tasks.md`, a
|
|
123
|
+
machine record, or ephemeral workspace scratch instead.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Project Postmortem Contract
|
|
2
|
+
|
|
3
|
+
cc-devflow treats project postmortems as a durable AI memory surface. They are not
|
|
4
|
+
chat summaries. They are repo-owned evidence that future agents can search before
|
|
5
|
+
planning, investigating, or executing work.
|
|
6
|
+
|
|
7
|
+
## Storage Layout
|
|
8
|
+
|
|
9
|
+
Project-level postmortems live under `devflow/postmortems/`:
|
|
10
|
+
|
|
11
|
+
| Path | Owner | Purpose |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| `INDEX.md` | `cc-act` | Progressive entry point, latest incidents, tags, and search hints |
|
|
14
|
+
| `principles.md` | `cc-act` | Generalized lessons about recurring model, process, and engineering mistakes |
|
|
15
|
+
| `incidents/<date>-<change-key>.md` | `cc-act` | Immutable-ish factual record for one incident, bug, or repeated AI failure |
|
|
16
|
+
|
|
17
|
+
`cc-act` owns writes because it has verified closeout, Git state, review state, and
|
|
18
|
+
ship facts. Earlier skills only read and project the relevant reminders into their
|
|
19
|
+
own artifacts.
|
|
20
|
+
|
|
21
|
+
## Progressive Disclosure
|
|
22
|
+
|
|
23
|
+
- Default layer: `INDEX.md` gives tags, one-line lessons, severity, affected
|
|
24
|
+
surfaces, and links to deeper incident files.
|
|
25
|
+
- Principle layer: `principles.md` gives reusable rules such as model failure
|
|
26
|
+
modes, domain-specific judgment traps, and required countermeasures.
|
|
27
|
+
- Incident layer: `incidents/*.md` gives the detailed facts, Git evidence,
|
|
28
|
+
timeline, root cause, detection gap, repair, follow-ups, and search terms.
|
|
29
|
+
|
|
30
|
+
Agents should start with keyword search over the default and principle layers, then
|
|
31
|
+
open incident files only when the tags or failure class match the current task.
|
|
32
|
+
|
|
33
|
+
## Required Incident Evidence
|
|
34
|
+
|
|
35
|
+
Every incident file should include:
|
|
36
|
+
|
|
37
|
+
- Symptom and impact.
|
|
38
|
+
- Trigger and timeline.
|
|
39
|
+
- Confirmed root cause and rejected near-causes.
|
|
40
|
+
- Why the failure escaped planning, investigation, execution, review, or ship.
|
|
41
|
+
- Git evidence: branch, base, head SHA, PR if any, relevant commits, review range,
|
|
42
|
+
and dirty-tree notes when they matter.
|
|
43
|
+
- Verification evidence: commands, exit status, key output, and artifact paths.
|
|
44
|
+
- Follow-up actions: root-cause fixes, detection improvements, and backlog items.
|
|
45
|
+
- AI failure mode: model limitation, pattern-matching trap, missing evidence habit,
|
|
46
|
+
over-broad abstraction, fake compatibility, test-seam mistake, or other reusable
|
|
47
|
+
class.
|
|
48
|
+
- Search terms future agents should use before repeating similar work.
|
|
49
|
+
|
|
50
|
+
## Redaction Guard
|
|
51
|
+
|
|
52
|
+
Postmortems are durable repo artifacts, so they must never preserve secrets,
|
|
53
|
+
tokens, private customer data, personal machine paths, or raw private logs unless
|
|
54
|
+
the repository already treats that exact artifact as public source truth.
|
|
55
|
+
|
|
56
|
+
- Record the command, file path, commit, or artifact pointer that proves the fact.
|
|
57
|
+
- Quote only the minimal output needed to prove the incident.
|
|
58
|
+
- Replace sensitive values with `<redacted>` and add a short redaction summary.
|
|
59
|
+
- If the only available proof is sensitive, cite the owner artifact and describe
|
|
60
|
+
the observed shape instead of copying the raw value.
|
|
61
|
+
|
|
62
|
+
## Read Gates
|
|
63
|
+
|
|
64
|
+
`cc-plan`, `cc-investigate`, and `cc-do` must run a quick local search before they
|
|
65
|
+
freeze direction or touch code:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
rg -n "<capability|module|error|failure-class|model-risk>" devflow/postmortems
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If `devflow/postmortems/` does not exist, record `no-project-postmortems-yet`.
|
|
72
|
+
If a match exists, load only `INDEX.md`, `principles.md`, and the one or two
|
|
73
|
+
incident files most relevant to the current work.
|
|
74
|
+
|
|
75
|
+
## State Ownership
|
|
76
|
+
|
|
77
|
+
Postmortems do not own task status, roadmap progress, review verdicts, or spec sync
|
|
78
|
+
state. They cite those stronger owners by path, commit, or command output.
|
|
@@ -159,9 +159,9 @@ describe('Skills Registry Generator', () => {
|
|
|
159
159
|
expect(execute.writes).toEqual(
|
|
160
160
|
expect.arrayContaining([
|
|
161
161
|
expect.objectContaining({
|
|
162
|
-
path: 'devflow/changes/<change-key>/execution/tasks/<task-id>/
|
|
162
|
+
path: 'devflow/changes/<change-key>/execution/tasks/<task-id>/events.jsonl',
|
|
163
163
|
durability: 'durable',
|
|
164
|
-
required:
|
|
164
|
+
required: false
|
|
165
165
|
})
|
|
166
166
|
])
|
|
167
167
|
);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
职责分组
|
|
5
5
|
入口层: `cli.js` 负责命令分发,`index.js` 提供给测试和内部脚本的稳定聚合入口。
|
|
6
6
|
基础层: `schemas.js`、`store.js`、`paths.js` 管住契约、持久化与路径规则,避免执行层重复造轮子。
|
|
7
|
-
状态层: `artifacts.js`、`lifecycle.js`、`query.js`、`review.js`、`team-state.js` 维护运行时真相源与只读查询。
|
|
7
|
+
状态层: `artifacts.js`、`lifecycle.js`、`query.js`、`workflow-context.js`、`review.js`、`team-state.js` 维护运行时真相源与只读查询。
|
|
8
8
|
规划与交接: `planner.js`、`intent.js`、`delegation.js` 把任务解析、handoff 生成和 team/workspace 委派收口成统一语义。
|
|
9
9
|
阶段操作: `operations/` 是唯一 stage 入口目录;具体阶段边界见 `operations/CLAUDE.md`。
|
|
10
10
|
测试布局: `__tests__/` 紧贴模块放置单元、回归与集成测试;顶层 `test/` 不再承载 `skill-runtime` 私有测试。
|
|
@@ -9,13 +9,13 @@ const {
|
|
|
9
9
|
getTaskManifestPath,
|
|
10
10
|
getReportCardPath,
|
|
11
11
|
getReleaseNotePath,
|
|
12
|
-
getRuntimeStatePath
|
|
13
|
-
getCheckpointPath
|
|
12
|
+
getRuntimeStatePath
|
|
14
13
|
} = require('../store');
|
|
15
14
|
const {
|
|
16
15
|
getIntentResumeIndexPath,
|
|
17
16
|
getIntentPrBriefPath
|
|
18
17
|
} = require('../artifacts');
|
|
18
|
+
const { getChangePaths } = require('../paths');
|
|
19
19
|
|
|
20
20
|
jest.setTimeout(20000);
|
|
21
21
|
|
|
@@ -41,6 +41,41 @@ function markManifestReviewsPassed(repoRoot, changeId) {
|
|
|
41
41
|
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
function writeCleanReviewLedger(repoRoot, changeId) {
|
|
45
|
+
const change = getChangePaths(repoRoot, changeId);
|
|
46
|
+
const ledgerPath = path.join(change.reviewDir, 'review-ledger.jsonl');
|
|
47
|
+
fs.mkdirSync(path.dirname(ledgerPath), { recursive: true });
|
|
48
|
+
fs.writeFileSync(ledgerPath, [
|
|
49
|
+
JSON.stringify({
|
|
50
|
+
schema: 'review-ledger.v2',
|
|
51
|
+
change: change.changeKey,
|
|
52
|
+
reviewId: 'RVW-20260512-001',
|
|
53
|
+
createdAt: '2026-05-12T00:00:00.000Z',
|
|
54
|
+
createdBy: 'cc-devflow-cli',
|
|
55
|
+
event: 'review-started',
|
|
56
|
+
mode: 'implementation',
|
|
57
|
+
scope: 'current-diff',
|
|
58
|
+
baseSha: 'abc123',
|
|
59
|
+
headSha: 'def456',
|
|
60
|
+
selectedNodes: [],
|
|
61
|
+
skippedNodes: [],
|
|
62
|
+
riskLanes: []
|
|
63
|
+
}),
|
|
64
|
+
JSON.stringify({
|
|
65
|
+
schema: 'review-ledger.v2',
|
|
66
|
+
change: change.changeKey,
|
|
67
|
+
reviewId: 'RVW-20260512-001',
|
|
68
|
+
createdAt: '2026-05-12T00:01:00.000Z',
|
|
69
|
+
createdBy: 'cc-devflow-cli',
|
|
70
|
+
event: 'review-closed',
|
|
71
|
+
status: 'clean',
|
|
72
|
+
blockingCount: 0,
|
|
73
|
+
warningCount: 0,
|
|
74
|
+
next: 'cc-check'
|
|
75
|
+
})
|
|
76
|
+
].join('\n'));
|
|
77
|
+
}
|
|
78
|
+
|
|
44
79
|
describe('runAutopilot', () => {
|
|
45
80
|
test('stops at the approval gate after planning without writing approval-phase handoff markdown', async () => {
|
|
46
81
|
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-autopilot-'));
|
|
@@ -81,7 +116,7 @@ describe('runAutopilot', () => {
|
|
|
81
116
|
expect(fs.existsSync(getIntentResumeIndexPath(repoRoot, 'REQ-123'))).toBe(false);
|
|
82
117
|
});
|
|
83
118
|
|
|
84
|
-
test('resumes after approval, executes delegated work, and prepares a PR from
|
|
119
|
+
test('resumes after approval, executes delegated work, and prepares a PR from task state', async () => {
|
|
85
120
|
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-autopilot-workers-'));
|
|
86
121
|
|
|
87
122
|
writeJson(path.join(repoRoot, 'package.json'), {
|
|
@@ -123,17 +158,16 @@ describe('runAutopilot', () => {
|
|
|
123
158
|
});
|
|
124
159
|
|
|
125
160
|
const manifest = JSON.parse(fs.readFileSync(getTaskManifestPath(repoRoot, 'REQ-123'), 'utf8'));
|
|
126
|
-
const delegatedCheckpoint = JSON.parse(fs.readFileSync(getCheckpointPath(repoRoot, 'REQ-123', 'T002'), 'utf8'));
|
|
127
161
|
const report = JSON.parse(fs.readFileSync(getReportCardPath(repoRoot, 'REQ-123'), 'utf8'));
|
|
128
162
|
|
|
129
163
|
expect(firstRun.executed).toEqual(expect.arrayContaining(['delegate', 'worker-run', 'dispatch', 'verify']));
|
|
130
164
|
expect(firstRun.currentStage).toBe('verify');
|
|
131
165
|
expect(manifest.tasks.find((task) => task.id === 'T002').status).toBe('passed');
|
|
132
|
-
expect(delegatedCheckpoint.outputExcerpt).toContain('delegate-ok');
|
|
133
166
|
expect(report.review.status).toBe('blocked');
|
|
134
167
|
expect(fs.existsSync(getIntentPrBriefPath(repoRoot, 'REQ-123'))).toBe(false);
|
|
135
168
|
|
|
136
169
|
markManifestReviewsPassed(repoRoot, 'REQ-123');
|
|
170
|
+
writeCleanReviewLedger(repoRoot, 'REQ-123');
|
|
137
171
|
|
|
138
172
|
const secondRun = await runAutopilot({
|
|
139
173
|
repoRoot,
|
|
@@ -146,7 +180,8 @@ describe('runAutopilot', () => {
|
|
|
146
180
|
|
|
147
181
|
expect(secondRun.executed).toEqual(expect.arrayContaining(['verify', 'prepare-pr']));
|
|
148
182
|
expect(secondRun.currentStage).toBe('prepare-pr');
|
|
149
|
-
expect(prBrief).toContain('
|
|
183
|
+
expect(prBrief).toContain('planning/task-manifest.json');
|
|
184
|
+
expect(prBrief).not.toContain('checkpoint.json');
|
|
150
185
|
});
|
|
151
186
|
|
|
152
187
|
test('runs release after prepare-pr when requested for an approved plan', async () => {
|
|
@@ -195,6 +230,7 @@ describe('runAutopilot', () => {
|
|
|
195
230
|
expect(fs.existsSync(getReleaseNotePath(repoRoot, 'REQ-123'))).toBe(false);
|
|
196
231
|
|
|
197
232
|
markManifestReviewsPassed(repoRoot, 'REQ-123');
|
|
233
|
+
writeCleanReviewLedger(repoRoot, 'REQ-123');
|
|
198
234
|
|
|
199
235
|
const result = await runAutopilot({
|
|
200
236
|
repoRoot,
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [INPUT]: 依赖 scripts/benchmark-artifacts.js 导出的 runBenchmarkArtifacts 和临时 artifact fixture。
|
|
3
|
+
* [OUTPUT]: 验证 benchmark:artifacts 使用 ceil(len/4) 估算并报告 profile 阈值 savings。
|
|
4
|
+
* [POS]: REQ-003-minimize-workflow-artifacts T017 的 Red/Green 证据。
|
|
5
|
+
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { spawnSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
const { runBenchmarkArtifacts } = require('../../../scripts/benchmark-artifacts');
|
|
14
|
+
|
|
15
|
+
const REPO_ROOT = path.resolve(__dirname, '../../..');
|
|
16
|
+
const BENCHMARK_SCRIPT = path.join(REPO_ROOT, 'scripts', 'benchmark-artifacts.js');
|
|
17
|
+
|
|
18
|
+
function writeText(filePath, text) {
|
|
19
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
20
|
+
fs.writeFileSync(filePath, text);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function writeJson(filePath, value) {
|
|
24
|
+
writeText(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function contractTasks({ changeKey, profile = 'standard', filler = '' }) {
|
|
28
|
+
return [
|
|
29
|
+
'# Tasks',
|
|
30
|
+
'',
|
|
31
|
+
'## Contract Summary',
|
|
32
|
+
'',
|
|
33
|
+
`Change: ${changeKey}`,
|
|
34
|
+
'Mode: plan',
|
|
35
|
+
`Profile: ${profile}`,
|
|
36
|
+
'Approval: approved',
|
|
37
|
+
'',
|
|
38
|
+
'Goal:',
|
|
39
|
+
'- Minimize workflow artifacts.',
|
|
40
|
+
'',
|
|
41
|
+
'Do Not Do:',
|
|
42
|
+
'- Do not change token estimator math.',
|
|
43
|
+
'',
|
|
44
|
+
'Approved Direction:',
|
|
45
|
+
'- Use tasks.md plus generated JSON records.',
|
|
46
|
+
'',
|
|
47
|
+
'Acceptance:',
|
|
48
|
+
'- Benchmark savings stay above threshold.',
|
|
49
|
+
'',
|
|
50
|
+
'Verification:',
|
|
51
|
+
'',
|
|
52
|
+
'```bash',
|
|
53
|
+
'npm run benchmark:artifacts',
|
|
54
|
+
'```',
|
|
55
|
+
'',
|
|
56
|
+
'Risk / Escalate If:',
|
|
57
|
+
'- Savings fall below profile threshold.',
|
|
58
|
+
'',
|
|
59
|
+
filler,
|
|
60
|
+
'## Phase 1',
|
|
61
|
+
'',
|
|
62
|
+
'- [ ] T001 benchmark minimized artifact surface',
|
|
63
|
+
' Vertical slice: Slice 1',
|
|
64
|
+
''
|
|
65
|
+
].join('\n');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function seedLegacyBaseline(repoRoot, changeKey, size = 6000) {
|
|
69
|
+
const changeDir = path.join(repoRoot, 'devflow', 'changes', changeKey);
|
|
70
|
+
writeText(path.join(changeDir, 'planning', 'design.md'), `# Design\n\n${'d'.repeat(size)}\n`);
|
|
71
|
+
writeText(path.join(changeDir, 'planning', 'analysis.md'), `# Analysis\n\n${'a'.repeat(size / 2)}\n`);
|
|
72
|
+
writeText(path.join(changeDir, 'planning', 'tasks.md'), `# Tasks\n\n${'t'.repeat(size / 2)}\n`);
|
|
73
|
+
writeJson(path.join(changeDir, 'planning', 'task-manifest.json'), { changeId: changeKey, tasks: [] });
|
|
74
|
+
writeJson(path.join(changeDir, 'change-meta.json'), { changeId: changeKey, goal: ['legacy'] });
|
|
75
|
+
writeJson(path.join(changeDir, 'review', 'report-card.json'), { overall: 'pass' });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function seedMinimizedChange(repoRoot, changeKey, options = {}) {
|
|
79
|
+
const changeDir = path.join(repoRoot, 'devflow', 'changes', changeKey);
|
|
80
|
+
writeText(path.join(changeDir, 'planning', 'tasks.md'), contractTasks({ changeKey, ...options }));
|
|
81
|
+
writeJson(path.join(changeDir, 'planning', 'task-manifest.json'), {
|
|
82
|
+
changeId: changeKey,
|
|
83
|
+
metadata: { source: 'tasks.md', generatedBy: 'cc-devflow task-contract', planVersion: 1 },
|
|
84
|
+
tasks: []
|
|
85
|
+
});
|
|
86
|
+
writeJson(path.join(changeDir, 'change-meta.json'), {
|
|
87
|
+
changeId: changeKey,
|
|
88
|
+
_meta: { generatedBy: 'cc-devflow task-contract' }
|
|
89
|
+
});
|
|
90
|
+
writeJson(path.join(changeDir, 'review', 'review-ledger.jsonl'), { note: 'counted as text by benchmark' });
|
|
91
|
+
writeJson(path.join(changeDir, 'review', 'review-findings.json'), { findings: [] });
|
|
92
|
+
writeJson(path.join(changeDir, 'review', 'report-card.json'), { overall: 'pass' });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
describe('benchmark:artifacts', () => {
|
|
96
|
+
let repoRoot;
|
|
97
|
+
|
|
98
|
+
beforeEach(() => {
|
|
99
|
+
repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-benchmark-artifacts-'));
|
|
100
|
+
seedLegacyBaseline(repoRoot, 'REQ-001-legacy-baseline');
|
|
101
|
+
seedLegacyBaseline(repoRoot, 'REQ-002-legacy-baseline');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
afterEach(() => {
|
|
105
|
+
fs.rmSync(repoRoot, { recursive: true, force: true });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('reports standard savings >= 30% for REQ-003-example', () => {
|
|
109
|
+
seedMinimizedChange(repoRoot, 'REQ-003-example', { profile: 'standard' });
|
|
110
|
+
|
|
111
|
+
const result = runBenchmarkArtifacts(repoRoot);
|
|
112
|
+
const row = result.rows.find((item) => item.changeKey === 'REQ-003-example');
|
|
113
|
+
|
|
114
|
+
expect(result.code).toBe(0);
|
|
115
|
+
expect(row).toMatchObject({
|
|
116
|
+
profile: 'standard',
|
|
117
|
+
threshold_pct: 30,
|
|
118
|
+
correctness_pass: true
|
|
119
|
+
});
|
|
120
|
+
expect(row.savings_vs_baseline_pct).toBeGreaterThanOrEqual(30);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('reports tiny savings >= 60% for tiny fixture', () => {
|
|
124
|
+
seedMinimizedChange(repoRoot, 'REQ-004-tiny-example', { profile: 'tiny' });
|
|
125
|
+
|
|
126
|
+
const result = runBenchmarkArtifacts(repoRoot);
|
|
127
|
+
const row = result.rows.find((item) => item.changeKey === 'REQ-004-tiny-example');
|
|
128
|
+
|
|
129
|
+
expect(result.code).toBe(0);
|
|
130
|
+
expect(row).toMatchObject({
|
|
131
|
+
profile: 'tiny',
|
|
132
|
+
threshold_pct: 60,
|
|
133
|
+
correctness_pass: true
|
|
134
|
+
});
|
|
135
|
+
expect(row.savings_vs_baseline_pct).toBeGreaterThanOrEqual(60);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('exits 1 when savings are below the profile threshold', () => {
|
|
139
|
+
seedMinimizedChange(repoRoot, 'REQ-005-bloated-example', {
|
|
140
|
+
profile: 'standard',
|
|
141
|
+
filler: 'x'.repeat(20000)
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const result = runBenchmarkArtifacts(repoRoot);
|
|
145
|
+
|
|
146
|
+
expect(result.code).toBe(1);
|
|
147
|
+
expect(result.rows[0]).toMatchObject({ correctness_pass: false });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('CLI prints stdout JSON array', () => {
|
|
151
|
+
seedMinimizedChange(repoRoot, 'REQ-003-example', { profile: 'standard' });
|
|
152
|
+
|
|
153
|
+
const result = spawnSync(process.execPath, [BENCHMARK_SCRIPT, repoRoot], { encoding: 'utf8' });
|
|
154
|
+
const rows = JSON.parse(result.stdout);
|
|
155
|
+
|
|
156
|
+
expect(result.status).toBe(0);
|
|
157
|
+
expect(Array.isArray(rows)).toBe(true);
|
|
158
|
+
expect(rows[0]).toHaveProperty('savings_vs_baseline_pct');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('package.json exposes npm run benchmark:artifacts', () => {
|
|
162
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf8'));
|
|
163
|
+
expect(pkg.scripts['benchmark:artifacts']).toBe('node scripts/benchmark-artifacts.js');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -217,9 +217,9 @@ describe('cc-devflow cli distribution bootstrap', () => {
|
|
|
217
217
|
expect(codexDoSkill.data.writes).toEqual(
|
|
218
218
|
expect.arrayContaining([
|
|
219
219
|
expect.objectContaining({
|
|
220
|
-
path: 'devflow/changes/<change-key>/execution/tasks/<task-id>/
|
|
220
|
+
path: 'devflow/changes/<change-key>/execution/tasks/<task-id>/events.jsonl',
|
|
221
221
|
durability: 'durable',
|
|
222
|
-
required:
|
|
222
|
+
required: false
|
|
223
223
|
})
|
|
224
224
|
])
|
|
225
225
|
);
|
|
@@ -7,7 +7,6 @@ const { runResume } = require('../operations/resume');
|
|
|
7
7
|
const {
|
|
8
8
|
getRuntimeStatePath,
|
|
9
9
|
getTaskManifestPath,
|
|
10
|
-
getCheckpointPath,
|
|
11
10
|
getEventsPath
|
|
12
11
|
} = require('../store');
|
|
13
12
|
|
|
@@ -76,7 +75,7 @@ describe('runDispatch', () => {
|
|
|
76
75
|
expect(nextManifest.tasks[0].status).toBe('pending');
|
|
77
76
|
});
|
|
78
77
|
|
|
79
|
-
test('rejects stale results when planVersion changes during task execution and records it in
|
|
78
|
+
test('rejects stale results when planVersion changes during task execution and records it in manifest and events', async () => {
|
|
80
79
|
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-dispatch-'));
|
|
81
80
|
const manifestPath = getTaskManifestPath(repoRoot, 'REQ-123');
|
|
82
81
|
|
|
@@ -133,28 +132,25 @@ describe('runDispatch', () => {
|
|
|
133
132
|
});
|
|
134
133
|
|
|
135
134
|
const nextManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
136
|
-
const checkpoint = JSON.parse(fs.readFileSync(getCheckpointPath(repoRoot, 'REQ-123', 'T001'), 'utf8'));
|
|
137
135
|
const events = fs.readFileSync(getEventsPath(repoRoot, 'REQ-123', 'T001'), 'utf8');
|
|
138
136
|
|
|
139
137
|
expect(result.success).toBe(false);
|
|
140
138
|
expect(nextManifest.tasks[0].status).toBe('failed');
|
|
141
139
|
expect(nextManifest.tasks[0].lastError).toContain('Stale result rejected');
|
|
142
|
-
expect(checkpoint.planVersion).toBe(1);
|
|
143
|
-
expect(checkpoint.error).toContain('Stale result rejected');
|
|
144
140
|
expect(events).toContain('task_stale_rejected');
|
|
145
141
|
});
|
|
146
142
|
|
|
147
|
-
test('restores unresolved work from the latest stable
|
|
143
|
+
test('restores unresolved work from the latest stable manifest state on resume without creating handoff markdown', async () => {
|
|
148
144
|
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-resume-stable-'));
|
|
149
145
|
const changeId = 'REQ-123';
|
|
150
146
|
const manifestPath = getTaskManifestPath(repoRoot, changeId);
|
|
151
147
|
|
|
152
148
|
writeJson(getRuntimeStatePath(repoRoot, changeId), {
|
|
153
149
|
changeId,
|
|
154
|
-
changeKey: 'REQ-123-recover-from-stable-
|
|
155
|
-
slug: 'recover-from-stable-
|
|
150
|
+
changeKey: 'REQ-123-recover-from-stable-state',
|
|
151
|
+
slug: 'recover-from-stable-state',
|
|
156
152
|
createdAt: '2026-04-09T01:00:00.000Z',
|
|
157
|
-
goal: 'Recover from stable
|
|
153
|
+
goal: 'Recover from stable state',
|
|
158
154
|
status: 'in_progress',
|
|
159
155
|
initializedAt: '2026-04-09T01:00:00.000Z',
|
|
160
156
|
plannedAt: '2026-04-09T01:01:00.000Z',
|
|
@@ -169,13 +165,13 @@ describe('runDispatch', () => {
|
|
|
169
165
|
|
|
170
166
|
writeJson(manifestPath, {
|
|
171
167
|
changeId,
|
|
172
|
-
goal: 'Recover from stable
|
|
168
|
+
goal: 'Recover from stable state',
|
|
173
169
|
createdAt: '2026-04-09T01:00:00.000Z',
|
|
174
170
|
updatedAt: '2026-04-09T01:02:00.000Z',
|
|
175
171
|
tasks: [
|
|
176
172
|
{
|
|
177
173
|
id: 'T001',
|
|
178
|
-
title: 'Stable
|
|
174
|
+
title: 'Stable completed task',
|
|
179
175
|
type: 'TEST',
|
|
180
176
|
dependsOn: [],
|
|
181
177
|
touches: ['src/a.ts'],
|
|
@@ -219,32 +215,6 @@ describe('runDispatch', () => {
|
|
|
219
215
|
}
|
|
220
216
|
});
|
|
221
217
|
|
|
222
|
-
writeJson(getCheckpointPath(repoRoot, changeId, 'T001'), {
|
|
223
|
-
changeId,
|
|
224
|
-
taskId: 'T001',
|
|
225
|
-
sessionId: 'stable-session',
|
|
226
|
-
planVersion: 1,
|
|
227
|
-
status: 'passed',
|
|
228
|
-
summary: 'Task passed after 1 attempt(s)',
|
|
229
|
-
error: '',
|
|
230
|
-
outputExcerpt: '',
|
|
231
|
-
timestamp: '2026-04-09T01:05:00.000Z',
|
|
232
|
-
attempt: 1
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
writeJson(getCheckpointPath(repoRoot, changeId, 'T002'), {
|
|
236
|
-
changeId,
|
|
237
|
-
taskId: 'T002',
|
|
238
|
-
sessionId: 'failed-session',
|
|
239
|
-
planVersion: 1,
|
|
240
|
-
status: 'failed',
|
|
241
|
-
summary: 'Task failed: Command failed',
|
|
242
|
-
error: 'Command failed',
|
|
243
|
-
outputExcerpt: 'Command failed',
|
|
244
|
-
timestamp: '2026-04-09T01:06:00.000Z',
|
|
245
|
-
attempt: 2
|
|
246
|
-
});
|
|
247
|
-
|
|
248
218
|
const result = await runResume({
|
|
249
219
|
repoRoot,
|
|
250
220
|
changeId,
|
|
@@ -255,7 +225,7 @@ describe('runDispatch', () => {
|
|
|
255
225
|
const nextManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
256
226
|
|
|
257
227
|
expect(result.success).toBe(true);
|
|
258
|
-
expect(result.
|
|
228
|
+
expect(result.restoredState).toMatchObject({
|
|
259
229
|
taskId: 'T001',
|
|
260
230
|
status: 'passed'
|
|
261
231
|
});
|