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
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [INPUT]: 依赖 scripts/verify-artifacts.js 导出的 runVerifyArtifacts 和临时 change fixture。
|
|
3
|
+
* [OUTPUT]: 验证 verify:artifacts 只实现 C1-C10,返回稳定 exit code 和 JSON 报告。
|
|
4
|
+
* [POS]: REQ-003-minimize-workflow-artifacts T016 的 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 { runVerifyArtifacts } = require('../../../scripts/verify-artifacts');
|
|
14
|
+
|
|
15
|
+
const REPO_ROOT = path.resolve(__dirname, '../../..');
|
|
16
|
+
const VERIFY_SCRIPT = path.join(REPO_ROOT, 'scripts', 'verify-artifacts.js');
|
|
17
|
+
|
|
18
|
+
function writeJson(filePath, value) {
|
|
19
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
20
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function tasksMarkdown({ profile = 'standard', taskCount = 1, extraBody = '' } = {}) {
|
|
24
|
+
const contract = [
|
|
25
|
+
'# Tasks',
|
|
26
|
+
'',
|
|
27
|
+
'## Contract Summary',
|
|
28
|
+
'',
|
|
29
|
+
'Change: REQ-900-valid-artifacts',
|
|
30
|
+
'Mode: plan',
|
|
31
|
+
`Profile: ${profile}`,
|
|
32
|
+
'Approval: approved',
|
|
33
|
+
'',
|
|
34
|
+
'Goal:',
|
|
35
|
+
'- Keep artifact verification deterministic.',
|
|
36
|
+
'',
|
|
37
|
+
'Do Not Do:',
|
|
38
|
+
'- Do not add checks outside C1-C10.',
|
|
39
|
+
'',
|
|
40
|
+
'Approved Direction:',
|
|
41
|
+
'- Verify the minimized artifact shape.',
|
|
42
|
+
'',
|
|
43
|
+
'Acceptance:',
|
|
44
|
+
'- The verifier returns stable rule ids and exit codes.',
|
|
45
|
+
'',
|
|
46
|
+
'Verification:',
|
|
47
|
+
'',
|
|
48
|
+
'```bash',
|
|
49
|
+
'npm run verify:artifacts',
|
|
50
|
+
'```',
|
|
51
|
+
'',
|
|
52
|
+
'Risk / Escalate If:',
|
|
53
|
+
'- The rule table drifts from C1-C10.',
|
|
54
|
+
''
|
|
55
|
+
];
|
|
56
|
+
const tasks = Array.from({ length: taskCount }, (_, index) => [
|
|
57
|
+
`- [ ] T${String(index + 1).padStart(3, '0')} validate artifacts`,
|
|
58
|
+
' Goal: Exercise one verifier rule.',
|
|
59
|
+
' Files: `scripts/verify-artifacts.js`',
|
|
60
|
+
' Verification: npm run verify:artifacts',
|
|
61
|
+
` Vertical slice: Slice ${index + 1}`,
|
|
62
|
+
''
|
|
63
|
+
].join('\n'));
|
|
64
|
+
return [...contract, extraBody, '## Phase 1', '', ...tasks].join('\n');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function seedValidChange(repoRoot, options = {}) {
|
|
68
|
+
const changeKey = options.changeKey || 'REQ-900-valid-artifacts';
|
|
69
|
+
const changeDir = path.join(repoRoot, 'devflow', 'changes', changeKey);
|
|
70
|
+
const planningDir = path.join(changeDir, 'planning');
|
|
71
|
+
fs.mkdirSync(planningDir, { recursive: true });
|
|
72
|
+
fs.writeFileSync(path.join(planningDir, 'tasks.md'), tasksMarkdown(options));
|
|
73
|
+
writeJson(path.join(planningDir, 'task-manifest.json'), {
|
|
74
|
+
changeId: changeKey,
|
|
75
|
+
createdAt: '2026-05-12T00:00:00.000Z',
|
|
76
|
+
updatedAt: '2026-05-12T00:00:00.000Z',
|
|
77
|
+
currentTaskId: null,
|
|
78
|
+
tasks: [],
|
|
79
|
+
metadata: {
|
|
80
|
+
source: 'tasks.md',
|
|
81
|
+
generatedBy: 'cc-devflow task-contract',
|
|
82
|
+
planVersion: 1
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
writeJson(path.join(changeDir, 'change-meta.json'), {
|
|
86
|
+
changeId: changeKey,
|
|
87
|
+
requirementId: changeKey,
|
|
88
|
+
goal: ['Verify artifacts'],
|
|
89
|
+
acceptance: ['Verifier passes'],
|
|
90
|
+
specReference: {
|
|
91
|
+
source: 'planning/tasks.md#contract-summary',
|
|
92
|
+
change: changeKey,
|
|
93
|
+
mode: 'plan',
|
|
94
|
+
profile: options.profile || 'standard',
|
|
95
|
+
approval: 'approved'
|
|
96
|
+
},
|
|
97
|
+
_meta: {
|
|
98
|
+
generatedBy: 'cc-devflow task-contract',
|
|
99
|
+
generatedAt: '2026-05-12T00:00:00.000Z'
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
return { changeDir, planningDir, changeKey };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
describe('verify:artifacts', () => {
|
|
106
|
+
let repoRoot;
|
|
107
|
+
|
|
108
|
+
beforeEach(() => {
|
|
109
|
+
repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-verify-artifacts-'));
|
|
110
|
+
writeJson(path.join(repoRoot, 'package.json'), {
|
|
111
|
+
name: 'verify-artifacts-fixture',
|
|
112
|
+
version: '0.0.0',
|
|
113
|
+
scripts: {
|
|
114
|
+
'verify:artifacts': 'node scripts/verify-artifacts.js'
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
afterEach(() => {
|
|
120
|
+
fs.rmSync(repoRoot, { recursive: true, force: true });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const ruleCases = [
|
|
124
|
+
['C1', 2, (ctx) => fs.writeFileSync(path.join(ctx.planningDir, 'design.md'), '# legacy design\n')],
|
|
125
|
+
['C2', 2, (ctx) => fs.writeFileSync(path.join(ctx.planningDir, 'analysis.md'), '# legacy analysis\n')],
|
|
126
|
+
['C3', 2, (ctx) => {
|
|
127
|
+
const reviewDir = path.join(ctx.changeDir, 'review');
|
|
128
|
+
fs.mkdirSync(reviewDir, { recursive: true });
|
|
129
|
+
fs.writeFileSync(path.join(reviewDir, 'cc-review-report.md'), '# legacy review\n');
|
|
130
|
+
}],
|
|
131
|
+
['C4', 3, (ctx) => fs.writeFileSync(path.join(ctx.planningDir, 'tasks.md'), '# Tasks\n\n- [ ] T001 no contract\n')],
|
|
132
|
+
['C5', 3, (ctx) => writeJson(path.join(ctx.planningDir, 'task-manifest.json'), {
|
|
133
|
+
changeId: ctx.changeKey,
|
|
134
|
+
createdAt: '2026-05-12T00:00:00.000Z',
|
|
135
|
+
updatedAt: '2026-05-12T00:00:00.000Z',
|
|
136
|
+
currentTaskId: null,
|
|
137
|
+
tasks: [],
|
|
138
|
+
metadata: { source: 'tasks.md', generatedBy: 'manual', planVersion: 1 }
|
|
139
|
+
})],
|
|
140
|
+
['C6', 3, (ctx) => writeJson(path.join(ctx.changeDir, 'change-meta.json'), {
|
|
141
|
+
_meta: { generatedBy: 'manual' }
|
|
142
|
+
})],
|
|
143
|
+
['C7', 4, (ctx) => fs.writeFileSync(path.join(ctx.planningDir, 'tasks.md'), tasksMarkdown({
|
|
144
|
+
profile: 'tiny',
|
|
145
|
+
taskCount: 2
|
|
146
|
+
}))],
|
|
147
|
+
['C8', 5, (ctx) => {
|
|
148
|
+
const reviewDir = path.join(ctx.changeDir, 'review');
|
|
149
|
+
fs.mkdirSync(reviewDir, { recursive: true });
|
|
150
|
+
fs.writeFileSync(path.join(reviewDir, 'review-ledger.jsonl'), '{"event":"review-started"}\n');
|
|
151
|
+
}],
|
|
152
|
+
['C9', 5, (ctx) => {
|
|
153
|
+
const reviewDir = path.join(ctx.changeDir, 'review');
|
|
154
|
+
fs.mkdirSync(reviewDir, { recursive: true });
|
|
155
|
+
fs.writeFileSync(path.join(reviewDir, 'report-card.json'), '{"overall":"pass"}\n');
|
|
156
|
+
}],
|
|
157
|
+
['C10', 6, (ctx) => fs.writeFileSync(path.join(ctx.planningDir, 'tasks.md'), tasksMarkdown({
|
|
158
|
+
profile: 'standard',
|
|
159
|
+
extraBody: 'x'.repeat(12000)
|
|
160
|
+
}))]
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
test.each(ruleCases)('%s returns exit code %i with JSON report', async (ruleId, exitCode, mutate) => {
|
|
164
|
+
const ctx = seedValidChange(repoRoot, { changeKey: `REQ-9${ruleId.slice(1).padStart(2, '0')}-${ruleId.toLowerCase()}` });
|
|
165
|
+
mutate(ctx);
|
|
166
|
+
|
|
167
|
+
const result = await runVerifyArtifacts(repoRoot);
|
|
168
|
+
|
|
169
|
+
expect(result.code).toBe(exitCode);
|
|
170
|
+
expect(result.violations[0]).toMatchObject({ ruleId, exitCode });
|
|
171
|
+
expect(result.checks).toHaveLength(10);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('passes a valid generated change and skips grandfathered legacy directories', async () => {
|
|
175
|
+
seedValidChange(repoRoot);
|
|
176
|
+
const legacy = seedValidChange(repoRoot, { changeKey: 'REQ-001-legacy-fallback' });
|
|
177
|
+
fs.writeFileSync(path.join(legacy.planningDir, 'design.md'), '# grandfathered design\n');
|
|
178
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(legacy.planningDir, 'task-manifest.json'), 'utf8'));
|
|
179
|
+
manifest.metadata.generatedBy = 'skill:cc-plan';
|
|
180
|
+
fs.writeFileSync(path.join(legacy.planningDir, 'task-manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
181
|
+
|
|
182
|
+
const result = await runVerifyArtifacts(repoRoot);
|
|
183
|
+
|
|
184
|
+
expect(result).toMatchObject({ code: 0, ok: true });
|
|
185
|
+
expect(result.skippedLegacy.map((item) => item.changeKey)).toContain('REQ-001-legacy-fallback');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('CLI prints stdout JSON and exits with the first violation code', () => {
|
|
189
|
+
const ctx = seedValidChange(repoRoot);
|
|
190
|
+
fs.writeFileSync(path.join(ctx.planningDir, 'design.md'), '# rogue design\n');
|
|
191
|
+
|
|
192
|
+
const result = spawnSync(process.execPath, [VERIFY_SCRIPT, repoRoot], { encoding: 'utf8' });
|
|
193
|
+
const report = JSON.parse(result.stdout);
|
|
194
|
+
|
|
195
|
+
expect(result.status).toBe(2);
|
|
196
|
+
expect(report.violations[0].ruleId).toBe('C1');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('package.json exposes npm run verify:artifacts', () => {
|
|
200
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, 'package.json'), 'utf8'));
|
|
201
|
+
expect(pkg.scripts['verify:artifacts']).toBe('node scripts/verify-artifacts.js');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
@@ -11,7 +11,6 @@ const {
|
|
|
11
11
|
const {
|
|
12
12
|
getRuntimeStatePath,
|
|
13
13
|
getTaskManifestPath,
|
|
14
|
-
getCheckpointPath,
|
|
15
14
|
getEventsPath
|
|
16
15
|
} = require('../store');
|
|
17
16
|
|
|
@@ -138,7 +137,7 @@ describe('runWorkerCommand', () => {
|
|
|
138
137
|
expect(claudeCommand).toContain('--dangerously-skip-permissions');
|
|
139
138
|
});
|
|
140
139
|
|
|
141
|
-
test('runs local worker command and records only
|
|
140
|
+
test('runs local worker command and records only task status truth by default', async () => {
|
|
142
141
|
const repoRoot = setupRepoRoot('cc-devflow-worker-run-pass-');
|
|
143
142
|
const manifest = createManifest();
|
|
144
143
|
writeManifest(repoRoot, manifest);
|
|
@@ -157,7 +156,6 @@ describe('runWorkerCommand', () => {
|
|
|
157
156
|
const log = fs.readFileSync(result.sessionLogPath, 'utf8');
|
|
158
157
|
const bus = fs.readFileSync(getMessageBusPath(repoRoot, 'REQ-123'), 'utf8');
|
|
159
158
|
const assignment = fs.readFileSync(getWorkerAssignmentPath(repoRoot, 'REQ-123', workerId), 'utf8');
|
|
160
|
-
const checkpoint = JSON.parse(fs.readFileSync(getCheckpointPath(repoRoot, 'REQ-123', 'T002'), 'utf8'));
|
|
161
159
|
const nextManifest = JSON.parse(fs.readFileSync(getTaskManifestPath(repoRoot, 'REQ-123'), 'utf8'));
|
|
162
160
|
const runtimeState = JSON.parse(fs.readFileSync(getRuntimeStatePath(repoRoot, 'REQ-123'), 'utf8'));
|
|
163
161
|
|
|
@@ -167,9 +165,6 @@ describe('runWorkerCommand', () => {
|
|
|
167
165
|
expect(log).toContain('worker-ok');
|
|
168
166
|
expect(bus).toContain(`${workerId} completed T002`);
|
|
169
167
|
expect(assignment).toContain('`T002` status=`completed`');
|
|
170
|
-
expect(checkpoint.status).toBe('passed');
|
|
171
|
-
expect(checkpoint.outputExcerpt).toContain('worker-ok');
|
|
172
|
-
expect(checkpoint.error).toBe('');
|
|
173
168
|
expect(fs.existsSync(getEventsPath(repoRoot, 'REQ-123', 'T002'))).toBe(false);
|
|
174
169
|
expect(fs.existsSync(path.join(repoRoot, 'devflow/changes/REQ-123-run-delegated-worker-command/execution/workers'))).toBe(false);
|
|
175
170
|
expect(nextManifest.tasks[0].status).toBe('passed');
|
|
@@ -192,7 +187,7 @@ describe('runWorkerCommand', () => {
|
|
|
192
187
|
expect(providerPrompt).toContain('Delegated task');
|
|
193
188
|
});
|
|
194
189
|
|
|
195
|
-
test('writes failure events and
|
|
190
|
+
test('writes failure events and manifest error when local command exits non-zero', async () => {
|
|
196
191
|
const repoRoot = setupRepoRoot('cc-devflow-worker-run-fail-');
|
|
197
192
|
const manifest = createManifest();
|
|
198
193
|
writeManifest(repoRoot, manifest);
|
|
@@ -210,7 +205,6 @@ describe('runWorkerCommand', () => {
|
|
|
210
205
|
const log = fs.readFileSync(result.sessionLogPath, 'utf8');
|
|
211
206
|
const assignment = fs.readFileSync(getWorkerAssignmentPath(repoRoot, 'REQ-123', workerId), 'utf8');
|
|
212
207
|
const failedManifest = JSON.parse(fs.readFileSync(getTaskManifestPath(repoRoot, 'REQ-123'), 'utf8'));
|
|
213
|
-
const checkpoint = JSON.parse(fs.readFileSync(getCheckpointPath(repoRoot, 'REQ-123', 'T002'), 'utf8'));
|
|
214
208
|
const events = fs.readFileSync(getEventsPath(repoRoot, 'REQ-123', 'T002'), 'utf8');
|
|
215
209
|
|
|
216
210
|
expect(result.status).toBe('failed');
|
|
@@ -218,10 +212,9 @@ describe('runWorkerCommand', () => {
|
|
|
218
212
|
expect(state).toContain('Status: `failed`');
|
|
219
213
|
expect(log).toContain('boom');
|
|
220
214
|
expect(assignment).toContain('`T002` status=`failed`');
|
|
221
|
-
expect(checkpoint.status).toBe('failed');
|
|
222
|
-
expect(checkpoint.error).toContain('boom');
|
|
223
215
|
expect(events).toContain('worker_run_failed');
|
|
224
216
|
expect(failedManifest.tasks[0].status).toBe('failed');
|
|
217
|
+
expect(failedManifest.tasks[0].lastError).toContain('boom');
|
|
225
218
|
});
|
|
226
219
|
|
|
227
220
|
test('updates only the selected task when a worker owns multiple assignments', async () => {
|
|
@@ -277,6 +270,6 @@ describe('runWorkerCommand', () => {
|
|
|
277
270
|
});
|
|
278
271
|
|
|
279
272
|
expect(fs.existsSync(path.join(repoRoot, 'blocked.txt'))).toBe(false);
|
|
280
|
-
expect(fs.existsSync(
|
|
273
|
+
expect(fs.existsSync(getEventsPath(repoRoot, 'REQ-123', 'T002'))).toBe(false);
|
|
281
274
|
});
|
|
282
275
|
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [INPUT]: 依赖 getWorkflowContext 查询 seam、仓库内真实 legacy workflow artifacts。
|
|
3
|
+
* [OUTPUT]: 验证 legacy design.md fallback 在 compact context 中仍是一等合同入口。
|
|
4
|
+
* [POS]: REQ-003-minimize-workflow-artifacts T010 的 Red/Green 证据。
|
|
5
|
+
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const { getWorkflowContext } = require('../query');
|
|
11
|
+
|
|
12
|
+
const repoRoot = path.resolve(__dirname, '../../..');
|
|
13
|
+
|
|
14
|
+
describe('workflow-context legacy fallback refs', () => {
|
|
15
|
+
test.each([
|
|
16
|
+
['REQ-002', 'REQ-002-unified-roadmap-truth']
|
|
17
|
+
])('%s keeps design.md as the first fallback contract ref', async (changeId, changeKey) => {
|
|
18
|
+
const context = await getWorkflowContext(repoRoot, changeId, { changeKey });
|
|
19
|
+
const designRef = `devflow/changes/${changeKey}/planning/design.md#approved-direction`;
|
|
20
|
+
|
|
21
|
+
expect(context.legacyFallback).toBe(true);
|
|
22
|
+
expect(context.source.contract.path).toBe(`devflow/changes/${changeKey}/planning/design.md`);
|
|
23
|
+
expect(context.progressiveDisclosure.defaultOpen[0]).toEqual(expect.objectContaining({
|
|
24
|
+
ref: designRef,
|
|
25
|
+
reason: 'primary task contract',
|
|
26
|
+
exists: true
|
|
27
|
+
}));
|
|
28
|
+
expect(context.progressiveDisclosure.defaultOpen.every((entry) => entry.exists === true))
|
|
29
|
+
.toBe(true);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [INPUT]: 依赖 getWorkflowContext 查询 seam、真实临时 workflow artifacts。
|
|
3
|
+
* [OUTPUT]: 验证 workflow-context 对新 tasks.md contract refs 的默认打开顺序。
|
|
4
|
+
* [POS]: REQ-003-minimize-workflow-artifacts T009 的 Red/Green 证据。
|
|
5
|
+
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const { getWorkflowContext } = require('../query');
|
|
13
|
+
const { getTaskManifestPath } = require('../store');
|
|
14
|
+
|
|
15
|
+
function writeJson(filePath, value) {
|
|
16
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
17
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('workflow-context contract refs', () => {
|
|
21
|
+
test.each([
|
|
22
|
+
['REQ-130', '## Contract Summary', 'contract-summary', 'PDCA'],
|
|
23
|
+
['FIX-130', '## Root Cause Contract', 'root-cause-contract', 'IDCA']
|
|
24
|
+
])('primary ref is tasks.md#%s for a new change', async (
|
|
25
|
+
changeId,
|
|
26
|
+
contractHeading,
|
|
27
|
+
fragment,
|
|
28
|
+
route
|
|
29
|
+
) => {
|
|
30
|
+
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-workflow-context-'));
|
|
31
|
+
const manifestPath = getTaskManifestPath(repoRoot, changeId);
|
|
32
|
+
const planningDir = path.dirname(manifestPath);
|
|
33
|
+
const relativeTasksPath = path.relative(repoRoot, path.join(planningDir, 'tasks.md'));
|
|
34
|
+
|
|
35
|
+
fs.mkdirSync(planningDir, { recursive: true });
|
|
36
|
+
fs.writeFileSync(
|
|
37
|
+
path.join(planningDir, 'tasks.md'),
|
|
38
|
+
[
|
|
39
|
+
'# TASKS',
|
|
40
|
+
'',
|
|
41
|
+
contractHeading,
|
|
42
|
+
'',
|
|
43
|
+
`Change: ${changeId}`,
|
|
44
|
+
'Mode: plan',
|
|
45
|
+
'Profile: standard',
|
|
46
|
+
contractHeading.includes('Root Cause') ? 'Diagnosis: Keep root-cause truth in tasks.md.' : 'Approval: approved',
|
|
47
|
+
'',
|
|
48
|
+
'## Phase 1: Execute',
|
|
49
|
+
'',
|
|
50
|
+
'- [ ] T001 Execute new contract'
|
|
51
|
+
].join('\n')
|
|
52
|
+
);
|
|
53
|
+
writeJson(manifestPath, {
|
|
54
|
+
changeId,
|
|
55
|
+
goal: 'Expose new contract refs',
|
|
56
|
+
createdAt: '2026-05-12T01:00:00.000Z',
|
|
57
|
+
updatedAt: '2026-05-12T01:05:00.000Z',
|
|
58
|
+
currentTaskId: 'T001',
|
|
59
|
+
tasks: [
|
|
60
|
+
{
|
|
61
|
+
id: 'T001',
|
|
62
|
+
title: 'Execute new contract',
|
|
63
|
+
type: 'OTHER',
|
|
64
|
+
phase: 1,
|
|
65
|
+
dependsOn: [],
|
|
66
|
+
run: ['npm test -- lib/skill-runtime/__tests__/workflow-context.test.js'],
|
|
67
|
+
verification: ['npm test -- lib/skill-runtime/__tests__/workflow-context.test.js'],
|
|
68
|
+
context: {
|
|
69
|
+
readFiles: ['tasks.md'],
|
|
70
|
+
commands: ['npm test -- lib/skill-runtime/__tests__/workflow-context.test.js'],
|
|
71
|
+
notes: []
|
|
72
|
+
},
|
|
73
|
+
status: 'pending',
|
|
74
|
+
attempts: 0,
|
|
75
|
+
maxRetries: 1
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
metadata: {
|
|
79
|
+
source: 'tasks.md',
|
|
80
|
+
generatedBy: 'test',
|
|
81
|
+
planVersion: 1
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const context = await getWorkflowContext(repoRoot, changeId);
|
|
86
|
+
|
|
87
|
+
expect(context.route).toBe(route);
|
|
88
|
+
expect(context.legacyFallback).toBe(false);
|
|
89
|
+
expect(context.source.contract.path).toBe(relativeTasksPath);
|
|
90
|
+
expect(context.progressiveDisclosure.defaultOpen[0]).toEqual(expect.objectContaining({
|
|
91
|
+
ref: `${relativeTasksPath}#${fragment}`,
|
|
92
|
+
exists: true,
|
|
93
|
+
reason: 'primary task contract'
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
fs.rmSync(repoRoot, { recursive: true, force: true });
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -16,10 +16,6 @@ function getIntentArtifactsDir(repoRoot, goalId, options = {}) {
|
|
|
16
16
|
return getChangePaths(repoRoot, goalId, options).executionDir;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
function getIntentCheckpointsDir(repoRoot, goalId, options = {}) {
|
|
20
|
-
return getChangePaths(repoRoot, goalId, options).tasksDir;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
19
|
function getIntentResumeIndexPath(repoRoot, goalId, options = {}) {
|
|
24
20
|
return `${getChangePaths(repoRoot, goalId, options).handoffDir}/resume-index.md`;
|
|
25
21
|
}
|
|
@@ -83,7 +79,6 @@ async function ensureIntentScaffold(repoRoot, goalId, options = {}) {
|
|
|
83
79
|
module.exports = {
|
|
84
80
|
getIntentDir,
|
|
85
81
|
getIntentArtifactsDir,
|
|
86
|
-
getIntentCheckpointsDir,
|
|
87
82
|
getIntentResumeIndexPath,
|
|
88
83
|
getIntentPrBriefPath,
|
|
89
84
|
getIntentStatusPath,
|