cc-devflow 4.5.11 → 4.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/.claude/skills/cc-act/CHANGELOG.md +18 -0
  2. package/.claude/skills/cc-act/PLAYBOOK.md +17 -269
  3. package/.claude/skills/cc-act/SKILL.md +38 -425
  4. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +2 -13
  5. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +1 -9
  6. package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +21 -177
  7. package/.claude/skills/cc-act/references/closure-contract.md +12 -63
  8. package/.claude/skills/cc-act/references/git-commit-guidelines.md +5 -5
  9. package/.claude/skills/cc-act/scripts/cc-act-common.sh +5 -322
  10. package/.claude/skills/cc-act/scripts/detect-ship-target.sh +11 -2
  11. package/.claude/skills/cc-act/scripts/inspect-git-index.sh +58 -0
  12. package/.claude/skills/cc-act/scripts/render-pr-brief.sh +40 -440
  13. package/.claude/skills/cc-act/scripts/verify-act-gate.sh +10 -50
  14. package/.claude/skills/cc-check/CHANGELOG.md +18 -0
  15. package/.claude/skills/cc-check/PLAYBOOK.md +19 -273
  16. package/.claude/skills/cc-check/SKILL.md +33 -456
  17. package/.claude/skills/cc-check/references/review-contract.md +12 -147
  18. package/.claude/skills/cc-dev/CHANGELOG.md +15 -0
  19. package/.claude/skills/cc-dev/PLAYBOOK.md +1 -1
  20. package/.claude/skills/cc-dev/SKILL.md +52 -137
  21. package/.claude/skills/cc-dev/scripts/resolve-cc-devflow.sh +181 -0
  22. package/.claude/skills/cc-do/CHANGELOG.md +11 -0
  23. package/.claude/skills/cc-do/PLAYBOOK.md +19 -113
  24. package/.claude/skills/cc-do/SKILL.md +39 -245
  25. package/.claude/skills/cc-do/references/execution-recovery.md +15 -109
  26. package/.claude/skills/cc-do/scripts/cc-do-common.sh +5 -57
  27. package/.claude/skills/cc-do/scripts/check-task-status.sh +35 -65
  28. package/.claude/skills/cc-do/scripts/mark-task-complete.sh +9 -46
  29. package/.claude/skills/cc-do/scripts/select-ready-tasks.sh +29 -97
  30. package/.claude/skills/cc-investigate/CHANGELOG.md +16 -0
  31. package/.claude/skills/cc-investigate/PLAYBOOK.md +20 -180
  32. package/.claude/skills/cc-investigate/SKILL.md +64 -246
  33. package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -98
  34. package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -218
  35. package/.claude/skills/cc-next/CHANGELOG.md +6 -0
  36. package/.claude/skills/cc-next/PLAYBOOK.md +12 -8
  37. package/.claude/skills/cc-next/SKILL.md +34 -140
  38. package/.claude/skills/cc-plan/CHANGELOG.md +16 -0
  39. package/.claude/skills/cc-plan/PLAYBOOK.md +22 -161
  40. package/.claude/skills/cc-plan/SKILL.md +45 -295
  41. package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +30 -228
  42. package/.claude/skills/cc-plan/references/planning-contract.md +24 -161
  43. package/.claude/skills/cc-plan/scripts/next-change-key.sh +8 -44
  44. package/.claude/skills/cc-plan/scripts/parse-task-dependencies.js +2 -2
  45. package/.claude/skills/cc-plan/scripts/validate-scope.sh +1 -1
  46. package/.claude/skills/cc-pr-land/SKILL.md +14 -114
  47. package/.claude/skills/cc-pr-review/CHANGELOG.md +4 -0
  48. package/.claude/skills/cc-pr-review/SKILL.md +20 -103
  49. package/.claude/skills/cc-review/CHANGELOG.md +17 -0
  50. package/.claude/skills/cc-review/PLAYBOOK.md +13 -86
  51. package/.claude/skills/cc-review/SKILL.md +53 -241
  52. package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +2 -2
  53. package/.claude/skills/cc-review/references/implementation-review-branch.md +7 -147
  54. package/.claude/skills/cc-review/references/plan-review-branch.md +5 -147
  55. package/.claude/skills/cc-review/references/review-methods.md +10 -218
  56. package/.claude/skills/cc-review/scripts/collect-review-context.sh +4 -63
  57. package/.claude/skills/cc-roadmap/PLAYBOOK.md +1 -1
  58. package/.claude/skills/cc-roadmap/SKILL.md +3 -3
  59. package/.claude/skills/cc-simplify/CHANGELOG.md +7 -0
  60. package/.claude/skills/cc-simplify/SKILL.md +26 -21
  61. package/.claude/skills/cc-spec-init/PLAYBOOK.md +12 -48
  62. package/.claude/skills/cc-spec-init/SKILL.md +29 -132
  63. package/.claude/skills/cc-spec-init/references/spec-contract.md +8 -17
  64. package/CHANGELOG.md +13 -0
  65. package/bin/cc-devflow-cli.js +20 -260
  66. package/bin/cc-devflow.js +44 -7
  67. package/docs/commands/README.md +1 -1
  68. package/docs/commands/README.zh-CN.md +1 -1
  69. package/docs/examples/README.md +1 -1
  70. package/docs/examples/START-HERE.md +14 -15
  71. package/docs/examples/example-bindings.json +11 -11
  72. package/docs/examples/full-design-blocked/README.md +4 -6
  73. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/{planning/tasks.md → task.md} +20 -15
  74. package/docs/examples/local-handoff/README.md +8 -11
  75. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/pr-brief.md +31 -0
  76. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/{planning/tasks.md → task.md} +18 -13
  77. package/docs/examples/pdca-loop/README.md +6 -9
  78. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +9 -11
  79. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/{planning/tasks.md → task.md} +18 -13
  80. package/docs/examples/scripts/check-example-bindings.sh +11 -62
  81. package/docs/guides/artifact-contract.md +10 -40
  82. package/docs/guides/getting-started.md +8 -8
  83. package/docs/guides/getting-started.zh-CN.md +8 -8
  84. package/docs/guides/minimize-artifacts.md +16 -130
  85. package/docs/guides/project-postmortem.md +14 -71
  86. package/lib/compiler/__tests__/skills-registry.test.js +9 -8
  87. package/lib/compiler/resource-copier.js +29 -0
  88. package/lib/skill-runtime/__tests__/archive-change.test.js +2 -2
  89. package/lib/skill-runtime/__tests__/benchmark-skills.test.js +3 -3
  90. package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +14 -4
  91. package/lib/skill-runtime/errors.js +3 -3
  92. package/lib/skill-runtime/index.js +5 -23
  93. package/lib/skill-runtime/paths.js +5 -52
  94. package/lib/skill-runtime/query-registry.js +4 -4
  95. package/lib/skill-runtime/query.js +89 -201
  96. package/lib/skill-runtime/store.js +4 -40
  97. package/lib/skill-runtime/trace.js +2 -2
  98. package/package.json +2 -5
  99. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +0 -29
  100. package/.claude/skills/cc-act/assets/RELEASE_NOTE_TEMPLATE.md +0 -54
  101. package/.claude/skills/cc-act/scripts/generate-status-report.sh +0 -92
  102. package/.claude/skills/cc-act/scripts/sync-act-docs.sh +0 -355
  103. package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +0 -234
  104. package/.claude/skills/cc-check/scripts/render-report-card.js +0 -438
  105. package/.claude/skills/cc-check/scripts/verify-gate.sh +0 -85
  106. package/.claude/skills/cc-do/scripts/build-task-context.sh +0 -175
  107. package/.claude/skills/cc-do/scripts/record-review-decision.sh +0 -88
  108. package/.claude/skills/cc-do/scripts/recover-workflow.sh +0 -82
  109. package/.claude/skills/cc-do/scripts/run-problem-analysis.sh +0 -70
  110. package/.claude/skills/cc-do/scripts/verify-task-gates.sh +0 -109
  111. package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +0 -92
  112. package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +0 -224
  113. package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +0 -178
  114. package/.claude/skills/cc-spec-init/assets/CHANGE_META_TEMPLATE.json +0 -28
  115. package/.claude/skills/cc-spec-init/scripts/validate-spec-links.sh +0 -45
  116. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +0 -234
  117. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +0 -488
  118. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +0 -189
  119. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/resume-index.md +0 -39
  120. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/status.md +0 -29
  121. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +0 -123
  122. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +0 -292
  123. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +0 -136
  124. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/status.md +0 -29
  125. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +0 -124
  126. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +0 -292
  127. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +0 -136
  128. package/docs/get-shit-done-strategy-audit.md +0 -518
  129. package/docs/skill-runtime-migration.md +0 -46
  130. package/lib/skill-runtime/__tests__/approve.test.js +0 -92
  131. package/lib/skill-runtime/__tests__/autopilot.test.js +0 -253
  132. package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +0 -165
  133. package/lib/skill-runtime/__tests__/delegation.test.js +0 -97
  134. package/lib/skill-runtime/__tests__/dispatch.test.js +0 -237
  135. package/lib/skill-runtime/__tests__/intent.test.js +0 -203
  136. package/lib/skill-runtime/__tests__/lifecycle.test.js +0 -169
  137. package/lib/skill-runtime/__tests__/planner.tdd.test.js +0 -331
  138. package/lib/skill-runtime/__tests__/prepare-pr.test.js +0 -126
  139. package/lib/skill-runtime/__tests__/query.test.js +0 -860
  140. package/lib/skill-runtime/__tests__/readiness.test.js +0 -53
  141. package/lib/skill-runtime/__tests__/release.test.js +0 -85
  142. package/lib/skill-runtime/__tests__/review-check-integration.test.js +0 -148
  143. package/lib/skill-runtime/__tests__/review-records.test.js +0 -619
  144. package/lib/skill-runtime/__tests__/runtime.integration.test.js +0 -351
  145. package/lib/skill-runtime/__tests__/schemas.test.js +0 -337
  146. package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +0 -137
  147. package/lib/skill-runtime/__tests__/task-contract.test.js +0 -874
  148. package/lib/skill-runtime/__tests__/team-state.test.js +0 -51
  149. package/lib/skill-runtime/__tests__/verify-artifacts.test.js +0 -203
  150. package/lib/skill-runtime/__tests__/worker-run.test.js +0 -275
  151. package/lib/skill-runtime/__tests__/worker.test.js +0 -56
  152. package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +0 -31
  153. package/lib/skill-runtime/__tests__/workflow-context.test.js +0 -98
  154. package/lib/skill-runtime/artifacts.js +0 -88
  155. package/lib/skill-runtime/context-index.js +0 -545
  156. package/lib/skill-runtime/delegation.js +0 -533
  157. package/lib/skill-runtime/intent.js +0 -309
  158. package/lib/skill-runtime/lifecycle.js +0 -294
  159. package/lib/skill-runtime/operations/CLAUDE.md +0 -19
  160. package/lib/skill-runtime/operations/approve.js +0 -81
  161. package/lib/skill-runtime/operations/autopilot-core.js +0 -337
  162. package/lib/skill-runtime/operations/autopilot-execution.js +0 -307
  163. package/lib/skill-runtime/operations/autopilot-shared.js +0 -48
  164. package/lib/skill-runtime/operations/autopilot.js +0 -163
  165. package/lib/skill-runtime/operations/dispatch.js +0 -416
  166. package/lib/skill-runtime/operations/init.js +0 -60
  167. package/lib/skill-runtime/operations/janitor.js +0 -61
  168. package/lib/skill-runtime/operations/plan.js +0 -59
  169. package/lib/skill-runtime/operations/prepare-pr.js +0 -25
  170. package/lib/skill-runtime/operations/release.js +0 -99
  171. package/lib/skill-runtime/operations/resume.js +0 -126
  172. package/lib/skill-runtime/operations/review-records.js +0 -265
  173. package/lib/skill-runtime/operations/snapshot.js +0 -45
  174. package/lib/skill-runtime/operations/task-contract.js +0 -593
  175. package/lib/skill-runtime/operations/verify.js +0 -170
  176. package/lib/skill-runtime/operations/worker-run.js +0 -531
  177. package/lib/skill-runtime/operations/worker.js +0 -33
  178. package/lib/skill-runtime/planner.js +0 -539
  179. package/lib/skill-runtime/readiness.js +0 -84
  180. package/lib/skill-runtime/review-records.js +0 -123
  181. package/lib/skill-runtime/review.js +0 -855
  182. package/lib/skill-runtime/schemas.js +0 -746
  183. package/lib/skill-runtime/task-contract.js +0 -188
  184. package/lib/skill-runtime/team-state.js +0 -122
  185. package/lib/skill-runtime/workflow-context.js +0 -748
@@ -1,237 +0,0 @@
1
- const fs = require('fs');
2
- const os = require('os');
3
- const path = require('path');
4
-
5
- const { runDispatch } = require('../operations/dispatch');
6
- const { runResume } = require('../operations/resume');
7
- const {
8
- getRuntimeStatePath,
9
- getTaskManifestPath,
10
- getEventsPath
11
- } = require('../store');
12
-
13
- function writeJson(filePath, value) {
14
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
15
- fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
16
- }
17
-
18
- describe('runDispatch', () => {
19
- test('blocks execution when the current plan version has not been approved', async () => {
20
- const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-dispatch-unapproved-'));
21
- const manifestPath = getTaskManifestPath(repoRoot, 'REQ-123');
22
-
23
- writeJson(getRuntimeStatePath(repoRoot, 'REQ-123'), {
24
- changeId: 'REQ-123',
25
- changeKey: 'REQ-123-block-unapproved-execution',
26
- slug: 'block-unapproved-execution',
27
- createdAt: '2026-03-25T01:00:00.000Z',
28
- goal: 'Block unapproved execution',
29
- status: 'planned',
30
- initializedAt: '2026-03-25T01:00:00.000Z',
31
- plannedAt: '2026-03-25T01:01:00.000Z',
32
- approval: {
33
- status: 'pending',
34
- executionMode: 'delegate'
35
- },
36
- updatedAt: '2026-03-25T01:01:00.000Z'
37
- });
38
-
39
- writeJson(manifestPath, {
40
- changeId: 'REQ-123',
41
- goal: 'Block unapproved execution',
42
- createdAt: '2026-03-25T01:00:00.000Z',
43
- updatedAt: '2026-03-25T01:01:00.000Z',
44
- tasks: [
45
- {
46
- id: 'T001',
47
- title: 'Should not execute before approval',
48
- type: 'IMPL',
49
- dependsOn: [],
50
- touches: ['lib/skill-runtime/operations/dispatch.js'],
51
- run: ['echo blocked'],
52
- checks: [],
53
- status: 'pending',
54
- attempts: 0,
55
- maxRetries: 0
56
- }
57
- ],
58
- metadata: {
59
- source: 'default',
60
- generatedBy: 'test',
61
- planVersion: 1
62
- }
63
- });
64
-
65
- const result = await runDispatch({
66
- repoRoot,
67
- changeId: 'REQ-123',
68
- parallel: 1
69
- });
70
-
71
- const nextManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
72
-
73
- expect(result.success).toBe(false);
74
- expect(result.reason).toContain('cc-plan approval gate');
75
- expect(nextManifest.tasks[0].status).toBe('pending');
76
- });
77
-
78
- test('rejects stale results when planVersion changes during task execution and records it in manifest and events', async () => {
79
- const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-dispatch-'));
80
- const manifestPath = getTaskManifestPath(repoRoot, 'REQ-123');
81
-
82
- writeJson(getRuntimeStatePath(repoRoot, 'REQ-123'), {
83
- changeId: 'REQ-123',
84
- changeKey: 'REQ-123-reject-stale-results',
85
- slug: 'reject-stale-results',
86
- createdAt: '2026-03-25T01:00:00.000Z',
87
- goal: 'Reject stale results',
88
- status: 'planned',
89
- initializedAt: '2026-03-25T01:00:00.000Z',
90
- plannedAt: '2026-03-25T01:01:00.000Z',
91
- approval: {
92
- status: 'approved',
93
- executionMode: 'delegate',
94
- planVersion: 1,
95
- approvedAt: '2026-03-25T01:01:30.000Z'
96
- },
97
- updatedAt: '2026-03-25T01:01:00.000Z'
98
- });
99
-
100
- writeJson(manifestPath, {
101
- changeId: 'REQ-123',
102
- goal: 'Reject stale results',
103
- createdAt: '2026-03-25T01:00:00.000Z',
104
- updatedAt: '2026-03-25T01:01:00.000Z',
105
- tasks: [
106
- {
107
- id: 'T001',
108
- title: 'Mutate plan version mid-flight',
109
- type: 'IMPL',
110
- dependsOn: [],
111
- touches: ['lib/skill-runtime/operations/dispatch.js'],
112
- run: [
113
- `node -e "const fs=require('fs'); const p='${manifestPath.replace(/\\/g, '\\\\')}'; const data=JSON.parse(fs.readFileSync(p,'utf8')); data.metadata.planVersion=2; fs.writeFileSync(p, JSON.stringify(data, null, 2)+'\\n');"`
114
- ],
115
- checks: [],
116
- status: 'pending',
117
- attempts: 0,
118
- maxRetries: 0
119
- }
120
- ],
121
- metadata: {
122
- source: 'default',
123
- generatedBy: 'test',
124
- planVersion: 1
125
- }
126
- });
127
-
128
- const result = await runDispatch({
129
- repoRoot,
130
- changeId: 'REQ-123',
131
- parallel: 1
132
- });
133
-
134
- const nextManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
135
- const events = fs.readFileSync(getEventsPath(repoRoot, 'REQ-123', 'T001'), 'utf8');
136
-
137
- expect(result.success).toBe(false);
138
- expect(nextManifest.tasks[0].status).toBe('failed');
139
- expect(nextManifest.tasks[0].lastError).toContain('Stale result rejected');
140
- expect(events).toContain('task_stale_rejected');
141
- });
142
-
143
- test('restores unresolved work from the latest stable manifest state on resume without creating handoff markdown', async () => {
144
- const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-resume-stable-'));
145
- const changeId = 'REQ-123';
146
- const manifestPath = getTaskManifestPath(repoRoot, changeId);
147
-
148
- writeJson(getRuntimeStatePath(repoRoot, changeId), {
149
- changeId,
150
- changeKey: 'REQ-123-recover-from-stable-state',
151
- slug: 'recover-from-stable-state',
152
- createdAt: '2026-04-09T01:00:00.000Z',
153
- goal: 'Recover from stable state',
154
- status: 'in_progress',
155
- initializedAt: '2026-04-09T01:00:00.000Z',
156
- plannedAt: '2026-04-09T01:01:00.000Z',
157
- approval: {
158
- status: 'approved',
159
- executionMode: 'delegate',
160
- planVersion: 1,
161
- approvedAt: '2026-04-09T01:01:30.000Z'
162
- },
163
- updatedAt: '2026-04-09T01:02:00.000Z'
164
- });
165
-
166
- writeJson(manifestPath, {
167
- changeId,
168
- goal: 'Recover from stable state',
169
- createdAt: '2026-04-09T01:00:00.000Z',
170
- updatedAt: '2026-04-09T01:02:00.000Z',
171
- tasks: [
172
- {
173
- id: 'T001',
174
- title: 'Stable completed task',
175
- type: 'TEST',
176
- dependsOn: [],
177
- touches: ['src/a.ts'],
178
- run: ['node -e "process.exit(0)"'],
179
- checks: [],
180
- status: 'passed',
181
- attempts: 1,
182
- maxRetries: 0
183
- },
184
- {
185
- id: 'T002',
186
- title: 'Retry-exhausted failed task',
187
- type: 'IMPL',
188
- dependsOn: ['T001'],
189
- touches: ['src/b.ts'],
190
- run: ['node -e "process.exit(0)"'],
191
- checks: [],
192
- status: 'failed',
193
- attempts: 2,
194
- maxRetries: 1,
195
- lastError: 'Command failed'
196
- },
197
- {
198
- id: 'T003',
199
- title: 'Task skipped by failed dependency',
200
- type: 'IMPL',
201
- dependsOn: ['T002'],
202
- touches: ['src/c.ts'],
203
- run: ['node -e "process.exit(0)"'],
204
- checks: [],
205
- status: 'skipped',
206
- attempts: 0,
207
- maxRetries: 0,
208
- lastError: 'Blocked by failed dependency'
209
- }
210
- ],
211
- metadata: {
212
- source: 'default',
213
- generatedBy: 'test',
214
- planVersion: 1
215
- }
216
- });
217
-
218
- const result = await runResume({
219
- repoRoot,
220
- changeId,
221
- parallel: 1,
222
- fromCheckpoint: 'stable'
223
- });
224
-
225
- const nextManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
226
-
227
- expect(result.success).toBe(true);
228
- expect(result.restoredState).toMatchObject({
229
- taskId: 'T001',
230
- status: 'passed'
231
- });
232
- expect(result.restoredTasks).toEqual(['T002', 'T003']);
233
- expect(nextManifest.tasks.map((task) => task.status)).toEqual(['passed', 'passed', 'passed']);
234
- expect(nextManifest.tasks[1].attempts).toBe(1);
235
- expect(nextManifest.tasks[2].attempts).toBe(1);
236
- });
237
- });
@@ -1,203 +0,0 @@
1
- const fs = require('fs');
2
- const os = require('os');
3
- const path = require('path');
4
-
5
- const {
6
- syncIntentMemory,
7
- syncIntentPrBrief
8
- } = require('../intent');
9
- const {
10
- getIntentPrBriefPath,
11
- getIntentResumeIndexPath,
12
- getIntentHandoffArtifactPaths,
13
- getLegacyIntentProjectionPaths
14
- } = require('../artifacts');
15
- const {
16
- getRuntimeStatePath,
17
- getTaskManifestPath,
18
- getReportCardPath
19
- } = require('../store');
20
-
21
- function writeJson(filePath, value) {
22
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
23
- fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
24
- }
25
-
26
- function writeText(filePath, content = 'stale\n') {
27
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
28
- fs.writeFileSync(filePath, content);
29
- }
30
-
31
- describe('intent handoff bridge', () => {
32
- let repoRoot;
33
-
34
- beforeEach(() => {
35
- repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-intent-'));
36
-
37
- writeJson(
38
- getRuntimeStatePath(repoRoot, 'REQ-123'),
39
- {
40
- changeId: 'REQ-123',
41
- changeKey: 'REQ-123-autopilot-memory-bridge',
42
- slug: 'autopilot-memory-bridge',
43
- createdAt: '2026-03-25T01:00:00.000Z',
44
- goal: 'Deliver autopilot memory bridge',
45
- status: 'verified',
46
- initializedAt: '2026-03-25T01:00:00.000Z',
47
- plannedAt: '2026-03-25T01:05:00.000Z',
48
- verifiedAt: '2026-03-25T01:10:00.000Z',
49
- approval: {
50
- status: 'approved',
51
- executionMode: 'delegate',
52
- planVersion: 1,
53
- approvedAt: '2026-03-25T01:06:00.000Z'
54
- },
55
- updatedAt: '2026-03-25T01:10:00.000Z'
56
- }
57
- );
58
-
59
- writeJson(
60
- getTaskManifestPath(repoRoot, 'REQ-123'),
61
- {
62
- changeId: 'REQ-123',
63
- goal: 'Deliver autopilot memory bridge',
64
- createdAt: '2026-03-25T01:05:00.000Z',
65
- updatedAt: '2026-03-25T01:10:00.000Z',
66
- tasks: [
67
- {
68
- id: 'T001',
69
- title: 'Write intent files',
70
- type: 'IMPL',
71
- dependsOn: [],
72
- touches: ['lib/skill-runtime/intent.js'],
73
- run: ['echo ok'],
74
- checks: [],
75
- status: 'passed',
76
- attempts: 1,
77
- maxRetries: 1
78
- }
79
- ],
80
- metadata: {
81
- source: 'default',
82
- generatedBy: 'test',
83
- planVersion: 1
84
- }
85
- }
86
- );
87
-
88
- });
89
-
90
- test('syncIntentMemory removes legacy projection files instead of writing them', async () => {
91
- const legacyFiles = getLegacyIntentProjectionPaths(repoRoot, 'REQ-123', ['T001']);
92
-
93
- for (const filePath of legacyFiles) {
94
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
95
- fs.writeFileSync(filePath, 'legacy\n');
96
- }
97
-
98
- await syncIntentMemory(repoRoot, 'REQ-123');
99
-
100
- for (const filePath of legacyFiles) {
101
- expect(fs.existsSync(filePath)).toBe(false);
102
- }
103
- expect(fs.existsSync(getIntentResumeIndexPath(repoRoot, 'REQ-123'))).toBe(false);
104
- });
105
-
106
- test('can emit a single local-handoff resume index when explicitly requested', async () => {
107
- const resumePath = getIntentResumeIndexPath(repoRoot, 'REQ-123');
108
-
109
- writeJson(
110
- getReportCardPath(repoRoot, 'REQ-123'),
111
- {
112
- changeId: 'REQ-123',
113
- overall: 'pass',
114
- quickGates: [],
115
- strictGates: [],
116
- review: { status: 'pass', summary: 'review-ok', details: '', findings: [], taskReviews: {}, diffReview: {} },
117
- blockingFindings: [],
118
- timestamp: '2026-03-25T01:12:00.000Z'
119
- }
120
- );
121
-
122
- for (const filePath of getIntentHandoffArtifactPaths(repoRoot, 'REQ-123')) {
123
- if (filePath !== resumePath) {
124
- writeText(filePath);
125
- }
126
- }
127
-
128
- await syncIntentMemory(repoRoot, 'REQ-123', { handoff: 'resume' });
129
-
130
- const resumeIndex = fs.readFileSync(resumePath, 'utf8');
131
-
132
- expect(resumeIndex).toContain('Resume Index: REQ-123');
133
- expect(resumeIndex).toContain('Durable Truth');
134
- for (const filePath of getIntentHandoffArtifactPaths(repoRoot, 'REQ-123')) {
135
- expect(fs.existsSync(filePath)).toBe(filePath === resumePath);
136
- }
137
- });
138
-
139
- test('writes pr brief from manifest task state and report-card only', async () => {
140
- const prBriefPath = getIntentPrBriefPath(repoRoot, 'REQ-123');
141
- const manifestPath = getTaskManifestPath(repoRoot, 'REQ-123');
142
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
143
-
144
- manifest.tasks.push({
145
- id: 'T002',
146
- title: 'Skip follow-up after dependency gate',
147
- type: 'IMPL',
148
- dependsOn: ['T001'],
149
- touches: ['lib/skill-runtime/operations/autopilot-core.js'],
150
- run: ['echo skipped'],
151
- checks: [],
152
- status: 'skipped',
153
- attempts: 0,
154
- maxRetries: 1,
155
- lastError: 'Blocked by failed dependency'
156
- });
157
- fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
158
-
159
- writeJson(
160
- getReportCardPath(repoRoot, 'REQ-123'),
161
- {
162
- changeId: 'REQ-123',
163
- overall: 'pass',
164
- quickGates: [
165
- { name: 'lint', status: 'pass', command: 'npm run lint', durationMs: 1200, details: '' }
166
- ],
167
- strictGates: [
168
- { name: 'test', status: 'pass', command: 'npm test', durationMs: 2400, details: '' }
169
- ],
170
- review: {
171
- status: 'pass',
172
- summary: 'review-ok',
173
- details: '',
174
- findings: [],
175
- taskReviews: { status: 'pass', required: true, summary: '', reviewers: [], findings: [] },
176
- diffReview: { status: 'pass', required: true, summary: '', reviewers: [], findings: [] }
177
- },
178
- blockingFindings: [],
179
- timestamp: '2026-03-25T01:12:00.000Z'
180
- }
181
- );
182
-
183
- for (const filePath of getIntentHandoffArtifactPaths(repoRoot, 'REQ-123')) {
184
- if (filePath !== prBriefPath) {
185
- writeText(filePath);
186
- }
187
- }
188
-
189
- await syncIntentPrBrief(repoRoot, 'REQ-123');
190
-
191
- const prBrief = fs.readFileSync(prBriefPath, 'utf8');
192
-
193
- expect(prBrief).toContain('PR Brief: REQ-123');
194
- expect(prBrief).toContain('Suggested Title: feat(req-123): Deliver autopilot memory bridge');
195
- expect(prBrief).toContain('planning/tasks.md');
196
- expect(prBrief).toContain('planning/task-manifest.json');
197
- expect(prBrief).toContain('存在 1 个 skipped 任务,需要确认是否可接受:T002');
198
- expect(prBrief).not.toContain('result.md');
199
- for (const filePath of getIntentHandoffArtifactPaths(repoRoot, 'REQ-123')) {
200
- expect(fs.existsSync(filePath)).toBe(filePath === prBriefPath);
201
- }
202
- });
203
- });
@@ -1,169 +0,0 @@
1
- const {
2
- deriveLifecycleStage,
3
- deriveLifecycleNextAction,
4
- classifyDelegationMode,
5
- deriveTaskProgress,
6
- getApprovalState,
7
- isTaskCompletedStatus,
8
- isTaskSettledStatus,
9
- normalizeTaskStatus,
10
- stageIndex
11
- } = require('../lifecycle');
12
-
13
- describe('lifecycle helpers', () => {
14
- test('derives approve stage from an unapproved planned manifest and switches to delegate after approval', () => {
15
- const state = {
16
- changeId: 'REQ-123',
17
- goal: 'Lock approval truth',
18
- status: 'planned',
19
- approval: {
20
- status: 'pending',
21
- executionMode: 'delegate'
22
- }
23
- };
24
- const manifest = {
25
- metadata: {
26
- planVersion: 3
27
- },
28
- tasks: [
29
- { id: 'T001', status: 'pending' }
30
- ]
31
- };
32
-
33
- expect(deriveLifecycleStage({ state, manifest, report: null, hasPrBrief: false })).toBe('approve');
34
- expect(getApprovalState(state, manifest)).toMatchObject({
35
- status: 'pending',
36
- executionMode: 'delegate',
37
- planVersion: 3
38
- });
39
-
40
- const approvedState = {
41
- ...state,
42
- approval: {
43
- status: 'approved',
44
- executionMode: 'delegate',
45
- planVersion: 3,
46
- approvedAt: '2026-04-09T05:00:00.000Z'
47
- }
48
- };
49
-
50
- expect(deriveLifecycleStage({ state: approvedState, manifest, report: null, hasPrBrief: false })).toBe('delegate');
51
- expect(stageIndex('approve')).toBeLessThan(stageIndex('delegate'));
52
- });
53
-
54
- test('keeps team mode explicit opt-in when routing tasks', () => {
55
- const task = {
56
- id: 'T900',
57
- title: 'Cross-module implementation',
58
- type: 'IMPL',
59
- dependsOn: ['T001', 'T002', 'T003'],
60
- touches: ['a.ts', 'b.ts', 'c.ts', 'd.ts'],
61
- run: ['echo one', 'echo two'],
62
- checks: []
63
- };
64
-
65
- expect(classifyDelegationMode(task, { approval: { executionMode: 'delegate' } })).toBe('delegate');
66
- expect(classifyDelegationMode(task, { approval: { executionMode: 'team' } })).toBe('team');
67
- });
68
-
69
- test('derives next action from shared lifecycle stage rules', () => {
70
- const state = {
71
- changeId: 'REQ-123',
72
- goal: 'Lock approval truth',
73
- status: 'planned',
74
- approval: {
75
- status: 'pending',
76
- executionMode: 'delegate'
77
- }
78
- };
79
- const manifest = {
80
- metadata: {
81
- planVersion: 3
82
- },
83
- tasks: [
84
- { id: 'T001', status: 'pending' },
85
- { id: 'T002', status: 'failed' }
86
- ]
87
- };
88
-
89
- expect(deriveLifecycleNextAction({ state: null, manifest: null, report: null })).toBe(
90
- '生成或刷新 task-manifest,然后回到批准闸。'
91
- );
92
- expect(deriveLifecycleNextAction({ state, manifest, report: null })).toBe(
93
- '先批准当前计划版本 3,再进入执行。'
94
- );
95
-
96
- const approvedState = {
97
- ...state,
98
- approval: {
99
- status: 'approved',
100
- executionMode: 'delegate',
101
- planVersion: 3,
102
- approvedAt: '2026-04-09T05:00:00.000Z'
103
- }
104
- };
105
-
106
- expect(deriveLifecycleNextAction({ state: approvedState, manifest, report: null })).toBe(
107
- '优先修复失败任务 T002,然后从 task-manifest 的最近稳定状态恢复。'
108
- );
109
- expect(
110
- deriveLifecycleNextAction({
111
- state: approvedState,
112
- manifest: {
113
- ...manifest,
114
- tasks: [
115
- { id: 'T001', status: 'passed' }
116
- ]
117
- },
118
- report: { overall: 'pass' },
119
- hasPrBrief: true
120
- })
121
- ).toBe('PR brief 已生成,可直接整理提交并创建 PR。');
122
- expect(
123
- deriveLifecycleNextAction({
124
- state: approvedState,
125
- manifest: {
126
- ...manifest,
127
- tasks: [
128
- { id: 'T001', status: 'passed' }
129
- ]
130
- },
131
- report: { overall: 'pass' },
132
- hasReleaseNote: true
133
- })
134
- ).toBe('发布材料已经生成,可归档或启动下一轮增量。');
135
- });
136
-
137
- test('derives query progress from the shared task summary', () => {
138
- expect(
139
- deriveTaskProgress([
140
- { id: 'T001', status: 'pending' },
141
- { id: 'T002', status: 'running' },
142
- { id: 'T003', status: 'passed' },
143
- { id: 'T004', status: 'failed' },
144
- { id: 'T005', status: 'skipped' },
145
- { id: 'T006', status: 'custom-state' }
146
- ])
147
- ).toEqual({
148
- totalTasks: 6,
149
- completedTasks: 1,
150
- failedTasks: 1,
151
- pendingTasks: 1,
152
- runningTasks: 1,
153
- skippedTasks: 1
154
- });
155
- });
156
-
157
- test('normalizes legacy completion aliases into shared lifecycle semantics', () => {
158
- expect(normalizeTaskStatus('completed')).toBe('passed');
159
- expect(normalizeTaskStatus('done')).toBe('passed');
160
- expect(normalizeTaskStatus('verified')).toBe('passed');
161
- expect(isTaskCompletedStatus('passed')).toBe(true);
162
- expect(isTaskCompletedStatus('verified')).toBe(true);
163
- expect(isTaskCompletedStatus('skipped')).toBe(false);
164
- expect(isTaskSettledStatus('passed')).toBe(true);
165
- expect(isTaskSettledStatus('verified')).toBe(true);
166
- expect(isTaskSettledStatus('skipped')).toBe(true);
167
- expect(isTaskSettledStatus('failed')).toBe(false);
168
- });
169
- });