@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
@@ -1,30 +1,20 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { createHash } from 'node:crypto';
4
+ import { readStableFileSync } from '../host-control/secure-fs.ts';
5
+ import { parseArtifactName } from './artifact-name.ts';
4
6
 
5
- import { scanVisibleMarkdown } from './decision-details.ts';
6
- import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.ts';
7
- import { readArtifactRepairIntent } from './artifact-repair-intent.ts';
7
+ import { scanVisibleMarkdown, type VisibleMarkdown, type VisibleHeading } from './markdown.ts';
8
8
  import {
9
9
  getArtifactSchema,
10
10
  renderArtifactSkeleton
11
11
  } from './artifact-schema.ts';
12
+ import { withTaskExecutionLock } from './task-execution-lock.ts';
12
13
  import type {
13
14
  ArtifactSchema,
14
15
  ArtifactSchemaFamily,
15
16
  ArtifactSection
16
17
  } from './artifact-schema.ts';
17
- import { withTaskExecutionLock } from './task-execution-lock.ts';
18
-
19
- type ArtifactRepairOperation = Readonly<{
20
- kind: 'replace-line' | 'insert-section';
21
- sectionId: string;
22
- line: number;
23
- from: string;
24
- to: string;
25
- start: number;
26
- end: number;
27
- }>;
28
18
 
29
19
  type ArtifactStructuralDiagnosticCode =
30
20
  | 'ARTIFACT_EMPTY'
@@ -44,8 +34,6 @@ type ArtifactStructuralDiagnostic = Readonly<{
44
34
  message: string;
45
35
  sectionId: string | null;
46
36
  line: number | null;
47
- repairable: boolean;
48
- operation?: ArtifactRepairOperation;
49
37
  }>;
50
38
 
51
39
  type ArtifactStructureResult = Readonly<{
@@ -53,7 +41,6 @@ type ArtifactStructureResult = Readonly<{
53
41
  family: ArtifactSchemaFamily;
54
42
  semanticDigest: string;
55
43
  diagnostics: readonly ArtifactStructuralDiagnostic[];
56
- repair: ArtifactRepairOperation | null;
57
44
  }>;
58
45
 
59
46
  type ArtifactFileResult = Readonly<{
@@ -61,21 +48,9 @@ type ArtifactFileResult = Readonly<{
61
48
  changed: boolean;
62
49
  artifactSha256: string | null;
63
50
  semanticDigest: string | null;
64
- operation: ArtifactRepairOperation | null;
65
51
  error: { code: string; message: string } | null;
66
52
  }>;
67
53
 
68
- type ArtifactRepairRequest = Readonly<{
69
- repoRoot: string;
70
- taskId: string;
71
- taskDir: string;
72
- family: ArtifactSchemaFamily;
73
- artifact: string;
74
- expectedSha256: string;
75
- expectedSemanticDigest: string;
76
- operation: ArtifactRepairOperation;
77
- }>;
78
-
79
54
  type ArtifactInitRequest = Readonly<{
80
55
  repoRoot: string;
81
56
  taskId: string;
@@ -89,35 +64,6 @@ function sha256Content(content: string): string {
89
64
  return createHash('sha256').update(content, 'utf8').digest('hex');
90
65
  }
91
66
 
92
- function sourceLines(content: string): Array<{ start: number; end: number; text: string }> {
93
- const lines: Array<{ start: number; end: number; text: string }> = [];
94
- let start = 0;
95
- while (start < content.length) {
96
- const newline = content.indexOf('\n', start);
97
- const end = newline === -1 ? content.length : newline;
98
- lines.push({ start, end, text: content.slice(start, end).replace(/\r$/, '') });
99
- start = newline === -1 ? content.length : newline + 1;
100
- }
101
- if (content.length === 0) lines.push({ start: 0, end: 0, text: '' });
102
- return lines;
103
- }
104
-
105
- function hasUnclosedFence(content: string): boolean {
106
- let fence: { character: '`' | '~'; length: number } | null = null;
107
- for (const line of sourceLines(content)) {
108
- if (fence) {
109
- const close = line.text.match(/^ {0,3}(`{3,}|~{3,})\s*$/);
110
- if (close && close[1]![0] === fence.character && close[1]!.length >= fence.length) fence = null;
111
- continue;
112
- }
113
- const open = line.text.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
114
- if (open && !(open[1]![0] === '`' && open[2]!.includes('`'))) {
115
- fence = { character: open[1]![0] as '`' | '~', length: open[1]!.length };
116
- }
117
- }
118
- return fence !== null;
119
- }
120
-
121
67
  function lineNumber(content: string, offset: number): number {
122
68
  return content.slice(0, offset).split('\n').length;
123
69
  }
@@ -130,9 +76,9 @@ function escapeRegExp(value: string): string {
130
76
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
131
77
  }
132
78
 
133
- function markerEntries(content: string): Array<{ marker: string; start: number; end: number; line: number; standalone: boolean }> {
79
+ function markerEntries(content: string, scanned: VisibleMarkdown): Array<{ marker: string; start: number; end: number; line: number; standalone: boolean }> {
134
80
  const entries: Array<{ marker: string; start: number; end: number; line: number; standalone: boolean }> = [];
135
- for (const line of scanVisibleMarkdown(content).lines) {
81
+ for (const line of scanned.lines) {
136
82
  const match = line.text.match(/<!--\s*(artifact-section:[^\s>]+)\s*-->/g);
137
83
  if (!match) continue;
138
84
  for (const raw of match) {
@@ -150,12 +96,9 @@ function markerEntries(content: string): Array<{ marker: string; start: number;
150
96
  return entries;
151
97
  }
152
98
 
153
- function sectionBodyBounds(content: string, heading: { start: number }): { start: number; end: number } {
154
- const next = scanVisibleMarkdown(content).headings.find((candidate) => (
155
- candidate.start > heading.start && candidate.level <= 2
156
- ));
157
- const headingEnd = scanVisibleMarkdown(content).headings.find((candidate) => candidate.start === heading.start)?.end ?? heading.start;
158
- return { start: headingEnd, end: next?.start ?? content.length };
99
+ function sectionBodyBounds(content: string, heading: VisibleHeading, scanned: VisibleMarkdown): { start: number; end: number } {
100
+ const next = scanned.headings.find((candidate) => candidate.start > heading.start && candidate.level <= 2);
101
+ return { start: heading.end, end: next?.start ?? content.length };
159
102
  }
160
103
 
161
104
  function stripHtmlComments(content: string): string {
@@ -176,71 +119,17 @@ function diagnostic(
176
119
  code: ArtifactStructuralDiagnosticCode,
177
120
  message: string,
178
121
  sectionId: string | null,
179
- line: number | null,
180
- repairable = false,
181
- operation?: ArtifactRepairOperation
122
+ line: number | null
182
123
  ): ArtifactStructuralDiagnostic {
183
- return {
184
- code, message, sectionId, line, repairable,
185
- ...(operation ? { operation } : {})
186
- };
124
+ return { code, message, sectionId, line };
187
125
  }
188
126
 
189
127
  function sectionHeadings(schema: ArtifactSchema, section: ArtifactSection): readonly string[] {
190
128
  return [section.headings.zh, section.headings.en];
191
129
  }
192
130
 
193
- function createHeadingRepair(
194
- content: string,
195
- section: ArtifactSection,
196
- heading: { start: number; end: number; text: string }
197
- ): ArtifactRepairOperation {
198
- const to = heading.text.slice(0, -1);
199
- return {
200
- kind: 'replace-line',
201
- sectionId: section.id,
202
- line: lineNumber(content, heading.start),
203
- from: heading.text,
204
- to,
205
- start: heading.start,
206
- end: heading.end
207
- };
208
- }
209
-
210
- function createSectionInsertionRepair(
211
- content: string,
212
- section: ArtifactSection,
213
- marker: { start: number; line: number },
214
- locale: 'zh-CN' | 'en'
215
- ): ArtifactRepairOperation {
216
- return {
217
- kind: 'insert-section',
218
- sectionId: section.id,
219
- line: marker.line,
220
- from: '',
221
- to: `## ${section.headings[locale === 'en' ? 'en' : 'zh']}\n`,
222
- start: marker.start,
223
- end: marker.start
224
- };
225
- }
226
-
227
- function applyRepairOperation(content: string, operation: ArtifactRepairOperation): string | null {
228
- if (operation.start < 0 || operation.end < operation.start || operation.end > content.length) return null;
229
- if (operation.kind === 'insert-section') {
230
- if (operation.from !== '' || operation.start !== operation.end || !operation.to) return null;
231
- return `${content.slice(0, operation.start)}${operation.to}${content.slice(operation.end)}`;
232
- }
233
- const raw = content.slice(operation.start, operation.end);
234
- const offset = raw.indexOf(operation.from);
235
- if (!operation.from || offset < 0 || raw.indexOf(operation.from, offset + operation.from.length) >= 0) return null;
236
- return `${content.slice(0, operation.start)}${raw.slice(0, offset)}${operation.to}${raw.slice(offset + operation.from.length)}${content.slice(operation.end)}`;
237
- }
238
-
239
- function canonicalContent(content: string, operation: ArtifactRepairOperation | null = null): string {
131
+ function canonicalContent(content: string): string {
240
132
  let normalized = content;
241
- if (operation) {
242
- normalized = applyRepairOperation(normalized, operation) ?? normalized;
243
- }
244
133
  normalized = normalized.replace(/\r\n/g, '\n');
245
134
  normalized = normalized.replace(/^<!--\s*artifact-context:[^\n]+-->\s*\n?/gm, '');
246
135
  normalized = normalized.replace(/^\s*<!--\s*artifact-section:[^\n]+-->\s*\n?/gm, '');
@@ -248,8 +137,8 @@ function canonicalContent(content: string, operation: ArtifactRepairOperation |
248
137
  return normalized;
249
138
  }
250
139
 
251
- function canonicalSemanticDigest(content: string, operation: ArtifactRepairOperation | null = null): string {
252
- return sha256Content(canonicalContent(content, operation));
140
+ function canonicalSemanticDigest(content: string): string {
141
+ return sha256Content(canonicalContent(content));
253
142
  }
254
143
 
255
144
  function inspectArtifactStructure(
@@ -258,16 +147,15 @@ function inspectArtifactStructure(
258
147
  ): ArtifactStructureResult {
259
148
  const diagnostics: ArtifactStructuralDiagnostic[] = [];
260
149
  const scanned = scanVisibleMarkdown(content);
261
- const repairCandidates: ArtifactRepairOperation[] = [];
262
150
  if (!content.trim()) diagnostics.push(diagnostic('ARTIFACT_EMPTY', 'artifact is empty', null, null));
263
- if (hasUnclosedFence(content)) diagnostics.push(diagnostic('ARTIFACT_UNCLOSED_FENCE', 'artifact contains an unclosed Markdown fence', null, null));
151
+ if (scanned.hasUnclosedFence) diagnostics.push(diagnostic('ARTIFACT_UNCLOSED_FENCE', 'artifact contains an unclosed Markdown fence', null, null));
264
152
 
265
153
  const headingsBySection = new Map<string, typeof scanned.headings>();
266
154
  for (const section of schema.sections) {
267
155
  const aliases = sectionHeadings(schema, section);
268
156
  const exact = scanned.headings.filter((heading) => heading.level === 2 && aliases.includes(heading.text));
269
157
  const punctuated = scanned.headings.filter((heading) => heading.level === 2 && aliases.some((alias) => heading.text === `${alias}:` || heading.text === `${alias}:`));
270
- headingsBySection.set(section.id, exact.length === 0 && punctuated.length === 1 ? punctuated : exact);
158
+ headingsBySection.set(section.id, exact.length > 0 ? exact : punctuated);
271
159
  if (exact.length > 1 || punctuated.length > 0 && exact.length > 0 || punctuated.length > 1) {
272
160
  const first = exact[1] ?? punctuated[0] ?? exact[0];
273
161
  diagnostics.push(diagnostic(
@@ -277,16 +165,18 @@ function inspectArtifactStructure(
277
165
  first ? lineNumber(content, first.start) : null
278
166
  ));
279
167
  } else if (exact.length === 0 && punctuated.length === 1) {
280
- repairCandidates.push(createHeadingRepair(content, section, punctuated[0]!));
168
+ diagnostics.push(diagnostic(
169
+ 'ARTIFACT_HEADING_TRAILING_PUNCTUATION',
170
+ `visible required H2 '${punctuated[0]!.text}' has trailing punctuation`,
171
+ section.id,
172
+ lineNumber(content, punctuated[0]!.start)
173
+ ));
281
174
  } else if (exact.length === 0) {
282
175
  diagnostics.push(diagnostic('ARTIFACT_MISSING_SECTION', `required section '${aliases[0]}' is missing`, section.id, null));
283
176
  }
284
177
  }
285
178
 
286
- const markers = markerEntries(content);
287
- const englishHeadings = scanned.headings.filter((heading) => heading.level === 2 && schema.sections.some((section) => section.headings.en === heading.text)).length;
288
- const chineseHeadings = scanned.headings.filter((heading) => heading.level === 2 && schema.sections.some((section) => section.headings.zh === heading.text)).length;
289
- const locale = englishHeadings > chineseHeadings ? 'en' : 'zh-CN';
179
+ const markers = markerEntries(content, scanned);
290
180
  for (const section of schema.sections) {
291
181
  const matches = markers.filter((entry) => entry.marker === section.marker);
292
182
  if (matches.length === 0) {
@@ -300,7 +190,7 @@ function inspectArtifactStructure(
300
190
  diagnostics.push(diagnostic('ARTIFACT_MARKER_MISMATCH', `section marker '${section.marker}' must occupy its own visible line`, section.id, marker.line));
301
191
  }
302
192
  if (marker && heading) {
303
- const bounds = sectionBodyBounds(content, heading);
193
+ const bounds = sectionBodyBounds(content, heading, scanned);
304
194
  if (marker.start < bounds.start || marker.start >= bounds.end) {
305
195
  diagnostics.push(diagnostic('ARTIFACT_MARKER_MISMATCH', `section marker '${section.marker}' is outside its section`, section.id, marker.line));
306
196
  }
@@ -310,9 +200,7 @@ function inspectArtifactStructure(
310
200
  if (marker && !heading && marker.standalone) {
311
201
  const nextHeading = scanned.headings.find((candidate) => candidate.start > marker.start && candidate.level <= 2);
312
202
  const body = stripHtmlComments(content.slice(marker.end, nextHeading?.start ?? content.length)).trim();
313
- if (body && repairCandidates.length === 0) {
314
- repairCandidates.push(createSectionInsertionRepair(content, section, marker, locale));
315
- } else if (!body) {
203
+ if (!body) {
316
204
  diagnostics.push(diagnostic('ARTIFACT_EMPTY_SECTION', `section '${section.headings.zh}' has no semantic body`, section.id, marker.line));
317
205
  }
318
206
  }
@@ -323,32 +211,6 @@ function inspectArtifactStructure(
323
211
  }
324
212
  }
325
213
 
326
- for (const candidate of repairCandidates) {
327
- if (candidate.kind === 'replace-line') {
328
- diagnostics.push(diagnostic(
329
- 'ARTIFACT_HEADING_TRAILING_PUNCTUATION',
330
- `visible required H2 '${candidate.from}' may be normalized to '${candidate.to}'`,
331
- candidate.sectionId,
332
- candidate.line,
333
- true,
334
- candidate
335
- ));
336
- continue;
337
- }
338
- const missing = diagnostics.findIndex((item) => item.code === 'ARTIFACT_MISSING_SECTION' && item.sectionId === candidate.sectionId);
339
- if (missing >= 0) {
340
- const section = schema.sections.find((item) => item.id === candidate.sectionId)!;
341
- diagnostics.splice(missing, 1, diagnostic(
342
- 'ARTIFACT_MISSING_SECTION',
343
- `required section '${section.headings.zh}' is missing and can be inserted at its unique marker`,
344
- candidate.sectionId,
345
- candidate.line,
346
- true,
347
- candidate
348
- ));
349
- }
350
- }
351
-
352
214
  const orderedHeadings = schema.sections.flatMap((section) => {
353
215
  const heading = headingsBySection.get(section.id)?.[0];
354
216
  return heading ? [{ section, start: heading.start }] : [];
@@ -364,16 +226,11 @@ function inspectArtifactStructure(
364
226
  diagnostics.push(diagnostic('ARTIFACT_SECTION_ORDER_INVALID', 'section markers are not in schema order', null, null));
365
227
  }
366
228
 
367
- const repair = repairCandidates.length === 1 && (
368
- diagnostics.length === 0 ||
369
- (diagnostics.length === 1 && diagnostics[0]?.operation?.kind === repairCandidates[0]?.kind)
370
- ) ? repairCandidates[0]! : null;
371
229
  return {
372
230
  ok: diagnostics.length === 0,
373
231
  family: schema.family,
374
- semanticDigest: canonicalSemanticDigest(content, repair),
375
- diagnostics,
376
- repair
232
+ semanticDigest: canonicalSemanticDigest(content),
233
+ diagnostics
377
234
  };
378
235
  }
379
236
 
@@ -393,8 +250,7 @@ function inspectArtifactPatterns(
393
250
  ok: diagnostics.length === 0,
394
251
  family: schema.family,
395
252
  semanticDigest: canonicalSemanticDigest(content),
396
- diagnostics,
397
- repair: null
253
+ diagnostics
398
254
  };
399
255
  }
400
256
 
@@ -405,18 +261,16 @@ function inspectArtifactContract(
405
261
  const structure = inspectArtifactStructure(content, schema);
406
262
  const patterns = inspectArtifactPatterns(content, schema);
407
263
  const diagnostics = [...structure.diagnostics, ...patterns.diagnostics];
408
- const repair = patterns.diagnostics.length === 0 ? structure.repair : null;
409
264
  return {
410
265
  ok: diagnostics.length === 0,
411
266
  family: schema.family,
412
- semanticDigest: canonicalSemanticDigest(content, repair),
413
- diagnostics,
414
- repair
267
+ semanticDigest: canonicalSemanticDigest(content),
268
+ diagnostics
415
269
  };
416
270
  }
417
271
 
418
272
  function resultFailure(code: string, message: string): ArtifactFileResult {
419
- return { status: 'failed', changed: false, artifactSha256: null, semanticDigest: null, operation: null, error: { code, message } };
273
+ return { status: 'failed', changed: false, artifactSha256: null, semanticDigest: null, error: { code, message } };
420
274
  }
421
275
 
422
276
  function resultNoOp(content: string): ArtifactFileResult {
@@ -425,139 +279,13 @@ function resultNoOp(content: string): ArtifactFileResult {
425
279
  changed: false,
426
280
  artifactSha256: sha256Content(content),
427
281
  semanticDigest: canonicalSemanticDigest(content),
428
- operation: null,
429
282
  error: null
430
283
  };
431
284
  }
432
285
 
433
- function validateRepairContext(request: ArtifactRepairRequest, content: string): { ok: true } | { ok: false; code: string; message: string } {
434
- const round = artifactRound(request.family, request.artifact);
435
- if (round === null) return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'artifact round is not canonical' };
436
- const contextPattern = /^<!--\s*artifact-context:([^:\s]+):([^:\s]+):(\d+)\s*-->\s*$/;
437
- const contexts = scanVisibleMarkdown(content).lines
438
- .map((line) => line.text.trim().match(contextPattern))
439
- .filter((match): match is RegExpMatchArray => match !== null);
440
- if (contexts.length !== 1
441
- || contexts[0]![1] !== request.taskId
442
- || contexts[0]![2] !== request.family
443
- || Number(contexts[0]![3]) !== round) {
444
- return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'artifact context marker does not match the requested task, family, and round' };
445
- }
446
- let taskContent: string;
447
- try { taskContent = fs.readFileSync(path.join(request.taskDir, 'task.md'), 'utf8'); }
448
- catch (error) { return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: `cannot read task lifecycle context: ${String(error)}` }; }
449
- const activity = locateActivityLog(taskContent);
450
- if (!activity) return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: 'task has no unique Activity Log section' };
451
- const label: Record<ArtifactSchemaFamily, string> = {
452
- analysis: 'Analyze Task',
453
- 'review-analysis': 'Review Analysis',
454
- plan: 'Plan Task',
455
- 'review-plan': 'Review Plan',
456
- code: 'Code Task',
457
- 'review-code': 'Review Code'
458
- };
459
- const escaped = escapeRegExp(label[request.family]);
460
- const qualifier = request.family === 'code'
461
- ? '(?:, (?:fix for review-code(?:-r(?:[2-9]|[1-9]\\d+))?\\.md|decision II-[1-9]\\d+))?'
462
- : '';
463
- const expected = new RegExp(`^${escaped} \\(Round ${round}${qualifier}\\)$`);
464
- const open = startedBackedRows(pairEntries(activity.entries)).filter((row) => expected.test(row.step) && !row.done);
465
- if (open.length !== 1) {
466
- return { ok: false, code: 'ARTIFACT_REPAIR_CONTEXT_INVALID', message: `artifact '${request.artifact}' does not have exactly one matching open started lifecycle event` };
467
- }
468
- let intent;
469
- try { intent = readArtifactRepairIntent(request.repoRoot, request.taskId, request.family, request.artifact); }
470
- catch (error) { return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: String(error) }; }
471
- if (!intent || intent.state !== 'awaiting-repair') {
472
- return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: 'artifact has no awaiting-repair finalization provenance' };
473
- }
474
- if (intent.artifactSha256 !== request.expectedSha256 || intent.semanticDigest !== request.expectedSemanticDigest) {
475
- return { ok: false, code: 'ARTIFACT_REPAIR_PROVENANCE_INVALID', message: 'repair request does not match the current finalization provenance' };
476
- }
477
- return { ok: true };
478
- }
479
-
480
286
  function artifactRound(family: ArtifactSchemaFamily, artifact: string): number | null {
481
- if (artifact === `${family}.md`) return 1;
482
- const match = artifact.match(new RegExp(`^${escapeRegExp(family)}-r([2-9]|[1-9]\\d+)\\.md$`));
483
- if (!match) return null;
484
- const round = Number(match[1]);
485
- return Number.isSafeInteger(round) ? round : null;
486
- }
487
-
488
- function validateTarget(taskDir: string, family: ArtifactSchemaFamily, artifact: string): { path: string } | ArtifactFileResult {
489
- if (path.basename(artifact) !== artifact || artifactRound(family, artifact) === null || !artifact.endsWith('.md')) return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact must be a canonical top-level Markdown file');
490
- if (!/^TASK-\d{8}-\d{6}$/.test(path.basename(path.resolve(taskDir)))) return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'target directory is not a task directory');
491
- const target = path.join(taskDir, artifact);
492
- if (path.dirname(path.resolve(target)) !== path.resolve(taskDir)) return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact must be inside the current task directory');
493
- let stat: fs.Stats;
494
- try { stat = fs.lstatSync(target); }
495
- catch (error) { return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', `artifact cannot be read: ${String(error)}`); }
496
- if (stat.isSymbolicLink() || !stat.isFile()) return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', 'artifact is not a regular file');
497
- try { fs.accessSync(target, fs.constants.R_OK | fs.constants.W_OK); }
498
- catch (error) { return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', `artifact is not readable and writable: ${String(error)}`); }
499
- return { path: target };
500
- }
501
-
502
- function applyRepairUnlocked(request: ArtifactRepairRequest): ArtifactFileResult {
503
- const schema = getArtifactSchema(request.family);
504
- if (!schema) return resultFailure('ARTIFACT_FAMILY_UNKNOWN', `unknown artifact family '${request.family}'`);
505
- const target = validateTarget(request.taskDir, request.family, request.artifact);
506
- if ('status' in target) return target;
507
- let content: string;
508
- try { content = fs.readFileSync(target.path, 'utf8'); }
509
- catch (error) { return resultFailure('ARTIFACT_REPAIR_TARGET_INVALID', String(error)); }
510
- const actualSha256 = sha256Content(content);
511
- if (actualSha256 !== request.expectedSha256) return resultFailure('ARTIFACT_REPAIR_BASELINE_MISMATCH', 'artifact SHA-256 does not match the expected repair baseline');
512
- const context = validateRepairContext(request, content);
513
- if (!context.ok) return resultFailure(context.code, context.message);
514
- const inspection = inspectArtifactStructure(content, schema);
515
- if (!inspection.repair || inspection.diagnostics.length !== 1) {
516
- return resultFailure('ARTIFACT_REPAIR_UNSAFE', inspection.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ') || 'no deterministic structural repair is available');
517
- }
518
- if (inspection.repair.kind !== request.operation.kind
519
- || inspection.repair.sectionId !== request.operation.sectionId
520
- || inspection.repair.from !== request.operation.from
521
- || inspection.repair.to !== request.operation.to
522
- || inspection.repair.start !== request.operation.start
523
- || inspection.repair.end !== request.operation.end) {
524
- return resultFailure('ARTIFACT_REPAIR_OPERATION_MISMATCH', 'repair operation does not match the current artifact structure');
525
- }
526
- if (inspection.semanticDigest !== request.expectedSemanticDigest) return resultFailure('ARTIFACT_REPAIR_BASELINE_MISMATCH', 'artifact semantic digest does not match the expected repair baseline');
527
- const transformed = applyRepairOperation(content, inspection.repair);
528
- if (transformed === null) return resultFailure('ARTIFACT_REPAIR_OPERATION_MISMATCH', 'repair operation does not match the current artifact bytes');
529
- if (transformed === content) return resultFailure('ARTIFACT_REPAIR_NO_PROGRESS', 'repair operation produced no byte change');
530
- const tempPath = path.join(request.taskDir, `.${request.artifact}.repair-${process.pid}-${Date.now()}.tmp`);
531
- let mode = 0o600;
532
- try {
533
- mode = fs.statSync(target.path).mode & 0o777;
534
- fs.writeFileSync(tempPath, transformed, { encoding: 'utf8', mode, flag: 'wx' });
535
- const currentStat = fs.lstatSync(target.path);
536
- if (currentStat.isSymbolicLink() || !currentStat.isFile() || sha256Content(fs.readFileSync(target.path, 'utf8')) !== actualSha256) {
537
- fs.unlinkSync(tempPath);
538
- return resultFailure('ARTIFACT_REPAIR_CONFLICT', 'artifact changed or was replaced during repair');
539
- }
540
- fs.renameSync(tempPath, target.path);
541
- } catch (error) {
542
- try { fs.unlinkSync(tempPath); } catch { /* preserve primary error */ }
543
- return resultFailure('ARTIFACT_REPAIR_WRITE_FAILED', String(error));
544
- }
545
- return {
546
- status: 'applied',
547
- changed: true,
548
- artifactSha256: sha256Content(transformed),
549
- semanticDigest: canonicalSemanticDigest(transformed),
550
- operation: inspection.repair,
551
- error: null
552
- };
553
- }
554
-
555
- function applyArtifactRepair(request: ArtifactRepairRequest): ArtifactFileResult {
556
- try {
557
- return withTaskExecutionLock(request.repoRoot, request.taskId, 'task-artifact.repair', () => applyRepairUnlocked(request));
558
- } catch (error) {
559
- return resultFailure('ARTIFACT_REPAIR_LOCK_FAILED', String(error));
560
- }
287
+ const identity = parseArtifactName(artifact);
288
+ return identity?.family === family ? identity.round : null;
561
289
  }
562
290
 
563
291
  function initializeArtifactSkeleton(request: ArtifactInitRequest): ArtifactFileResult {
@@ -572,7 +300,7 @@ function initializeArtifactSkeleton(request: ArtifactInitRequest): ArtifactFileR
572
300
  try {
573
301
  const existing = fs.lstatSync(target);
574
302
  if (existing.isSymbolicLink() || !existing.isFile()) return resultFailure('ARTIFACT_INIT_TARGET_INVALID', 'artifact target is not a regular file');
575
- return resultNoOp(fs.readFileSync(target, 'utf8'));
303
+ return resultNoOp(readStableFileSync(target, { maxBytes: 1024 * 1024 }).bytes.toString('utf8'));
576
304
  } catch (error) {
577
305
  if ((error as NodeJS.ErrnoException).code !== 'ENOENT') return resultFailure('ARTIFACT_INIT_TARGET_INVALID', String(error));
578
306
  }
@@ -597,7 +325,6 @@ function initializeArtifactSkeleton(request: ArtifactInitRequest): ArtifactFileR
597
325
  changed: true,
598
326
  artifactSha256: sha256Content(content),
599
327
  semanticDigest: canonicalSemanticDigest(content),
600
- operation: null,
601
328
  error: null
602
329
  };
603
330
  });
@@ -607,7 +334,6 @@ function initializeArtifactSkeleton(request: ArtifactInitRequest): ArtifactFileR
607
334
  }
608
335
 
609
336
  export {
610
- applyArtifactRepair,
611
337
  canonicalSemanticDigest,
612
338
  initializeArtifactSkeleton,
613
339
  inspectArtifactContract,
@@ -618,8 +344,6 @@ export {
618
344
  export type {
619
345
  ArtifactFileResult,
620
346
  ArtifactInitRequest,
621
- ArtifactRepairOperation,
622
- ArtifactRepairRequest,
623
347
  ArtifactStructuralDiagnostic,
624
348
  ArtifactStructuralDiagnosticCode,
625
349
  ArtifactStructureResult
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import { createHash } from 'node:crypto';
3
3
 
4
+ import { parseArtifactName } from './artifact-name.ts';
4
5
  import { extractSection, findSectionHeading, parseTable } from './sections.ts';
5
6
 
6
7
  const RECEIPT_SECTION_ALIASES = ['产物生命周期收据', 'Artifact Lifecycle Receipts'] as const;
@@ -12,7 +13,6 @@ const RECEIPT_EVENTS = new Set([
12
13
  'code.completed'
13
14
  ]);
14
15
  const SHA256_RE = /^[a-f0-9]{64}$/;
15
- const ARTIFACT_NAME_RE = /^(analysis|review-analysis|plan|review-plan|code|review-code|manual-validation|validation-run|pr-review)(?:-r([2-9]|[1-9]\d+))?\.md$/;
16
16
  const RECEIPT_SHAPES = {
17
17
  'review-analysis.completed': { output: 'review-analysis', input: 'analysis' },
18
18
  'review-plan.completed': { output: 'review-plan', input: 'plan' },
@@ -59,20 +59,9 @@ function sha256File(filePath: string): string {
59
59
  return sha256Bytes(fs.readFileSync(filePath));
60
60
  }
61
61
 
62
- function parseReceiptArtifactName(name: string): { family: string } | null {
63
- const match = ARTIFACT_NAME_RE.exec(name);
64
- if (!match) return null;
65
- const roundText = match[2];
66
- if (roundText !== undefined) {
67
- const round = Number(roundText);
68
- if (!Number.isSafeInteger(round) || String(round) !== roundText) return null;
69
- }
70
- return { family: match[1]! };
71
- }
72
-
73
62
  function validateReceiptShape(event: ArtifactReceiptEvent, output: string, input: string): void {
74
- const outputIdentity = parseReceiptArtifactName(output);
75
- const inputIdentity = parseReceiptArtifactName(input);
63
+ const outputIdentity = parseArtifactName(output);
64
+ const inputIdentity = parseArtifactName(input);
76
65
  if (!outputIdentity || !inputIdentity) throw new ArtifactReceiptError(`receipt artifact identity is invalid: ${output} -> ${input}`);
77
66
  const shape = RECEIPT_SHAPES[event];
78
67
  if (outputIdentity.family !== shape.output || inputIdentity.family !== shape.input) {