@fitlab-ai/agent-infra 0.11.15 → 0.12.1

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 (265) hide show
  1. package/bin/cli.ts +1 -1
  2. package/dist/bin/cli.js +1 -1
  3. package/dist/bin/internal-cli.js +6 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +6 -6
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +8 -18
  6. package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -1
  7. package/dist/lib/decide.js +2 -7
  8. package/dist/lib/internal/cli-route-inventory.js +3 -0
  9. package/dist/lib/internal/manual-validation.js +19 -33
  10. package/dist/lib/internal/platform-comment.js +59 -9
  11. package/dist/lib/internal/task-activity.js +1 -12
  12. package/dist/lib/internal/task-event.js +1 -12
  13. package/dist/lib/internal/task-ledger.js +1 -12
  14. package/dist/lib/internal/task-operation-registry.js +2 -1
  15. package/dist/lib/internal/task-qualification.js +6 -15
  16. package/dist/lib/internal/task-review.js +3 -16
  17. package/dist/lib/internal/task-short-id.js +2 -1
  18. package/dist/lib/internal/task-verify.js +1 -11
  19. package/dist/lib/internal/task-warning.js +1 -12
  20. package/dist/lib/internal/task-workspace.js +43 -0
  21. package/dist/lib/merge.js +4 -3
  22. package/dist/lib/platform/issue-comments.js +373 -267
  23. package/dist/lib/platform/issue-summary-content.js +112 -0
  24. package/dist/lib/platform/pr-change-report.js +175 -26
  25. package/dist/lib/platform/pr-summary-content.js +54 -0
  26. package/dist/lib/platform/pr-summary.js +321 -296
  27. package/dist/lib/platform/pull-requests.js +3 -15
  28. package/dist/lib/platform/summary-content-pipeline.js +18 -0
  29. package/dist/lib/pr-review/evidence-grading.js +3 -2
  30. package/dist/lib/process-data/index.js +5 -4
  31. package/dist/lib/process-data/sources.js +6 -0
  32. package/dist/lib/run/index.js +3 -2
  33. package/dist/lib/sandbox/commands/create.js +5 -1
  34. package/dist/lib/sandbox/commands/enter.js +1 -1
  35. package/dist/lib/sandbox/commands/list-running.js +2 -1
  36. package/dist/lib/sandbox/control/client.js +8 -13
  37. package/dist/lib/sandbox/control/completed-reentry.js +2 -1
  38. package/dist/lib/sandbox/control/task-view.js +4 -2
  39. package/dist/lib/sandbox/mounts.js +4 -4
  40. package/dist/lib/sandbox/recovery.js +26 -16
  41. package/dist/lib/sandbox/removal.js +62 -12
  42. package/dist/lib/sandbox/task-resolver.js +9 -6
  43. package/dist/lib/sandbox/workspace-identity.js +6 -5
  44. package/dist/lib/sandbox/workspace-view.js +29 -22
  45. package/dist/lib/task/activity-intent.js +2 -12
  46. package/dist/lib/task/artifact-lifecycle.js +17 -11
  47. package/dist/lib/task/artifact-operations.js +41 -49
  48. package/dist/lib/task/capabilities.js +2 -2
  49. package/dist/lib/task/commands/ls.js +4 -3
  50. package/dist/lib/task/commands/status.js +1 -1
  51. package/dist/lib/task/commit-operation.js +6 -6
  52. package/dist/lib/task/control-authority.js +32 -32
  53. package/dist/lib/task/create-service.js +2 -4
  54. package/dist/lib/task/create.js +12 -65
  55. package/dist/lib/task/delegation-receipts.js +1 -1
  56. package/dist/lib/task/delivery.js +2 -8
  57. package/dist/lib/task/events.js +5 -31
  58. package/dist/lib/task/finalization.js +33 -29
  59. package/dist/lib/task/lifecycle.js +9 -48
  60. package/dist/lib/task/local-artifact-finalization.js +1 -2
  61. package/dist/lib/task/orchestration.js +24 -57
  62. package/dist/lib/task/platform-operation-recovery.js +87 -88
  63. package/dist/lib/task/resolve-ref.js +17 -12
  64. package/dist/lib/task/review-finalization.js +2 -35
  65. package/dist/lib/task/review-identity.js +3 -1
  66. package/dist/lib/task/short-id.js +10 -8
  67. package/dist/lib/task/verification-engine.js +19 -58
  68. package/dist/lib/task/workspace-root.js +59 -0
  69. package/dist/lib/task/workspace-snapshot.js +4 -3
  70. package/dist/lifecycle-build-manifest.json +3 -3
  71. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +6 -6
  72. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +8 -34
  73. package/lib/agent-clients/adapters/codex-orchestration.ts +1 -1
  74. package/lib/decide.ts +2 -7
  75. package/lib/internal/cli-route-inventory.ts +2 -0
  76. package/lib/internal/manual-validation.ts +20 -37
  77. package/lib/internal/platform-comment.ts +49 -6
  78. package/lib/internal/task-activity.ts +1 -10
  79. package/lib/internal/task-event.ts +1 -10
  80. package/lib/internal/task-ledger.ts +1 -10
  81. package/lib/internal/task-operation-registry.ts +2 -1
  82. package/lib/internal/task-qualification.ts +5 -11
  83. package/lib/internal/task-review.ts +3 -14
  84. package/lib/internal/task-short-id.ts +2 -1
  85. package/lib/internal/task-verify.ts +1 -9
  86. package/lib/internal/task-warning.ts +1 -10
  87. package/lib/internal/task-workspace.ts +39 -0
  88. package/lib/merge.ts +4 -3
  89. package/lib/platform/issue-comments.ts +478 -280
  90. package/lib/platform/issue-summary-content.ts +132 -0
  91. package/lib/platform/pr-change-report.ts +165 -20
  92. package/lib/platform/pr-summary-content.ts +60 -0
  93. package/lib/platform/pr-summary.ts +367 -269
  94. package/lib/platform/pull-requests.ts +3 -24
  95. package/lib/platform/summary-content-pipeline.ts +23 -0
  96. package/lib/pr-review/evidence-grading.ts +3 -2
  97. package/lib/process-data/index.ts +4 -3
  98. package/lib/process-data/sources.ts +6 -0
  99. package/lib/run/index.ts +3 -2
  100. package/lib/sandbox/commands/create.ts +5 -1
  101. package/lib/sandbox/commands/enter.ts +1 -1
  102. package/lib/sandbox/commands/list-running.ts +2 -1
  103. package/lib/sandbox/control/client.ts +7 -12
  104. package/lib/sandbox/control/completed-reentry.ts +2 -1
  105. package/lib/sandbox/control/task-view.ts +4 -2
  106. package/lib/sandbox/mounts.ts +4 -4
  107. package/lib/sandbox/recovery.ts +26 -18
  108. package/lib/sandbox/removal.ts +60 -27
  109. package/lib/sandbox/task-resolver.ts +9 -6
  110. package/lib/sandbox/workspace-identity.ts +6 -5
  111. package/lib/sandbox/workspace-view.ts +28 -20
  112. package/lib/task/activity-intent.ts +3 -17
  113. package/lib/task/artifact-lifecycle.ts +39 -11
  114. package/lib/task/artifact-operations.ts +28 -35
  115. package/lib/task/capabilities.ts +2 -2
  116. package/lib/task/commands/ls.ts +4 -3
  117. package/lib/task/commands/status.ts +1 -1
  118. package/lib/task/commit-operation.ts +5 -6
  119. package/lib/task/control-authority.ts +29 -35
  120. package/lib/task/create-service.ts +2 -4
  121. package/lib/task/create.ts +10 -51
  122. package/lib/task/delegation-receipts.ts +1 -1
  123. package/lib/task/delivery.ts +2 -10
  124. package/lib/task/events.ts +5 -32
  125. package/lib/task/finalization.ts +27 -30
  126. package/lib/task/lifecycle.ts +9 -50
  127. package/lib/task/local-artifact-finalization.ts +1 -4
  128. package/lib/task/orchestration.ts +31 -80
  129. package/lib/task/platform-operation-recovery.ts +70 -82
  130. package/lib/task/resolve-ref.ts +18 -12
  131. package/lib/task/review-finalization.ts +2 -52
  132. package/lib/task/review-identity.ts +3 -1
  133. package/lib/task/short-id.ts +10 -8
  134. package/lib/task/verification-engine.ts +20 -62
  135. package/lib/task/workspace-root.ts +78 -0
  136. package/lib/task/workspace-snapshot.ts +4 -3
  137. package/package.json +1 -1
  138. package/templates/.agents/QUICKSTART.en.md +19 -8
  139. package/templates/.agents/QUICKSTART.zh-CN.md +19 -8
  140. package/templates/.agents/README.en.md +10 -15
  141. package/templates/.agents/README.zh-CN.md +9 -13
  142. package/templates/.agents/rules/issue-sync.en.md +1 -1
  143. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  144. package/templates/.agents/rules/lifecycle-orchestration.en.md +1 -1
  145. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +1 -1
  146. package/templates/.agents/rules/next-step-output.en.md +2 -2
  147. package/templates/.agents/rules/next-step-output.zh-CN.md +2 -2
  148. package/templates/.agents/rules/pr-sync.en.md +5 -3
  149. package/templates/.agents/rules/pr-sync.zh-CN.md +7 -10
  150. package/templates/.agents/rules/release-commands.en.md +3 -0
  151. package/templates/.agents/rules/release-commands.zh-CN.md +3 -0
  152. package/templates/.agents/rules/task-management.en.md +0 -2
  153. package/templates/.agents/rules/task-management.zh-CN.md +0 -2
  154. package/templates/.agents/rules/task-short-id.en.md +2 -2
  155. package/templates/.agents/rules/task-short-id.zh-CN.md +2 -2
  156. package/templates/.agents/scripts/task-short-id.js +2 -8
  157. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  158. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  159. package/templates/.agents/skills/analyze-task/config/verify.en.json +0 -1
  160. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +0 -1
  161. package/templates/.agents/skills/archive-tasks/SKILL.en.md +5 -5
  162. package/templates/.agents/skills/archive-tasks/SKILL.zh-CN.md +5 -5
  163. package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +10 -2
  164. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  165. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +5 -5
  166. package/templates/.agents/skills/block-task/config/verify.json +0 -1
  167. package/templates/.agents/skills/cancel-task/SKILL.en.md +7 -7
  168. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +7 -7
  169. package/templates/.agents/skills/cancel-task/config/verify.json +0 -1
  170. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  171. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
  172. package/templates/.agents/skills/code-task/config/verify.en.json +0 -1
  173. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +0 -1
  174. package/templates/.agents/skills/code-task/reference/output-template.en.md +1 -1
  175. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +1 -1
  176. package/templates/.agents/skills/commit/SKILL.en.md +1 -1
  177. package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
  178. package/templates/.agents/skills/commit/config/verify.json +0 -1
  179. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +2 -2
  180. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +2 -2
  181. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +2 -2
  182. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +2 -2
  183. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
  184. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  185. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +0 -1
  186. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +0 -1
  187. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +3 -3
  188. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +3 -3
  189. package/templates/.agents/skills/complete-task/SKILL.en.md +15 -30
  190. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +17 -32
  191. package/templates/.agents/skills/complete-task/config/verify.en.json +0 -1
  192. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
  193. package/templates/.agents/skills/create-pr/SKILL.en.md +3 -3
  194. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +3 -3
  195. package/templates/.agents/skills/create-pr/config/verify.json +0 -1
  196. package/templates/.agents/skills/create-pr/reference/change-report.en.md +10 -3
  197. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +10 -3
  198. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +3 -3
  199. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +3 -3
  200. package/templates/.agents/skills/create-release-note/SKILL.en.md +2 -2
  201. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +2 -2
  202. package/templates/.agents/skills/create-task/SKILL.en.md +4 -22
  203. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -22
  204. package/templates/.agents/skills/create-task/config/verify.json +0 -1
  205. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  206. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  207. package/templates/.agents/skills/import-codescan/config/verify.json +0 -1
  208. package/templates/.agents/skills/import-dependabot/SKILL.en.md +3 -3
  209. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +3 -3
  210. package/templates/.agents/skills/import-dependabot/config/verify.json +0 -1
  211. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  212. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  213. package/templates/.agents/skills/import-issue/config/verify.json +0 -1
  214. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  215. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  216. package/templates/.agents/skills/plan-task/config/verify.en.json +0 -1
  217. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +0 -1
  218. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  219. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -3
  220. package/templates/.agents/skills/restore-task/config/verify.json +0 -1
  221. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  222. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  223. package/templates/.agents/skills/review-analysis/config/verify.en.json +0 -1
  224. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +0 -1
  225. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +5 -5
  226. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +5 -5
  227. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  228. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/review-code/reference/output-templates.en.md +4 -4
  230. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +4 -4
  231. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  232. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  233. package/templates/.agents/skills/review-plan/config/verify.en.json +0 -1
  234. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +0 -1
  235. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +5 -5
  236. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +5 -5
  237. package/templates/.agents/skills/review-pr/SKILL.en.md +2 -2
  238. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +2 -2
  239. package/templates/.agents/skills/review-pr/config/verify.en.json +0 -1
  240. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +0 -1
  241. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +1 -1
  242. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +1 -1
  243. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +0 -1
  244. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +0 -1
  245. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  246. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  247. package/templates/.agents/skills/test/SKILL.en.md +0 -3
  248. package/templates/.agents/skills/test/SKILL.zh-CN.md +0 -3
  249. package/templates/.agents/skills/test-integration/SKILL.en.md +0 -3
  250. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +0 -3
  251. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +13 -6
  252. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +0 -3
  253. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +0 -3
  254. package/templates/.agents/skills/watch-pr/SKILL.en.md +4 -4
  255. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +4 -4
  256. package/templates/.agents/skills/watch-pr/config/verify.json +0 -1
  257. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +3 -3
  258. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +3 -3
  259. package/templates/.agents/templates/handoff.en.md +1 -1
  260. package/templates/.agents/templates/handoff.zh-CN.md +1 -1
  261. package/templates/.agents/workflows/bug-fix.en.yaml +1 -1
  262. package/templates/.agents/workflows/feature-development.en.yaml +1 -1
  263. package/templates/.agents/workflows/refactoring.en.yaml +1 -1
  264. package/dist/lib/task/task-execution-lock.js +0 -250
  265. package/lib/task/task-execution-lock.ts +0 -320
@@ -0,0 +1,112 @@
1
+ import { runSummaryContentPipeline } from './summary-content-pipeline.js';
2
+ function text(value, name) {
3
+ if (typeof value !== 'string' || !value.trim())
4
+ throw new Error(`${name} must be a non-empty string`);
5
+ return value.trim();
6
+ }
7
+ function isMissingText(value) {
8
+ return typeof value !== 'string' || !value.trim();
9
+ }
10
+ function fillText(input, key, fallback) {
11
+ if (isMissingText(input[key]))
12
+ input[key] = fallback;
13
+ }
14
+ function fillDeliveryVerification(input, fallback) {
15
+ if (!input.verification || typeof input.verification !== 'object' || Array.isArray(input.verification)) {
16
+ input.verification = fallback;
17
+ return;
18
+ }
19
+ const verification = { ...input.verification };
20
+ fillText(verification, 'state', fallback.state);
21
+ fillText(verification, 'detail', fallback.detail);
22
+ input.verification = verification;
23
+ }
24
+ function isStringArray(value) {
25
+ return Array.isArray(value) && value.every((item) => typeof item === 'string' && item.trim());
26
+ }
27
+ export function parseDeliverySummaryContent(value) {
28
+ if (!value || typeof value !== 'object' || Array.isArray(value))
29
+ throw new Error('delivery summary must be an object');
30
+ const input = value;
31
+ if (!Array.isArray(input.artifacts) || input.artifacts.some((item) => typeof item !== 'string'))
32
+ throw new Error('artifacts must be a string array');
33
+ if (!input.verification || typeof input.verification !== 'object' || Array.isArray(input.verification))
34
+ throw new Error('verification must be an object');
35
+ const verification = input.verification;
36
+ return {
37
+ taskId: text(input.taskId, 'taskId'), title: text(input.title, 'title'), summary: text(input.summary, 'summary'),
38
+ artifacts: input.artifacts.map((item) => text(item, 'artifacts[]')),
39
+ verification: { state: text(verification.state, 'verification.state'), detail: text(verification.detail, 'verification.detail') }
40
+ };
41
+ }
42
+ function correctDeliverySummaryContent(value, sources) {
43
+ if (!value || typeof value !== 'object' || Array.isArray(value))
44
+ throw new Error('delivery summary must be an object');
45
+ const input = { ...value };
46
+ fillText(input, 'taskId', sources.taskId);
47
+ if (input.taskId !== sources.taskId)
48
+ throw new Error('taskId does not match the active task');
49
+ fillText(input, 'title', sources.title);
50
+ if (!isStringArray(input.artifacts))
51
+ input.artifacts = [...sources.artifacts];
52
+ if (sources.verificationFallback)
53
+ fillDeliveryVerification(input, sources.verificationFallback);
54
+ return input;
55
+ }
56
+ export function prepareAndRenderDeliverySummaryContent(value, sources) {
57
+ return runSummaryContentPipeline(value, {
58
+ validate: parseDeliverySummaryContent,
59
+ correct: (input) => correctDeliverySummaryContent(input, sources),
60
+ render: renderDeliverySummaryContent
61
+ });
62
+ }
63
+ export function renderDeliverySummaryContent(value) {
64
+ const content = parseDeliverySummaryContent(value);
65
+ return [
66
+ `# ${content.title}`, '', content.summary, '', '## 交付产物', '',
67
+ ...(content.artifacts.length ? content.artifacts.map((item) => `- ${item}`) : ['- 无']), '',
68
+ '## 验证', '', `状态:${content.verification.state}`, '', content.verification.detail, ''
69
+ ].join('\n');
70
+ }
71
+ export function parseCancelSummaryContent(value) {
72
+ if (!value || typeof value !== 'object' || Array.isArray(value))
73
+ throw new Error('cancel summary must be an object');
74
+ const input = value;
75
+ if (input.outcome !== 'cancelled')
76
+ throw new Error('outcome must be cancelled');
77
+ if (input.lastCompletedArtifact !== null && typeof input.lastCompletedArtifact !== 'string')
78
+ throw new Error('lastCompletedArtifact must be a string or null');
79
+ return {
80
+ taskId: text(input.taskId, 'taskId'), title: text(input.title, 'title'), outcome: 'cancelled',
81
+ reason: text(input.reason, 'reason'), cancelledAt: text(input.cancelledAt, 'cancelledAt'),
82
+ lastCompletedArtifact: input.lastCompletedArtifact === null ? null : text(input.lastCompletedArtifact, 'lastCompletedArtifact')
83
+ };
84
+ }
85
+ function correctCancelSummaryContent(value, sources) {
86
+ if (!value || typeof value !== 'object' || Array.isArray(value))
87
+ throw new Error('cancel summary must be an object');
88
+ const input = { ...value };
89
+ fillText(input, 'taskId', sources.taskId);
90
+ if (input.taskId !== sources.taskId)
91
+ throw new Error('taskId does not match the task');
92
+ fillText(input, 'title', sources.title);
93
+ fillText(input, 'reason', sources.reason);
94
+ fillText(input, 'cancelledAt', sources.cancelledAt);
95
+ if (input.lastCompletedArtifact === undefined
96
+ || (input.lastCompletedArtifact !== null && (typeof input.lastCompletedArtifact !== 'string' || !input.lastCompletedArtifact.trim()))) {
97
+ input.lastCompletedArtifact = sources.lastCompletedArtifact;
98
+ }
99
+ return input;
100
+ }
101
+ export function prepareAndRenderCancelSummaryContent(value, sources) {
102
+ return runSummaryContentPipeline(value, {
103
+ validate: parseCancelSummaryContent,
104
+ correct: (input) => correctCancelSummaryContent(input, sources),
105
+ render: renderCancelSummaryContent
106
+ });
107
+ }
108
+ export function renderCancelSummaryContent(value) {
109
+ const content = parseCancelSummaryContent(value);
110
+ return [`# ${content.title}`, '', `结果:${content.outcome}`, '', `取消时间:${content.cancelledAt}`, '', `原因:${content.reason}`, '', `最近完成产物:${content.lastCompletedArtifact ?? '无'}`, ''].join('\n');
111
+ }
112
+ //# sourceMappingURL=issue-summary-content.js.map
@@ -5,7 +5,7 @@ import { execFileSync } from 'node:child_process';
5
5
  import { escapeHtmlText } from './comment-safety.js';
6
6
  import { isResourceIdentity } from './resource-identity.js';
7
7
  const REPORT_FILE_NAME = 'pr-change-report.json';
8
- const REPORT_VERSION = 2;
8
+ const REPORT_VERSION = 3;
9
9
  const CANONICAL_REPORT_PLACEHOLDER = '<!-- canonical-pr-change-report -->';
10
10
  const CANONICAL_REPORT_HEADING = '### PR 代码增减';
11
11
  const PRECHECK_IDS = [
@@ -131,32 +131,158 @@ function parseEvidence(value, checkId, index) {
131
131
  return invalid(`precheck.checks.${checkId}.evidence[${index}] line range is invalid`);
132
132
  return { ok: true, value: { path: value.path, startLine, endLine, detail: value.detail } };
133
133
  }
134
- function parseChecks(value, prefix = 'precheck.checks') {
134
+ function parseFunctionalGroupFiles(value, prefix) {
135
+ if (!Array.isArray(value) || value.length === 0)
136
+ return invalid(`${prefix} must be a non-empty array`);
137
+ const files = [];
138
+ for (let index = 0; index < value.length; index += 1) {
139
+ const file = value[index];
140
+ if (!isSafeRenderedText(file))
141
+ return invalid(`${prefix}[${index}] must be a non-empty string`);
142
+ files.push(file.trim());
143
+ }
144
+ return { ok: true, value: files };
145
+ }
146
+ function parseFunctionalGroupTests(value, prefix) {
147
+ if (!isRecord(value))
148
+ return invalid(`${prefix} must be an object`);
149
+ if (!isSafeRenderedText(value.coverage))
150
+ return invalid(`${prefix}.coverage is required`);
151
+ if (!isSafeRenderedText(value.cost))
152
+ return invalid(`${prefix}.cost is required`);
153
+ return { ok: true, value: { coverage: value.coverage.trim(), cost: value.cost.trim() } };
154
+ }
155
+ function parseFunctionalGroup(value, prefix, ids) {
156
+ if (!isRecord(value))
157
+ return invalid(`${prefix} must be an object`);
158
+ if (Object.hasOwn(value, 'additions') || Object.hasOwn(value, 'deletions') || Object.hasOwn(value, 'net') || Object.hasOwn(value, 'counts')) {
159
+ return invalid(`${prefix} line counts are derived from diff files and must not be supplied`);
160
+ }
161
+ if (!isSafeRenderedText(value.id) || ids.has(value.id.trim()))
162
+ return invalid(`${prefix}.id must be non-empty and unique`);
163
+ if (!isSafeRenderedText(value.target))
164
+ return invalid(`${prefix}.target is required`);
165
+ if (!isSafeRenderedText(value.behaviorChange))
166
+ return invalid(`${prefix}.behaviorChange is required`);
167
+ if (!isSafeRenderedText(value.necessity))
168
+ return invalid(`${prefix}.necessity is required`);
169
+ const files = parseFunctionalGroupFiles(value.files, `${prefix}.files`);
170
+ if (!files.ok)
171
+ return files;
172
+ const tests = parseFunctionalGroupTests(value.tests, `${prefix}.tests`);
173
+ if (!tests.ok)
174
+ return tests;
175
+ const id = value.id.trim();
176
+ ids.add(id);
177
+ return {
178
+ ok: true,
179
+ value: {
180
+ id, target: value.target.trim(), behaviorChange: value.behaviorChange.trim(),
181
+ necessity: value.necessity.trim(), files: files.value, tests: tests.value
182
+ }
183
+ };
184
+ }
185
+ function parseFunctionalGroups(value, prefix) {
186
+ if (!Array.isArray(value) || value.length === 0)
187
+ return invalid(`${prefix} must be a non-empty array`);
188
+ const groups = [];
189
+ const ids = new Set();
190
+ for (let index = 0; index < value.length; index += 1) {
191
+ const group = parseFunctionalGroup(value[index], `${prefix}[${index}]`, ids);
192
+ if (!group.ok)
193
+ return group;
194
+ groups.push(group.value);
195
+ }
196
+ return { ok: true, value: groups };
197
+ }
198
+ function indexDiffFiles(files) {
199
+ const fileByPath = new Map();
200
+ for (const file of files) {
201
+ const key = file.oldPath ?? file.newPath;
202
+ if (!key || fileByPath.has(key))
203
+ return invalid(`diff file path '${key ?? ''}' is missing or duplicated`);
204
+ fileByPath.set(key, file);
205
+ }
206
+ return { ok: true, value: fileByPath };
207
+ }
208
+ function collectGroupMembership(groups, fileByPath) {
209
+ const assigned = new Set();
210
+ for (const group of groups) {
211
+ for (const member of group.files) {
212
+ const file = fileByPath.get(member);
213
+ if (!file)
214
+ return invalid(`change-composition group '${group.id}' references unknown diff file '${member}'`);
215
+ if (assigned.has(member))
216
+ return invalid(`diff file '${member}' belongs to more than one functional group`);
217
+ assigned.add(member);
218
+ }
219
+ }
220
+ return { ok: true, value: assigned };
221
+ }
222
+ function validateGroupMembership(checks, files) {
223
+ const composition = checks.find((check) => check.id === 'change-composition');
224
+ if (!composition?.groups)
225
+ return invalid('precheck.checks.change-composition.groups is required');
226
+ const indexed = indexDiffFiles(files);
227
+ if (!indexed.ok)
228
+ return indexed;
229
+ const assigned = collectGroupMembership(composition.groups, indexed.value);
230
+ if (!assigned.ok)
231
+ return assigned;
232
+ const missing = [...indexed.value.keys()].find((key) => !assigned.value.has(key));
233
+ if (missing)
234
+ return invalid(`diff file '${missing}' has no functional group`);
235
+ return { ok: true, value: checks };
236
+ }
237
+ function parseEvidenceList(value, checkId, prefix) {
238
+ if (!Array.isArray(value) || value.length === 0)
239
+ return invalid(`${prefix}.${checkId}.evidence must not be empty`);
240
+ const evidence = [];
241
+ for (let index = 0; index < value.length; index += 1) {
242
+ const parsed = parseEvidence(value[index], checkId, index);
243
+ if (!parsed.ok)
244
+ return parsed;
245
+ evidence.push(parsed.value);
246
+ }
247
+ return { ok: true, value: evidence };
248
+ }
249
+ function parseCheck(value, index, prefix) {
250
+ const expectedId = PRECHECK_IDS[index];
251
+ const entryPrefix = `${prefix}[${index}]`;
252
+ const at = `${prefix}.${expectedId}`;
253
+ if (!isRecord(value))
254
+ return invalid(`${entryPrefix} must be an object`);
255
+ if (value.id !== expectedId)
256
+ return invalid(`${entryPrefix}.id must be '${expectedId}'`);
257
+ if (value.verdict !== 'pass' && value.verdict !== 'needs-review')
258
+ return invalid(`${at}.verdict is invalid`);
259
+ const evidence = parseEvidenceList(value.evidence, expectedId, prefix);
260
+ if (!evidence.ok)
261
+ return evidence;
262
+ if (!isSafeRenderedText(value.rationale))
263
+ return invalid(`${at}.rationale is required`);
264
+ if (expectedId !== 'change-composition') {
265
+ if (Object.hasOwn(value, 'groups'))
266
+ return invalid(`${at}.groups is not allowed`);
267
+ return { ok: true, value: { id: expectedId, verdict: value.verdict, evidence: evidence.value, rationale: value.rationale } };
268
+ }
269
+ const groups = parseFunctionalGroups(value.groups, `${at}.groups`);
270
+ if (!groups.ok)
271
+ return groups;
272
+ return { ok: true, value: { id: expectedId, verdict: value.verdict, evidence: evidence.value, rationale: value.rationale, groups: groups.value } };
273
+ }
274
+ function parseChecks(value, prefix = 'precheck.checks', diffFiles) {
135
275
  if (!Array.isArray(value) || value.length !== PRECHECK_IDS.length)
136
276
  return invalid(`${prefix} must contain exactly six checks`);
137
277
  const checks = [];
138
278
  for (let index = 0; index < value.length; index += 1) {
139
- const raw = value[index];
140
- if (!isRecord(raw))
141
- return invalid(`${prefix}[${index}] must be an object`);
142
- const expectedId = PRECHECK_IDS[index];
143
- if (raw.id !== expectedId)
144
- return invalid(`${prefix}[${index}].id must be '${expectedId}'`);
145
- if (raw.verdict !== 'pass' && raw.verdict !== 'needs-review')
146
- return invalid(`${prefix}.${expectedId}.verdict is invalid`);
147
- if (!Array.isArray(raw.evidence) || raw.evidence.length === 0)
148
- return invalid(`${prefix}.${expectedId}.evidence must not be empty`);
149
- const evidence = [];
150
- for (let evidenceIndex = 0; evidenceIndex < raw.evidence.length; evidenceIndex += 1) {
151
- const parsed = parseEvidence(raw.evidence[evidenceIndex], expectedId, evidenceIndex);
152
- if (!parsed.ok)
153
- return parsed;
154
- evidence.push(parsed.value);
155
- }
156
- if (!isSafeRenderedText(raw.rationale))
157
- return invalid(`${prefix}.${expectedId}.rationale is required`);
158
- checks.push({ id: expectedId, verdict: raw.verdict, evidence, rationale: raw.rationale });
279
+ const parsed = parseCheck(value[index], index, prefix);
280
+ if (!parsed.ok)
281
+ return parsed;
282
+ checks.push(parsed.value);
159
283
  }
284
+ if (diffFiles)
285
+ return validateGroupMembership(checks, diffFiles);
160
286
  return { ok: true, value: checks };
161
287
  }
162
288
  function validateMechanicalReport(value) {
@@ -247,7 +373,7 @@ function validatePrChangeReport(value) {
247
373
  if (value.precheck.formalReview !== false || (value.precheck.verdict !== 'clear' && value.precheck.verdict !== 'needs-review') ||
248
374
  (value.precheck.route !== 'watch-pr' && value.precheck.route !== 'review-code'))
249
375
  return invalid('change report precheck envelope is invalid');
250
- const checks = parseChecks(value.precheck.checks);
376
+ const checks = parseChecks(value.precheck.checks, 'precheck.checks', files);
251
377
  if (!checks.ok)
252
378
  return checks;
253
379
  const derivedVerdict = checks.value.every((check) => check.verdict === 'pass') ? 'clear' : 'needs-review';
@@ -496,10 +622,33 @@ function renderEvidence(evidence) {
496
622
  : `:${evidence.startLine ?? evidence.endLine}`;
497
623
  return ` - 证据:<code>${escapeReportText(evidence.path)}</code>${location}:${escapeReportText(evidence.detail)}`;
498
624
  }
499
- function renderPrechecks(checks) {
625
+ function renderFunctionalGroups(groups, files) {
626
+ const filesByPath = new Map(files.map((file) => [file.oldPath ?? file.newPath, file]));
627
+ return groups.flatMap((group) => {
628
+ const members = group.files.map((member) => filesByPath.get(member));
629
+ const additions = members.reduce((total, file) => total + (file.additions ?? 0), 0);
630
+ const deletions = members.reduce((total, file) => total + (file.deletions ?? 0), 0);
631
+ const unknownFiles = members.filter((file) => file.additions === null).length;
632
+ return [
633
+ ` - **功能组 ${escapeReportText(group.id)}**:${escapeReportText(group.target)}`,
634
+ ` - 行为变化:${escapeReportText(group.behaviorChange)}`,
635
+ ` - 必要性:${escapeReportText(group.necessity)}`,
636
+ ` - 文件:${group.files.map((member) => {
637
+ const file = filesByPath.get(member);
638
+ return file.oldPath && file.newPath && file.oldPath !== file.newPath
639
+ ? `<code>${escapeReportText(file.oldPath)}</code> → <code>${escapeReportText(file.newPath)}</code>`
640
+ : `<code>${escapeReportText(member)}</code>`;
641
+ }).join('、')}`,
642
+ ` - 可计数行数(由正式 diff 逐文件推导):新增 ${additions}、删除 ${deletions}、净增 ${additions - deletions}${unknownFiles ? `;另有 ${unknownFiles} 个文件行数不可计` : ''}。`,
643
+ ` - 测试覆盖:${escapeReportText(group.tests.coverage)};成本:${escapeReportText(group.tests.cost)}`
644
+ ];
645
+ });
646
+ }
647
+ function renderPrechecks(checks, files) {
500
648
  return checks.flatMap((check) => [
501
649
  `- **${PRECHECK_LABELS[check.id]}(${check.id})**:${check.verdict === 'pass' ? '通过' : '需复核'}。${escapeReportText(check.rationale)}`,
502
- ...check.evidence.map(renderEvidence)
650
+ ...check.evidence.map(renderEvidence),
651
+ ...(check.groups ? renderFunctionalGroups(check.groups, files) : [])
503
652
  ]);
504
653
  }
505
654
  function renderCanonicalChangeReport(report) {
@@ -532,7 +681,7 @@ function renderCanonicalChangeReport(report) {
532
681
  '',
533
682
  '#### 适宜性预检',
534
683
  `- 结论:${precheckSummary};${route};正式审查:否。`,
535
- ...renderPrechecks(report.precheck.checks)
684
+ ...renderPrechecks(report.precheck.checks, report.diff.files)
536
685
  ].join('\n');
537
686
  }
538
687
  function replaceCanonicalReportPlaceholder(body, report) {
@@ -0,0 +1,54 @@
1
+ import { CANONICAL_REPORT_PLACEHOLDER } from './pr-change-report.js';
2
+ import { MANUAL_VALIDATION_RECEIPT_PLACEHOLDER } from '../task/manual-validation-receipt.js';
3
+ function nonEmpty(value, field) {
4
+ if (typeof value !== 'string' || !value.trim())
5
+ throw new Error(`${field} must be a non-empty string`);
6
+ return value.trim();
7
+ }
8
+ export function parsePrSummaryContent(value) {
9
+ if (!value || typeof value !== 'object' || Array.isArray(value))
10
+ throw new Error('PR summary must be an object');
11
+ const input = value;
12
+ const pair = (key) => {
13
+ const item = input[key];
14
+ if (!item || typeof item !== 'object' || Array.isArray(item))
15
+ throw new Error(`${key} must be an object`);
16
+ const fields = item;
17
+ return { state: nonEmpty(fields.state, `${key}.state`), detail: nonEmpty(fields.detail, `${key}.detail`) };
18
+ };
19
+ const decisions = input.humanDecisions;
20
+ if (!decisions || typeof decisions !== 'object' || Array.isArray(decisions))
21
+ throw new Error('humanDecisions must be an object');
22
+ const counts = decisions;
23
+ for (const key of ['open', 'resolved']) {
24
+ if (!Number.isSafeInteger(counts[key]) || Number(counts[key]) < 0)
25
+ throw new Error(`humanDecisions.${key} must be a non-negative integer`);
26
+ }
27
+ return {
28
+ overview: nonEmpty(input.overview, 'overview'),
29
+ tests: pair('tests'), review: pair('review'), manualValidation: pair('manualValidation'),
30
+ humanDecisions: { open: counts.open, resolved: counts.resolved, detail: nonEmpty(counts.detail, 'humanDecisions.detail') },
31
+ risks: nonEmpty(input.risks, 'risks')
32
+ };
33
+ }
34
+ export function renderPrSummaryContent(value) {
35
+ const content = parsePrSummaryContent(value);
36
+ const manualValidationHeading = content.manualValidation.state === 'required' ? '⚠️ 需人工校验'
37
+ : content.manualValidation.state === 'pending' ? '⏳ 人工验证待收尾'
38
+ : content.manualValidation.state === 'passed' ? '✅ 人工验证已通过'
39
+ : ['none', 'not-required'].includes(content.manualValidation.state) ? '✅ 无需人工校验'
40
+ : content.manualValidation.state === 'unavailable' ? '⚠️ 人工校验状态不可用'
41
+ : content.manualValidation.state === 'not-observed' ? 'ℹ️ 尚未读取人工校验状态'
42
+ : `人工校验状态:${content.manualValidation.state}`;
43
+ return [
44
+ '## 审查摘要', '', content.overview, '',
45
+ '### 关键技术决策', '', '(见审查历程)', '',
46
+ '### 审查历程', '', `状态:${content.review.state}`, '', content.review.detail, '',
47
+ '### 测试结果', '', `状态:${content.tests.state}`, '', content.tests.detail, '',
48
+ `### ${manualValidationHeading}`, '', `状态:${content.manualValidation.state}`, '', content.manualValidation.detail,
49
+ ...(content.manualValidation.state === 'passed' ? ['', MANUAL_VALIDATION_RECEIPT_PLACEHOLDER] : []), '',
50
+ '### 人工决策', '', `未解决:${content.humanDecisions.open};已解决:${content.humanDecisions.resolved}`, '', content.humanDecisions.detail, '',
51
+ '### 风险', '', content.risks, '', CANONICAL_REPORT_PLACEHOLDER, ''
52
+ ].join('\n');
53
+ }
54
+ //# sourceMappingURL=pr-summary-content.js.map