@fitlab-ai/agent-infra 0.11.10 → 0.11.12

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 (219) hide show
  1. package/dist/bin/internal-cli.js +2 -2
  2. package/dist/lib/agent-clients/adapter.js +27 -1
  3. package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
  9. package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
  10. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
  12. package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
  13. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
  14. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +222 -210
  15. package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
  16. package/dist/lib/agent-clients/adapters/codex-sandbox.js +13 -0
  17. package/dist/lib/agent-clients/adapters/codex.js +44 -1
  18. package/dist/lib/agent-clients/adapters/index.js +2 -10
  19. package/dist/lib/agent-clients/tokens.js +3 -2
  20. package/dist/lib/internal/agent-client.js +132 -72
  21. package/dist/lib/internal/cli-route-inventory.js +2 -2
  22. package/dist/lib/internal/manual-validation.js +2 -2
  23. package/dist/lib/internal/platform-pr.js +4 -5
  24. package/dist/lib/internal/task-artifact.js +1 -1
  25. package/dist/lib/internal/task-create.js +15 -1
  26. package/dist/lib/internal/task-finalization.js +1 -3
  27. package/dist/lib/internal/task-operation-registry.js +5 -139
  28. package/dist/lib/internal/task-validate.js +1 -1
  29. package/dist/lib/platform/issues.js +18 -6
  30. package/dist/lib/platform/pr-summary.js +45 -8
  31. package/dist/lib/platform/pull-requests.js +79 -14
  32. package/dist/lib/platform/verification-sync.js +17 -14
  33. package/dist/lib/runtime/agent-runtime.js +6 -29
  34. package/dist/lib/sandbox/commands/control-status.js +1 -0
  35. package/dist/lib/sandbox/commands/create.js +19 -2
  36. package/dist/lib/sandbox/commands/enter.js +47 -33
  37. package/dist/lib/sandbox/control/agent-operation.js +34 -0
  38. package/dist/lib/sandbox/control/client.js +68 -161
  39. package/dist/lib/sandbox/control/executor.js +13 -87
  40. package/dist/lib/sandbox/control/host-recovery.js +218 -0
  41. package/dist/lib/sandbox/control/protocol.js +15 -44
  42. package/dist/lib/sandbox/control/server.js +57 -51
  43. package/dist/lib/sandbox/environment.js +150 -0
  44. package/dist/lib/sandbox/mounts.js +2 -1
  45. package/dist/lib/sandbox/recovery.js +11 -1
  46. package/dist/lib/sandbox/removal.js +9 -5
  47. package/dist/lib/sandbox/workspace-identity.js +0 -14
  48. package/dist/lib/sandbox/workspace-view.js +41 -23
  49. package/dist/lib/task/artifact-command.js +7 -2
  50. package/dist/lib/task/artifact-lifecycle.js +2 -1
  51. package/dist/lib/task/capabilities.js +4 -1
  52. package/dist/lib/task/control-authority.js +54 -266
  53. package/dist/lib/task/control-recovery.js +1 -1
  54. package/dist/lib/task/create.js +2 -2
  55. package/dist/lib/task/delegation-receipts.js +74 -161
  56. package/dist/lib/task/events.js +89 -45
  57. package/dist/lib/task/finalization.js +58 -60
  58. package/dist/lib/task/frontmatter.js +24 -1
  59. package/dist/lib/task/ledger-intents.js +2 -1
  60. package/dist/lib/task/lifecycle.js +84 -97
  61. package/dist/lib/task/orchestration.js +182 -129
  62. package/dist/lib/task/platform-operation-journal.js +12 -0
  63. package/dist/lib/task/platform-operation-recovery.js +14 -7
  64. package/dist/lib/task/resolve-ref.js +8 -3
  65. package/dist/lib/task/runtime-paths.js +10 -0
  66. package/dist/lib/task/verification-engine.js +28 -15
  67. package/dist/lib/task/workspace-snapshot.js +1 -1
  68. package/dist/lifecycle-build-manifest.json +3 -3
  69. package/lib/agent-clients/adapter.ts +61 -0
  70. package/lib/agent-clients/adapters/claude-code.ts +5 -1
  71. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
  72. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
  73. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
  74. package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
  75. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
  76. package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
  77. package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
  78. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
  79. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
  80. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
  81. package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
  82. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
  83. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +272 -223
  84. package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
  85. package/lib/agent-clients/adapters/codex-sandbox.ts +13 -0
  86. package/lib/agent-clients/adapters/codex.ts +48 -1
  87. package/lib/agent-clients/adapters/index.ts +7 -20
  88. package/lib/agent-clients/tokens.ts +4 -3
  89. package/lib/internal/agent-client.ts +142 -73
  90. package/lib/internal/cli-route-inventory.ts +2 -2
  91. package/lib/internal/manual-validation.ts +2 -2
  92. package/lib/internal/platform-pr.ts +3 -4
  93. package/lib/internal/task-artifact.ts +1 -1
  94. package/lib/internal/task-create.ts +15 -2
  95. package/lib/internal/task-finalization.ts +1 -3
  96. package/lib/internal/task-operation-registry.ts +5 -151
  97. package/lib/internal/task-validate.ts +1 -1
  98. package/lib/platform/issues.ts +18 -6
  99. package/lib/platform/pr-summary.ts +43 -7
  100. package/lib/platform/pull-requests.ts +74 -13
  101. package/lib/platform/verification-sync.ts +18 -14
  102. package/lib/runtime/agent-runtime.ts +6 -41
  103. package/lib/sandbox/commands/control-status.ts +1 -0
  104. package/lib/sandbox/commands/create.ts +25 -2
  105. package/lib/sandbox/commands/enter.ts +42 -39
  106. package/lib/sandbox/control/agent-operation.ts +49 -0
  107. package/lib/sandbox/control/client.ts +86 -210
  108. package/lib/sandbox/control/executor.ts +17 -96
  109. package/lib/sandbox/control/host-recovery.ts +195 -0
  110. package/lib/sandbox/control/protocol.ts +24 -55
  111. package/lib/sandbox/control/server.ts +54 -52
  112. package/lib/sandbox/environment.ts +162 -0
  113. package/lib/sandbox/mounts.ts +2 -1
  114. package/lib/sandbox/recovery.ts +13 -1
  115. package/lib/sandbox/removal.ts +12 -5
  116. package/lib/sandbox/tool-types.ts +1 -0
  117. package/lib/sandbox/workspace-identity.ts +0 -26
  118. package/lib/sandbox/workspace-view.ts +42 -24
  119. package/lib/task/artifact-command.ts +9 -2
  120. package/lib/task/artifact-lifecycle.ts +2 -1
  121. package/lib/task/capabilities.ts +5 -2
  122. package/lib/task/control-authority.ts +65 -382
  123. package/lib/task/control-recovery.ts +2 -2
  124. package/lib/task/create.ts +3 -3
  125. package/lib/task/delegation-receipts.ts +87 -231
  126. package/lib/task/events.ts +101 -43
  127. package/lib/task/finalization.ts +57 -58
  128. package/lib/task/frontmatter.ts +25 -1
  129. package/lib/task/ledger-intents.ts +2 -1
  130. package/lib/task/lifecycle.ts +82 -87
  131. package/lib/task/orchestration.ts +237 -176
  132. package/lib/task/platform-operation-journal.ts +29 -2
  133. package/lib/task/platform-operation-recovery.ts +12 -6
  134. package/lib/task/resolve-ref.ts +8 -3
  135. package/lib/task/runtime-paths.ts +12 -0
  136. package/lib/task/verification-engine.ts +27 -16
  137. package/lib/task/workspace-snapshot.ts +1 -1
  138. package/package.json +1 -1
  139. package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
  140. package/templates/.agents/rules/README.en.md +2 -2
  141. package/templates/.agents/rules/README.zh-CN.md +2 -2
  142. package/templates/.agents/rules/issue-sync.en.md +1 -1
  143. package/templates/.agents/rules/issue-sync.zh-CN.md +1 -1
  144. package/templates/.agents/rules/next-step-output.en.md +6 -19
  145. package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
  146. package/templates/.agents/rules/no-mid-flow-questions.en.md +19 -0
  147. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +19 -0
  148. package/templates/.agents/rules/review-handshake.en.md +3 -0
  149. package/templates/.agents/rules/review-handshake.zh-CN.md +3 -0
  150. package/templates/.agents/rules/task-management.en.md +1 -1
  151. package/templates/.agents/rules/task-management.zh-CN.md +1 -1
  152. package/templates/.agents/skills/analyze-task/SKILL.en.md +2 -2
  153. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
  154. package/templates/.agents/skills/block-task/SKILL.en.md +1 -1
  155. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +1 -1
  156. package/templates/.agents/skills/cancel-task/SKILL.en.md +1 -1
  157. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +1 -1
  158. package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
  159. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
  160. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -0
  161. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -0
  162. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -0
  163. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -0
  164. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  165. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  166. package/templates/.agents/skills/code-task/reference/branch-management.en.md +1 -1
  167. package/templates/.agents/skills/code-task/reference/branch-management.zh-CN.md +1 -1
  168. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +2 -0
  169. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +2 -0
  170. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +1 -1
  171. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +1 -1
  172. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +1 -2
  173. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -2
  174. package/templates/.agents/skills/complete-task/SKILL.en.md +0 -3
  175. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +0 -3
  176. package/templates/.agents/skills/create-pr/SKILL.en.md +1 -1
  177. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
  178. package/templates/.agents/skills/create-pr/reference/branch-strategy.en.md +2 -2
  179. package/templates/.agents/skills/create-pr/reference/branch-strategy.zh-CN.md +2 -2
  180. package/templates/.agents/skills/create-task/SKILL.en.md +3 -5
  181. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +3 -4
  182. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  183. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  184. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  185. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  186. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  187. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  188. package/templates/.agents/skills/plan-task/SKILL.en.md +1 -1
  189. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +1 -1
  190. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  191. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
  192. package/templates/.agents/skills/review-code/SKILL.en.md +5 -5
  193. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -5
  194. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  195. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
  196. package/templates/.agents/skills/review-pr/SKILL.en.md +1 -1
  197. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -1
  198. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  199. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  200. package/templates/.agents/skills/test/SKILL.en.md +4 -1
  201. package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -1
  202. package/templates/.agents/skills/test-integration/SKILL.en.md +5 -2
  203. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +5 -2
  204. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +4 -1
  205. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +4 -1
  206. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +5 -2
  207. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +5 -2
  208. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  209. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  210. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
  211. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
  212. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
  213. package/dist/lib/internal/codex-lifecycle.js +0 -385
  214. package/dist/lib/task/codex-orchestration.js +0 -206
  215. package/dist/lib/task/finalization-handoff.js +0 -93
  216. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
  217. package/lib/internal/codex-lifecycle.ts +0 -390
  218. package/lib/task/codex-orchestration.ts +0 -276
  219. package/lib/task/finalization-handoff.ts +0 -105
@@ -33,7 +33,18 @@ type PlatformPullRequestIntent = Readonly<{
33
33
  closingIssue?: boolean;
34
34
  }>;
35
35
  type PlatformIssueCreateIntent = Readonly<{ title: string; bodyDigest: string }>;
36
- type PlatformPullRequestSummaryIntent = Readonly<{ body: string; changeReportFile: string }>;
36
+ type PlatformPullRequestSummaryManualValidationIntent = Readonly<{
37
+ phase: 'pending' | 'final';
38
+ transactionId: string;
39
+ receiptDigest?: string;
40
+ prHeadSha?: string;
41
+ authority?: 'coordinator';
42
+ }>;
43
+ type PlatformPullRequestSummaryIntent = Readonly<{
44
+ body: string;
45
+ changeReportFile: string;
46
+ manualValidation?: PlatformPullRequestSummaryManualValidationIntent;
47
+ }>;
37
48
  type PlatformPullRequestReviewIntent = Readonly<{
38
49
  prNumber: string;
39
50
  resource: ResourceIdentity;
@@ -147,6 +158,22 @@ function parseJournal(file: string, taskId: string): PlatformOperationJournal {
147
158
  ? !item.pullRequestSummary || typeof item.pullRequestSummary.body !== 'string'
148
159
  || !item.pullRequestSummary.body.trim() || typeof item.pullRequestSummary.changeReportFile !== 'string'
149
160
  || !item.pullRequestSummary.changeReportFile.trim()
161
+ || (item.pullRequestSummary.manualValidation !== undefined && (
162
+ !item.pullRequestSummary.manualValidation
163
+ || typeof item.pullRequestSummary.manualValidation !== 'object'
164
+ || Array.isArray(item.pullRequestSummary.manualValidation)
165
+ || !['pending', 'final'].includes(item.pullRequestSummary.manualValidation.phase)
166
+ || typeof item.pullRequestSummary.manualValidation.transactionId !== 'string'
167
+ || !item.pullRequestSummary.manualValidation.transactionId.trim()
168
+ || (item.pullRequestSummary.manualValidation.receiptDigest !== undefined && !/^[a-f0-9]{64}$/u.test(item.pullRequestSummary.manualValidation.receiptDigest))
169
+ || (item.pullRequestSummary.manualValidation.prHeadSha !== undefined && !/^[a-f0-9]{40}$/u.test(item.pullRequestSummary.manualValidation.prHeadSha))
170
+ || (item.pullRequestSummary.manualValidation.authority !== undefined && item.pullRequestSummary.manualValidation.authority !== 'coordinator')
171
+ || (item.pullRequestSummary.manualValidation.phase === 'final' && (
172
+ !/^[a-f0-9]{64}$/u.test(item.pullRequestSummary.manualValidation.receiptDigest ?? '')
173
+ || !/^[a-f0-9]{40}$/u.test(item.pullRequestSummary.manualValidation.prHeadSha ?? '')
174
+ || item.pullRequestSummary.manualValidation.authority !== 'coordinator'
175
+ ))
176
+ ))
150
177
  : item.pullRequestSummary !== undefined)
151
178
  || (item.kind === 'pull-request-review'
152
179
  ? !item.pullRequestReview || typeof item.pullRequestReview.prNumber !== 'string'
@@ -234,4 +261,4 @@ function readPlatformOperationJournal(taskRef: string, cwd?: string): PlatformOp
234
261
  }
235
262
 
236
263
  export { JOURNAL_FILE as PLATFORM_OPERATION_JOURNAL_FILE, operationId, recordPlatformOperation, readPlatformOperationJournal };
237
- export type { PlatformIssueCreateIntent, PlatformIssueMetadataIntent, PlatformOperation, PlatformOperationJournal, PlatformOperationKind, PlatformOperationState, PlatformPullRequestIntent, PlatformPullRequestReviewIntent, PlatformPullRequestSummaryIntent, RecordOperationInput };
264
+ export type { PlatformIssueCreateIntent, PlatformIssueMetadataIntent, PlatformOperation, PlatformOperationJournal, PlatformOperationKind, PlatformOperationState, PlatformPullRequestIntent, PlatformPullRequestReviewIntent, PlatformPullRequestSummaryIntent, PlatformPullRequestSummaryManualValidationIntent, RecordOperationInput };
@@ -187,6 +187,14 @@ async function replayPullRequestSummary(taskId: string, operation: ReturnType<ty
187
187
  let identity;
188
188
  try { identity = parseResourceIdentity(JSON.parse(operation.target), 'queued pull-request summary identity'); }
189
189
  catch { return platformResult('failed', { error: { code: 'PLATFORM_OPERATION_PAYLOAD_INVALID', message: 'Queued pull-request summary identity is invalid', retryable: false } }); }
190
+ const summary = operation.pullRequestSummary;
191
+ if (!summary) return platformResult('blocked', { error: {
192
+ code: 'PLATFORM_OPERATION_PAYLOAD_INVALID', message: 'Pull-request summary recovery content is missing', retryable: false
193
+ } });
194
+ const manualValidationHeading = /^###\s+(?:⏳\s+(?:Manual Validation Pending|人工验证待收尾)|✅\s+(?:Manual Validation Passed|人工验证已通过))\s*$/mu.test(summary.body);
195
+ if (manualValidationHeading && !summary.manualValidation) return platformResult('blocked', { error: {
196
+ code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'Queued manual-validation summary has no persisted transaction context', retryable: false
197
+ } });
190
198
  const resolved = await resolvePlatformProviderContext({ cwd, client });
191
199
  if (!resolved.ok) return resolved.context;
192
200
  const listed = resolved.value.provider.comments?.list
@@ -198,12 +206,9 @@ async function replayPullRequestSummary(taskId: string, operation: ReturnType<ty
198
206
  if (summaries.length > 1) return platformResult('blocked', { error: {
199
207
  code: 'PR_SUMMARY_MARKER_AMBIGUOUS', message: 'Multiple pull-request comments contain the queued summary marker', retryable: false
200
208
  } });
201
- if (summaries.length === 1 && createHash('sha256').update(normalizeCommentContent(summaries[0]!.body)).digest('hex') === operation.expectedDigest) {
209
+ if (!summary.manualValidation && summaries.length === 1 && createHash('sha256').update(normalizeCommentContent(summaries[0]!.body)).digest('hex') === operation.expectedDigest) {
202
210
  return platformResult('no-op');
203
211
  }
204
- if (!operation.pullRequestSummary) return platformResult('blocked', { error: {
205
- code: 'PLATFORM_OPERATION_PAYLOAD_INVALID', message: 'Pull-request summary recovery content is missing', retryable: false
206
- } });
207
212
  const bound = await inspectPlatformPullRequest(taskId, { cwd, client });
208
213
  if (!bound.pullRequest || !resourceIdentityEquals(bound.pullRequest.identity, identity)) return platformResult('blocked', { error: bound.error ?? {
209
214
  code: 'PLATFORM_OPERATION_IDENTITY_MISMATCH', message: 'Bound pull request differs from the queued summary target', retryable: false
@@ -212,8 +217,9 @@ async function replayPullRequestSummary(taskId: string, operation: ReturnType<ty
212
217
  agent,
213
218
  cwd,
214
219
  client,
215
- body: operation.pullRequestSummary.body,
216
- changeReportFile: operation.pullRequestSummary.changeReportFile,
220
+ body: summary.body,
221
+ changeReportFile: summary.changeReportFile,
222
+ ...(summary.manualValidation ? { manualValidation: summary.manualValidation } : {}),
217
223
  primaryResult: 'no_op',
218
224
  strict: true,
219
225
  skipQueue: true,
@@ -139,13 +139,18 @@ function enumerateTaskDirs(repoRoot: string): { taskId: string; taskDir: string
139
139
  */
140
140
  function enumerateAllTaskDirs(repoRoot: string): { taskId: string; taskDir: string; state: TaskWorkspaceState }[] {
141
141
  const out: { taskId: string; taskDir: string; state: TaskWorkspaceState }[] = [];
142
- const seen = new Set<string>();
142
+ const seen = new Map<string, string>();
143
143
  const add = (taskId: string, taskDir: string, state: TaskWorkspaceState) => {
144
144
  if (!TASK_ID_RE.test(taskId)) return;
145
145
  const stat = fs.lstatSync(taskDir);
146
146
  if (!stat.isDirectory() || stat.isSymbolicLink() || !fs.existsSync(path.join(taskDir, 'task.md'))) return;
147
- if (seen.has(taskId)) throw new Error(`duplicate task identity: ${taskId}`);
148
- seen.add(taskId);
147
+ const canonicalDir = fs.realpathSync.native(taskDir);
148
+ const previousDir = seen.get(taskId);
149
+ if (previousDir !== undefined) {
150
+ if (previousDir === canonicalDir) return;
151
+ throw new Error(`duplicate task identity: ${taskId}`);
152
+ }
153
+ seen.set(taskId, canonicalDir);
149
154
  out.push({ taskId, taskDir, state });
150
155
  };
151
156
 
@@ -0,0 +1,12 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { resolveTaskRef } from './resolve-ref.ts';
4
+
5
+ export function resolveTaskRuntimeRoot(
6
+ taskRef: string,
7
+ options: Readonly<{ repoRoot?: string }> = {}
8
+ ): string {
9
+ const resolved = resolveTaskRef(taskRef, options);
10
+ if (!resolved.ok) throw new Error(`${resolved.code}: ${resolved.message}`);
11
+ return path.join(fs.realpathSync.native(resolved.taskDir), '.runtime');
12
+ }
@@ -43,6 +43,7 @@ import { manualValidationFinalSummaryProjectionMatches } from "./manual-validati
43
43
  import { readManualValidationCompletion } from "./manual-validation-completion.ts";
44
44
  import { sha256File } from "./artifact-receipts.ts";
45
45
  import { inspectArtifactDirectory } from "./artifact-lifecycle.ts";
46
+ import { getAgentClientAdapter } from "../agent-clients/registry.ts";
46
47
  import { parseArtifactName } from "./artifact-name.ts";
47
48
  import { summaryCommentState } from "../platform/pr-summary.ts";
48
49
  import { taskIssueIdentity } from "../platform/task-identities.ts";
@@ -147,7 +148,7 @@ async function runCheck(type: any, context: any, shared: any): Promise<any> {
147
148
  }
148
149
 
149
150
  function checkOrchestrationState({ taskDir }: any): any {
150
- const file = path.join(taskDir, 'orchestration.json');
151
+ const file = path.join(taskDir, '.runtime', 'orchestration.json');
151
152
  const stat = safeStat(file);
152
153
  if (!stat?.isFile()) return failResult('orchestration-state', 'orchestration.json is missing');
153
154
  let run: OrchestrationRun | null;
@@ -160,6 +161,8 @@ function checkOrchestrationState({ taskDir }: any): any {
160
161
  if (!run || !['paused', 'completed'].includes(run.status)) {
161
162
  return failResult('orchestration-state', `Expected paused or completed run, received '${run?.status ?? 'missing'}'`);
162
163
  }
164
+ const adapterEvidenceError = validateAdapterReceiptEvidence(run);
165
+ if (adapterEvidenceError) return failResult('orchestration-state', adapterEvidenceError);
163
166
  if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
164
167
  return failResult('orchestration-state', 'Paused run requires a stable pause code and message');
165
168
  }
@@ -215,8 +218,19 @@ function validateCleanCompletionEvidence(run: OrchestrationRun): string | null {
215
218
  return null;
216
219
  }
217
220
 
221
+ function validateAdapterReceiptEvidence(run: OrchestrationRun): string | null {
222
+ const receipts = [...run.receipts, ...(run.pendingDelegation ? [run.pendingDelegation] : [])];
223
+ for (const receipt of receipts) {
224
+ const validate = getAgentClientAdapter(receipt.client).orchestrationAdapter?.validateReceiptEvidence;
225
+ if (validate && !validate(receipt)) {
226
+ return `Receipt '${receipt.id}' has invalid evidence from its agent adapter`;
227
+ }
228
+ }
229
+ return null;
230
+ }
231
+
218
232
  function checkOrchestrationEvidence({ taskDir }: any): any {
219
- const file = path.join(taskDir, 'orchestration.json');
233
+ const file = path.join(taskDir, '.runtime', 'orchestration.json');
220
234
  const stat = safeStat(file);
221
235
  if (!stat?.isFile()) return failResult('orchestration-evidence', 'orchestration.json is missing');
222
236
  let run: OrchestrationRun | null;
@@ -227,6 +241,8 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
227
241
  return failResult('orchestration-evidence', `Invalid orchestration.json: ${message}`);
228
242
  }
229
243
  if (!run) return failResult('orchestration-evidence', 'orchestration.json is missing');
244
+ const adapterEvidenceError = validateAdapterReceiptEvidence(run);
245
+ if (adapterEvidenceError) return failResult('orchestration-evidence', adapterEvidenceError);
230
246
  // readRun validates the persisted schema, receipt identities and client provenance.
231
247
  const policy = run.modelPolicy;
232
248
  if (run.completionEvidence != null) {
@@ -249,20 +265,22 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
249
265
  }
250
266
  const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
251
267
  if (activated) {
252
- const isClaudeCode = receipt.client === 'claude-code';
253
- if (!isClaudeCode) {
268
+ const evidencePolicy = getAgentClientAdapter(receipt.client).delegationEvidence;
269
+ if (evidencePolicy.actualModelRequired !== false) {
254
270
  if (!exactText(receipt.actualModel)) {
255
271
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual model`);
256
272
  }
273
+ }
274
+ if (evidencePolicy.actualReasoningEffortRequired !== false) {
257
275
  if (!exactText(receipt.actualReasoningEffort)) {
258
276
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual reasoning effort`);
259
277
  }
260
278
  }
261
- if (!isClaudeCode && receipt.spawnMode !== 'fresh') {
279
+ if (evidencePolicy.spawnModeRequired !== false && receipt.spawnMode !== 'fresh') {
262
280
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid fresh delegation identity`);
263
281
  }
264
282
  if (receipt.actualModel != null && receipt.actualModel !== receipt.requestedModel) {
265
- if (!isClaudeCode && !exactText(receipt.modelFallbackReason)) {
283
+ if (evidencePolicy.fallbackReasonRequired !== false && !exactText(receipt.modelFallbackReason)) {
266
284
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' model fallback is not justified`);
267
285
  }
268
286
  }
@@ -273,7 +291,7 @@ function checkOrchestrationEvidence({ taskDir }: any): any {
273
291
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated model fallback reason`);
274
292
  }
275
293
  if (receipt.actualReasoningEffort != null && receipt.actualReasoningEffort !== receipt.requestedReasoningEffort) {
276
- if (!isClaudeCode && !exactText(receipt.reasoningEffortFallbackReason)) {
294
+ if (evidencePolicy.fallbackReasonRequired !== false && !exactText(receipt.reasoningEffortFallbackReason)) {
277
295
  return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' reasoning-effort fallback is not justified`);
278
296
  }
279
297
  }
@@ -790,7 +808,6 @@ function checkActivityLog({ taskDir, config, skillName, artifactFile }: any): an
790
808
  const entries = section.entries;
791
809
  if (entries.length === 0) return failResult("activity-log", "Activity Log has no entries");
792
810
 
793
- let previousTimestamp = "";
794
811
  let latestAction = "";
795
812
  let latestTimestamp = "";
796
813
  const doneActions: string[] = [];
@@ -798,14 +815,8 @@ function checkActivityLog({ taskDir, config, skillName, artifactFile }: any): an
798
815
  for (const entry of entries) {
799
816
  if (!entry.note.trim()) return failResult("activity-log", "Activity Log entry note is required");
800
817
  const { time: timestamp, step: action } = entry;
801
- if (previousTimestamp && timestamp < previousTimestamp) {
802
- return failResult("activity-log", "Activity Log timestamps are not in ascending order");
803
- }
804
-
805
- previousTimestamp = timestamp;
806
- // Ascending order is checked over every entry, but a `[started]` marker is
807
- // not a terminal action: keep latestAction/latestTimestamp on the most
808
- // recent done entry so expected_action_pattern sees it.
818
+ // A `[started]` marker is not a terminal action: keep latestAction/latestTimestamp
819
+ // on the most recent done entry so expected_action_pattern sees it.
809
820
  if (!ACTIVITY_LOG_STARTED_RE.test(action)) {
810
821
  latestTimestamp = timestamp;
811
822
  latestAction = action;
@@ -67,7 +67,7 @@ function taskFileFingerprints(stateRoot: string, taskId: string | null): TaskFil
67
67
  return;
68
68
  }
69
69
  if (!stat.isFile() && !stat.isSymbolicLink()) return;
70
- if (relative.startsWith('.agents/workspace/active/') && path.posix.basename(relative) === 'orchestration.json') return;
70
+ if (/^\.agents\/workspace\/(?:active|blocked|completed)\/TASK-[0-9]{8}-[0-9]{6}\/\.runtime\/orchestration\.json$/u.test(relative)) return;
71
71
  const content = stat.isSymbolicLink()
72
72
  ? Buffer.from(fs.readlinkSync(absolute))
73
73
  : fs.readFileSync(absolute);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fitlab-ai/agent-infra",
3
- "version": "0.11.10",
3
+ "version": "0.11.12",
4
4
  "description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Antigravity CLI, and OpenCode",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -16,28 +16,6 @@ function internalCliInvocation(args) {
16
16
  return { command: 'agent-infra-internal', commandArgs: args, shell: process.platform === 'win32' };
17
17
  }
18
18
 
19
- function findCapabilityRef(value, depth = 0) {
20
- if (depth > 5 || value == null) return '';
21
- if (typeof value === 'string') {
22
- const match = /agent-infra-codex-capability:([A-Za-z0-9_-]{20,})/.exec(value);
23
- return match?.[1] || '';
24
- }
25
- if (Array.isArray(value)) {
26
- for (const entry of value) {
27
- const found = findCapabilityRef(entry, depth + 1);
28
- if (found) return found;
29
- }
30
- return '';
31
- }
32
- if (typeof value === 'object') {
33
- for (const entry of Object.values(value)) {
34
- const found = findCapabilityRef(entry, depth + 1);
35
- if (found) return found;
36
- }
37
- }
38
- return '';
39
- }
40
-
41
19
  let input = '';
42
20
  process.stdin.setEncoding('utf8');
43
21
  process.stdin.on('data', (chunk) => {
@@ -77,11 +55,10 @@ process.stdin.on('end', () => {
77
55
  process.exit(1);
78
56
  }
79
57
  const managedAgent = /^agent-infra-lifecycle-(executor|reviewer)$/.test(String(nativeAgent || ''));
80
- const capabilityRef = hook === 'post-tool' ? findCapabilityRef(toolResponse) : '';
81
58
  const parentReconcile = hook === 'post-tool'
82
59
  && toolName === 'collaborationwait_agent'
83
60
  && toolResponse.timed_out !== true;
84
- if (!managedAgent && !parentReconcile && !capabilityRef) process.exit(0);
61
+ if (!managedAgent && !parentReconcile) process.exit(0);
85
62
  const hookDefinitionHash = createHash('sha256').update(readFileSync(codexHooks)).digest('hex');
86
63
  const normalized = {
87
64
  sessionId: String(event.session_id || ''),
@@ -96,7 +73,6 @@ process.stdin.on('end', () => {
96
73
  taskName: String(toolInput.task_name || ''),
97
74
  transcriptPath: String(event.transcript_path || '')
98
75
  };
99
- if (capabilityRef) normalized.capabilityRef = capabilityRef;
100
76
  const args = ['codex-lifecycle', 'hook-event', '--event', hook, '--bridge', 'true'];
101
77
  try {
102
78
  const { command, commandArgs, shell } = internalCliInvocation(args);
@@ -8,8 +8,8 @@ so you can quickly find "which ones to read" without opening each file.
8
8
 
9
9
  ## General Principles
10
10
 
11
- - [`no-mid-flow-questions.md`](no-mid-flow-questions.md) — Silence during SKILL runs: no user questions by default, plus the exceptions the rule lists.
12
- - [`next-step-output.md`](next-step-output.md) — "Next step" output rules: task short-id rendering and the `Completed at` trailer.
11
+ - [`no-mid-flow-questions.md`](no-mid-flow-questions.md) — Question boundaries, error diagnosis order, and autonomous recovery during SKILL runs.
12
+ - [`next-step-output.md`](next-step-output.md) — "Next step" output rules: task short-id rendering, pending human decisions, and workflow warnings.
13
13
  - [`version-stamp.md`](version-stamp.md) — How and when to stamp `agent_infra_version`.
14
14
  - [`debugging-guide.md`](debugging-guide.md) — Structured debugging flow: gather evidence → form hypothesis → verify hypothesis → fix the root cause; no blind patch-and-retry.
15
15
  - [`compatibility-policy.md`](compatibility-policy.md) — Compatibility by exception: admission evidence, implementation boundaries, exit conditions, and lifecycle review requirements.
@@ -7,8 +7,8 @@
7
7
 
8
8
  ## 通用准则
9
9
 
10
- - [`no-mid-flow-questions.md`](no-mid-flow-questions.md) — SKILL 执行期禁言:默认不向用户提问,及规则列明的例外。
11
- - [`next-step-output.md`](next-step-output.md) — 「下一步」输出规则:任务短号渲染与 `Completed at` 收尾行。
10
+ - [`no-mid-flow-questions.md`](no-mid-flow-questions.md) — SKILL 执行期提问边界、错误诊断顺序和自主恢复原则。
11
+ - [`next-step-output.md`](next-step-output.md) — 「下一步」输出规则:任务短号、人工裁决提示与工作流告警。
12
12
  - [`version-stamp.md`](version-stamp.md) — `agent_infra_version` 版本戳的取值命令与写入时机。
13
13
  - [`debugging-guide.md`](debugging-guide.md) — 结构化调试流程:收集证据→形成假设→验证假设→修复根因,禁止盲目改代码重试。
14
14
  - [`compatibility-policy.md`](compatibility-policy.md) — 兼容性默认关闭:准入证据、实现边界、退出条件与生命周期审查要求。
@@ -35,7 +35,7 @@ The task comment synchronizes only a deterministic current-task projection. Proc
35
35
 
36
36
  Recovery comments use `recovery-action`, `recovery-prepare`, and `recovery-commit` markers with a stable recovery id and a body file. Task sync publishes prepare, action, a task snapshot that is written and re-read, and then commit. Replaying identical content is a no-op; conflicting immutable content fails with `RECOVERY_IMMUTABLE_CONFLICT`. Restore accepts only an author-consistent, complete, phase/digest/marker-valid transaction bound to the current task comment body SHA-256.
37
37
 
38
- PR event `in:` synchronization uses `agent-infra-internal platform-pr sync-in-labels --pr <N> [--cwd <path>]`. PR files are the event evidence; a unique closing Issue is written and re-read before the PR. Zero or multiple closing Issues update only the PR and return `degraded`; unknown side effects or failed convergence return `blocked` with `IN_LABEL_SYNC_PARTIAL`.
38
+ PR event `in:` synchronization uses `agent-infra-internal platform-pr sync-in-labels --pr <token> [--cwd <path>]`. The shared entry turns the token into a resource identity using the provider declaration, then passes that identity to the provider; the provider interprets the token for its platform. PR files are the event evidence; a unique closing Issue is written and re-read before the PR. Zero or multiple closing Issues update only the PR and return `degraded`; unknown side effects or failed convergence return `blocked` with `IN_LABEL_SYNC_PARTIAL`.
39
39
 
40
40
  `planned|applied|no-op|degraded` exit 0; `failed` exits 1; `blocked` exits 2.
41
41
 
@@ -76,7 +76,7 @@ agent-infra-internal platform-issue sync {task-id} --agent {standard-agent-token
76
76
 
77
77
  status labels 始终至多一个;关闭后不保留 status label。`in:` labels 只从项目允许映射与仓库实际 labels 交集产生。需求复选框以 task.md 原文为身份,歧义时 fail closed。
78
78
 
79
- PR event 的 `in:` 同步使用 `agent-infra-internal platform-pr sync-in-labels --pr <N> [--cwd <path>]`。PR files 是事件证据;唯一 closing Issue 按 Issue→PR 顺序写入并复读,零或多个 closing Issue 只写 PR 并返回 `degraded`。写入后副作用不明或复读未收敛必须返回 `blocked` 与 `IN_LABEL_SYNC_PARTIAL`。
79
+ PR event 的 `in:` 同步使用 `agent-infra-internal platform-pr sync-in-labels --pr <token> [--cwd <path>]`。共享入口按 provider 声明把 token 转成资源 identity,再将 identity 交给 provider;token 的平台语义由 provider 处理。PR files 是事件证据;唯一 closing Issue 按 Issue→PR 顺序写入并复读,零或多个 closing Issue 只写 PR 并返回 `degraded`。写入后副作用不明或复读未收敛必须返回 `blocked` 与 `IN_LABEL_SYNC_PARTIAL`。
80
80
 
81
81
  ## 补发
82
82
 
@@ -1,11 +1,10 @@
1
1
  # Next-Step Output Rule
2
2
 
3
- This file defines four **independent** rules for a skill's "notify-user / Next steps" output (the 3rd applies to review-* only); read this file before rendering the final output and apply whichever rules apply:
3
+ This file defines three **independent** rules for a skill's "notify-user / Next steps" output (the 2nd applies to review-* only); read this file before rendering the final output and apply whichever rules apply:
4
4
 
5
5
  1. **Next-step output structure**: how "Next steps" commands and the "Task info" block present the task ID (placeholders / short-id lookup / fallback).
6
- 2. **Agent output trailing line (Completed at)**: the **very last line** of user-facing output, **independent of the "Next steps" block**, applying to normal / error / early-return paths alike.
7
- 3. **Pending human-decision pre-block**: applies only to `review-analysis` / `review-plan` / `review-code` when this stage has pending rulings (`{h} > 0`) — expand the pending items before the "Next steps" commands and prompt to resolve them first.
8
- 4. **Workflow Warnings output block**: applies when task.md has `status=open` rows in `## Workflow Warnings` — print the warning summary after all normal information and "Next steps" commands, and before `Completed at`.
6
+ 2. **Pending human-decision pre-block**: applies only to `review-analysis` / `review-plan` / `review-code` when this stage has pending rulings (`{h} > 0`) — expand the pending items before the "Next steps" commands and prompt to resolve them first.
7
+ 3. **Workflow Warnings output block**: applies when task.md has `status=open` rows in `## Workflow Warnings` — print the warning summary after all normal information and "Next steps" commands.
9
8
 
10
9
  ## Placeholder semantics
11
10
 
@@ -34,7 +33,7 @@ agent-infra-internal agent-client next-steps \
34
33
  - Pass the next skill name for the already-selected scenario. Include `--task-ref` only when that command needs a task reference.
35
34
  - Include `--version` only for a versioned release command. It must start with a digit and satisfy `semver.valid(raw) === raw`; never pass an action, complete command, shell fragment, whitespace, or a `v` / `V` / `=` prefix.
36
35
  - The helper lists enabled built-in clients, followed by selected sandbox custom tools with a valid `invoke` field in `sandbox.tools.ids` order.
37
- - Insert non-empty stdout verbatim at `{next-step-commands}` below the current "Next steps" heading. When stdout is empty, omit the client command block while still rendering reminders, warnings, and `Completed at`.
36
+ - Insert non-empty stdout verbatim at `{next-step-commands}` below the current "Next steps" heading. When stdout is empty, omit the client command block while still rendering reminders and warnings.
38
37
  - If the helper writes stderr or exits non-zero, use the current skill's error path and stop; never fall back to a hard-coded client table.
39
38
  - A complex skill selects one scenario first and invokes the helper once for that scenario; it must not pre-render every branch.
40
39
 
@@ -69,21 +68,9 @@ process.stdout.write(hit?hit[0]:full);
69
68
 
70
69
  Short ids are always rendered as zero-padded bare digits `NN`. The removed `#NN` syntax must neither be generated nor accepted, avoiding bash comment interpretation.
71
70
 
72
- ## Agent output trailing line (Completed at)
73
-
74
- This section is a standalone rule, **co-equal with the next-step output structure** and **not part of the "Next steps" block**. Every skill that renders user-facing output must append the completion-time line as the **very last line** of that output — including **complete-task, which renders no next-step commands**, and **error / early-return paths** where a precondition is unmet. This lets users scanning across tmux windows tell at a glance which agent finished most recently:
75
-
76
- ```text
77
- Completed at: YYYY-MM-DD HH:mm:ss
78
- ```
79
-
80
- - Value command (local timezone, no offset): `date "+%Y-%m-%d %H:%M:%S"`
81
- - Position: it must be the last line of the entire user-facing output, after all "Next steps" commands. If a scenario has a conditional reminder line after the commands (e.g. the manual-validation reminder), the completion line goes after that reminder.
82
- - This line is for terminal scanning only; it is never written to any artifact file or Issue/PR comment. The single source of truth for completion time remains the Activity Log in task.md.
83
-
84
71
  ## Workflow Warnings Output Block
85
72
 
86
- If the current task's `## Workflow Warnings` / `## 工作流告警` table has any `status=open` row, the skill's final user output must append a summary block after all normal information and "Next steps" commands, and before `Completed at`. When no open warning exists, do not render this block.
73
+ If the current task's `## Workflow Warnings` / `## 工作流告警` table has any `status=open` row, the skill's final user output must append a summary block after all normal information and "Next steps" commands. When no open warning exists, do not render this block.
87
74
 
88
75
  Format:
89
76
 
@@ -96,7 +83,7 @@ Use `[ACTION REQUIRED]` when any open row has `severity=ACTION_REQUIRED`; otherw
96
83
 
97
84
  ## Pending human-decision pre-block (review-* only, when {h} > 0)
98
85
 
99
- This section is a **third standalone rule, co-equal with the two above**, used only by the "notify-user / report conclusion" step of `review-analysis` / `review-plan` / `review-code`.
86
+ This section is the **second independent rule**, used only by the "notify-user / report conclusion" step of `review-analysis` / `review-plan` / `review-code`.
100
87
 
101
88
  `{h}` has the same meaning as the count line in each review skill's `reference/output-templates.md`: the number of rows in task.md `## 审查分歧账本` (Review Disagreement Ledger) for **this stage** (`stage ∈ {analysis|plan|code}`) whose `status = needs-human-decision` — **pending items only, excluding rows already `human-decided`**.
102
89
 
@@ -1,11 +1,10 @@
1
1
  # 下一步输出规则
2
2
 
3
- 本文件定义 skill「告知用户 / 下一步」输出的四类**相互独立**的规则(第 3 类仅 review-* 适用);渲染最终输出前先读取本文件并落实其中适用的规则:
3
+ 本文件定义 skill「告知用户 / 下一步」输出的三类**相互独立**的规则(第 2 类仅 review-* 适用);渲染最终输出前先读取本文件并落实其中适用的规则:
4
4
 
5
5
  1. **下一步输出结构**:「下一步」命令与「任务信息」段如何呈现任务 ID 形态(占位符 / 取短号 / 回退)。
6
- 2. **Agent 输出收尾行(Completed at)**:面向用户输出的**绝对最后一行**,**独立于「下一步」块**,正常 / 错误 / 早退路径都适用。
7
- 3. **人工裁决待办前置块**:仅 `review-analysis` / `review-plan` / `review-code`,且本阶段存在待裁决项(`{h} > 0`)时适用——在「下一步」命令前展开待裁决项并提示先完成裁决。
8
- 4. **Workflow Warnings 输出块**:当前 task.md 存在 `status=open` 的 `## 工作流告警` 行时适用——在所有常规信息和「下一步」命令之后、`Completed at` 之前输出告警摘要。
6
+ 2. **人工裁决待办前置块**:仅 `review-analysis` / `review-plan` / `review-code`,且本阶段存在待裁决项(`{h} > 0`)时适用——在「下一步」命令前展开待裁决项并提示先完成裁决。
7
+ 3. **Workflow Warnings 输出块**:当前 task.md 存在 `status=open` 的 `## 工作流告警` 行时适用——在所有常规信息和「下一步」命令之后输出告警摘要。
9
8
 
10
9
  ## 占位符语义
11
10
 
@@ -34,7 +33,7 @@ agent-infra-internal agent-client next-steps \
34
33
  - 静态传入当前已选场景的下一 skill 名;只有命令需要任务引用时才传 `--task-ref`。
35
34
  - 只有版本化发布命令需要版本时才传 `--version`;值必须是以数字开头且 `semver.valid(raw) === raw` 的规范 SemVer。不得传 action、完整命令、shell 片段、空白或 `v` / `V` / `=` 前缀。
36
35
  - helper 展示 `.agents/.airc.json` 中 `agentClients` 已启用的内建客户端,并按 `sandbox.tools.ids` 顺序追加具有有效 `invoke` 的已选自定义沙箱工具。
37
- - 将非空 stdout 原样放入当前“下一步”标题后的 `{next-step-commands}`;stdout 为空时省略整个客户端命令块,但继续输出提醒、告警和 `Completed at`。
36
+ - 将非空 stdout 原样放入当前“下一步”标题后的 `{next-step-commands}`;stdout 为空时省略整个客户端命令块,但继续输出提醒和告警。
38
37
  - helper 写 stderr 或返回非零时,按当前 skill 的错误路径报告并停止;禁止回退到硬编码客户端表。
39
38
  - 复杂 skill 先选定唯一场景,再只为该场景调用一次 helper,不得提前渲染所有分支。
40
39
 
@@ -69,21 +68,9 @@ process.stdout.write(hit?hit[0]:full);
69
68
 
70
69
  短号统一渲染为零填充裸数字 `NN`。已移除的 `#NN` 语法不得生成或接受,避免 bash 将其解释为注释。
71
70
 
72
- ## Agent 输出收尾行(Completed at)
73
-
74
- 本节是与「下一步输出结构」**并列的独立规则**,不隶属于「下一步」块。任何向用户渲染输出的 skill 都必须在面向用户输出的**绝对最后一行**追加完成时间收尾行——包括**声明「不渲染下一步命令」的 complete-task**,以及前置条件未满足而提前 return 的**错误 / 早退路径**。便于用户在 tmux 多窗口扫视时一眼判断各 Agent 的完成先后:
75
-
76
- ```text
77
- Completed at: YYYY-MM-DD HH:mm:ss
78
- ```
79
-
80
- - 取值命令(本地时区、不带偏移):`date "+%Y-%m-%d %H:%M:%S"`
81
- - 位置:必须是整段面向用户输出的最后一行,排在所有「下一步」命令之后。若某场景在命令之后还有条件性提醒行(如 manual-validation 提醒),收尾行排在该提醒行之后。
82
- - 该行只用于终端扫视,不写入任何产物文件或 Issue/PR 评论;完成时刻的单一事实源仍是 task.md 的 Activity Log。
83
-
84
71
  ## Workflow Warnings 输出块
85
72
 
86
- 若当前任务的 `## 工作流告警` / `## Workflow Warnings` 表中存在 `status=open` 行,skill 最终输出必须在所有常规信息和「下一步」命令之后、`Completed at` 之前追加摘要块;无 open 告警时不输出本块。
73
+ 若当前任务的 `## 工作流告警` / `## Workflow Warnings` 表中存在 `status=open` 行,skill 最终输出必须在所有常规信息和「下一步」命令之后追加摘要块;无 open 告警时不输出本块。
87
74
 
88
75
  格式:
89
76
 
@@ -96,7 +83,7 @@ Completed at: YYYY-MM-DD HH:mm:ss
96
83
 
97
84
  ## 人工裁决待办前置块(review-* 专用,{h} > 0 时)
98
85
 
99
- 本节是与上面两类规则**并列的第三类独立规则**,仅 `review-analysis` / `review-plan` / `review-code` 的「向用户汇报结论」步骤使用。
86
+ 本节是与下一步输出结构并列的第二类规则,仅 `review-analysis` / `review-plan` / `review-code` 的「向用户汇报结论」步骤使用。
100
87
 
101
88
  `{h}` 含义与各 review 技能 `reference/output-templates.md` 计数行一致:task.md `## 审查分歧账本` 中**本阶段**(`stage ∈ {analysis|plan|code}`)`status = needs-human-decision` 的行数——**只含待裁决项,不含已 `human-decided`**。
102
89
 
@@ -57,6 +57,15 @@ SKILLs currently covered by this exemption:
57
57
 
58
58
  - `run-task`: may collect only the atomic model policy explicitly required by core
59
59
 
60
+ ### Exemption 6: Necessary facts or security accountability that cannot be inferred
61
+
62
+ Only when the executor has checked the current task, configuration, code, Git, and platform records and still cannot safely infer a user-specific fact or security-accountability information required to proceed may it ask the user for that missing information. Keep the question limited to the missing fact itself. Do not ask for ordinary implementation preferences that existing evidence can resolve, and do not turn an exhausted recovery budget into general permission to ask.
63
+
64
+ SKILLs currently covered by this exemption:
65
+
66
+ - `create-pr`: may request the target base branch when it cannot be derived from user input, configuration, Git history, or PR facts
67
+ - `close-codescan` and `close-dependabot`: may request the reason or accountability statement required for a security-alert disposition; the final dismissal still follows its dedicated confirmation gate
68
+
60
69
  ## No-Mid-Flow-Questions Clause (default behavior)
61
70
 
62
71
  For every SKILL execution context not covered by any exemption above, the default behavior is:
@@ -98,6 +107,16 @@ If a key decision needs human judgment during execution, follow the rule above a
98
107
 
99
108
  The `run-task` orchestrator may continue after a child skill artifact only when core mechanical state permits it; this does not bypass human decisions. When core reports a human decision, manual validation, or another stable pause condition, the orchestrator must stop without asking mid-flow or approving it itself.
100
109
 
110
+ ## Error diagnosis and recovery order
111
+
112
+ When a tool, gate, platform, or local operation fails, read the complete error and structured result first. Determine state from the operation identity, acceptance status, side effects, receipt, and explicit retryability. Do not infer network failure, non-execution, or a need for user intervention from an exit code, wording, or temporary lack of output alone.
113
+
114
+ 1. Correct Agent-generated formatting/schema errors explicitly rejected by a validator; preserve domain meaning, revalidate with the same validator, then retry within bounds.
115
+ 2. For operations explicitly known not to have executed and marked retryable, fix the diagnosed cause first and preserve the idempotency identity required by the contract; obey existing no-progress, repeated-diagnostic, and self-healing budgets.
116
+ 3. For accepted operations, unknown outcomes, or possible side effects, retain the original request identity and receipt and follow typed recovery to inspect or recover; do not change the payload, issue another business write, or claim success.
117
+ 4. For temporary blocks, record the exact state and recovery condition. Continue read-only, local, or other independent work that does not depend on the blocked operation. A blocked step remains incomplete and must not be treated as success.
118
+ 5. Ask for user action only when a safety boundary cannot be verified, recovery requires user-specific facts or authorization, user workspace changes cannot be safely preserved, or the allowed recovery budget is exhausted with no safe independent work remaining; necessary facts or security-accountability information follow the scope of Exemption 6. Continue to honor domain hard gates for security, permissions, human review, manual validation, explicit publish authorization, and delivery stages.
119
+
101
120
  ## Anchor Location
102
121
 
103
122
  This rule's sole global anchor lives in the project-level AGENTS.md "AI Behavior Principles" preamble, which every AI tool loads. Individual SKILL.md files no longer reference this rule, so no per-skill duplicate bullet needs to be maintained.
@@ -57,6 +57,15 @@
57
57
 
58
58
  - `run-task`:仅补齐核心明确要求的原子模型策略
59
59
 
60
+ ### 例外 6:无法推导的必要事实或安全责任信息
61
+
62
+ 仅当已检查当前任务、配置、代码、Git 与平台记录后,仍无法安全推导继续执行所必需的用户专属事实或安全责任信息时,才可请求用户提供缺失内容。问题必须限定在缺失事实本身;不得询问可由现有证据决定的普通实现偏好,也不得把恢复预算耗尽泛化为提问许可。
63
+
64
+ 当前归入本例外的 SKILL:
65
+
66
+ - `create-pr`:目标 base 无法从用户输入、配置、Git 历史或 PR 事实推导时,可请求目标分支
67
+ - `close-codescan`、`close-dependabot`:安全告警处置需要用户提供理由或责任说明时,可请求该说明;最终关闭仍遵守专属确认门禁
68
+
60
69
  ## 禁言条款(默认行为)
61
70
 
62
71
  不属于上述任一例外的所有 SKILL 执行场景,遵循以下默认行为:
@@ -98,6 +107,16 @@
98
107
 
99
108
  `run-task` 总控可以在子技能产出后依据核心机械状态继续到下一阶段;这不构成绕过人工裁决。只要核心返回人工裁决、人工验证或其他稳定暂停条件,总控必须停止,不能向用户中途提问,也不能自行批准。
100
109
 
110
+ ## 错误诊断与恢复顺序
111
+
112
+ 遇到工具、校验、平台或本地操作失败时,先读取完整错误与结构化结果,再按当前操作的身份、是否接纳、已发生副作用、receipt 和明确的 retryability 判断状态。不得仅凭退出码、错误措辞或暂时无输出推断网络故障、操作未执行或需要用户介入。
113
+
114
+ 1. 修复由 Agent 自身生成且被 validator 明确拒绝的格式/schema 错误;保留领域含义,使用同一 validator 验证后再有限重试。
115
+ 2. 对明确未执行且允许重试的操作,先处理已诊断的根因,保留合同要求的幂等身份;遵守现有无进展、重复诊断和自愈预算。
116
+ 3. 对已接纳、结果未知或有副作用的操作,保留原请求身份和 receipt,按 typed recovery 检查或恢复;不得改 payload、另发业务写入或宣称成功。
117
+ 4. 对暂时阻塞,记录准确状态和恢复条件;继续不依赖该阻塞的只读、本地或其他独立工作。阻塞的步骤仍未完成,不得降格为成功。
118
+ 5. 仅当安全边界无法验证、恢复需要用户独有事实/授权、用户工作区改动无法安全保留,或允许的恢复预算耗尽且无安全的独立工作时,才请求用户动作;必要事实或安全责任信息遵循例外 6 的范围。遇到安全、权限、人工审查、人工验证、显式发布授权和交付阶段门时继续遵守其专属硬门禁。
119
+
101
120
  ## 锚点位置
102
121
 
103
122
  本规则的唯一全局锚点在项目级 AGENTS.md 的「AI 行为准则」preamble,所有 AI 工具加载 AGENTS.md 时即可见。各 SKILL.md 不再单独引用本规则,避免在每个 SKILL 顶部维护重复 bullet。
@@ -20,6 +20,8 @@ For each finding in the latest `review-*`, first Read/Grep the cited `file:line`
20
20
  | `refuted` | After verification, judged invalid / hallucinated / based on a wrong `file:line` | counter-evidence (`file:line` or raw command output); awaits reviewer confirmation |
21
21
  | `cannot-judge` | Insufficient evidence to decide | the verification path attempted; handed to reviewer/human |
22
22
 
23
+ `finding-respond --round` is the finding's handshake round, independent of the current code/plan/analysis artifact round. The executor reads each finding's current ledger `round` from `task-ledger stage-status --stage {stage}` and submits current value plus one; different findings may require different response rounds.
24
+
23
25
  ## Reviewer hand-back duty (`review-*` skills, when re-reviewing the executor response)
24
26
 
25
27
  After the executor gives `adjusted` / `refuted` / `cannot-judge`, the reviewer must respond per item — never re-reading the original finding nor ignoring the hand-back:
@@ -64,6 +66,7 @@ The single source of truth for disagreement state is the fixed `## 审查分歧
64
66
  - **Terminal set (gate passes)**: `{confirmed, closed, human-decided}`; everything else is blocking.
65
67
  - **Same-round fix-and-close**: only when the reviewer fixes a `minor` finding during the current review round may `finding-review` move that row directly from `open` to `closed`. This transition does not increment `round`, and `evidence` must point to the fix in the current review artifact; it does not apply to `blocker` or `major` findings.
66
68
  - **Severity is independent from advancement**: `blocker` / `major` / `minor` express impact only. Every formal finding blocks approval until terminal. After all writes, review verdicts, event counts, and next steps must be derived from `task-ledger stage-status --stage {stage}`.
69
+ - **Evidence correction on open findings**: `finding-review` may keep a finding `open` while updating its evidence anchor to correct or refine an unresolved report reference; this does not change the finding round or terminal state.
67
70
  - **Non-blocking advisories**: only future optimizations that do not affect the current artifact's completeness, correctness, or acceptance. Advisories stay in a separate report section and never enter this ledger, finding counts, or verdict; manual-validation remains a separate category.
68
71
  - **Write responsibility**: callers submit structured intents only; they do not scan ids, assemble table rows, or decide mechanical transitions. `review-*` uses `agent-infra-internal task-ledger {task-id} finding-upsert|finding-review ...`; `*-task` uses `finding-respond ...`; `ai decide` applies human rulings atomically. The core validates and commits each intent through one task write.
69
72
  - **Backward compatible**: when task.md has no such section the gate treats it as no open disagreements and passes.
@@ -20,6 +20,8 @@
20
20
  | `refuted` | 核实后判定不成立 / 幻觉 / 基于错误 `file:line` | 反证(`file:line` 或命令原文);待检视方确认 |
21
21
  | `cannot-judge` | 证据不足,无法判断 | 已尝试的核实路径;交检视方/人工 |
22
22
 
23
+ `finding-respond --round` 使用 finding 自身的握手轮次,不使用当前 code/plan/analysis artifact 的轮次。执行方先从 `task-ledger stage-status --stage {stage}` 读取该 finding 当前账本 `round`,响应时传入当前值加一;不同 finding 的响应轮次可能不同。
24
+
23
25
  ## 检视方回交义务(`review-*` 技能,对执行方响应复核时)
24
26
 
25
27
  执行方给出 `adjusted` / `refuted` / `cannot-judge` 后,检视方必须逐条回应,不得复读原意见或无视:
@@ -64,6 +66,7 @@
64
66
  - **终态集合(gate 放行)**:`{confirmed, closed, human-decided}`;其余为阻塞态。
65
67
  - **同轮 fix-and-close**:仅当检视方在当前审查轮当场修复 `minor` finding 时,可通过 `finding-review` 将该行从 `open` 直接置为 `closed`。此转换不增加 `round`,`evidence` 必须指向当前 review 产物中的修复证据;`blocker` / `major` 不适用。
66
68
  - **severity 与推进解耦**:`blocker` / `major` / `minor` 只表示影响大小。任何正式 finding 只要尚未进入终态就阻止当前阶段通过;review 结论、事件计数与下一步必须在全部写入后通过 `task-ledger stage-status --stage {stage}` 从同一语义导出。
69
+ - **open finding 证据修正**:`finding-review` 可保持 finding 为 `open` 并更新证据锚点,以修正或细化未解决意见的报告引用;该操作不改变 finding 轮次或终态。
67
70
  - **非阻塞 advisory**:仅限不影响当前产物完整性、正确性和验收的后续优化。advisory 只写入报告的独立段落,不进入本账本、不进入 finding 计数、不影响 verdict;manual-validation 仍是独立分类。
68
71
  - **写入责任**:调用方只提交结构化意图,不扫描编号、不拼表格行、不自行判断机械状态迁移。`review-*` 用 `agent-infra-internal task-ledger {task-id} finding-upsert|finding-review ...`;`*-task` 用 `finding-respond ...`;人工裁决由 `ai decide` 原子完成。核心统一校验并通过一次任务写入提交。
69
72
  - **向后兼容**:task.md 无此段时,gate 视为无未决分歧而放行。
@@ -38,7 +38,7 @@ The seven internal commands (`task-event` / `task-lifecycle` / `task-finalizatio
38
38
 
39
39
  ## Platform Results and Skill Blocking Boundaries
40
40
 
41
- A `blocked` platform write means only that the operation is unfinished. Whether the skill stops depends on its delivery goal. The typed core handles write queuing, recovery, and idempotency; skills consume structured results and do not replay writes themselves.
41
+ A `blocked` platform write or `task-verify` result means that operation or gate is incomplete; it does not by itself mean a network interruption, that the user must intervene, or success. Skills inspect the structured summary, preserve the blocked state and recovery condition, and continue only independent work. They do not replay platform writes; typed core owns their queuing, recovery, and idempotency.
42
42
 
43
43
  - `import-issue`: stop importing when the source Issue cannot be read.
44
44
  - `create-pr`: stop the creation flow when the PR was not created or its identity cannot be confirmed.
@@ -38,7 +38,7 @@
38
38
 
39
39
  ## 平台结果与技能阻塞边界
40
40
 
41
- 平台写入返回 `blocked` 只表示该操作尚未完成;技能是否停止由当前技能的交付目标决定。平台写入的排队、恢复和幂等由 typed core 执行,技能只消费结构化结果,不自行重放。
41
+ 平台写入或 `task-verify` 返回 `blocked` 表示对应操作/校验尚未完成,不表示网络中断、用户必须介入或成功。技能按当前交付目标读取结构化摘要,保留 blocked 与恢复条件;只继续不依赖该阻塞的独立工作,不自行重放平台写入。平台写入的排队、恢复和幂等由 typed core 执行。
42
42
 
43
43
  - `import-issue`:源 Issue 读取失败时停止导入。
44
44
  - `create-pr`:PR 未创建或无法确认其身份时停止创建流程。