cc-devflow 4.5.11 → 4.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +17 -0
- package/.claude/skills/cc-review/PLAYBOOK.md +13 -86
- package/.claude/skills/cc-review/SKILL.md +53 -241
- 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 +13 -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,593 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* [INPUT]: 接收 repoRoot/changeId/changeKey,读取 planning/tasks.md。
|
|
3
|
-
* [OUTPUT]: 对外提供 task-contract compile / validate / migrate,生成、检查、迁移最小 workflow artifacts。
|
|
4
|
-
* [POS]: REQ-003 task-contract CLI operation 层。
|
|
5
|
-
* [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { createTaskManifest } = require('../planner');
|
|
9
|
-
const fsp = require('fs/promises');
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const { getChangePaths } = require('../paths');
|
|
12
|
-
const {
|
|
13
|
-
extractTasksContractSummary,
|
|
14
|
-
extractTasksRootCauseContract
|
|
15
|
-
} = require('../task-contract');
|
|
16
|
-
const {
|
|
17
|
-
exists,
|
|
18
|
-
ensureDir,
|
|
19
|
-
getTaskManifestPath,
|
|
20
|
-
getTasksMarkdownPath,
|
|
21
|
-
readText,
|
|
22
|
-
readJson,
|
|
23
|
-
writeText,
|
|
24
|
-
writeJson
|
|
25
|
-
} = require('../store');
|
|
26
|
-
|
|
27
|
-
const GENERATED_BY = 'cc-devflow task-contract';
|
|
28
|
-
const ACCEPTED_GENERATORS = new Set(['cc-devflow task-contract', 'skill:cc-plan']);
|
|
29
|
-
const PROFILE_BUDGETS = {
|
|
30
|
-
tiny: 1200,
|
|
31
|
-
standard: 2500,
|
|
32
|
-
deep: 5000
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
function listField(value) {
|
|
36
|
-
if (Array.isArray(value)) return value;
|
|
37
|
-
return value ? [value] : [];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function buildSummaryChangeMeta(change, fields) {
|
|
41
|
-
const contractChange = fields.change || change.changeKey;
|
|
42
|
-
return {
|
|
43
|
-
changeId: contractChange,
|
|
44
|
-
requirementId: contractChange,
|
|
45
|
-
goal: listField(fields.goal),
|
|
46
|
-
acceptance: listField(fields.acceptance),
|
|
47
|
-
specReference: {
|
|
48
|
-
source: 'planning/tasks.md#contract-summary',
|
|
49
|
-
change: contractChange,
|
|
50
|
-
mode: fields.mode || '',
|
|
51
|
-
profile: fields.profile || '',
|
|
52
|
-
approval: fields.approval || ''
|
|
53
|
-
},
|
|
54
|
-
_meta: {
|
|
55
|
-
generatedBy: GENERATED_BY,
|
|
56
|
-
generatedAt: new Date().toISOString()
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function buildRootCauseChangeMeta(change, fields) {
|
|
62
|
-
const contractChange = fields.change || change.changeKey;
|
|
63
|
-
return {
|
|
64
|
-
changeId: contractChange,
|
|
65
|
-
requirementId: contractChange,
|
|
66
|
-
goal: listField(fields.rootCause || fields.symptom || `Repair confirmed root cause for ${contractChange}`),
|
|
67
|
-
acceptance: [
|
|
68
|
-
...listField(fields.repairBoundary),
|
|
69
|
-
...listField(fields.verification)
|
|
70
|
-
],
|
|
71
|
-
specReference: {
|
|
72
|
-
source: 'planning/tasks.md#root-cause-contract',
|
|
73
|
-
change: contractChange,
|
|
74
|
-
mode: fields.mode || 'investigation',
|
|
75
|
-
profile: fields.profile || '',
|
|
76
|
-
approval: fields.diagnosis || ''
|
|
77
|
-
},
|
|
78
|
-
rootCause: {
|
|
79
|
-
diagnosis: fields.diagnosis || '',
|
|
80
|
-
symptom: listField(fields.symptom),
|
|
81
|
-
reproduction: listField(fields.reproduction),
|
|
82
|
-
confirmed: listField(fields.rootCause),
|
|
83
|
-
repairBoundary: listField(fields.repairBoundary),
|
|
84
|
-
prevention: listField(fields.prevention)
|
|
85
|
-
},
|
|
86
|
-
_meta: {
|
|
87
|
-
generatedBy: GENERATED_BY,
|
|
88
|
-
generatedAt: new Date().toISOString()
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function extractCompileContract(tasksText) {
|
|
94
|
-
const summary = extractTasksContractSummary(tasksText);
|
|
95
|
-
if (summary.found) {
|
|
96
|
-
return {
|
|
97
|
-
type: 'summary',
|
|
98
|
-
source: 'planning/tasks.md#contract-summary',
|
|
99
|
-
fields: summary.fields
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const rootCause = extractTasksRootCauseContract(tasksText);
|
|
104
|
-
if (rootCause.found) {
|
|
105
|
-
return {
|
|
106
|
-
type: 'root-cause',
|
|
107
|
-
source: 'planning/tasks.md#root-cause-contract',
|
|
108
|
-
fields: rootCause.fields
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function buildChangeMeta(change, contract) {
|
|
116
|
-
if (contract.type === 'root-cause') {
|
|
117
|
-
return buildRootCauseChangeMeta(change, contract.fields);
|
|
118
|
-
}
|
|
119
|
-
return buildSummaryChangeMeta(change, contract.fields);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function summarizeContractGoal(contract) {
|
|
123
|
-
const fields = contract.fields || {};
|
|
124
|
-
if (contract.type === 'root-cause') {
|
|
125
|
-
return listField(fields.rootCause)[0] || listField(fields.symptom)[0] || '';
|
|
126
|
-
}
|
|
127
|
-
return listField(fields.goal)[0] || '';
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function relativeExists(change, relativePath) {
|
|
131
|
-
return exists(path.join(change.changeDir, relativePath));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function readJsonSafe(filePath) {
|
|
135
|
-
try {
|
|
136
|
-
return await readJson(filePath, null);
|
|
137
|
-
} catch {
|
|
138
|
-
return null;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function estimateTokens(text) {
|
|
143
|
-
return Math.ceil(String(text || '').length / 4);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function contractProfile(tasksText) {
|
|
147
|
-
const contract = extractTasksContractSummary(tasksText);
|
|
148
|
-
if (contract.found) return contract.fields.profile || 'standard';
|
|
149
|
-
const rootCause = extractTasksRootCauseContract(tasksText);
|
|
150
|
-
if (rootCause.found) return rootCause.fields.profile || 'standard';
|
|
151
|
-
return 'standard';
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function hasAnyContract(tasksText) {
|
|
155
|
-
return extractTasksContractSummary(tasksText).found || extractTasksRootCauseContract(tasksText).found;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function countTracerSlices(tasksText) {
|
|
159
|
-
const matches = [...String(tasksText || '').matchAll(/Vertical slice:\s*([^\n]+)/gi)];
|
|
160
|
-
if (matches.length > 0) {
|
|
161
|
-
return new Set(matches.map((match) => match[1].trim())).size;
|
|
162
|
-
}
|
|
163
|
-
return [...String(tasksText || '').matchAll(/^- \[[ xX]\]\s+T\d{3}\b/gm)].length;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function quoteLegacyMarkdown(text) {
|
|
167
|
-
return String(text || '')
|
|
168
|
-
.replace(/\r\n/g, '\n')
|
|
169
|
-
.replace(/\n*$/, '')
|
|
170
|
-
.split('\n')
|
|
171
|
-
.map((line) => (line.length > 0 ? `> ${line}` : '>'))
|
|
172
|
-
.join('\n');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function appendToSection(tasksText, heading, addition) {
|
|
176
|
-
const lines = String(tasksText || '').replace(/\r\n/g, '\n').split('\n');
|
|
177
|
-
const start = lines.findIndex((line) => line.trimEnd() === heading);
|
|
178
|
-
if (start === -1) return '';
|
|
179
|
-
let end = start + 1;
|
|
180
|
-
while (end < lines.length && !lines[end].startsWith('## ')) end += 1;
|
|
181
|
-
return [...lines.slice(0, end), '', addition, '', ...lines.slice(end)].join('\n').replace(/\n*$/, '\n');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function insertAfterTitle(tasksText, section) {
|
|
185
|
-
const lines = String(tasksText || '').replace(/\r\n/g, '\n').split('\n');
|
|
186
|
-
if (!lines[0]?.startsWith('# ')) {
|
|
187
|
-
return `${section}\n\n${String(tasksText || '').replace(/\n*$/, '\n')}`;
|
|
188
|
-
}
|
|
189
|
-
let tailStart = 1;
|
|
190
|
-
while (lines[tailStart] === '') tailStart += 1;
|
|
191
|
-
return [lines[0], '', section, '', ...lines.slice(tailStart)].join('\n').replace(/\n*$/, '\n');
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function upsertContractSection(tasksText, heading, section, addition) {
|
|
195
|
-
return appendToSection(tasksText, heading, addition) || insertAfterTitle(tasksText, section);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function legacyBlock(label, content) {
|
|
199
|
-
return [`${label}:`, quoteLegacyMarkdown(content)].join('\n');
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function designContractSection(change, content) {
|
|
203
|
-
const block = legacyBlock('Legacy Design', content);
|
|
204
|
-
return [
|
|
205
|
-
'## Contract Summary',
|
|
206
|
-
'',
|
|
207
|
-
`Change: ${change.changeKey}`,
|
|
208
|
-
'Mode: migrated',
|
|
209
|
-
'Profile: standard',
|
|
210
|
-
'Approval: migrated',
|
|
211
|
-
'',
|
|
212
|
-
'Goal:',
|
|
213
|
-
'- Preserve the legacy planning contract inside tasks.md.',
|
|
214
|
-
'',
|
|
215
|
-
'Do Not Do:',
|
|
216
|
-
'- Do not auto-run this migration during compile.',
|
|
217
|
-
'',
|
|
218
|
-
'Approved Direction:',
|
|
219
|
-
'- Use tasks.md as the canonical human-authored contract after migration.',
|
|
220
|
-
'',
|
|
221
|
-
'Acceptance:',
|
|
222
|
-
'- Legacy planning/design.md content is folded into this section.',
|
|
223
|
-
'- planning/design.md is archived to assets/legacy/design.md.bak.',
|
|
224
|
-
'',
|
|
225
|
-
'Verification:',
|
|
226
|
-
'',
|
|
227
|
-
'```bash',
|
|
228
|
-
'npm test -- -t "task-contract migrate"',
|
|
229
|
-
'```',
|
|
230
|
-
'',
|
|
231
|
-
'Risk / Escalate If:',
|
|
232
|
-
'- Review migrated content before deleting any legacy references.',
|
|
233
|
-
'',
|
|
234
|
-
block
|
|
235
|
-
].join('\n');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function analysisContractSection(change, content) {
|
|
239
|
-
const block = legacyBlock('Legacy Analysis', content);
|
|
240
|
-
return [
|
|
241
|
-
'## Root Cause Contract',
|
|
242
|
-
'',
|
|
243
|
-
`Change: ${change.changeKey}`,
|
|
244
|
-
'Mode: migrated',
|
|
245
|
-
'Profile: standard',
|
|
246
|
-
'Diagnosis: Migrated from legacy planning/analysis.md.',
|
|
247
|
-
'',
|
|
248
|
-
'Symptom:',
|
|
249
|
-
'- Legacy investigation contract lived outside tasks.md.',
|
|
250
|
-
'',
|
|
251
|
-
'Reproduction:',
|
|
252
|
-
'- Run task-contract migrate with --fold-analysis.',
|
|
253
|
-
'',
|
|
254
|
-
'Expected:',
|
|
255
|
-
'- tasks.md owns the root-cause contract.',
|
|
256
|
-
'',
|
|
257
|
-
'Actual:',
|
|
258
|
-
'- planning/analysis.md was the legacy contract file.',
|
|
259
|
-
'',
|
|
260
|
-
'Root Cause:',
|
|
261
|
-
'- The pre-minimization workflow used a separate analysis.md artifact.',
|
|
262
|
-
'',
|
|
263
|
-
'Evidence Chain:',
|
|
264
|
-
'- Migrated content is quoted below.',
|
|
265
|
-
'',
|
|
266
|
-
'Repair Boundary:',
|
|
267
|
-
'- Archive only the requested legacy analysis file.',
|
|
268
|
-
'',
|
|
269
|
-
'Prevention:',
|
|
270
|
-
'- Keep future root-cause contracts in tasks.md.',
|
|
271
|
-
'',
|
|
272
|
-
'Risk / Escalate If:',
|
|
273
|
-
'- Review migrated content before deleting any legacy references.',
|
|
274
|
-
'',
|
|
275
|
-
block
|
|
276
|
-
].join('\n');
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function firstViolation(violations) {
|
|
280
|
-
if (violations.length === 0) {
|
|
281
|
-
return {
|
|
282
|
-
code: 0,
|
|
283
|
-
ok: true,
|
|
284
|
-
violations: [],
|
|
285
|
-
stderr: ''
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
const code = violations[0].exitCode;
|
|
289
|
-
return {
|
|
290
|
-
code,
|
|
291
|
-
ok: false,
|
|
292
|
-
violations,
|
|
293
|
-
stderr: violations.map((violation) => `${violation.ruleId}: ${violation.message}`).join('\n')
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const VALIDATE_RULES = [
|
|
298
|
-
{
|
|
299
|
-
id: 'C1',
|
|
300
|
-
exitCode: 2,
|
|
301
|
-
check: async ({ change }) => (await relativeExists(change, 'planning/design.md'))
|
|
302
|
-
? 'planning/design.md is a legacy default artifact'
|
|
303
|
-
: ''
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
id: 'C2',
|
|
307
|
-
exitCode: 2,
|
|
308
|
-
check: async ({ change }) => (await relativeExists(change, 'planning/analysis.md'))
|
|
309
|
-
? 'planning/analysis.md is a legacy default artifact'
|
|
310
|
-
: ''
|
|
311
|
-
},
|
|
312
|
-
{
|
|
313
|
-
id: 'C3',
|
|
314
|
-
exitCode: 2,
|
|
315
|
-
check: async ({ change }) => {
|
|
316
|
-
const legacy = [];
|
|
317
|
-
if (await relativeExists(change, 'review/cc-review-report.md')) legacy.push('review/cc-review-report.md');
|
|
318
|
-
if (await relativeExists(change, 'review/cc-review-plan.md')) legacy.push('review/cc-review-plan.md');
|
|
319
|
-
return legacy.length > 0 ? `legacy review Markdown exists: ${legacy.join(', ')}` : '';
|
|
320
|
-
}
|
|
321
|
-
},
|
|
322
|
-
{
|
|
323
|
-
id: 'C4',
|
|
324
|
-
exitCode: 3,
|
|
325
|
-
check: async ({ tasksText }) => hasAnyContract(tasksText)
|
|
326
|
-
? ''
|
|
327
|
-
: 'planning/tasks.md is missing ## Contract Summary or ## Root Cause Contract'
|
|
328
|
-
},
|
|
329
|
-
{
|
|
330
|
-
id: 'C5',
|
|
331
|
-
exitCode: 3,
|
|
332
|
-
check: async ({ manifestPath }) => {
|
|
333
|
-
const manifest = await readJsonSafe(manifestPath);
|
|
334
|
-
const generator = manifest?.metadata?.generatedBy || '';
|
|
335
|
-
return ACCEPTED_GENERATORS.has(generator) ? '' : `task-manifest.json generatedBy is unknown: ${generator || '<missing>'}`;
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
id: 'C6',
|
|
340
|
-
exitCode: 3,
|
|
341
|
-
check: async ({ changeMetaPath }) => {
|
|
342
|
-
const changeMeta = await readJsonSafe(changeMetaPath);
|
|
343
|
-
const generator = changeMeta?._meta?.generatedBy || '';
|
|
344
|
-
return ACCEPTED_GENERATORS.has(generator) ? '' : `change-meta.json _meta.generatedBy is unknown: ${generator || '<missing>'}`;
|
|
345
|
-
}
|
|
346
|
-
},
|
|
347
|
-
{
|
|
348
|
-
id: 'C7',
|
|
349
|
-
exitCode: 4,
|
|
350
|
-
check: async ({ tasksText }) => {
|
|
351
|
-
const profile = contractProfile(tasksText);
|
|
352
|
-
const slices = countTracerSlices(tasksText);
|
|
353
|
-
return profile === 'tiny' && slices > 1 ? `tiny profile has ${slices} tracer slices` : '';
|
|
354
|
-
}
|
|
355
|
-
},
|
|
356
|
-
{
|
|
357
|
-
id: 'C8',
|
|
358
|
-
exitCode: 5,
|
|
359
|
-
check: async ({ change }) => {
|
|
360
|
-
const ledgerPath = path.join(change.changeDir, 'review', 'review-ledger.jsonl');
|
|
361
|
-
if (!(await exists(ledgerPath))) return '';
|
|
362
|
-
const lines = (await readText(ledgerPath)).split(/\r?\n/).filter(Boolean);
|
|
363
|
-
const missing = lines.some((line) => {
|
|
364
|
-
try {
|
|
365
|
-
return !JSON.parse(line).createdBy;
|
|
366
|
-
} catch {
|
|
367
|
-
return true;
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
return missing ? 'review-ledger.jsonl has a row without createdBy' : '';
|
|
371
|
-
}
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
id: 'C9',
|
|
375
|
-
exitCode: 5,
|
|
376
|
-
check: async ({ change }) => {
|
|
377
|
-
const reportPath = path.join(change.changeDir, 'review', 'report-card.json');
|
|
378
|
-
if (!(await exists(reportPath))) return '';
|
|
379
|
-
const report = await readJsonSafe(reportPath);
|
|
380
|
-
const generator = report?._meta?.generatedBy || '';
|
|
381
|
-
return ACCEPTED_GENERATORS.has(generator) ? '' : `report-card.json generatedBy is unknown: ${generator || '<missing>'}`;
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
{
|
|
385
|
-
id: 'C10',
|
|
386
|
-
exitCode: 6,
|
|
387
|
-
check: async ({ tasksText }) => {
|
|
388
|
-
const profile = contractProfile(tasksText);
|
|
389
|
-
const budget = PROFILE_BUDGETS[profile] || PROFILE_BUDGETS.standard;
|
|
390
|
-
const tokens = estimateTokens(tasksText);
|
|
391
|
-
return tokens > budget ? `tasks.md token estimate ${tokens} exceeds ${profile} budget ${budget}` : '';
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
];
|
|
395
|
-
|
|
396
|
-
async function runCompile({
|
|
397
|
-
repoRoot,
|
|
398
|
-
changeId,
|
|
399
|
-
changeKey,
|
|
400
|
-
goal,
|
|
401
|
-
overwrite = true
|
|
402
|
-
}) {
|
|
403
|
-
const pathOptions = changeKey ? { changeKey } : {};
|
|
404
|
-
const change = getChangePaths(repoRoot, changeId, pathOptions);
|
|
405
|
-
const tasksPath = getTasksMarkdownPath(repoRoot, changeId, { changeKey: change.changeKey });
|
|
406
|
-
|
|
407
|
-
if (!(await exists(tasksPath))) {
|
|
408
|
-
return {
|
|
409
|
-
code: 3,
|
|
410
|
-
changeId,
|
|
411
|
-
changeKey: change.changeKey,
|
|
412
|
-
error: `Missing planning/tasks.md: ${tasksPath}`
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
const contract = extractCompileContract(await readText(tasksPath));
|
|
417
|
-
if (!contract) {
|
|
418
|
-
return {
|
|
419
|
-
code: 3,
|
|
420
|
-
changeId,
|
|
421
|
-
changeKey: change.changeKey,
|
|
422
|
-
error: `Missing ## Contract Summary or ## Root Cause Contract in planning/tasks.md: ${tasksPath}`
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
await createTaskManifest({
|
|
427
|
-
repoRoot,
|
|
428
|
-
changeId,
|
|
429
|
-
changeKey: change.changeKey,
|
|
430
|
-
goal: goal || summarizeContractGoal(contract),
|
|
431
|
-
overwrite
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
const manifestPath = getTaskManifestPath(repoRoot, changeId, { changeKey: change.changeKey });
|
|
435
|
-
const changeMetaPath = path.join(change.changeDir, 'change-meta.json');
|
|
436
|
-
const manifest = await readJson(manifestPath);
|
|
437
|
-
const compiled = {
|
|
438
|
-
...manifest,
|
|
439
|
-
metadata: {
|
|
440
|
-
...manifest.metadata,
|
|
441
|
-
source: 'tasks.md',
|
|
442
|
-
generatedBy: GENERATED_BY
|
|
443
|
-
}
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
await writeJson(manifestPath, compiled);
|
|
447
|
-
await writeJson(changeMetaPath, buildChangeMeta(change, contract));
|
|
448
|
-
|
|
449
|
-
return {
|
|
450
|
-
code: 0,
|
|
451
|
-
changeId,
|
|
452
|
-
changeKey: change.changeKey,
|
|
453
|
-
manifestPath,
|
|
454
|
-
changeMetaPath,
|
|
455
|
-
metadata: compiled.metadata
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
async function runValidate({ repoRoot, changeId, changeKey }) {
|
|
460
|
-
const pathOptions = changeKey ? { changeKey } : {};
|
|
461
|
-
const change = getChangePaths(repoRoot, changeId, pathOptions);
|
|
462
|
-
const tasksPath = getTasksMarkdownPath(repoRoot, changeId, { changeKey: change.changeKey });
|
|
463
|
-
const manifestPath = getTaskManifestPath(repoRoot, changeId, { changeKey: change.changeKey });
|
|
464
|
-
const changeMetaPath = path.join(change.changeDir, 'change-meta.json');
|
|
465
|
-
const tasksText = await readText(tasksPath);
|
|
466
|
-
const context = { change, tasksPath, manifestPath, changeMetaPath, tasksText };
|
|
467
|
-
const violations = [];
|
|
468
|
-
|
|
469
|
-
for (const rule of VALIDATE_RULES) {
|
|
470
|
-
const message = await rule.check(context);
|
|
471
|
-
if (message) {
|
|
472
|
-
violations.push({ ruleId: rule.id, exitCode: rule.exitCode, message });
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return {
|
|
477
|
-
...firstViolation(violations),
|
|
478
|
-
changeId,
|
|
479
|
-
changeKey: change.changeKey
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
async function prepareLegacyFold(change, tasksText, config) {
|
|
484
|
-
const sourcePath = path.join(change.changeDir, 'planning', config.fileName);
|
|
485
|
-
if (!(await exists(sourcePath))) {
|
|
486
|
-
return { code: 3, error: `Missing planning/${config.fileName}: ${sourcePath}` };
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
let legacyText;
|
|
490
|
-
try {
|
|
491
|
-
legacyText = await readText(sourcePath);
|
|
492
|
-
} catch (error) {
|
|
493
|
-
return { code: 4, error: `Unable to read planning/${config.fileName}: ${error.message}` };
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
const archivePath = path.join(change.changeDir, 'assets', 'legacy', `${config.fileName}.bak`);
|
|
497
|
-
if (await exists(archivePath)) {
|
|
498
|
-
return { code: 4, error: `Legacy archive already exists: ${archivePath}` };
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
return {
|
|
502
|
-
code: 0,
|
|
503
|
-
fileName: config.fileName,
|
|
504
|
-
sourcePath,
|
|
505
|
-
archivePath,
|
|
506
|
-
tasksText: upsertContractSection(
|
|
507
|
-
tasksText,
|
|
508
|
-
config.heading,
|
|
509
|
-
config.section(change, legacyText),
|
|
510
|
-
legacyBlock(config.label, legacyText)
|
|
511
|
-
)
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
async function runMigrate({
|
|
516
|
-
repoRoot,
|
|
517
|
-
changeId,
|
|
518
|
-
changeKey,
|
|
519
|
-
foldDesign = false,
|
|
520
|
-
foldAnalysis = false
|
|
521
|
-
}) {
|
|
522
|
-
if (!foldDesign && !foldAnalysis) {
|
|
523
|
-
return {
|
|
524
|
-
code: 3,
|
|
525
|
-
changeId,
|
|
526
|
-
changeKey,
|
|
527
|
-
error: 'task-contract migrate requires --fold-design and/or --fold-analysis'
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
const pathOptions = changeKey ? { changeKey } : {};
|
|
532
|
-
const change = getChangePaths(repoRoot, changeId, pathOptions);
|
|
533
|
-
const tasksPath = getTasksMarkdownPath(repoRoot, changeId, { changeKey: change.changeKey });
|
|
534
|
-
if (!(await exists(tasksPath))) {
|
|
535
|
-
return {
|
|
536
|
-
code: 3,
|
|
537
|
-
changeId,
|
|
538
|
-
changeKey: change.changeKey,
|
|
539
|
-
error: `Missing planning/tasks.md: ${tasksPath}`
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
let tasksText = await readText(tasksPath);
|
|
544
|
-
const folds = [];
|
|
545
|
-
if (foldDesign) {
|
|
546
|
-
folds.push({
|
|
547
|
-
fileName: 'design.md',
|
|
548
|
-
heading: '## Contract Summary',
|
|
549
|
-
label: 'Legacy Design',
|
|
550
|
-
section: designContractSection
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
if (foldAnalysis) {
|
|
554
|
-
folds.push({
|
|
555
|
-
fileName: 'analysis.md',
|
|
556
|
-
heading: '## Root Cause Contract',
|
|
557
|
-
label: 'Legacy Analysis',
|
|
558
|
-
section: analysisContractSection
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const prepared = [];
|
|
563
|
-
for (const fold of folds) {
|
|
564
|
-
const result = await prepareLegacyFold(change, tasksText, fold);
|
|
565
|
-
if (result.code !== 0) {
|
|
566
|
-
return { ...result, changeId, changeKey: change.changeKey };
|
|
567
|
-
}
|
|
568
|
-
tasksText = result.tasksText;
|
|
569
|
-
prepared.push(result);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
await writeText(tasksPath, tasksText);
|
|
573
|
-
for (const fold of prepared) {
|
|
574
|
-
await ensureDir(path.dirname(fold.archivePath));
|
|
575
|
-
await fsp.rename(fold.sourcePath, fold.archivePath);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
return {
|
|
579
|
-
code: 0,
|
|
580
|
-
changeId,
|
|
581
|
-
changeKey: change.changeKey,
|
|
582
|
-
tasksPath,
|
|
583
|
-
migrated: prepared.map((fold) => fold.fileName),
|
|
584
|
-
archives: prepared.map((fold) => fold.archivePath)
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
module.exports = {
|
|
589
|
-
GENERATED_BY,
|
|
590
|
-
runCompile,
|
|
591
|
-
runValidate,
|
|
592
|
-
runMigrate
|
|
593
|
-
};
|