@fitlab-ai/agent-infra 0.9.1 → 0.9.2

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 (283) hide show
  1. package/README.md +16 -4
  2. package/README.zh-CN.md +16 -4
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +15 -0
  6. package/dist/lib/agent-client.js +163 -0
  7. package/dist/lib/agent-clients/adapter.js +294 -0
  8. package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
  9. package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
  10. package/dist/lib/agent-clients/adapters/codex.js +54 -0
  11. package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
  12. package/dist/lib/agent-clients/adapters/index.js +20 -0
  13. package/dist/lib/agent-clients/adapters/opencode.js +64 -0
  14. package/dist/lib/agent-clients/config.js +177 -0
  15. package/dist/lib/agent-clients/custom-tuis.js +99 -0
  16. package/dist/lib/agent-clients/invocation.js +9 -0
  17. package/dist/lib/agent-clients/next-steps.js +38 -0
  18. package/dist/lib/agent-clients/project-assets.js +45 -0
  19. package/dist/lib/agent-clients/reconcile.js +235 -0
  20. package/dist/lib/agent-clients/registry.js +72 -0
  21. package/dist/lib/agent-clients/schema.js +57 -0
  22. package/dist/lib/agent-clients/types.js +29 -0
  23. package/dist/lib/builtin-tuis.js +9 -14
  24. package/dist/lib/defaults.json +85 -73
  25. package/dist/lib/git/review-snapshot.js +25 -10
  26. package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
  27. package/dist/lib/init.js +47 -91
  28. package/dist/lib/internal/agent-client.js +117 -0
  29. package/dist/lib/internal/git-workflow.js +1 -1
  30. package/dist/lib/internal/release-workflow.js +71 -16
  31. package/dist/lib/internal/task-orchestration.js +151 -0
  32. package/dist/lib/internal/task-review.js +65 -0
  33. package/dist/lib/merge.js +2 -1
  34. package/dist/lib/platform/adapters.js +38 -1
  35. package/dist/lib/platform/change-request-git-evidence.js +113 -0
  36. package/dist/lib/platform/merged-pr-equivalence.js +41 -11
  37. package/dist/lib/platform/pr-checks.js +38 -10
  38. package/dist/lib/platform/pull-requests.js +110 -6
  39. package/dist/lib/platform/releases.js +1 -1
  40. package/dist/lib/platform/verification-required.js +8 -10
  41. package/dist/lib/prompt.js +15 -3
  42. package/dist/lib/release/channels.js +4 -1
  43. package/dist/lib/release/workflow.js +4 -2
  44. package/dist/lib/remove-dir.js +25 -0
  45. package/dist/lib/run/tui.js +8 -2
  46. package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
  47. package/dist/lib/sandbox/clipboard/linux.js +2 -1
  48. package/dist/lib/sandbox/commands/create.js +76 -180
  49. package/dist/lib/sandbox/commands/enter.js +17 -1
  50. package/dist/lib/sandbox/commands/prune.js +2 -2
  51. package/dist/lib/sandbox/commands/rm.js +3 -2
  52. package/dist/lib/sandbox/config.js +21 -4
  53. package/dist/lib/sandbox/constants.js +3 -0
  54. package/dist/lib/sandbox/dockerfile.js +2 -1
  55. package/dist/lib/sandbox/managed-fs.js +10 -5
  56. package/dist/lib/sandbox/recovery.js +52 -36
  57. package/dist/lib/sandbox/shell.js +1 -1
  58. package/dist/lib/sandbox/tool-types.js +8 -0
  59. package/dist/lib/sandbox/tools.js +2 -126
  60. package/dist/lib/task/artifact-lifecycle.js +1 -49
  61. package/dist/lib/task/commands/decisions.js +50 -20
  62. package/dist/lib/task/delegation-receipts.js +107 -0
  63. package/dist/lib/task/events.js +100 -1
  64. package/dist/lib/task/orchestration.js +420 -0
  65. package/dist/lib/task/review-artifacts.js +143 -59
  66. package/dist/lib/task/review-finalization.js +166 -0
  67. package/dist/lib/task/review-fingerprint.js +4 -0
  68. package/dist/lib/task/verification-checks.js +2 -1
  69. package/dist/lib/task/verification-engine.js +156 -28
  70. package/dist/lib/task/verification-review.js +1 -1
  71. package/dist/lib/task/verification.js +3 -1
  72. package/dist/lib/task/workspace-snapshot.js +34 -0
  73. package/dist/lib/update.js +42 -161
  74. package/lib/agent-client.ts +214 -0
  75. package/lib/agent-clients/adapter.ts +461 -0
  76. package/lib/agent-clients/adapters/claude-code.ts +70 -0
  77. package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
  78. package/lib/agent-clients/adapters/codex.ts +58 -0
  79. package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
  80. package/lib/agent-clients/adapters/index.ts +27 -0
  81. package/lib/agent-clients/adapters/opencode.ts +66 -0
  82. package/lib/agent-clients/config.ts +278 -0
  83. package/lib/agent-clients/custom-tuis.ts +149 -0
  84. package/lib/agent-clients/invocation.ts +19 -0
  85. package/lib/agent-clients/next-steps.ts +72 -0
  86. package/lib/agent-clients/project-assets.ts +96 -0
  87. package/lib/agent-clients/reconcile.ts +324 -0
  88. package/lib/agent-clients/registry.ts +144 -0
  89. package/lib/agent-clients/schema.ts +62 -0
  90. package/lib/agent-clients/types.ts +78 -0
  91. package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
  92. package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
  93. package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
  94. package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
  95. package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
  96. package/lib/builtin-tuis.ts +16 -15
  97. package/lib/defaults.json +24 -12
  98. package/lib/git/review-snapshot.ts +39 -9
  99. package/lib/git/reviewed-commit-equivalence.ts +135 -0
  100. package/lib/init.ts +53 -121
  101. package/lib/internal/agent-client.ts +131 -0
  102. package/lib/internal/git-workflow.ts +1 -1
  103. package/lib/internal/release-workflow.ts +68 -16
  104. package/lib/internal/task-orchestration.ts +132 -0
  105. package/lib/internal/task-review.ts +65 -0
  106. package/lib/merge.ts +2 -1
  107. package/lib/platform/adapters.ts +142 -3
  108. package/lib/platform/change-request-git-evidence.ts +159 -0
  109. package/lib/platform/merged-pr-equivalence.ts +51 -14
  110. package/lib/platform/pr-checks.ts +50 -22
  111. package/lib/platform/pull-requests.ts +134 -25
  112. package/lib/platform/releases.ts +1 -1
  113. package/lib/platform/verification-required.ts +12 -10
  114. package/lib/prompt.ts +16 -3
  115. package/lib/release/channels.ts +4 -1
  116. package/lib/release/workflow.ts +4 -3
  117. package/lib/remove-dir.ts +28 -0
  118. package/lib/run/tui.ts +9 -1
  119. package/lib/sandbox/agent-client-reconciler.ts +378 -0
  120. package/lib/sandbox/clipboard/linux.ts +2 -1
  121. package/lib/sandbox/commands/create.ts +111 -192
  122. package/lib/sandbox/commands/enter.ts +23 -1
  123. package/lib/sandbox/commands/prune.ts +2 -2
  124. package/lib/sandbox/commands/rm.ts +3 -2
  125. package/lib/sandbox/config.ts +31 -4
  126. package/lib/sandbox/constants.ts +6 -0
  127. package/lib/sandbox/dockerfile.ts +2 -1
  128. package/lib/sandbox/managed-fs.ts +18 -5
  129. package/lib/sandbox/recovery.ts +98 -67
  130. package/lib/sandbox/shell.ts +1 -1
  131. package/lib/sandbox/tool-types.ts +143 -0
  132. package/lib/sandbox/tools.ts +5 -157
  133. package/lib/task/artifact-lifecycle.ts +1 -38
  134. package/lib/task/commands/decisions.ts +53 -20
  135. package/lib/task/delegation-receipts.ts +170 -0
  136. package/lib/task/events.ts +110 -2
  137. package/lib/task/orchestration.ts +543 -0
  138. package/lib/task/review-artifacts.ts +201 -69
  139. package/lib/task/review-finalization.ts +275 -0
  140. package/lib/task/review-fingerprint.ts +5 -0
  141. package/lib/task/verification-checks.ts +2 -1
  142. package/lib/task/verification-engine.ts +177 -28
  143. package/lib/task/verification-review.ts +1 -0
  144. package/lib/task/verification.ts +5 -2
  145. package/lib/task/workspace-snapshot.ts +36 -0
  146. package/lib/update.ts +48 -184
  147. package/package.json +2 -2
  148. package/templates/.agents/README.en.md +21 -20
  149. package/templates/.agents/README.zh-CN.md +21 -20
  150. package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
  151. package/templates/.agents/rules/README.en.md +2 -0
  152. package/templates/.agents/rules/README.zh-CN.md +2 -0
  153. package/templates/.agents/rules/commit-and-pr.en.md +1 -0
  154. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
  155. package/templates/.agents/rules/human-decision-context.en.md +14 -12
  156. package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
  157. package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
  158. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
  159. package/templates/.agents/rules/next-step-output.en.md +16 -0
  160. package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
  161. package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
  162. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
  163. package/templates/.agents/rules/release-commands.github.en.md +11 -5
  164. package/templates/.agents/rules/release-commands.github.zh-CN.md +11 -5
  165. package/templates/.agents/rules/review-handshake.en.md +4 -4
  166. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  167. package/templates/.agents/rules/review-method.en.md +113 -0
  168. package/templates/.agents/rules/review-method.zh-CN.md +98 -0
  169. package/templates/.agents/skills/analyze-task/SKILL.en.md +5 -5
  170. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +5 -5
  171. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  172. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
  173. package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
  174. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
  175. package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
  176. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
  177. package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
  178. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
  179. package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
  180. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
  181. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  182. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  183. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
  184. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
  185. package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
  186. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
  187. package/templates/.agents/skills/commit/SKILL.en.md +11 -1
  188. package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
  189. package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
  190. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
  191. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  192. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  193. package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
  194. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
  195. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
  196. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
  197. package/templates/.agents/skills/create-task/SKILL.en.md +11 -11
  198. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +11 -11
  199. package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
  200. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
  201. package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
  202. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
  203. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  204. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  205. package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
  206. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
  207. package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
  208. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
  209. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  210. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  211. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  212. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  213. package/templates/.agents/skills/release/SKILL.en.md +1 -1
  214. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
  215. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
  216. package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
  217. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
  218. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
  219. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
  220. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
  221. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
  222. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
  223. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
  224. package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
  225. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
  226. package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
  227. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
  228. package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
  229. package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
  230. package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
  231. package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
  232. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
  233. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
  234. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
  235. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
  236. package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
  237. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
  238. package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
  239. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
  240. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
  241. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
  242. package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
  243. package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
  244. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
  245. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
  246. package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
  247. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
  248. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
  249. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
  250. package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
  251. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
  252. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
  253. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
  254. package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
  255. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
  256. package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
  257. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
  258. package/templates/.agents/skills/test/SKILL.en.md +4 -4
  259. package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
  260. package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
  261. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
  262. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
  263. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
  264. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
  265. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
  266. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
  267. package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
  268. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
  269. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
  270. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
  271. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
  272. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
  273. package/templates/.claude/commands/run-task.en.md +8 -0
  274. package/templates/.claude/commands/run-task.zh-CN.md +8 -0
  275. package/templates/.claude/settings.json +24 -0
  276. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
  277. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
  278. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
  279. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
  280. package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
  281. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
  282. package/templates/.opencode/commands/run-task.en.md +11 -0
  283. package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
@@ -1,116 +1,248 @@
1
- // Shared helpers for review-artifact parsing.
2
- // Imported by typed artifact lifecycle and verification so the round/verdict vocabulary stays
3
- // in a single source of truth (prevents the cross-file drift this lifecycle
4
- // is designed to eliminate).
5
- import fs from "node:fs";
6
- import path from "node:path";
7
-
8
- export function escapeRegExp(value: string): string {
9
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ type ReviewFindingCounts = {
5
+ blocker: number;
6
+ major: number;
7
+ minor: number;
8
+ };
9
+
10
+ type ReviewVerdict = 'Approved' | 'Changes Requested' | 'Rejected';
11
+ type ReviewSummary = {
12
+ verdict: ReviewVerdict;
13
+ counts: ReviewFindingCounts | null;
14
+ manualValidation: number | null;
15
+ countState: 'placeholders' | 'numeric';
16
+ };
17
+ type ReviewSummaryErrorCode =
18
+ | 'REVIEW_SUMMARY_NOT_FOUND'
19
+ | 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
20
+ | 'REVIEW_SUMMARY_COUNT_MISMATCH';
21
+ type ReviewSummaryParseResult =
22
+ | { ok: true; summary: ReviewSummary; findingsStart: number; findingsEnd: number }
23
+ | { ok: false; code: ReviewSummaryErrorCode; message: string };
24
+ type ReviewSummaryFinalizeResult =
25
+ | { ok: true; changed: boolean; content: string }
26
+ | { ok: false; code: ReviewSummaryErrorCode; message: string };
27
+
28
+ const SUMMARY_HEADINGS = new Set(['审查摘要', 'Review Summary']);
29
+ const PLACEHOLDERS = [
30
+ '{unresolved-blockers}',
31
+ '{unresolved-major}',
32
+ '{unresolved-minor}'
33
+ ] as const;
34
+
35
+ function escapeRegExp(value: string): string {
36
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
10
37
  }
11
38
 
12
- export function maxRound(entries: string[], stem: string): number {
39
+ function maxRound(entries: string[], stem: string): number {
13
40
  let max = 0;
14
41
  for (const entry of entries) {
15
42
  if (entry === `${stem}.md`) {
16
43
  max = Math.max(max, 1);
17
44
  continue;
18
45
  }
19
-
20
46
  const match = entry.match(new RegExp(`^${escapeRegExp(stem)}-r(\\d+)\\.md$`));
21
- if (match) {
22
- max = Math.max(max, Number(match[1]));
23
- }
47
+ if (match) max = Math.max(max, Number(match[1]));
24
48
  }
25
49
  return max;
26
50
  }
27
51
 
28
- export function artifactName(stem: string, round: number): string {
52
+ function artifactName(stem: string, round: number): string {
29
53
  return round === 1 ? `${stem}.md` : `${stem}-r${round}.md`;
30
54
  }
31
55
 
32
- export function normalizeVerdict(raw: unknown): "Approved" | "Changes Requested" | "Rejected" | "" {
56
+ function normalizeVerdict(raw: unknown): ReviewVerdict | '' {
33
57
  const value = String(raw).trim().toLowerCase();
34
- if (value === "通过" || value === "approved") {
35
- return "Approved";
36
- }
37
- if (value === "需要修改" || value === "changes requested") {
38
- return "Changes Requested";
39
- }
40
- if (value === "拒绝" || value === "rejected") {
41
- return "Rejected";
42
- }
43
- return "";
58
+ if (value === '通过' || value === 'approved') return 'Approved';
59
+ if (value === '需要修改' || value === 'changes requested') return 'Changes Requested';
60
+ if (value === '拒绝' || value === 'rejected') return 'Rejected';
61
+ return '';
44
62
  }
45
63
 
46
- export function extractSection(content: string, names: string[]): string {
64
+ function extractSection(content: string, names: string[]): string {
47
65
  const lines = content.split(/\r?\n/);
48
66
  const nameSet = new Set(names);
49
67
  const start = lines.findIndex((line) => {
50
68
  const match = line.trim().match(/^##\s+(.+?)\s*$/);
51
69
  return match ? nameSet.has(match[1]!) : false;
52
70
  });
53
-
54
- if (start === -1) {
55
- return "";
56
- }
57
-
71
+ if (start === -1) return '';
58
72
  const sectionLines = [];
59
73
  for (let index = start + 1; index < lines.length; index += 1) {
60
- if (/^##\s+/.test(lines[index]!)) {
61
- break;
62
- }
74
+ if (/^##\s+/.test(lines[index]!)) break;
63
75
  sectionLines.push(lines[index]);
64
76
  }
65
- return sectionLines.join("\n");
77
+ return sectionLines.join('\n');
66
78
  }
67
79
 
68
- // Parse the canonical verdict out of a review-* artifact.
69
- // Returns { ok, verdict, message }. Verdict collapses Approved into
70
- // "Approved-with-issues" when the findings counts are non-zero.
71
- export function parseVerdict(reviewPath: string) {
72
- if (!fs.existsSync(reviewPath)) {
73
- return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
74
- }
80
+ function summaryBounds(content: string): { start: number; end: number } | null {
81
+ const headings = [...content.matchAll(/^##\s+(.+?)\s*$/gm)]
82
+ .map((match) => ({ index: match.index, end: match.index + match[0].length, name: match[1]! }));
83
+ const summaries = headings.filter((heading) => SUMMARY_HEADINGS.has(heading.name));
84
+ if (summaries.length !== 1) return null;
85
+ const summary = summaries[0]!;
86
+ const next = headings.find((heading) => heading.index > summary.index);
87
+ return { start: summary.end, end: next?.index ?? content.length };
88
+ }
75
89
 
76
- const content = fs.readFileSync(reviewPath, "utf8");
77
- const summary = extractSection(content, ["审查摘要", "Review Summary"]);
78
- const fileName = path.basename(reviewPath);
79
- if (!summary) {
80
- return { ok: false, verdict: null, message: `cannot locate review summary section in ${fileName}` };
81
- }
90
+ function uniqueLine(
91
+ content: string,
92
+ bounds: { start: number; end: number },
93
+ pattern: RegExp
94
+ ): { value: string; start: number; end: number } | null {
95
+ const section = content.slice(bounds.start, bounds.end);
96
+ const matches = [...section.matchAll(pattern)];
97
+ if (matches.length !== 1) return null;
98
+ const match = matches[0]!;
99
+ return {
100
+ value: match[1]!,
101
+ start: bounds.start + match.index,
102
+ end: bounds.start + match.index + match[0].length
103
+ };
104
+ }
105
+
106
+ function parseCounts(value: string): ReviewFindingCounts | null {
107
+ const zh = /^(\d+)\s*阻塞项,\s*(\d+)\s*主要,\s*(\d+)\s*次要(?:\s*\/.*)?$/.exec(value);
108
+ const en = /^(\d+)\s*blockers?,\s*(\d+)\s*majors?,\s*(\d+)\s*minors?(?:\s*\/.*)?$/i.exec(value);
109
+ const match = zh ?? en;
110
+ if (!match) return null;
111
+ const [blocker, major, minor] = match.slice(1, 4).map(Number);
112
+ if (![blocker, major, minor].every(Number.isSafeInteger)) return null;
113
+ return { blocker: blocker!, major: major!, minor: minor! };
114
+ }
82
115
 
83
- const verdictMatch = summary.match(/^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.+?)\s*$/im);
84
- if (!verdictMatch) {
85
- return { ok: false, verdict: null, message: `cannot parse verdict in ${fileName}` };
116
+ function parseManualValidation(value: string): number | null {
117
+ const match = /\*\*(?:人工校验|Manual[- ]validation)\*\*[::]\s*(\d+)/i.exec(value);
118
+ if (!match) return null;
119
+ const count = Number(match[1]);
120
+ return Number.isSafeInteger(count) ? count : null;
121
+ }
122
+
123
+ function hasCanonicalPlaceholders(value: string): boolean {
124
+ const zh = /^\{unresolved-blockers\}\s*阻塞项,\s*\{unresolved-major\}\s*主要,\s*\{unresolved-minor\}\s*次要(?:\s*\/.*)?$/.test(value);
125
+ const en = /^\{unresolved-blockers\}\s*blockers?,\s*\{unresolved-major\}\s*majors?,\s*\{unresolved-minor\}\s*minors?(?:\s*\/.*)?$/i.test(value);
126
+ return zh || en;
127
+ }
128
+
129
+ function parseReviewSummary(content: string): ReviewSummaryParseResult {
130
+ const bounds = summaryBounds(content);
131
+ if (!bounds) {
132
+ return { ok: false, code: 'REVIEW_SUMMARY_NOT_FOUND', message: 'review artifact must contain one review summary section' };
133
+ }
134
+ const verdictLine = uniqueLine(
135
+ content,
136
+ bounds,
137
+ /^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.+?)\s*$/gim
138
+ );
139
+ const findingsLine = uniqueLine(
140
+ content,
141
+ bounds,
142
+ /^[-*]?\s*\*\*(?:发现(AI 可处理)|Findings \(AI-actionable\))\*\*[::]\s*(.+?)\s*$/gim
143
+ );
144
+ const verdict = verdictLine ? normalizeVerdict(verdictLine.value) : '';
145
+ if (!verdict || !findingsLine) {
146
+ return { ok: false, code: 'REVIEW_SUMMARY_NOT_FOUND', message: 'review summary verdict or finding count line is missing or repeated' };
86
147
  }
87
148
 
88
- const verdict = normalizeVerdict(verdictMatch[1]);
89
- if (!verdict) {
149
+ const counts = parseCounts(findingsLine.value);
150
+ if (counts) {
90
151
  return {
91
- ok: false,
92
- verdict: null,
93
- message: `unrecognized verdict '${verdictMatch[1]!.trim()}' in ${fileName}`
152
+ ok: true,
153
+ summary: { verdict, counts, manualValidation: parseManualValidation(findingsLine.value), countState: 'numeric' },
154
+ findingsStart: findingsLine.start,
155
+ findingsEnd: findingsLine.end
94
156
  };
95
157
  }
96
-
97
- if (verdict !== "Approved") {
98
- return { ok: true, verdict };
158
+ const placeholderOccurrences = PLACEHOLDERS.map((token) => (
159
+ [...findingsLine.value.matchAll(new RegExp(escapeRegExp(token), 'g'))].length
160
+ ));
161
+ if (hasCanonicalPlaceholders(findingsLine.value) && placeholderOccurrences.every((count) => count === 1)) {
162
+ return {
163
+ ok: true,
164
+ summary: { verdict, counts: null, manualValidation: parseManualValidation(findingsLine.value), countState: 'placeholders' },
165
+ findingsStart: findingsLine.start,
166
+ findingsEnd: findingsLine.end
167
+ };
99
168
  }
169
+ return {
170
+ ok: false,
171
+ code: 'REVIEW_SUMMARY_PLACEHOLDER_INVALID',
172
+ message: 'review summary finding counts must be three canonical placeholders or non-negative integers'
173
+ };
174
+ }
100
175
 
101
- const findingsMatch = summary.match(/^[-*]?\s*\*\*(?:发现(AI 可处理)|Findings \(AI-actionable\))\*\*[::]\s*(.+?)\s*$/im);
102
- if (!findingsMatch) {
103
- return { ok: false, verdict, message: `cannot parse findings count in ${fileName}` };
104
- }
176
+ function equalCounts(left: ReviewFindingCounts, right: ReviewFindingCounts): boolean {
177
+ return left.blocker === right.blocker && left.major === right.major && left.minor === right.minor;
178
+ }
105
179
 
106
- const counts = findingsMatch[1]!.match(/(\d+)\s*(?:阻塞项|blockers?).*?(\d+)\s*(?:主要|majors?).*?(\d+)\s*(?:次要|minors?)/i);
107
- if (!counts) {
108
- return { ok: false, verdict, message: `cannot parse findings count in ${fileName}` };
180
+ function finalizeReviewSummaryContent(
181
+ content: string,
182
+ counts: ReviewFindingCounts
183
+ ): ReviewSummaryFinalizeResult {
184
+ const parsed = parseReviewSummary(content);
185
+ if (!parsed.ok) return parsed;
186
+ if (parsed.summary.countState === 'numeric') {
187
+ if (equalCounts(parsed.summary.counts!, counts)) return { ok: true, changed: false, content };
188
+ return {
189
+ ok: false,
190
+ code: 'REVIEW_SUMMARY_COUNT_MISMATCH',
191
+ message: 'review summary finding counts do not match the ledger snapshot'
192
+ };
109
193
  }
194
+ let line = content.slice(parsed.findingsStart, parsed.findingsEnd);
195
+ line = line
196
+ .replace(PLACEHOLDERS[0], String(counts.blocker))
197
+ .replace(PLACEHOLDERS[1], String(counts.major))
198
+ .replace(PLACEHOLDERS[2], String(counts.minor));
199
+ return {
200
+ ok: true,
201
+ changed: true,
202
+ content: content.slice(0, parsed.findingsStart) + line + content.slice(parsed.findingsEnd)
203
+ };
204
+ }
110
205
 
111
- const [, blockers, majors, minors] = counts.map(Number);
206
+ function parseVerdict(reviewPath: string):
207
+ | { ok: true; verdict: 'Approved' | 'Approved-with-issues' | 'Changes Requested' | 'Rejected' }
208
+ | { ok: false; verdict: null; message: string } {
209
+ if (!fs.existsSync(reviewPath)) {
210
+ return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
211
+ }
212
+ const parsed = parseReviewSummary(fs.readFileSync(reviewPath, 'utf8'));
213
+ if (!parsed.ok) {
214
+ return {
215
+ ok: false,
216
+ verdict: null,
217
+ message: `cannot parse review summary in ${path.basename(reviewPath)}: ${parsed.message}`
218
+ };
219
+ }
220
+ const { verdict, counts } = parsed.summary;
221
+ if (verdict !== 'Approved') return { ok: true, verdict };
112
222
  return {
113
223
  ok: true,
114
- verdict: blockers === 0 && majors === 0 && minors === 0 ? "Approved" : "Approved-with-issues"
224
+ verdict: counts && equalCounts(counts, { blocker: 0, major: 0, minor: 0 })
225
+ ? 'Approved'
226
+ : 'Approved-with-issues'
115
227
  };
116
228
  }
229
+
230
+ export {
231
+ artifactName,
232
+ equalCounts,
233
+ escapeRegExp,
234
+ extractSection,
235
+ finalizeReviewSummaryContent,
236
+ maxRound,
237
+ normalizeVerdict,
238
+ parseReviewSummary,
239
+ parseVerdict
240
+ };
241
+ export type {
242
+ ReviewFindingCounts,
243
+ ReviewSummary,
244
+ ReviewSummaryErrorCode,
245
+ ReviewSummaryFinalizeResult,
246
+ ReviewSummaryParseResult,
247
+ ReviewVerdict
248
+ };
@@ -0,0 +1,275 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+
5
+ import { locateActivityLog, pairEntries } from './activity-log.ts';
6
+ import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.ts';
7
+ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
8
+ import type { LedgerStageStatus, ReviewStage } from './ledger.ts';
9
+ import { finalizeReviewSummaryContent } from './review-artifacts.ts';
10
+ import { resolveTaskRef } from './resolve-ref.ts';
11
+ import { validateOrchestrationStage } from './orchestration.ts';
12
+ import type { ResolveTaskRefErrorCode } from './resolve-ref.ts';
13
+
14
+ type ReviewFinalizationErrorCode =
15
+ | ResolveTaskRefErrorCode
16
+ | 'TASK_STATE_MISMATCH'
17
+ | 'REVIEW_PAYLOAD_INVALID'
18
+ | 'REVIEW_STAGE_INVALID'
19
+ | 'REVIEW_ARTIFACT_IDENTITY_INVALID'
20
+ | 'REVIEW_ARTIFACT_NOT_REGULAR'
21
+ | 'REVIEW_LEDGER_INVALID'
22
+ | 'REVIEW_SUMMARY_NOT_FOUND'
23
+ | 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
24
+ | 'REVIEW_SUMMARY_COUNT_MISMATCH'
25
+ | 'REVIEW_ARTIFACT_CONFLICT'
26
+ | 'REVIEW_PROVENANCE_INVALID'
27
+ | 'REVIEW_TEMP_WRITE_FAILED'
28
+ | 'REVIEW_RENAME_FAILED';
29
+ type ReviewFinalizationError = { code: ReviewFinalizationErrorCode; message: string };
30
+ type ReviewFinalizationRequest = {
31
+ taskRef: string;
32
+ stage: string;
33
+ artifact: string;
34
+ dryRun?: boolean;
35
+ };
36
+ type ReviewFinalizationResult = {
37
+ status: 'planned' | 'applied' | 'no-op' | 'failed';
38
+ changed: boolean;
39
+ intent: 'finalize-summary';
40
+ requestRef: string;
41
+ taskId: string | null;
42
+ stage: string;
43
+ artifact: string;
44
+ stageStatus: LedgerStageStatus | null;
45
+ operations: readonly {
46
+ kind: 'artifact';
47
+ artifact: string;
48
+ operation: 'update';
49
+ }[];
50
+ error: ReviewFinalizationError | null;
51
+ };
52
+ type ReviewFileSystem = {
53
+ readFileSync: (file: string) => string;
54
+ statModeSync: (file: string) => number;
55
+ writeFileSync: (file: string, content: string, mode: number) => void;
56
+ renameSync: (from: string, to: string) => void;
57
+ unlinkSync: (file: string) => void;
58
+ };
59
+ type ReviewFinalizationOptions = {
60
+ repoRoot?: string;
61
+ randomSuffix?: () => string;
62
+ fileSystem?: Partial<ReviewFileSystem>;
63
+ };
64
+
65
+ const STAGES: Record<ReviewStage, { family: 'review-analysis' | 'review-plan' | 'review-code'; action: string }> = {
66
+ analysis: { family: 'review-analysis', action: 'Review Analysis' },
67
+ plan: { family: 'review-plan', action: 'Review Plan' },
68
+ code: { family: 'review-code', action: 'Review Code' }
69
+ };
70
+ const DEFAULT_FILE_SYSTEM: ReviewFileSystem = {
71
+ readFileSync: (file) => fs.readFileSync(file, 'utf8'),
72
+ statModeSync: (file) => fs.statSync(file).mode,
73
+ writeFileSync: (file, content, mode) => fs.writeFileSync(file, content, { encoding: 'utf8', flag: 'wx', mode }),
74
+ renameSync: (from, to) => fs.renameSync(from, to),
75
+ unlinkSync: (file) => fs.unlinkSync(file)
76
+ };
77
+
78
+ function failed(
79
+ request: ReviewFinalizationRequest,
80
+ code: ReviewFinalizationErrorCode,
81
+ message: string,
82
+ taskId: string | null = null,
83
+ stageStatus: LedgerStageStatus | null = null
84
+ ): ReviewFinalizationResult {
85
+ return {
86
+ status: 'failed',
87
+ changed: false,
88
+ intent: 'finalize-summary',
89
+ requestRef: request.taskRef,
90
+ taskId,
91
+ stage: request.stage,
92
+ artifact: request.artifact,
93
+ stageStatus,
94
+ operations: [],
95
+ error: { code, message }
96
+ };
97
+ }
98
+
99
+ function openReviewRound(content: string, action: string, round: number): boolean {
100
+ const section = locateActivityLog(content);
101
+ if (!section) return false;
102
+ const expected = `${action} (Round ${round})`;
103
+ return pairEntries(section.entries).filter((row) => (
104
+ row.step === expected && Boolean(row.started) && !row.done
105
+ )).length === 1;
106
+ }
107
+
108
+ function cleanupTemp(fileSystem: ReviewFileSystem, tempPath: string): void {
109
+ try {
110
+ fileSystem.unlinkSync(tempPath);
111
+ } catch {
112
+ // Best-effort cleanup preserves the primary failure.
113
+ }
114
+ }
115
+
116
+ function finalizeReviewSummary(
117
+ request: ReviewFinalizationRequest,
118
+ options: ReviewFinalizationOptions = {}
119
+ ): ReviewFinalizationResult {
120
+ const stage = request.stage as ReviewStage;
121
+ const spec = STAGES[stage];
122
+ if (!spec) return failed(request, 'REVIEW_STAGE_INVALID', `unsupported review stage '${request.stage}'`);
123
+ if (!request.taskRef || !request.artifact) {
124
+ return failed(request, 'REVIEW_PAYLOAD_INVALID', 'taskRef, stage, and artifact are required');
125
+ }
126
+ const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
127
+ if (!resolved.ok) return failed(request, resolved.code, resolved.message, resolved.taskId);
128
+ if (resolved.state !== 'active') {
129
+ return failed(request, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
130
+ }
131
+ const parsedArtifact = parseArtifactName(request.artifact);
132
+ if (!parsedArtifact || parsedArtifact.family !== spec.family) {
133
+ return failed(
134
+ request,
135
+ 'REVIEW_ARTIFACT_IDENTITY_INVALID',
136
+ `artifact '${request.artifact}' does not match ${spec.family}`,
137
+ resolved.taskId
138
+ );
139
+ }
140
+ const validated = validateCompletedArtifact(
141
+ resolved.taskDir,
142
+ spec.family,
143
+ request.artifact,
144
+ parsedArtifact.round
145
+ );
146
+ if (!validated.ok) {
147
+ const code = validated.error.code === 'ARTIFACT_NOT_REGULAR'
148
+ ? 'REVIEW_ARTIFACT_NOT_REGULAR'
149
+ : 'REVIEW_ARTIFACT_IDENTITY_INVALID';
150
+ return failed(request, code, validated.error.message, resolved.taskId);
151
+ }
152
+
153
+ const fileSystem = { ...DEFAULT_FILE_SYSTEM, ...options.fileSystem };
154
+ let taskContent: string;
155
+ let artifactContent: string;
156
+ try {
157
+ taskContent = fileSystem.readFileSync(resolved.taskMdPath);
158
+ artifactContent = fileSystem.readFileSync(validated.artifact.path);
159
+ } catch (error) {
160
+ return failed(request, 'REVIEW_ARTIFACT_NOT_REGULAR', String(error), resolved.taskId);
161
+ }
162
+ if (!openReviewRound(taskContent, spec.action, parsedArtifact.round)) {
163
+ return failed(
164
+ request,
165
+ 'REVIEW_ARTIFACT_IDENTITY_INVALID',
166
+ `${request.artifact} does not have one matching open started review event`,
167
+ resolved.taskId
168
+ );
169
+ }
170
+ const provenance = validateOrchestrationStage(request.taskRef, {
171
+ stage: spec.family,
172
+ round: parsedArtifact.round,
173
+ artifact: request.artifact,
174
+ role: 'reviewer'
175
+ }, { repoRoot: options.repoRoot });
176
+ if (provenance.status === 'failed' || provenance.status === 'paused') {
177
+ return failed(
178
+ request,
179
+ 'REVIEW_PROVENANCE_INVALID',
180
+ provenance.error?.message ?? provenance.run?.pause?.message ?? 'orchestration provenance validation failed',
181
+ resolved.taskId
182
+ );
183
+ }
184
+
185
+ let rows;
186
+ try {
187
+ rows = parseLedger(taskContent);
188
+ } catch (error) {
189
+ return failed(request, 'REVIEW_LEDGER_INVALID', String(error), resolved.taskId);
190
+ }
191
+ const ledgerError = validateLedgerRows(rows);
192
+ if (ledgerError) {
193
+ return failed(
194
+ request,
195
+ 'REVIEW_LEDGER_INVALID',
196
+ `${ledgerError.code}: ${ledgerError.message}`,
197
+ resolved.taskId
198
+ );
199
+ }
200
+ const stageStatus = summarizeLedgerStage(rows, stage);
201
+ const transformed = finalizeReviewSummaryContent(
202
+ artifactContent,
203
+ stageStatus.unresolvedFindingCounts
204
+ );
205
+ if (!transformed.ok) {
206
+ return failed(request, transformed.code, transformed.message, resolved.taskId, stageStatus);
207
+ }
208
+ if (!transformed.changed) {
209
+ return {
210
+ ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
211
+ status: 'no-op',
212
+ error: null
213
+ };
214
+ }
215
+ const operations = [{ kind: 'artifact' as const, artifact: request.artifact, operation: 'update' as const }];
216
+ if (request.dryRun) {
217
+ return {
218
+ ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
219
+ status: 'planned',
220
+ changed: true,
221
+ operations,
222
+ error: null
223
+ };
224
+ }
225
+
226
+ const tempPath = path.join(
227
+ resolved.taskDir,
228
+ `.${request.artifact}.tmp-${process.pid}-${(options.randomSuffix ?? randomUUID)()}`
229
+ );
230
+ let mode: number;
231
+ try {
232
+ mode = fileSystem.statModeSync(validated.artifact.path);
233
+ fileSystem.writeFileSync(tempPath, transformed.content, mode);
234
+ } catch (error) {
235
+ cleanupTemp(fileSystem, tempPath);
236
+ return failed(request, 'REVIEW_TEMP_WRITE_FAILED', String(error), resolved.taskId, stageStatus);
237
+ }
238
+ try {
239
+ if (fileSystem.readFileSync(validated.artifact.path) !== artifactContent) {
240
+ cleanupTemp(fileSystem, tempPath);
241
+ return failed(
242
+ request,
243
+ 'REVIEW_ARTIFACT_CONFLICT',
244
+ `${request.artifact} changed during finalization`,
245
+ resolved.taskId,
246
+ stageStatus
247
+ );
248
+ }
249
+ fileSystem.renameSync(tempPath, validated.artifact.path);
250
+ } catch (error) {
251
+ cleanupTemp(fileSystem, tempPath);
252
+ return failed(request, 'REVIEW_RENAME_FAILED', String(error), resolved.taskId, stageStatus);
253
+ }
254
+ return {
255
+ status: 'applied',
256
+ changed: true,
257
+ intent: 'finalize-summary',
258
+ requestRef: request.taskRef,
259
+ taskId: resolved.taskId,
260
+ stage,
261
+ artifact: request.artifact,
262
+ stageStatus,
263
+ operations,
264
+ error: null
265
+ };
266
+ }
267
+
268
+ export { finalizeReviewSummary };
269
+ export type {
270
+ ReviewFinalizationError,
271
+ ReviewFinalizationErrorCode,
272
+ ReviewFinalizationOptions,
273
+ ReviewFinalizationRequest,
274
+ ReviewFinalizationResult
275
+ };
@@ -56,6 +56,11 @@ export function extractReviewBaseline(content: string): string {
56
56
  return match ? match[1]!.trim().replace(/`/g, "") : "";
57
57
  }
58
58
 
59
+ export function extractReviewDiffBase(content: string): string {
60
+ const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异基线|Reviewed Diff Base)\*\*[::]\s*(.*?)\s*$/m);
61
+ return match ? match[1]!.trim().replace(/`/g, "") : "";
62
+ }
63
+
59
64
  export function extractReviewDiffFingerprint(content: string): string {
60
65
  const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异指纹|Reviewed Diff Fingerprint)\*\*[::]\s*(.*?)\s*$/m);
61
66
  return match ? match[1]!.trim().replace(/`/g, "") : "";
@@ -1,5 +1,6 @@
1
1
  const LOCAL_VERIFICATION_CHECKS = [
2
- 'task-meta', 'artifact', 'implementation-input', 'activity-log', 'completion-checklist'
2
+ 'task-meta', 'artifact', 'implementation-input', 'activity-log', 'completion-checklist',
3
+ 'orchestration-state'
3
4
  ] as const;
4
5
 
5
6
  function isLocalVerificationCheck(value: string): value is typeof LOCAL_VERIFICATION_CHECKS[number] {