cc-devflow 4.5.10 → 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 (187) hide show
  1. package/.claude/skills/cc-act/CHANGELOG.md +23 -0
  2. package/.claude/skills/cc-act/PLAYBOOK.md +17 -269
  3. package/.claude/skills/cc-act/SKILL.md +38 -418
  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 +24 -0
  15. package/.claude/skills/cc-check/PLAYBOOK.md +19 -273
  16. package/.claude/skills/cc-check/SKILL.md +33 -454
  17. package/.claude/skills/cc-check/references/review-contract.md +12 -147
  18. package/.claude/skills/cc-dev/CHANGELOG.md +20 -0
  19. package/.claude/skills/cc-dev/PLAYBOOK.md +1 -1
  20. package/.claude/skills/cc-dev/SKILL.md +52 -130
  21. package/.claude/skills/cc-dev/scripts/resolve-cc-devflow.sh +181 -0
  22. package/.claude/skills/cc-do/CHANGELOG.md +17 -0
  23. package/.claude/skills/cc-do/PLAYBOOK.md +19 -113
  24. package/.claude/skills/cc-do/SKILL.md +39 -236
  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 +23 -0
  31. package/.claude/skills/cc-investigate/PLAYBOOK.md +20 -180
  32. package/.claude/skills/cc-investigate/SKILL.md +65 -513
  33. package/.claude/skills/cc-investigate/assets/TASKS_TEMPLATE.md +48 -95
  34. package/.claude/skills/cc-investigate/references/investigation-contract.md +14 -217
  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 +29 -0
  39. package/.claude/skills/cc-plan/PLAYBOOK.md +22 -161
  40. package/.claude/skills/cc-plan/SKILL.md +47 -640
  41. package/.claude/skills/cc-plan/assets/TASKS_TEMPLATE.md +30 -225
  42. package/.claude/skills/cc-plan/references/planning-contract.md +24 -160
  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 +27 -0
  65. package/README.md +5 -3
  66. package/README.zh-CN.md +5 -3
  67. package/bin/cc-devflow-cli.js +20 -260
  68. package/bin/cc-devflow.js +44 -7
  69. package/docs/commands/README.md +1 -1
  70. package/docs/commands/README.zh-CN.md +1 -1
  71. package/docs/examples/README.md +1 -1
  72. package/docs/examples/START-HERE.md +14 -14
  73. package/docs/examples/example-bindings.json +11 -11
  74. package/docs/examples/full-design-blocked/README.md +4 -6
  75. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/{planning/tasks.md → task.md} +20 -15
  76. package/docs/examples/local-handoff/README.md +8 -11
  77. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/pr-brief.md +31 -0
  78. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/{planning/tasks.md → task.md} +18 -13
  79. package/docs/examples/pdca-loop/README.md +6 -9
  80. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/pr-brief.md +9 -11
  81. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/{planning/tasks.md → task.md} +18 -13
  82. package/docs/examples/scripts/check-example-bindings.sh +11 -62
  83. package/docs/guides/artifact-contract.md +10 -36
  84. package/docs/guides/getting-started.md +8 -7
  85. package/docs/guides/getting-started.zh-CN.md +8 -7
  86. package/docs/guides/minimize-artifacts.md +16 -116
  87. package/docs/guides/project-postmortem.md +14 -71
  88. package/lib/compiler/__tests__/skills-registry.test.js +9 -8
  89. package/lib/compiler/resource-copier.js +29 -0
  90. package/lib/skill-runtime/__tests__/archive-change.test.js +2 -2
  91. package/lib/skill-runtime/__tests__/benchmark-skills.test.js +109 -0
  92. package/lib/skill-runtime/__tests__/cli-bootstrap.integration.test.js +14 -4
  93. package/lib/skill-runtime/errors.js +3 -3
  94. package/lib/skill-runtime/index.js +5 -23
  95. package/lib/skill-runtime/paths.js +5 -52
  96. package/lib/skill-runtime/query-registry.js +4 -4
  97. package/lib/skill-runtime/query.js +89 -201
  98. package/lib/skill-runtime/store.js +4 -40
  99. package/lib/skill-runtime/trace.js +2 -2
  100. package/package.json +5 -7
  101. package/.claude/skills/cc-act/assets/PROJECT_POSTMORTEM_PRINCIPLES_TEMPLATE.md +0 -29
  102. package/.claude/skills/cc-act/assets/RELEASE_NOTE_TEMPLATE.md +0 -54
  103. package/.claude/skills/cc-act/scripts/generate-status-report.sh +0 -92
  104. package/.claude/skills/cc-act/scripts/sync-act-docs.sh +0 -355
  105. package/.claude/skills/cc-check/assets/REPORT_CARD_TEMPLATE.json +0 -234
  106. package/.claude/skills/cc-check/scripts/render-report-card.js +0 -438
  107. package/.claude/skills/cc-check/scripts/verify-gate.sh +0 -85
  108. package/.claude/skills/cc-do/scripts/build-task-context.sh +0 -175
  109. package/.claude/skills/cc-do/scripts/record-review-decision.sh +0 -88
  110. package/.claude/skills/cc-do/scripts/recover-workflow.sh +0 -82
  111. package/.claude/skills/cc-do/scripts/run-problem-analysis.sh +0 -70
  112. package/.claude/skills/cc-do/scripts/verify-task-gates.sh +0 -109
  113. package/.claude/skills/cc-do/scripts/write-task-checkpoint.sh +0 -92
  114. package/.claude/skills/cc-investigate/assets/TASK_MANIFEST_TEMPLATE.json +0 -225
  115. package/.claude/skills/cc-plan/assets/TASK_MANIFEST_TEMPLATE.json +0 -179
  116. package/.claude/skills/cc-spec-init/assets/CHANGE_META_TEMPLATE.json +0 -28
  117. package/.claude/skills/cc-spec-init/scripts/validate-spec-links.sh +0 -45
  118. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/design.md +0 -234
  119. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/planning/task-manifest.json +0 -488
  120. package/docs/examples/full-design-blocked/changes/REQ-002-bulk-invite-import/review/report-card.json +0 -189
  121. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/resume-index.md +0 -39
  122. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/handoff/status.md +0 -29
  123. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/design.md +0 -123
  124. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/planning/task-manifest.json +0 -292
  125. package/docs/examples/local-handoff/changes/REQ-003-audit-log-export/review/report-card.json +0 -136
  126. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/handoff/status.md +0 -29
  127. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/design.md +0 -124
  128. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/planning/task-manifest.json +0 -292
  129. package/docs/examples/pdca-loop/changes/REQ-001-copy-invite-link/review/report-card.json +0 -136
  130. package/docs/get-shit-done-strategy-audit.md +0 -518
  131. package/docs/skill-runtime-migration.md +0 -46
  132. package/lib/skill-runtime/__tests__/approve.test.js +0 -92
  133. package/lib/skill-runtime/__tests__/autopilot.test.js +0 -253
  134. package/lib/skill-runtime/__tests__/benchmark-artifacts.test.js +0 -165
  135. package/lib/skill-runtime/__tests__/delegation.test.js +0 -97
  136. package/lib/skill-runtime/__tests__/dispatch.test.js +0 -237
  137. package/lib/skill-runtime/__tests__/intent.test.js +0 -203
  138. package/lib/skill-runtime/__tests__/lifecycle.test.js +0 -169
  139. package/lib/skill-runtime/__tests__/planner.tdd.test.js +0 -331
  140. package/lib/skill-runtime/__tests__/prepare-pr.test.js +0 -126
  141. package/lib/skill-runtime/__tests__/query.test.js +0 -860
  142. package/lib/skill-runtime/__tests__/readiness.test.js +0 -53
  143. package/lib/skill-runtime/__tests__/release.test.js +0 -85
  144. package/lib/skill-runtime/__tests__/review-check-integration.test.js +0 -148
  145. package/lib/skill-runtime/__tests__/review-records.test.js +0 -619
  146. package/lib/skill-runtime/__tests__/runtime.integration.test.js +0 -351
  147. package/lib/skill-runtime/__tests__/schemas.test.js +0 -337
  148. package/lib/skill-runtime/__tests__/task-contract-migrate.test.js +0 -137
  149. package/lib/skill-runtime/__tests__/task-contract.test.js +0 -783
  150. package/lib/skill-runtime/__tests__/team-state.test.js +0 -51
  151. package/lib/skill-runtime/__tests__/verify-artifacts.test.js +0 -203
  152. package/lib/skill-runtime/__tests__/worker-run.test.js +0 -275
  153. package/lib/skill-runtime/__tests__/worker.test.js +0 -56
  154. package/lib/skill-runtime/__tests__/workflow-context-legacy-fallback.test.js +0 -31
  155. package/lib/skill-runtime/__tests__/workflow-context.test.js +0 -98
  156. package/lib/skill-runtime/artifacts.js +0 -88
  157. package/lib/skill-runtime/context-index.js +0 -545
  158. package/lib/skill-runtime/delegation.js +0 -533
  159. package/lib/skill-runtime/intent.js +0 -309
  160. package/lib/skill-runtime/lifecycle.js +0 -294
  161. package/lib/skill-runtime/operations/CLAUDE.md +0 -19
  162. package/lib/skill-runtime/operations/approve.js +0 -81
  163. package/lib/skill-runtime/operations/autopilot-core.js +0 -337
  164. package/lib/skill-runtime/operations/autopilot-execution.js +0 -307
  165. package/lib/skill-runtime/operations/autopilot-shared.js +0 -48
  166. package/lib/skill-runtime/operations/autopilot.js +0 -163
  167. package/lib/skill-runtime/operations/dispatch.js +0 -416
  168. package/lib/skill-runtime/operations/init.js +0 -60
  169. package/lib/skill-runtime/operations/janitor.js +0 -61
  170. package/lib/skill-runtime/operations/plan.js +0 -59
  171. package/lib/skill-runtime/operations/prepare-pr.js +0 -25
  172. package/lib/skill-runtime/operations/release.js +0 -99
  173. package/lib/skill-runtime/operations/resume.js +0 -126
  174. package/lib/skill-runtime/operations/review-records.js +0 -265
  175. package/lib/skill-runtime/operations/snapshot.js +0 -45
  176. package/lib/skill-runtime/operations/task-contract.js +0 -524
  177. package/lib/skill-runtime/operations/verify.js +0 -170
  178. package/lib/skill-runtime/operations/worker-run.js +0 -531
  179. package/lib/skill-runtime/operations/worker.js +0 -33
  180. package/lib/skill-runtime/planner.js +0 -539
  181. package/lib/skill-runtime/readiness.js +0 -84
  182. package/lib/skill-runtime/review-records.js +0 -123
  183. package/lib/skill-runtime/review.js +0 -855
  184. package/lib/skill-runtime/schemas.js +0 -746
  185. package/lib/skill-runtime/task-contract.js +0 -187
  186. package/lib/skill-runtime/team-state.js +0 -122
  187. 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
- };