@fitlab-ai/agent-infra 0.9.14 → 0.10.0

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 (354) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/internal-cli.sh +45 -0
  4. package/dist/bin/internal-cli.js +93 -30
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +9 -4
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
  8. package/dist/lib/defaults.json +103 -103
  9. package/dist/lib/fs/durable-write.js +39 -0
  10. package/dist/lib/fs/file-lock.js +111 -0
  11. package/dist/lib/host-control/audit.js +19 -0
  12. package/dist/lib/host-control/client.js +142 -0
  13. package/dist/lib/host-control/command.js +49 -0
  14. package/dist/lib/host-control/path.js +160 -0
  15. package/dist/lib/host-control/secure-fs.js +140 -0
  16. package/dist/lib/host-control/server.js +159 -0
  17. package/dist/lib/host-control/service.js +112 -0
  18. package/dist/lib/init.js +11 -2
  19. package/dist/lib/internal/cli-route-inventory.js +4 -2
  20. package/dist/lib/internal/codex-lifecycle.js +6 -6
  21. package/dist/lib/internal/demo-promotion.js +322 -0
  22. package/dist/lib/internal/demo-transcript.js +454 -0
  23. package/dist/lib/internal/host-control.js +28 -0
  24. package/dist/lib/internal/manual-validation.js +331 -0
  25. package/dist/lib/internal/platform-pr.js +18 -4
  26. package/dist/lib/internal/release-workflow.js +63 -84
  27. package/dist/lib/internal/sandbox-control.js +149 -2
  28. package/dist/lib/internal/task-artifact.js +16 -263
  29. package/dist/lib/internal/task-create.js +6 -5
  30. package/dist/lib/internal/task-event.js +37 -37
  31. package/dist/lib/internal/task-lifecycle.js +2 -2
  32. package/dist/lib/internal/task-operation-registry.js +101 -4
  33. package/dist/lib/internal/task-review.js +11 -56
  34. package/dist/lib/internal/task-validate.js +30 -10
  35. package/dist/lib/owned-path.js +33 -0
  36. package/dist/lib/platform/check-status.js +11 -0
  37. package/dist/lib/platform/comment-safety.js +4 -1
  38. package/dist/lib/platform/completion-backfill.js +1 -1
  39. package/dist/lib/platform/github-data.js +241 -0
  40. package/dist/lib/platform/github-provider.js +15 -20
  41. package/dist/lib/platform/issue-comments.js +18 -12
  42. package/dist/lib/platform/issues.js +2 -180
  43. package/dist/lib/platform/pr-change-report.js +5 -4
  44. package/dist/lib/platform/pr-checks.js +12 -65
  45. package/dist/lib/platform/pr-summary.js +117 -33
  46. package/dist/lib/platform/pull-requests.js +5 -108
  47. package/dist/lib/platform/snapshots.js +2 -0
  48. package/dist/lib/platform/task-identities.js +2 -9
  49. package/dist/lib/platform/verification-sync.js +132 -143
  50. package/dist/lib/pr-review/evidence-grading.js +4 -8
  51. package/dist/lib/release/workflow.js +1 -1
  52. package/dist/lib/sandbox/commands/create.js +7 -2
  53. package/dist/lib/sandbox/commands/rm.js +19 -1511
  54. package/dist/lib/sandbox/control/audit.js +203 -0
  55. package/dist/lib/sandbox/control/client.js +93 -54
  56. package/dist/lib/sandbox/control/executor.js +129 -65
  57. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  58. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  59. package/dist/lib/sandbox/control/lifecycle.js +36 -2
  60. package/dist/lib/sandbox/control/native-file-lock.js +6 -98
  61. package/dist/lib/sandbox/control/protocol.js +55 -4
  62. package/dist/lib/sandbox/control/server.js +518 -127
  63. package/dist/lib/sandbox/control/state.js +64 -0
  64. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  65. package/dist/lib/sandbox/control/workflow-executor.js +89 -0
  66. package/dist/lib/sandbox/index.js +1 -2
  67. package/dist/lib/sandbox/recovery.js +28 -4
  68. package/dist/lib/sandbox/removal.js +822 -0
  69. package/dist/lib/sandbox/task-cleanup.js +217 -0
  70. package/dist/lib/sandbox/workspace-identity.js +17 -1
  71. package/dist/lib/sandbox/workspace-view.js +13 -14
  72. package/dist/lib/task/activity-intent.js +2 -1
  73. package/dist/lib/task/activity-log.js +30 -24
  74. package/dist/lib/task/artifact-command.js +79 -0
  75. package/dist/lib/task/artifact-lifecycle.js +30 -28
  76. package/dist/lib/task/artifact-name.js +36 -0
  77. package/dist/lib/task/artifact-operations.js +29 -289
  78. package/dist/lib/task/artifact-receipts.js +3 -15
  79. package/dist/lib/task/artifact-recovery.js +430 -0
  80. package/dist/lib/task/artifact-repair-intent.js +134 -29
  81. package/dist/lib/task/artifact-schema.js +6 -14
  82. package/dist/lib/task/capabilities.js +12 -19
  83. package/dist/lib/task/codex-orchestration.js +71 -25
  84. package/dist/lib/task/commit-identity.js +28 -0
  85. package/dist/lib/task/commit-operation.js +33 -20
  86. package/dist/lib/task/control-authority.js +403 -6
  87. package/dist/lib/task/control-recovery.js +140 -0
  88. package/dist/lib/task/create-service.js +1 -8
  89. package/dist/lib/task/create.js +62 -9
  90. package/dist/lib/task/decision-details.js +2 -46
  91. package/dist/lib/task/delegation-receipts.js +64 -3
  92. package/dist/lib/task/events.js +275 -22
  93. package/dist/lib/task/finalization-state.js +9 -0
  94. package/dist/lib/task/human-override.js +8 -14
  95. package/dist/lib/task/implementation-inputs.js +3 -3
  96. package/dist/lib/task/intermediate-cleanup.js +441 -0
  97. package/dist/lib/task/invalidation.js +18 -65
  98. package/dist/lib/task/ledger-intents.js +7 -22
  99. package/dist/lib/task/ledger.js +1 -3
  100. package/dist/lib/task/lifecycle-recovery.js +549 -0
  101. package/dist/lib/task/lifecycle.js +31 -4
  102. package/dist/lib/task/local-artifact-finalization.js +197 -197
  103. package/dist/lib/task/manual-validation-completion.js +26 -0
  104. package/dist/lib/task/manual-validation-evidence.js +130 -0
  105. package/dist/lib/task/manual-validation-receipt.js +105 -0
  106. package/dist/lib/task/manual-validation-shared.js +40 -0
  107. package/dist/lib/task/manual-validation-transaction.js +243 -0
  108. package/dist/lib/task/markdown.js +47 -0
  109. package/dist/lib/task/orchestration.js +66 -19
  110. package/dist/lib/task/pr-delivery-fact.js +12 -56
  111. package/dist/lib/task/qualification-audit.js +20 -34
  112. package/dist/lib/task/recovery-warning.js +19 -0
  113. package/dist/lib/task/review-artifacts.js +2 -18
  114. package/dist/lib/task/review-command.js +26 -0
  115. package/dist/lib/task/review-finalization.js +175 -216
  116. package/dist/lib/task/review-fingerprint.js +2 -2
  117. package/dist/lib/task/sections.js +34 -56
  118. package/dist/lib/task/short-id.js +15 -7
  119. package/dist/lib/task/task-execution-lock.js +1 -1
  120. package/dist/lib/task/verification-engine.js +119 -337
  121. package/dist/lib/task/verification.js +1 -1
  122. package/dist/lib/task/workflow-command.js +16 -0
  123. package/dist/lib/task/workflow-warning-intents.js +0 -16
  124. package/dist/lib/task/workflow-warnings.js +28 -2
  125. package/dist/lib/update.js +2 -0
  126. package/dist/lib/version.js +1 -11
  127. package/dist/lifecycle-build-manifest.json +3 -3
  128. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  129. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +10 -4
  130. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
  131. package/lib/fs/durable-write.ts +30 -0
  132. package/lib/fs/file-lock.ts +113 -0
  133. package/lib/host-control/audit.ts +22 -0
  134. package/lib/host-control/client.ts +172 -0
  135. package/lib/host-control/command.ts +60 -0
  136. package/lib/host-control/path.ts +167 -0
  137. package/lib/host-control/secure-fs.ts +135 -0
  138. package/lib/host-control/server.ts +180 -0
  139. package/lib/host-control/service.ts +133 -0
  140. package/lib/init.ts +15 -2
  141. package/lib/internal/cli-route-inventory.ts +4 -2
  142. package/lib/internal/codex-lifecycle.ts +6 -6
  143. package/lib/internal/demo-promotion.ts +354 -0
  144. package/lib/internal/demo-transcript.ts +419 -0
  145. package/lib/internal/host-control.ts +28 -0
  146. package/lib/internal/manual-validation.ts +315 -0
  147. package/lib/internal/platform-pr.ts +15 -4
  148. package/lib/internal/release-workflow.ts +74 -82
  149. package/lib/internal/sandbox-control.ts +162 -1
  150. package/lib/internal/task-artifact.ts +14 -217
  151. package/lib/internal/task-create.ts +5 -5
  152. package/lib/internal/task-event.ts +20 -11
  153. package/lib/internal/task-lifecycle.ts +4 -3
  154. package/lib/internal/task-operation-registry.ts +111 -4
  155. package/lib/internal/task-review.ts +9 -54
  156. package/lib/internal/task-validate.ts +26 -10
  157. package/lib/owned-path.ts +26 -0
  158. package/lib/platform/check-status.ts +9 -0
  159. package/lib/platform/comment-safety.ts +5 -0
  160. package/lib/platform/completion-backfill.ts +1 -1
  161. package/lib/platform/github-data.ts +322 -0
  162. package/lib/platform/github-provider.ts +15 -20
  163. package/lib/platform/issue-comments.ts +21 -14
  164. package/lib/platform/issues.ts +5 -246
  165. package/lib/platform/pr-change-report.ts +7 -5
  166. package/lib/platform/pr-checks.ts +13 -77
  167. package/lib/platform/pr-summary.ts +123 -35
  168. package/lib/platform/pull-requests.ts +6 -173
  169. package/lib/platform/snapshots.ts +53 -0
  170. package/lib/platform/task-identities.ts +3 -14
  171. package/lib/platform/verification-sync.ts +150 -156
  172. package/lib/pr-review/evidence-grading.ts +4 -9
  173. package/lib/release/workflow.ts +3 -3
  174. package/lib/sandbox/commands/create.ts +7 -6
  175. package/lib/sandbox/commands/rm.ts +21 -1895
  176. package/lib/sandbox/control/audit.ts +253 -0
  177. package/lib/sandbox/control/client.ts +109 -66
  178. package/lib/sandbox/control/executor.ts +134 -74
  179. package/lib/sandbox/control/finalization-response.ts +29 -0
  180. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  181. package/lib/sandbox/control/lifecycle.ts +45 -3
  182. package/lib/sandbox/control/native-file-lock.ts +6 -107
  183. package/lib/sandbox/control/protocol.ts +80 -6
  184. package/lib/sandbox/control/server.ts +567 -132
  185. package/lib/sandbox/control/state.ts +86 -0
  186. package/lib/sandbox/control/task-workflow.ts +95 -0
  187. package/lib/sandbox/control/workflow-executor.ts +92 -0
  188. package/lib/sandbox/index.ts +1 -2
  189. package/lib/sandbox/recovery.ts +28 -5
  190. package/lib/sandbox/removal.ts +1142 -0
  191. package/lib/sandbox/task-cleanup.ts +209 -0
  192. package/lib/sandbox/workspace-identity.ts +19 -3
  193. package/lib/sandbox/workspace-view.ts +17 -15
  194. package/lib/task/activity-intent.ts +2 -2
  195. package/lib/task/activity-log.ts +27 -19
  196. package/lib/task/artifact-command.ts +85 -0
  197. package/lib/task/artifact-lifecycle.ts +31 -26
  198. package/lib/task/artifact-name.ts +55 -0
  199. package/lib/task/artifact-operations.ts +34 -310
  200. package/lib/task/artifact-receipts.ts +3 -14
  201. package/lib/task/artifact-recovery.ts +519 -0
  202. package/lib/task/artifact-repair-intent.ts +162 -33
  203. package/lib/task/artifact-schema.ts +5 -34
  204. package/lib/task/capabilities.ts +13 -22
  205. package/lib/task/codex-orchestration.ts +85 -24
  206. package/lib/task/commit-identity.ts +31 -0
  207. package/lib/task/commit-operation.ts +37 -20
  208. package/lib/task/control-authority.ts +540 -10
  209. package/lib/task/control-recovery.ts +186 -0
  210. package/lib/task/create-service.ts +1 -10
  211. package/lib/task/create.ts +63 -8
  212. package/lib/task/decision-details.ts +3 -71
  213. package/lib/task/delegation-receipts.ts +73 -2
  214. package/lib/task/events.ts +309 -24
  215. package/lib/task/finalization-state.ts +11 -0
  216. package/lib/task/human-override.ts +8 -16
  217. package/lib/task/implementation-inputs.ts +3 -3
  218. package/lib/task/intermediate-cleanup.ts +493 -0
  219. package/lib/task/invalidation.ts +18 -62
  220. package/lib/task/ledger-intents.ts +7 -24
  221. package/lib/task/ledger.ts +1 -3
  222. package/lib/task/lifecycle-recovery.ts +691 -0
  223. package/lib/task/lifecycle.ts +30 -5
  224. package/lib/task/local-artifact-finalization.ts +244 -234
  225. package/lib/task/manual-validation-completion.ts +40 -0
  226. package/lib/task/manual-validation-evidence.ts +209 -0
  227. package/lib/task/manual-validation-receipt.ts +156 -0
  228. package/lib/task/manual-validation-shared.ts +50 -0
  229. package/lib/task/manual-validation-transaction.ts +304 -0
  230. package/lib/task/markdown.ts +70 -0
  231. package/lib/task/orchestration.ts +84 -19
  232. package/lib/task/pr-delivery-fact.ts +15 -62
  233. package/lib/task/qualification-audit.ts +22 -33
  234. package/lib/task/recovery-warning.ts +21 -0
  235. package/lib/task/review-artifacts.ts +1 -20
  236. package/lib/task/review-command.ts +24 -0
  237. package/lib/task/review-finalization.ts +183 -292
  238. package/lib/task/review-fingerprint.ts +2 -2
  239. package/lib/task/sections.ts +33 -54
  240. package/lib/task/short-id.ts +27 -5
  241. package/lib/task/task-execution-lock.ts +1 -1
  242. package/lib/task/verification-engine.ts +113 -383
  243. package/lib/task/verification-types.ts +15 -0
  244. package/lib/task/verification.ts +1 -1
  245. package/lib/task/workflow-command.ts +17 -0
  246. package/lib/task/workflow-warning-intents.ts +0 -13
  247. package/lib/task/workflow-warnings.ts +21 -2
  248. package/lib/update.ts +6 -0
  249. package/lib/version.ts +1 -15
  250. package/package.json +6 -6
  251. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  252. package/templates/.agents/rules/create-issue.en.md +1 -1
  253. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  254. package/templates/.agents/rules/issue-sync.en.md +4 -2
  255. package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
  256. package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
  257. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
  258. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  259. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  260. package/templates/.agents/scripts/package.json +3 -0
  261. package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
  262. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
  263. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
  264. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
  265. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  266. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  267. package/templates/.agents/skills/block-task/config/verify.json +1 -1
  268. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  269. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  270. package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
  271. package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
  272. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
  273. package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
  274. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
  275. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  276. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  277. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
  278. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -21
  279. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  280. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  281. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  282. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  283. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  284. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  285. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  286. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  287. package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
  288. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
  289. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  290. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  291. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
  292. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
  293. package/templates/.agents/skills/create-pr/config/verify.json +1 -1
  294. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
  295. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
  296. package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
  297. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
  298. package/templates/.agents/skills/create-task/config/verify.json +1 -1
  299. package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
  300. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
  301. package/templates/.agents/skills/import-issue/config/verify.json +2 -2
  302. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  303. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  304. package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
  305. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
  306. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  307. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  308. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  309. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
  310. package/templates/.agents/skills/restore-task/config/verify.json +1 -1
  311. package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
  312. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  313. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
  314. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
  315. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
  316. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
  317. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  318. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  319. package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
  320. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
  321. package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
  322. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
  323. package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
  324. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  325. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
  326. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
  327. package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
  328. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
  329. package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
  330. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
  331. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
  332. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
  333. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
  334. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
  335. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
  336. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
  337. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  338. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  339. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  340. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  341. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  342. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  343. package/templates/.agents/skills/test/SKILL.en.md +17 -6
  344. package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
  345. package/templates/.agents/templates/task.en.md +1 -1
  346. package/templates/.agents/templates/task.zh-CN.md +1 -1
  347. package/templates/.claude/commands/import-issue.en.md +1 -1
  348. package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
  349. package/templates/.claude/commands/restore-task.en.md +1 -1
  350. package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
  351. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  352. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  353. package/dist/lib/platform/adapters.js +0 -56
  354. package/lib/platform/adapters.ts +0 -197
@@ -0,0 +1,217 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { inspectOwnedPath } from '../owned-path.js';
4
+ import { parseTaskFrontmatter } from '../task/frontmatter.js';
5
+ import { readTaskFinalizationReceipt } from '../task/finalization.js';
6
+ import { taskFinalizationReceiptState } from '../task/finalization-state.js';
7
+ import { enumerateAllTaskDirs } from '../task/resolve-ref.js';
8
+ import { resolveSandboxCleanupTarget } from './workspace-identity.js';
9
+ import { readSandboxControlManifest } from './control/lifecycle.js';
10
+ import { readJsonFile, readSandboxControlStatus } from './control/state.js';
11
+ import { finalizationTerminalResponse } from './control/finalization-response.js';
12
+ const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
13
+ function matchingControlManifest(repoRoot, controlRoot, taskId, binding) {
14
+ if (!controlRootIdentity(controlRoot))
15
+ return null;
16
+ const manifestPath = path.join(controlRoot, 'manifest.json');
17
+ let manifest;
18
+ try {
19
+ manifest = readSandboxControlManifest(manifestPath);
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ if (manifest.mode !== 'task-bound' || manifest.taskId !== taskId
25
+ || canonicalPath(manifest.repoRoot) !== repoRoot || manifest.generation !== binding.generation
26
+ || path.resolve(manifest.channelDir) !== path.join(controlRoot, 'channel')
27
+ || path.resolve(manifest.publicStatusDir) !== path.join(controlRoot, 'public')
28
+ || path.resolve(manifest.processingDir) !== path.join(controlRoot, 'processing')
29
+ || path.resolve(manifest.runtimeDir) !== path.join(controlRoot, 'runtime'))
30
+ return null;
31
+ return manifest;
32
+ }
33
+ function errorCode(error) {
34
+ const code = error?.code;
35
+ return typeof code === 'string' ? code : null;
36
+ }
37
+ function controlBindingKey(taskId, binding) {
38
+ return `${taskId}\0${binding.generation}\0${binding.requestId}`;
39
+ }
40
+ function controlRootIdentity(root) {
41
+ try {
42
+ const stat = fs.lstatSync(root);
43
+ if (!stat.isDirectory() || stat.isSymbolicLink())
44
+ return null;
45
+ return { dev: String(stat.dev), ino: String(stat.ino) };
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ function controlRootState(root, expected) {
52
+ let stat;
53
+ try {
54
+ stat = fs.lstatSync(root);
55
+ }
56
+ catch (error) {
57
+ return errorCode(error) === 'ENOENT' ? 'missing' : 'replaced';
58
+ }
59
+ if (stat.isSymbolicLink() || !stat.isDirectory())
60
+ return 'replaced';
61
+ return String(stat.dev) === expected.dev && String(stat.ino) === expected.ino ? 'same' : 'replaced';
62
+ }
63
+ function controlRootIsAbsent(root) {
64
+ try {
65
+ fs.lstatSync(root);
66
+ return false;
67
+ }
68
+ catch (error) {
69
+ return errorCode(error) === 'ENOENT';
70
+ }
71
+ }
72
+ function canonicalPath(input) {
73
+ try {
74
+ return fs.realpathSync.native(input);
75
+ }
76
+ catch {
77
+ return path.resolve(input);
78
+ }
79
+ }
80
+ function taskFinalizationReceiptComplete(receipt, taskId, binding) {
81
+ return receipt.taskId === taskId
82
+ && taskFinalizationReceiptState(receipt) === 'complete'
83
+ && receipt.controlBinding?.generation === binding.generation
84
+ && receipt.controlBinding.requestId === binding.requestId;
85
+ }
86
+ function terminalControlBindingEvidence(repoRootInput, controlRootInput, taskId, binding) {
87
+ const repoRoot = canonicalPath(repoRootInput);
88
+ const controlRoot = path.resolve(controlRootInput);
89
+ if (!TASK_ID_RE.test(taskId) || !controlRootIdentity(controlRoot))
90
+ return false;
91
+ const manifestPath = path.join(controlRoot, 'manifest.json');
92
+ if (!inspectOwnedPath(controlRoot, manifestPath, 'file'))
93
+ return false;
94
+ const manifest = matchingControlManifest(repoRoot, controlRoot, taskId, binding);
95
+ if (!manifest)
96
+ return false;
97
+ try {
98
+ const leasePath = path.join(controlRoot, 'lease.json');
99
+ try {
100
+ fs.lstatSync(leasePath);
101
+ return false;
102
+ }
103
+ catch (error) {
104
+ if (errorCode(error) !== 'ENOENT')
105
+ return false;
106
+ }
107
+ const receipt = readTaskFinalizationReceipt(repoRoot, taskId);
108
+ if (!receipt || !taskFinalizationReceiptComplete(receipt, taskId, binding)) {
109
+ return false;
110
+ }
111
+ const status = readSandboxControlStatus(manifest.publicStatusDir);
112
+ const viewReceipt = status.taskView.receipt;
113
+ const terminalTaskView = (status.taskView.state === 'current' && status.taskView.observedSource === 'completed')
114
+ || (status.taskView.state === 'finalized-stale' && status.taskView.observedSource === 'active');
115
+ if (status.generation !== binding.generation || status.state !== 'healthy'
116
+ || status.activeRequestId !== null || !terminalTaskView || status.taskView.taskId !== taskId
117
+ || !viewReceipt || viewReceipt.generation !== binding.generation
118
+ || viewReceipt.requestId !== binding.requestId
119
+ || viewReceipt.receiptId !== receipt.receiptId
120
+ || viewReceipt.revision !== receipt.revision)
121
+ return false;
122
+ const responsePath = path.join(manifest.channelDir, 'responses', `${binding.requestId}.json`);
123
+ if (!inspectOwnedPath(path.join(controlRoot, 'channel'), responsePath, 'file'))
124
+ return false;
125
+ const actual = readJsonFile(responsePath);
126
+ const expected = finalizationTerminalResponse(taskId, binding.requestId, receipt);
127
+ return JSON.stringify(actual) === JSON.stringify(expected);
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ }
133
+ function removedControlBindingEvidence(repoRootInput, journal, taskId, binding) {
134
+ const repoRoot = canonicalPath(repoRootInput);
135
+ if (journal.phase !== 'completed'
136
+ || journal.generation !== binding.generation
137
+ || !path.isAbsolute(journal.target.controlRoot))
138
+ return false;
139
+ const controlRoot = path.resolve(journal.target.controlRoot);
140
+ if (!controlRootIsAbsent(controlRoot))
141
+ return false;
142
+ const task = enumerateAllTaskDirs(repoRoot).find((entry) => entry.taskId === taskId);
143
+ if (!task || (task.state !== 'completed' && task.state !== 'archive'))
144
+ return false;
145
+ let frontmatter;
146
+ try {
147
+ frontmatter = parseTaskFrontmatter(fs.readFileSync(path.join(task.taskDir, 'task.md'), 'utf8'));
148
+ }
149
+ catch {
150
+ return false;
151
+ }
152
+ if (frontmatter.id !== taskId || frontmatter.branch !== journal.target.branch)
153
+ return false;
154
+ try {
155
+ const receipt = readTaskFinalizationReceipt(repoRoot, taskId);
156
+ return receipt !== null && taskFinalizationReceiptComplete(receipt, taskId, binding);
157
+ }
158
+ catch {
159
+ return false;
160
+ }
161
+ }
162
+ export function createSandboxControlBindingEvidence(repoRoot, controlRoots, removalJournals = []) {
163
+ const canonicalRepoRoot = canonicalPath(repoRoot);
164
+ const roots = [...new Set(controlRoots.map((candidate) => path.resolve(candidate)))];
165
+ const captured = roots.flatMap((controlRoot) => {
166
+ let manifest;
167
+ try {
168
+ manifest = readSandboxControlManifest(path.join(controlRoot, 'manifest.json'));
169
+ }
170
+ catch {
171
+ return [];
172
+ }
173
+ const rootIdentity = controlRootIdentity(controlRoot);
174
+ if (!rootIdentity)
175
+ return [];
176
+ const receipt = (() => {
177
+ try {
178
+ return readSandboxControlStatus(manifest.publicStatusDir).taskView.receipt;
179
+ }
180
+ catch {
181
+ return null;
182
+ }
183
+ })();
184
+ if (!receipt || !terminalControlBindingEvidence(canonicalRepoRoot, controlRoot, manifest.taskId ?? '', receipt))
185
+ return [];
186
+ return [{
187
+ controlRoot,
188
+ taskId: manifest.taskId,
189
+ key: controlBindingKey(manifest.taskId, receipt),
190
+ rootIdentity
191
+ }];
192
+ });
193
+ return (taskId, binding) => {
194
+ const terminal = captured.some((candidate) => {
195
+ if (candidate.taskId !== taskId || candidate.key !== controlBindingKey(taskId, binding))
196
+ return false;
197
+ const state = controlRootState(candidate.controlRoot, candidate.rootIdentity);
198
+ return state === 'missing'
199
+ || state === 'same' && terminalControlBindingEvidence(canonicalRepoRoot, candidate.controlRoot, taskId, binding);
200
+ }) || removalJournals.some((journal) => removedControlBindingEvidence(canonicalRepoRoot, journal, taskId, binding));
201
+ if (terminal)
202
+ return 'terminal';
203
+ if (roots.some((root) => matchingControlManifest(canonicalRepoRoot, root, taskId, binding)))
204
+ return 'pending';
205
+ try {
206
+ const branch = resolveSandboxCleanupTarget(taskId, canonicalRepoRoot, { allowProtected: true }).branch;
207
+ if (removalJournals.some((journal) => journal.phase !== 'completed'
208
+ && journal.generation === binding.generation && journal.target.branch === branch))
209
+ return 'pending';
210
+ }
211
+ catch {
212
+ // Missing or conflicting task identity cannot authorize removal.
213
+ }
214
+ return 'mismatch';
215
+ };
216
+ }
217
+ //# sourceMappingURL=task-cleanup.js.map
@@ -98,7 +98,23 @@ export function resolveSandboxCleanupTarget(requestedRef, repoRoot, options = {}
98
98
  throw new Error(`Invalid task short id '${requestedRef}': task short ids must use bare digits`);
99
99
  }
100
100
  if (TASK_ID_RE.test(requestedRef)) {
101
- const task = resolveTaskWorkspace(requestedRef, repoRoot);
101
+ let task;
102
+ try {
103
+ task = resolveTaskWorkspace(requestedRef, repoRoot);
104
+ }
105
+ catch (error) {
106
+ if (!(error instanceof Error && error.message === `Task not found: ${requestedRef}`))
107
+ throw error;
108
+ const branch = options.resolveMissingTask?.(requestedRef);
109
+ if (!branch)
110
+ throw error;
111
+ return {
112
+ requestedRef,
113
+ branch,
114
+ workspace: { mode: 'task-bound', taskId: requestedRef },
115
+ taskState: 'unknown'
116
+ };
117
+ }
102
118
  if (!options.allowProtected && (task.state === 'blocked' || task.state === 'archive')) {
103
119
  throw new Error(`SANDBOX_CLEANUP_STATE_UNSUPPORTED: task ${requestedRef} is ${task.state}`);
104
120
  }
@@ -1,6 +1,7 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
+ import { createSandboxControlIdentitySentinel, writeSandboxControlIdentitySentinel } from './control/identity-sentinel.js';
4
5
  export const SANDBOX_WORKSPACE_VIEW_STATES = Object.freeze([
5
6
  'active',
6
7
  'completed',
@@ -20,12 +21,7 @@ export function sandboxWorkspaceViewPaths(params) {
20
21
  const digest = createHash('sha256').update(identityKey).digest('hex').slice(0, 16);
21
22
  const projectRoot = path.resolve(params.base, params.project);
22
23
  const root = path.resolve(projectRoot, params.container, digest);
23
- return {
24
- root,
25
- taskMountPath: params.identity.mode === 'task-bound'
26
- ? path.join(root, 'active', params.identity.taskId)
27
- : null
28
- };
24
+ return { root };
29
25
  }
30
26
  export function sandboxControlPaths(params) {
31
27
  const identityKey = params.identity.mode === 'task-bound'
@@ -108,7 +104,7 @@ export function prepareSandboxWorkspaceMountTargets(worktreeRoot) {
108
104
  }
109
105
  export function materializeSandboxWorkspaceView(params) {
110
106
  const projectRoot = path.resolve(params.base, params.project);
111
- const { root, taskMountPath } = sandboxWorkspaceViewPaths(params);
107
+ const { root } = sandboxWorkspaceViewPaths(params);
112
108
  assertSafeDirectory(projectRoot, path.resolve(params.base));
113
109
  assertSafeDirectory(root, projectRoot);
114
110
  fs.mkdirSync(root, { recursive: true, mode: 0o700 });
@@ -128,11 +124,7 @@ export function materializeSandboxWorkspaceView(params) {
128
124
  encoding: 'utf8',
129
125
  mode: 0o600
130
126
  });
131
- if (taskMountPath) {
132
- assertSafeDirectory(taskMountPath, active);
133
- fs.mkdirSync(taskMountPath, { recursive: true, mode: 0o700 });
134
- }
135
- return { root, taskMountPath };
127
+ return { root };
136
128
  }
137
129
  export function materializeSandboxControl(params) {
138
130
  const { root, channelDir, statusDir, processingDir, runtimeDir, manifestPath } = sandboxControlPaths(params);
@@ -170,6 +162,12 @@ export function materializeSandboxControl(params) {
170
162
  }
171
163
  const token = randomBytes(32).toString('hex');
172
164
  const generation = randomBytes(16).toString('hex');
165
+ const identitySentinel = createSandboxControlIdentitySentinel({
166
+ mode: params.identity.mode,
167
+ taskId: params.identity.mode === 'task-bound' ? params.identity.taskId : null,
168
+ generation
169
+ });
170
+ writeSandboxControlIdentitySentinel(statusDir, identitySentinel);
173
171
  const repoRoot = fs.realpathSync.native(params.repoRoot);
174
172
  const manifestDraft = {
175
173
  engine: params.engine ?? 'docker',
@@ -182,12 +180,13 @@ export function materializeSandboxControl(params) {
182
180
  taskId: params.identity.mode === 'task-bound' ? params.identity.taskId : null,
183
181
  token,
184
182
  generation,
183
+ controlRootId: identitySentinel.controlRootId,
185
184
  channelDir,
186
185
  publicStatusDir: statusDir,
187
186
  processingDir,
188
- runtimeDir
187
+ runtimeDir,
189
188
  };
190
- return { root, channelDir, statusDir, runtimeDir, manifestPath, manifestDraft, token, generation };
189
+ return { root, channelDir, statusDir, runtimeDir, manifestPath, manifestDraft, token, generation, controlRootId: identitySentinel.controlRootId };
191
190
  }
192
191
  export function finalizeSandboxControlManifest(setup, identity) {
193
192
  if (!identity.engine || !identity.id)
@@ -2,7 +2,8 @@ import fs from 'node:fs';
2
2
  import { normalizeAgentToken } from '../agent-clients/tokens.js';
3
3
  import { resolveTaskRef } from './resolve-ref.js';
4
4
  import { locateActivityLog, appendActivityEntry } from './activity-log.js';
5
- import { assertWritableInventory, buildArtifactLinkSection, inspectTaskArtifacts, parseArtifactName } from './artifact-lifecycle.js';
5
+ import { assertWritableInventory, buildArtifactLinkSection, inspectTaskArtifacts } from './artifact-lifecycle.js';
6
+ import { parseArtifactName } from './artifact-name.js';
6
7
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
7
8
  import { captureTaskWriteMetadata, writeTask } from './write.js';
8
9
  import { allowsManualOverride } from './guard-override.js';
@@ -1,5 +1,4 @@
1
- const HEADING_RE = /^##\s+(活动日志|Activity Log)\s*$/;
2
- const NEXT_H2_RE = /^##\s/;
1
+ import { scanVisibleMarkdown } from './markdown.js';
3
2
  const ENTRY_RE = /^- (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}) — \*\*(.+?)\*\* by (.+?) — (.*)$/;
4
3
  const STARTED_SUFFIX_RE = /\s*\[started\]\s*$/;
5
4
  const ABORTED_SUFFIX_RE = /\s*\[aborted\]\s*$/;
@@ -10,29 +9,36 @@ function parseActivityLog(content) {
10
9
  const section = locateActivityLog(content);
11
10
  return section ? { sectionFound: true, entries: section.entries } : { sectionFound: false, entries: [] };
12
11
  }
13
- function locateActivityLog(content) {
14
- const lines = content.split('\n');
15
- const headings = lines.map((line, index) => HEADING_RE.test(line) ? index : -1).filter((index) => index >= 0);
16
- if (headings.length !== 1)
17
- return null;
18
- const i = headings[0];
19
- let end = lines.length;
20
- for (let j = i + 1; j < lines.length; j += 1) {
21
- if (NEXT_H2_RE.test(lines[j])) {
22
- end = j;
23
- break;
24
- }
25
- }
26
- const bodyLines = lines.slice(i + 1, end);
27
- const parsed = [];
28
- for (const line of bodyLines) {
29
- const m = ENTRY_RE.exec(line);
30
- if (!m)
12
+ /** Returns source-order entries and malformed bullets without applying display or gate policy. */
13
+ function inspectActivityLog(content) {
14
+ const scanned = scanVisibleMarkdown(content);
15
+ const headings = scanned.headings.filter((heading) => heading.level === 2);
16
+ const matches = headings.filter((heading) => heading.text === '活动日志' || heading.text === 'Activity Log');
17
+ if (matches.length !== 1)
18
+ return { section: null, invalidEntries: [] };
19
+ const heading = matches[0];
20
+ const end = headings.find((candidate) => candidate.start > heading.start)?.start ?? content.length;
21
+ const entries = [];
22
+ const invalidEntries = [];
23
+ for (const line of scanned.lines) {
24
+ if (line.start <= heading.end || line.start >= end)
31
25
  continue;
32
- parsed.push({ entry: { time: m[1], step: m[2], agent: m[3], note: m[4] }, epoch: Date.parse(m[1].replace(' ', 'T')), order: parsed.length });
26
+ const text = line.text.trimStart();
27
+ const match = ENTRY_RE.exec(text);
28
+ if (match)
29
+ entries.push({ time: match[1], step: match[2], agent: match[3], note: match[4] });
30
+ else if (text.startsWith('- '))
31
+ invalidEntries.push(text);
33
32
  }
34
- parsed.sort((a, b) => a.epoch - b.epoch || a.order - b.order);
35
- return { heading: HEADING_RE.exec(lines[i])[1], body: bodyLines.join('\n').replace(/^\n+|\n+$/g, ''), entries: parsed.map((item) => item.entry) };
33
+ const body = content.slice(heading.end + 1, end).replace(/\r\n/g, '\n').replace(/^\n+|\n+$/g, '');
34
+ return { section: { heading: heading.text, body, entries }, invalidEntries };
35
+ }
36
+ function locateActivityLog(content) {
37
+ const { section } = inspectActivityLog(content);
38
+ if (!section)
39
+ return null;
40
+ section.entries.sort((left, right) => Date.parse(left.time.replace(' ', 'T')) - Date.parse(right.time.replace(' ', 'T')));
41
+ return section;
36
42
  }
37
43
  function appendActivityEntry(section, entry) {
38
44
  const line = `- ${entry.time} — **${entry.step}** by ${entry.agent} — ${entry.note}`;
@@ -94,5 +100,5 @@ function hasOpenLifecycleExecution(content) {
94
100
  return false;
95
101
  return pairEntries(section.entries).some((row) => row.done === '' && LIFECYCLE_STEP_RE.test(row.step));
96
102
  }
97
- export { activityAttempt, appendActivityEntry, hasOpenLifecycleExecution, commitAttemptStartedNote, locateActivityLog, pairEntries, parseActivityLog, parseCommitAttemptStarted, startedBackedRows };
103
+ export { activityAttempt, appendActivityEntry, hasOpenLifecycleExecution, commitAttemptStartedNote, inspectActivityLog, locateActivityLog, pairEntries, parseActivityLog, parseCommitAttemptStarted, startedBackedRows };
98
104
  //# sourceMappingURL=activity-log.js.map
@@ -0,0 +1,79 @@
1
+ import fs from 'node:fs';
2
+ import { resolveArtifactContext, hasOpenArtifactRound } from './artifact-lifecycle.js';
3
+ import { parseArtifactName } from './artifact-name.js';
4
+ import { resolveTaskRef } from './resolve-ref.js';
5
+ import { getArtifactSchema } from './artifact-schema.js';
6
+ import { finalizeLocalArtifact } from './local-artifact-finalization.js';
7
+ import { initializeArtifactSkeleton } from './artifact-operations.js';
8
+ /** One option contract for the CLI and trusted projection executor. */
9
+ export function parseArtifactCommand(args) {
10
+ const [taskRef, operation] = args;
11
+ if (!taskRef || !['inspect', 'init', 'finalize-local'].includes(operation ?? '')) {
12
+ throw new Error('task ref and a supported artifact operation are required');
13
+ }
14
+ const fields = {};
15
+ const allowed = {
16
+ inspect: ['--family'],
17
+ init: ['--family', '--artifact', '--locale'],
18
+ 'finalize-local': ['--family', '--artifact', '--recovery-id']
19
+ }[operation];
20
+ for (let index = 2; index < args.length; index += 1) {
21
+ const flag = args[index];
22
+ if (!allowed.includes(flag))
23
+ throw new Error(`unknown option '${flag}'`);
24
+ if (Object.hasOwn(fields, flag))
25
+ throw new Error(`duplicate option '${flag}'`);
26
+ const value = args[++index];
27
+ if (!value || value.startsWith('--'))
28
+ throw new Error(`option '${flag}' requires a value`);
29
+ fields[flag] = value;
30
+ }
31
+ if (!fields['--family'])
32
+ throw new Error("option '--family' is required");
33
+ if (operation !== 'inspect' && !fields['--artifact'])
34
+ throw new Error("option '--artifact' is required");
35
+ const locale = fields['--locale'];
36
+ if (locale !== undefined && locale !== 'zh-CN' && locale !== 'en')
37
+ throw new Error("option '--locale' must be 'zh-CN' or 'en'");
38
+ return {
39
+ taskRef, operation: operation, family: fields['--family'],
40
+ artifact: fields['--artifact'] ?? '', locale, recoveryId: fields['--recovery-id']
41
+ };
42
+ }
43
+ /** Resolve authoritative task metadata separately from a trusted candidate directory. */
44
+ export function executeArtifactCommand(command, options = {}) {
45
+ const { taskRef, operation, family, artifact, locale, recoveryId } = command;
46
+ const fail = (code, message) => ({ status: 'failed', changed: false, error: { code, message } });
47
+ if (operation === 'inspect') {
48
+ const result = resolveArtifactContext(taskRef, family, { repoRoot: options.repoRoot });
49
+ return family === 'code' && result.codeMode ? {
50
+ ...result, mode: result.codeMode.mode, code_max: result.codeMode.codeMax, rev_max: result.codeMode.reviewMax,
51
+ verdict: result.codeMode.verdict, next_round: result.next?.round ?? null, next_artifact: result.next?.name ?? null,
52
+ review_artifact: result.codeMode.reviewArtifact, implementation_input: result.codeMode.implementationInput,
53
+ decision_id: result.codeMode.decisionId, decision_evidence: result.codeMode.decisionEvidence, message: result.codeMode.message
54
+ } : { ...result };
55
+ }
56
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
57
+ if (!resolved.ok)
58
+ return fail(resolved.code, resolved.message);
59
+ const schema = getArtifactSchema(family);
60
+ const parsed = parseArtifactName(artifact);
61
+ if (!schema || !parsed || parsed.family !== family) {
62
+ return fail('ARTIFACT_IDENTITY_INVALID', `artifact '${artifact}' does not match ${family}`);
63
+ }
64
+ const taskDir = options.artifactDir ?? resolved.taskDir;
65
+ const identity = { taskId: resolved.taskId, taskDir, family, artifact };
66
+ const input = { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskDir, family: schema.family, artifact };
67
+ if (operation === 'init') {
68
+ const context = resolveArtifactContext(taskRef, family, { repoRoot: resolved.repoRoot });
69
+ if (context.status !== 'ready' || (context.next?.name !== artifact && !hasOpenArtifactRound(fs.readFileSync(resolved.taskMdPath, 'utf8'), family, parsed.round))) {
70
+ return fail('ARTIFACT_INIT_CONTEXT_INVALID', context.error?.message ?? `artifact '${artifact}' is not the next ${family} artifact`);
71
+ }
72
+ return { ...initializeArtifactSkeleton({ ...input, ...(locale ? { locale } : {}) }), ...identity };
73
+ }
74
+ if (family !== 'analysis' && family !== 'plan' && family !== 'code') {
75
+ return fail('ARTIFACT_PAYLOAD_INVALID', "finalize-local only supports 'analysis', 'plan', and 'code'");
76
+ }
77
+ return { ...finalizeLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot, recoveryId }) };
78
+ }
79
+ //# sourceMappingURL=artifact-command.js.map
@@ -1,16 +1,32 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { ARTIFACT_FAMILY_CATALOG, artifactName, parseArtifactName } from './artifact-name.js';
3
4
  import { resolveTaskRef } from './resolve-ref.js';
4
- import { locateActivityLog } from './activity-log.js';
5
+ import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
5
6
  import { parseImplementationInputs, selectPendingImplementationInput } from './implementation-inputs.js';
6
7
  import { parseVerdict } from './review-artifacts.js';
7
8
  import { extractSection, findSectionHeading } from './sections.js';
8
9
  import { receiptForOutput, sha256File } from './artifact-receipts.js';
9
10
  import { isArtifactInvalidated, parseInvalidationDocument } from './invalidation.js';
10
11
  import { validateQualificationAudit } from './qualification-audit.js';
11
- import { ARTIFACT_FAMILY_CATALOG } from './artifact-schema.js';
12
12
  const artifactFamilyCatalog = ARTIFACT_FAMILY_CATALOG;
13
- const FAMILY_SET = new Set(artifactFamilyCatalog.map((item) => item.family));
13
+ const ARTIFACT_STEPS = {
14
+ analysis: 'Analyze Task', 'review-analysis': 'Review Analysis',
15
+ plan: 'Plan Task', 'review-plan': 'Review Plan',
16
+ code: 'Code Task', 'review-code': 'Review Code'
17
+ };
18
+ /** Init, repair and finalization share the same authoritative round identity. */
19
+ export function hasOpenArtifactRound(content, family, round) {
20
+ const activity = locateActivityLog(content);
21
+ const step = ARTIFACT_STEPS[family];
22
+ if (!activity || !step)
23
+ return false;
24
+ const qualifier = family === 'code'
25
+ ? '(?:, (?:fix for review-code(?:-r(?:[2-9]|[1-9]\\d+))?\\.md|decision II-[1-9]\\d*))?'
26
+ : '';
27
+ const expected = new RegExp(`^${step} \\(Round ${round}${qualifier}\\)$`);
28
+ return startedBackedRows(pairEntries(activity.entries)).filter((row) => expected.test(row.step) && !row.done).length === 1;
29
+ }
14
30
  const BLOCKING_DIAGNOSTICS = new Set([
15
31
  'NONCANONICAL_NAME', 'ROUND_OUT_OF_RANGE', 'MISSING_BASE', 'ROUND_GAP',
16
32
  'DUPLICATE_LOGICAL_ROUND', 'NON_REGULAR_FILE', 'SYMBOLIC_LINK',
@@ -19,30 +35,6 @@ const BLOCKING_DIAGNOSTICS = new Set([
19
35
  function familySpec(family) {
20
36
  return artifactFamilyCatalog.find((item) => item.family === family) ?? null;
21
37
  }
22
- function artifactName(family, round) {
23
- if (!FAMILY_SET.has(family))
24
- throw new Error(`unknown artifact family '${family}'`);
25
- if (!Number.isSafeInteger(round) || round < 1)
26
- throw new Error('artifact round must be a safe positive integer');
27
- return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
28
- }
29
- function parseArtifactName(name) {
30
- if (path.basename(name) !== name)
31
- return null;
32
- const matches = [];
33
- for (const spec of artifactFamilyCatalog) {
34
- if (name === `${spec.family}.md`)
35
- matches.push({ family: spec.family, round: 1, name });
36
- const match = new RegExp(`^${escapeRegExp(spec.family)}-r([1-9]\\d*)\\.md$`).exec(name);
37
- if (!match)
38
- continue;
39
- const round = Number(match[1]);
40
- if (Number.isSafeInteger(round) && round >= 2 && String(round) === match[1]) {
41
- matches.push({ family: spec.family, round, name });
42
- }
43
- }
44
- return matches.length === 1 ? matches[0] : null;
45
- }
46
38
  function escapeRegExp(value) {
47
39
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
48
40
  }
@@ -478,6 +470,16 @@ function validateCompletedArtifact(taskDir, family, name, round) {
478
470
  const artifact = inventory.artifacts.find((item) => item.name === name);
479
471
  return artifact ? { ok: true, artifact } : { ok: false, error: { code: 'ARTIFACT_NOT_FOUND', message: `artifact '${name}' is not in inventory` } };
480
472
  }
473
+ /** A candidate may replace its current slot or fill the next slot, never skip rounds. */
474
+ export function validateArtifactPublication(taskDir, family, name) {
475
+ const inventory = inspectArtifactDirectory(taskDir, family);
476
+ const error = assertWritableInventory(inventory);
477
+ if (error)
478
+ return error;
479
+ if (inventory.next?.name === name || inventory.artifacts.some((artifact) => artifact.name === name))
480
+ return null;
481
+ return { code: 'ARTIFACT_IDENTITY_INVALID', message: `artifact '${name}' is not a current or next ${family} slot` };
482
+ }
481
483
  function buildArtifactLinkSection(content, artifact) {
482
484
  const spec = familySpec(artifact.family);
483
485
  const aliases = [...spec.sectionAliases];
@@ -507,5 +509,5 @@ function buildArtifactLinkSection(content, artifact) {
507
509
  body = lines.join('\n').replace(/^\n+|\n+$/g, '');
508
510
  return { aliases, heading, body };
509
511
  }
510
- export { artifactFamilyCatalog, artifactName, parseArtifactName, familySpec, inspectTaskArtifacts, inspectArtifactDirectory, resolveArtifactContext, parseReviewedInputReference, parseCodePlanInputReference, resolveCodePlanInput, reviewEventName, assertWritableInventory, validateCompletedArtifact, buildArtifactLinkSection };
512
+ export { artifactFamilyCatalog, familySpec, inspectTaskArtifacts, inspectArtifactDirectory, resolveArtifactContext, parseReviewedInputReference, parseCodePlanInputReference, resolveCodePlanInput, reviewEventName, assertWritableInventory, validateCompletedArtifact, buildArtifactLinkSection };
511
513
  //# sourceMappingURL=artifact-lifecycle.js.map
@@ -0,0 +1,36 @@
1
+ const ARTIFACT_FAMILY_CATALOG = [
2
+ { family: 'analysis', sectionAliases: ['分析', 'Analysis'], heading: '分析', labels: ['需求分析报告', 'Requirements Analysis'] },
3
+ { family: 'review-analysis', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['需求分析审查', 'Analysis Review'] },
4
+ { family: 'plan', sectionAliases: ['设计', 'Design'], heading: '设计', labels: ['技术方案', 'Technical Plan'] },
5
+ { family: 'review-plan', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['技术方案审查', 'Plan Review'] },
6
+ { family: 'code', sectionAliases: ['实现备注', 'Implementation Notes'], heading: '实现备注', labels: ['实现报告', 'Implementation Report'] },
7
+ { family: 'review-code', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['代码审查', 'Code Review'] },
8
+ { family: 'manual-validation', sectionAliases: ['实现备注', 'Implementation Notes'], heading: '实现备注', labels: ['人工验证', 'Manual Validation'] },
9
+ { family: 'validation-run', sectionAliases: ['实现备注', 'Implementation Notes'], heading: '实现备注', labels: ['验证运行证据', 'Validation Run Evidence'] },
10
+ { family: 'pr-review', sectionAliases: ['审查反馈', 'Review Feedback'], heading: '审查反馈', labels: ['PR 审查报告', 'PR Review Report'] }
11
+ ];
12
+ const FAMILIES = new Set(ARTIFACT_FAMILY_CATALOG.map(({ family }) => family));
13
+ export function artifactName(family, round) {
14
+ if (!FAMILIES.has(family))
15
+ throw new Error(`unknown artifact family '${family}'`);
16
+ if (!Number.isSafeInteger(round) || round < 1)
17
+ throw new Error('artifact round must be a safe positive integer');
18
+ return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
19
+ }
20
+ export function parseArtifactName(name) {
21
+ const match = /^(.+?)(?:-r([1-9]\d*))?\.md$/.exec(name);
22
+ if (!match || match[0] !== name || !FAMILIES.has(match[1]))
23
+ return null;
24
+ const round = match[2] ? Number(match[2]) : 1;
25
+ if (!Number.isSafeInteger(round) || (match[2] && round < 2))
26
+ return null;
27
+ return { family: match[1], round, name };
28
+ }
29
+ export function maxArtifactRound(names, family) {
30
+ return names.reduce((max, name) => {
31
+ const identity = parseArtifactName(name);
32
+ return identity?.family === family ? Math.max(max, identity.round) : max;
33
+ }, 0);
34
+ }
35
+ export { ARTIFACT_FAMILY_CATALOG };
36
+ //# sourceMappingURL=artifact-name.js.map