cc-devflow 4.5.11 → 4.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/.claude/skills/cc-act/CHANGELOG.md +18 -0
  2. package/.claude/skills/cc-act/PLAYBOOK.md +17 -269
  3. package/.claude/skills/cc-act/SKILL.md +38 -425
  4. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_INDEX_TEMPLATE.md +2 -13
  5. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_TEMPLATE.md +1 -9
  6. package/.claude/skills/cc-act/assets/PR_BRIEF_TEMPLATE.md +21 -177
  7. package/.claude/skills/cc-act/references/closure-contract.md +12 -63
  8. package/.claude/skills/cc-act/references/git-commit-guidelines.md +5 -5
  9. package/.claude/skills/cc-act/scripts/cc-act-common.sh +5 -322
  10. package/.claude/skills/cc-act/scripts/detect-ship-target.sh +11 -2
  11. package/.claude/skills/cc-act/scripts/inspect-git-index.sh +58 -0
  12. package/.claude/skills/cc-act/scripts/render-pr-brief.sh +40 -440
  13. package/.claude/skills/cc-act/scripts/verify-act-gate.sh +10 -50
  14. package/.claude/skills/cc-check/CHANGELOG.md +18 -0
  15. package/.claude/skills/cc-check/PLAYBOOK.md +19 -273
  16. package/.claude/skills/cc-check/SKILL.md +33 -456
  17. package/.claude/skills/cc-check/references/review-contract.md +12 -147
  18. package/.claude/skills/cc-dev/CHANGELOG.md +15 -0
  19. package/.claude/skills/cc-dev/PLAYBOOK.md +1 -1
  20. package/.claude/skills/cc-dev/SKILL.md +52 -137
  21. package/.claude/skills/cc-dev/scripts/resolve-cc-devflow.sh +181 -0
  22. package/.claude/skills/cc-do/CHANGELOG.md +11 -0
  23. package/.claude/skills/cc-do/PLAYBOOK.md +19 -113
  24. package/.claude/skills/cc-do/SKILL.md +39 -245
  25. package/.claude/skills/cc-do/references/execution-recovery.md +15 -109
  26. package/.claude/skills/cc-do/scripts/cc-do-common.sh +5 -57
  27. package/.claude/skills/cc-do/scripts/check-task-status.sh +35 -65
  28. package/.claude/skills/cc-do/scripts/mark-task-complete.sh +9 -46
  29. package/.claude/skills/cc-do/scripts/select-ready-tasks.sh +29 -97
  30. package/.claude/skills/cc-investigate/CHANGELOG.md +16 -0
  31. package/.claude/skills/cc-investigate/PLAYBOOK.md +20 -180
  32. package/.claude/skills/cc-investigate/SKILL.md +64 -246
  33. package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -98
  34. package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -218
  35. package/.claude/skills/cc-next/CHANGELOG.md +6 -0
  36. package/.claude/skills/cc-next/PLAYBOOK.md +12 -8
  37. package/.claude/skills/cc-next/SKILL.md +34 -140
  38. package/.claude/skills/cc-plan/CHANGELOG.md +16 -0
  39. package/.claude/skills/cc-plan/PLAYBOOK.md +22 -161
  40. package/.claude/skills/cc-plan/SKILL.md +45 -295
  41. package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +30 -228
  42. package/.claude/skills/cc-plan/references/planning-contract.md +24 -161
  43. package/.claude/skills/cc-plan/scripts/next-change-key.sh +8 -44
  44. package/.claude/skills/cc-plan/scripts/parse-task-dependencies.js +2 -2
  45. package/.claude/skills/cc-plan/scripts/validate-scope.sh +1 -1
  46. package/.claude/skills/cc-pr-land/SKILL.md +14 -114
  47. package/.claude/skills/cc-pr-review/CHANGELOG.md +4 -0
  48. package/.claude/skills/cc-pr-review/SKILL.md +20 -103
  49. package/.claude/skills/cc-review/CHANGELOG.md +17 -0
  50. package/.claude/skills/cc-review/PLAYBOOK.md +13 -86
  51. package/.claude/skills/cc-review/SKILL.md +53 -241
  52. package/.claude/skills/cc-review/references/e2e-and-plugin-verification.md +2 -2
  53. package/.claude/skills/cc-review/references/implementation-review-branch.md +7 -147
  54. package/.claude/skills/cc-review/references/plan-review-branch.md +5 -147
  55. package/.claude/skills/cc-review/references/review-methods.md +10 -218
  56. package/.claude/skills/cc-review/scripts/collect-review-context.sh +4 -63
  57. package/.claude/skills/cc-roadmap/PLAYBOOK.md +1 -1
  58. package/.claude/skills/cc-roadmap/SKILL.md +3 -3
  59. package/.claude/skills/cc-simplify/CHANGELOG.md +7 -0
  60. package/.claude/skills/cc-simplify/SKILL.md +26 -21
  61. package/.claude/skills/cc-spec-init/PLAYBOOK.md +12 -48
  62. package/.claude/skills/cc-spec-init/SKILL.md +29 -132
  63. package/.claude/skills/cc-spec-init/references/spec-contract.md +8 -17
  64. package/CHANGELOG.md +13 -0
  65. package/bin/cc-devflow-cli.js +20 -260
  66. package/bin/cc-devflow.js +44 -7
  67. package/docs/commands/README.md +1 -1
  68. package/docs/commands/README.zh-CN.md +1 -1
  69. package/docs/examples/README.md +1 -1
  70. package/docs/examples/START-HERE.md +14 -15
  71. package/docs/examples/example-bindings.json +11 -11
  72. package/docs/examples/full-design-blocked/README.md +4 -6
  73. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/{planning/tasks.md → task.md} +20 -15
  74. package/docs/examples/local-handoff/README.md +8 -11
  75. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/pr-brief.md +31 -0
  76. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/{planning/tasks.md → task.md} +18 -13
  77. package/docs/examples/pdca-loop/README.md +6 -9
  78. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +9 -11
  79. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/{planning/tasks.md → task.md} +18 -13
  80. package/docs/examples/scripts/check-example-bindings.sh +11 -62
  81. package/docs/guides/artifact-contract.md +10 -40
  82. package/docs/guides/getting-started.md +8 -8
  83. package/docs/guides/getting-started.zh-CN.md +8 -8
  84. package/docs/guides/minimize-artifacts.md +16 -130
  85. package/docs/guides/project-postmortem.md +14 -71
  86. package/lib/compiler/__tests__/skills-registry.test.js +9 -8
  87. package/lib/compiler/resource-copier.js +29 -0
  88. package/lib/skill-runtime/__tests__/archive-change.test.js +2 -2
  89. package/lib/skill-runtime/__tests__/benchmark-skills.test.js +3 -3
  90. package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +14 -4
  91. package/lib/skill-runtime/errors.js +3 -3
  92. package/lib/skill-runtime/index.js +5 -23
  93. package/lib/skill-runtime/paths.js +5 -52
  94. package/lib/skill-runtime/query-registry.js +4 -4
  95. package/lib/skill-runtime/query.js +89 -201
  96. package/lib/skill-runtime/store.js +4 -40
  97. package/lib/skill-runtime/trace.js +2 -2
  98. package/package.json +2 -5
  99. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +0 -29
  100. package/.claude/skills/cc-act/assets/RELEASE_NOTE_TEMPLATE.md +0 -54
  101. package/.claude/skills/cc-act/scripts/generate-status-report.sh +0 -92
  102. package/.claude/skills/cc-act/scripts/sync-act-docs.sh +0 -355
  103. package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +0 -234
  104. package/.claude/skills/cc-check/scripts/render-report-card.js +0 -438
  105. package/.claude/skills/cc-check/scripts/verify-gate.sh +0 -85
  106. package/.claude/skills/cc-do/scripts/build-task-context.sh +0 -175
  107. package/.claude/skills/cc-do/scripts/record-review-decision.sh +0 -88
  108. package/.claude/skills/cc-do/scripts/recover-workflow.sh +0 -82
  109. package/.claude/skills/cc-do/scripts/run-problem-analysis.sh +0 -70
  110. package/.claude/skills/cc-do/scripts/verify-task-gates.sh +0 -109
  111. package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +0 -92
  112. package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +0 -224
  113. package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +0 -178
  114. package/.claude/skills/cc-spec-init/assets/CHANGE_META_TEMPLATE.json +0 -28
  115. package/.claude/skills/cc-spec-init/scripts/validate-spec-links.sh +0 -45
  116. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +0 -234
  117. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +0 -488
  118. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +0 -189
  119. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/resume-index.md +0 -39
  120. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/status.md +0 -29
  121. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +0 -123
  122. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +0 -292
  123. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +0 -136
  124. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/status.md +0 -29
  125. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +0 -124
  126. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +0 -292
  127. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +0 -136
  128. package/docs/get-shit-done-strategy-audit.md +0 -518
  129. package/docs/skill-runtime-migration.md +0 -46
  130. package/lib/skill-runtime/__tests__/approve.test.js +0 -92
  131. package/lib/skill-runtime/__tests__/autopilot.test.js +0 -253
  132. package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +0 -165
  133. package/lib/skill-runtime/__tests__/delegation.test.js +0 -97
  134. package/lib/skill-runtime/__tests__/dispatch.test.js +0 -237
  135. package/lib/skill-runtime/__tests__/intent.test.js +0 -203
  136. package/lib/skill-runtime/__tests__/lifecycle.test.js +0 -169
  137. package/lib/skill-runtime/__tests__/planner.tdd.test.js +0 -331
  138. package/lib/skill-runtime/__tests__/prepare-pr.test.js +0 -126
  139. package/lib/skill-runtime/__tests__/query.test.js +0 -860
  140. package/lib/skill-runtime/__tests__/readiness.test.js +0 -53
  141. package/lib/skill-runtime/__tests__/release.test.js +0 -85
  142. package/lib/skill-runtime/__tests__/review-check-integration.test.js +0 -148
  143. package/lib/skill-runtime/__tests__/review-records.test.js +0 -619
  144. package/lib/skill-runtime/__tests__/runtime.integration.test.js +0 -351
  145. package/lib/skill-runtime/__tests__/schemas.test.js +0 -337
  146. package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +0 -137
  147. package/lib/skill-runtime/__tests__/task-contract.test.js +0 -874
  148. package/lib/skill-runtime/__tests__/team-state.test.js +0 -51
  149. package/lib/skill-runtime/__tests__/verify-artifacts.test.js +0 -203
  150. package/lib/skill-runtime/__tests__/worker-run.test.js +0 -275
  151. package/lib/skill-runtime/__tests__/worker.test.js +0 -56
  152. package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +0 -31
  153. package/lib/skill-runtime/__tests__/workflow-context.test.js +0 -98
  154. package/lib/skill-runtime/artifacts.js +0 -88
  155. package/lib/skill-runtime/context-index.js +0 -545
  156. package/lib/skill-runtime/delegation.js +0 -533
  157. package/lib/skill-runtime/intent.js +0 -309
  158. package/lib/skill-runtime/lifecycle.js +0 -294
  159. package/lib/skill-runtime/operations/CLAUDE.md +0 -19
  160. package/lib/skill-runtime/operations/approve.js +0 -81
  161. package/lib/skill-runtime/operations/autopilot-core.js +0 -337
  162. package/lib/skill-runtime/operations/autopilot-execution.js +0 -307
  163. package/lib/skill-runtime/operations/autopilot-shared.js +0 -48
  164. package/lib/skill-runtime/operations/autopilot.js +0 -163
  165. package/lib/skill-runtime/operations/dispatch.js +0 -416
  166. package/lib/skill-runtime/operations/init.js +0 -60
  167. package/lib/skill-runtime/operations/janitor.js +0 -61
  168. package/lib/skill-runtime/operations/plan.js +0 -59
  169. package/lib/skill-runtime/operations/prepare-pr.js +0 -25
  170. package/lib/skill-runtime/operations/release.js +0 -99
  171. package/lib/skill-runtime/operations/resume.js +0 -126
  172. package/lib/skill-runtime/operations/review-records.js +0 -265
  173. package/lib/skill-runtime/operations/snapshot.js +0 -45
  174. package/lib/skill-runtime/operations/task-contract.js +0 -593
  175. package/lib/skill-runtime/operations/verify.js +0 -170
  176. package/lib/skill-runtime/operations/worker-run.js +0 -531
  177. package/lib/skill-runtime/operations/worker.js +0 -33
  178. package/lib/skill-runtime/planner.js +0 -539
  179. package/lib/skill-runtime/readiness.js +0 -84
  180. package/lib/skill-runtime/review-records.js +0 -123
  181. package/lib/skill-runtime/review.js +0 -855
  182. package/lib/skill-runtime/schemas.js +0 -746
  183. package/lib/skill-runtime/task-contract.js +0 -188
  184. package/lib/skill-runtime/team-state.js +0 -122
  185. package/lib/skill-runtime/workflow-context.js +0 -748
@@ -1,855 +0,0 @@
1
- /**
2
- * [INPUT]: 依赖 manifest/runtime/codex 输出,接收 repoRoot、changeId、strict、skipReview 等上下文。
3
- * [OUTPUT]: 生成任务层与需求层的统一审查结果,包含 reviewer、findings、summary 与 blocking 结论。
4
- * [POS]: skill runtime 原生审查层,被 verify/intent/query 等阶段复用。
5
- * [PROTOCOL]: 变更时更新此头部,然后检查 CLAUDE.md
6
- */
7
-
8
- const {
9
- exists,
10
- readJson,
11
- readText,
12
- runCommand
13
- } = require('./store');
14
- const { getChangePaths } = require('./paths');
15
- const { parseReviewFindingsDoc } = require('./schemas');
16
- const {
17
- getReviewFindingsPath,
18
- getReviewLedgerPath,
19
- parseReviewLedger
20
- } = require('./review-records');
21
-
22
- const CODEX_BOUNDARY = [
23
- 'IMPORTANT: Do NOT read or execute any files under ~/.claude/, ~/.agents/, .claude/skills/, or agents/.',
24
- 'These are skill definitions for other AI systems and are not the repository under review.',
25
- 'Stay focused on the repository code and the current diff only.'
26
- ].join(' ');
27
-
28
- const REVIEW_ORDER = ['pass', 'skipped', 'blocked', 'fail'];
29
- const TASK_REVIEW_KINDS = ['spec', 'code'];
30
-
31
- function shellEscape(value) {
32
- return `'${String(value).replace(/'/g, `'\\''`)}'`;
33
- }
34
-
35
- function normalizeReviewStatus(value, fallback = 'pending') {
36
- const normalized = String(value || fallback).trim().toLowerCase();
37
- if (['pass', 'fail', 'blocked', 'pending', 'skipped'].includes(normalized)) {
38
- return normalized;
39
- }
40
- return fallback;
41
- }
42
-
43
- function statusRank(status) {
44
- const index = REVIEW_ORDER.indexOf(status);
45
- return index === -1 ? REVIEW_ORDER.length : index;
46
- }
47
-
48
- function mergeSectionStatus(current, next) {
49
- return statusRank(next) > statusRank(current) ? next : current;
50
- }
51
-
52
- function severityRank(severity) {
53
- return {
54
- critical: 4,
55
- important: 3,
56
- minor: 2,
57
- info: 1
58
- }[severity] || 0;
59
- }
60
-
61
- function truncate(value, max = 240) {
62
- const normalized = String(value || '').replace(/\s+/g, ' ').trim();
63
- if (normalized.length <= max) {
64
- return normalized;
65
- }
66
- return `${normalized.slice(0, max - 3).trimEnd()}...`;
67
- }
68
-
69
- function makeEvidence(kind, label, ref = '', observation = '') {
70
- return {
71
- kind,
72
- label,
73
- ref,
74
- observation: truncate(observation, 400)
75
- };
76
- }
77
-
78
- function makeFinding({
79
- id,
80
- source,
81
- scope,
82
- category,
83
- severity,
84
- summary,
85
- details = '',
86
- file,
87
- line,
88
- action = 'none',
89
- status = 'open',
90
- fingerprint,
91
- confidenceScore,
92
- displayTier
93
- }) {
94
- const derivedConfidenceScore = severityRank(severity) >= severityRank('important') ? 8 : 6;
95
- return {
96
- id,
97
- source,
98
- scope,
99
- category,
100
- severity,
101
- summary,
102
- details: truncate(details, 600),
103
- ...(file ? { file } : {}),
104
- ...(typeof line === 'number' ? { line } : {}),
105
- action,
106
- status,
107
- confidenceScore: confidenceScore || derivedConfidenceScore,
108
- fingerprint: fingerprint || `${source}:${category}:${id}`,
109
- displayTier: displayTier || (status === 'informational'
110
- ? 'info'
111
- : (severityRank(severity) >= severityRank('important') ? 'blocking' : 'warning')),
112
- suppressionReason: null
113
- };
114
- }
115
-
116
- function makeReviewer({
117
- key,
118
- scope,
119
- mode,
120
- source,
121
- status,
122
- summary,
123
- evidence = [],
124
- findings = []
125
- }) {
126
- return {
127
- key,
128
- scope,
129
- mode,
130
- source,
131
- status,
132
- summary: truncate(summary, 300),
133
- evidence,
134
- findings
135
- };
136
- }
137
-
138
- function mapPriorityTag(tag) {
139
- switch (tag) {
140
- case 'P1':
141
- return 'critical';
142
- case 'P2':
143
- return 'important';
144
- case 'P3':
145
- return 'minor';
146
- default:
147
- return 'info';
148
- }
149
- }
150
-
151
- function extractLocation(text) {
152
- const match = String(text || '').match(/([A-Za-z0-9_./-]+\.[A-Za-z0-9]+):(\d+)/);
153
- if (!match) {
154
- return {};
155
- }
156
-
157
- return {
158
- file: match[1],
159
- line: Number(match[2])
160
- };
161
- }
162
-
163
- function parseCodexStructuredOutput(rawOutput = '') {
164
- const findings = [];
165
- const lines = String(rawOutput || '').split(/\r?\n/);
166
-
167
- for (const line of lines) {
168
- const match = line.match(/\[(P\d)\]\s*(.+)$/);
169
- if (!match) {
170
- continue;
171
- }
172
-
173
- const [, tag, summary] = match;
174
- const severity = mapPriorityTag(tag);
175
- const location = extractLocation(line);
176
-
177
- findings.push(makeFinding({
178
- id: `codex-structured-${findings.length + 1}`,
179
- source: 'codex:structured',
180
- scope: 'requirement',
181
- category: 'diff-review',
182
- severity,
183
- summary: truncate(summary, 240),
184
- details: line,
185
- action: severityRank(severity) >= severityRank('important') ? 'fix_now' : 'follow_up',
186
- ...location,
187
- fingerprint: location.file
188
- ? `${location.file}:${location.line || 0}:diff-review:${tag}`
189
- : `codex-structured:${findings.length + 1}`
190
- }));
191
- }
192
-
193
- return findings;
194
- }
195
-
196
- function parseJsonLine(line) {
197
- try {
198
- return JSON.parse(line);
199
- } catch {
200
- return null;
201
- }
202
- }
203
-
204
- function parseCodexAdversarialOutput(rawOutput = '') {
205
- const findings = [];
206
- const lines = String(rawOutput || '').split(/\r?\n/);
207
-
208
- for (const line of lines) {
209
- const trimmed = line.trim();
210
- if (!trimmed || trimmed === 'NO_FINDINGS') {
211
- continue;
212
- }
213
-
214
- const parsed = parseJsonLine(trimmed);
215
- if (!parsed || !parsed.summary) {
216
- continue;
217
- }
218
-
219
- const severity = ['critical', 'important', 'minor', 'info'].includes(parsed.severity)
220
- ? parsed.severity
221
- : 'important';
222
-
223
- findings.push(makeFinding({
224
- id: parsed.id || `codex-adversarial-${findings.length + 1}`,
225
- source: 'codex:adversarial',
226
- scope: 'requirement',
227
- category: parsed.category || 'failure-mode',
228
- severity,
229
- summary: truncate(parsed.summary, 240),
230
- details: parsed.details || '',
231
- file: parsed.file,
232
- line: typeof parsed.line === 'number' ? parsed.line : undefined,
233
- action: parsed.action || (severityRank(severity) >= severityRank('important') ? 'cc-investigate' : 'follow_up'),
234
- fingerprint: parsed.fingerprint
235
- }));
236
- }
237
-
238
- return findings;
239
- }
240
-
241
- async function detectBaseRef(repoRoot) {
242
- for (const candidate of ['main', 'origin/main', 'master', 'origin/master', 'HEAD~1']) {
243
- const result = await runCommand(`git rev-parse --verify ${candidate}`, { cwd: repoRoot });
244
- if (result.code === 0) {
245
- return candidate;
246
- }
247
- }
248
-
249
- return 'HEAD~1';
250
- }
251
-
252
- async function detectCurrentCommit(repoRoot) {
253
- const result = await runCommand('git rev-parse --short HEAD', { cwd: repoRoot });
254
- if (result.code === 0 && result.stdout.trim()) {
255
- return result.stdout.trim();
256
- }
257
- return 'working-tree';
258
- }
259
-
260
- async function runCodexStructuredReview({ repoRoot, baseRef }) {
261
- const command = [
262
- 'codex review',
263
- shellEscape(`${CODEX_BOUNDARY}\n\nReview the diff against the base branch. Focus on correctness, requirement drift, missing tests, and production risks.`),
264
- '--base',
265
- shellEscape(baseRef),
266
- '-c',
267
- shellEscape('model_reasoning_effort="high"')
268
- ].join(' ');
269
- const result = await runCommand(command, {
270
- cwd: repoRoot,
271
- timeoutMs: 30 * 60 * 1000
272
- });
273
- const findings = result.code === 0 ? parseCodexStructuredOutput(result.stdout) : [];
274
-
275
- return {
276
- result,
277
- findings
278
- };
279
- }
280
-
281
- async function runCodexAdversarialReview({ repoRoot, baseRef }) {
282
- const prompt = [
283
- CODEX_BOUNDARY,
284
- '',
285
- `Review the changes on this branch against ${baseRef}.`,
286
- 'Run git diff against the base branch before answering.',
287
- 'Output ONLY JSON objects, one per line, with keys:',
288
- 'id, severity (critical|important|minor|info), category, summary, details, file, line, action, fingerprint.',
289
- 'If nothing stands out, output NO_FINDINGS.',
290
- 'Be adversarial. Focus on edge cases, failure modes, unsafe assumptions, and silent data corruption.'
291
- ].join('\n');
292
- const command = [
293
- 'codex exec',
294
- shellEscape(prompt),
295
- '-C',
296
- shellEscape(repoRoot),
297
- '-s',
298
- 'read-only',
299
- '-c',
300
- shellEscape('model_reasoning_effort="high"')
301
- ].join(' ');
302
- const result = await runCommand(command, {
303
- cwd: repoRoot,
304
- timeoutMs: 15 * 60 * 1000
305
- });
306
- const findings = result.code === 0 ? parseCodexAdversarialOutput(result.stdout) : [];
307
-
308
- return {
309
- result,
310
- findings
311
- };
312
- }
313
-
314
- function collectTaskReviewEvidence({ task, kind }) {
315
- const manifestVerdict = normalizeReviewStatus(task.reviews?.[kind], 'pending');
316
-
317
- return {
318
- verdict: manifestVerdict,
319
- summary: ''
320
- };
321
- }
322
-
323
- async function runTaskReviewSection({ repoRoot, changeId, manifest }) {
324
- const passedTasks = (manifest.tasks || []).filter((task) => task.status === 'passed');
325
- if (passedTasks.length === 0) {
326
- return {
327
- status: 'skipped',
328
- required: false,
329
- summary: 'No passed tasks yet, task-level review gate skipped.',
330
- reviewers: [],
331
- findings: []
332
- };
333
- }
334
-
335
- let status = 'pass';
336
- const reviewers = [];
337
- const findings = [];
338
-
339
- for (const task of passedTasks) {
340
- for (const kind of TASK_REVIEW_KINDS) {
341
- const evidence = collectTaskReviewEvidence({ task, kind });
342
- const verdict = normalizeReviewStatus(evidence.verdict, 'pending');
343
- const reviewerStatus = verdict === 'pending' ? 'blocked' : verdict;
344
- let summary = evidence.summary || `${task.id} ${kind} review is ${reviewerStatus}.`;
345
-
346
- if (reviewerStatus === 'blocked') {
347
- summary = `${task.id} is missing ${kind} review proof.`;
348
- findings.push(makeFinding({
349
- id: `${task.id}-${kind}-missing`,
350
- source: 'task-review',
351
- scope: 'task',
352
- category: `${kind}-review`,
353
- severity: 'important',
354
- summary,
355
- details: 'Completed tasks must carry spec/code review proof before cc-check can pass.',
356
- action: 'fix_now',
357
- fingerprint: `${task.id}:${kind}:missing`
358
- }));
359
- } else if (reviewerStatus === 'fail') {
360
- findings.push(makeFinding({
361
- id: `${task.id}-${kind}-failed`,
362
- source: 'task-review',
363
- scope: 'task',
364
- category: `${kind}-review`,
365
- severity: 'critical',
366
- summary: `${task.id} ${kind} review failed.`,
367
- details: summary,
368
- action: 'fix_now',
369
- fingerprint: `${task.id}:${kind}:failed`
370
- }));
371
- }
372
-
373
- reviewers.push(makeReviewer({
374
- key: `task-${task.id}-${kind}`,
375
- scope: 'task',
376
- mode: kind,
377
- source: 'manifest',
378
- status: reviewerStatus,
379
- summary,
380
- evidence: [
381
- makeEvidence('note', `${task.id} ${kind} review state`, '', `Manifest verdict: ${verdict}`)
382
- ],
383
- findings: findings.filter((item) => item.id.startsWith(`${task.id}-${kind}`))
384
- }));
385
-
386
- status = mergeSectionStatus(status, reviewerStatus);
387
- }
388
- }
389
-
390
- const openIssues = findings.filter((item) => item.status === 'open').length;
391
- const reviewedPairs = reviewers.filter((item) => item.status === 'pass' || item.status === 'skipped').length;
392
-
393
- return {
394
- status,
395
- required: true,
396
- summary: `${reviewedPairs}/${passedTasks.length * TASK_REVIEW_KINDS.length} task review gates cleared. Open review findings: ${openIssues}.`,
397
- reviewers,
398
- findings
399
- };
400
- }
401
-
402
- function buildCodexErrorFinding(source, summary, details, severity = 'important') {
403
- return makeFinding({
404
- id: `${source}-unavailable`,
405
- source,
406
- scope: 'requirement',
407
- category: 'review-runtime',
408
- severity,
409
- summary,
410
- details,
411
- action: 'cc-investigate',
412
- fingerprint: `${source}:runtime`
413
- });
414
- }
415
-
416
- async function firstExistingPath(paths) {
417
- for (const filePath of paths) {
418
- if (await exists(filePath)) {
419
- return filePath;
420
- }
421
- }
422
- return '';
423
- }
424
-
425
- function recordStatus(summary = {}, findings = []) {
426
- if (summary.status === 'blocked') {
427
- return 'blocked';
428
- }
429
- if (
430
- summary.status === 'findings'
431
- || Number(summary.blockingCount || 0) > 0
432
- || findings.some((item) => item.displayTier === 'blocking' || severityRank(item.severity) >= severityRank('important'))
433
- ) {
434
- return 'fail';
435
- }
436
- return 'pass';
437
- }
438
-
439
- function reviewRecordSeverity(value) {
440
- return value === 'advisory' ? 'minor' : value;
441
- }
442
-
443
- function reviewRecordAction(route, displayTier) {
444
- if (route === 'cc-plan') return 'reroute-cc-plan';
445
- if (route === 'cc-investigate') return 'reroute-cc-investigate';
446
- if (route === 'cc-do') return displayTier === 'blocking' ? 'fix_now' : 'reroute-cc-do';
447
- return displayTier === 'blocking' ? 'fix_now' : 'follow_up';
448
- }
449
-
450
- function makeReviewRecordFinding(item, index) {
451
- const displayTier = item.displayTier || 'blocking';
452
- const status = displayTier === 'info'
453
- ? 'informational'
454
- : (displayTier === 'suppressed' ? 'accepted' : 'open');
455
- return makeFinding({
456
- id: item.id || item.findingId || `review-record-${index + 1}`,
457
- source: 'review-records',
458
- scope: 'requirement',
459
- category: 'review-record',
460
- severity: reviewRecordSeverity(item.severity),
461
- summary: truncate(item.evidence || item.recommendation || 'Review finding', 240),
462
- details: item.recommendation || item.evidence || '',
463
- file: item.path,
464
- action: reviewRecordAction(item.route, displayTier),
465
- status,
466
- fingerprint: item.fingerprint,
467
- confidenceScore: Number(item.confidence) || undefined,
468
- displayTier
469
- });
470
- }
471
-
472
- function normalizeFreshness(freshness = {}) {
473
- return {
474
- status: freshness.status || 'unknown',
475
- reviewedCommit: freshness.reviewedCommit || '',
476
- currentCommit: freshness.currentCommit || '',
477
- commitsSinceReview: freshness.commitsSinceReview ?? null,
478
- staleReason: freshness.staleReason || ''
479
- };
480
- }
481
-
482
- function makeRecordReviewer(recordReview) {
483
- return makeReviewer({
484
- key: 'requirement-review-records',
485
- scope: 'requirement',
486
- mode: 'structured',
487
- source: 'runtime',
488
- status: recordReview.status,
489
- summary: recordReview.summary,
490
- evidence: [
491
- makeEvidence('file', 'review record source', recordReview.source, recordReview.summary)
492
- ],
493
- findings: recordReview.findings || []
494
- });
495
- }
496
-
497
- async function readReviewFindingsDoc(filePath) {
498
- try {
499
- const doc = parseReviewFindingsDoc(await readJson(filePath));
500
- const findings = doc.findings.map(makeReviewRecordFinding);
501
- return {
502
- source: 'review-findings.json',
503
- status: recordStatus(doc.summary, findings),
504
- required: true,
505
- summary: `review-findings.json reports ${doc.summary.status} with ${doc.summary.blockingCount} blocking finding(s).`,
506
- freshness: normalizeFreshness(doc.freshness),
507
- reviewers: [],
508
- findings,
509
- errors: []
510
- };
511
- } catch (error) {
512
- return {
513
- source: 'review-findings.json',
514
- status: 'blocked',
515
- required: true,
516
- summary: `review-findings.json is unreadable: ${error.message}`,
517
- freshness: normalizeFreshness({ status: 'unknown', staleReason: error.message }),
518
- reviewers: [],
519
- findings: [makeFinding({
520
- id: 'review-findings-unreadable',
521
- source: 'review-records',
522
- scope: 'requirement',
523
- category: 'review-record',
524
- severity: 'important',
525
- summary: 'review-findings.json is unreadable.',
526
- details: error.message,
527
- action: 'fix_now'
528
- })],
529
- errors: [error.message]
530
- };
531
- }
532
- }
533
-
534
- async function readReviewLedger(filePath) {
535
- const parsed = parseReviewLedger(await readText(filePath, ''));
536
- const findings = parsed.findings.map(makeReviewRecordFinding);
537
- return {
538
- source: 'review-ledger.jsonl',
539
- status: recordStatus(parsed.summary, findings),
540
- required: true,
541
- summary: `review ledger reports ${parsed.summary.status} with ${parsed.summary.blockingCount} blocking finding(s).`,
542
- freshness: normalizeFreshness(parsed.freshness),
543
- reviewers: [],
544
- findings,
545
- errors: parsed.errors
546
- };
547
- }
548
-
549
- async function runReviewRecordSection({ repoRoot, changeId }) {
550
- const change = getChangePaths(repoRoot, changeId);
551
- const findingsPath = await firstExistingPath([
552
- getReviewFindingsPath(repoRoot, changeId, { changeKey: change.changeKey }),
553
- `${change.reviewDir}/cc-review-findings.json`
554
- ]);
555
- if (findingsPath) {
556
- const recordReview = await readReviewFindingsDoc(findingsPath);
557
- return {
558
- ...recordReview,
559
- reviewers: [makeRecordReviewer(recordReview)]
560
- };
561
- }
562
-
563
- const ledgerPath = await firstExistingPath([
564
- getReviewLedgerPath(repoRoot, changeId, { changeKey: change.changeKey }),
565
- `${change.reviewDir}/cc-review-ledger.jsonl`
566
- ]);
567
- if (ledgerPath) {
568
- const recordReview = await readReviewLedger(ledgerPath);
569
- return {
570
- ...recordReview,
571
- reviewers: [makeRecordReviewer(recordReview)]
572
- };
573
- }
574
-
575
- const legacyReportPath = await firstExistingPath([
576
- `${change.reviewDir}/cc-review-report.md`
577
- ]);
578
- if (legacyReportPath) {
579
- const recordReview = {
580
- source: 'cc-review-report.md',
581
- status: 'pass',
582
- required: true,
583
- summary: 'legacy cc-review-report.md found; review freshness is unknown.',
584
- freshness: normalizeFreshness({
585
- status: 'unknown',
586
- staleReason: 'legacy cc-review-report.md has no machine freshness fields'
587
- }),
588
- reviewers: [],
589
- findings: [],
590
- errors: []
591
- };
592
- return {
593
- ...recordReview,
594
- reviewers: [makeRecordReviewer(recordReview)]
595
- };
596
- }
597
-
598
- const finding = makeFinding({
599
- id: 'review-missing',
600
- source: 'review-records',
601
- scope: 'requirement',
602
- category: 'review-record',
603
- severity: 'important',
604
- summary: 'review-missing: no review records are present.',
605
- details: 'Expected review-findings.json, review-ledger.jsonl, or legacy cc-review-report.md before cc-check can pass.',
606
- action: 'fix_now',
607
- fingerprint: `${change.changeKey}:review-missing`
608
- });
609
- const recordReview = {
610
- source: 'review-records',
611
- status: 'blocked',
612
- required: true,
613
- summary: 'review-missing: no review records found.',
614
- freshness: normalizeFreshness({
615
- status: 'unknown',
616
- staleReason: 'review-missing'
617
- }),
618
- reviewers: [],
619
- findings: [finding],
620
- errors: ['review-missing']
621
- };
622
- return {
623
- ...recordReview,
624
- reviewers: [makeRecordReviewer(recordReview)]
625
- };
626
- }
627
-
628
- async function runDiffReviewSection({ repoRoot, strict, skipReview }) {
629
- if (!strict) {
630
- return {
631
- status: 'skipped',
632
- required: false,
633
- summary: 'Strict mode disabled, diff review skipped.',
634
- reviewers: [],
635
- findings: []
636
- };
637
- }
638
-
639
- if (skipReview) {
640
- return {
641
- status: 'skipped',
642
- required: true,
643
- summary: 'Diff review skipped by --skip-review.',
644
- reviewers: [],
645
- findings: []
646
- };
647
- }
648
-
649
- const hasCodex = await runCommand('command -v codex', { cwd: repoRoot });
650
- if (hasCodex.code !== 0) {
651
- const finding = buildCodexErrorFinding(
652
- 'codex',
653
- 'Codex CLI is required for strict diff review.',
654
- 'Run strict verify on a machine with codex installed, or use --skip-review intentionally.'
655
- );
656
-
657
- return {
658
- status: 'blocked',
659
- required: true,
660
- summary: finding.summary,
661
- reviewers: [],
662
- findings: [finding]
663
- };
664
- }
665
-
666
- const baseRef = await detectBaseRef(repoRoot);
667
- const reviewers = [];
668
- const findings = [];
669
- let status = 'pass';
670
-
671
- const structured = await runCodexStructuredReview({ repoRoot, baseRef });
672
- const structuredError = structured.result.code !== 0
673
- ? buildCodexErrorFinding(
674
- 'codex:structured',
675
- 'Codex structured diff review failed to run.',
676
- structured.result.stderr || structured.result.stdout || 'codex review failed'
677
- )
678
- : null;
679
- const structuredFindings = structuredError ? [structuredError] : structured.findings;
680
- if (structuredError) {
681
- status = mergeSectionStatus(status, 'blocked');
682
- } else if (structuredFindings.some((item) => severityRank(item.severity) >= severityRank('important'))) {
683
- status = mergeSectionStatus(status, 'fail');
684
- }
685
- findings.push(...structuredFindings);
686
- reviewers.push(makeReviewer({
687
- key: 'requirement-structured',
688
- scope: 'requirement',
689
- mode: 'structured',
690
- source: 'codex',
691
- status: structuredError ? 'blocked' : (structuredFindings.length > 0 ? 'fail' : 'pass'),
692
- summary: structuredError
693
- ? structuredError.summary
694
- : (structuredFindings.length > 0
695
- ? `Structured diff review found ${structuredFindings.length} issue(s).`
696
- : 'Structured diff review passed with no parsed findings.'),
697
- evidence: [
698
- makeEvidence('command', 'codex review', `base=${baseRef}`, structured.result.stderr || structured.result.stdout)
699
- ],
700
- findings: structuredFindings
701
- }));
702
-
703
- const adversarial = await runCodexAdversarialReview({ repoRoot, baseRef });
704
- const adversarialError = adversarial.result.code !== 0
705
- ? buildCodexErrorFinding(
706
- 'codex:adversarial',
707
- 'Codex adversarial review was unavailable.',
708
- adversarial.result.stderr || adversarial.result.stdout || 'codex exec failed',
709
- 'minor'
710
- )
711
- : null;
712
- const adversarialFindings = adversarialError ? [adversarialError] : adversarial.findings;
713
- if (!adversarialError && adversarialFindings.some((item) => severityRank(item.severity) >= severityRank('important'))) {
714
- status = mergeSectionStatus(status, 'fail');
715
- }
716
- findings.push(...adversarialFindings);
717
- reviewers.push(makeReviewer({
718
- key: 'requirement-adversarial',
719
- scope: 'requirement',
720
- mode: 'adversarial',
721
- source: 'codex',
722
- status: adversarialError ? 'blocked' : (adversarialFindings.length > 0 ? 'fail' : 'pass'),
723
- summary: adversarialError
724
- ? adversarialError.summary
725
- : (adversarialFindings.length > 0
726
- ? `Adversarial diff review found ${adversarialFindings.length} issue(s).`
727
- : 'Adversarial diff review found no issues.'),
728
- evidence: [
729
- makeEvidence('command', 'codex exec adversarial', `base=${baseRef}`, adversarial.result.stderr || adversarial.result.stdout)
730
- ],
731
- findings: adversarialFindings
732
- }));
733
-
734
- if (structuredError && !adversarialError) {
735
- status = 'blocked';
736
- }
737
-
738
- findings.sort((left, right) => severityRank(right.severity) - severityRank(left.severity));
739
-
740
- return {
741
- status,
742
- required: true,
743
- summary: `Base ${baseRef}. Structured reviewers: ${structuredFindings.length}. Adversarial findings: ${adversarialFindings.length}.`,
744
- reviewers,
745
- findings
746
- };
747
- }
748
-
749
- function flattenFindings(...sections) {
750
- return sections.flatMap((section) => section.findings || []);
751
- }
752
-
753
- function deriveReviewStatus(taskReviews, diffReview, recordReview) {
754
- let status = 'pass';
755
-
756
- for (const section of [taskReviews, recordReview, diffReview]) {
757
- if (!section.required && section.status === 'skipped') {
758
- continue;
759
- }
760
- status = mergeSectionStatus(status, section.status);
761
- }
762
-
763
- return status;
764
- }
765
-
766
- async function runReviewSuite({ repoRoot, changeId, manifest, strict, skipReview }) {
767
- const taskReviews = await runTaskReviewSection({ repoRoot, changeId, manifest });
768
- const recordReview = await runReviewRecordSection({ repoRoot, changeId });
769
- const diffReview = await runDiffReviewSection({ repoRoot, strict, skipReview });
770
- const findings = flattenFindings(taskReviews, recordReview, diffReview);
771
- const status = deriveReviewStatus(taskReviews, diffReview, recordReview);
772
- const currentCommit = await detectCurrentCommit(repoRoot);
773
- const details = [
774
- taskReviews.summary,
775
- recordReview.summary,
776
- diffReview.summary
777
- ].filter(Boolean).join(' ');
778
-
779
- return {
780
- status,
781
- summary: `Task review: ${taskReviews.status}. Record review: ${recordReview.status}. Diff review: ${diffReview.status}.`,
782
- details,
783
- freshness: recordReview.freshness || {
784
- status: 'fresh',
785
- reviewedCommit: currentCommit,
786
- currentCommit,
787
- commitsSinceReview: 0,
788
- staleReason: ''
789
- },
790
- qualityScore: status === 'pass' ? 9 : (status === 'blocked' ? 5 : 3),
791
- specialistReviews: [
792
- {
793
- name: 'testing',
794
- status: taskReviews.status,
795
- required: true,
796
- summary: taskReviews.summary,
797
- skipReason: '',
798
- findings: []
799
- },
800
- {
801
- name: 'review-records',
802
- status: recordReview.status,
803
- required: true,
804
- summary: recordReview.summary,
805
- skipReason: '',
806
- findings: recordReview.findings || []
807
- }
808
- ],
809
- runtime: {
810
- status: status === 'pass' ? 'pass' : (status === 'fail' ? 'fail' : 'blocked'),
811
- failureOwnership: []
812
- },
813
- qa: {
814
- status: 'pass',
815
- regressionProof: [],
816
- testQuality: [],
817
- coverageAudit: {
818
- status: 'pass',
819
- coveragePct: null,
820
- pathMap: [],
821
- gaps: [],
822
- testsAdded: [],
823
- e2eRequired: false,
824
- evalRequired: false,
825
- qualityStars: ''
826
- },
827
- browserEvidence: {
828
- status: 'skipped',
829
- mode: 'not-applicable',
830
- affectedRoutes: [],
831
- screenshots: [],
832
- consoleErrors: [],
833
- healthScore: null,
834
- issues: [],
835
- skipReason: 'runtime verify did not identify a UI browser QA target'
836
- },
837
- tddException: null
838
- },
839
- taskReviews,
840
- recordReview,
841
- diffReview,
842
- findings
843
- };
844
- }
845
-
846
- function summarizeOpenReviewFindings(findings = []) {
847
- return findings
848
- .filter((item) => item.status === 'open' && severityRank(item.severity) >= severityRank('important'))
849
- .map((item) => `${item.source}: ${item.summary}`);
850
- }
851
-
852
- module.exports = {
853
- runReviewSuite,
854
- summarizeOpenReviewFindings
855
- };