@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,599 @@
1
+ import { createHash } from 'node:crypto';
2
+
3
+ import { parseTypedTaskFrontmatter } from './frontmatter.ts';
4
+ import { parseTable } from './sections.ts';
5
+
6
+ const TASK_CONSTRAINT_HEADINGS = ['约束', 'Constraints'] as const;
7
+ const TASK_CANDIDATE_HEADINGS = ['候选与否决方案', 'Candidate and Rejected Options', 'Candidates and Rejected Options', 'Candidates and Rejected Alternatives'] as const;
8
+ const AUDIT_HEADINGS = ['资格审计', '资格审计复核', 'Qualification Audit', 'Qualification Audit Review'] as const;
9
+ const CONFIRMATION_HEADINGS = ['资格确认记录', 'Qualification Confirmations'] as const;
10
+ const AUDIT_SUBSECTIONS = ['约束依赖', '候选资格', '分类结果', '上游关系', '依赖快照'] as const;
11
+ const AUDIT_SUBSECTION_HEADINGS: Record<(typeof AUDIT_SUBSECTIONS)[number], readonly string[]> = {
12
+ '约束依赖': ['约束依赖', 'Constraint Dependencies'],
13
+ '候选资格': ['候选资格', 'Candidate Qualification'],
14
+ '分类结果': ['分类结果', 'Classification Results'],
15
+ '上游关系': ['上游关系', 'Upstream Relations'],
16
+ '依赖快照': ['依赖快照', 'Dependency Snapshot']
17
+ };
18
+
19
+ const CONSTRAINT_COLUMNS = ['constraint_id', 'statement', 'status', 'authority', 'source', 'evidence', 'derived_from', 'approval_evidence'] as const;
20
+ const CANDIDATE_COLUMNS = ['candidate_id', 'statement', 'status', 'constraint_ids', 'impact', 'evidence'] as const;
21
+ const DEPENDENCY_COLUMNS = ['constraint_id', 'constraint_digest', 'role', 'evidence'] as const;
22
+ const QUALIFICATION_COLUMNS = ['candidate_id', 'status', 'impact', 'constraint_ids', 'evidence'] as const;
23
+ const CLASSIFICATION_COLUMNS = ['decision_id', 'classification', 'evidence'] as const;
24
+ const RELATION_COLUMNS = ['upstream_family', 'upstream_artifact', 'upstream_round', 'upstream_sha256', 'relation'] as const;
25
+ const SNAPSHOT_COLUMNS = ['task_input_digest', 'non_constraint_input_digest', 'upstream_artifact_digest'] as const;
26
+ const CONFIRMATION_COLUMNS = ['qcr_id', 'constraint_id', 'actor', 'entrypoint', 'request_id', 'approved_digest', 'confirmed_at', 'rationale'] as const;
27
+
28
+ const ARTIFACT_FAMILIES = ['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code'] as const;
29
+ const RELATIONS = ['required-input', 'reviewed-input', 'review-context', 'approval-context'] as const;
30
+ const CONSTRAINT_STATUSES = ['confirmed', 'derived', 'assumption', 'open', 'conflicted', 'superseded'] as const;
31
+ const CANDIDATE_STATUSES = ['qualified', 'rejected', 'pending', 'needs-human-decision', 'confirmed', 'excluded'] as const;
32
+ const CLASSIFICATIONS = ['deterministic', 'qualified', 'rejected', 'needs-human-decision', 'not-applicable'] as const;
33
+ const ROLES = ['required', 'inherited', 'context', 'filter'] as const;
34
+
35
+ type ConstraintStatus = (typeof CONSTRAINT_STATUSES)[number];
36
+ type CandidateStatus = (typeof CANDIDATE_STATUSES)[number];
37
+ type QualificationClassification = (typeof CLASSIFICATIONS)[number];
38
+ type ArtifactFamily = (typeof ARTIFACT_FAMILIES)[number];
39
+ type QualificationRelation = (typeof RELATIONS)[number];
40
+ type QualificationRole = (typeof ROLES)[number];
41
+
42
+ type Constraint = {
43
+ constraintId: string;
44
+ statement: string;
45
+ status: ConstraintStatus;
46
+ authority: string;
47
+ source: string;
48
+ evidence: string;
49
+ derivedFrom: readonly string[];
50
+ approvalEvidence: string;
51
+ digest: string;
52
+ };
53
+ type Candidate = {
54
+ candidateId: string;
55
+ statement: string;
56
+ status: CandidateStatus;
57
+ constraintIds: readonly string[];
58
+ impact: string;
59
+ evidence: string;
60
+ };
61
+ type TaskQualification = {
62
+ present: boolean;
63
+ constraints: readonly Constraint[];
64
+ candidates: readonly Candidate[];
65
+ constraintDigest: string;
66
+ taskInputDigest: string;
67
+ nonConstraintInputDigest: string;
68
+ };
69
+ type ConstraintDependency = {
70
+ constraintId: string;
71
+ constraintDigest: string;
72
+ role: QualificationRole;
73
+ evidence: string;
74
+ };
75
+ type CandidateQualification = {
76
+ candidateId: string;
77
+ status: CandidateStatus;
78
+ impact: string;
79
+ constraintIds: readonly string[];
80
+ evidence: string;
81
+ };
82
+ type QualificationClassificationRow = {
83
+ decisionId: string;
84
+ classification: QualificationClassification;
85
+ evidence: string;
86
+ };
87
+ type UpstreamRelation = {
88
+ upstreamFamily: ArtifactFamily;
89
+ upstreamArtifact: string;
90
+ upstreamRound: number;
91
+ upstreamSha256: string;
92
+ relation: QualificationRelation;
93
+ };
94
+ type DependencySnapshot = {
95
+ taskInputDigest: string;
96
+ nonConstraintInputDigest: string;
97
+ upstreamArtifactDigest: string;
98
+ };
99
+ type QualificationAudit = {
100
+ present: boolean;
101
+ constraintDependencies: readonly ConstraintDependency[];
102
+ candidateQualifications: readonly CandidateQualification[];
103
+ classifications: readonly QualificationClassificationRow[];
104
+ upstreamRelations: readonly UpstreamRelation[];
105
+ snapshot: DependencySnapshot | null;
106
+ };
107
+ type QualificationConfirmation = {
108
+ qcrId: string;
109
+ constraintId: string;
110
+ actor: 'human-declared';
111
+ entrypoint: 'task-qualification';
112
+ requestId: string;
113
+ approvedDigest: string;
114
+ confirmedAt: string;
115
+ rationale: string;
116
+ };
117
+ type QualificationValidationError = { code: string; message: string };
118
+ type QualificationValidationResult =
119
+ | { ok: true; qualification: TaskQualification; audit: QualificationAudit }
120
+ | { ok: false; code: string; message: string };
121
+
122
+ function digest(value: unknown): string {
123
+ return createHash('sha256').update(JSON.stringify(value), 'utf8').digest('hex');
124
+ }
125
+
126
+ function normalizeText(value: string): string {
127
+ return value.normalize('NFC').replace(/\r\n?/g, '\n').trim();
128
+ }
129
+
130
+ function canonicalList(value: string): string[] {
131
+ return normalizeText(value)
132
+ .split(/[,\s]+/u)
133
+ .map((item) => item.trim())
134
+ .filter(Boolean)
135
+ .sort();
136
+ }
137
+
138
+ function canonicalConstraintShape(row: Omit<Constraint, 'digest'>): Record<string, unknown> {
139
+ return {
140
+ constraintId: row.constraintId,
141
+ statement: normalizeText(row.statement),
142
+ status: row.status,
143
+ authority: normalizeText(row.authority),
144
+ source: normalizeText(row.source),
145
+ evidence: normalizeText(row.evidence),
146
+ derivedFrom: [...row.derivedFrom].sort(),
147
+ approvalEvidence: normalizeText(row.approvalEvidence)
148
+ };
149
+ }
150
+
151
+ function constraintDigest(row: Omit<Constraint, 'digest'>): string {
152
+ return digest(canonicalConstraintShape(row));
153
+ }
154
+
155
+ function canonicalTaskInput(qualification: Pick<TaskQualification, 'constraints' | 'candidates'>): Record<string, unknown> {
156
+ return {
157
+ constraints: [...qualification.constraints].map(({ digest: _digest, ...row }) => canonicalConstraintShape(row)).sort((a, b) => String(a.constraintId).localeCompare(String(b.constraintId))),
158
+ candidates: [...qualification.candidates].map((row) => ({
159
+ candidateId: row.candidateId,
160
+ statement: normalizeText(row.statement),
161
+ status: row.status,
162
+ constraintIds: [...row.constraintIds].sort(),
163
+ impact: normalizeText(row.impact),
164
+ evidence: normalizeText(row.evidence)
165
+ })).sort((a, b) => a.candidateId.localeCompare(b.candidateId))
166
+ };
167
+ }
168
+
169
+ function taskInputDigest(qualification: Pick<TaskQualification, 'constraints' | 'candidates'>): string {
170
+ return digest(canonicalTaskInput(qualification));
171
+ }
172
+
173
+ function sectionBody(content: string, aliases: readonly string[], level = 2): string | null {
174
+ const heading = aliases.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
175
+ const marker = '#'.repeat(level);
176
+ const match = new RegExp(`^${marker}\\s+(${heading})\\s*$`, 'm').exec(content);
177
+ if (!match) return null;
178
+ const rest = content.slice((match.index ?? 0) + match[0].length);
179
+ const end = rest.search(new RegExp(`^#{2,${level}}\\s+`, 'm'));
180
+ return rest.slice(0, end < 0 ? rest.length : end);
181
+ }
182
+
183
+ function stripSection(content: string, aliases: readonly string[]): string {
184
+ const body = sectionBody(content, aliases);
185
+ if (body === null) return normalizeText(content);
186
+ const heading = aliases.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
187
+ return normalizeText(content.replace(new RegExp(`^##\\s+(?:${heading})\\s*$[\\s\\S]*?(?=^##\\s+|(?![\\s\\S]))`, 'm'), ''));
188
+ }
189
+
190
+ function stripFrontmatter(content: string): string {
191
+ const opening = /^---(?:\r?\n)/.exec(content);
192
+ if (!opening) return content;
193
+ const closing = /^---\r?$/gm;
194
+ closing.lastIndex = opening[0].length;
195
+ const match = closing.exec(content);
196
+ return match ? content.slice(match.index + match[0].length) : content;
197
+ }
198
+
199
+ function nonConstraintInputDigest(content: string): string {
200
+ let projection = stripFrontmatter(content);
201
+ projection = stripTaskInputSection(projection, TASK_CONSTRAINT_HEADINGS);
202
+ projection = stripTaskInputSection(projection, TASK_CANDIDATE_HEADINGS);
203
+ projection = [
204
+ ['上下文', 'Context'],
205
+ ['需求', 'Requirements'],
206
+ ['分析', 'Analysis'],
207
+ ['设计', 'Design'],
208
+ ['实现备注', 'Implementation Notes'],
209
+ ['审查反馈', 'Review Feedback'],
210
+ ['审查分歧账本', 'Review Disagreement Ledger'],
211
+ ['人工裁决', 'Human Rulings', 'Decision Records'],
212
+ ['活动日志', 'Activity Log'],
213
+ ['产物生命周期收据', 'Artifact Lifecycle Receipts'],
214
+ ['产物失效记录', 'Artifact Invalidation'],
215
+ ['账本', 'Ledger'],
216
+ ['实现输入', 'Implementation Inputs'],
217
+ ['工作流告警', 'Warnings', 'Workflow Warnings'],
218
+ ['返工意图', 'Rework Intents'],
219
+ CONFIRMATION_HEADINGS,
220
+ ['完成检查清单', 'Completion Checklist']
221
+ ].reduce((value, aliases) => stripSection(value, aliases), projection);
222
+ return digest(projection);
223
+ }
224
+
225
+ function taskInputSection(content: string, aliases: readonly string[]): string | null {
226
+ return sectionBody(content, aliases, 3) ?? sectionBody(content, aliases, 2);
227
+ }
228
+
229
+ function stripTaskInputSection(content: string, aliases: readonly string[]): string {
230
+ let result = stripSectionAtLevel(content, aliases, 3);
231
+ return stripSectionAtLevel(result, aliases, 2);
232
+ }
233
+
234
+ function stripSectionAtLevel(content: string, aliases: readonly string[], level: number): string {
235
+ const body = sectionBody(content, aliases, level);
236
+ if (body === null) return normalizeText(content);
237
+ const heading = aliases.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
238
+ const marker = '#'.repeat(level);
239
+ return normalizeText(content.replace(new RegExp(`^${marker}\\s+(?:${heading})\\s*$[\\s\\S]*?(?=^#{2,${level}}\\s+|(?![\\s\\S]))`, 'm'), ''));
240
+ }
241
+
242
+ function parseTaskQualification(content: string): { ok: true; qualification: TaskQualification } | { ok: false; code: string; message: string } {
243
+ const constraintBody = taskInputSection(content, TASK_CONSTRAINT_HEADINGS);
244
+ const candidateBody = taskInputSection(content, TASK_CANDIDATE_HEADINGS);
245
+ const hasConstraintHeading = constraintBody !== null;
246
+ const hasCandidateHeading = candidateBody !== null;
247
+ if ((!hasConstraintHeading && !hasCandidateHeading)
248
+ || (hasConstraintHeading && hasCandidateHeading
249
+ && !/^\s*\|/m.test(constraintBody!) && !/^\s*\|/m.test(candidateBody!))) {
250
+ return { ok: true, qualification: { present: false, constraints: [], candidates: [], constraintDigest: digest([]), taskInputDigest: digest({ constraints: [], candidates: [] }), nonConstraintInputDigest: nonConstraintInputDigest(content) } };
251
+ }
252
+ if (!hasConstraintHeading || !hasCandidateHeading) {
253
+ const hasCanonicalTable = new RegExp(`\\|\\s*${CONSTRAINT_COLUMNS.join('\\s*\\|\\s*')}\\s*\\|`).test(content)
254
+ || new RegExp(`\\|\\s*${CANDIDATE_COLUMNS.join('\\s*\\|\\s*')}\\s*\\|`).test(content);
255
+ if (hasCanonicalTable) return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_INVALID', message: 'task qualification requires both constraints and candidates sections' };
256
+ return { ok: true, qualification: { present: false, constraints: [], candidates: [], constraintDigest: digest([]), taskInputDigest: digest({ constraints: [], candidates: [] }), nonConstraintInputDigest: nonConstraintInputDigest(content) } };
257
+ }
258
+ try {
259
+ const constraintTable = constraintBody === null ? null : parseTable(`## 约束\n${constraintBody}`, { sectionAliases: ['约束'], columns: CONSTRAINT_COLUMNS });
260
+ const candidateTable = candidateBody === null ? null : parseTable(`## 候选与否决方案\n${candidateBody}`, { sectionAliases: ['候选与否决方案'], columns: CANDIDATE_COLUMNS });
261
+ if (!constraintTable || !candidateTable) return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_INVALID', message: 'task qualification requires canonical constraints and candidates tables' };
262
+ const constraints = constraintTable.rows.map(({ values }) => {
263
+ const row = {
264
+ constraintId: values.constraint_id ?? '', statement: values.statement ?? '', status: values.status as ConstraintStatus,
265
+ authority: values.authority ?? '', source: values.source ?? '', evidence: values.evidence ?? '',
266
+ derivedFrom: canonicalList(values.derived_from ?? ''), approvalEvidence: values.approval_evidence ?? ''
267
+ } satisfies Omit<Constraint, 'digest'>;
268
+ validateConstraint(row);
269
+ return { ...row, digest: constraintDigest(row) };
270
+ });
271
+ const ids = new Set(constraints.map((row) => row.constraintId));
272
+ for (const row of constraints) for (const parent of row.derivedFrom) if (!ids.has(parent)) throw new Error(`constraint '${row.constraintId}' references unknown derived constraint '${parent}'`);
273
+ const candidates = candidateTable.rows.map(({ values }) => {
274
+ const row: Candidate = {
275
+ candidateId: values.candidate_id ?? '', statement: values.statement ?? '', status: values.status as CandidateStatus,
276
+ constraintIds: canonicalList(values.constraint_ids ?? ''), impact: values.impact ?? '', evidence: values.evidence ?? ''
277
+ };
278
+ validateCandidate(row);
279
+ for (const id of row.constraintIds) if (!ids.has(id)) throw new Error(`candidate '${row.candidateId}' references unknown constraint '${id}'`);
280
+ return row;
281
+ });
282
+ const qualification: TaskQualification = {
283
+ present: true, constraints, candidates,
284
+ constraintDigest: digest(constraints.map((row) => canonicalConstraintShape(row)).sort((a, b) => String(a.constraintId).localeCompare(String(b.constraintId)))),
285
+ taskInputDigest: taskInputDigest({ constraints, candidates }),
286
+ nonConstraintInputDigest: nonConstraintInputDigest(content)
287
+ };
288
+ return { ok: true, qualification };
289
+ } catch (error) {
290
+ return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_INVALID', message: error instanceof Error ? error.message : String(error) };
291
+ }
292
+ }
293
+
294
+ function validateConstraint(row: Omit<Constraint, 'digest'>): void {
295
+ if (!/^C-[1-9]\d*$/.test(row.constraintId)) throw new Error(`constraint id '${row.constraintId}' is invalid`);
296
+ if (!normalizeText(row.statement)) throw new Error(`constraint '${row.constraintId}' statement is required`);
297
+ if (!CONSTRAINT_STATUSES.includes(row.status)) throw new Error(`constraint '${row.constraintId}' status is invalid`);
298
+ if (!normalizeText(row.authority) || !normalizeText(row.source) || !normalizeText(row.evidence)) throw new Error(`constraint '${row.constraintId}' authority, source, and evidence are required`);
299
+ if (row.status === 'confirmed' && !normalizeText(row.approvalEvidence)) throw new Error(`confirmed constraint '${row.constraintId}' requires approval_evidence`);
300
+ }
301
+
302
+ function validateCandidate(row: Pick<Candidate, 'candidateId' | 'status' | 'impact' | 'evidence'> & Partial<Pick<Candidate, 'statement'>>): void {
303
+ if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(row.candidateId)) throw new Error(`candidate id '${row.candidateId}' is invalid`);
304
+ if ('statement' in row && !normalizeText(row.statement ?? '')) throw new Error(`candidate '${row.candidateId}' statement is required`);
305
+ if (!CANDIDATE_STATUSES.includes(row.status)) throw new Error(`candidate '${row.candidateId}' status is invalid`);
306
+ if (!normalizeText(row.impact) || !normalizeText(row.evidence)) throw new Error(`candidate '${row.candidateId}' impact and evidence are required`);
307
+ }
308
+
309
+ function parseAuditTable(content: string, heading: (typeof AUDIT_SUBSECTIONS)[number], columns: readonly string[]): Record<string, string>[] | null {
310
+ const body = sectionBody(content, AUDIT_HEADINGS);
311
+ if (body === null) return null;
312
+ const subsection = sectionBody(body, AUDIT_SUBSECTION_HEADINGS[heading], 3);
313
+ if (subsection === null) return null;
314
+ const table = parseTable(`## Qualification Audit Body\n${subsection}`, { sectionAliases: ['Qualification Audit Body'], columns });
315
+ return table?.rows.map((row) => ({ ...row.values })) ?? null;
316
+ }
317
+
318
+ function parseArtifactName(value: string): { family: ArtifactFamily; round: number } | null {
319
+ const match = /^(analysis|review-analysis|plan|review-plan|code|review-code)(?:-r([2-9]|[1-9]\d+))?\.md$/.exec(value);
320
+ if (!match) return null;
321
+ return { family: match[1] as ArtifactFamily, round: match[2] ? Number(match[2]) : 1 };
322
+ }
323
+
324
+ function upstreamArtifactDigest(rows: readonly UpstreamRelation[]): string {
325
+ return digest([...rows].map((row) => ({ ...row })).sort((a, b) =>
326
+ `${a.upstreamFamily}/${a.upstreamArtifact}/${a.relation}`.localeCompare(`${b.upstreamFamily}/${b.upstreamArtifact}/${b.relation}`)));
327
+ }
328
+
329
+ function validateUpstreamRelation(row: UpstreamRelation): void {
330
+ const identity = parseArtifactName(row.upstreamArtifact);
331
+ if (!ARTIFACT_FAMILIES.includes(row.upstreamFamily) || !identity || identity.family !== row.upstreamFamily
332
+ || identity.round !== row.upstreamRound || !Number.isSafeInteger(row.upstreamRound)
333
+ || !/^[a-f0-9]{64}$/i.test(row.upstreamSha256) || !RELATIONS.includes(row.relation)) {
334
+ throw new Error(`invalid qualification upstream relation '${row.upstreamArtifact}'`);
335
+ }
336
+ }
337
+
338
+ function expectedQualificationRelations(
339
+ taskContent: string,
340
+ family: ArtifactFamily
341
+ ): { ok: true; relations: readonly UpstreamRelation[] | undefined } | { ok: false; code: string; message: string } {
342
+ let frontmatter;
343
+ try { frontmatter = parseTypedTaskFrontmatter(taskContent); }
344
+ catch (error) { return { ok: false, code: 'QUALIFICATION_STARTED_INPUT_INVALID', message: error instanceof Error ? error.message : String(error) }; }
345
+ const encoded = frontmatter.qualification_input_relations;
346
+ if (typeof encoded === 'string' && encoded) {
347
+ try {
348
+ const parsed = JSON.parse(encoded) as unknown;
349
+ if (!Array.isArray(parsed)) throw new Error('qualification_input_relations must be an array');
350
+ const relations = parsed.map((value) => {
351
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('qualification input relation must be an object');
352
+ const row = value as Record<string, unknown>;
353
+ const relation: UpstreamRelation = {
354
+ upstreamFamily: row.upstreamFamily as ArtifactFamily,
355
+ upstreamArtifact: String(row.upstreamArtifact ?? ''),
356
+ upstreamRound: Number(row.upstreamRound),
357
+ upstreamSha256: String(row.upstreamSha256 ?? ''),
358
+ relation: row.relation as QualificationRelation
359
+ };
360
+ validateUpstreamRelation(relation);
361
+ return relation;
362
+ });
363
+ return { ok: true, relations };
364
+ } catch (error) {
365
+ return { ok: false, code: 'QUALIFICATION_STARTED_INPUT_INVALID', message: error instanceof Error ? error.message : String(error) };
366
+ }
367
+ }
368
+ const inputName = family === 'code' ? frontmatter.code_input_artifact : family.startsWith('review-') ? frontmatter.review_input_artifact : '';
369
+ const inputSha256 = family === 'code' ? frontmatter.code_input_sha256 : family.startsWith('review-') ? frontmatter.review_input_sha256 : '';
370
+ if (typeof inputName !== 'string' || !inputName || typeof inputSha256 !== 'string' || !inputSha256) return { ok: true, relations: undefined };
371
+ const identity = parseArtifactName(inputName);
372
+ if (!identity) return { ok: false, code: 'QUALIFICATION_STARTED_INPUT_INVALID', message: `started input '${inputName}' is not canonical` };
373
+ const relation: UpstreamRelation = {
374
+ upstreamFamily: identity.family, upstreamArtifact: inputName, upstreamRound: identity.round,
375
+ upstreamSha256: inputSha256, relation: family === 'code' ? 'required-input' : 'reviewed-input'
376
+ };
377
+ try { validateUpstreamRelation(relation); }
378
+ catch (error) { return { ok: false, code: 'QUALIFICATION_STARTED_INPUT_INVALID', message: error instanceof Error ? error.message : String(error) }; }
379
+ return { ok: true, relations: [relation] };
380
+ }
381
+
382
+ function parseQualificationAudit(content: string): { ok: true; audit: QualificationAudit } | { ok: false; code: string; message: string } {
383
+ if (sectionBody(content, AUDIT_HEADINGS) === null) return { ok: true, audit: { present: false, constraintDependencies: [], candidateQualifications: [], classifications: [], upstreamRelations: [], snapshot: null } };
384
+ try {
385
+ const dependencies = parseAuditTable(content, '约束依赖', DEPENDENCY_COLUMNS);
386
+ const qualifications = parseAuditTable(content, '候选资格', QUALIFICATION_COLUMNS);
387
+ const classifications = parseAuditTable(content, '分类结果', CLASSIFICATION_COLUMNS);
388
+ const relations = parseAuditTable(content, '上游关系', RELATION_COLUMNS);
389
+ const snapshots = parseAuditTable(content, '依赖快照', SNAPSHOT_COLUMNS);
390
+ if (!dependencies || !qualifications || !classifications || !relations || !snapshots || snapshots.length !== 1) {
391
+ throw new Error('qualification audit requires all five canonical tables and exactly one dependency snapshot');
392
+ }
393
+ const constraintDependencies = dependencies.map((row) => {
394
+ if (!/^C-[1-9]\d*$/.test(row.constraint_id ?? '') || !/^[a-f0-9]{64}$/i.test(row.constraint_digest ?? '') || !ROLES.includes(row.role as QualificationRole) || !row.evidence?.trim()) throw new Error(`invalid qualification constraint dependency '${row.constraint_id ?? ''}'`);
395
+ return { constraintId: row.constraint_id!, constraintDigest: row.constraint_digest!, role: row.role as QualificationRole, evidence: row.evidence! };
396
+ });
397
+ const candidateQualifications = qualifications.map((row) => {
398
+ const parsed: CandidateQualification = { candidateId: row.candidate_id ?? '', status: row.status as CandidateStatus, impact: row.impact ?? '', constraintIds: canonicalList(row.constraint_ids ?? ''), evidence: row.evidence ?? '' };
399
+ validateCandidate(parsed);
400
+ return parsed;
401
+ });
402
+ const classificationRows = classifications.map((row) => {
403
+ if (!row.decision_id?.trim() || !CLASSIFICATIONS.includes(row.classification as QualificationClassification) || !row.evidence?.trim()) throw new Error(`invalid qualification classification '${row.decision_id ?? ''}'`);
404
+ return { decisionId: row.decision_id, classification: row.classification as QualificationClassification, evidence: row.evidence };
405
+ });
406
+ const upstreamRelations = relations.map((row) => {
407
+ const relation = { upstreamFamily: row.upstream_family as ArtifactFamily, upstreamArtifact: row.upstream_artifact ?? '', upstreamRound: Number(row.upstream_round), upstreamSha256: row.upstream_sha256 ?? '', relation: row.relation as QualificationRelation };
408
+ validateUpstreamRelation(relation);
409
+ return relation;
410
+ });
411
+ const snapshotRow = snapshots[0]!;
412
+ 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 ?? '')) throw new Error('qualification dependency snapshot has invalid digest');
413
+ return {
414
+ ok: true,
415
+ audit: {
416
+ present: true, constraintDependencies, candidateQualifications,
417
+ classifications: classificationRows, upstreamRelations,
418
+ snapshot: { taskInputDigest: snapshotRow.task_input_digest!, nonConstraintInputDigest: snapshotRow.non_constraint_input_digest!, upstreamArtifactDigest: snapshotRow.upstream_artifact_digest! }
419
+ }
420
+ };
421
+ } catch (error) {
422
+ return { ok: false, code: 'QUALIFICATION_AUDIT_INVALID', message: error instanceof Error ? error.message : String(error) };
423
+ }
424
+ }
425
+
426
+ function parseQualificationConfirmations(content: string): { ok: true; confirmations: readonly QualificationConfirmation[] } | { ok: false; code: string; message: string } {
427
+ const body = sectionBody(content, CONFIRMATION_HEADINGS);
428
+ if (body === null) return { ok: true, confirmations: [] };
429
+ try {
430
+ const table = parseTable(content, { sectionAliases: CONFIRMATION_HEADINGS, columns: CONFIRMATION_COLUMNS });
431
+ if (!table) throw new Error('qualification confirmation section requires a canonical table');
432
+ const seen = new Set<string>();
433
+ const confirmations = table.rows.map(({ values }) => {
434
+ const row: QualificationConfirmation = {
435
+ qcrId: values.qcr_id ?? '', constraintId: values.constraint_id ?? '', actor: values.actor as 'human-declared',
436
+ entrypoint: values.entrypoint as 'task-qualification', requestId: values.request_id ?? '', approvedDigest: values.approved_digest ?? '',
437
+ confirmedAt: values.confirmed_at ?? '', rationale: values.rationale ?? ''
438
+ };
439
+ 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)) throw new Error(`qualification confirmation '${row.qcrId}' is invalid`);
440
+ if (seen.has(row.qcrId)) throw new Error(`duplicate qualification confirmation '${row.qcrId}'`);
441
+ seen.add(row.qcrId);
442
+ return row;
443
+ });
444
+ return { ok: true, confirmations };
445
+ } catch (error) {
446
+ return { ok: false, code: 'QUALIFICATION_CONFIRMATION_INVALID', message: error instanceof Error ? error.message : String(error) };
447
+ }
448
+ }
449
+
450
+ function validateQualificationAudit(
451
+ taskContent: string,
452
+ artifactContent: string,
453
+ options: {
454
+ family?: ArtifactFamily;
455
+ artifact?: string;
456
+ require?: boolean;
457
+ expectedUpstreamRelations?: readonly UpstreamRelation[];
458
+ } = {}
459
+ ): QualificationValidationResult {
460
+ const task = parseTaskQualification(taskContent);
461
+ if (!task.ok) return task;
462
+ const audit = parseQualificationAudit(artifactContent);
463
+ if (!audit.ok) return audit;
464
+ if (!task.qualification.present && !options.require) return { ok: true, qualification: task.qualification, audit: audit.audit };
465
+ if (!task.qualification.present) return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_MISSING', message: 'task qualification contract is missing' };
466
+ if (!audit.audit.present) return { ok: false, code: 'QUALIFICATION_AUDIT_MISSING', message: 'artifact qualification audit is missing' };
467
+ const constraintMap = new Map(task.qualification.constraints.map((row) => [row.constraintId, row]));
468
+ const confirmations = parseQualificationConfirmations(taskContent);
469
+ if (!confirmations.ok) return confirmations;
470
+ const confirmationIds = new Set<string>();
471
+ for (const confirmation of confirmations.confirmations) {
472
+ if (confirmationIds.has(confirmation.qcrId)) return { ok: false, code: 'QUALIFICATION_CONFIRMATION_DUPLICATE', message: `qualification confirmation '${confirmation.qcrId}' is duplicated` };
473
+ confirmationIds.add(confirmation.qcrId);
474
+ if (!constraintMap.has(confirmation.constraintId)) return { ok: false, code: 'QUALIFICATION_CONSTRAINT_UNKNOWN', message: `qualification confirmation references unknown constraint '${confirmation.constraintId}'` };
475
+ }
476
+ const confirmationMap = new Map(confirmations.confirmations.map((row) => [row.qcrId, row]));
477
+ for (const constraint of task.qualification.constraints) {
478
+ if (constraint.status !== 'confirmed') continue;
479
+ const confirmation = confirmationMap.get(constraint.approvalEvidence);
480
+ if (!confirmation || confirmation.constraintId !== constraint.constraintId) return { ok: false, code: 'QUALIFICATION_CONFIRMATION_MISSING', message: `confirmed constraint '${constraint.constraintId}' has no matching QCR` };
481
+ if (confirmation.approvedDigest !== constraint.digest) return { ok: false, code: 'QUALIFICATION_CONFIRMATION_DIGEST_MISMATCH', message: `qualification confirmation for '${constraint.constraintId}' is stale` };
482
+ }
483
+ for (const row of audit.audit.constraintDependencies) {
484
+ const constraint = constraintMap.get(row.constraintId);
485
+ if (!constraint) return { ok: false, code: 'QUALIFICATION_CONSTRAINT_UNKNOWN', message: `qualification audit references unknown constraint '${row.constraintId}'` };
486
+ if (constraint.digest !== row.constraintDigest) return { ok: false, code: 'QUALIFICATION_CONSTRAINT_DIGEST_MISMATCH', message: `qualification audit digest does not match '${row.constraintId}'` };
487
+ }
488
+ const candidateMap = new Map(task.qualification.candidates.map((row) => [row.candidateId, row]));
489
+ const candidateIds = new Set<string>();
490
+ for (const row of audit.audit.candidateQualifications) {
491
+ if (candidateIds.has(row.candidateId)) return { ok: false, code: 'QUALIFICATION_CANDIDATE_MISMATCH', message: `qualification candidate '${row.candidateId}' is duplicated` };
492
+ candidateIds.add(row.candidateId);
493
+ const current = candidateMap.get(row.candidateId);
494
+ if (!current) return { ok: false, code: 'QUALIFICATION_CANDIDATE_UNKNOWN', message: `qualification audit references unknown candidate '${row.candidateId}'` };
495
+ if (row.constraintIds.some((id) => !constraintMap.has(id))) return { ok: false, code: 'QUALIFICATION_CONSTRAINT_UNKNOWN', message: `candidate '${row.candidateId}' references unknown qualification constraint` };
496
+ if (row.status !== current.status || row.impact !== current.impact || row.evidence !== current.evidence || row.constraintIds.join(',') !== current.constraintIds.join(',')) {
497
+ return { ok: false, code: 'QUALIFICATION_CANDIDATE_MISMATCH', message: `qualification candidate '${row.candidateId}' does not match task input` };
498
+ }
499
+ }
500
+ if (candidateIds.size !== candidateMap.size) return { ok: false, code: 'QUALIFICATION_CANDIDATE_MISMATCH', message: 'qualification audit does not contain the complete task candidate set' };
501
+ const snapshot = audit.audit.snapshot!;
502
+ if (snapshot.taskInputDigest !== task.qualification.taskInputDigest) return { ok: false, code: 'QUALIFICATION_TASK_DIGEST_MISMATCH', message: 'qualification audit task input digest is stale' };
503
+ if (snapshot.nonConstraintInputDigest !== task.qualification.nonConstraintInputDigest) return { ok: false, code: 'QUALIFICATION_NON_CONSTRAINT_DIGEST_MISMATCH', message: 'qualification audit non-constraint input digest is stale' };
504
+ if (snapshot.upstreamArtifactDigest !== upstreamArtifactDigest(audit.audit.upstreamRelations)) return { ok: false, code: 'QUALIFICATION_UPSTREAM_DIGEST_MISMATCH', message: 'qualification audit upstream digest does not match its relation rows' };
505
+ if (options.expectedUpstreamRelations && (
506
+ options.expectedUpstreamRelations.length !== audit.audit.upstreamRelations.length
507
+ || upstreamArtifactDigest(options.expectedUpstreamRelations) !== upstreamArtifactDigest(audit.audit.upstreamRelations)
508
+ )) return { ok: false, code: 'QUALIFICATION_UPSTREAM_RELATION_MISMATCH', message: 'qualification audit upstream relations do not match the started artifact inputs' };
509
+ if (options.family && options.artifact) {
510
+ const identity = parseArtifactName(options.artifact);
511
+ if (!identity || identity.family !== options.family) return { ok: false, code: 'QUALIFICATION_ARTIFACT_IDENTITY_INVALID', message: `artifact '${options.artifact}' is not canonical for '${options.family}'` };
512
+ }
513
+ return { ok: true, qualification: task.qualification, audit: audit.audit };
514
+ }
515
+
516
+ function renderQualificationAudit(audit: QualificationAudit): string {
517
+ const table = (columns: readonly string[], rows: readonly (readonly string[])[]) => [
518
+ `| ${columns.join(' | ')} |`,
519
+ `| ${columns.map(() => '---').join(' | ')} |`,
520
+ ...rows.map((row) => `| ${row.map((value) => value.replace(/\\/g, '\\\\').replace(/\|/g, '\\|').replace(/[\r\n]/g, ' ')).join(' | ')} |`)
521
+ ].join('\n');
522
+ const list = (values: readonly string[]) => values.join(',');
523
+ const snapshot = audit.snapshot ?? { taskInputDigest: '', nonConstraintInputDigest: '', upstreamArtifactDigest: upstreamArtifactDigest(audit.upstreamRelations) };
524
+ return [
525
+ '### 约束依赖', '', table(DEPENDENCY_COLUMNS, audit.constraintDependencies.map((row) => [row.constraintId, row.constraintDigest, row.role, row.evidence])), '',
526
+ '### 候选资格', '', table(QUALIFICATION_COLUMNS, audit.candidateQualifications.map((row) => [row.candidateId, row.status, row.impact, list(row.constraintIds), row.evidence])), '',
527
+ '### 分类结果', '', table(CLASSIFICATION_COLUMNS, audit.classifications.map((row) => [row.decisionId, row.classification, row.evidence])), '',
528
+ '### 上游关系', '', table(RELATION_COLUMNS, audit.upstreamRelations.map((row) => [row.upstreamFamily, row.upstreamArtifact, String(row.upstreamRound), row.upstreamSha256, row.relation])), '',
529
+ '### 依赖快照', '', table(SNAPSHOT_COLUMNS, [[snapshot.taskInputDigest, snapshot.nonConstraintInputDigest, snapshot.upstreamArtifactDigest]])
530
+ ].join('\n');
531
+ }
532
+
533
+ function buildQualificationAudit(
534
+ taskContent: string,
535
+ input: { constraints?: readonly string[]; candidates?: readonly string[]; classifications?: readonly QualificationClassificationRow[]; upstreamRelations?: readonly UpstreamRelation[] } = {}
536
+ ): { ok: true; audit: QualificationAudit } | { ok: false; code: string; message: string } {
537
+ const parsed = parseTaskQualification(taskContent);
538
+ if (!parsed.ok) return parsed;
539
+ if (!parsed.qualification.present) return { ok: false, code: 'QUALIFICATION_TASK_CONTRACT_MISSING', message: 'task qualification contract is missing' };
540
+ const ids = new Set(input.constraints ?? parsed.qualification.constraints.map((row) => row.constraintId));
541
+ const candidates = input.candidates ?? parsed.qualification.candidates.map((row) => row.candidateId);
542
+ const constraintDependencies = parsed.qualification.constraints.filter((row) => ids.has(row.constraintId)).map((row) => ({ constraintId: row.constraintId, constraintDigest: row.digest, role: 'required' as const, evidence: row.evidence }));
543
+ 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 }));
544
+ const upstreamRelations = [...(input.upstreamRelations ?? [])];
545
+ return {
546
+ ok: true,
547
+ audit: {
548
+ present: true, constraintDependencies, candidateQualifications,
549
+ classifications: input.classifications ?? [], upstreamRelations,
550
+ snapshot: { taskInputDigest: parsed.qualification.taskInputDigest, nonConstraintInputDigest: parsed.qualification.nonConstraintInputDigest, upstreamArtifactDigest: upstreamArtifactDigest(upstreamRelations) }
551
+ }
552
+ };
553
+ }
554
+
555
+ export {
556
+ ARTIFACT_FAMILIES,
557
+ AUDIT_HEADINGS,
558
+ AUDIT_SUBSECTIONS,
559
+ CONFIRMATION_COLUMNS,
560
+ CONFIRMATION_HEADINGS,
561
+ CANDIDATE_COLUMNS,
562
+ CONSTRAINT_COLUMNS,
563
+ DEPENDENCY_COLUMNS,
564
+ QUALIFICATION_COLUMNS,
565
+ CLASSIFICATION_COLUMNS,
566
+ RELATION_COLUMNS,
567
+ SNAPSHOT_COLUMNS,
568
+ constraintDigest,
569
+ expectedQualificationRelations,
570
+ buildQualificationAudit,
571
+ nonConstraintInputDigest,
572
+ parseQualificationAudit,
573
+ parseQualificationConfirmations,
574
+ parseTaskQualification,
575
+ renderQualificationAudit,
576
+ taskInputDigest,
577
+ upstreamArtifactDigest,
578
+ validateQualificationAudit
579
+ };
580
+ export type {
581
+ Candidate,
582
+ CandidateQualification,
583
+ CandidateStatus,
584
+ Constraint,
585
+ ConstraintDependency,
586
+ ConstraintStatus,
587
+ DependencySnapshot,
588
+ QualificationAudit,
589
+ QualificationConfirmation,
590
+ QualificationClassification,
591
+ QualificationClassificationRow,
592
+ QualificationRelation,
593
+ QualificationRole,
594
+ QualificationValidationError,
595
+ QualificationValidationResult,
596
+ TaskQualification,
597
+ UpstreamRelation,
598
+ ArtifactFamily
599
+ };