@fitlab-ai/agent-infra 0.9.13 → 0.9.15

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 (428) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/cli.ts +26 -9
  4. package/bin/internal-cli.sh +45 -0
  5. package/dist/bin/cli.js +173 -157
  6. package/dist/bin/internal-cli.js +126 -42
  7. package/dist/lib/agent-client.js +5 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
  10. package/dist/lib/defaults.json +103 -103
  11. package/dist/lib/fs/durable-write.js +37 -0
  12. package/dist/lib/fs/file-lock.js +111 -0
  13. package/dist/lib/host-control/audit.js +19 -0
  14. package/dist/lib/host-control/client.js +142 -0
  15. package/dist/lib/host-control/command.js +49 -0
  16. package/dist/lib/host-control/path.js +160 -0
  17. package/dist/lib/host-control/secure-fs.js +140 -0
  18. package/dist/lib/host-control/server.js +159 -0
  19. package/dist/lib/host-control/service.js +112 -0
  20. package/dist/lib/init.js +11 -2
  21. package/dist/lib/internal/agent-client.js +5 -2
  22. package/dist/lib/internal/cli-route-inventory.js +154 -0
  23. package/dist/lib/internal/codex-lifecycle.js +15 -12
  24. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  25. package/dist/lib/internal/demo-promotion.js +322 -0
  26. package/dist/lib/internal/demo-transcript.js +454 -0
  27. package/dist/lib/internal/git-workflow.js +10 -6
  28. package/dist/lib/internal/host-control.js +28 -0
  29. package/dist/lib/internal/manual-validation.js +322 -0
  30. package/dist/lib/internal/platform-checks.js +16 -4
  31. package/dist/lib/internal/platform-comment.js +16 -4
  32. package/dist/lib/internal/platform-context.js +4 -1
  33. package/dist/lib/internal/platform-issue.js +12 -4
  34. package/dist/lib/internal/platform-metadata.js +88 -0
  35. package/dist/lib/internal/platform-pr-review.js +16 -3
  36. package/dist/lib/internal/platform-pr.js +42 -14
  37. package/dist/lib/internal/platform-release-notes.js +6 -3
  38. package/dist/lib/internal/platform-security.js +93 -0
  39. package/dist/lib/internal/pr-review-grade.js +14 -3
  40. package/dist/lib/internal/release-workflow.js +82 -89
  41. package/dist/lib/internal/sandbox-control.js +171 -7
  42. package/dist/lib/internal/task-activity.js +13 -4
  43. package/dist/lib/internal/task-artifact.js +19 -107
  44. package/dist/lib/internal/task-context.js +4 -1
  45. package/dist/lib/internal/task-create.js +48 -14
  46. package/dist/lib/internal/task-delivery.js +4 -1
  47. package/dist/lib/internal/task-event.js +38 -35
  48. package/dist/lib/internal/task-finalization.js +4 -1
  49. package/dist/lib/internal/task-invalidation.js +4 -1
  50. package/dist/lib/internal/task-ledger.js +12 -1
  51. package/dist/lib/internal/task-lifecycle.js +4 -1
  52. package/dist/lib/internal/task-operation-registry.js +519 -0
  53. package/dist/lib/internal/task-orchestration.js +12 -0
  54. package/dist/lib/internal/task-override.js +10 -3
  55. package/dist/lib/internal/task-qualification.js +90 -0
  56. package/dist/lib/internal/task-review.js +10 -52
  57. package/dist/lib/internal/task-short-id.js +11 -1
  58. package/dist/lib/internal/task-snapshot.js +4 -1
  59. package/dist/lib/internal/task-validate.js +37 -12
  60. package/dist/lib/internal/task-verify.js +5 -1
  61. package/dist/lib/internal/task-warning.js +8 -1
  62. package/dist/lib/owned-path.js +33 -0
  63. package/dist/lib/platform/check-status.js +11 -0
  64. package/dist/lib/platform/comment-safety.js +229 -0
  65. package/dist/lib/platform/completion-backfill.js +1 -1
  66. package/dist/lib/platform/github-data.js +241 -0
  67. package/dist/lib/platform/github-provider.js +248 -26
  68. package/dist/lib/platform/issue-comments.js +146 -34
  69. package/dist/lib/platform/issues.js +1 -179
  70. package/dist/lib/platform/pr-change-report.js +6 -12
  71. package/dist/lib/platform/pr-checks.js +12 -65
  72. package/dist/lib/platform/pr-summary.js +83 -10
  73. package/dist/lib/platform/provider-contract.d.ts +67 -1
  74. package/dist/lib/platform/provider-contract.js +6 -2
  75. package/dist/lib/platform/provider-validation.js +76 -3
  76. package/dist/lib/platform/pull-requests.js +117 -275
  77. package/dist/lib/platform/repository-metadata.js +377 -0
  78. package/dist/lib/platform/security-alerts.js +155 -0
  79. package/dist/lib/platform/snapshots.js +2 -0
  80. package/dist/lib/platform/verification-sync.js +154 -162
  81. package/dist/lib/process-data/index.js +7 -0
  82. package/dist/lib/release/workflow.js +1 -1
  83. package/dist/lib/run/index.js +5 -0
  84. package/dist/lib/sandbox/commands/control-status.js +22 -0
  85. package/dist/lib/sandbox/commands/create.js +39 -8
  86. package/dist/lib/sandbox/commands/ls.js +5 -0
  87. package/dist/lib/sandbox/commands/rm.js +3 -807
  88. package/dist/lib/sandbox/commands/show.js +16 -0
  89. package/dist/lib/sandbox/control/audit.js +203 -0
  90. package/dist/lib/sandbox/control/client.js +125 -12
  91. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  92. package/dist/lib/sandbox/control/container-identity.js +28 -31
  93. package/dist/lib/sandbox/control/executor.js +233 -34
  94. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  95. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  96. package/dist/lib/sandbox/control/lifecycle.js +656 -128
  97. package/dist/lib/sandbox/control/native-file-lock.js +85 -0
  98. package/dist/lib/sandbox/control/protocol.js +55 -4
  99. package/dist/lib/sandbox/control/server.js +720 -79
  100. package/dist/lib/sandbox/control/state.js +63 -3
  101. package/dist/lib/sandbox/control/task-view.js +183 -0
  102. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  103. package/dist/lib/sandbox/control/workflow-executor.js +99 -0
  104. package/dist/lib/sandbox/engines/authority.js +289 -0
  105. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  106. package/dist/lib/sandbox/index.js +7 -2
  107. package/dist/lib/sandbox/recovery.js +49 -5
  108. package/dist/lib/sandbox/removal.js +1717 -0
  109. package/dist/lib/sandbox/task-cleanup.js +217 -0
  110. package/dist/lib/sandbox/workspace-view.js +15 -15
  111. package/dist/lib/sandbox/worktree-safety.js +4 -1
  112. package/dist/lib/server/index.js +8 -0
  113. package/dist/lib/task/activity-intent.js +2 -1
  114. package/dist/lib/task/activity-log.js +30 -24
  115. package/dist/lib/task/artifact-command.js +101 -0
  116. package/dist/lib/task/artifact-lifecycle.js +71 -40
  117. package/dist/lib/task/artifact-name.js +36 -0
  118. package/dist/lib/task/artifact-operations.js +474 -0
  119. package/dist/lib/task/artifact-receipts.js +3 -15
  120. package/dist/lib/task/artifact-repair-intent.js +130 -0
  121. package/dist/lib/task/artifact-schema.js +127 -0
  122. package/dist/lib/task/capabilities.js +83 -19
  123. package/dist/lib/task/codex-orchestration.js +71 -25
  124. package/dist/lib/task/commit-identity.js +28 -0
  125. package/dist/lib/task/commit-operation.js +2 -16
  126. package/dist/lib/task/control-authority.js +375 -5
  127. package/dist/lib/task/control-recovery.js +128 -0
  128. package/dist/lib/task/create-service.js +103 -4
  129. package/dist/lib/task/create.js +80 -2
  130. package/dist/lib/task/decision-details.js +2 -46
  131. package/dist/lib/task/events.js +306 -76
  132. package/dist/lib/task/finalization-state.js +9 -0
  133. package/dist/lib/task/human-override.js +3 -6
  134. package/dist/lib/task/implementation-inputs.js +3 -3
  135. package/dist/lib/task/index.js +11 -3
  136. package/dist/lib/task/intermediate-cleanup.js +383 -0
  137. package/dist/lib/task/invalidation.js +18 -65
  138. package/dist/lib/task/ledger-intents.js +7 -22
  139. package/dist/lib/task/ledger.js +1 -3
  140. package/dist/lib/task/lifecycle.js +19 -1
  141. package/dist/lib/task/local-artifact-finalization.js +190 -290
  142. package/dist/lib/task/manual-validation-completion.js +26 -0
  143. package/dist/lib/task/manual-validation-evidence.js +130 -0
  144. package/dist/lib/task/manual-validation-receipt.js +101 -0
  145. package/dist/lib/task/manual-validation-shared.js +40 -0
  146. package/dist/lib/task/manual-validation-transaction.js +243 -0
  147. package/dist/lib/task/markdown.js +47 -0
  148. package/dist/lib/task/orchestration.js +206 -69
  149. package/dist/lib/task/qualification-audit.js +476 -0
  150. package/dist/lib/task/qualification-confirmation.js +109 -0
  151. package/dist/lib/task/qualification-intents.js +148 -0
  152. package/dist/lib/task/review-artifacts.js +23 -40
  153. package/dist/lib/task/review-command.js +25 -0
  154. package/dist/lib/task/review-finalization.js +146 -97
  155. package/dist/lib/task/review-fingerprint.js +2 -2
  156. package/dist/lib/task/sections.js +34 -54
  157. package/dist/lib/task/verification-config.js +11 -0
  158. package/dist/lib/task/verification-engine.js +170 -327
  159. package/dist/lib/task/verification.js +1 -1
  160. package/dist/lib/task/workflow-command.js +17 -0
  161. package/dist/lib/task/workflow-warning-intents.js +0 -16
  162. package/dist/lib/task/workflow-warnings.js +28 -2
  163. package/dist/lib/update.js +2 -0
  164. package/dist/lifecycle-build-manifest.json +3 -3
  165. package/lib/agent-client.ts +9 -0
  166. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  167. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
  168. package/lib/fs/durable-write.ts +29 -0
  169. package/lib/fs/file-lock.ts +113 -0
  170. package/lib/host-control/audit.ts +22 -0
  171. package/lib/host-control/client.ts +172 -0
  172. package/lib/host-control/command.ts +60 -0
  173. package/lib/host-control/path.ts +167 -0
  174. package/lib/host-control/secure-fs.ts +135 -0
  175. package/lib/host-control/server.ts +180 -0
  176. package/lib/host-control/service.ts +133 -0
  177. package/lib/init.ts +15 -2
  178. package/lib/internal/agent-client.ts +4 -2
  179. package/lib/internal/cli-route-inventory.ts +142 -0
  180. package/lib/internal/codex-lifecycle.ts +14 -12
  181. package/lib/internal/codex-sandbox-controller.ts +7 -2
  182. package/lib/internal/demo-promotion.ts +354 -0
  183. package/lib/internal/demo-transcript.ts +419 -0
  184. package/lib/internal/git-workflow.ts +9 -6
  185. package/lib/internal/host-control.ts +28 -0
  186. package/lib/internal/manual-validation.ts +306 -0
  187. package/lib/internal/platform-checks.ts +12 -4
  188. package/lib/internal/platform-comment.ts +12 -4
  189. package/lib/internal/platform-context.ts +3 -1
  190. package/lib/internal/platform-issue.ts +11 -4
  191. package/lib/internal/platform-metadata.ts +70 -0
  192. package/lib/internal/platform-pr-review.ts +14 -3
  193. package/lib/internal/platform-pr.ts +38 -14
  194. package/lib/internal/platform-release-notes.ts +5 -3
  195. package/lib/internal/platform-security.ts +83 -0
  196. package/lib/internal/pr-review-grade.ts +10 -3
  197. package/lib/internal/release-workflow.ts +91 -87
  198. package/lib/internal/sandbox-control.ts +181 -6
  199. package/lib/internal/task-activity.ts +12 -4
  200. package/lib/internal/task-artifact.ts +15 -79
  201. package/lib/internal/task-context.ts +3 -1
  202. package/lib/internal/task-create.ts +76 -15
  203. package/lib/internal/task-delivery.ts +3 -1
  204. package/lib/internal/task-event.ts +21 -10
  205. package/lib/internal/task-finalization.ts +3 -1
  206. package/lib/internal/task-invalidation.ts +3 -1
  207. package/lib/internal/task-ledger.ts +11 -1
  208. package/lib/internal/task-lifecycle.ts +3 -1
  209. package/lib/internal/task-operation-registry.ts +590 -0
  210. package/lib/internal/task-orchestration.ts +12 -0
  211. package/lib/internal/task-override.ts +9 -3
  212. package/lib/internal/task-qualification.ts +61 -0
  213. package/lib/internal/task-review.ts +7 -50
  214. package/lib/internal/task-short-id.ts +10 -1
  215. package/lib/internal/task-snapshot.ts +3 -1
  216. package/lib/internal/task-validate.ts +32 -12
  217. package/lib/internal/task-verify.ts +4 -1
  218. package/lib/internal/task-warning.ts +7 -1
  219. package/lib/owned-path.ts +26 -0
  220. package/lib/platform/check-status.ts +9 -0
  221. package/lib/platform/comment-safety.ts +255 -0
  222. package/lib/platform/completion-backfill.ts +1 -1
  223. package/lib/platform/github-data.ts +322 -0
  224. package/lib/platform/github-provider.ts +244 -27
  225. package/lib/platform/issue-comments.ts +159 -35
  226. package/lib/platform/issues.ts +4 -245
  227. package/lib/platform/pr-change-report.ts +6 -12
  228. package/lib/platform/pr-checks.ts +13 -77
  229. package/lib/platform/pr-summary.ts +88 -11
  230. package/lib/platform/provider-contract.ts +78 -3
  231. package/lib/platform/provider-validation.ts +85 -3
  232. package/lib/platform/pull-requests.ts +129 -348
  233. package/lib/platform/repository-metadata.ts +453 -0
  234. package/lib/platform/security-alerts.ts +191 -0
  235. package/lib/platform/snapshots.ts +53 -0
  236. package/lib/platform/verification-sync.ts +171 -177
  237. package/lib/process-data/index.ts +7 -0
  238. package/lib/release/workflow.ts +3 -3
  239. package/lib/run/index.ts +5 -0
  240. package/lib/sandbox/commands/control-status.ts +26 -0
  241. package/lib/sandbox/commands/create.ts +53 -12
  242. package/lib/sandbox/commands/ls.ts +7 -0
  243. package/lib/sandbox/commands/rm.ts +3 -996
  244. package/lib/sandbox/commands/show.ts +16 -0
  245. package/lib/sandbox/control/audit.ts +253 -0
  246. package/lib/sandbox/control/client.ts +150 -13
  247. package/lib/sandbox/control/completed-reentry.ts +98 -0
  248. package/lib/sandbox/control/container-identity.ts +27 -43
  249. package/lib/sandbox/control/executor.ts +262 -37
  250. package/lib/sandbox/control/finalization-response.ts +29 -0
  251. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  252. package/lib/sandbox/control/lifecycle.ts +672 -18
  253. package/lib/sandbox/control/native-file-lock.ts +108 -0
  254. package/lib/sandbox/control/protocol.ts +80 -8
  255. package/lib/sandbox/control/server.ts +779 -82
  256. package/lib/sandbox/control/state.ts +84 -3
  257. package/lib/sandbox/control/task-view.ts +240 -0
  258. package/lib/sandbox/control/task-workflow.ts +95 -0
  259. package/lib/sandbox/control/workflow-executor.ts +102 -0
  260. package/lib/sandbox/engines/authority.ts +365 -0
  261. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  262. package/lib/sandbox/index.ts +9 -2
  263. package/lib/sandbox/recovery.ts +49 -6
  264. package/lib/sandbox/removal.ts +2144 -0
  265. package/lib/sandbox/task-cleanup.ts +209 -0
  266. package/lib/sandbox/workspace-view.ts +26 -18
  267. package/lib/sandbox/worktree-safety.ts +4 -1
  268. package/lib/server/index.ts +10 -0
  269. package/lib/task/activity-intent.ts +2 -2
  270. package/lib/task/activity-log.ts +27 -19
  271. package/lib/task/artifact-command.ts +104 -0
  272. package/lib/task/artifact-lifecycle.ts +66 -39
  273. package/lib/task/artifact-name.ts +55 -0
  274. package/lib/task/artifact-operations.ts +580 -0
  275. package/lib/task/artifact-receipts.ts +3 -14
  276. package/lib/task/artifact-repair-intent.ts +152 -0
  277. package/lib/task/artifact-schema.ts +163 -0
  278. package/lib/task/capabilities.ts +82 -22
  279. package/lib/task/codex-orchestration.ts +85 -24
  280. package/lib/task/commit-identity.ts +31 -0
  281. package/lib/task/commit-operation.ts +2 -16
  282. package/lib/task/control-authority.ts +513 -6
  283. package/lib/task/control-recovery.ts +174 -0
  284. package/lib/task/create-service.ts +131 -6
  285. package/lib/task/create.ts +83 -1
  286. package/lib/task/decision-details.ts +3 -71
  287. package/lib/task/events.ts +292 -75
  288. package/lib/task/finalization-state.ts +11 -0
  289. package/lib/task/human-override.ts +3 -8
  290. package/lib/task/implementation-inputs.ts +3 -3
  291. package/lib/task/index.ts +16 -3
  292. package/lib/task/intermediate-cleanup.ts +455 -0
  293. package/lib/task/invalidation.ts +18 -62
  294. package/lib/task/ledger-intents.ts +7 -24
  295. package/lib/task/ledger.ts +1 -3
  296. package/lib/task/lifecycle.ts +17 -1
  297. package/lib/task/local-artifact-finalization.ts +223 -388
  298. package/lib/task/manual-validation-completion.ts +40 -0
  299. package/lib/task/manual-validation-evidence.ts +209 -0
  300. package/lib/task/manual-validation-receipt.ts +152 -0
  301. package/lib/task/manual-validation-shared.ts +50 -0
  302. package/lib/task/manual-validation-transaction.ts +304 -0
  303. package/lib/task/markdown.ts +70 -0
  304. package/lib/task/orchestration.ts +191 -68
  305. package/lib/task/qualification-audit.ts +588 -0
  306. package/lib/task/qualification-confirmation.ts +117 -0
  307. package/lib/task/qualification-intents.ts +150 -0
  308. package/lib/task/review-artifacts.ts +21 -41
  309. package/lib/task/review-command.ts +23 -0
  310. package/lib/task/review-finalization.ts +154 -153
  311. package/lib/task/review-fingerprint.ts +2 -2
  312. package/lib/task/sections.ts +33 -52
  313. package/lib/task/verification-config.ts +12 -0
  314. package/lib/task/verification-engine.ts +165 -370
  315. package/lib/task/verification-types.ts +17 -0
  316. package/lib/task/verification.ts +1 -1
  317. package/lib/task/workflow-command.ts +18 -0
  318. package/lib/task/workflow-warning-intents.ts +0 -13
  319. package/lib/task/workflow-warnings.ts +21 -2
  320. package/lib/update.ts +6 -0
  321. package/package.json +7 -2
  322. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  323. package/templates/.agents/rules/README.en.md +2 -0
  324. package/templates/.agents/rules/README.zh-CN.md +2 -0
  325. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  326. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  327. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  328. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  329. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  330. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  331. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  332. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  333. package/templates/.agents/rules/security-alerts.en.md +8 -8
  334. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  335. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  336. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  337. package/templates/.agents/scripts/package.json +3 -0
  338. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  339. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  340. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  341. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  342. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  343. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  344. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  345. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  346. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  347. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  348. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  349. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  350. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  351. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  352. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  353. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  354. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  355. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  356. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  357. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  358. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +37 -21
  359. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +37 -21
  360. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  361. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  362. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +4 -0
  363. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +4 -0
  364. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  365. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  366. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -23
  367. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -23
  368. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  369. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  370. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  371. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  372. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  373. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  374. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  375. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  376. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  377. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  378. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  379. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  380. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  381. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  382. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  383. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  384. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  385. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  386. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  387. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  388. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  389. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  390. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  391. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  392. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  393. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  394. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  395. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  396. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  397. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  398. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  399. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  400. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  401. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  402. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  403. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  404. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  405. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  406. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +25 -4
  407. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +25 -4
  408. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  409. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  410. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +5 -1
  411. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +5 -1
  412. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  413. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  414. package/templates/.agents/skills/test/SKILL.en.md +29 -7
  415. package/templates/.agents/skills/test/SKILL.zh-CN.md +29 -7
  416. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  417. package/templates/.agents/templates/task.en.md +6 -0
  418. package/templates/.agents/templates/task.zh-CN.md +6 -0
  419. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  420. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  421. package/dist/lib/platform/adapters.js +0 -56
  422. package/lib/platform/adapters.ts +0 -197
  423. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  424. package/templates/.agents/scripts/security-alerts.sh +0 -31
  425. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  426. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  427. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  428. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -2,8 +2,8 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
 
5
- import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.ts';
6
- import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.ts';
5
+ import { parseArtifactName } from './artifact-name.ts';
6
+ import { validateCompletedArtifact, hasOpenArtifactRound, validateArtifactPublication } from './artifact-lifecycle.ts';
7
7
  import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
8
8
  import type { LedgerStageStatus, ReviewStage } from './ledger.ts';
9
9
  import { finalizeReviewSummaryContent } from './review-artifacts.ts';
@@ -14,6 +14,11 @@ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-
14
14
  import type { ResolveTaskRefErrorCode } from './resolve-ref.ts';
15
15
  import { allowsManualOverride } from './guard-override.ts';
16
16
  import type { ManualOverrideCapability } from './guard-override.ts';
17
+ import { getArtifactSchema } from './artifact-schema.ts';
18
+ import { canonicalSemanticDigest, inspectArtifactContract, sha256Content } from './artifact-operations.ts';
19
+ import { readArtifactRepairIntent, writeArtifactRepairIntent } from './artifact-repair-intent.ts';
20
+ import type { ArtifactRepairOperation } from './artifact-operations.ts';
21
+ import type { ArtifactRepairIntent } from './artifact-repair-intent.ts';
17
22
 
18
23
  type ReviewFinalizationErrorCode =
19
24
  | ResolveTaskRefErrorCode
@@ -26,6 +31,7 @@ type ReviewFinalizationErrorCode =
26
31
  | 'REVIEW_SUMMARY_NOT_FOUND'
27
32
  | 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
28
33
  | 'REVIEW_SUMMARY_COUNT_MISMATCH'
34
+ | 'REVIEW_ARTIFACT_STRUCTURE_INVALID'
29
35
  | 'REVIEW_DECISION_DETAIL_INVALID'
30
36
  | 'REVIEW_ARTIFACT_CONFLICT'
31
37
  | 'REVIEW_PROVENANCE_INVALID'
@@ -48,6 +54,10 @@ type ReviewFinalizationResult = {
48
54
  stage: string;
49
55
  artifact: string;
50
56
  stageStatus: LedgerStageStatus | null;
57
+ artifactSha256: string | null;
58
+ semanticDigest: string | null;
59
+ repairable: boolean;
60
+ operation: ArtifactRepairOperation | null;
51
61
  operations: readonly {
52
62
  kind: 'artifact';
53
63
  artifact: string;
@@ -70,10 +80,10 @@ type ReviewFinalizationOptions = {
70
80
  lockAlreadyHeld?: boolean;
71
81
  };
72
82
 
73
- const STAGES: Record<ReviewStage, { family: 'review-analysis' | 'review-plan' | 'review-code'; action: string }> = {
74
- analysis: { family: 'review-analysis', action: 'Review Analysis' },
75
- plan: { family: 'review-plan', action: 'Review Plan' },
76
- code: { family: 'review-code', action: 'Review Code' }
83
+ const STAGES: Record<ReviewStage, { family: 'review-analysis' | 'review-plan' | 'review-code' }> = {
84
+ analysis: { family: 'review-analysis' },
85
+ plan: { family: 'review-plan' },
86
+ code: { family: 'review-code' }
77
87
  };
78
88
  const DEFAULT_FILE_SYSTEM: ReviewFileSystem = {
79
89
  readFileSync: (file) => fs.readFileSync(file, 'utf8'),
@@ -88,7 +98,8 @@ function failed(
88
98
  code: ReviewFinalizationErrorCode,
89
99
  message: string,
90
100
  taskId: string | null = null,
91
- stageStatus: LedgerStageStatus | null = null
101
+ stageStatus: LedgerStageStatus | null = null,
102
+ extra: Partial<ReviewFinalizationResult> = {}
92
103
  ): ReviewFinalizationResult {
93
104
  return {
94
105
  status: 'failed',
@@ -99,20 +110,16 @@ function failed(
99
110
  stage: request.stage,
100
111
  artifact: request.artifact,
101
112
  stageStatus,
113
+ artifactSha256: null,
114
+ semanticDigest: null,
115
+ repairable: false,
116
+ operation: null,
102
117
  operations: [],
103
- error: { code, message }
118
+ error: { code, message },
119
+ ...extra
104
120
  };
105
121
  }
106
122
 
107
- function openReviewRound(content: string, action: string, round: number): boolean {
108
- const section = locateActivityLog(content);
109
- if (!section) return false;
110
- const expected = `${action} (Round ${round})`;
111
- return startedBackedRows(pairEntries(section.entries)).filter((row) => (
112
- row.step === expected && !row.done
113
- )).length === 1;
114
- }
115
-
116
123
  function cleanupTemp(fileSystem: ReviewFileSystem, tempPath: string): void {
117
124
  try {
118
125
  fileSystem.unlinkSync(tempPath);
@@ -121,174 +128,167 @@ function cleanupTemp(fileSystem: ReviewFileSystem, tempPath: string): void {
121
128
  }
122
129
  }
123
130
 
124
- function finalizeReviewSummaryUnlocked(
131
+ type ReviewSummaryCandidatePreparation = Readonly<{
132
+ result: ReviewFinalizationResult;
133
+ content: string;
134
+ provenance?: Parameters<typeof writeArtifactRepairIntent>[1];
135
+ expectedIntent?: ArtifactRepairIntent | null;
136
+ }>;
137
+
138
+ /** Prepare domain validation, summary bytes and provenance before any publication. */
139
+ function prepareReviewSummaryCandidate(
125
140
  request: ReviewFinalizationRequest,
141
+ artifactContent: string,
126
142
  options: ReviewFinalizationOptions = {}
127
- ): ReviewFinalizationResult {
143
+ ): ReviewSummaryCandidatePreparation {
144
+ let taskId: string | null = null;
145
+ let stageStatus: LedgerStageStatus | null = null;
146
+ const reject = (code: ReviewFinalizationErrorCode, message: string, extra: Partial<ReviewFinalizationResult> = {}): ReviewSummaryCandidatePreparation => ({
147
+ result: failed(request, code, message, taskId, stageStatus, extra), content: artifactContent
148
+ });
128
149
  const stage = request.stage as ReviewStage;
129
150
  const spec = STAGES[stage];
130
- if (!spec) return failed(request, 'REVIEW_STAGE_INVALID', `unsupported review stage '${request.stage}'`);
131
- if (!request.taskRef || !request.artifact) {
132
- return failed(request, 'REVIEW_PAYLOAD_INVALID', 'taskRef, stage, and artifact are required');
133
- }
151
+ if (!spec) return reject('REVIEW_STAGE_INVALID', `unsupported review stage '${request.stage}'`);
152
+ if (!request.taskRef || !request.artifact) return reject('REVIEW_PAYLOAD_INVALID', 'taskRef, stage, and artifact are required');
134
153
  const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
135
- if (!resolved.ok) return failed(request, resolved.code, resolved.message, resolved.taskId);
154
+ taskId = resolved.taskId ?? null;
155
+ if (!resolved.ok) return reject(resolved.code, resolved.message);
136
156
  if (resolved.state !== 'active' && !allowsManualOverride(options.manualOverride, 'review-finalization', 'TASK_STATE_MISMATCH')) {
137
- return failed(request, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
157
+ return reject('TASK_STATE_MISMATCH', `task ${taskId} is ${resolved.state}, expected active`);
138
158
  }
139
- const parsedArtifact = parseArtifactName(request.artifact);
140
- if (!parsedArtifact || parsedArtifact.family !== spec.family) {
141
- return failed(
142
- request,
143
- 'REVIEW_ARTIFACT_IDENTITY_INVALID',
144
- `artifact '${request.artifact}' does not match ${spec.family}`,
145
- resolved.taskId
146
- );
159
+ const parsed = parseArtifactName(request.artifact);
160
+ if (!parsed || parsed.family !== spec.family) return reject('REVIEW_ARTIFACT_IDENTITY_INVALID', `artifact '${request.artifact}' does not match ${spec.family}`);
161
+ const publicationError = validateArtifactPublication(resolved.taskDir, spec.family, request.artifact);
162
+ if (publicationError) return reject('REVIEW_ARTIFACT_IDENTITY_INVALID', publicationError.message);
163
+ let taskContent: string;
164
+ try { taskContent = (options.fileSystem?.readFileSync ?? DEFAULT_FILE_SYSTEM.readFileSync)(resolved.taskMdPath); }
165
+ catch (error) { return reject('REVIEW_ARTIFACT_NOT_REGULAR', String(error)); }
166
+ const artifactSha256 = sha256Content(artifactContent);
167
+ const semanticDigest = canonicalSemanticDigest(artifactContent);
168
+ const digests = { artifactSha256, semanticDigest };
169
+ let repairIntent;
170
+ try { repairIntent = readArtifactRepairIntent(resolved.repoRoot, taskId!, spec.family, request.artifact); }
171
+ catch (error) { return reject('REVIEW_PROVENANCE_INVALID', String(error), digests); }
172
+ const schema = getArtifactSchema(spec.family)!;
173
+ const structure = inspectArtifactContract(artifactContent, schema);
174
+ if (!structure.ok) {
175
+ const repairable = structure.repair !== null && structure.diagnostics.length === 1 && structure.diagnostics[0]?.repairable === true;
176
+ if (repairable && repairIntent && (repairIntent.state !== 'awaiting-repair'
177
+ || repairIntent.artifactSha256 !== artifactSha256 || repairIntent.semanticDigest !== structure.semanticDigest)) {
178
+ return reject('REVIEW_PROVENANCE_INVALID', 'a different review repair baseline is already recorded for this artifact', digests);
179
+ }
180
+ return {
181
+ ...reject('REVIEW_ARTIFACT_STRUCTURE_INVALID', structure.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; '), {
182
+ artifactSha256, semanticDigest: structure.semanticDigest, repairable, operation: structure.repair
183
+ }),
184
+ ...(repairable && !repairIntent ? { provenance: {
185
+ version: 2 as const, taskId: taskId!, family: spec.family, artifact: request.artifact,
186
+ state: 'awaiting-repair' as const, baselineSemanticDigest: structure.semanticDigest,
187
+ artifactSha256, semanticDigest: structure.semanticDigest,
188
+ recoveryOperationId: null, phase: null, authorityDigest: null,
189
+ requestId: `review-finalize:${taskId!}`, createdAt: Date.now(), updatedAt: Date.now()
190
+ }, expectedIntent: null } : {})
191
+ };
147
192
  }
148
- const validated = validateCompletedArtifact(
149
- resolved.taskDir,
150
- spec.family,
151
- request.artifact,
152
- parsedArtifact.round
153
- );
154
- if (!validated.ok) {
155
- const code = validated.error.code === 'ARTIFACT_NOT_REGULAR'
156
- ? 'REVIEW_ARTIFACT_NOT_REGULAR'
157
- : 'REVIEW_ARTIFACT_IDENTITY_INVALID';
158
- return failed(request, code, validated.error.message, resolved.taskId);
193
+ if (repairIntent?.state === 'awaiting-repair' && repairIntent.baselineSemanticDigest !== semanticDigest) {
194
+ return reject('REVIEW_PROVENANCE_INVALID', 'the repaired review artifact semantic digest does not match the recorded repair baseline', digests);
159
195
  }
160
-
161
- const fileSystem = { ...DEFAULT_FILE_SYSTEM, ...options.fileSystem };
162
- let taskContent: string;
163
- let artifactContent: string;
164
- try {
165
- taskContent = fileSystem.readFileSync(resolved.taskMdPath);
166
- artifactContent = fileSystem.readFileSync(validated.artifact.path);
167
- } catch (error) {
168
- return failed(request, 'REVIEW_ARTIFACT_NOT_REGULAR', String(error), resolved.taskId);
196
+ if ((repairIntent?.state === 'passed' || repairIntent?.state === 'consumed')
197
+ && (repairIntent.artifactSha256 !== artifactSha256 || repairIntent.semanticDigest !== semanticDigest)) {
198
+ return reject('REVIEW_PROVENANCE_INVALID', 'the review artifact changed after its finalization provenance was recorded', digests);
169
199
  }
170
- if (!openReviewRound(taskContent, spec.action, parsedArtifact.round)) {
171
- return failed(
172
- request,
173
- 'REVIEW_ARTIFACT_IDENTITY_INVALID',
174
- `${request.artifact} does not have one matching open started review event`,
175
- resolved.taskId
176
- );
200
+ if (!hasOpenArtifactRound(taskContent, spec.family, parsed.round)) {
201
+ return reject('REVIEW_ARTIFACT_IDENTITY_INVALID', `${request.artifact} does not have one matching open started review event`);
177
202
  }
178
203
  const execution = validateLifecycleExecution(request.taskRef, {
179
204
  mode: request.orchestrated ? 'orchestrated' : 'standalone',
180
- identity: {
181
- stage: spec.family,
182
- round: parsedArtifact.round,
183
- artifact: request.artifact,
184
- role: 'reviewer'
185
- },
205
+ identity: { stage: spec.family, round: parsed.round, artifact: request.artifact, role: 'reviewer' },
186
206
  dryRun: request.dryRun
187
207
  }, { repoRoot: options.repoRoot });
188
208
  if (!execution.ok) {
189
- return failed(
190
- request,
191
- 'REVIEW_PROVENANCE_INVALID',
192
- `${execution.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH'}: ${execution.error?.message ?? 'orchestration provenance validation failed'}`,
193
- resolved.taskId
194
- );
209
+ return reject('REVIEW_PROVENANCE_INVALID', `${execution.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH'}: ${execution.error?.message ?? 'orchestration provenance validation failed'}`);
195
210
  }
196
-
197
- let rows;
198
211
  try {
199
212
  const ledger = parseLedgerDocument(taskContent);
200
- if (!ledger.present) {
201
- return failed(request, 'REVIEW_LEDGER_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
213
+ if (!ledger.present) return reject('REVIEW_LEDGER_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
214
+ const error = validateLedgerRows(ledger.rows);
215
+ if (error) return reject('REVIEW_LEDGER_INVALID', `${error.code}: ${error.message}`);
216
+ stageStatus = summarizeLedgerStage(ledger.rows, stage);
217
+ } catch (error) { return reject('REVIEW_LEDGER_INVALID', String(error)); }
218
+ const detail = inspectDecisionDetailDuplicates(artifactContent);
219
+ if (!detail.ok) return reject('REVIEW_DECISION_DETAIL_INVALID', `${detail.code}: ${detail.message}`);
220
+ const transformed = finalizeReviewSummaryContent(artifactContent, stageStatus.unresolvedFindingCounts);
221
+ if (!transformed.ok) return reject(transformed.code, transformed.message);
222
+ const finalDigests = { artifactSha256: sha256Content(transformed.content), semanticDigest: canonicalSemanticDigest(transformed.content) };
223
+ return {
224
+ content: transformed.content,
225
+ result: {
226
+ ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', taskId, stageStatus, finalDigests),
227
+ status: transformed.changed ? request.dryRun ? 'planned' : 'applied' : 'no-op',
228
+ changed: transformed.changed,
229
+ operations: transformed.changed ? [{ kind: 'artifact', artifact: request.artifact, operation: 'update' }] : [],
230
+ error: null
231
+ },
232
+ ...(repairIntent?.state === 'awaiting-repair' ? {
233
+ provenance: { ...repairIntent, state: 'passed' as const, ...finalDigests },
234
+ expectedIntent: repairIntent
235
+ } : {})
236
+ };
237
+ }
238
+
239
+ function commitReviewSummaryProvenance(
240
+ prepared: ReviewSummaryCandidatePreparation,
241
+ repoRoot: string
242
+ ): ReviewFinalizationResult {
243
+ try {
244
+ if (prepared.provenance) {
245
+ writeArtifactRepairIntent(repoRoot, prepared.provenance, { expected: prepared.expectedIntent ?? null });
202
246
  }
203
- rows = ledger.rows;
247
+ return prepared.result;
204
248
  } catch (error) {
205
- return failed(request, 'REVIEW_LEDGER_INVALID', String(error), resolved.taskId);
206
- }
207
- const ledgerError = validateLedgerRows(rows);
208
- if (ledgerError) {
209
- return failed(
210
- request,
211
- 'REVIEW_LEDGER_INVALID',
212
- `${ledgerError.code}: ${ledgerError.message}`,
213
- resolved.taskId
214
- );
215
- }
216
- const stageStatus = summarizeLedgerStage(rows, stage);
217
- const detailInspection = inspectDecisionDetailDuplicates(artifactContent);
218
- if (!detailInspection.ok) {
219
- return failed(
220
- request,
221
- 'REVIEW_DECISION_DETAIL_INVALID',
222
- `${detailInspection.code}: ${detailInspection.message}`,
223
- resolved.taskId,
224
- stageStatus
225
- );
226
- }
227
- const transformed = finalizeReviewSummaryContent(
228
- artifactContent,
229
- stageStatus.unresolvedFindingCounts
230
- );
231
- if (!transformed.ok) {
232
- return failed(request, transformed.code, transformed.message, resolved.taskId, stageStatus);
233
- }
234
- if (!transformed.changed) {
235
- return {
236
- ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
237
- status: 'no-op',
238
- error: null
239
- };
240
- }
241
- const operations = [{ kind: 'artifact' as const, artifact: request.artifact, operation: 'update' as const }];
242
- if (request.dryRun) {
243
- return {
244
- ...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
245
- status: 'planned',
246
- changed: true,
247
- operations,
248
- error: null
249
- };
249
+ return { ...prepared.result, status: 'failed', error: {
250
+ code: 'REVIEW_PROVENANCE_INVALID', message: `cannot record review provenance: ${String(error)}`
251
+ } };
250
252
  }
253
+ }
251
254
 
252
- const tempPath = path.join(
253
- resolved.taskDir,
254
- `.${request.artifact}.tmp-${process.pid}-${(options.randomSuffix ?? randomUUID)()}`
255
- );
256
- let mode: number;
255
+ function finalizeReviewSummaryUnlocked(
256
+ request: ReviewFinalizationRequest,
257
+ options: ReviewFinalizationOptions = {}
258
+ ): ReviewFinalizationResult {
259
+ const spec = STAGES[request.stage as ReviewStage];
260
+ if (!spec || !request.taskRef || !request.artifact) return prepareReviewSummaryCandidate(request, '', options).result;
261
+ const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
262
+ if (!resolved.ok) return failed(request, resolved.code, resolved.message, resolved.taskId);
263
+ const validated = validateCompletedArtifact(resolved.taskDir, spec.family, request.artifact);
264
+ if (!validated.ok) {
265
+ return failed(request, validated.error.code === 'ARTIFACT_NOT_REGULAR' ? 'REVIEW_ARTIFACT_NOT_REGULAR' : 'REVIEW_ARTIFACT_IDENTITY_INVALID', validated.error.message, resolved.taskId);
266
+ }
267
+ const fileSystem = { ...DEFAULT_FILE_SYSTEM, ...options.fileSystem };
268
+ let content: string;
269
+ try { content = fileSystem.readFileSync(validated.artifact.path); }
270
+ catch (error) { return failed(request, 'REVIEW_ARTIFACT_NOT_REGULAR', String(error), resolved.taskId); }
271
+ const prepared = prepareReviewSummaryCandidate(request, content, options);
272
+ if (request.dryRun) return prepared.result;
273
+ if (prepared.result.status !== 'applied') return commitReviewSummaryProvenance(prepared, resolved.repoRoot);
274
+ const tempPath = path.join(resolved.taskDir, `.${request.artifact}.tmp-${process.pid}-${(options.randomSuffix ?? randomUUID)()}`);
257
275
  try {
258
- mode = fileSystem.statModeSync(validated.artifact.path);
259
- fileSystem.writeFileSync(tempPath, transformed.content, mode);
276
+ fileSystem.writeFileSync(tempPath, prepared.content, fileSystem.statModeSync(validated.artifact.path));
260
277
  } catch (error) {
261
278
  cleanupTemp(fileSystem, tempPath);
262
- return failed(request, 'REVIEW_TEMP_WRITE_FAILED', String(error), resolved.taskId, stageStatus);
279
+ return failed(request, 'REVIEW_TEMP_WRITE_FAILED', String(error), resolved.taskId, prepared.result.stageStatus);
263
280
  }
264
281
  try {
265
- if (fileSystem.readFileSync(validated.artifact.path) !== artifactContent) {
282
+ if (fileSystem.readFileSync(validated.artifact.path) !== content) {
266
283
  cleanupTemp(fileSystem, tempPath);
267
- return failed(
268
- request,
269
- 'REVIEW_ARTIFACT_CONFLICT',
270
- `${request.artifact} changed during finalization`,
271
- resolved.taskId,
272
- stageStatus
273
- );
284
+ return failed(request, 'REVIEW_ARTIFACT_CONFLICT', `${request.artifact} changed during finalization`, resolved.taskId, prepared.result.stageStatus);
274
285
  }
275
286
  fileSystem.renameSync(tempPath, validated.artifact.path);
276
287
  } catch (error) {
277
288
  cleanupTemp(fileSystem, tempPath);
278
- return failed(request, 'REVIEW_RENAME_FAILED', String(error), resolved.taskId, stageStatus);
289
+ return failed(request, 'REVIEW_RENAME_FAILED', String(error), resolved.taskId, prepared.result.stageStatus);
279
290
  }
280
- return {
281
- status: 'applied',
282
- changed: true,
283
- intent: 'finalize-summary',
284
- requestRef: request.taskRef,
285
- taskId: resolved.taskId,
286
- stage,
287
- artifact: request.artifact,
288
- stageStatus,
289
- operations,
290
- error: null
291
- };
291
+ return commitReviewSummaryProvenance(prepared, resolved.repoRoot);
292
292
  }
293
293
 
294
294
  function finalizeReviewSummary(
@@ -316,11 +316,12 @@ function finalizeReviewSummary(
316
316
  }
317
317
  }
318
318
 
319
- export { finalizeReviewSummary };
319
+ export { finalizeReviewSummary, prepareReviewSummaryCandidate, commitReviewSummaryProvenance };
320
320
  export type {
321
321
  ReviewFinalizationError,
322
322
  ReviewFinalizationErrorCode,
323
323
  ReviewFinalizationOptions,
324
324
  ReviewFinalizationRequest,
325
- ReviewFinalizationResult
325
+ ReviewFinalizationResult,
326
+ ReviewSummaryCandidatePreparation
326
327
  };
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
 
4
- import { artifactName, maxRound } from "./review-artifacts.ts";
4
+ import { artifactName, maxArtifactRound } from "./artifact-name.ts";
5
5
  import { invalidationBlocks, isArtifactInvalidated, parseInvalidationDocument } from "./invalidation.ts";
6
6
 
7
7
  type ReviewedGitTree = string & { readonly __reviewedGitTree: unique symbol };
@@ -45,7 +45,7 @@ export function resolvePostReviewGlobs(
45
45
 
46
46
  export function findAuthoritativeReviewCodeArtifact(taskDir: string) {
47
47
  const entries = fs.existsSync(taskDir) ? fs.readdirSync(taskDir) : [];
48
- const round = maxRound(entries, "review-code");
48
+ const round = maxArtifactRound(entries, "review-code");
49
49
  if (round === 0) {
50
50
  return { ok: false, round: 0, fileName: null, path: null, error: null };
51
51
  }
@@ -1,4 +1,5 @@
1
1
  import type { FrontmatterScalar } from './frontmatter.ts';
2
+ import { scanVisibleMarkdown } from './markdown.ts';
2
3
 
3
4
  function escapeRegExp(value: string): string {
4
5
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
@@ -93,12 +94,14 @@ function parseTable(
93
94
  throw new DocumentMutationError('TASK_DOCUMENT_INVALID', 'table section is ambiguous');
94
95
  }
95
96
  const section = sections[0]!;
97
+ const visibleStarts = new Set(scanVisibleMarkdown(content).lines.map((line) => line.start));
96
98
  const sectionLines = linesOf(content).filter(
97
99
  (line) => line.start >= section.bodyStart && line.start < section.end
98
100
  );
99
101
  const firstContent = sectionLines.findIndex((line) => line.text.trim() !== '');
100
102
  const tables: number[] = [];
101
103
  for (let index = 0; index + 1 < sectionLines.length; index += 1) {
104
+ if (!visibleStarts.has(sectionLines[index]!.start) || !visibleStarts.has(sectionLines[index + 1]!.start)) continue;
102
105
  const header = splitTableCells(sectionLines[index]!.text);
103
106
  const separator = splitTableCells(sectionLines[index + 1]!.text);
104
107
  if (!header || !separator || header.cells.length !== input.columns.length) continue;
@@ -173,22 +176,24 @@ function documentEol(content: string): '\n' | '\r\n' {
173
176
 
174
177
  function matchingSections(content: string, aliases: readonly string[]) {
175
178
  const allowed = new Set(aliases);
176
- return linesOf(content)
177
- .map((line, index, lines) => {
178
- const match = /^##\s+(.+?)\s*$/.exec(line.text);
179
- if (!match?.[1] || !allowed.has(match[1])) return null;
180
- let end = content.length;
181
- for (let i = index + 1; i < lines.length; i += 1) {
182
- if (/^##\s+/.test(lines[i]!.text)) {
183
- end = lines[i]!.start;
184
- break;
185
- }
186
- }
187
- return { heading: match[1], line, bodyStart: line.end, end };
179
+ const lines = new Map(linesOf(content).map((line) => [line.start, line]));
180
+ const headings = scanVisibleMarkdown(content).headings.filter((heading) => heading.level === 2 || heading.level === 3);
181
+ return headings
182
+ .map((heading, index) => {
183
+ if (!allowed.has(heading.text)) return null;
184
+ const next = headings.slice(index + 1).find((candidate) => candidate.level <= heading.level);
185
+ const line = lines.get(heading.start)!;
186
+ return { heading: heading.text, level: heading.level, line, bodyStart: line.end, end: next?.start ?? content.length };
188
187
  })
189
188
  .filter((match): match is NonNullable<typeof match> => match !== null);
190
189
  }
191
190
 
191
+ // Select the first visible section at the requested level, in document order.
192
+ function findSectionRange(content: string, aliases: readonly string[], level: 2 | 3 = 2) {
193
+ const section = matchingSections(content, aliases).find((candidate) => candidate.level === level);
194
+ return section ? { start: section.line.start, bodyStart: section.bodyStart, end: section.end, heading: section.heading } : null;
195
+ }
196
+
192
197
  function validateAliases(aliases: readonly string[], name: string): void {
193
198
  if (!Array.isArray(aliases) || aliases.length === 0 || aliases.some((alias) => !alias || /[\r\n]/.test(alias))) {
194
199
  throw new DocumentMutationError('MUTATION_INVALID', `${name} must contain headings`);
@@ -349,11 +354,13 @@ function mutateTableRow(content: string, mutation: TableRowMutation): TableMutat
349
354
  );
350
355
  }
351
356
  const section = sections[0]!;
357
+ const visibleStarts = new Set(scanVisibleMarkdown(content).lines.map((line) => line.start));
352
358
  const sectionLines = linesOf(content).filter(
353
359
  (line) => line.start >= section.bodyStart && line.start < section.end
354
360
  );
355
361
  const tables: { headerIndex: number; cells: string[] }[] = [];
356
362
  for (let i = 0; i + 1 < sectionLines.length; i += 1) {
363
+ if (!visibleStarts.has(sectionLines[i]!.start) || !visibleStarts.has(sectionLines[i + 1]!.start)) continue;
357
364
  const header = splitTableCells(sectionLines[i]!.text);
358
365
  const separator = splitTableCells(sectionLines[i + 1]!.text);
359
366
  if (!header || !separator || header.cells.length !== mutation.columns.length) continue;
@@ -475,24 +482,11 @@ function mutateTableRow(content: string, mutation: TableRowMutation): TableMutat
475
482
  * lines are trimmed. Returns '' when no alias heading is present.
476
483
  */
477
484
  function extractSection(content: string, aliases: string[]): string {
478
- const lines = content.split('\n');
479
- let start = -1;
480
- for (let i = 0; i < lines.length; i += 1) {
481
- const line = lines[i]!.trim();
482
- if (aliases.some((alias) => new RegExp(`^##\\s+${escapeRegExp(alias)}\\s*$`).test(line))) {
483
- start = i + 1;
484
- break;
485
- }
486
- }
487
- if (start === -1) return '';
488
- let end = lines.length;
489
- for (let i = start; i < lines.length; i += 1) {
490
- if (/^##\s+/.test(lines[i]!)) {
491
- end = i;
492
- break;
493
- }
494
- }
495
- return lines.slice(start, end).join('\n').replace(/^\n+/, '').replace(/\n+$/, '');
485
+ const headings = scanVisibleMarkdown(content).headings.filter((heading) => heading.level === 2);
486
+ const index = headings.findIndex((heading) => aliases.includes(heading.text));
487
+ if (index === -1) return '';
488
+ return content.slice(headings[index]!.end + 1, headings[index + 1]?.start ?? content.length)
489
+ .replace(/^(?:\r?\n)+|(?:\r?\n)+$/g, '');
496
490
  }
497
491
 
498
492
  /**
@@ -501,10 +495,8 @@ function extractSection(content: string, aliases: string[]): string {
501
495
  * alias when none is present.
502
496
  */
503
497
  function findSectionHeading(content: string, aliases: string[]): string {
504
- for (const alias of aliases) {
505
- if (new RegExp(`^##\\s+${escapeRegExp(alias)}\\s*$`, 'm').test(content)) return alias;
506
- }
507
- return aliases[0]!;
498
+ const headings = scanVisibleMarkdown(content).headings.filter((heading) => heading.level === 2);
499
+ return aliases.find((alias) => headings.some((heading) => heading.text === alias)) ?? aliases[0]!;
508
500
  }
509
501
 
510
502
  /**
@@ -516,27 +508,16 @@ function findSectionHeading(content: string, aliases: string[]): string {
516
508
  * and trailing blank lines are trimmed. Returns '' when no match is present.
517
509
  */
518
510
  function extractSubSection(content: string, headingPrefix: string): string {
519
- const lines = content.split('\n');
520
- const headRe = new RegExp(`^###\\s+${escapeRegExp(headingPrefix)}(?![\\w-])`);
521
- let start = -1;
522
- for (let i = 0; i < lines.length; i += 1) {
523
- if (headRe.test(lines[i]!.trim())) {
524
- start = i;
525
- break;
526
- }
527
- }
528
- if (start === -1) return '';
529
- let end = lines.length;
530
- for (let i = start + 1; i < lines.length; i += 1) {
531
- if (/^###?\s+/.test(lines[i]!)) {
532
- end = i;
533
- break;
534
- }
535
- }
536
- return lines.slice(start, end).join('\n').replace(/^\n+/, '').replace(/\n+$/, '');
511
+ const headings = scanVisibleMarkdown(content).headings.filter((heading) => heading.level === 2 || heading.level === 3);
512
+ const headRe = new RegExp(`^${escapeRegExp(headingPrefix)}(?![\\w-])`);
513
+ const index = headings.findIndex((heading) => heading.level === 3 && headRe.test(heading.text));
514
+ if (index === -1) return '';
515
+ return content.slice(headings[index]!.start, headings[index + 1]?.start ?? content.length)
516
+ .replace(/^(?:\r?\n)+|(?:\r?\n)+$/g, '');
537
517
  }
538
518
 
539
519
  export {
520
+ findSectionRange,
540
521
  extractSection,
541
522
  findSectionHeading,
542
523
  extractSubSection,
@@ -1,6 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
+ import { getArtifactSchema } from './artifact-schema.ts';
5
+
4
6
  type VerificationConfig = {
5
7
  skill?: string;
6
8
  checks: Record<string, Record<string, unknown> | null>;
@@ -13,6 +15,16 @@ function loadVerificationConfig(repositoryRoot: string, skillName: string): Veri
13
15
  if (!parsed.checks || typeof parsed.checks !== 'object' || Array.isArray(parsed.checks)) {
14
16
  throw new Error(`config/verify.json has invalid checks for skill '${skillName}'`);
15
17
  }
18
+ const artifact = parsed.checks.artifact;
19
+ if (artifact && typeof artifact === 'object' && !Array.isArray(artifact)) {
20
+ const schema = artifact.schema;
21
+ if (schema !== undefined && (typeof schema !== 'string' || !getArtifactSchema(schema))) {
22
+ throw new Error(`config/verify.json has unknown artifact schema '${String(schema)}' for skill '${skillName}'`);
23
+ }
24
+ if (schema !== undefined && 'required_patterns' in artifact) {
25
+ throw new Error(`config/verify.json must not duplicate required_patterns for schema '${String(schema)}' in skill '${skillName}'`);
26
+ }
27
+ }
16
28
  return { ...(parsed.skill ? { skill: parsed.skill } : {}), checks: parsed.checks };
17
29
  }
18
30