@fitlab-ai/agent-infra 0.9.14 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) 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 +3 -2
  7. package/dist/lib/defaults.json +103 -103
  8. package/dist/lib/fs/durable-write.js +37 -0
  9. package/dist/lib/fs/file-lock.js +111 -0
  10. package/dist/lib/host-control/audit.js +19 -0
  11. package/dist/lib/host-control/client.js +142 -0
  12. package/dist/lib/host-control/command.js +49 -0
  13. package/dist/lib/host-control/path.js +160 -0
  14. package/dist/lib/host-control/secure-fs.js +140 -0
  15. package/dist/lib/host-control/server.js +159 -0
  16. package/dist/lib/host-control/service.js +112 -0
  17. package/dist/lib/init.js +11 -2
  18. package/dist/lib/internal/cli-route-inventory.js +3 -1
  19. package/dist/lib/internal/codex-lifecycle.js +6 -6
  20. package/dist/lib/internal/demo-promotion.js +322 -0
  21. package/dist/lib/internal/demo-transcript.js +454 -0
  22. package/dist/lib/internal/host-control.js +28 -0
  23. package/dist/lib/internal/manual-validation.js +322 -0
  24. package/dist/lib/internal/platform-pr.js +18 -4
  25. package/dist/lib/internal/release-workflow.js +63 -84
  26. package/dist/lib/internal/sandbox-control.js +149 -2
  27. package/dist/lib/internal/task-artifact.js +15 -262
  28. package/dist/lib/internal/task-create.js +6 -5
  29. package/dist/lib/internal/task-event.js +37 -37
  30. package/dist/lib/internal/task-operation-registry.js +101 -3
  31. package/dist/lib/internal/task-review.js +8 -53
  32. package/dist/lib/internal/task-validate.js +30 -10
  33. package/dist/lib/owned-path.js +33 -0
  34. package/dist/lib/platform/check-status.js +11 -0
  35. package/dist/lib/platform/comment-safety.js +4 -1
  36. package/dist/lib/platform/completion-backfill.js +1 -1
  37. package/dist/lib/platform/github-data.js +241 -0
  38. package/dist/lib/platform/github-provider.js +15 -20
  39. package/dist/lib/platform/issue-comments.js +2 -2
  40. package/dist/lib/platform/issues.js +1 -179
  41. package/dist/lib/platform/pr-checks.js +12 -65
  42. package/dist/lib/platform/pr-summary.js +55 -3
  43. package/dist/lib/platform/pull-requests.js +2 -105
  44. package/dist/lib/platform/snapshots.js +2 -0
  45. package/dist/lib/platform/verification-sync.js +127 -142
  46. package/dist/lib/release/workflow.js +1 -1
  47. package/dist/lib/sandbox/commands/create.js +7 -2
  48. package/dist/lib/sandbox/commands/rm.js +3 -1514
  49. package/dist/lib/sandbox/control/audit.js +203 -0
  50. package/dist/lib/sandbox/control/client.js +93 -54
  51. package/dist/lib/sandbox/control/executor.js +129 -65
  52. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  53. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  54. package/dist/lib/sandbox/control/lifecycle.js +36 -2
  55. package/dist/lib/sandbox/control/native-file-lock.js +6 -98
  56. package/dist/lib/sandbox/control/protocol.js +55 -4
  57. package/dist/lib/sandbox/control/server.js +491 -120
  58. package/dist/lib/sandbox/control/state.js +58 -0
  59. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  60. package/dist/lib/sandbox/control/workflow-executor.js +99 -0
  61. package/dist/lib/sandbox/index.js +2 -2
  62. package/dist/lib/sandbox/recovery.js +28 -4
  63. package/dist/lib/sandbox/removal.js +1717 -0
  64. package/dist/lib/sandbox/task-cleanup.js +217 -0
  65. package/dist/lib/sandbox/workspace-view.js +13 -14
  66. package/dist/lib/task/activity-intent.js +2 -1
  67. package/dist/lib/task/activity-log.js +30 -24
  68. package/dist/lib/task/artifact-command.js +101 -0
  69. package/dist/lib/task/artifact-lifecycle.js +30 -28
  70. package/dist/lib/task/artifact-name.js +36 -0
  71. package/dist/lib/task/artifact-operations.js +23 -69
  72. package/dist/lib/task/artifact-receipts.js +3 -15
  73. package/dist/lib/task/artifact-repair-intent.js +87 -16
  74. package/dist/lib/task/artifact-schema.js +6 -14
  75. package/dist/lib/task/capabilities.js +12 -19
  76. package/dist/lib/task/codex-orchestration.js +71 -25
  77. package/dist/lib/task/commit-identity.js +28 -0
  78. package/dist/lib/task/commit-operation.js +2 -16
  79. package/dist/lib/task/control-authority.js +371 -5
  80. package/dist/lib/task/control-recovery.js +128 -0
  81. package/dist/lib/task/create.js +62 -9
  82. package/dist/lib/task/decision-details.js +2 -46
  83. package/dist/lib/task/events.js +120 -9
  84. package/dist/lib/task/finalization-state.js +9 -0
  85. package/dist/lib/task/human-override.js +3 -6
  86. package/dist/lib/task/implementation-inputs.js +3 -3
  87. package/dist/lib/task/intermediate-cleanup.js +383 -0
  88. package/dist/lib/task/invalidation.js +18 -65
  89. package/dist/lib/task/ledger-intents.js +7 -22
  90. package/dist/lib/task/ledger.js +1 -3
  91. package/dist/lib/task/lifecycle.js +19 -1
  92. package/dist/lib/task/local-artifact-finalization.js +132 -157
  93. package/dist/lib/task/manual-validation-completion.js +26 -0
  94. package/dist/lib/task/manual-validation-evidence.js +130 -0
  95. package/dist/lib/task/manual-validation-receipt.js +101 -0
  96. package/dist/lib/task/manual-validation-shared.js +40 -0
  97. package/dist/lib/task/manual-validation-transaction.js +243 -0
  98. package/dist/lib/task/markdown.js +47 -0
  99. package/dist/lib/task/orchestration.js +14 -17
  100. package/dist/lib/task/qualification-audit.js +20 -34
  101. package/dist/lib/task/review-artifacts.js +2 -18
  102. package/dist/lib/task/review-command.js +25 -0
  103. package/dist/lib/task/review-finalization.js +125 -193
  104. package/dist/lib/task/review-fingerprint.js +2 -2
  105. package/dist/lib/task/sections.js +34 -56
  106. package/dist/lib/task/verification-engine.js +116 -323
  107. package/dist/lib/task/verification.js +1 -1
  108. package/dist/lib/task/workflow-command.js +17 -0
  109. package/dist/lib/task/workflow-warning-intents.js +0 -16
  110. package/dist/lib/task/workflow-warnings.js +28 -2
  111. package/dist/lib/update.js +2 -0
  112. package/dist/lifecycle-build-manifest.json +3 -3
  113. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  114. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
  115. package/lib/fs/durable-write.ts +29 -0
  116. package/lib/fs/file-lock.ts +113 -0
  117. package/lib/host-control/audit.ts +22 -0
  118. package/lib/host-control/client.ts +172 -0
  119. package/lib/host-control/command.ts +60 -0
  120. package/lib/host-control/path.ts +167 -0
  121. package/lib/host-control/secure-fs.ts +135 -0
  122. package/lib/host-control/server.ts +180 -0
  123. package/lib/host-control/service.ts +133 -0
  124. package/lib/init.ts +15 -2
  125. package/lib/internal/cli-route-inventory.ts +3 -1
  126. package/lib/internal/codex-lifecycle.ts +6 -6
  127. package/lib/internal/demo-promotion.ts +354 -0
  128. package/lib/internal/demo-transcript.ts +419 -0
  129. package/lib/internal/host-control.ts +28 -0
  130. package/lib/internal/manual-validation.ts +306 -0
  131. package/lib/internal/platform-pr.ts +15 -4
  132. package/lib/internal/release-workflow.ts +74 -82
  133. package/lib/internal/sandbox-control.ts +162 -1
  134. package/lib/internal/task-artifact.ts +13 -216
  135. package/lib/internal/task-create.ts +5 -5
  136. package/lib/internal/task-event.ts +20 -11
  137. package/lib/internal/task-operation-registry.ts +111 -3
  138. package/lib/internal/task-review.ts +6 -51
  139. package/lib/internal/task-validate.ts +26 -10
  140. package/lib/owned-path.ts +26 -0
  141. package/lib/platform/check-status.ts +9 -0
  142. package/lib/platform/comment-safety.ts +5 -0
  143. package/lib/platform/completion-backfill.ts +1 -1
  144. package/lib/platform/github-data.ts +322 -0
  145. package/lib/platform/github-provider.ts +15 -20
  146. package/lib/platform/issue-comments.ts +3 -4
  147. package/lib/platform/issues.ts +4 -245
  148. package/lib/platform/pr-checks.ts +13 -77
  149. package/lib/platform/pr-summary.ts +61 -5
  150. package/lib/platform/pull-requests.ts +3 -170
  151. package/lib/platform/snapshots.ts +53 -0
  152. package/lib/platform/verification-sync.ts +145 -155
  153. package/lib/release/workflow.ts +3 -3
  154. package/lib/sandbox/commands/create.ts +7 -6
  155. package/lib/sandbox/commands/rm.ts +3 -1909
  156. package/lib/sandbox/control/audit.ts +253 -0
  157. package/lib/sandbox/control/client.ts +109 -66
  158. package/lib/sandbox/control/executor.ts +134 -74
  159. package/lib/sandbox/control/finalization-response.ts +29 -0
  160. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  161. package/lib/sandbox/control/lifecycle.ts +45 -3
  162. package/lib/sandbox/control/native-file-lock.ts +6 -107
  163. package/lib/sandbox/control/protocol.ts +75 -6
  164. package/lib/sandbox/control/server.ts +538 -123
  165. package/lib/sandbox/control/state.ts +78 -0
  166. package/lib/sandbox/control/task-workflow.ts +95 -0
  167. package/lib/sandbox/control/workflow-executor.ts +102 -0
  168. package/lib/sandbox/index.ts +2 -2
  169. package/lib/sandbox/recovery.ts +28 -5
  170. package/lib/sandbox/removal.ts +2144 -0
  171. package/lib/sandbox/task-cleanup.ts +209 -0
  172. package/lib/sandbox/workspace-view.ts +17 -15
  173. package/lib/task/activity-intent.ts +2 -2
  174. package/lib/task/activity-log.ts +27 -19
  175. package/lib/task/artifact-command.ts +104 -0
  176. package/lib/task/artifact-lifecycle.ts +31 -26
  177. package/lib/task/artifact-name.ts +55 -0
  178. package/lib/task/artifact-operations.ts +22 -68
  179. package/lib/task/artifact-receipts.ts +3 -14
  180. package/lib/task/artifact-repair-intent.ts +99 -18
  181. package/lib/task/artifact-schema.ts +5 -34
  182. package/lib/task/capabilities.ts +13 -22
  183. package/lib/task/codex-orchestration.ts +85 -24
  184. package/lib/task/commit-identity.ts +31 -0
  185. package/lib/task/commit-operation.ts +2 -16
  186. package/lib/task/control-authority.ts +502 -5
  187. package/lib/task/control-recovery.ts +174 -0
  188. package/lib/task/create.ts +63 -8
  189. package/lib/task/decision-details.ts +3 -71
  190. package/lib/task/events.ts +131 -10
  191. package/lib/task/finalization-state.ts +11 -0
  192. package/lib/task/human-override.ts +3 -8
  193. package/lib/task/implementation-inputs.ts +3 -3
  194. package/lib/task/intermediate-cleanup.ts +455 -0
  195. package/lib/task/invalidation.ts +18 -62
  196. package/lib/task/ledger-intents.ts +7 -24
  197. package/lib/task/ledger.ts +1 -3
  198. package/lib/task/lifecycle.ts +17 -1
  199. package/lib/task/local-artifact-finalization.ts +148 -182
  200. package/lib/task/manual-validation-completion.ts +40 -0
  201. package/lib/task/manual-validation-evidence.ts +209 -0
  202. package/lib/task/manual-validation-receipt.ts +152 -0
  203. package/lib/task/manual-validation-shared.ts +50 -0
  204. package/lib/task/manual-validation-transaction.ts +304 -0
  205. package/lib/task/markdown.ts +70 -0
  206. package/lib/task/orchestration.ts +19 -19
  207. package/lib/task/qualification-audit.ts +22 -33
  208. package/lib/task/review-artifacts.ts +1 -20
  209. package/lib/task/review-command.ts +23 -0
  210. package/lib/task/review-finalization.ts +131 -259
  211. package/lib/task/review-fingerprint.ts +2 -2
  212. package/lib/task/sections.ts +33 -54
  213. package/lib/task/verification-engine.ts +110 -366
  214. package/lib/task/verification-types.ts +17 -0
  215. package/lib/task/verification.ts +1 -1
  216. package/lib/task/workflow-command.ts +18 -0
  217. package/lib/task/workflow-warning-intents.ts +0 -13
  218. package/lib/task/workflow-warnings.ts +21 -2
  219. package/lib/update.ts +6 -0
  220. package/package.json +6 -6
  221. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  222. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  223. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  224. package/templates/.agents/scripts/package.json +3 -0
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +32 -20
  226. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +32 -20
  227. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  228. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  229. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  230. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  231. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  232. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  233. package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
  234. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
  235. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  236. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  237. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
  238. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
  239. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  240. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  241. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
  242. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
  243. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  244. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  245. package/templates/.agents/skills/test/SKILL.en.md +17 -6
  246. package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
  247. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  248. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  249. package/dist/lib/platform/adapters.js +0 -56
  250. package/lib/platform/adapters.ts +0 -197
@@ -6,7 +6,7 @@ import { loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.js';
6
6
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
7
7
  import { readDeliveryDefaults, validateBaseRef, validateRemote } from './delivery-target.js';
8
8
  import { buildUnboundFact, encodePrDeliveryFact } from './pr-delivery-fact.js';
9
- import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS } from './qualification-audit.js';
9
+ import { CANDIDATE_COLUMNS, CONSTRAINT_COLUMNS, parseTaskQualification } from './qualification-audit.js';
10
10
  const AGENTS = ['claude', 'codex', 'antigravity', 'opencode', 'cursor'];
11
11
  const TYPES = ['feature', 'bugfix', 'refactor', 'docs', 'chore'];
12
12
  const PRIORITIES = ['Urgent', 'High', 'Medium', 'Low'];
@@ -150,12 +150,64 @@ function renderTable(columns, rows) {
150
150
  ...rows.map((row) => `| ${row.map(tableCell).join(' | ')} |`)
151
151
  ].join('\n');
152
152
  }
153
- function appendCanonicalRows(content, columns, rows) {
153
+ function escapeRegExp(value) {
154
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
155
+ }
156
+ function candidateIdFor(index) {
157
+ let value = index + 1;
158
+ let id = '';
159
+ while (value > 0) {
160
+ const remainder = (value - 1) % 26;
161
+ id = `${String.fromCharCode(65 + remainder)}${id}`;
162
+ value = Math.floor((value - 1) / 26);
163
+ }
164
+ return id;
165
+ }
166
+ function appendCanonicalRows(content, heading, columns, rows) {
154
167
  if (rows.length === 0)
155
168
  return content;
156
169
  const table = renderTable(columns, rows);
157
- const separator = `| ${columns.map(() => '---').join(' | ')} |`;
158
- return content.replace(separator, `${separator}\n${table.split('\n').slice(2).join('\n')}`);
170
+ const [header, separator, ...rowLines] = table.split('\n');
171
+ const eol = content.includes('\r\n') ? '\r\n' : '\n';
172
+ const lines = content.split(/\r?\n/);
173
+ const headingPattern = new RegExp(`^###\\s+${escapeRegExp(heading)}\\s*$`);
174
+ const sectionStart = lines.findIndex((line) => headingPattern.test(line));
175
+ if (sectionStart < 0)
176
+ throw new Error(`TASK_CREATE_QUALIFICATION_INVALID: section '${heading}' is missing`);
177
+ let sectionEnd = lines.length;
178
+ for (let index = sectionStart + 1; index < lines.length; index += 1) {
179
+ if (/^#{2,3}\s+/.test(lines[index])) {
180
+ sectionEnd = index;
181
+ break;
182
+ }
183
+ }
184
+ const matches = [];
185
+ for (let index = sectionStart + 1; index + 1 < sectionEnd; index += 1) {
186
+ if (lines[index].trim() === header && lines[index + 1].trim() === separator)
187
+ matches.push(index);
188
+ }
189
+ if (matches.length !== 1) {
190
+ throw new Error(`TASK_CREATE_QUALIFICATION_INVALID: section '${heading}' must contain exactly one canonical qualification table`);
191
+ }
192
+ lines.splice(matches[0] + 2, 0, ...rowLines);
193
+ return lines.join(eol);
194
+ }
195
+ function validateRenderedQualification(content, candidate) {
196
+ const parsed = parseTaskQualification(content);
197
+ if (!parsed.ok) {
198
+ throw new Error(`TASK_CREATE_QUALIFICATION_INVALID: ${parsed.code}: ${parsed.message}`);
199
+ }
200
+ if (!parsed.qualification.present) {
201
+ throw new Error('TASK_CREATE_QUALIFICATION_INVALID: rendered task qualification contract is missing');
202
+ }
203
+ const expectedConstraintIds = candidate.taskInput.constraints.map((_, index) => `C-${index + 1}`);
204
+ const expectedCandidateIds = candidate.taskInput.alternatives.map((_, index) => candidateIdFor(index));
205
+ const actualConstraintIds = parsed.qualification.constraints.map((row) => row.constraintId);
206
+ const actualCandidateIds = parsed.qualification.candidates.map((row) => row.candidateId);
207
+ if (JSON.stringify(actualConstraintIds) !== JSON.stringify(expectedConstraintIds)
208
+ || JSON.stringify(actualCandidateIds) !== JSON.stringify(expectedCandidateIds)) {
209
+ throw new Error('TASK_CREATE_QUALIFICATION_INVALID: rendered task qualification rows do not match task input');
210
+ }
159
211
  }
160
212
  function replaceEmptySubsection(content, heading, items) {
161
213
  if (items.length === 0)
@@ -202,11 +254,11 @@ function renderTask(params) {
202
254
  for (const [heading, key] of sections)
203
255
  content = replaceEmptySubsection(content, heading, candidate.taskInput[key]);
204
256
  const constraintIds = candidate.taskInput.constraints.map((_, index) => `C-${index + 1}`);
205
- content = appendCanonicalRows(content, CONSTRAINT_COLUMNS, candidate.taskInput.constraints.map((statement, index) => [
257
+ content = appendCanonicalRows(content, '约束', CONSTRAINT_COLUMNS, candidate.taskInput.constraints.map((statement, index) => [
206
258
  constraintIds[index], statement, 'assumption', 'create-task', 'taskInput', 'create-task input', '', ''
207
259
  ]));
208
- content = appendCanonicalRows(content, CANDIDATE_COLUMNS, candidate.taskInput.alternatives.map((statement, index) => [
209
- String.fromCharCode(65 + index), statement, 'pending', constraintIds.join(','), 'requires qualification', 'create-task input'
260
+ content = appendCanonicalRows(content, '候选与否决方案', CANDIDATE_COLUMNS, candidate.taskInput.alternatives.map((statement, index) => [
261
+ candidateIdFor(index), statement, 'pending', constraintIds.join(','), 'requires qualification', 'create-task input'
210
262
  ]));
211
263
  content = content.replace('- **关联 Issue**:#XXX', '- **关联 Issue**:N/A');
212
264
  content = content.replace('- **关联 PR**:#XXX', '- **关联 PR**:N/A');
@@ -328,8 +380,6 @@ function createLocalTask(value, options) {
328
380
  return withCreateLock(repoRoot, workspaceRoot, () => {
329
381
  if (fs.existsSync(receipts))
330
382
  assertRealDirectory(receipts, 'TASK_CREATE_RECEIPT_INVALID');
331
- else
332
- fs.mkdirSync(receipts, { mode: 0o700 });
333
383
  const receiptPath = path.join(receipts, `${keyDigest}.json`);
334
384
  if (fs.existsSync(receiptPath)) {
335
385
  const stat = fs.lstatSync(receiptPath);
@@ -345,6 +395,7 @@ function createLocalTask(value, options) {
345
395
  }
346
396
  const recovered = recoverPublishedTask(activeRoot, repoRoot, keyDigest, candidateDigest);
347
397
  if (recovered) {
398
+ ensureRealDirectory(receipts, 'TASK_CREATE_RECEIPT_INVALID');
348
399
  writeReceipt(receiptPath, {
349
400
  version: 1, candidateDigest, taskId: recovered.taskId, shortId: recovered.shortId, status: 'recovered'
350
401
  });
@@ -366,6 +417,8 @@ function createLocalTask(value, options) {
366
417
  template: fs.readFileSync(templatePath, 'utf8'), candidate, taskId, project: projectConfig.project, delivery: projectConfig.delivery, timestamp,
367
418
  agentInfraVersion, keyDigest, candidateDigest
368
419
  });
420
+ validateRenderedQualification(rendered, candidate);
421
+ ensureRealDirectory(receipts, 'TASK_CREATE_RECEIPT_INVALID');
369
422
  const temporary = path.join(workspaceRoot, `.task-create.tmp.${process.pid}.${randomUUID()}`);
370
423
  fs.mkdirSync(temporary, { mode: 0o700 });
371
424
  fs.writeFileSync(path.join(temporary, 'task.md'), rendered, { flag: 'wx', mode: 0o600 });
@@ -1,51 +1,7 @@
1
+ import { scanVisibleMarkdown } from './markdown.js';
1
2
  const DECISION_ID = /^(AN|PL|CD|HD)-[1-9]\d*(?=$|[\s::])/i;
2
- const HEADING = /^ {0,3}(#{1,6})[ \t]+(.+?)\s*$/;
3
- const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
4
- const FENCE_CLOSE = /^ {0,3}(`{3,}|~{3,})\s*$/;
5
- const ANCHOR = /^\s*<a\s+id=(['"])([^'"]+)\1\s*><\/a>\s*$/;
6
3
  const FIELD = /^\s*[-*]\s+\*\*([^*]+)\*\*\s*[::]\s*/;
7
4
  const OPTION_HEADING = /^(?:方案|Option)\s+[A-Z](?:\b|\s*[::])/i;
8
- function sourceLines(content) {
9
- const lines = [];
10
- let start = 0;
11
- while (start < content.length) {
12
- const newline = content.indexOf('\n', start);
13
- const end = newline === -1 ? content.length : newline;
14
- lines.push({ start, end, text: content.slice(start, end).replace(/\r$/, '') });
15
- start = newline === -1 ? content.length : newline + 1;
16
- }
17
- return lines;
18
- }
19
- function scanVisibleMarkdown(content) {
20
- const lines = sourceLines(content);
21
- const visible = [];
22
- const headings = [];
23
- const anchors = [];
24
- let fence = null;
25
- for (const line of lines) {
26
- if (fence) {
27
- const close = line.text.match(FENCE_CLOSE);
28
- if (close && close[1][0] === fence.character && close[1].length >= fence.length)
29
- fence = null;
30
- continue;
31
- }
32
- const open = line.text.match(FENCE_OPEN);
33
- const marker = open?.[1];
34
- const info = open?.[2] ?? '';
35
- if (marker && !(marker[0] === '`' && info.includes('`'))) {
36
- fence = { character: marker[0], length: marker.length };
37
- continue;
38
- }
39
- visible.push(line);
40
- const heading = line.text.match(HEADING);
41
- if (heading)
42
- headings.push({ ...line, level: heading[1].length, text: heading[2].trim() });
43
- const anchor = line.text.match(ANCHOR);
44
- if (anchor)
45
- anchors.push({ ...line, id: anchor[2] });
46
- }
47
- return { lines: visible, headings, anchors };
48
- }
49
5
  function parseDecisionDetailBlocks(content) {
50
6
  const scanned = scanVisibleMarkdown(content);
51
7
  const details = scanned.headings.filter((heading) => heading.level === 3 && DECISION_ID.test(heading.text));
@@ -214,5 +170,5 @@ function normalizeDecisionDetailForDisplay(block) {
214
170
  ordered.push(byKey.get('implementation'));
215
171
  return [body.slice(heading.start, heading.end).trim(), ...ordered].join('\n\n').trim();
216
172
  }
217
- export { inspectDecisionDetailDuplicates, normalizeDecisionDetailForDisplay, parseDecisionDetailBlocks, parseDecisionEvidence, resolveDecisionDetail, scanVisibleMarkdown };
173
+ export { inspectDecisionDetailDuplicates, normalizeDecisionDetailForDisplay, parseDecisionDetailBlocks, parseDecisionEvidence, resolveDecisionDetail };
218
174
  //# sourceMappingURL=decision-details.js.map
@@ -1,7 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { appendActivityEntry, locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
4
- import { buildArtifactLinkSection, artifactName, inspectArtifactDirectory, parseReviewedInputReference, parseCodePlanInputReference, parseArtifactName, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
4
+ import { buildArtifactLinkSection, inspectArtifactDirectory, parseReviewedInputReference, parseCodePlanInputReference, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
5
+ import { artifactName, parseArtifactName } from './artifact-name.js';
5
6
  import { ArtifactReceiptError, parseArtifactReceipts, sha256File, upsertArtifactReceipt } from './artifact-receipts.js';
6
7
  import { parseTypedTaskFrontmatter } from './frontmatter.js';
7
8
  import { consumeImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
@@ -15,12 +16,15 @@ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-
15
16
  import { captureTaskWriteMetadata, writeTask } from './write.js';
16
17
  import { allowsManualOverride } from './guard-override.js';
17
18
  import { consumeLocalArtifactFinalizationIntent, readLocalArtifactFinalizationIntent, validateLocalArtifact } from './local-artifact-finalization.js';
19
+ import { writeArtifactRepairIntent } from './artifact-repair-intent.js';
18
20
  import { buildLifecycleFacts, canStart } from './capabilities.js';
19
21
  import { createInvalidationOperation, invalidationMutation, parseInvalidationDocument, targetIdFor, upsertInvalidation } from './invalidation.js';
20
22
  import { consumeReworkIntents, parseReworkIntentDocument, reworkIntentMutation, supersedeReworkIntents } from './rework-intent.js';
21
23
  import { ARTIFACT_FAMILIES, expectedQualificationRelations, parseQualificationAudit, parseTaskQualification, upstreamArtifactDigest, validateQualificationAudit } from './qualification-audit.js';
22
24
  import { getArtifactSchema } from './artifact-schema.js';
23
25
  import { inspectArtifactContract } from './artifact-operations.js';
26
+ import { consumeLifecycleRecoveryAttestation, lifecycleRecoveryAttestationDigest, validateLifecycleRecoveryAttestation } from './control-authority.js';
27
+ import { readManualValidationCompletion } from './manual-validation-completion.js';
24
28
  const eventCatalog = [
25
29
  'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
26
30
  'review-analysis.started', 'review-analysis.completed',
@@ -46,8 +50,8 @@ const SCHEMAS = {
46
50
  'code.completed': { required: ['artifact', 'artifactSha256', 'semanticDigest'], optional: ['round', 'fixFor', 'implementationInput', 'filesModified', 'testsPassed', 'blockers', 'major', 'minor', 'manualValidation', 'orchestrated'] },
47
51
  'review-code.started': { optional: ['round'] },
48
52
  'review-code.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round', 'orchestrated'] },
49
- 'manual-validation.started': { optional: ['round'] },
50
- 'manual-validation.completed': { required: ['artifact', 'summaryResult'], optional: ['round'] },
53
+ 'manual-validation.started': { optional: ['round', 'transactionId'] },
54
+ 'manual-validation.completed': { required: ['artifact', 'summaryResult', 'transactionId', 'receiptDigest', 'prHeadSha'], optional: ['round'] },
51
55
  'validation-run.started': { optional: ['round'] },
52
56
  'validation-run.completed': { required: ['artifact'], optional: ['round'] }
53
57
  };
@@ -92,7 +96,7 @@ function validateTaskEventRequest(request) {
92
96
  if (modeRequired.some((key) => request[key] === undefined) || forbidden.some((key) => request[key] !== undefined))
93
97
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'code.completed requires either initial or fix completion payload' };
94
98
  }
95
- if (request.fixFor && !/^review-code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/.test(request.fixFor))
99
+ if (request.fixFor && parseArtifactName(request.fixFor)?.family !== 'review-code')
96
100
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor must reference a canonical review-code artifact' };
97
101
  if (request.implementationInput && !/^II-[1-9]\d*$/.test(request.implementationInput))
98
102
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'implementationInput must be a canonical II-N id' };
@@ -104,6 +108,19 @@ function validateTaskEventRequest(request) {
104
108
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor and implementationInput are mutually exclusive' };
105
109
  if (request.summaryResult !== undefined && (!request.summaryResult.trim() || /[\r\n]/.test(request.summaryResult)))
106
110
  return { code: 'EVENT_PAYLOAD_INVALID', message: 'summaryResult must be a non-empty single line' };
111
+ if (request.event === 'manual-validation.completed') {
112
+ for (const [name, value, pattern] of [
113
+ ['transactionId', request.transactionId, /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u],
114
+ ['receiptDigest', request.receiptDigest, /^[a-f0-9]{64}$/u],
115
+ ['prHeadSha', request.prHeadSha, /^[a-f0-9]{40}$/u]
116
+ ])
117
+ if (!value || !pattern.test(value))
118
+ return { code: 'EVENT_PAYLOAD_INVALID', message: `manual-validation.completed requires a valid ${name}` };
119
+ }
120
+ if (request.event === 'manual-validation.started' && request.transactionId !== undefined
121
+ && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(request.transactionId)) {
122
+ return { code: 'EVENT_PAYLOAD_INVALID', message: 'manual-validation.started requires a valid transactionId' };
123
+ }
107
124
  if (request.event !== 'analyze.awaiting-input' && /\.(?:started|completed)$/.test(request.event)) {
108
125
  if (!request.initiator || !request.requestId || !request.reasonCode) {
109
126
  return { code: 'EVENT_TRIGGER_REQUIRED', message: `${request.event} requires --initiator, --request-id, and --reason-code` };
@@ -202,7 +219,9 @@ function identity(request) {
202
219
  if (family === 'manual-validation') {
203
220
  return {
204
221
  family, phase, action: spec.label,
205
- note: phase === 'started' ? 'started' : `Manual validation passed → ${request.artifact}; ${request.summaryResult}`,
222
+ note: phase === 'started'
223
+ ? `started${request.transactionId ? `; transaction=${request.transactionId}` : ''}`
224
+ : `Manual validation passed → ${request.artifact}; ${request.summaryResult}; transaction=${request.transactionId}; receipt=${request.receiptDigest}; head=${request.prHeadSha}`,
206
225
  target: null
207
226
  };
208
227
  }
@@ -257,6 +276,24 @@ function normalizeStarted(request, repoRoot) {
257
276
  return { error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with artifact context` }, context };
258
277
  return { request: { ...request, round, artifact: context.next.name, fixFor: expectedFix, implementationInput: expectedImplementation }, context };
259
278
  }
279
+ function validateManualValidationCompletion(taskDir, taskId, request, artifactPath) {
280
+ const completion = readManualValidationCompletion(taskDir, {
281
+ transactionId: request.transactionId,
282
+ taskId,
283
+ prHeadSha: request.prHeadSha,
284
+ artifact: request.artifact,
285
+ receiptDigest: request.receiptDigest
286
+ });
287
+ if (!completion.ok)
288
+ return completion.error;
289
+ const { receipt, transaction } = completion.value;
290
+ if (!['receipt-committed', 'final-promotion-in-progress', 'committed'].includes(transaction.phase)) {
291
+ return { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'manual validation completion requires the receipt-backed transaction phase' };
292
+ }
293
+ if (sha256File(artifactPath) !== receipt.artifactSha256)
294
+ return { code: 'MANUAL_VALIDATION_RECEIPT_INVALID', message: 'manual-validation artifact digest does not match the receipt' };
295
+ return null;
296
+ }
260
297
  function openStartedIdentity(rows, family) {
261
298
  const spec = FAMILY[family];
262
299
  if (family === 'manual-validation') {
@@ -623,6 +660,11 @@ function applyTaskEventUnlocked(request, options = {}) {
623
660
  if (openIdentity) {
624
661
  if (openIdentity.row.agent !== request.agent)
625
662
  return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
663
+ if (initialParts.family === 'manual-validation' && request.transactionId !== undefined) {
664
+ const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(openIdentity.row.note)?.[1];
665
+ if (existingTransactionId !== undefined && existingTransactionId !== request.transactionId)
666
+ return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
667
+ }
626
668
  if (request.round !== undefined && request.round !== openIdentity.round)
627
669
  return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `round ${request.round} conflicts with open round ${openIdentity.round}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
628
670
  if (request.fixFor !== undefined && request.fixFor !== openIdentity.fixFor)
@@ -644,6 +686,25 @@ function applyTaskEventUnlocked(request, options = {}) {
644
686
  normalized = { ...request, round: identity.round };
645
687
  }
646
688
  const eventIdentity = identity(normalized);
689
+ const lifecycleAuthority = options.lifecycleRecoveryAttestation ?? null;
690
+ if (lifecycleAuthority) {
691
+ try {
692
+ validateLifecycleRecoveryAttestation(lifecycleAuthority);
693
+ }
694
+ catch (error) {
695
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
696
+ }
697
+ const expectedFamily = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
698
+ if (eventIdentity.phase !== 'completed'
699
+ || lifecycleAuthority.phase !== 'task-event.completed'
700
+ || lifecycleAuthority.taskId !== resolved.taskId
701
+ || lifecycleAuthority.family !== expectedFamily
702
+ || lifecycleAuthority.artifact !== normalized.artifact
703
+ || lifecycleAuthority.round !== normalized.round
704
+ || (normalized.requestId !== undefined && lifecycleAuthority.lifecycleRequestId !== normalized.requestId)) {
705
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'lifecycle completion authority does not match the event tuple' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
706
+ }
707
+ }
647
708
  const currentStep = typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '';
648
709
  const matchingRows = rows.filter((item) => item.step === eventIdentity.action);
649
710
  const manual = eventIdentity.family === 'manual-validation';
@@ -657,6 +718,12 @@ function applyTaskEventUnlocked(request, options = {}) {
657
718
  if (eventIdentity.phase === 'started' && row) {
658
719
  if (row.agent !== normalized.agent)
659
720
  return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
721
+ if (manual && normalized.transactionId !== undefined) {
722
+ const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(row.note)?.[1];
723
+ if (existingTransactionId !== undefined && existingTransactionId !== normalized.transactionId) {
724
+ return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
725
+ }
726
+ }
660
727
  return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, row.started, frontmatter, artifactContext);
661
728
  }
662
729
  if (eventIdentity.phase === 'started' && !manual && completedRows.length > 0)
@@ -673,6 +740,11 @@ function applyTaskEventUnlocked(request, options = {}) {
673
740
  if (!validated.ok)
674
741
  return failed(normalized, validated.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
675
742
  completedArtifact = validated.artifact;
743
+ if (eventIdentity.family === 'manual-validation') {
744
+ const validationError = validateManualValidationCompletion(resolved.taskDir, resolved.taskId, normalized, completedArtifact.path);
745
+ if (validationError)
746
+ return failed(normalized, validationError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
747
+ }
676
748
  if (eventIdentity.family.startsWith('review-')) {
677
749
  let reviewContent;
678
750
  try {
@@ -743,7 +815,7 @@ function applyTaskEventUnlocked(request, options = {}) {
743
815
  message: `cannot read local finalizer provenance for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
744
816
  }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
745
817
  }
746
- if (!localFinalizationIntent || !['passed', 'consumed'].includes(localFinalizationIntent.state)) {
818
+ if (!localFinalizationIntent || !['passed', 'consumed', 'finalize-ready', 'commit-started'].includes(localFinalizationIntent.state)) {
747
819
  return failed(normalized, {
748
820
  code: 'EVENT_ARTIFACT_CONFLICT',
749
821
  message: `local finalizer provenance is missing or incomplete for ${completedArtifact.name}`
@@ -755,6 +827,18 @@ function applyTaskEventUnlocked(request, options = {}) {
755
827
  message: `local finalizer provenance does not match ${completedArtifact.name}`
756
828
  }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
757
829
  }
830
+ if (localFinalizationIntent.recoveryOperationId !== null) {
831
+ if (!lifecycleAuthority
832
+ || localFinalizationIntent.recoveryOperationId !== lifecycleAuthority.operationId
833
+ || localFinalizationIntent.requestId !== lifecycleAuthority.lifecycleRequestId
834
+ || (localFinalizationIntent.phase !== 'artifact.finalize-local'
835
+ && localFinalizationIntent.authorityDigest !== lifecycleRecoveryAttestationDigest(lifecycleAuthority))) {
836
+ return failed(normalized, {
837
+ code: 'EVENT_ARTIFACT_CONFLICT',
838
+ message: `local finalizer provenance does not match lifecycle completion authority for ${completedArtifact.name}`
839
+ }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
840
+ }
841
+ }
758
842
  }
759
843
  }
760
844
  if (eventIdentity.phase === 'waiting' && section.entries.some((entry) => entry.step === eventIdentity.action && entry.note === eventIdentity.note)) {
@@ -914,14 +998,28 @@ function applyTaskEventUnlocked(request, options = {}) {
914
998
  });
915
999
  }
916
1000
  mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
917
- if (!normalized.dryRun && localFinalizationIntent && completedArtifact && localFinalizationIntent.state === 'passed') {
1001
+ if (!normalized.dryRun && localFinalizationIntent && completedArtifact) {
918
1002
  try {
919
- localFinalizationIntent = consumeLocalArtifactFinalizationIntent(resolved.repoRoot, localFinalizationIntent);
1003
+ if (lifecycleAuthority && localFinalizationIntent.state === 'passed') {
1004
+ const expectedIntent = localFinalizationIntent;
1005
+ localFinalizationIntent = {
1006
+ ...localFinalizationIntent,
1007
+ state: 'commit-started',
1008
+ phase: 'task-event.completed',
1009
+ authorityDigest: lifecycleRecoveryAttestationDigest(lifecycleAuthority),
1010
+ requestId: lifecycleAuthority.lifecycleRequestId,
1011
+ updatedAt: Date.now()
1012
+ };
1013
+ writeArtifactRepairIntent(resolved.repoRoot, localFinalizationIntent, { expected: expectedIntent });
1014
+ }
1015
+ else if (!lifecycleAuthority && localFinalizationIntent.state === 'passed') {
1016
+ localFinalizationIntent = consumeLocalArtifactFinalizationIntent(resolved.repoRoot, localFinalizationIntent);
1017
+ }
920
1018
  }
921
1019
  catch (error) {
922
1020
  return failed(normalized, {
923
1021
  code: 'EVENT_ARTIFACT_CONFLICT',
924
- message: `local finalizer provenance could not be consumed before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
1022
+ message: `local finalizer provenance could not be advanced before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
925
1023
  }, {
926
1024
  taskId: resolved.taskId,
927
1025
  taskMdPath: resolved.taskMdPath,
@@ -938,6 +1036,19 @@ function applyTaskEventUnlocked(request, options = {}) {
938
1036
  const result = writeTask({ taskRef: normalized.taskRef, expectedState: stateOverride ? resolved.state : 'active', dryRun: normalized.dryRun, mutations }, { ...options, invalidationContext: sourceCompletion ? 'source-completion' : 'standard', metadataProvider: () => metadata });
939
1037
  if (result.status === 'failed')
940
1038
  return failed(normalized, result.error, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
1039
+ if (!normalized.dryRun && lifecycleAuthority && localFinalizationIntent && completedArtifact) {
1040
+ try {
1041
+ if (!options.deferLifecycleRecoveryConsumption)
1042
+ consumeLifecycleRecoveryAttestation(lifecycleAuthority);
1043
+ writeArtifactRepairIntent(resolved.repoRoot, { ...localFinalizationIntent, state: 'consumed', updatedAt: Date.now() }, { expected: localFinalizationIntent });
1044
+ }
1045
+ catch (error) {
1046
+ return failed(normalized, {
1047
+ code: 'EVENT_ARTIFACT_CONFLICT',
1048
+ message: `task completion was written but lifecycle recovery consumption is incomplete: ${error instanceof Error ? error.message : String(error)}`
1049
+ }, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
1050
+ }
1051
+ }
941
1052
  if (!normalized.dryRun && orchestrationCompletion) {
942
1053
  try {
943
1054
  (options.commitOrchestrationCompletion ?? commitOrchestrationStageCompletion)(orchestrationCompletion);
@@ -0,0 +1,9 @@
1
+ export function taskFinalizationReceiptState(receipt) {
2
+ if (receipt.lifecycle !== 'done' || receipt.taskComment === 'pending'
3
+ || receipt.verification === 'pending' || receipt.warningProjection !== 'done')
4
+ return 'pending';
5
+ if (receipt.lastError !== null || receipt.warnings.some((warning) => warning.status === 'open'))
6
+ return 'unresolved';
7
+ return 'complete';
8
+ }
9
+ //# sourceMappingURL=finalization-state.js.map
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { createHash, randomUUID } from 'node:crypto';
4
+ import { parseArtifactName } from './artifact-name.js';
4
5
  import { locateActivityLog } from './activity-log.js';
5
6
  import { applyPrReviewActivityIntent } from './activity-intent.js';
6
7
  import { applyHumanDecision } from './decision-intents.js';
@@ -577,12 +578,8 @@ function latestArtifact(taskDir, family) {
577
578
  catch {
578
579
  return undefined;
579
580
  }
580
- const candidates = names.filter((name) => (name === `${family}.md` || new RegExp(`^${family}-r\\d+\\.md$`).test(name)));
581
- candidates.sort((a, b) => {
582
- const round = (name) => Number(/-r(\d+)\.md$/.exec(name)?.[1] ?? 1);
583
- return round(b) - round(a);
584
- });
585
- return candidates[0];
581
+ return names.map(parseArtifactName).filter((identity) => identity !== null)
582
+ .filter((identity) => identity.family === family).sort((left, right) => right.round - left.round)[0]?.name;
586
583
  }
587
584
  async function probeVerificationEngineFailure(policy, resolved) {
588
585
  const candidates = [
@@ -1,3 +1,4 @@
1
+ import { parseArtifactName } from './artifact-name.js';
1
2
  import { parseTable } from './sections.js';
2
3
  const SECTION_ALIASES = ['实现输入', 'Implementation Inputs'];
3
4
  const COLUMNS = [
@@ -5,7 +6,6 @@ const COLUMNS = [
5
6
  'decided_at', 'status', 'consumed_by'
6
7
  ];
7
8
  const TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/;
8
- const ARTIFACT_RE = /^code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/;
9
9
  function validateInput(row) {
10
10
  if (!/^II-[1-9]\d*$/.test(row.id))
11
11
  throw new Error(`implementation input id '${row.id}' is invalid`);
@@ -17,7 +17,7 @@ function validateInput(row) {
17
17
  const valid = (row.status === 'declared' && row.decidedAt === '' && row.consumedBy === '') ||
18
18
  (hasValidTimestamp && row.needsImplementation && row.status === 'pending' && row.consumedBy === '') ||
19
19
  (hasValidTimestamp && !row.needsImplementation && row.status === 'not-required' && row.consumedBy === '') ||
20
- (hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && ARTIFACT_RE.test(row.consumedBy));
20
+ (hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && parseArtifactName(row.consumedBy)?.family === 'code');
21
21
  if (!valid)
22
22
  throw new Error(`implementation input ${row.id} has an invalid state combination`);
23
23
  }
@@ -122,7 +122,7 @@ function selectPendingImplementationInput(rows) {
122
122
  })[0] ?? null;
123
123
  }
124
124
  function consumeImplementationInput(rows, id, artifact) {
125
- if (!ARTIFACT_RE.test(artifact))
125
+ if (parseArtifactName(artifact)?.family !== 'code')
126
126
  throw new Error(`implementation input artifact '${artifact}' is invalid`);
127
127
  let found = false;
128
128
  const next = rows.map((row) => {