@fitlab-ai/agent-infra 0.9.12 → 0.9.14

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 (402) hide show
  1. package/README.md +11 -11
  2. package/README.zh-CN.md +11 -11
  3. package/bin/cli.ts +26 -9
  4. package/dist/bin/cli.js +173 -157
  5. package/dist/bin/internal-cli.js +66 -40
  6. package/dist/lib/agent-client.js +5 -0
  7. package/dist/lib/agent-clients/next-steps.js +6 -1
  8. package/dist/lib/decide.js +3 -3
  9. package/dist/lib/internal/agent-client.js +5 -2
  10. package/dist/lib/internal/cli-route-inventory.js +152 -0
  11. package/dist/lib/internal/codex-lifecycle.js +9 -6
  12. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  13. package/dist/lib/internal/git-workflow.js +10 -6
  14. package/dist/lib/internal/platform-checks.js +19 -7
  15. package/dist/lib/internal/platform-comment.js +25 -13
  16. package/dist/lib/internal/platform-context.js +6 -3
  17. package/dist/lib/internal/platform-issue.js +20 -13
  18. package/dist/lib/internal/platform-metadata.js +88 -0
  19. package/dist/lib/internal/platform-pr-review.js +26 -13
  20. package/dist/lib/internal/platform-pr.js +84 -31
  21. package/dist/lib/internal/platform-release-notes.js +9 -6
  22. package/dist/lib/internal/platform-security.js +93 -0
  23. package/dist/lib/internal/pr-review-grade.js +25 -12
  24. package/dist/lib/internal/release-workflow.js +23 -9
  25. package/dist/lib/internal/sandbox-control.js +50 -7
  26. package/dist/lib/internal/task-activity.js +16 -7
  27. package/dist/lib/internal/task-artifact.js +235 -24
  28. package/dist/lib/internal/task-context.js +4 -1
  29. package/dist/lib/internal/task-create.js +50 -17
  30. package/dist/lib/internal/task-delivery.js +4 -1
  31. package/dist/lib/internal/task-event.js +10 -4
  32. package/dist/lib/internal/task-finalization.js +6 -3
  33. package/dist/lib/internal/task-invalidation.js +63 -0
  34. package/dist/lib/internal/task-ledger.js +20 -6
  35. package/dist/lib/internal/task-lifecycle.js +6 -3
  36. package/dist/lib/internal/task-operation-registry.js +421 -0
  37. package/dist/lib/internal/task-orchestration.js +12 -0
  38. package/dist/lib/internal/task-override.js +14 -7
  39. package/dist/lib/internal/task-qualification.js +90 -0
  40. package/dist/lib/internal/task-review.js +7 -4
  41. package/dist/lib/internal/task-short-id.js +11 -1
  42. package/dist/lib/internal/task-snapshot.js +4 -1
  43. package/dist/lib/internal/task-validate.js +7 -2
  44. package/dist/lib/internal/task-verify.js +11 -7
  45. package/dist/lib/internal/task-warning.js +11 -4
  46. package/dist/lib/platform/adapters.js +1 -3
  47. package/dist/lib/platform/change-request-git-evidence.js +27 -7
  48. package/dist/lib/platform/comment-safety.js +226 -0
  49. package/dist/lib/platform/completion-backfill.js +6 -5
  50. package/dist/lib/platform/context.js +70 -130
  51. package/dist/lib/platform/github-client.js +1 -1
  52. package/dist/lib/platform/github-provider.js +1130 -0
  53. package/dist/lib/platform/in-label-sync.js +143 -0
  54. package/dist/lib/platform/issue-comments.js +237 -72
  55. package/dist/lib/platform/issues.js +335 -189
  56. package/dist/lib/platform/metadata-labels.js +6 -2
  57. package/dist/lib/platform/none-provider.js +28 -0
  58. package/dist/lib/platform/pr-change-report.js +545 -0
  59. package/dist/lib/platform/pr-checks.js +119 -104
  60. package/dist/lib/platform/pr-review.js +88 -72
  61. package/dist/lib/platform/pr-summary.js +541 -50
  62. package/dist/lib/platform/provider-bridge.js +45 -0
  63. package/dist/lib/platform/provider-contract.d.ts +553 -0
  64. package/dist/lib/platform/provider-contract.js +99 -0
  65. package/dist/lib/platform/provider-loader.js +251 -0
  66. package/dist/lib/platform/provider-validation.js +577 -0
  67. package/dist/lib/platform/pull-request-metadata.js +8 -6
  68. package/dist/lib/platform/pull-requests.js +734 -543
  69. package/dist/lib/platform/release-notes.js +78 -55
  70. package/dist/lib/platform/releases.js +79 -50
  71. package/dist/lib/platform/repository-metadata.js +377 -0
  72. package/dist/lib/platform/resource-identity.d.ts +26 -0
  73. package/dist/lib/platform/resource-identity.js +92 -0
  74. package/dist/lib/platform/security-alerts.js +155 -0
  75. package/dist/lib/platform/task-identities.js +31 -0
  76. package/dist/lib/platform/verification-required.js +11 -12
  77. package/dist/lib/platform/verification-sync.js +129 -248
  78. package/dist/lib/pr-review/evidence-grading.js +19 -6
  79. package/dist/lib/process-data/index.js +7 -0
  80. package/dist/lib/run/index.js +68 -23
  81. package/dist/lib/sandbox/commands/control-status.js +22 -0
  82. package/dist/lib/sandbox/commands/create.js +32 -6
  83. package/dist/lib/sandbox/commands/ls.js +5 -0
  84. package/dist/lib/sandbox/commands/rm.js +827 -120
  85. package/dist/lib/sandbox/commands/show.js +16 -0
  86. package/dist/lib/sandbox/control/client.js +174 -19
  87. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  88. package/dist/lib/sandbox/control/container-identity.js +28 -31
  89. package/dist/lib/sandbox/control/executor.js +151 -16
  90. package/dist/lib/sandbox/control/lifecycle.js +621 -127
  91. package/dist/lib/sandbox/control/native-file-lock.js +177 -0
  92. package/dist/lib/sandbox/control/protocol.js +77 -0
  93. package/dist/lib/sandbox/control/server.js +677 -34
  94. package/dist/lib/sandbox/control/state.js +209 -4
  95. package/dist/lib/sandbox/control/task-view.js +183 -0
  96. package/dist/lib/sandbox/engines/authority.js +289 -0
  97. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  98. package/dist/lib/sandbox/index.js +5 -0
  99. package/dist/lib/sandbox/recovery.js +21 -1
  100. package/dist/lib/sandbox/workspace-view.js +2 -1
  101. package/dist/lib/sandbox/worktree-safety.js +4 -1
  102. package/dist/lib/server/index.js +8 -0
  103. package/dist/lib/server/protocol.js +38 -1
  104. package/dist/lib/task/activity-log.js +8 -1
  105. package/dist/lib/task/artifact-lifecycle.js +75 -18
  106. package/dist/lib/task/artifact-operations.js +520 -0
  107. package/dist/lib/task/artifact-repair-intent.js +59 -0
  108. package/dist/lib/task/artifact-schema.js +135 -0
  109. package/dist/lib/task/capabilities.js +310 -0
  110. package/dist/lib/task/commands/status.js +32 -0
  111. package/dist/lib/task/commit-operation.js +12 -1
  112. package/dist/lib/task/control-authority.js +12 -5
  113. package/dist/lib/task/create-service.js +111 -12
  114. package/dist/lib/task/create.js +25 -0
  115. package/dist/lib/task/current-contract.js +5 -1
  116. package/dist/lib/task/decision-details.js +1 -1
  117. package/dist/lib/task/events.js +416 -17
  118. package/dist/lib/task/finalization.js +37 -13
  119. package/dist/lib/task/human-override.js +58 -36
  120. package/dist/lib/task/index.js +11 -3
  121. package/dist/lib/task/invalidation-command.js +86 -0
  122. package/dist/lib/task/invalidation.js +260 -0
  123. package/dist/lib/task/ledger-intents.js +37 -0
  124. package/dist/lib/task/local-artifact-finalization.js +299 -0
  125. package/dist/lib/task/orchestration.js +240 -144
  126. package/dist/lib/task/pr-delivery-fact.js +102 -101
  127. package/dist/lib/task/qualification-audit.js +490 -0
  128. package/dist/lib/task/qualification-confirmation.js +109 -0
  129. package/dist/lib/task/qualification-intents.js +148 -0
  130. package/dist/lib/task/review-artifacts.js +22 -23
  131. package/dist/lib/task/review-finalization.js +121 -4
  132. package/dist/lib/task/review-fingerprint.js +24 -2
  133. package/dist/lib/task/rework-intent.js +138 -0
  134. package/dist/lib/task/sections.js +6 -4
  135. package/dist/lib/task/skill-scope.js +23 -0
  136. package/dist/lib/task/task-execution-lock.js +27 -17
  137. package/dist/lib/task/verification-config.js +11 -0
  138. package/dist/lib/task/verification-engine.js +93 -16
  139. package/dist/lib/task/verification.js +3 -3
  140. package/dist/lib/task/workflow-warning-intents.js +1 -1
  141. package/dist/lib/task/write.js +11 -0
  142. package/dist/lib/version.js +11 -1
  143. package/dist/lifecycle-build-manifest.json +3 -3
  144. package/lib/agent-client.ts +9 -0
  145. package/lib/agent-clients/next-steps.ts +6 -1
  146. package/lib/decide.ts +3 -3
  147. package/lib/init.ts +1 -1
  148. package/lib/internal/agent-client.ts +4 -2
  149. package/lib/internal/cli-route-inventory.ts +140 -0
  150. package/lib/internal/codex-lifecycle.ts +8 -6
  151. package/lib/internal/codex-sandbox-controller.ts +7 -2
  152. package/lib/internal/git-workflow.ts +9 -6
  153. package/lib/internal/platform-checks.ts +14 -6
  154. package/lib/internal/platform-comment.ts +20 -12
  155. package/lib/internal/platform-context.ts +5 -3
  156. package/lib/internal/platform-issue.ts +17 -11
  157. package/lib/internal/platform-metadata.ts +70 -0
  158. package/lib/internal/platform-pr-review.ts +23 -12
  159. package/lib/internal/platform-pr.ts +65 -27
  160. package/lib/internal/platform-release-notes.ts +8 -6
  161. package/lib/internal/platform-security.ts +83 -0
  162. package/lib/internal/pr-review-grade.ts +20 -11
  163. package/lib/internal/release-workflow.ts +21 -9
  164. package/lib/internal/sandbox-control.ts +44 -6
  165. package/lib/internal/task-activity.ts +15 -7
  166. package/lib/internal/task-artifact.ts +204 -22
  167. package/lib/internal/task-context.ts +3 -1
  168. package/lib/internal/task-create.ts +78 -17
  169. package/lib/internal/task-delivery.ts +3 -1
  170. package/lib/internal/task-event.ts +9 -4
  171. package/lib/internal/task-finalization.ts +6 -4
  172. package/lib/internal/task-invalidation.ts +41 -0
  173. package/lib/internal/task-ledger.ts +21 -8
  174. package/lib/internal/task-lifecycle.ts +5 -3
  175. package/lib/internal/task-operation-registry.ts +482 -0
  176. package/lib/internal/task-orchestration.ts +12 -0
  177. package/lib/internal/task-override.ts +15 -9
  178. package/lib/internal/task-qualification.ts +61 -0
  179. package/lib/internal/task-review.ts +6 -4
  180. package/lib/internal/task-short-id.ts +10 -1
  181. package/lib/internal/task-snapshot.ts +3 -1
  182. package/lib/internal/task-validate.ts +6 -2
  183. package/lib/internal/task-verify.ts +10 -7
  184. package/lib/internal/task-warning.ts +10 -4
  185. package/lib/platform/adapters.ts +3 -2
  186. package/lib/platform/change-request-git-evidence.ts +36 -9
  187. package/lib/platform/comment-safety.ts +250 -0
  188. package/lib/platform/completion-backfill.ts +9 -8
  189. package/lib/platform/context.ts +97 -134
  190. package/lib/platform/github-client.ts +2 -2
  191. package/lib/platform/github-provider.ts +1132 -0
  192. package/lib/platform/in-label-sync.ts +208 -0
  193. package/lib/platform/issue-comments.ts +255 -80
  194. package/lib/platform/issues.ts +344 -174
  195. package/lib/platform/metadata-labels.ts +5 -2
  196. package/lib/platform/none-provider.ts +35 -0
  197. package/lib/platform/pr-change-report.ts +639 -0
  198. package/lib/platform/pr-checks.ts +126 -106
  199. package/lib/platform/pr-review.ts +88 -85
  200. package/lib/platform/pr-summary.ts +581 -53
  201. package/lib/platform/provider-bridge.ts +82 -0
  202. package/lib/platform/provider-contract.ts +611 -0
  203. package/lib/platform/provider-loader.ts +307 -0
  204. package/lib/platform/provider-validation.ts +611 -0
  205. package/lib/platform/pull-request-metadata.ts +10 -7
  206. package/lib/platform/pull-requests.ts +781 -550
  207. package/lib/platform/release-notes.ts +76 -59
  208. package/lib/platform/releases.ts +77 -43
  209. package/lib/platform/repository-metadata.ts +453 -0
  210. package/lib/platform/resource-identity.ts +121 -0
  211. package/lib/platform/security-alerts.ts +191 -0
  212. package/lib/platform/task-identities.ts +40 -0
  213. package/lib/platform/types.ts +4 -2
  214. package/lib/platform/verification-required.ts +11 -13
  215. package/lib/platform/verification-sync.ts +128 -304
  216. package/lib/pr-review/evidence-grading.ts +25 -8
  217. package/lib/process-data/index.ts +7 -0
  218. package/lib/run/index.ts +62 -22
  219. package/lib/sandbox/commands/control-status.ts +26 -0
  220. package/lib/sandbox/commands/create.ts +46 -6
  221. package/lib/sandbox/commands/ls.ts +7 -0
  222. package/lib/sandbox/commands/rm.ts +1000 -87
  223. package/lib/sandbox/commands/show.ts +16 -0
  224. package/lib/sandbox/control/client.ts +203 -19
  225. package/lib/sandbox/control/completed-reentry.ts +98 -0
  226. package/lib/sandbox/control/container-identity.ts +27 -43
  227. package/lib/sandbox/control/executor.ts +180 -15
  228. package/lib/sandbox/control/lifecycle.ts +629 -17
  229. package/lib/sandbox/control/native-file-lock.ts +209 -0
  230. package/lib/sandbox/control/protocol.ts +118 -2
  231. package/lib/sandbox/control/server.ts +724 -34
  232. package/lib/sandbox/control/state.ts +257 -4
  233. package/lib/sandbox/control/task-view.ts +240 -0
  234. package/lib/sandbox/engines/authority.ts +365 -0
  235. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  236. package/lib/sandbox/index.ts +7 -0
  237. package/lib/sandbox/recovery.ts +21 -1
  238. package/lib/sandbox/workspace-view.ts +9 -3
  239. package/lib/sandbox/worktree-safety.ts +4 -1
  240. package/lib/server/index.ts +10 -0
  241. package/lib/server/protocol.ts +36 -1
  242. package/lib/task/activity-log.ts +8 -0
  243. package/lib/task/artifact-lifecycle.ts +70 -22
  244. package/lib/task/artifact-operations.ts +626 -0
  245. package/lib/task/artifact-repair-intent.ts +71 -0
  246. package/lib/task/artifact-schema.ts +192 -0
  247. package/lib/task/capabilities.ts +331 -0
  248. package/lib/task/commands/status.ts +38 -0
  249. package/lib/task/commit-operation.ts +12 -1
  250. package/lib/task/control-authority.ts +22 -9
  251. package/lib/task/create-service.ts +139 -14
  252. package/lib/task/create.ts +27 -0
  253. package/lib/task/current-contract.ts +6 -1
  254. package/lib/task/decision-details.ts +6 -2
  255. package/lib/task/events.ts +392 -16
  256. package/lib/task/finalization.ts +50 -14
  257. package/lib/task/human-override.ts +70 -46
  258. package/lib/task/index.ts +16 -3
  259. package/lib/task/invalidation-command.ts +86 -0
  260. package/lib/task/invalidation.ts +332 -0
  261. package/lib/task/ledger-intents.ts +35 -1
  262. package/lib/task/local-artifact-finalization.ts +432 -0
  263. package/lib/task/orchestration.ts +223 -133
  264. package/lib/task/pr-delivery-fact.ts +131 -148
  265. package/lib/task/qualification-audit.ts +599 -0
  266. package/lib/task/qualification-confirmation.ts +117 -0
  267. package/lib/task/qualification-intents.ts +150 -0
  268. package/lib/task/review-artifacts.ts +21 -22
  269. package/lib/task/review-finalization.ts +133 -4
  270. package/lib/task/review-fingerprint.ts +23 -2
  271. package/lib/task/rework-intent.ts +165 -0
  272. package/lib/task/sections.ts +6 -4
  273. package/lib/task/skill-scope.ts +24 -0
  274. package/lib/task/task-execution-lock.ts +23 -14
  275. package/lib/task/verification-config.ts +12 -0
  276. package/lib/task/verification-engine.ts +103 -17
  277. package/lib/task/verification.ts +4 -4
  278. package/lib/task/workflow-warning-intents.ts +1 -1
  279. package/lib/task/write.ts +19 -0
  280. package/lib/update.ts +1 -1
  281. package/lib/version.ts +15 -1
  282. package/package.json +12 -1
  283. package/templates/.agents/rules/README.en.md +3 -1
  284. package/templates/.agents/rules/README.zh-CN.md +3 -1
  285. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  286. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  287. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  288. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  289. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  290. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  291. package/templates/.agents/rules/issue-sync.en.md +2 -0
  292. package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
  293. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  294. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  295. package/templates/.agents/rules/local-artifact-repair.en.md +13 -2
  296. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -2
  297. package/templates/.agents/rules/pr-sync.en.md +10 -4
  298. package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
  299. package/templates/.agents/rules/security-alerts.en.md +8 -8
  300. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  301. package/templates/.agents/rules/task-short-id.en.md +1 -4
  302. package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
  303. package/templates/.agents/skills/analyze-task/SKILL.en.md +28 -5
  304. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +29 -5
  305. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  306. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  307. package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
  308. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
  309. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  310. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  311. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  312. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  313. package/templates/.agents/skills/code-task/SKILL.en.md +41 -9
  314. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +43 -11
  315. package/templates/.agents/skills/code-task/config/verify.en.json +6 -14
  316. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +6 -14
  317. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  318. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  319. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  320. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  321. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  322. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  323. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  324. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  325. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
  326. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
  327. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
  328. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
  329. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +10 -4
  330. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +10 -4
  331. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  332. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  333. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
  334. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
  335. package/templates/.agents/skills/complete-task/SKILL.en.md +26 -21
  336. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +25 -21
  337. package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
  338. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
  339. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  340. package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
  341. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
  342. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
  343. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
  344. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  345. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  346. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
  347. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  348. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  349. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  350. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  351. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  352. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  353. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  354. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  355. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  356. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  357. package/templates/.agents/skills/plan-task/SKILL.en.md +27 -4
  358. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +26 -4
  359. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  360. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  361. package/templates/.agents/skills/review-analysis/SKILL.en.md +17 -4
  362. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +17 -4
  363. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  364. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  365. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  366. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  367. package/templates/.agents/skills/review-code/SKILL.en.md +18 -5
  368. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +18 -5
  369. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  370. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  371. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  372. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  373. package/templates/.agents/skills/review-plan/SKILL.en.md +17 -4
  374. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +17 -4
  375. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  376. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  377. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  378. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  379. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  380. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  381. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  382. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  383. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +9 -3
  384. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +9 -3
  385. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
  386. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
  387. package/templates/.agents/skills/test/SKILL.en.md +13 -2
  388. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
  389. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  390. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  391. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  392. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
  393. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
  394. package/templates/.agents/templates/task.en.md +24 -1
  395. package/templates/.agents/templates/task.zh-CN.md +24 -1
  396. package/templates/.github/workflows/pr-label.yml +7 -26
  397. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  398. package/templates/.agents/scripts/security-alerts.sh +0 -31
  399. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  400. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  401. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  402. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -0,0 +1,490 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { parseTypedTaskFrontmatter } from './frontmatter.js';
3
+ import { parseTable } from './sections.js';
4
+ const TASK_CONSTRAINT_HEADINGS = ['约束', 'Constraints'];
5
+ const TASK_CANDIDATE_HEADINGS = ['候选与否决方案', 'Candidate and Rejected Options', 'Candidates and Rejected Options', 'Candidates and Rejected Alternatives'];
6
+ const AUDIT_HEADINGS = ['资格审计', '资格审计复核', 'Qualification Audit', 'Qualification Audit Review'];
7
+ const CONFIRMATION_HEADINGS = ['资格确认记录', 'Qualification Confirmations'];
8
+ const AUDIT_SUBSECTIONS = ['约束依赖', '候选资格', '分类结果', '上游关系', '依赖快照'];
9
+ const AUDIT_SUBSECTION_HEADINGS = {
10
+ '约束依赖': ['约束依赖', 'Constraint Dependencies'],
11
+ '候选资格': ['候选资格', 'Candidate Qualification'],
12
+ '分类结果': ['分类结果', 'Classification Results'],
13
+ '上游关系': ['上游关系', 'Upstream Relations'],
14
+ '依赖快照': ['依赖快照', 'Dependency Snapshot']
15
+ };
16
+ const CONSTRAINT_COLUMNS = ['constraint_id', 'statement', 'status', 'authority', 'source', 'evidence', 'derived_from', 'approval_evidence'];
17
+ const CANDIDATE_COLUMNS = ['candidate_id', 'statement', 'status', 'constraint_ids', 'impact', 'evidence'];
18
+ const DEPENDENCY_COLUMNS = ['constraint_id', 'constraint_digest', 'role', 'evidence'];
19
+ const QUALIFICATION_COLUMNS = ['candidate_id', 'status', 'impact', 'constraint_ids', 'evidence'];
20
+ const CLASSIFICATION_COLUMNS = ['decision_id', 'classification', 'evidence'];
21
+ const RELATION_COLUMNS = ['upstream_family', 'upstream_artifact', 'upstream_round', 'upstream_sha256', 'relation'];
22
+ const SNAPSHOT_COLUMNS = ['task_input_digest', 'non_constraint_input_digest', 'upstream_artifact_digest'];
23
+ const CONFIRMATION_COLUMNS = ['qcr_id', 'constraint_id', 'actor', 'entrypoint', 'request_id', 'approved_digest', 'confirmed_at', 'rationale'];
24
+ const ARTIFACT_FAMILIES = ['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code'];
25
+ const RELATIONS = ['required-input', 'reviewed-input', 'review-context', 'approval-context'];
26
+ const CONSTRAINT_STATUSES = ['confirmed', 'derived', 'assumption', 'open', 'conflicted', 'superseded'];
27
+ const CANDIDATE_STATUSES = ['qualified', 'rejected', 'pending', 'needs-human-decision', 'confirmed', 'excluded'];
28
+ const CLASSIFICATIONS = ['deterministic', 'qualified', 'rejected', 'needs-human-decision', 'not-applicable'];
29
+ const ROLES = ['required', 'inherited', 'context', 'filter'];
30
+ function digest(value) {
31
+ return createHash('sha256').update(JSON.stringify(value), 'utf8').digest('hex');
32
+ }
33
+ function normalizeText(value) {
34
+ return value.normalize('NFC').replace(/\r\n?/g, '\n').trim();
35
+ }
36
+ function canonicalList(value) {
37
+ return normalizeText(value)
38
+ .split(/[,\s]+/u)
39
+ .map((item) => item.trim())
40
+ .filter(Boolean)
41
+ .sort();
42
+ }
43
+ function canonicalConstraintShape(row) {
44
+ return {
45
+ constraintId: row.constraintId,
46
+ statement: normalizeText(row.statement),
47
+ status: row.status,
48
+ authority: normalizeText(row.authority),
49
+ source: normalizeText(row.source),
50
+ evidence: normalizeText(row.evidence),
51
+ derivedFrom: [...row.derivedFrom].sort(),
52
+ approvalEvidence: normalizeText(row.approvalEvidence)
53
+ };
54
+ }
55
+ function constraintDigest(row) {
56
+ return digest(canonicalConstraintShape(row));
57
+ }
58
+ function canonicalTaskInput(qualification) {
59
+ return {
60
+ constraints: [...qualification.constraints].map(({ digest: _digest, ...row }) => canonicalConstraintShape(row)).sort((a, b) => String(a.constraintId).localeCompare(String(b.constraintId))),
61
+ candidates: [...qualification.candidates].map((row) => ({
62
+ candidateId: row.candidateId,
63
+ statement: normalizeText(row.statement),
64
+ status: row.status,
65
+ constraintIds: [...row.constraintIds].sort(),
66
+ impact: normalizeText(row.impact),
67
+ evidence: normalizeText(row.evidence)
68
+ })).sort((a, b) => a.candidateId.localeCompare(b.candidateId))
69
+ };
70
+ }
71
+ function taskInputDigest(qualification) {
72
+ return digest(canonicalTaskInput(qualification));
73
+ }
74
+ function sectionBody(content, aliases, level = 2) {
75
+ const heading = aliases.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
76
+ const marker = '#'.repeat(level);
77
+ const match = new RegExp(`^${marker}\\s+(${heading})\\s*$`, 'm').exec(content);
78
+ if (!match)
79
+ return null;
80
+ const rest = content.slice((match.index ?? 0) + match[0].length);
81
+ const end = rest.search(new RegExp(`^#{2,${level}}\\s+`, 'm'));
82
+ return rest.slice(0, end < 0 ? rest.length : end);
83
+ }
84
+ function stripSection(content, aliases) {
85
+ const body = sectionBody(content, aliases);
86
+ if (body === null)
87
+ return normalizeText(content);
88
+ const heading = aliases.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
89
+ return normalizeText(content.replace(new RegExp(`^##\\s+(?:${heading})\\s*$[\\s\\S]*?(?=^##\\s+|(?![\\s\\S]))`, 'm'), ''));
90
+ }
91
+ function stripFrontmatter(content) {
92
+ const opening = /^---(?:\r?\n)/.exec(content);
93
+ if (!opening)
94
+ return content;
95
+ const closing = /^---\r?$/gm;
96
+ closing.lastIndex = opening[0].length;
97
+ const match = closing.exec(content);
98
+ return match ? content.slice(match.index + match[0].length) : content;
99
+ }
100
+ function nonConstraintInputDigest(content) {
101
+ let projection = stripFrontmatter(content);
102
+ projection = stripTaskInputSection(projection, TASK_CONSTRAINT_HEADINGS);
103
+ projection = stripTaskInputSection(projection, TASK_CANDIDATE_HEADINGS);
104
+ projection = [
105
+ ['上下文', 'Context'],
106
+ ['需求', 'Requirements'],
107
+ ['分析', 'Analysis'],
108
+ ['设计', 'Design'],
109
+ ['实现备注', 'Implementation Notes'],
110
+ ['审查反馈', 'Review Feedback'],
111
+ ['审查分歧账本', 'Review Disagreement Ledger'],
112
+ ['人工裁决', 'Human Rulings', 'Decision Records'],
113
+ ['活动日志', 'Activity Log'],
114
+ ['产物生命周期收据', 'Artifact Lifecycle Receipts'],
115
+ ['产物失效记录', 'Artifact Invalidation'],
116
+ ['账本', 'Ledger'],
117
+ ['实现输入', 'Implementation Inputs'],
118
+ ['工作流告警', 'Warnings', 'Workflow Warnings'],
119
+ ['返工意图', 'Rework Intents'],
120
+ CONFIRMATION_HEADINGS,
121
+ ['完成检查清单', 'Completion Checklist']
122
+ ].reduce((value, aliases) => stripSection(value, aliases), projection);
123
+ return digest(projection);
124
+ }
125
+ function taskInputSection(content, aliases) {
126
+ return sectionBody(content, aliases, 3) ?? sectionBody(content, aliases, 2);
127
+ }
128
+ function stripTaskInputSection(content, aliases) {
129
+ let result = stripSectionAtLevel(content, aliases, 3);
130
+ return stripSectionAtLevel(result, aliases, 2);
131
+ }
132
+ function stripSectionAtLevel(content, aliases, level) {
133
+ const body = sectionBody(content, aliases, level);
134
+ if (body === null)
135
+ return normalizeText(content);
136
+ const heading = aliases.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
137
+ const marker = '#'.repeat(level);
138
+ return normalizeText(content.replace(new RegExp(`^${marker}\\s+(?:${heading})\\s*$[\\s\\S]*?(?=^#{2,${level}}\\s+|(?![\\s\\S]))`, 'm'), ''));
139
+ }
140
+ function parseTaskQualification(content) {
141
+ const constraintBody = taskInputSection(content, TASK_CONSTRAINT_HEADINGS);
142
+ const candidateBody = taskInputSection(content, TASK_CANDIDATE_HEADINGS);
143
+ const hasConstraintHeading = constraintBody !== null;
144
+ const hasCandidateHeading = candidateBody !== null;
145
+ if ((!hasConstraintHeading && !hasCandidateHeading)
146
+ || (hasConstraintHeading && hasCandidateHeading
147
+ && !/^\s*\|/m.test(constraintBody) && !/^\s*\|/m.test(candidateBody))) {
148
+ return { ok: true, qualification: { present: false, constraints: [], candidates: [], constraintDigest: digest([]), taskInputDigest: digest({ constraints: [], candidates: [] }), nonConstraintInputDigest: nonConstraintInputDigest(content) } };
149
+ }
150
+ if (!hasConstraintHeading || !hasCandidateHeading) {
151
+ const hasCanonicalTable = new RegExp(`\\|\\s*${CONSTRAINT_COLUMNS.join('\\s*\\|\\s*')}\\s*\\|`).test(content)
152
+ || new RegExp(`\\|\\s*${CANDIDATE_COLUMNS.join('\\s*\\|\\s*')}\\s*\\|`).test(content);
153
+ if (hasCanonicalTable)
154
+ return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_INVALID', message: 'task qualification requires both constraints and candidates sections' };
155
+ return { ok: true, qualification: { present: false, constraints: [], candidates: [], constraintDigest: digest([]), taskInputDigest: digest({ constraints: [], candidates: [] }), nonConstraintInputDigest: nonConstraintInputDigest(content) } };
156
+ }
157
+ try {
158
+ const constraintTable = constraintBody === null ? null : parseTable(`## 约束\n${constraintBody}`, { sectionAliases: ['约束'], columns: CONSTRAINT_COLUMNS });
159
+ const candidateTable = candidateBody === null ? null : parseTable(`## 候选与否决方案\n${candidateBody}`, { sectionAliases: ['候选与否决方案'], columns: CANDIDATE_COLUMNS });
160
+ if (!constraintTable || !candidateTable)
161
+ return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_INVALID', message: 'task qualification requires canonical constraints and candidates tables' };
162
+ const constraints = constraintTable.rows.map(({ values }) => {
163
+ const row = {
164
+ constraintId: values.constraint_id ?? '', statement: values.statement ?? '', status: values.status,
165
+ authority: values.authority ?? '', source: values.source ?? '', evidence: values.evidence ?? '',
166
+ derivedFrom: canonicalList(values.derived_from ?? ''), approvalEvidence: values.approval_evidence ?? ''
167
+ };
168
+ validateConstraint(row);
169
+ return { ...row, digest: constraintDigest(row) };
170
+ });
171
+ const ids = new Set(constraints.map((row) => row.constraintId));
172
+ for (const row of constraints)
173
+ for (const parent of row.derivedFrom)
174
+ if (!ids.has(parent))
175
+ throw new Error(`constraint '${row.constraintId}' references unknown derived constraint '${parent}'`);
176
+ const candidates = candidateTable.rows.map(({ values }) => {
177
+ const row = {
178
+ candidateId: values.candidate_id ?? '', statement: values.statement ?? '', status: values.status,
179
+ constraintIds: canonicalList(values.constraint_ids ?? ''), impact: values.impact ?? '', evidence: values.evidence ?? ''
180
+ };
181
+ validateCandidate(row);
182
+ for (const id of row.constraintIds)
183
+ if (!ids.has(id))
184
+ throw new Error(`candidate '${row.candidateId}' references unknown constraint '${id}'`);
185
+ return row;
186
+ });
187
+ const qualification = {
188
+ present: true, constraints, candidates,
189
+ constraintDigest: digest(constraints.map((row) => canonicalConstraintShape(row)).sort((a, b) => String(a.constraintId).localeCompare(String(b.constraintId)))),
190
+ taskInputDigest: taskInputDigest({ constraints, candidates }),
191
+ nonConstraintInputDigest: nonConstraintInputDigest(content)
192
+ };
193
+ return { ok: true, qualification };
194
+ }
195
+ catch (error) {
196
+ return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_INVALID', message: error instanceof Error ? error.message : String(error) };
197
+ }
198
+ }
199
+ function validateConstraint(row) {
200
+ if (!/^C-[1-9]\d*$/.test(row.constraintId))
201
+ throw new Error(`constraint id '${row.constraintId}' is invalid`);
202
+ if (!normalizeText(row.statement))
203
+ throw new Error(`constraint '${row.constraintId}' statement is required`);
204
+ if (!CONSTRAINT_STATUSES.includes(row.status))
205
+ throw new Error(`constraint '${row.constraintId}' status is invalid`);
206
+ if (!normalizeText(row.authority) || !normalizeText(row.source) || !normalizeText(row.evidence))
207
+ throw new Error(`constraint '${row.constraintId}' authority, source, and evidence are required`);
208
+ if (row.status === 'confirmed' && !normalizeText(row.approvalEvidence))
209
+ throw new Error(`confirmed constraint '${row.constraintId}' requires approval_evidence`);
210
+ }
211
+ function validateCandidate(row) {
212
+ if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(row.candidateId))
213
+ throw new Error(`candidate id '${row.candidateId}' is invalid`);
214
+ if ('statement' in row && !normalizeText(row.statement ?? ''))
215
+ throw new Error(`candidate '${row.candidateId}' statement is required`);
216
+ if (!CANDIDATE_STATUSES.includes(row.status))
217
+ throw new Error(`candidate '${row.candidateId}' status is invalid`);
218
+ if (!normalizeText(row.impact) || !normalizeText(row.evidence))
219
+ throw new Error(`candidate '${row.candidateId}' impact and evidence are required`);
220
+ }
221
+ function parseAuditTable(content, heading, columns) {
222
+ const body = sectionBody(content, AUDIT_HEADINGS);
223
+ if (body === null)
224
+ return null;
225
+ const subsection = sectionBody(body, AUDIT_SUBSECTION_HEADINGS[heading], 3);
226
+ if (subsection === null)
227
+ return null;
228
+ const table = parseTable(`## Qualification Audit Body\n${subsection}`, { sectionAliases: ['Qualification Audit Body'], columns });
229
+ return table?.rows.map((row) => ({ ...row.values })) ?? null;
230
+ }
231
+ function parseArtifactName(value) {
232
+ const match = /^(analysis|review-analysis|plan|review-plan|code|review-code)(?:-r([2-9]|[1-9]\d+))?\.md$/.exec(value);
233
+ if (!match)
234
+ return null;
235
+ return { family: match[1], round: match[2] ? Number(match[2]) : 1 };
236
+ }
237
+ function upstreamArtifactDigest(rows) {
238
+ return digest([...rows].map((row) => ({ ...row })).sort((a, b) => `${a.upstreamFamily}/${a.upstreamArtifact}/${a.relation}`.localeCompare(`${b.upstreamFamily}/${b.upstreamArtifact}/${b.relation}`)));
239
+ }
240
+ function validateUpstreamRelation(row) {
241
+ const identity = parseArtifactName(row.upstreamArtifact);
242
+ if (!ARTIFACT_FAMILIES.includes(row.upstreamFamily) || !identity || identity.family !== row.upstreamFamily
243
+ || identity.round !== row.upstreamRound || !Number.isSafeInteger(row.upstreamRound)
244
+ || !/^[a-f0-9]{64}$/i.test(row.upstreamSha256) || !RELATIONS.includes(row.relation)) {
245
+ throw new Error(`invalid qualification upstream relation '${row.upstreamArtifact}'`);
246
+ }
247
+ }
248
+ function expectedQualificationRelations(taskContent, family) {
249
+ let frontmatter;
250
+ try {
251
+ frontmatter = parseTypedTaskFrontmatter(taskContent);
252
+ }
253
+ catch (error) {
254
+ return { ok: false, code: 'QUALIFICATION_STARTED_INPUT_INVALID', message: error instanceof Error ? error.message : String(error) };
255
+ }
256
+ const encoded = frontmatter.qualification_input_relations;
257
+ if (typeof encoded === 'string' && encoded) {
258
+ try {
259
+ const parsed = JSON.parse(encoded);
260
+ if (!Array.isArray(parsed))
261
+ throw new Error('qualification_input_relations must be an array');
262
+ const relations = parsed.map((value) => {
263
+ if (!value || typeof value !== 'object' || Array.isArray(value))
264
+ throw new Error('qualification input relation must be an object');
265
+ const row = value;
266
+ const relation = {
267
+ upstreamFamily: row.upstreamFamily,
268
+ upstreamArtifact: String(row.upstreamArtifact ?? ''),
269
+ upstreamRound: Number(row.upstreamRound),
270
+ upstreamSha256: String(row.upstreamSha256 ?? ''),
271
+ relation: row.relation
272
+ };
273
+ validateUpstreamRelation(relation);
274
+ return relation;
275
+ });
276
+ return { ok: true, relations };
277
+ }
278
+ catch (error) {
279
+ return { ok: false, code: 'QUALIFICATION_STARTED_INPUT_INVALID', message: error instanceof Error ? error.message : String(error) };
280
+ }
281
+ }
282
+ const inputName = family === 'code' ? frontmatter.code_input_artifact : family.startsWith('review-') ? frontmatter.review_input_artifact : '';
283
+ const inputSha256 = family === 'code' ? frontmatter.code_input_sha256 : family.startsWith('review-') ? frontmatter.review_input_sha256 : '';
284
+ if (typeof inputName !== 'string' || !inputName || typeof inputSha256 !== 'string' || !inputSha256)
285
+ return { ok: true, relations: undefined };
286
+ const identity = parseArtifactName(inputName);
287
+ if (!identity)
288
+ return { ok: false, code: 'QUALIFICATION_STARTED_INPUT_INVALID', message: `started input '${inputName}' is not canonical` };
289
+ const relation = {
290
+ upstreamFamily: identity.family, upstreamArtifact: inputName, upstreamRound: identity.round,
291
+ upstreamSha256: inputSha256, relation: family === 'code' ? 'required-input' : 'reviewed-input'
292
+ };
293
+ try {
294
+ validateUpstreamRelation(relation);
295
+ }
296
+ catch (error) {
297
+ return { ok: false, code: 'QUALIFICATION_STARTED_INPUT_INVALID', message: error instanceof Error ? error.message : String(error) };
298
+ }
299
+ return { ok: true, relations: [relation] };
300
+ }
301
+ function parseQualificationAudit(content) {
302
+ if (sectionBody(content, AUDIT_HEADINGS) === null)
303
+ return { ok: true, audit: { present: false, constraintDependencies: [], candidateQualifications: [], classifications: [], upstreamRelations: [], snapshot: null } };
304
+ try {
305
+ const dependencies = parseAuditTable(content, '约束依赖', DEPENDENCY_COLUMNS);
306
+ const qualifications = parseAuditTable(content, '候选资格', QUALIFICATION_COLUMNS);
307
+ const classifications = parseAuditTable(content, '分类结果', CLASSIFICATION_COLUMNS);
308
+ const relations = parseAuditTable(content, '上游关系', RELATION_COLUMNS);
309
+ const snapshots = parseAuditTable(content, '依赖快照', SNAPSHOT_COLUMNS);
310
+ if (!dependencies || !qualifications || !classifications || !relations || !snapshots || snapshots.length !== 1) {
311
+ throw new Error('qualification audit requires all five canonical tables and exactly one dependency snapshot');
312
+ }
313
+ const constraintDependencies = dependencies.map((row) => {
314
+ if (!/^C-[1-9]\d*$/.test(row.constraint_id ?? '') || !/^[a-f0-9]{64}$/i.test(row.constraint_digest ?? '') || !ROLES.includes(row.role) || !row.evidence?.trim())
315
+ throw new Error(`invalid qualification constraint dependency '${row.constraint_id ?? ''}'`);
316
+ return { constraintId: row.constraint_id, constraintDigest: row.constraint_digest, role: row.role, evidence: row.evidence };
317
+ });
318
+ const candidateQualifications = qualifications.map((row) => {
319
+ const parsed = { candidateId: row.candidate_id ?? '', status: row.status, impact: row.impact ?? '', constraintIds: canonicalList(row.constraint_ids ?? ''), evidence: row.evidence ?? '' };
320
+ validateCandidate(parsed);
321
+ return parsed;
322
+ });
323
+ const classificationRows = classifications.map((row) => {
324
+ if (!row.decision_id?.trim() || !CLASSIFICATIONS.includes(row.classification) || !row.evidence?.trim())
325
+ throw new Error(`invalid qualification classification '${row.decision_id ?? ''}'`);
326
+ return { decisionId: row.decision_id, classification: row.classification, evidence: row.evidence };
327
+ });
328
+ const upstreamRelations = relations.map((row) => {
329
+ const relation = { upstreamFamily: row.upstream_family, upstreamArtifact: row.upstream_artifact ?? '', upstreamRound: Number(row.upstream_round), upstreamSha256: row.upstream_sha256 ?? '', relation: row.relation };
330
+ validateUpstreamRelation(relation);
331
+ return relation;
332
+ });
333
+ const snapshotRow = snapshots[0];
334
+ if (!/^[a-f0-9]{64}$/i.test(snapshotRow.task_input_digest ?? '') || !/^[a-f0-9]{64}$/i.test(snapshotRow.non_constraint_input_digest ?? '') || !/^[a-f0-9]{64}$/i.test(snapshotRow.upstream_artifact_digest ?? ''))
335
+ throw new Error('qualification dependency snapshot has invalid digest');
336
+ return {
337
+ ok: true,
338
+ audit: {
339
+ present: true, constraintDependencies, candidateQualifications,
340
+ classifications: classificationRows, upstreamRelations,
341
+ snapshot: { taskInputDigest: snapshotRow.task_input_digest, nonConstraintInputDigest: snapshotRow.non_constraint_input_digest, upstreamArtifactDigest: snapshotRow.upstream_artifact_digest }
342
+ }
343
+ };
344
+ }
345
+ catch (error) {
346
+ return { ok: false, code: 'QUALIFICATION_AUDIT_INVALID', message: error instanceof Error ? error.message : String(error) };
347
+ }
348
+ }
349
+ function parseQualificationConfirmations(content) {
350
+ const body = sectionBody(content, CONFIRMATION_HEADINGS);
351
+ if (body === null)
352
+ return { ok: true, confirmations: [] };
353
+ try {
354
+ const table = parseTable(content, { sectionAliases: CONFIRMATION_HEADINGS, columns: CONFIRMATION_COLUMNS });
355
+ if (!table)
356
+ throw new Error('qualification confirmation section requires a canonical table');
357
+ const seen = new Set();
358
+ const confirmations = table.rows.map(({ values }) => {
359
+ const row = {
360
+ qcrId: values.qcr_id ?? '', constraintId: values.constraint_id ?? '', actor: values.actor,
361
+ entrypoint: values.entrypoint, requestId: values.request_id ?? '', approvedDigest: values.approved_digest ?? '',
362
+ confirmedAt: values.confirmed_at ?? '', rationale: values.rationale ?? ''
363
+ };
364
+ if (!/^QCR-[1-9]\d*$/.test(row.qcrId) || !/^C-[1-9]\d*$/.test(row.constraintId) || row.actor !== 'human-declared' || row.entrypoint !== 'task-qualification' || !row.requestId || !/^[a-f0-9]{64}$/.test(row.approvedDigest) || !row.confirmedAt || Number.isNaN(Date.parse(row.confirmedAt.replace(' ', 'T'))) || !row.rationale || /[\r\n]/.test(row.rationale))
365
+ throw new Error(`qualification confirmation '${row.qcrId}' is invalid`);
366
+ if (seen.has(row.qcrId))
367
+ throw new Error(`duplicate qualification confirmation '${row.qcrId}'`);
368
+ seen.add(row.qcrId);
369
+ return row;
370
+ });
371
+ return { ok: true, confirmations };
372
+ }
373
+ catch (error) {
374
+ return { ok: false, code: 'QUALIFICATION_CONFIRMATION_INVALID', message: error instanceof Error ? error.message : String(error) };
375
+ }
376
+ }
377
+ function validateQualificationAudit(taskContent, artifactContent, options = {}) {
378
+ const task = parseTaskQualification(taskContent);
379
+ if (!task.ok)
380
+ return task;
381
+ const audit = parseQualificationAudit(artifactContent);
382
+ if (!audit.ok)
383
+ return audit;
384
+ if (!task.qualification.present && !options.require)
385
+ return { ok: true, qualification: task.qualification, audit: audit.audit };
386
+ if (!task.qualification.present)
387
+ return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_MISSING', message: 'task qualification contract is missing' };
388
+ if (!audit.audit.present)
389
+ return { ok: false, code: 'QUALIFICATION_AUDIT_MISSING', message: 'artifact qualification audit is missing' };
390
+ const constraintMap = new Map(task.qualification.constraints.map((row) => [row.constraintId, row]));
391
+ const confirmations = parseQualificationConfirmations(taskContent);
392
+ if (!confirmations.ok)
393
+ return confirmations;
394
+ const confirmationIds = new Set();
395
+ for (const confirmation of confirmations.confirmations) {
396
+ if (confirmationIds.has(confirmation.qcrId))
397
+ return { ok: false, code: 'QUALIFICATION_CONFIRMATION_DUPLICATE', message: `qualification confirmation '${confirmation.qcrId}' is duplicated` };
398
+ confirmationIds.add(confirmation.qcrId);
399
+ if (!constraintMap.has(confirmation.constraintId))
400
+ return { ok: false, code: 'QUALIFICATION_CONSTRAINT_UNKNOWN', message: `qualification confirmation references unknown constraint '${confirmation.constraintId}'` };
401
+ }
402
+ const confirmationMap = new Map(confirmations.confirmations.map((row) => [row.qcrId, row]));
403
+ for (const constraint of task.qualification.constraints) {
404
+ if (constraint.status !== 'confirmed')
405
+ continue;
406
+ const confirmation = confirmationMap.get(constraint.approvalEvidence);
407
+ if (!confirmation || confirmation.constraintId !== constraint.constraintId)
408
+ return { ok: false, code: 'QUALIFICATION_CONFIRMATION_MISSING', message: `confirmed constraint '${constraint.constraintId}' has no matching QCR` };
409
+ if (confirmation.approvedDigest !== constraint.digest)
410
+ return { ok: false, code: 'QUALIFICATION_CONFIRMATION_DIGEST_MISMATCH', message: `qualification confirmation for '${constraint.constraintId}' is stale` };
411
+ }
412
+ for (const row of audit.audit.constraintDependencies) {
413
+ const constraint = constraintMap.get(row.constraintId);
414
+ if (!constraint)
415
+ return { ok: false, code: 'QUALIFICATION_CONSTRAINT_UNKNOWN', message: `qualification audit references unknown constraint '${row.constraintId}'` };
416
+ if (constraint.digest !== row.constraintDigest)
417
+ return { ok: false, code: 'QUALIFICATION_CONSTRAINT_DIGEST_MISMATCH', message: `qualification audit digest does not match '${row.constraintId}'` };
418
+ }
419
+ const candidateMap = new Map(task.qualification.candidates.map((row) => [row.candidateId, row]));
420
+ const candidateIds = new Set();
421
+ for (const row of audit.audit.candidateQualifications) {
422
+ if (candidateIds.has(row.candidateId))
423
+ return { ok: false, code: 'QUALIFICATION_CANDIDATE_MISMATCH', message: `qualification candidate '${row.candidateId}' is duplicated` };
424
+ candidateIds.add(row.candidateId);
425
+ const current = candidateMap.get(row.candidateId);
426
+ if (!current)
427
+ return { ok: false, code: 'QUALIFICATION_CANDIDATE_UNKNOWN', message: `qualification audit references unknown candidate '${row.candidateId}'` };
428
+ if (row.constraintIds.some((id) => !constraintMap.has(id)))
429
+ return { ok: false, code: 'QUALIFICATION_CONSTRAINT_UNKNOWN', message: `candidate '${row.candidateId}' references unknown qualification constraint` };
430
+ if (row.status !== current.status || row.impact !== current.impact || row.evidence !== current.evidence || row.constraintIds.join(',') !== current.constraintIds.join(',')) {
431
+ return { ok: false, code: 'QUALIFICATION_CANDIDATE_MISMATCH', message: `qualification candidate '${row.candidateId}' does not match task input` };
432
+ }
433
+ }
434
+ if (candidateIds.size !== candidateMap.size)
435
+ return { ok: false, code: 'QUALIFICATION_CANDIDATE_MISMATCH', message: 'qualification audit does not contain the complete task candidate set' };
436
+ const snapshot = audit.audit.snapshot;
437
+ if (snapshot.taskInputDigest !== task.qualification.taskInputDigest)
438
+ return { ok: false, code: 'QUALIFICATION_TASK_DIGEST_MISMATCH', message: 'qualification audit task input digest is stale' };
439
+ if (snapshot.nonConstraintInputDigest !== task.qualification.nonConstraintInputDigest)
440
+ return { ok: false, code: 'QUALIFICATION_NON_CONSTRAINT_DIGEST_MISMATCH', message: 'qualification audit non-constraint input digest is stale' };
441
+ if (snapshot.upstreamArtifactDigest !== upstreamArtifactDigest(audit.audit.upstreamRelations))
442
+ return { ok: false, code: 'QUALIFICATION_UPSTREAM_DIGEST_MISMATCH', message: 'qualification audit upstream digest does not match its relation rows' };
443
+ if (options.expectedUpstreamRelations && (options.expectedUpstreamRelations.length !== audit.audit.upstreamRelations.length
444
+ || upstreamArtifactDigest(options.expectedUpstreamRelations) !== upstreamArtifactDigest(audit.audit.upstreamRelations)))
445
+ return { ok: false, code: 'QUALIFICATION_UPSTREAM_RELATION_MISMATCH', message: 'qualification audit upstream relations do not match the started artifact inputs' };
446
+ if (options.family && options.artifact) {
447
+ const identity = parseArtifactName(options.artifact);
448
+ if (!identity || identity.family !== options.family)
449
+ return { ok: false, code: 'QUALIFICATION_ARTIFACT_IDENTITY_INVALID', message: `artifact '${options.artifact}' is not canonical for '${options.family}'` };
450
+ }
451
+ return { ok: true, qualification: task.qualification, audit: audit.audit };
452
+ }
453
+ function renderQualificationAudit(audit) {
454
+ const table = (columns, rows) => [
455
+ `| ${columns.join(' | ')} |`,
456
+ `| ${columns.map(() => '---').join(' | ')} |`,
457
+ ...rows.map((row) => `| ${row.map((value) => value.replace(/\\/g, '\\\\').replace(/\|/g, '\\|').replace(/[\r\n]/g, ' ')).join(' | ')} |`)
458
+ ].join('\n');
459
+ const list = (values) => values.join(',');
460
+ const snapshot = audit.snapshot ?? { taskInputDigest: '', nonConstraintInputDigest: '', upstreamArtifactDigest: upstreamArtifactDigest(audit.upstreamRelations) };
461
+ return [
462
+ '### 约束依赖', '', table(DEPENDENCY_COLUMNS, audit.constraintDependencies.map((row) => [row.constraintId, row.constraintDigest, row.role, row.evidence])), '',
463
+ '### 候选资格', '', table(QUALIFICATION_COLUMNS, audit.candidateQualifications.map((row) => [row.candidateId, row.status, row.impact, list(row.constraintIds), row.evidence])), '',
464
+ '### 分类结果', '', table(CLASSIFICATION_COLUMNS, audit.classifications.map((row) => [row.decisionId, row.classification, row.evidence])), '',
465
+ '### 上游关系', '', table(RELATION_COLUMNS, audit.upstreamRelations.map((row) => [row.upstreamFamily, row.upstreamArtifact, String(row.upstreamRound), row.upstreamSha256, row.relation])), '',
466
+ '### 依赖快照', '', table(SNAPSHOT_COLUMNS, [[snapshot.taskInputDigest, snapshot.nonConstraintInputDigest, snapshot.upstreamArtifactDigest]])
467
+ ].join('\n');
468
+ }
469
+ function buildQualificationAudit(taskContent, input = {}) {
470
+ const parsed = parseTaskQualification(taskContent);
471
+ if (!parsed.ok)
472
+ return parsed;
473
+ if (!parsed.qualification.present)
474
+ return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_MISSING', message: 'task qualification contract is missing' };
475
+ const ids = new Set(input.constraints ?? parsed.qualification.constraints.map((row) => row.constraintId));
476
+ const candidates = input.candidates ?? parsed.qualification.candidates.map((row) => row.candidateId);
477
+ const constraintDependencies = parsed.qualification.constraints.filter((row) => ids.has(row.constraintId)).map((row) => ({ constraintId: row.constraintId, constraintDigest: row.digest, role: 'required', evidence: row.evidence }));
478
+ const candidateQualifications = parsed.qualification.candidates.filter((row) => candidates.includes(row.candidateId)).map((row) => ({ candidateId: row.candidateId, status: row.status, impact: row.impact, constraintIds: row.constraintIds, evidence: row.evidence }));
479
+ const upstreamRelations = [...(input.upstreamRelations ?? [])];
480
+ return {
481
+ ok: true,
482
+ audit: {
483
+ present: true, constraintDependencies, candidateQualifications,
484
+ classifications: input.classifications ?? [], upstreamRelations,
485
+ snapshot: { taskInputDigest: parsed.qualification.taskInputDigest, nonConstraintInputDigest: parsed.qualification.nonConstraintInputDigest, upstreamArtifactDigest: upstreamArtifactDigest(upstreamRelations) }
486
+ }
487
+ };
488
+ }
489
+ export { ARTIFACT_FAMILIES, AUDIT_HEADINGS, AUDIT_SUBSECTIONS, CONFIRMATION_COLUMNS, CONFIRMATION_HEADINGS, CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS, DEPENDENCY_COLUMNS, QUALIFICATION_COLUMNS, CLASSIFICATION_COLUMNS, RELATION_COLUMNS, SNAPSHOT_COLUMNS, constraintDigest, expectedQualificationRelations, buildQualificationAudit, nonConstraintInputDigest, parseQualificationAudit, parseQualificationConfirmations, parseTaskQualification, renderQualificationAudit, taskInputDigest, upstreamArtifactDigest, validateQualificationAudit };
490
+ //# sourceMappingURL=qualification-audit.js.map
@@ -0,0 +1,109 @@
1
+ import { createHash } from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import { appendActivityEntry, locateActivityLog } from './activity-log.js';
4
+ import { CONFIRMATION_COLUMNS, CONFIRMATION_HEADINGS, constraintDigest, parseQualificationConfirmations, parseTaskQualification } from './qualification-audit.js';
5
+ import { findSectionHeading } from './sections.js';
6
+ import { resolveTaskRef } from './resolve-ref.js';
7
+ import { captureTaskWriteMetadata, writeTask } from './write.js';
8
+ function failed(code, message, taskId = null) {
9
+ return { status: 'failed', changed: false, taskId, qcrId: null, requestId: null, operations: [], error: { code, message } };
10
+ }
11
+ function nextQcrId(content) {
12
+ let max = 0;
13
+ for (const match of content.matchAll(/^\|\s*(QCR-(\d+))\s*\|/gm))
14
+ max = Math.max(max, Number(match[2]));
15
+ return `QCR-${max + 1}`;
16
+ }
17
+ function requestIdFor(taskId, constraintId, expectedDigest, rationale, timestamp) {
18
+ const hash = createHash('sha256').update(JSON.stringify([taskId, constraintId, expectedDigest, rationale, timestamp])).digest('hex');
19
+ return `QCR-REQUEST-${hash.slice(0, 20)}`;
20
+ }
21
+ function renderConfirmations(rows) {
22
+ return [
23
+ `| ${CONFIRMATION_COLUMNS.join(' | ')} |`,
24
+ `| ${CONFIRMATION_COLUMNS.map(() => '---').join(' | ')} |`,
25
+ ...rows.map((row) => [row.qcrId, row.constraintId, row.actor, row.entrypoint, row.requestId, row.approvedDigest, row.confirmedAt, row.rationale]
26
+ .map((value) => value.replace(/\\/g, '\\\\').replace(/\|/g, '\\|').replace(/[\r\n]/g, ' '))
27
+ .join(' | ')).map((line) => `| ${line} |`)
28
+ ].join('\n');
29
+ }
30
+ function applyQualificationConfirmation(request, options = {}) {
31
+ if (!request || typeof request !== 'object' || !request.taskRef || !/^C-[1-9]\d*$/.test(request.constraintId) || !/^[a-f0-9]{64}$/i.test(request.expectedDigest) || !request.rationale?.trim() || /[\r\n]/.test(request.rationale)) {
32
+ return failed('QUALIFICATION_CONFIRMATION_INVALID', 'taskRef, constraintId, expectedDigest, and a single-line rationale are required');
33
+ }
34
+ const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
35
+ if (!resolved.ok)
36
+ return failed(resolved.code, resolved.message, resolved.taskId);
37
+ if (resolved.state !== 'active')
38
+ return failed('TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
39
+ let content;
40
+ try {
41
+ content = fs.readFileSync(resolved.taskMdPath, 'utf8');
42
+ }
43
+ catch (error) {
44
+ return failed('TASK_READ_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
45
+ }
46
+ const parsed = parseTaskQualification(content);
47
+ if (!parsed.ok)
48
+ return failed(parsed.code, parsed.message, resolved.taskId);
49
+ if (!parsed.qualification.present)
50
+ return failed('QUALIFICATION_TASK_CONTRACT_MISSING', 'task qualification contract is missing', resolved.taskId);
51
+ const constraint = parsed.qualification.constraints.find((row) => row.constraintId === request.constraintId);
52
+ if (!constraint)
53
+ return failed('QUALIFICATION_CONSTRAINT_UNKNOWN', `unknown constraint '${request.constraintId}'`, resolved.taskId);
54
+ const operation = request.operation ?? 'confirm';
55
+ const existing = parseQualificationConfirmations(content);
56
+ if (!existing.ok)
57
+ return failed(existing.code, existing.message, resolved.taskId);
58
+ const previous = existing.confirmations.find((row) => row.qcrId === constraint.approvalEvidence);
59
+ if (operation === 'confirm' && constraint.status === 'confirmed') {
60
+ if (previous?.constraintId === constraint.constraintId && previous.approvedDigest === constraint.digest && previous.rationale === request.rationale.trim()) {
61
+ return { status: 'no-op', changed: false, taskId: resolved.taskId, qcrId: previous.qcrId, requestId: previous.requestId, operations: [], error: null };
62
+ }
63
+ return failed('QUALIFICATION_CONFIRMATION_CONFLICT', `constraint '${request.constraintId}' is already confirmed with different evidence`, resolved.taskId);
64
+ }
65
+ const transitionStatus = operation === 'supersede' ? 'superseded' : 'open';
66
+ if (operation !== 'confirm' && constraint.status === transitionStatus && !constraint.approvalEvidence) {
67
+ return { status: 'no-op', changed: false, taskId: resolved.taskId, qcrId: null, requestId: null, operations: [], error: null };
68
+ }
69
+ if (operation !== 'confirm' && constraint.status !== 'confirmed')
70
+ return failed('QUALIFICATION_CONFIRMATION_REQUIRED', `constraint '${request.constraintId}' is not confirmed`, resolved.taskId);
71
+ if (parsed.qualification.constraintDigest !== request.expectedDigest)
72
+ return failed('QUALIFICATION_DIGEST_CONFLICT', 'constraint digest changed before human confirmation', resolved.taskId);
73
+ let metadata;
74
+ try {
75
+ metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
76
+ }
77
+ catch (error) {
78
+ return failed('METADATA_CAPTURE_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
79
+ }
80
+ const qcrId = operation === 'confirm' ? nextQcrId(content) : null;
81
+ const requestId = operation === 'confirm' ? requestIdFor(resolved.taskId, request.constraintId, request.expectedDigest, request.rationale.trim(), metadata.timestamp) : null;
82
+ const confirmedConstraint = qcrId ? {
83
+ constraintId: constraint.constraintId, statement: constraint.statement, status: 'confirmed',
84
+ authority: 'human-declared', source: constraint.source, evidence: constraint.evidence,
85
+ derivedFrom: constraint.derivedFrom, approvalEvidence: qcrId
86
+ } : null;
87
+ const confirmation = confirmedConstraint && qcrId && requestId ? {
88
+ qcrId, constraintId: request.constraintId, actor: 'human-declared', entrypoint: 'task-qualification',
89
+ requestId, approvedDigest: constraintDigest(confirmedConstraint), confirmedAt: metadata.timestamp, rationale: request.rationale.trim()
90
+ } : null;
91
+ const confirmations = confirmation ? [...existing.confirmations, confirmation] : existing.confirmations;
92
+ const activity = locateActivityLog(content);
93
+ if (!activity)
94
+ return failed('QUALIFICATION_ACTIVITY_MISSING', 'task has no unique Activity Log section', resolved.taskId);
95
+ const mutations = [
96
+ {
97
+ kind: 'table-row', action: 'upsert', sectionAliases: ['约束', 'Constraints'], columns: ['constraint_id', 'statement', 'status', 'authority', 'source', 'evidence', 'derived_from', 'approval_evidence'], keyColumn: 'constraint_id', key: constraint.constraintId,
98
+ values: { statement: constraint.statement, status: operation === 'confirm' ? 'confirmed' : transitionStatus, authority: operation === 'confirm' ? 'human-declared' : constraint.authority, source: constraint.source, evidence: constraint.evidence, derived_from: constraint.derivedFrom.join(','), approval_evidence: qcrId ?? '' }
99
+ },
100
+ ...(operation === 'confirm' ? [{ kind: 'section', aliases: CONFIRMATION_HEADINGS, heading: findSectionHeading(content, [...CONFIRMATION_HEADINGS]), body: renderConfirmations(confirmations) }] : []),
101
+ { kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: activity.heading, body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Qualification Confirmation', agent: 'human', note: operation === 'confirm' ? `${request.constraintId} confirmed → ${qcrId}` : `${request.constraintId} ${operation}d` }) }
102
+ ];
103
+ const result = writeTask({ taskRef: resolved.taskId, expectedState: 'active', mutations, dryRun: request.dryRun }, { ...options, taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state }, metadataProvider: () => metadata });
104
+ if (result.status === 'failed')
105
+ return failed(result.error.code, result.error.message, result.taskId);
106
+ return { status: result.status, changed: result.changed, taskId: result.taskId, qcrId, requestId, operations: result.operations, error: null };
107
+ }
108
+ export { applyQualificationConfirmation };
109
+ //# sourceMappingURL=qualification-confirmation.js.map