cc-devflow 4.5.11 → 4.5.13
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 +18 -0
- package/.claude/skills/cc-act/PLAYBOOK.md +17 -269
- package/.claude/skills/cc-act/SKILL.md +38 -425
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +2 -13
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +1 -9
- package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +21 -177
- package/.claude/skills/cc-act/references/closure-contract.md +12 -63
- package/.claude/skills/cc-act/references/git-commit-guidelines.md +5 -5
- package/.claude/skills/cc-act/scripts/cc-act-common.sh +5 -322
- package/.claude/skills/cc-act/scripts/detect-ship-target.sh +11 -2
- package/.claude/skills/cc-act/scripts/inspect-git-index.sh +58 -0
- package/.claude/skills/cc-act/scripts/render-pr-brief.sh +40 -440
- package/.claude/skills/cc-act/scripts/verify-act-gate.sh +10 -50
- package/.claude/skills/cc-check/CHANGELOG.md +18 -0
- package/.claude/skills/cc-check/PLAYBOOK.md +19 -273
- package/.claude/skills/cc-check/SKILL.md +33 -456
- package/.claude/skills/cc-check/references/review-contract.md +12 -147
- package/.claude/skills/cc-dev/CHANGELOG.md +15 -0
- package/.claude/skills/cc-dev/PLAYBOOK.md +1 -1
- package/.claude/skills/cc-dev/SKILL.md +52 -137
- package/.claude/skills/cc-dev/scripts/resolve-cc-devflow.sh +181 -0
- package/.claude/skills/cc-do/CHANGELOG.md +11 -0
- package/.claude/skills/cc-do/PLAYBOOK.md +19 -113
- package/.claude/skills/cc-do/SKILL.md +39 -245
- package/.claude/skills/cc-do/references/execution-recovery.md +15 -109
- package/.claude/skills/cc-do/scripts/cc-do-common.sh +5 -57
- package/.claude/skills/cc-do/scripts/check-task-status.sh +35 -65
- package/.claude/skills/cc-do/scripts/mark-task-complete.sh +9 -46
- package/.claude/skills/cc-do/scripts/select-ready-tasks.sh +29 -97
- package/.claude/skills/cc-investigate/CHANGELOG.md +16 -0
- package/.claude/skills/cc-investigate/PLAYBOOK.md +20 -180
- package/.claude/skills/cc-investigate/SKILL.md +64 -246
- package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -98
- package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -218
- package/.claude/skills/cc-next/CHANGELOG.md +6 -0
- package/.claude/skills/cc-next/PLAYBOOK.md +12 -8
- package/.claude/skills/cc-next/SKILL.md +34 -140
- package/.claude/skills/cc-plan/CHANGELOG.md +16 -0
- package/.claude/skills/cc-plan/PLAYBOOK.md +22 -161
- package/.claude/skills/cc-plan/SKILL.md +45 -295
- package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +30 -228
- package/.claude/skills/cc-plan/references/planning-contract.md +24 -161
- package/.claude/skills/cc-plan/scripts/next-change-key.sh +8 -44
- package/.claude/skills/cc-plan/scripts/parse-task-dependencies.js +2 -2
- package/.claude/skills/cc-plan/scripts/validate-scope.sh +1 -1
- package/.claude/skills/cc-pr-land/SKILL.md +14 -114
- package/.claude/skills/cc-pr-review/CHANGELOG.md +4 -0
- package/.claude/skills/cc-pr-review/SKILL.md +20 -103
- package/.claude/skills/cc-review/CHANGELOG.md +23 -0
- package/.claude/skills/cc-review/PLAYBOOK.md +13 -86
- package/.claude/skills/cc-review/SKILL.md +67 -238
- package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +2 -2
- package/.claude/skills/cc-review/references/implementation-review-branch.md +7 -147
- package/.claude/skills/cc-review/references/plan-review-branch.md +5 -147
- package/.claude/skills/cc-review/references/review-methods.md +10 -218
- package/.claude/skills/cc-review/scripts/collect-review-context.sh +4 -63
- package/.claude/skills/cc-roadmap/PLAYBOOK.md +1 -1
- package/.claude/skills/cc-roadmap/SKILL.md +3 -3
- package/.claude/skills/cc-simplify/CHANGELOG.md +7 -0
- package/.claude/skills/cc-simplify/SKILL.md +26 -21
- package/.claude/skills/cc-spec-init/PLAYBOOK.md +12 -48
- package/.claude/skills/cc-spec-init/SKILL.md +29 -132
- package/.claude/skills/cc-spec-init/references/spec-contract.md +8 -17
- package/CHANGELOG.md +21 -0
- package/bin/cc-devflow-cli.js +20 -260
- package/bin/cc-devflow.js +44 -7
- package/docs/commands/README.md +1 -1
- package/docs/commands/README.zh-CN.md +1 -1
- package/docs/examples/README.md +1 -1
- package/docs/examples/START-HERE.md +14 -15
- package/docs/examples/example-bindings.json +11 -11
- package/docs/examples/full-design-blocked/README.md +4 -6
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/{planning/tasks.md → task.md} +20 -15
- package/docs/examples/local-handoff/README.md +8 -11
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/pr-brief.md +31 -0
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/{planning/tasks.md → task.md} +18 -13
- package/docs/examples/pdca-loop/README.md +6 -9
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +9 -11
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/{planning/tasks.md → task.md} +18 -13
- package/docs/examples/scripts/check-example-bindings.sh +11 -62
- package/docs/guides/artifact-contract.md +10 -40
- package/docs/guides/getting-started.md +8 -8
- package/docs/guides/getting-started.zh-CN.md +8 -8
- package/docs/guides/minimize-artifacts.md +16 -130
- package/docs/guides/project-postmortem.md +14 -71
- package/lib/compiler/__tests__/skills-registry.test.js +9 -8
- package/lib/compiler/resource-copier.js +29 -0
- package/lib/skill-runtime/__tests__/archive-change.test.js +2 -2
- package/lib/skill-runtime/__tests__/benchmark-skills.test.js +3 -3
- package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +14 -4
- package/lib/skill-runtime/errors.js +3 -3
- package/lib/skill-runtime/index.js +5 -23
- package/lib/skill-runtime/paths.js +5 -52
- package/lib/skill-runtime/query-registry.js +4 -4
- package/lib/skill-runtime/query.js +89 -201
- package/lib/skill-runtime/store.js +4 -40
- package/lib/skill-runtime/trace.js +2 -2
- package/package.json +2 -5
- package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +0 -29
- package/.claude/skills/cc-act/assets/RELEASE_NOTE_TEMPLATE.md +0 -54
- package/.claude/skills/cc-act/scripts/generate-status-report.sh +0 -92
- package/.claude/skills/cc-act/scripts/sync-act-docs.sh +0 -355
- package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +0 -234
- package/.claude/skills/cc-check/scripts/render-report-card.js +0 -438
- package/.claude/skills/cc-check/scripts/verify-gate.sh +0 -85
- package/.claude/skills/cc-do/scripts/build-task-context.sh +0 -175
- package/.claude/skills/cc-do/scripts/record-review-decision.sh +0 -88
- package/.claude/skills/cc-do/scripts/recover-workflow.sh +0 -82
- package/.claude/skills/cc-do/scripts/run-problem-analysis.sh +0 -70
- package/.claude/skills/cc-do/scripts/verify-task-gates.sh +0 -109
- package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +0 -92
- package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +0 -224
- package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +0 -178
- package/.claude/skills/cc-spec-init/assets/CHANGE_META_TEMPLATE.json +0 -28
- package/.claude/skills/cc-spec-init/scripts/validate-spec-links.sh +0 -45
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +0 -234
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +0 -488
- package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +0 -189
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/resume-index.md +0 -39
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/status.md +0 -29
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +0 -123
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +0 -292
- package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +0 -136
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/status.md +0 -29
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +0 -124
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +0 -292
- package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +0 -136
- package/docs/get-shit-done-strategy-audit.md +0 -518
- package/docs/skill-runtime-migration.md +0 -46
- package/lib/skill-runtime/__tests__/approve.test.js +0 -92
- package/lib/skill-runtime/__tests__/autopilot.test.js +0 -253
- package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +0 -165
- package/lib/skill-runtime/__tests__/delegation.test.js +0 -97
- package/lib/skill-runtime/__tests__/dispatch.test.js +0 -237
- package/lib/skill-runtime/__tests__/intent.test.js +0 -203
- package/lib/skill-runtime/__tests__/lifecycle.test.js +0 -169
- package/lib/skill-runtime/__tests__/planner.tdd.test.js +0 -331
- package/lib/skill-runtime/__tests__/prepare-pr.test.js +0 -126
- package/lib/skill-runtime/__tests__/query.test.js +0 -860
- package/lib/skill-runtime/__tests__/readiness.test.js +0 -53
- package/lib/skill-runtime/__tests__/release.test.js +0 -85
- package/lib/skill-runtime/__tests__/review-check-integration.test.js +0 -148
- package/lib/skill-runtime/__tests__/review-records.test.js +0 -619
- package/lib/skill-runtime/__tests__/runtime.integration.test.js +0 -351
- package/lib/skill-runtime/__tests__/schemas.test.js +0 -337
- package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +0 -137
- package/lib/skill-runtime/__tests__/task-contract.test.js +0 -874
- package/lib/skill-runtime/__tests__/team-state.test.js +0 -51
- package/lib/skill-runtime/__tests__/verify-artifacts.test.js +0 -203
- package/lib/skill-runtime/__tests__/worker-run.test.js +0 -275
- package/lib/skill-runtime/__tests__/worker.test.js +0 -56
- package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +0 -31
- package/lib/skill-runtime/__tests__/workflow-context.test.js +0 -98
- package/lib/skill-runtime/artifacts.js +0 -88
- package/lib/skill-runtime/context-index.js +0 -545
- package/lib/skill-runtime/delegation.js +0 -533
- package/lib/skill-runtime/intent.js +0 -309
- package/lib/skill-runtime/lifecycle.js +0 -294
- package/lib/skill-runtime/operations/CLAUDE.md +0 -19
- package/lib/skill-runtime/operations/approve.js +0 -81
- package/lib/skill-runtime/operations/autopilot-core.js +0 -337
- package/lib/skill-runtime/operations/autopilot-execution.js +0 -307
- package/lib/skill-runtime/operations/autopilot-shared.js +0 -48
- package/lib/skill-runtime/operations/autopilot.js +0 -163
- package/lib/skill-runtime/operations/dispatch.js +0 -416
- package/lib/skill-runtime/operations/init.js +0 -60
- package/lib/skill-runtime/operations/janitor.js +0 -61
- package/lib/skill-runtime/operations/plan.js +0 -59
- package/lib/skill-runtime/operations/prepare-pr.js +0 -25
- package/lib/skill-runtime/operations/release.js +0 -99
- package/lib/skill-runtime/operations/resume.js +0 -126
- package/lib/skill-runtime/operations/review-records.js +0 -265
- package/lib/skill-runtime/operations/snapshot.js +0 -45
- package/lib/skill-runtime/operations/task-contract.js +0 -593
- package/lib/skill-runtime/operations/verify.js +0 -170
- package/lib/skill-runtime/operations/worker-run.js +0 -531
- package/lib/skill-runtime/operations/worker.js +0 -33
- package/lib/skill-runtime/planner.js +0 -539
- package/lib/skill-runtime/readiness.js +0 -84
- package/lib/skill-runtime/review-records.js +0 -123
- package/lib/skill-runtime/review.js +0 -855
- package/lib/skill-runtime/schemas.js +0 -746
- package/lib/skill-runtime/task-contract.js +0 -188
- package/lib/skill-runtime/team-state.js +0 -122
- package/lib/skill-runtime/workflow-context.js +0 -748
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TDD Order Validation Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests for Constitution Article VI enforcement in planner.js
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const fs = require('fs');
|
|
8
|
-
const os = require('os');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
const { spawnSync } = require('child_process');
|
|
11
|
-
|
|
12
|
-
const { parseTasksMarkdown, createTaskManifest, deriveManifestExecutionState } = require('../planner');
|
|
13
|
-
|
|
14
|
-
describe('TDD Order Validation', () => {
|
|
15
|
-
describe('Rich task metadata parsing', () => {
|
|
16
|
-
test('should preserve phase, files, acceptance, verification, evidence and context', () => {
|
|
17
|
-
const markdown = `
|
|
18
|
-
## Phase 1: Foundation
|
|
19
|
-
|
|
20
|
-
- [ ] T001 [TEST] Counter behavior (dependsOn:none) \`src/counter.test.ts\`
|
|
21
|
-
Goal: prove the current behavior is broken first
|
|
22
|
-
Files: \`src/counter.test.ts\`
|
|
23
|
-
Read first: \`design.md\`, \`tasks.md\`
|
|
24
|
-
Verification: npm test -- src/counter.test.ts
|
|
25
|
-
Evidence: failing output, screenshot
|
|
26
|
-
|
|
27
|
-
- [ ] T002 [P] [IMPL] Counter behavior (dependsOn:T001) \`src/counter.ts\`
|
|
28
|
-
Goal: make the counter test pass
|
|
29
|
-
Files: \`src/counter.ts\`
|
|
30
|
-
Read first: \`design.md\`, \`src/counter.test.ts\`
|
|
31
|
-
Verification: npm test -- src/counter.test.ts
|
|
32
|
-
Evidence: passing output, review notes
|
|
33
|
-
`.trim();
|
|
34
|
-
|
|
35
|
-
const tasks = parseTasksMarkdown(markdown);
|
|
36
|
-
|
|
37
|
-
expect(tasks).toHaveLength(2);
|
|
38
|
-
expect(tasks[0]).toMatchObject({
|
|
39
|
-
id: 'T001',
|
|
40
|
-
phase: 1,
|
|
41
|
-
parallel: false,
|
|
42
|
-
type: 'TEST',
|
|
43
|
-
files: ['src/counter.test.ts'],
|
|
44
|
-
touches: ['src/counter.test.ts'],
|
|
45
|
-
acceptance: ['prove the current behavior is broken first'],
|
|
46
|
-
verification: ['npm test -- src/counter.test.ts'],
|
|
47
|
-
evidence: ['failing output', 'screenshot'],
|
|
48
|
-
reviews: { spec: 'pending', code: 'pending' }
|
|
49
|
-
});
|
|
50
|
-
expect(tasks[0].checks).toContain('npm test -- src/counter.test.ts');
|
|
51
|
-
expect(tasks[0].context.readFiles).toEqual(['design.md', 'tasks.md']);
|
|
52
|
-
expect(tasks[0].context.commands).toContain('npm test -- src/counter.test.ts');
|
|
53
|
-
|
|
54
|
-
expect(tasks[1]).toMatchObject({
|
|
55
|
-
id: 'T002',
|
|
56
|
-
phase: 1,
|
|
57
|
-
parallel: true,
|
|
58
|
-
type: 'IMPL',
|
|
59
|
-
files: ['src/counter.ts'],
|
|
60
|
-
touches: ['src/counter.ts'],
|
|
61
|
-
acceptance: ['make the counter test pass'],
|
|
62
|
-
verification: ['npm test -- src/counter.test.ts'],
|
|
63
|
-
evidence: ['passing output', 'review notes']
|
|
64
|
-
});
|
|
65
|
-
expect(tasks[1].context.readFiles).toEqual(['design.md', 'src/counter.test.ts']);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('should quote generated run command titles as shell data', () => {
|
|
69
|
-
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-planner-shell-'));
|
|
70
|
-
const markerPath = path.join(repoRoot, 'pwned');
|
|
71
|
-
const markdown = `
|
|
72
|
-
- [ ] T001 hostile title " && touch ${markerPath} && echo "
|
|
73
|
-
`.trim();
|
|
74
|
-
|
|
75
|
-
const [task] = parseTasksMarkdown(markdown);
|
|
76
|
-
const result = spawnSync(task.run[0], {
|
|
77
|
-
cwd: repoRoot,
|
|
78
|
-
shell: true,
|
|
79
|
-
encoding: 'utf8'
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
expect(result.status).toBe(0);
|
|
83
|
-
expect(result.stdout).toContain('hostile title');
|
|
84
|
-
expect(fs.existsSync(markerPath)).toBe(false);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('should backfill minimum metadata for TEST and IMPL tasks from plain TASKS lines', () => {
|
|
88
|
-
const markdown = `
|
|
89
|
-
## Phase 1: Build
|
|
90
|
-
|
|
91
|
-
- [ ] T001 [TEST] Counter behavior \`src/counter.test.ts\`
|
|
92
|
-
- [ ] T002 [IMPL] Counter behavior (dependsOn:T001) \`src/counter.ts\`
|
|
93
|
-
`.trim();
|
|
94
|
-
|
|
95
|
-
const tasks = parseTasksMarkdown(markdown);
|
|
96
|
-
|
|
97
|
-
expect(tasks[0].acceptance).toEqual(['Prove Counter behavior fails before implementation']);
|
|
98
|
-
expect(tasks[0].verification).toEqual(['npm test -- src/counter.test.ts']);
|
|
99
|
-
expect(tasks[0].evidence).toEqual(['failing test output']);
|
|
100
|
-
expect(tasks[0].context.readFiles).toEqual(['design.md', 'tasks.md', 'src/counter.test.ts']);
|
|
101
|
-
|
|
102
|
-
expect(tasks[1].acceptance).toEqual(['Make Counter behavior pass with the smallest implementation']);
|
|
103
|
-
expect(tasks[1].verification).toEqual(['echo "verify T002"']);
|
|
104
|
-
expect(tasks[1].evidence).toEqual(['passing test output']);
|
|
105
|
-
expect(tasks[1].context.readFiles).toEqual(['design.md']);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
describe('Manifest execution state', () => {
|
|
110
|
-
test('createTaskManifest reads tasks.md when legacy design.md is absent', async () => {
|
|
111
|
-
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-planner-new-contract-'));
|
|
112
|
-
const changeId = 'REQ-322';
|
|
113
|
-
const changeKey = 'REQ-322-new-contract';
|
|
114
|
-
const tasksPath = path.join(repoRoot, 'devflow', 'changes', changeKey, 'planning', 'tasks.md');
|
|
115
|
-
fs.mkdirSync(path.dirname(tasksPath), { recursive: true });
|
|
116
|
-
fs.writeFileSync(
|
|
117
|
-
tasksPath,
|
|
118
|
-
[
|
|
119
|
-
'## Phase 1: Build',
|
|
120
|
-
'',
|
|
121
|
-
'- [ ] T001 [TEST] Counter behavior `src/counter.test.ts`',
|
|
122
|
-
'- [ ] T002 [IMPL] Counter behavior (dependsOn:T001) `src/counter.ts`'
|
|
123
|
-
].join('\n')
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
const manifest = await createTaskManifest({
|
|
127
|
-
repoRoot,
|
|
128
|
-
changeId,
|
|
129
|
-
changeKey,
|
|
130
|
-
goal: 'Exercise new contract readFiles',
|
|
131
|
-
overwrite: true
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
expect(manifest.tasks[0].context.readFiles).toEqual(['tasks.md', 'src/counter.test.ts']);
|
|
135
|
-
expect(manifest.tasks[1].context.readFiles).toEqual(['tasks.md']);
|
|
136
|
-
|
|
137
|
-
fs.rmSync(repoRoot, { recursive: true, force: true });
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('createTaskManifest keeps design.md when legacy design.md exists', async () => {
|
|
141
|
-
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-planner-legacy-contract-'));
|
|
142
|
-
const changeId = 'REQ-323';
|
|
143
|
-
const changeKey = 'REQ-323-legacy-contract';
|
|
144
|
-
const planningDir = path.join(repoRoot, 'devflow', 'changes', changeKey, 'planning');
|
|
145
|
-
fs.mkdirSync(planningDir, { recursive: true });
|
|
146
|
-
fs.writeFileSync(path.join(planningDir, 'design.md'), '# DESIGN\n');
|
|
147
|
-
fs.writeFileSync(
|
|
148
|
-
path.join(planningDir, 'tasks.md'),
|
|
149
|
-
[
|
|
150
|
-
'## Phase 1: Build',
|
|
151
|
-
'',
|
|
152
|
-
'- [ ] T001 [TEST] Counter behavior `src/counter.test.ts`',
|
|
153
|
-
'- [ ] T002 [IMPL] Counter behavior (dependsOn:T001) `src/counter.ts`'
|
|
154
|
-
].join('\n')
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
const manifest = await createTaskManifest({
|
|
158
|
-
repoRoot,
|
|
159
|
-
changeId,
|
|
160
|
-
changeKey,
|
|
161
|
-
goal: 'Exercise legacy contract readFiles',
|
|
162
|
-
overwrite: true
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
expect(manifest.tasks[0].context.readFiles).toEqual(['design.md', 'tasks.md', 'src/counter.test.ts']);
|
|
166
|
-
expect(manifest.tasks[1].context.readFiles).toEqual(['design.md']);
|
|
167
|
-
|
|
168
|
-
fs.rmSync(repoRoot, { recursive: true, force: true });
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test('should derive active phase and current task from ready tasks', () => {
|
|
172
|
-
const state = deriveManifestExecutionState([
|
|
173
|
-
{ id: 'T001', phase: 1, status: 'passed', dependsOn: [] },
|
|
174
|
-
{ id: 'T002', phase: 2, status: 'pending', dependsOn: ['T001'] },
|
|
175
|
-
{ id: 'T003', phase: 2, status: 'pending', dependsOn: ['T002'] }
|
|
176
|
-
]);
|
|
177
|
-
|
|
178
|
-
expect(state).toEqual({
|
|
179
|
-
currentTaskId: 'T002',
|
|
180
|
-
activePhase: 2
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
test('should write currentTaskId while deriving activePhase from the task graph', async () => {
|
|
185
|
-
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-planner-'));
|
|
186
|
-
const changeId = 'REQ-321';
|
|
187
|
-
const tasksPath = path.join(repoRoot, 'devflow', 'changes', `${changeId}-change`, 'planning', 'tasks.md');
|
|
188
|
-
fs.mkdirSync(path.dirname(tasksPath), { recursive: true });
|
|
189
|
-
fs.writeFileSync(
|
|
190
|
-
tasksPath,
|
|
191
|
-
[
|
|
192
|
-
'## Phase 1: Foundation',
|
|
193
|
-
'',
|
|
194
|
-
'- [x] T001 [TEST] Setup smoke test `src/setup.test.ts`',
|
|
195
|
-
'- [ ] T002 [IMPL] Setup smoke test (dependsOn:T001) `src/setup.ts`',
|
|
196
|
-
'',
|
|
197
|
-
'## Phase 2: Verify',
|
|
198
|
-
'',
|
|
199
|
-
'- [ ] T003 Run final verification'
|
|
200
|
-
].join('\n')
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
const manifest = await createTaskManifest({
|
|
204
|
-
repoRoot,
|
|
205
|
-
changeId,
|
|
206
|
-
goal: 'Exercise manifest execution state',
|
|
207
|
-
overwrite: true
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
expect(manifest.activePhase).toBeUndefined();
|
|
211
|
-
expect(manifest.currentTaskId).toBe('T002');
|
|
212
|
-
expect(manifest.tasks[1].reviews).toEqual({ spec: 'pending', code: 'pending' });
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
describe('Valid TDD sequences', () => {
|
|
217
|
-
test('should accept TEST before IMPL with correct dependency', () => {
|
|
218
|
-
const markdown = `
|
|
219
|
-
- [ ] T001 [TEST] 用户登录功能测试
|
|
220
|
-
- [ ] T002 [IMPL] 用户登录功能实现 (dependsOn:T001)
|
|
221
|
-
`.trim();
|
|
222
|
-
|
|
223
|
-
expect(() => parseTasksMarkdown(markdown)).not.toThrow();
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
test('should accept multiple TEST-IMPL pairs', () => {
|
|
227
|
-
const markdown = `
|
|
228
|
-
- [ ] T001 [TEST] 用户登录测试
|
|
229
|
-
- [ ] T002 [IMPL] 用户登录实现 (dependsOn:T001)
|
|
230
|
-
- [ ] T003 [TEST] 用户注册测试 [P]
|
|
231
|
-
- [ ] T004 [IMPL] 用户注册实现 (dependsOn:T003)
|
|
232
|
-
`.trim();
|
|
233
|
-
|
|
234
|
-
expect(() => parseTasksMarkdown(markdown)).not.toThrow();
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
test('should accept tasks without TEST/IMPL markers', () => {
|
|
238
|
-
const markdown = `
|
|
239
|
-
- [ ] T001 初始化项目结构
|
|
240
|
-
- [ ] T002 配置开发环境
|
|
241
|
-
`.trim();
|
|
242
|
-
|
|
243
|
-
expect(() => parseTasksMarkdown(markdown)).not.toThrow();
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
describe('TDD violations', () => {
|
|
248
|
-
test('should reject IMPL without corresponding TEST', () => {
|
|
249
|
-
const markdown = `
|
|
250
|
-
- [ ] T001 [IMPL] 用户登录功能实现
|
|
251
|
-
`.trim();
|
|
252
|
-
|
|
253
|
-
expect(() => parseTasksMarkdown(markdown)).toThrow(
|
|
254
|
-
/missing corresponding TEST task/
|
|
255
|
-
);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
test('should reject IMPL not depending on TEST', () => {
|
|
259
|
-
const markdown = `
|
|
260
|
-
- [ ] T001 [TEST] 用户登录功能测试
|
|
261
|
-
- [ ] T002 [IMPL] 用户登录功能实现 [P]
|
|
262
|
-
`.trim();
|
|
263
|
-
|
|
264
|
-
expect(() => parseTasksMarkdown(markdown)).toThrow(
|
|
265
|
-
/must depend on T001/
|
|
266
|
-
);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
test('should reject TEST depending on IMPL', () => {
|
|
270
|
-
const markdown = `
|
|
271
|
-
- [ ] T001 [IMPL] 用户登录功能实现
|
|
272
|
-
- [ ] T002 [TEST] 用户登录功能测试 (dependsOn:T001)
|
|
273
|
-
`.trim();
|
|
274
|
-
|
|
275
|
-
expect(() => parseTasksMarkdown(markdown)).toThrow(
|
|
276
|
-
/Tests must be written BEFORE implementation/
|
|
277
|
-
);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test('should reject IMPL depending on wrong TEST', () => {
|
|
281
|
-
const markdown = `
|
|
282
|
-
- [ ] T001 [TEST] 用户注册测试
|
|
283
|
-
- [ ] T002 [IMPL] 用户登录实现 (dependsOn:T001)
|
|
284
|
-
`.trim();
|
|
285
|
-
|
|
286
|
-
expect(() => parseTasksMarkdown(markdown)).toThrow(
|
|
287
|
-
/missing corresponding TEST task/
|
|
288
|
-
);
|
|
289
|
-
});
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
describe('Feature name extraction', () => {
|
|
293
|
-
test('should match TEST and IMPL with same feature name', () => {
|
|
294
|
-
const markdown = `
|
|
295
|
-
- [ ] T001 [TEST] 实现用户认证功能
|
|
296
|
-
- [ ] T002 [IMPL] 实现用户认证功能 (dependsOn:T001)
|
|
297
|
-
`.trim();
|
|
298
|
-
|
|
299
|
-
expect(() => parseTasksMarkdown(markdown)).not.toThrow();
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
test('should handle case-insensitive markers', () => {
|
|
303
|
-
const markdown = `
|
|
304
|
-
- [ ] T001 [test] 用户登录
|
|
305
|
-
- [ ] T002 [impl] 用户登录 (dependsOn:T001)
|
|
306
|
-
`.trim();
|
|
307
|
-
|
|
308
|
-
expect(() => parseTasksMarkdown(markdown)).not.toThrow();
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
describe('Error messages', () => {
|
|
313
|
-
test('should provide clear violation details', () => {
|
|
314
|
-
const markdown = `
|
|
315
|
-
- [ ] T001 [IMPL] 功能A
|
|
316
|
-
- [ ] T002 [TEST] 功能B
|
|
317
|
-
- [ ] T003 [IMPL] 功能B
|
|
318
|
-
`.trim();
|
|
319
|
-
|
|
320
|
-
try {
|
|
321
|
-
parseTasksMarkdown(markdown);
|
|
322
|
-
fail('Should have thrown TDD violation error');
|
|
323
|
-
} catch (error) {
|
|
324
|
-
expect(error.message).toContain('TDD Order Validation Failed');
|
|
325
|
-
expect(error.message).toContain('Constitution Article VI');
|
|
326
|
-
expect(error.message).toContain('T001');
|
|
327
|
-
expect(error.message).toContain('missing corresponding TEST task');
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
});
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const os = require('os');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
const { runPreparePr } = require('../operations/prepare-pr');
|
|
6
|
-
const {
|
|
7
|
-
getRuntimeStatePath,
|
|
8
|
-
getTaskManifestPath,
|
|
9
|
-
getReportCardPath
|
|
10
|
-
} = require('../store');
|
|
11
|
-
const {
|
|
12
|
-
getIntentPrBriefPath,
|
|
13
|
-
getIntentHandoffArtifactPaths
|
|
14
|
-
} = require('../artifacts');
|
|
15
|
-
|
|
16
|
-
function writeJson(filePath, value) {
|
|
17
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
18
|
-
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function writeText(filePath, content = 'stale\n') {
|
|
22
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
23
|
-
fs.writeFileSync(filePath, content);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
describe('runPreparePr', () => {
|
|
27
|
-
test('generates pr brief and keeps it as the only handoff artifact', async () => {
|
|
28
|
-
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cc-devflow-prepare-pr-'));
|
|
29
|
-
const prBriefPath = getIntentPrBriefPath(repoRoot, 'REQ-123');
|
|
30
|
-
|
|
31
|
-
writeJson(path.join(repoRoot, 'package.json'), {
|
|
32
|
-
name: 'prepare-pr-test',
|
|
33
|
-
version: '1.0.0'
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
writeJson(getRuntimeStatePath(repoRoot, 'REQ-123'), {
|
|
37
|
-
changeId: 'REQ-123',
|
|
38
|
-
changeKey: 'REQ-123-prepare-a-pr-ready-brief',
|
|
39
|
-
slug: 'prepare-a-pr-ready-brief',
|
|
40
|
-
createdAt: '2026-03-25T01:00:00.000Z',
|
|
41
|
-
goal: 'Prepare a PR-ready brief',
|
|
42
|
-
status: 'verified',
|
|
43
|
-
initializedAt: '2026-03-25T01:00:00.000Z',
|
|
44
|
-
plannedAt: '2026-03-25T01:05:00.000Z',
|
|
45
|
-
verifiedAt: '2026-03-25T01:10:00.000Z',
|
|
46
|
-
updatedAt: '2026-03-25T01:10:00.000Z'
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
writeJson(getTaskManifestPath(repoRoot, 'REQ-123'), {
|
|
50
|
-
changeId: 'REQ-123',
|
|
51
|
-
goal: 'Prepare a PR-ready brief',
|
|
52
|
-
createdAt: '2026-03-25T01:05:00.000Z',
|
|
53
|
-
updatedAt: '2026-03-25T01:10:00.000Z',
|
|
54
|
-
tasks: [
|
|
55
|
-
{
|
|
56
|
-
id: 'T001',
|
|
57
|
-
title: 'Finish delivery',
|
|
58
|
-
type: 'IMPL',
|
|
59
|
-
dependsOn: [],
|
|
60
|
-
touches: ['src/a.ts'],
|
|
61
|
-
run: ['echo ok'],
|
|
62
|
-
checks: [],
|
|
63
|
-
status: 'passed',
|
|
64
|
-
attempts: 1,
|
|
65
|
-
maxRetries: 1
|
|
66
|
-
}
|
|
67
|
-
],
|
|
68
|
-
metadata: {
|
|
69
|
-
source: 'default',
|
|
70
|
-
generatedBy: 'test',
|
|
71
|
-
planVersion: 1
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
writeJson(getReportCardPath(repoRoot, 'REQ-123'), {
|
|
76
|
-
changeId: 'REQ-123',
|
|
77
|
-
overall: 'pass',
|
|
78
|
-
quickGates: [
|
|
79
|
-
{ name: 'lint', status: 'pass', command: 'npm run lint', durationMs: 10, details: '' }
|
|
80
|
-
],
|
|
81
|
-
strictGates: [
|
|
82
|
-
{ name: 'test', status: 'pass', command: 'npm test', durationMs: 20, details: '' }
|
|
83
|
-
],
|
|
84
|
-
review: {
|
|
85
|
-
status: 'pass',
|
|
86
|
-
summary: 'review-ok',
|
|
87
|
-
details: '',
|
|
88
|
-
findings: [],
|
|
89
|
-
taskReviews: { status: 'pass', required: true, summary: '', reviewers: [], findings: [] },
|
|
90
|
-
diffReview: { status: 'pass', required: true, summary: '', reviewers: [], findings: [] }
|
|
91
|
-
},
|
|
92
|
-
blockingFindings: [],
|
|
93
|
-
timestamp: '2026-03-25T01:11:00.000Z'
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
writeJson(getRuntimeStatePath(repoRoot, 'REQ-123'), {
|
|
97
|
-
...JSON.parse(fs.readFileSync(getRuntimeStatePath(repoRoot, 'REQ-123'), 'utf8')),
|
|
98
|
-
approval: {
|
|
99
|
-
status: 'approved',
|
|
100
|
-
executionMode: 'direct',
|
|
101
|
-
planVersion: 1,
|
|
102
|
-
approvedAt: '2026-03-25T01:06:00.000Z'
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
for (const filePath of getIntentHandoffArtifactPaths(repoRoot, 'REQ-123')) {
|
|
107
|
-
if (filePath !== prBriefPath) {
|
|
108
|
-
writeText(filePath);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const result = await runPreparePr({ repoRoot, changeId: 'REQ-123' });
|
|
113
|
-
|
|
114
|
-
const prBrief = fs.readFileSync(prBriefPath, 'utf8');
|
|
115
|
-
|
|
116
|
-
expect(result.status).toBe('prepared');
|
|
117
|
-
expect(result.suggestedTitle).toBe('feat(req-123): Prepare a PR-ready brief');
|
|
118
|
-
expect(prBrief).toContain('PR Brief: REQ-123');
|
|
119
|
-
expect(prBrief).toContain('planning/task-manifest.json');
|
|
120
|
-
expect(prBrief).not.toContain('execution/tasks/T001/checkpoint.json');
|
|
121
|
-
expect(prBrief).not.toContain('result.md');
|
|
122
|
-
for (const filePath of getIntentHandoffArtifactPaths(repoRoot, 'REQ-123')) {
|
|
123
|
-
expect(fs.existsSync(filePath)).toBe(filePath === prBriefPath);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
});
|