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.
Files changed (149) hide show
  1. package/.claude/skills/cc-act/CHANGELOG.md +33 -0
  2. package/.claude/skills/cc-act/PLAYBOOK.md +9 -4
  3. package/.claude/skills/cc-act/SKILL.md +73 -12
  4. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +30 -0
  5. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +29 -0
  6. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +103 -0
  7. package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +61 -5
  8. package/.claude/skills/cc-act/references/closure-contract.md +4 -1
  9. package/.claude/skills/cc-act/references/git-commit-guidelines.md +342 -37
  10. package/.claude/skills/cc-act/scripts/cc-act-common.sh +29 -1
  11. package/.claude/skills/cc-act/scripts/render-pr-brief.sh +164 -0
  12. package/.claude/skills/cc-act/scripts/sync-act-docs.sh +1 -1
  13. package/.claude/skills/cc-check/CHANGELOG.md +17 -0
  14. package/.claude/skills/cc-check/PLAYBOOK.md +1 -0
  15. package/.claude/skills/cc-check/SKILL.md +9 -5
  16. package/.claude/skills/cc-check/references/review-contract.md +7 -0
  17. package/.claude/skills/cc-check/scripts/render-report-card.js +6 -1
  18. package/.claude/skills/cc-dev/CHANGELOG.md +5 -0
  19. package/.claude/skills/cc-dev/SKILL.md +26 -1
  20. package/.claude/skills/cc-do/CHANGELOG.md +23 -0
  21. package/.claude/skills/cc-do/PLAYBOOK.md +7 -7
  22. package/.claude/skills/cc-do/SKILL.md +49 -45
  23. package/.claude/skills/cc-do/references/execution-recovery.md +18 -13
  24. package/.claude/skills/cc-do/scripts/build-task-context.sh +13 -22
  25. package/.claude/skills/cc-do/scripts/mark-task-complete.sh +0 -6
  26. package/.claude/skills/cc-do/scripts/record-review-decision.sh +4 -5
  27. package/.claude/skills/cc-do/scripts/recover-workflow.sh +9 -11
  28. package/.claude/skills/cc-do/scripts/verify-task-gates.sh +12 -10
  29. package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +7 -29
  30. package/.claude/skills/cc-investigate/CHANGELOG.md +34 -0
  31. package/.claude/skills/cc-investigate/PLAYBOOK.md +21 -5
  32. package/.claude/skills/cc-investigate/SKILL.md +97 -40
  33. package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +66 -4
  34. package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +30 -59
  35. package/.claude/skills/cc-investigate/assets/{ANALYSIS_TEMPLATE.md → legacy/ANALYSIS_TEMPLATE.md} +48 -0
  36. package/.claude/skills/cc-investigate/references/investigation-contract.md +16 -2
  37. package/.claude/skills/cc-investigate/scripts/bootstrap-analysis.sh +1 -1
  38. package/.claude/skills/cc-next/CHANGELOG.md +6 -0
  39. package/.claude/skills/cc-next/PLAYBOOK.md +26 -4
  40. package/.claude/skills/cc-next/SKILL.md +39 -4
  41. package/.claude/skills/cc-plan/CHANGELOG.md +38 -0
  42. package/.claude/skills/cc-plan/PLAYBOOK.md +60 -53
  43. package/.claude/skills/cc-plan/SKILL.md +164 -87
  44. package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +101 -9
  45. package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +58 -229
  46. package/.claude/skills/cc-plan/assets/{DESIGN_TEMPLATE.md → legacy/DESIGN_TEMPLATE.md} +68 -0
  47. package/.claude/skills/cc-plan/assets/{TINY_DESIGN_TEMPLATE.md → legacy/TINY_DESIGN_TEMPLATE.md} +47 -1
  48. package/.claude/skills/cc-plan/references/planning-contract.md +48 -33
  49. package/.claude/skills/cc-review/CHANGELOG.md +6 -0
  50. package/.claude/skills/cc-review/PLAYBOOK.md +9 -11
  51. package/.claude/skills/cc-review/SKILL.md +37 -61
  52. package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +1 -1
  53. package/.claude/skills/cc-review/references/implementation-review-branch.md +5 -5
  54. package/.claude/skills/cc-review/references/plan-review-branch.md +1 -1
  55. package/.claude/skills/cc-review/references/review-methods.md +4 -4
  56. package/.claude/skills/cc-review/scripts/collect-review-context.sh +14 -7
  57. package/.claude/skills/cc-roadmap/CHANGELOG.md +6 -0
  58. package/.claude/skills/cc-roadmap/PLAYBOOK.md +30 -0
  59. package/.claude/skills/cc-roadmap/SKILL.md +45 -8
  60. package/.claude/skills/cc-roadmap/assets/BACKLOG_TEMPLATE.md +8 -0
  61. package/.claude/skills/cc-roadmap/assets/ROADMAP_TEMPLATE.md +22 -0
  62. package/.claude/skills/cc-roadmap/assets/TRACKING_TEMPLATE.json +32 -1
  63. package/.claude/skills/cc-roadmap/references/roadmap-dialogue.md +14 -14
  64. package/CHANGELOG.md +28 -0
  65. package/CONTRIBUTING.md +40 -4
  66. package/CONTRIBUTING.zh-CN.md +40 -4
  67. package/README.md +57 -43
  68. package/README.zh-CN.md +57 -43
  69. package/bin/cc-devflow-cli.js +293 -36
  70. package/docs/examples/START-HERE.md +5 -4
  71. package/docs/examples/example-bindings.json +10 -10
  72. package/docs/examples/full-design-blocked/BACKLOG.md +1 -1
  73. package/docs/examples/full-design-blocked/README.md +2 -2
  74. package/docs/examples/full-design-blocked/ROADMAP.md +1 -1
  75. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +2 -1
  76. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +29 -312
  77. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/tasks.md +11 -8
  78. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +4 -4
  79. package/docs/examples/full-design-blocked/roadmap.json +1 -1
  80. package/docs/examples/local-handoff/BACKLOG.md +1 -1
  81. package/docs/examples/local-handoff/README.md +2 -2
  82. package/docs/examples/local-handoff/ROADMAP.md +1 -1
  83. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +2 -1
  84. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +27 -210
  85. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/tasks.md +9 -6
  86. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +1 -1
  87. package/docs/examples/local-handoff/roadmap.json +1 -1
  88. package/docs/examples/pdca-loop/BACKLOG.md +1 -1
  89. package/docs/examples/pdca-loop/README.md +2 -2
  90. package/docs/examples/pdca-loop/ROADMAP.md +1 -1
  91. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +65 -1
  92. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +2 -1
  93. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +26 -228
  94. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/tasks.md +9 -6
  95. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +1 -1
  96. package/docs/examples/pdca-loop/roadmap.json +1 -1
  97. package/docs/examples/scripts/check-example-bindings.sh +11 -5
  98. package/docs/get-shit-done-strategy-audit.md +22 -22
  99. package/docs/guides/artifact-contract.md +44 -0
  100. package/docs/guides/getting-started.md +10 -8
  101. package/docs/guides/getting-started.zh-CN.md +10 -8
  102. package/docs/guides/minimize-artifacts.md +123 -0
  103. package/docs/guides/project-postmortem.md +78 -0
  104. package/lib/compiler/__tests__/skills-registry.test.js +2 -2
  105. package/lib/skill-runtime/CLAUDE.md +1 -1
  106. package/lib/skill-runtime/__tests__/autopilot.test.js +42 -6
  107. package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +165 -0
  108. package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +2 -2
  109. package/lib/skill-runtime/__tests__/dispatch.test.js +8 -38
  110. package/lib/skill-runtime/__tests__/intent.test.js +4 -20
  111. package/lib/skill-runtime/__tests__/lifecycle.test.js +1 -1
  112. package/lib/skill-runtime/__tests__/paths.test.js +7 -1
  113. package/lib/skill-runtime/__tests__/planner.tdd.test.js +63 -2
  114. package/lib/skill-runtime/__tests__/prepare-pr.test.js +3 -16
  115. package/lib/skill-runtime/__tests__/query.test.js +388 -7
  116. package/lib/skill-runtime/__tests__/review-check-integration.test.js +148 -0
  117. package/lib/skill-runtime/__tests__/review-records.test.js +619 -0
  118. package/lib/skill-runtime/__tests__/runtime.integration.test.js +64 -23
  119. package/lib/skill-runtime/__tests__/schemas.test.js +76 -2
  120. package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +137 -0
  121. package/lib/skill-runtime/__tests__/task-contract.test.js +783 -0
  122. package/lib/skill-runtime/__tests__/verify-artifacts.test.js +203 -0
  123. package/lib/skill-runtime/__tests__/worker-run.test.js +4 -11
  124. package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +31 -0
  125. package/lib/skill-runtime/__tests__/workflow-context.test.js +98 -0
  126. package/lib/skill-runtime/artifacts.js +0 -5
  127. package/lib/skill-runtime/context-index.js +545 -0
  128. package/lib/skill-runtime/intent.js +9 -33
  129. package/lib/skill-runtime/lifecycle.js +1 -1
  130. package/lib/skill-runtime/operations/CLAUDE.md +2 -2
  131. package/lib/skill-runtime/operations/dispatch.js +4 -42
  132. package/lib/skill-runtime/operations/init.js +2 -6
  133. package/lib/skill-runtime/operations/janitor.js +2 -18
  134. package/lib/skill-runtime/operations/resume.js +21 -38
  135. package/lib/skill-runtime/operations/review-records.js +265 -0
  136. package/lib/skill-runtime/operations/snapshot.js +1 -1
  137. package/lib/skill-runtime/operations/task-contract.js +524 -0
  138. package/lib/skill-runtime/operations/worker-run.js +2 -30
  139. package/lib/skill-runtime/paths.js +4 -4
  140. package/lib/skill-runtime/planner.js +25 -13
  141. package/lib/skill-runtime/query-registry.js +2 -2
  142. package/lib/skill-runtime/query.js +16 -3
  143. package/lib/skill-runtime/review-records.js +123 -0
  144. package/lib/skill-runtime/review.js +246 -11
  145. package/lib/skill-runtime/schemas.js +179 -15
  146. package/lib/skill-runtime/store.js +0 -10
  147. package/lib/skill-runtime/task-contract.js +187 -0
  148. package/lib/skill-runtime/workflow-context.js +748 -0
  149. package/package.json +7 -4
@@ -0,0 +1,748 @@
1
+ /**
2
+ * [INPUT]: 依赖 task-manifest/report/change-meta/runtime-state 等已有 artifact,接收 repoRoot/changeId/changeKey。
3
+ * [OUTPUT]: 生成 compact workflow context,告诉 PDCA/IDCA 下一步、默认读取集、可信命令与深层展开条件。
4
+ * [POS]: skill runtime 的渐进式披露查询层,只读派生状态,不拥有 workflow 规则本身。
5
+ * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
+ */
7
+
8
+ const path = require('path');
9
+
10
+ const {
11
+ getRuntimeStatePath,
12
+ getTaskManifestPath,
13
+ getReportCardPath,
14
+ getTasksMarkdownPath,
15
+ exists,
16
+ readJson,
17
+ readText
18
+ } = require('./store');
19
+ const { getIntentPrBriefPath } = require('./artifacts');
20
+ const { getChangePaths } = require('./paths');
21
+ const { deriveManifestExecutionState } = require('./planner');
22
+ const {
23
+ getApprovalState,
24
+ deriveLifecycleStage,
25
+ deriveTaskProgress,
26
+ isTaskCompletedStatus
27
+ } = require('./lifecycle');
28
+ const { namedError } = require('./errors');
29
+ const { deriveShipReadiness } = require('./readiness');
30
+ const {
31
+ buildDefaultOpenRefs,
32
+ buildMustNotForget,
33
+ buildPacketOnly,
34
+ buildSourceHashes,
35
+ buildSourceIndex,
36
+ dedupeOpenRefs,
37
+ deepOpenGroup,
38
+ openRef,
39
+ validateDeepOpenGroups,
40
+ validateOpenRefs,
41
+ withFragment
42
+ } = require('./context-index');
43
+
44
+ const RESUMABLE_TASK_STATUSES = new Set([
45
+ 'pending',
46
+ 'running',
47
+ 'in_progress',
48
+ 'active',
49
+ 'blocked',
50
+ 'needs_fix',
51
+ 'failed'
52
+ ]);
53
+
54
+ async function readWorkflowArtifact(filePath, { required = true } = {}) {
55
+ try {
56
+ const value = await readJson(filePath, null);
57
+
58
+ if (required && value === null) {
59
+ throw namedError(
60
+ 'MissingQueryArtifactError',
61
+ `Missing required query artifact: ${filePath}`,
62
+ {
63
+ artifactRefs: [filePath],
64
+ rescueAction: 'create required workflow artifacts before running this query'
65
+ }
66
+ );
67
+ }
68
+
69
+ return value;
70
+ } catch (error) {
71
+ if (error.name === 'MissingQueryArtifactError') {
72
+ throw error;
73
+ }
74
+
75
+ throw namedError(
76
+ 'InvalidQueryArtifactError',
77
+ `Invalid query artifact ${filePath}: ${error.message}`,
78
+ {
79
+ artifactRefs: [filePath],
80
+ rescueAction: 'repair or regenerate the invalid workflow artifact before running this query',
81
+ details: {
82
+ cause: error.name || 'Error'
83
+ }
84
+ }
85
+ );
86
+ }
87
+ }
88
+
89
+ function relativePath(repoRoot, filePath) {
90
+ return path.relative(repoRoot, filePath) || filePath;
91
+ }
92
+
93
+ async function maybeRelativePath(repoRoot, filePath) {
94
+ return (await exists(filePath)) ? relativePath(repoRoot, filePath) : null;
95
+ }
96
+
97
+ function dedupe(values) {
98
+ return [...new Set((values || []).filter(Boolean))];
99
+ }
100
+
101
+ function normalizeTaskReadFile(filePath, refs) {
102
+ const value = String(filePath || '').trim().replace(/^\.\//, '');
103
+ if (!value) {
104
+ return null;
105
+ }
106
+
107
+ if (path.isAbsolute(value)) {
108
+ return relativePath(refs.repoRoot, value);
109
+ }
110
+
111
+ const artifactAliases = new Map([
112
+ ['design.md', refs.relativeDesignPath],
113
+ ['analysis.md', refs.relativeAnalysisPath],
114
+ ['tasks.md', refs.relativeTasksPath],
115
+ ['task-manifest.json', refs.relativeManifestPath],
116
+ ['change-meta.json', refs.relativeChangeMetaPath],
117
+ ['report-card.json', refs.relativeReportPath],
118
+ ['change-state.json', refs.relativeStatePath]
119
+ ]);
120
+ const normalizedDir = path.dirname(value).replace(/\\/g, '/');
121
+ const basename = path.basename(value);
122
+
123
+ if (normalizedDir === '.' && artifactAliases.has(value)) {
124
+ return artifactAliases.get(value) || null;
125
+ }
126
+
127
+ if (normalizedDir === 'planning' && artifactAliases.has(basename)) {
128
+ return artifactAliases.get(basename) || null;
129
+ }
130
+
131
+ if (normalizedDir === 'review' && basename === 'report-card.json') {
132
+ return refs.relativeReportPath || null;
133
+ }
134
+
135
+ if (normalizedDir === 'meta' && basename === 'change-state.json') {
136
+ return refs.relativeStatePath || null;
137
+ }
138
+
139
+ return value;
140
+ }
141
+
142
+ function normalizeTaskReadFiles(readFiles, refs) {
143
+ return dedupe((readFiles || []).map((filePath) => normalizeTaskReadFile(filePath, refs)));
144
+ }
145
+
146
+ function isArtifactReadFile(filePath, refs) {
147
+ return [
148
+ refs.relativeDesignPath,
149
+ refs.relativeAnalysisPath,
150
+ refs.relativeTasksPath,
151
+ refs.relativeManifestPath,
152
+ refs.relativeChangeMetaPath,
153
+ refs.relativeStatePath,
154
+ refs.relativeReportPath
155
+ ].filter(Boolean).includes(filePath);
156
+ }
157
+
158
+ function summarizeTask(task) {
159
+ if (!task) {
160
+ return null;
161
+ }
162
+
163
+ return {
164
+ id: task.id,
165
+ title: task.title || task.id,
166
+ type: task.type || 'OTHER',
167
+ phase: task.phase || 1,
168
+ status: task.status || 'pending',
169
+ tddPhase: task.tddPhase || null,
170
+ dependsOn: task.dependsOn || [],
171
+ parallel: Boolean(task.parallel),
172
+ touches: task.touches || task.files || [],
173
+ files: task.files || [],
174
+ verification: task.verification || [],
175
+ evidence: task.evidence || [],
176
+ context: {
177
+ readFiles: task.context?.readFiles || [],
178
+ commands: task.context?.commands || [],
179
+ notes: task.context?.notes || []
180
+ }
181
+ };
182
+ }
183
+
184
+ function deriveTaskQueues(manifest) {
185
+ const tasks = manifest.tasks || [];
186
+ const executionState = deriveManifestExecutionState(tasks);
187
+ const activePhase = executionState.activePhase;
188
+ const completedIds = new Set(
189
+ tasks.filter((task) => isTaskCompletedStatus(task.status)).map((task) => task.id)
190
+ );
191
+ const unfinished = tasks.filter((task) => !isTaskCompletedStatus(task.status));
192
+
193
+ function waitingOn(task) {
194
+ return (task.dependsOn || []).filter((depId) => !completedIds.has(depId));
195
+ }
196
+
197
+ const activeTasks = unfinished.filter((task) => (
198
+ activePhase === null || activePhase === undefined || (task.phase || 1) === activePhase
199
+ ));
200
+
201
+ return {
202
+ activePhase,
203
+ readyTasks: activeTasks
204
+ .filter((task) => (task.status || 'pending') === 'pending' && waitingOn(task).length === 0)
205
+ .map((task) => ({
206
+ ...summarizeTask(task),
207
+ waitingOn: []
208
+ })),
209
+ runningTasks: activeTasks
210
+ .filter((task) => (task.status || 'pending') === 'running')
211
+ .map(summarizeTask),
212
+ blockedTasks: activeTasks
213
+ .filter((task) => !['pending', 'running'].includes(task.status || 'pending') || waitingOn(task).length > 0)
214
+ .map((task) => ({
215
+ ...summarizeTask(task),
216
+ waitingOn: waitingOn(task)
217
+ })),
218
+ deferredTasks: unfinished
219
+ .filter((task) => activePhase !== null && activePhase !== undefined && (task.phase || 1) !== activePhase)
220
+ .map(summarizeTask)
221
+ };
222
+ }
223
+
224
+ function selectNextTask(manifest, queues) {
225
+ const currentTaskId = manifest.currentTaskId;
226
+
227
+ if (currentTaskId) {
228
+ const currentTask = (manifest.tasks || []).find((task) => task.id === currentTaskId);
229
+ if (currentTask && RESUMABLE_TASK_STATUSES.has(currentTask.status || 'pending')) {
230
+ return currentTask;
231
+ }
232
+ }
233
+
234
+ const runningTask = queues.runningTasks[0]
235
+ || queues.blockedTasks.find((task) => RESUMABLE_TASK_STATUSES.has(task.status) && task.status !== 'pending');
236
+ if (runningTask) {
237
+ return (manifest.tasks || []).find((task) => task.id === runningTask.id) || runningTask;
238
+ }
239
+
240
+ const nextReady = queues.readyTasks[0];
241
+ if (!nextReady) {
242
+ return null;
243
+ }
244
+
245
+ return (manifest.tasks || []).find((task) => task.id === nextReady.id) || null;
246
+ }
247
+
248
+ function verificationCommandsForTask(task) {
249
+ return dedupe([
250
+ ...(task?.context?.commands || []),
251
+ ...(task?.verification || []),
252
+ ...(task?.run || [])
253
+ ]);
254
+ }
255
+
256
+ function deriveCommandsToTrust({ manifest, nextTask, progress }) {
257
+ if (nextTask) {
258
+ return verificationCommandsForTask(nextTask);
259
+ }
260
+
261
+ if (progress.totalTasks > 0 && progress.completedTasks === progress.totalTasks) {
262
+ return dedupe(
263
+ (manifest.tasks || [])
264
+ .filter((task) => isTaskCompletedStatus(task.status))
265
+ .flatMap(verificationCommandsForTask)
266
+ );
267
+ }
268
+
269
+ return [];
270
+ }
271
+
272
+ function markdownHasHeading(markdown, heading) {
273
+ const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
274
+ return new RegExp(`(^|\\n)${escaped}\\s*(\\n|$)`, 'i').test(String(markdown || ''));
275
+ }
276
+
277
+ function resolveLegacyContractFragment(markdown) {
278
+ const candidates = [
279
+ ['## Progressive Disclosure Index', 'progressive-disclosure-index'],
280
+ ['## Frozen Design Card', 'frozen-design-card'],
281
+ ['## Approved Direction', 'approved-direction'],
282
+ ['## Requirement Snapshot', 'requirement-snapshot'],
283
+ ['## Success Criteria', 'success-criteria'],
284
+ ['## Validation Strategy', 'validation-strategy'],
285
+ ['## Review Gate', 'review-gate'],
286
+ ['# DESIGN', 'design'],
287
+ ['# ANALYSIS', 'analysis']
288
+ ];
289
+
290
+ const match = candidates.find(([heading]) => markdownHasHeading(markdown, heading));
291
+ return match ? match[1] : '';
292
+ }
293
+
294
+ async function resolveCanonicalContractPath({ changeId, tasksPath, designPath, analysisPath }) {
295
+ const isFix = changeId.startsWith('FIX-');
296
+ const primaryHeading = isFix ? '## Root Cause Contract' : '## Contract Summary';
297
+ const primaryFragment = isFix ? 'root-cause-contract' : 'contract-summary';
298
+ const tasksText = await readText(tasksPath, '');
299
+
300
+ if (markdownHasHeading(tasksText, primaryHeading)) {
301
+ return {
302
+ path: tasksPath,
303
+ fragment: primaryFragment,
304
+ usesTaskContract: true
305
+ };
306
+ }
307
+
308
+ const preferredContractPaths = isFix
309
+ ? [analysisPath, designPath]
310
+ : [designPath, analysisPath];
311
+ const legacyPath = (await Promise.all(
312
+ preferredContractPaths.map(async (candidate) => ((await exists(candidate)) ? candidate : null))
313
+ )).find(Boolean);
314
+ const legacyText = legacyPath ? await readText(legacyPath, '') : '';
315
+
316
+ return {
317
+ path: legacyPath,
318
+ fragment: resolveLegacyContractFragment(legacyText),
319
+ usesTaskContract: false
320
+ };
321
+ }
322
+
323
+ function deriveWorkflowNextAction({ progress, nextTask, report, reportPath, missingVerificationCommands }) {
324
+ if (missingVerificationCommands) {
325
+ return {
326
+ skill: 'cc-plan',
327
+ action: 'repair-task-verification',
328
+ taskId: nextTask.id,
329
+ reason: 'current task has no executable verification command',
330
+ mustOpen: ['planning/task-manifest.json']
331
+ };
332
+ }
333
+
334
+ if (nextTask) {
335
+ const status = nextTask.status || 'pending';
336
+ return {
337
+ skill: 'cc-do',
338
+ action: status === 'pending' ? 'execute-current-task' : 'resume-current-task',
339
+ taskId: nextTask.id,
340
+ reason: status === 'pending'
341
+ ? 'a dependency-ready task is still pending'
342
+ : `current task ${nextTask.id} is ${status} and should be resumed before selecting new work`
343
+ };
344
+ }
345
+
346
+ if (progress.totalTasks > 0 && progress.completedTasks === progress.totalTasks) {
347
+ const readiness = report ? deriveShipReadiness(report, { reportPath }) : null;
348
+ if (readiness?.ready) {
349
+ return {
350
+ skill: 'cc-act',
351
+ action: 'ship-or-handoff',
352
+ reason: 'all tasks are complete and ship-readiness is ready'
353
+ };
354
+ }
355
+
356
+ return {
357
+ skill: 'cc-check',
358
+ action: 'build-fresh-verdict',
359
+ reason: report
360
+ ? 'all tasks are complete but ship-readiness is blocked'
361
+ : 'all tasks are complete but no report card exists',
362
+ blockers: readiness?.blockers || ['missing report-card']
363
+ };
364
+ }
365
+
366
+ return {
367
+ skill: 'cc-plan',
368
+ action: 'repair-task-graph',
369
+ reason: 'no ready task can be selected from the current manifest'
370
+ };
371
+ }
372
+
373
+ function getWorkflowContextArtifactRefs(repoRoot, changeId, options = {}) {
374
+ const change = getChangePaths(repoRoot, changeId, options);
375
+ return [
376
+ getTasksMarkdownPath(repoRoot, changeId, options),
377
+ path.join(change.planningDir, 'design.md'),
378
+ path.join(change.planningDir, 'analysis.md'),
379
+ getTaskManifestPath(repoRoot, changeId, options),
380
+ path.join(change.changeDir, 'change-meta.json'),
381
+ getRuntimeStatePath(repoRoot, changeId, options),
382
+ getReportCardPath(repoRoot, changeId, options)
383
+ ];
384
+ }
385
+
386
+ function getWorkflowContextRequiredArtifactRefs(repoRoot, changeId, options = {}) {
387
+ return [
388
+ getTaskManifestPath(repoRoot, changeId, options)
389
+ ];
390
+ }
391
+
392
+ async function getWorkflowContext(repoRoot, changeId, options = {}) {
393
+ const change = getChangePaths(repoRoot, changeId, options);
394
+ const manifestPath = getTaskManifestPath(repoRoot, changeId, options);
395
+ const reportPath = getReportCardPath(repoRoot, changeId, options);
396
+ const statePath = getRuntimeStatePath(repoRoot, changeId, options);
397
+ const tasksPath = getTasksMarkdownPath(repoRoot, changeId, options);
398
+ const changeMetaPath = path.join(change.changeDir, 'change-meta.json');
399
+ const designPath = path.join(change.planningDir, 'design.md');
400
+ const analysisPath = path.join(change.planningDir, 'analysis.md');
401
+ const contractResolution = await resolveCanonicalContractPath({
402
+ changeId,
403
+ tasksPath,
404
+ designPath,
405
+ analysisPath
406
+ });
407
+ const canonicalContractPath = contractResolution.path;
408
+
409
+ const [manifest, report, state, changeMeta] = await Promise.all([
410
+ readWorkflowArtifact(manifestPath),
411
+ readWorkflowArtifact(reportPath, { required: false }),
412
+ readWorkflowArtifact(statePath, { required: false }),
413
+ readWorkflowArtifact(changeMetaPath, { required: false })
414
+ ]);
415
+ const progress = deriveTaskProgress(manifest.tasks || []);
416
+ const queues = deriveTaskQueues(manifest);
417
+ const nextTask = selectNextTask(manifest, queues);
418
+ const relativeContractPath = canonicalContractPath ? relativePath(repoRoot, canonicalContractPath) : null;
419
+ const relativeDesignPath = await maybeRelativePath(repoRoot, designPath);
420
+ const relativeAnalysisPath = await maybeRelativePath(repoRoot, analysisPath);
421
+ const relativeManifestPath = relativePath(repoRoot, manifestPath);
422
+ const relativeTasksPath = await maybeRelativePath(repoRoot, tasksPath);
423
+ const relativeChangeMetaPath = await maybeRelativePath(repoRoot, changeMetaPath);
424
+ const relativeStatePath = await maybeRelativePath(repoRoot, statePath);
425
+ const relativeReportPath = await maybeRelativePath(repoRoot, reportPath);
426
+ const relativePrimaryContractRef = relativeContractPath && contractResolution.fragment
427
+ ? withFragment(relativeContractPath, contractResolution.fragment)
428
+ : relativeContractPath;
429
+ const relativeContractRef = relativeContractPath && contractResolution.fragment
430
+ ? withFragment(relativeContractPath, contractResolution.fragment)
431
+ : relativeContractPath;
432
+ const relativeContractIndexRef = contractResolution.usesTaskContract && relativeContractPath
433
+ ? withFragment(relativeContractPath, 'progressive-disclosure-index')
434
+ : relativeContractRef;
435
+ const refs = {
436
+ repoRoot,
437
+ relativeContractPath,
438
+ relativeContractRef,
439
+ relativeContractIndexRef,
440
+ relativePrimaryContractRef,
441
+ relativeDesignPath,
442
+ relativeAnalysisPath,
443
+ relativeTasksPath,
444
+ relativeManifestPath,
445
+ relativeChangeMetaPath,
446
+ relativeStatePath,
447
+ relativeReportPath
448
+ };
449
+ const queryCommand = [
450
+ 'cc-devflow query workflow-context',
451
+ `--change ${changeId}`,
452
+ options.changeKey ? `--change-key ${change.changeKey}` : '',
453
+ '--cwd <repo-root>'
454
+ ].filter(Boolean).join(' ');
455
+ const taskReadFiles = normalizeTaskReadFiles(nextTask?.context?.readFiles || [], refs);
456
+ const taskDeepOpen = taskReadFiles.filter((filePath) => !isArtifactReadFile(filePath, refs));
457
+ const source = await buildSourceIndex({
458
+ manifestPath,
459
+ canonicalContractPath,
460
+ tasksPath,
461
+ changeMetaPath,
462
+ reportPath,
463
+ refs
464
+ });
465
+ const defaultOpen = await validateOpenRefs(
466
+ buildDefaultOpenRefs({ nextTask, refs, source }),
467
+ { repoRoot }
468
+ );
469
+ const taskCommands = deriveCommandsToTrust({ manifest, nextTask, progress });
470
+ const missingVerificationCommands = Boolean(nextTask && taskCommands.length === 0);
471
+ const currentTaskSummary = summarizeTask(nextTask);
472
+ const mustNotForget = await buildMustNotForget({
473
+ manifest,
474
+ nextTask,
475
+ report,
476
+ source,
477
+ refs,
478
+ canonicalContractPath,
479
+ missingVerificationCommands
480
+ });
481
+ const packetOnly = await buildPacketOnly({
482
+ canonicalContractPath,
483
+ manifest,
484
+ currentTaskSummary,
485
+ nextTask,
486
+ report,
487
+ mustNotForget
488
+ });
489
+ const deepOpen = await validateDeepOpenGroups([
490
+ deepOpenGroup({
491
+ when: 'scope_or_contract_uncertain',
492
+ conditions: [
493
+ 'confidence.scope < 0.85',
494
+ "task.type in ['ARCH', 'SECURITY', 'DATA_MIGRATION']",
495
+ 'user_request_changes_scope == true',
496
+ 'do_not_redecide_missing == true'
497
+ ],
498
+ refs: [
499
+ { ref: relativeContractPath, reason: 'full contract for scope and do-not-redecide disputes' },
500
+ { ref: withFragment(relativeContractPath, 'approved-direction'), reason: 'approved direction section' },
501
+ { ref: relativeTasksPath, reason: 'task protocol and handoff context' },
502
+ { ref: withFragment(relativeChangeMetaPath, '/risk'), reason: 'risk and spec sync context' }
503
+ ].filter((entry) => entry.ref),
504
+ source
505
+ }),
506
+ deepOpenGroup({
507
+ when: 'task_or_dependency_uncertain',
508
+ conditions: [
509
+ 'currentTask == null',
510
+ 'dependency_state_unclear == true',
511
+ 'active_phase_unclear == true'
512
+ ],
513
+ refs: [
514
+ { ref: withFragment(relativeManifestPath, nextTask ? `/tasks/${nextTask.id}` : '/summary'), reason: 'task graph source of truth' },
515
+ { ref: withFragment(relativeTasksPath, nextTask ? `task.${nextTask.id}` : 'execution-handoff'), reason: 'human task block and execution protocol' }
516
+ ],
517
+ source,
518
+ command: queryCommand
519
+ }),
520
+ deepOpenGroup({
521
+ when: 'parallel_or_touch_ownership_uncertain',
522
+ conditions: [
523
+ 'task.touches.length > 3',
524
+ 'parallel_candidates.length > 1',
525
+ 'current_file_not_listed_in_task_files == true',
526
+ 'touch_paths_overlap == true'
527
+ ],
528
+ refs: [
529
+ { ref: relativeTasksPath, reason: 'task ownership and slicing details' },
530
+ { ref: relativeManifestPath, reason: 'machine task graph and touched files' }
531
+ ],
532
+ source,
533
+ command: `bash .claude/skills/cc-do/scripts/select-ready-tasks.sh --manifest ${relativeManifestPath}`
534
+ }),
535
+ deepOpenGroup({
536
+ when: 'verification_or_recovery_needed',
537
+ conditions: [
538
+ 'verification_failed == true',
539
+ 'missingVerificationCommands == true',
540
+ 'report.reroute != none',
541
+ 'runtime_log_needed == true'
542
+ ],
543
+ refs: [
544
+ { ref: relativeTasksPath, reason: 'current task status and completion marks' },
545
+ { ref: relativeReportPath, reason: 'latest review gate verdict' },
546
+ { ref: relativeManifestPath, reason: 'verification command source' }
547
+ ].filter((entry) => entry.ref),
548
+ source
549
+ }),
550
+ deepOpenGroup({
551
+ when: 'implementation_details_needed',
552
+ conditions: [
553
+ 'agent_needs_code_context == true',
554
+ 'current_file_not_listed_in_task_files == true'
555
+ ],
556
+ refs: taskDeepOpen.map((ref) => ({ ref, reason: 'selected task implementation context' })),
557
+ source
558
+ }),
559
+ deepOpenGroup({
560
+ when: 'delivery_or_ship_readiness_needed',
561
+ conditions: [
562
+ 'progress.completedTasks == progress.totalTasks',
563
+ 'nextAction.skill == cc-act',
564
+ 'ship_mode_unclear == true'
565
+ ],
566
+ refs: [
567
+ { ref: relativeReportPath, reason: 'ship-readiness verdict input' },
568
+ { ref: 'devflow/changes/<change-key>/handoff/', reason: 'delivery handoff artifacts' }
569
+ ],
570
+ source,
571
+ command: `cc-devflow query ship-readiness --change ${changeId}${options.changeKey ? ` --change-key ${change.changeKey}` : ''} --cwd <repo-root>`
572
+ })
573
+ ], { repoRoot });
574
+ const nextAction = deriveWorkflowNextAction({
575
+ progress,
576
+ nextTask,
577
+ report,
578
+ reportPath,
579
+ missingVerificationCommands
580
+ });
581
+ const sourceHashes = buildSourceHashes(source);
582
+
583
+ return {
584
+ schemaVersion: 'workflow-context.v2',
585
+ changeId: manifest.changeId || changeId,
586
+ changeKey: change.changeKey,
587
+ generatedAt: new Date().toISOString(),
588
+ source,
589
+ legacyFallback: contractResolution.usesTaskContract ? false : Boolean(canonicalContractPath),
590
+ goal: manifest.goal || state?.goal || '',
591
+ route: changeId.startsWith('FIX-') ? 'IDCA' : 'PDCA',
592
+ lifecycle: {
593
+ stage: state ? deriveLifecycleStage({
594
+ state,
595
+ manifest,
596
+ report,
597
+ hasPrBrief: await exists(getIntentPrBriefPath(repoRoot, changeId, options))
598
+ }) : null,
599
+ status: state?.status || null,
600
+ approval: state ? getApprovalState(state, manifest) : null
601
+ },
602
+ progress,
603
+ nextAction,
604
+ currentTask: currentTaskSummary,
605
+ queues,
606
+ progressiveDisclosure: {
607
+ mode: 'compact-first',
608
+ rule: 'Use this as a context index: packetOnly routes, mustNotForget guards judgment, source artifacts decide disputed facts. If you need to guess, open the referenced source first.',
609
+ contextBudget: {
610
+ packetFields: Object.keys(packetOnly).length,
611
+ defaultRefs: defaultOpen.length,
612
+ defaultCommands: taskCommands.length,
613
+ deepOpenGroups: deepOpen.length,
614
+ deepOpeners: 6
615
+ },
616
+ packetOnly,
617
+ mustNotForget,
618
+ sourceHashes,
619
+ stalenessCheck: {
620
+ policy: 'if any source hash differs from sourceHashes, rerun workflow-context before acting',
621
+ command: queryCommand
622
+ },
623
+ defaultOpen,
624
+ deepOpen,
625
+ commandsToTrust: taskCommands,
626
+ missingVerificationCommands,
627
+ manifestIssue: missingVerificationCommands
628
+ ? 'current task has no executable verification command'
629
+ : null,
630
+ failClosed: [
631
+ 'If manifest, contract, change-meta, report-card, tasks, or Git state changed after packet generation, rerun workflow-context.',
632
+ 'If current task status is running, in_progress, active, blocked, needs_fix, or failed, resume it before selecting another ready task.',
633
+ 'If verification command is missing, route to cc-plan repair-task-verification instead of cc-do.',
634
+ 'If scope, ownership, dependency, or source confidence is uncertain, open the source artifact before acting.'
635
+ ],
636
+ uncertaintyPolicy: {
637
+ mode: 'open-source-before-acting',
638
+ openOn: [
639
+ 'scope uncertainty',
640
+ 'task ambiguity',
641
+ 'dependency ambiguity',
642
+ 'missing verification',
643
+ 'changed files outside manifest',
644
+ 'test failure',
645
+ 'user changes requirement',
646
+ 'security or data migration impact'
647
+ ]
648
+ },
649
+ openWhen: await Promise.all([
650
+ {
651
+ trigger: 'the current task, dependency state, or active phase is unclear',
652
+ conditions: [
653
+ 'currentTask == null',
654
+ 'dependency_state_unclear == true',
655
+ 'active_phase_unclear == true'
656
+ ],
657
+ open: dedupeOpenRefs([
658
+ openRef(nextTask ? withFragment(relativeManifestPath, `/tasks/${nextTask.id}`) : withFragment(relativeManifestPath, '/summary'), 'task graph source of truth', source),
659
+ openRef(withFragment(relativeTasksPath, nextTask ? `task.${nextTask.id}` : 'execution-handoff'), 'task block and execution handoff', source)
660
+ ]),
661
+ command: queryCommand
662
+ },
663
+ {
664
+ trigger: 'scope, design, root cause, or do-not-redecide contract is disputed',
665
+ conditions: [
666
+ 'confidence.scope < 0.85',
667
+ 'user_request_changes_scope == true',
668
+ 'do_not_redecide_missing == true'
669
+ ],
670
+ open: dedupeOpenRefs([
671
+ openRef(relativeContractPath, 'full contract for scope and do-not-redecide disputes', source),
672
+ openRef(relativeTasksPath, 'task protocol and handoff context', source),
673
+ openRef(withFragment(relativeChangeMetaPath, '/risk'), 'risk and spec sync context', source)
674
+ ])
675
+ },
676
+ {
677
+ trigger: 'parallel dispatch, touched-path ownership, or task slicing is disputed',
678
+ conditions: [
679
+ 'parallel_candidates.length > 1',
680
+ 'touch_paths_overlap == true',
681
+ 'current_file_not_listed_in_task_files == true'
682
+ ],
683
+ open: dedupeOpenRefs([
684
+ openRef(relativeTasksPath, 'task ownership and slicing details', source),
685
+ openRef(relativeManifestPath, 'machine task graph and touched files', source)
686
+ ]),
687
+ command: `bash .claude/skills/cc-do/scripts/select-ready-tasks.sh --manifest ${relativeManifestPath}`
688
+ },
689
+ {
690
+ trigger: 'task evidence, review gates, or recovery state is needed',
691
+ conditions: [
692
+ 'verification_failed == true',
693
+ 'report.reroute != none',
694
+ 'runtime_log_needed == true'
695
+ ],
696
+ open: dedupeOpenRefs([
697
+ openRef(relativeTasksPath, 'current task status and completion marks', source),
698
+ openRef(relativeReportPath, 'latest review gate verdict', source),
699
+ openRef(relativeManifestPath, 'verification command source', source)
700
+ ])
701
+ },
702
+ {
703
+ trigger: 'implementation file details are needed for the selected task',
704
+ conditions: [
705
+ 'agent_needs_code_context == true',
706
+ 'current_file_not_listed_in_task_files == true'
707
+ ],
708
+ open: dedupeOpenRefs(taskDeepOpen.map((ref) => openRef(ref, 'selected task implementation context', source)))
709
+ },
710
+ {
711
+ trigger: 'all tasks are complete and delivery can start',
712
+ conditions: [
713
+ 'progress.completedTasks == progress.totalTasks',
714
+ 'nextAction.skill == cc-act',
715
+ 'ship_mode_unclear == true'
716
+ ],
717
+ open: dedupeOpenRefs([
718
+ openRef(relativeReportPath, 'ship-readiness verdict input', source),
719
+ openRef('devflow/changes/<change-key>/handoff/', 'delivery handoff artifacts', source)
720
+ ]),
721
+ command: `cc-devflow query ship-readiness --change ${changeId}${options.changeKey ? ` --change-key ${change.changeKey}` : ''} --cwd <repo-root>`
722
+ }
723
+ ].map(async (entry) => ({
724
+ ...entry,
725
+ open: await validateOpenRefs(entry.open, { repoRoot })
726
+ })))
727
+ },
728
+ artifactRefs: dedupe([
729
+ relativeContractPath,
730
+ relativeTasksPath,
731
+ relativeManifestPath,
732
+ relativeChangeMetaPath,
733
+ relativeReportPath
734
+ ]),
735
+ planningMeta: {
736
+ planVersion: manifest.metadata?.planVersion || null,
737
+ reqPlanSkillVersion: manifest.planningMeta?.reqPlanSkillVersion || null,
738
+ sourceCapability: changeMeta?.spec?.primaryCapability || null,
739
+ specSyncStatus: changeMeta?.spec?.syncStatus || null
740
+ }
741
+ };
742
+ }
743
+
744
+ module.exports = {
745
+ getWorkflowContext,
746
+ getWorkflowContextArtifactRefs,
747
+ getWorkflowContextRequiredArtifactRefs
748
+ };