@fitlab-ai/agent-infra 0.9.13 → 0.9.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/bin/cli.ts +26 -9
  2. package/dist/bin/cli.js +173 -157
  3. package/dist/bin/internal-cli.js +42 -21
  4. package/dist/lib/agent-client.js +5 -0
  5. package/dist/lib/internal/agent-client.js +5 -2
  6. package/dist/lib/internal/cli-route-inventory.js +152 -0
  7. package/dist/lib/internal/codex-lifecycle.js +9 -6
  8. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  9. package/dist/lib/internal/git-workflow.js +10 -6
  10. package/dist/lib/internal/platform-checks.js +16 -4
  11. package/dist/lib/internal/platform-comment.js +16 -4
  12. package/dist/lib/internal/platform-context.js +4 -1
  13. package/dist/lib/internal/platform-issue.js +12 -4
  14. package/dist/lib/internal/platform-metadata.js +88 -0
  15. package/dist/lib/internal/platform-pr-review.js +16 -3
  16. package/dist/lib/internal/platform-pr.js +24 -10
  17. package/dist/lib/internal/platform-release-notes.js +6 -3
  18. package/dist/lib/internal/platform-security.js +93 -0
  19. package/dist/lib/internal/pr-review-grade.js +14 -3
  20. package/dist/lib/internal/release-workflow.js +19 -5
  21. package/dist/lib/internal/sandbox-control.js +22 -5
  22. package/dist/lib/internal/task-activity.js +13 -4
  23. package/dist/lib/internal/task-artifact.js +186 -27
  24. package/dist/lib/internal/task-context.js +4 -1
  25. package/dist/lib/internal/task-create.js +48 -15
  26. package/dist/lib/internal/task-delivery.js +4 -1
  27. package/dist/lib/internal/task-event.js +4 -1
  28. package/dist/lib/internal/task-finalization.js +4 -1
  29. package/dist/lib/internal/task-invalidation.js +4 -1
  30. package/dist/lib/internal/task-ledger.js +12 -1
  31. package/dist/lib/internal/task-lifecycle.js +4 -1
  32. package/dist/lib/internal/task-operation-registry.js +421 -0
  33. package/dist/lib/internal/task-orchestration.js +12 -0
  34. package/dist/lib/internal/task-override.js +10 -3
  35. package/dist/lib/internal/task-qualification.js +90 -0
  36. package/dist/lib/internal/task-review.js +4 -1
  37. package/dist/lib/internal/task-short-id.js +11 -1
  38. package/dist/lib/internal/task-snapshot.js +4 -1
  39. package/dist/lib/internal/task-validate.js +7 -2
  40. package/dist/lib/internal/task-verify.js +5 -1
  41. package/dist/lib/internal/task-warning.js +8 -1
  42. package/dist/lib/platform/comment-safety.js +226 -0
  43. package/dist/lib/platform/github-provider.js +233 -6
  44. package/dist/lib/platform/issue-comments.js +146 -34
  45. package/dist/lib/platform/pr-change-report.js +6 -12
  46. package/dist/lib/platform/pr-summary.js +28 -7
  47. package/dist/lib/platform/provider-contract.d.ts +67 -1
  48. package/dist/lib/platform/provider-contract.js +6 -2
  49. package/dist/lib/platform/provider-validation.js +76 -3
  50. package/dist/lib/platform/pull-requests.js +115 -170
  51. package/dist/lib/platform/repository-metadata.js +377 -0
  52. package/dist/lib/platform/security-alerts.js +155 -0
  53. package/dist/lib/platform/verification-sync.js +33 -26
  54. package/dist/lib/process-data/index.js +7 -0
  55. package/dist/lib/run/index.js +5 -0
  56. package/dist/lib/sandbox/commands/control-status.js +22 -0
  57. package/dist/lib/sandbox/commands/create.js +32 -6
  58. package/dist/lib/sandbox/commands/ls.js +5 -0
  59. package/dist/lib/sandbox/commands/rm.js +827 -120
  60. package/dist/lib/sandbox/commands/show.js +16 -0
  61. package/dist/lib/sandbox/control/client.js +83 -9
  62. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  63. package/dist/lib/sandbox/control/container-identity.js +28 -31
  64. package/dist/lib/sandbox/control/executor.js +149 -14
  65. package/dist/lib/sandbox/control/lifecycle.js +621 -127
  66. package/dist/lib/sandbox/control/native-file-lock.js +177 -0
  67. package/dist/lib/sandbox/control/server.js +305 -35
  68. package/dist/lib/sandbox/control/state.js +5 -3
  69. package/dist/lib/sandbox/control/task-view.js +183 -0
  70. package/dist/lib/sandbox/engines/authority.js +289 -0
  71. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  72. package/dist/lib/sandbox/index.js +5 -0
  73. package/dist/lib/sandbox/recovery.js +21 -1
  74. package/dist/lib/sandbox/workspace-view.js +2 -1
  75. package/dist/lib/sandbox/worktree-safety.js +4 -1
  76. package/dist/lib/server/index.js +8 -0
  77. package/dist/lib/task/artifact-lifecycle.js +42 -13
  78. package/dist/lib/task/artifact-operations.js +520 -0
  79. package/dist/lib/task/artifact-repair-intent.js +59 -0
  80. package/dist/lib/task/artifact-schema.js +135 -0
  81. package/dist/lib/task/capabilities.js +72 -1
  82. package/dist/lib/task/control-authority.js +4 -0
  83. package/dist/lib/task/create-service.js +103 -4
  84. package/dist/lib/task/create.js +25 -0
  85. package/dist/lib/task/events.js +186 -67
  86. package/dist/lib/task/index.js +11 -3
  87. package/dist/lib/task/local-artifact-finalization.js +75 -150
  88. package/dist/lib/task/orchestration.js +192 -52
  89. package/dist/lib/task/qualification-audit.js +490 -0
  90. package/dist/lib/task/qualification-confirmation.js +109 -0
  91. package/dist/lib/task/qualification-intents.js +148 -0
  92. package/dist/lib/task/review-artifacts.js +22 -23
  93. package/dist/lib/task/review-finalization.js +121 -4
  94. package/dist/lib/task/sections.js +6 -4
  95. package/dist/lib/task/verification-config.js +11 -0
  96. package/dist/lib/task/verification-engine.js +54 -4
  97. package/dist/lifecycle-build-manifest.json +3 -3
  98. package/lib/agent-client.ts +9 -0
  99. package/lib/internal/agent-client.ts +4 -2
  100. package/lib/internal/cli-route-inventory.ts +140 -0
  101. package/lib/internal/codex-lifecycle.ts +8 -6
  102. package/lib/internal/codex-sandbox-controller.ts +7 -2
  103. package/lib/internal/git-workflow.ts +9 -6
  104. package/lib/internal/platform-checks.ts +12 -4
  105. package/lib/internal/platform-comment.ts +12 -4
  106. package/lib/internal/platform-context.ts +3 -1
  107. package/lib/internal/platform-issue.ts +11 -4
  108. package/lib/internal/platform-metadata.ts +70 -0
  109. package/lib/internal/platform-pr-review.ts +14 -3
  110. package/lib/internal/platform-pr.ts +23 -10
  111. package/lib/internal/platform-release-notes.ts +5 -3
  112. package/lib/internal/platform-security.ts +83 -0
  113. package/lib/internal/pr-review-grade.ts +10 -3
  114. package/lib/internal/release-workflow.ts +17 -5
  115. package/lib/internal/sandbox-control.ts +19 -5
  116. package/lib/internal/task-activity.ts +12 -4
  117. package/lib/internal/task-artifact.ts +164 -25
  118. package/lib/internal/task-context.ts +3 -1
  119. package/lib/internal/task-create.ts +76 -15
  120. package/lib/internal/task-delivery.ts +3 -1
  121. package/lib/internal/task-event.ts +3 -1
  122. package/lib/internal/task-finalization.ts +3 -1
  123. package/lib/internal/task-invalidation.ts +3 -1
  124. package/lib/internal/task-ledger.ts +11 -1
  125. package/lib/internal/task-lifecycle.ts +3 -1
  126. package/lib/internal/task-operation-registry.ts +482 -0
  127. package/lib/internal/task-orchestration.ts +12 -0
  128. package/lib/internal/task-override.ts +9 -3
  129. package/lib/internal/task-qualification.ts +61 -0
  130. package/lib/internal/task-review.ts +3 -1
  131. package/lib/internal/task-short-id.ts +10 -1
  132. package/lib/internal/task-snapshot.ts +3 -1
  133. package/lib/internal/task-validate.ts +6 -2
  134. package/lib/internal/task-verify.ts +4 -1
  135. package/lib/internal/task-warning.ts +7 -1
  136. package/lib/platform/comment-safety.ts +250 -0
  137. package/lib/platform/github-provider.ts +229 -7
  138. package/lib/platform/issue-comments.ts +160 -35
  139. package/lib/platform/pr-change-report.ts +6 -12
  140. package/lib/platform/pr-summary.ts +27 -6
  141. package/lib/platform/provider-contract.ts +78 -3
  142. package/lib/platform/provider-validation.ts +85 -3
  143. package/lib/platform/pull-requests.ts +126 -178
  144. package/lib/platform/repository-metadata.ts +453 -0
  145. package/lib/platform/security-alerts.ts +191 -0
  146. package/lib/platform/verification-sync.ts +32 -28
  147. package/lib/process-data/index.ts +7 -0
  148. package/lib/run/index.ts +5 -0
  149. package/lib/sandbox/commands/control-status.ts +26 -0
  150. package/lib/sandbox/commands/create.ts +46 -6
  151. package/lib/sandbox/commands/ls.ts +7 -0
  152. package/lib/sandbox/commands/rm.ts +1000 -87
  153. package/lib/sandbox/commands/show.ts +16 -0
  154. package/lib/sandbox/control/client.ts +103 -9
  155. package/lib/sandbox/control/completed-reentry.ts +98 -0
  156. package/lib/sandbox/control/container-identity.ts +27 -43
  157. package/lib/sandbox/control/executor.ts +177 -12
  158. package/lib/sandbox/control/lifecycle.ts +629 -17
  159. package/lib/sandbox/control/native-file-lock.ts +209 -0
  160. package/lib/sandbox/control/protocol.ts +5 -2
  161. package/lib/sandbox/control/server.ts +319 -37
  162. package/lib/sandbox/control/state.ts +6 -3
  163. package/lib/sandbox/control/task-view.ts +240 -0
  164. package/lib/sandbox/engines/authority.ts +365 -0
  165. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  166. package/lib/sandbox/index.ts +7 -0
  167. package/lib/sandbox/recovery.ts +21 -1
  168. package/lib/sandbox/workspace-view.ts +9 -3
  169. package/lib/sandbox/worktree-safety.ts +4 -1
  170. package/lib/server/index.ts +10 -0
  171. package/lib/task/artifact-lifecycle.ts +38 -16
  172. package/lib/task/artifact-operations.ts +626 -0
  173. package/lib/task/artifact-repair-intent.ts +71 -0
  174. package/lib/task/artifact-schema.ts +192 -0
  175. package/lib/task/capabilities.ts +70 -1
  176. package/lib/task/control-authority.ts +11 -1
  177. package/lib/task/create-service.ts +131 -6
  178. package/lib/task/create.ts +27 -0
  179. package/lib/task/events.ts +161 -65
  180. package/lib/task/index.ts +16 -3
  181. package/lib/task/local-artifact-finalization.ts +87 -218
  182. package/lib/task/orchestration.ts +172 -49
  183. package/lib/task/qualification-audit.ts +599 -0
  184. package/lib/task/qualification-confirmation.ts +117 -0
  185. package/lib/task/qualification-intents.ts +150 -0
  186. package/lib/task/review-artifacts.ts +21 -22
  187. package/lib/task/review-finalization.ts +133 -4
  188. package/lib/task/sections.ts +6 -4
  189. package/lib/task/verification-config.ts +12 -0
  190. package/lib/task/verification-engine.ts +55 -4
  191. package/package.json +6 -1
  192. package/templates/.agents/rules/README.en.md +2 -0
  193. package/templates/.agents/rules/README.zh-CN.md +2 -0
  194. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  195. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  196. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  197. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  198. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  199. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  200. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  201. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  202. package/templates/.agents/rules/security-alerts.en.md +8 -8
  203. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  204. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  205. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  206. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  207. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  208. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  209. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  210. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  211. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  212. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  213. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  214. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  215. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  216. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  217. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  218. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  219. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  220. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  221. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  222. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  223. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  224. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -1
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -1
  226. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  227. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  228. package/templates/.agents/skills/complete-task/SKILL.en.md +9 -22
  229. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +9 -22
  230. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  231. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  232. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  233. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  234. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  235. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  236. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  237. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  238. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  239. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  240. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  241. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  242. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  243. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  244. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  245. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  246. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  247. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  248. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  249. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  250. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  251. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  252. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  253. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  254. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  255. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  256. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  257. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  258. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  259. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  260. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  261. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  262. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  263. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  264. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  265. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  266. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -1
  267. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -1
  268. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
  269. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
  270. package/templates/.agents/skills/test/SKILL.en.md +13 -2
  271. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
  272. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  273. package/templates/.agents/templates/task.en.md +6 -0
  274. package/templates/.agents/templates/task.zh-CN.md +6 -0
  275. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  276. package/templates/.agents/scripts/security-alerts.sh +0 -31
  277. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  278. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  279. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  280. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -14,11 +14,13 @@ import { commitOrchestrationStageCompletion } from './orchestration.js';
14
14
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
15
15
  import { captureTaskWriteMetadata, writeTask } from './write.js';
16
16
  import { allowsManualOverride } from './guard-override.js';
17
- import { LOCAL_ARTIFACT_REQUIRED_PATTERNS, LOCAL_ARTIFACT_REQUIRED_SECTIONS, consumeLocalArtifactFinalizationIntent, readLocalArtifactFinalizationIntent, validateLocalArtifact } from './local-artifact-finalization.js';
18
- import { loadVerificationConfig } from './verification-config.js';
17
+ import { consumeLocalArtifactFinalizationIntent, readLocalArtifactFinalizationIntent, validateLocalArtifact } from './local-artifact-finalization.js';
19
18
  import { buildLifecycleFacts, canStart } from './capabilities.js';
20
19
  import { createInvalidationOperation, invalidationMutation, parseInvalidationDocument, targetIdFor, upsertInvalidation } from './invalidation.js';
21
20
  import { consumeReworkIntents, parseReworkIntentDocument, reworkIntentMutation, supersedeReworkIntents } from './rework-intent.js';
21
+ import { ARTIFACT_FAMILIES, expectedQualificationRelations, parseQualificationAudit, parseTaskQualification, upstreamArtifactDigest, validateQualificationAudit } from './qualification-audit.js';
22
+ import { getArtifactSchema } from './artifact-schema.js';
23
+ import { inspectArtifactContract } from './artifact-operations.js';
22
24
  const eventCatalog = [
23
25
  'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
24
26
  'review-analysis.started', 'review-analysis.completed',
@@ -49,31 +51,6 @@ const SCHEMAS = {
49
51
  'validation-run.started': { optional: ['round'] },
50
52
  'validation-run.completed': { required: ['artifact'], optional: ['round'] }
51
53
  };
52
- function localArtifactValidationConfig(repoRoot, family) {
53
- const fallback = {
54
- requiredSections: LOCAL_ARTIFACT_REQUIRED_SECTIONS[family],
55
- requiredPatterns: LOCAL_ARTIFACT_REQUIRED_PATTERNS
56
- };
57
- const skillName = family === 'analysis' ? 'analyze-task' : family === 'plan' ? 'plan-task' : 'code-task';
58
- try {
59
- const artifact = loadVerificationConfig(repoRoot, skillName).checks.artifact;
60
- if (!artifact || typeof artifact !== 'object' || Array.isArray(artifact))
61
- return fallback;
62
- const sections = artifact.required_sections;
63
- const patterns = artifact.required_patterns;
64
- return {
65
- requiredSections: Array.isArray(sections)
66
- ? sections.filter((value) => typeof value === 'string')
67
- : fallback.requiredSections,
68
- requiredPatterns: Array.isArray(patterns)
69
- ? patterns.filter((value) => typeof value === 'string')
70
- : fallback.requiredPatterns
71
- };
72
- }
73
- catch {
74
- return fallback;
75
- }
76
- }
77
54
  function validateTaskEventRequest(request) {
78
55
  if (!eventCatalog.includes(request.event))
79
56
  return { code: 'EVENT_UNKNOWN', message: `unknown task event '${request.event}'` };
@@ -300,6 +277,24 @@ function openStartedIdentity(rows, family) {
300
277
  function reviewInputFamily(family) {
301
278
  return family === 'review-analysis' ? 'analysis' : family === 'review-plan' ? 'plan' : 'code';
302
279
  }
280
+ function relationForStartedInput(family, input) {
281
+ if (family.startsWith('review-')) {
282
+ return input.family === reviewInputFamily(family) ? 'reviewed-input' : 'approval-context';
283
+ }
284
+ const consumer = family === 'analyze' ? 'analysis' : family;
285
+ if ((consumer === 'plan' && input.family === 'analysis') || (consumer === 'code' && input.family === 'plan'))
286
+ return 'required-input';
287
+ return 'review-context';
288
+ }
289
+ function qualificationRelationsForStarted(family, inputs) {
290
+ return inputs.map((input) => ({
291
+ upstreamFamily: input.family,
292
+ upstreamArtifact: input.name,
293
+ upstreamRound: input.round,
294
+ upstreamSha256: sha256File(input.path),
295
+ relation: relationForStartedInput(family, input)
296
+ }));
297
+ }
303
298
  function lifecycleAction(family) {
304
299
  return family === 'analyze' ? 'analysis' : family;
305
300
  }
@@ -328,6 +323,7 @@ function invalidationMutationForCompletion(content, taskDir, family, artifact, t
328
323
  code: ['review-code']
329
324
  };
330
325
  const sourceFamily = family;
326
+ const sourceArtifactFamily = sourceFamily === 'analyze' ? 'analysis' : sourceFamily;
331
327
  let receipts = [];
332
328
  try {
333
329
  receipts = parseArtifactReceipts(content).rows;
@@ -335,46 +331,134 @@ function invalidationMutationForCompletion(content, taskDir, family, artifact, t
335
331
  catch (error) {
336
332
  return { error: error instanceof Error ? error.message : String(error) };
337
333
  }
338
- const targets = downstream[sourceFamily].flatMap((targetFamily) => {
339
- const names = fs.readdirSync(taskDir).filter((name) => {
340
- const pattern = targetFamily === 'review-analysis' ? /^review-analysis(?:-r\d+)?\.md$/
341
- : targetFamily === 'review-plan' ? /^review-plan(?:-r\d+)?\.md$/
342
- : targetFamily === 'review-code' ? /^review-code(?:-r\d+)?\.md$/
343
- : new RegExp(`^${targetFamily}(?:-r\\d+)?\\.md$`);
344
- return pattern.test(name);
345
- });
346
- return names.flatMap((name) => {
347
- try {
348
- const stat = fs.lstatSync(path.join(taskDir, name));
349
- if (!stat.isFile() || stat.isSymbolicLink())
350
- return [];
351
- const targetRound = parseArtifactName(name)?.round ?? 1;
352
- const targetSha256 = sha256File(path.join(taskDir, name));
353
- const shapes = [
354
- { targetKind: 'artifact', targetFamily, targetArtifact: name, targetRound, targetSha256 }
355
- ];
356
- const receipt = receipts.find((candidate) => candidate.output === name);
357
- if (receipt)
358
- shapes.push({
359
- targetKind: 'receipt', targetFamily, targetArtifact: name, targetRound,
360
- targetSha256: receipt.inputSha256
361
- });
362
- if (targetFamily.startsWith('review-')) {
363
- shapes.push({ targetKind: 'approval', targetFamily, targetArtifact: name, targetRound, targetSha256 });
364
- }
365
- if (targetFamily === 'review-code') {
366
- shapes.push({ targetKind: 'reviewed-snapshot', targetFamily, targetArtifact: name, targetRound, targetSha256 });
334
+ const inventory = fs.readdirSync(taskDir).flatMap((name) => {
335
+ const identity = parseArtifactName(name);
336
+ if (!identity || !ARTIFACT_FAMILIES.includes(identity.family))
337
+ return [];
338
+ try {
339
+ const stat = fs.lstatSync(path.join(taskDir, name));
340
+ if (!stat.isFile() || stat.isSymbolicLink())
341
+ return [];
342
+ return [{ family: identity.family, name, round: identity.round, sha256: sha256File(path.join(taskDir, name)) }];
343
+ }
344
+ catch (error) {
345
+ throw new Error(`cannot inspect invalidation artifact '${name}': ${error instanceof Error ? error.message : String(error)}`);
346
+ }
347
+ });
348
+ const taskQualification = parseTaskQualification(content);
349
+ if (!taskQualification.ok)
350
+ return { error: taskQualification.message };
351
+ const nodeMap = new Map(inventory.map((node) => [`${node.family}/${node.name}`, node]));
352
+ const audits = new Map();
353
+ let taskInputChanged = false;
354
+ let graphUsable = taskQualification.qualification.present;
355
+ if (graphUsable) {
356
+ for (const node of inventory) {
357
+ const artifactContent = fs.readFileSync(path.join(taskDir, node.name), 'utf8');
358
+ const parsedAudit = parseQualificationAudit(artifactContent);
359
+ if (!parsedAudit.ok || !parsedAudit.audit.present || !parsedAudit.audit.snapshot) {
360
+ graphUsable = false;
361
+ break;
362
+ }
363
+ audits.set(`${node.family}/${node.name}`, parsedAudit.audit);
364
+ if (parsedAudit.audit.snapshot.taskInputDigest !== taskQualification.qualification.taskInputDigest)
365
+ taskInputChanged = true;
366
+ if (parsedAudit.audit.snapshot.nonConstraintInputDigest !== taskQualification.qualification.nonConstraintInputDigest) {
367
+ graphUsable = false;
368
+ break;
369
+ }
370
+ if (parsedAudit.audit.snapshot.upstreamArtifactDigest !== upstreamArtifactDigest(parsedAudit.audit.upstreamRelations)) {
371
+ graphUsable = false;
372
+ break;
373
+ }
374
+ for (const relation of parsedAudit.audit.upstreamRelations) {
375
+ const upstream = nodeMap.get(`${relation.upstreamFamily}/${relation.upstreamArtifact}`);
376
+ if (!upstream || upstream.round !== relation.upstreamRound || upstream.sha256 !== relation.upstreamSha256) {
377
+ graphUsable = false;
378
+ break;
367
379
  }
368
- return shapes.map((targetShape) => ({
369
- ...targetShape,
370
- targetId: targetIdFor('pending', targetShape), operationId: 'pending', status: 'pending',
371
- reasonCode: 'upstream-replaced', updatedAt: timestamp
372
- }));
373
380
  }
374
- catch (error) {
375
- throw new Error(`cannot inspect invalidation target '${name}': ${error instanceof Error ? error.message : String(error)}`);
381
+ if (!graphUsable)
382
+ break;
383
+ }
384
+ }
385
+ const selected = new Set();
386
+ if (graphUsable) {
387
+ const changedConstraints = new Set();
388
+ for (const audit of audits.values()) {
389
+ for (const dependency of audit.constraintDependencies) {
390
+ const current = taskQualification.qualification.constraints.find((row) => row.constraintId === dependency.constraintId);
391
+ if (!current || current.digest !== dependency.constraintDigest)
392
+ changedConstraints.add(dependency.constraintId);
376
393
  }
377
- });
394
+ }
395
+ const currentCandidates = new Map(taskQualification.qualification.candidates.map((candidate) => [candidate.candidateId, candidate]));
396
+ const candidateSnapshotMatches = [...audits.values()].every((audit) => audit.candidateQualifications.length === currentCandidates.size && audit.candidateQualifications.every((row) => {
397
+ const current = currentCandidates.get(row.candidateId);
398
+ return Boolean(current && current.status === row.status && current.impact === row.impact
399
+ && current.evidence === row.evidence && current.constraintIds.join(',') === row.constraintIds.join(','));
400
+ }));
401
+ // A task-input digest also covers candidates. A narrowed invalidation graph
402
+ // is safe only when the old audit rows show that the candidate projection
403
+ // stayed unchanged and the change is explained by at least one constraint.
404
+ if (taskInputChanged && (changedConstraints.size === 0 || !candidateSnapshotMatches))
405
+ graphUsable = false;
406
+ for (const node of inventory) {
407
+ const audit = audits.get(`${node.family}/${node.name}`);
408
+ if (audit.constraintDependencies.some((dependency) => changedConstraints.has(dependency.constraintId)))
409
+ selected.add(`${node.family}/${node.name}`);
410
+ if (audit.upstreamRelations.some((relation) => relation.upstreamFamily === sourceArtifactFamily))
411
+ selected.add(`${node.family}/${node.name}`);
412
+ }
413
+ // Relation rows point from a consumer to its actual upstream artifact. Walk
414
+ // that reverse edge so review-only seeds and multi-hop consumers receive the
415
+ // same derived targets as ordinary executor seeds.
416
+ let changed = true;
417
+ while (changed) {
418
+ changed = false;
419
+ for (const node of inventory) {
420
+ const key = `${node.family}/${node.name}`;
421
+ const audit = audits.get(key);
422
+ if (audit.upstreamRelations.some((relation) => selected.has(`${relation.upstreamFamily}/${relation.upstreamArtifact}`)) && !selected.has(key)) {
423
+ selected.add(key);
424
+ changed = true;
425
+ }
426
+ }
427
+ }
428
+ // A relation cycle cannot establish a safe downstream closure.
429
+ const visiting = new Set();
430
+ const visited = new Set();
431
+ const hasCycle = (key) => {
432
+ if (visiting.has(key))
433
+ return true;
434
+ if (visited.has(key))
435
+ return false;
436
+ visiting.add(key);
437
+ const cycle = audits.get(key)?.upstreamRelations.some((relation) => hasCycle(`${relation.upstreamFamily}/${relation.upstreamArtifact}`)) ?? false;
438
+ visiting.delete(key);
439
+ visited.add(key);
440
+ return cycle;
441
+ };
442
+ if ([...nodeMap.keys()].some(hasCycle))
443
+ graphUsable = false;
444
+ }
445
+ const reasonCode = graphUsable ? 'qualification-changed' : 'upstream-replaced';
446
+ const targetNodes = (graphUsable
447
+ ? inventory.filter((node) => selected.has(`${node.family}/${node.name}`))
448
+ : inventory.filter((node) => downstream[sourceFamily].includes(node.family)))
449
+ .filter((node) => !(node.family === FAMILY[family].artifact && node.name === artifact.name));
450
+ const targets = targetNodes.flatMap((node) => {
451
+ const shapes = [
452
+ { targetKind: 'artifact', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: node.sha256 }
453
+ ];
454
+ const receipt = receipts.find((candidate) => candidate.output === node.name);
455
+ if (receipt)
456
+ shapes.push({ targetKind: 'receipt', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: receipt.inputSha256 });
457
+ if (node.family.startsWith('review-'))
458
+ shapes.push({ targetKind: 'approval', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: node.sha256 });
459
+ if (node.family === 'review-code')
460
+ shapes.push({ targetKind: 'reviewed-snapshot', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: node.sha256 });
461
+ return shapes.map((targetShape) => ({ ...targetShape, targetId: targetIdFor('pending', targetShape), operationId: 'pending', status: 'pending', reasonCode, updatedAt: timestamp }));
378
462
  });
379
463
  if (targets.length === 0)
380
464
  return { mutation: null };
@@ -589,6 +673,32 @@ function applyTaskEventUnlocked(request, options = {}) {
589
673
  if (!validated.ok)
590
674
  return failed(normalized, validated.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
591
675
  completedArtifact = validated.artifact;
676
+ if (eventIdentity.family.startsWith('review-')) {
677
+ let reviewContent;
678
+ try {
679
+ reviewContent = fs.readFileSync(completedArtifact.path, 'utf8');
680
+ }
681
+ catch (error) {
682
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot read qualification audit from ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
683
+ }
684
+ const schema = getArtifactSchema(eventIdentity.family);
685
+ const structure = schema ? inspectArtifactContract(reviewContent, schema) : null;
686
+ if (structure && !structure.ok) {
687
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `shared artifact structure invalid: ${structure.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
688
+ }
689
+ const expected = expectedQualificationRelations(content, eventIdentity.family);
690
+ if (!expected.ok) {
691
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `${expected.code}: ${expected.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
692
+ }
693
+ const qualification = validateQualificationAudit(content, reviewContent, {
694
+ family: eventIdentity.family,
695
+ artifact: completedArtifact.name,
696
+ expectedUpstreamRelations: expected.relations
697
+ });
698
+ if (!qualification.ok) {
699
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `${qualification.code}: ${qualification.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
700
+ }
701
+ }
592
702
  if (eventIdentity.family === 'analyze' || eventIdentity.family === 'plan' || eventIdentity.family === 'code') {
593
703
  const localFamily = eventIdentity.family === 'analyze'
594
704
  ? 'analysis'
@@ -600,11 +710,10 @@ function applyTaskEventUnlocked(request, options = {}) {
600
710
  catch (error) {
601
711
  return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot read ${completedArtifact.name}: ${String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
602
712
  }
603
- const validationConfig = localArtifactValidationConfig(resolved.repoRoot, localFamily);
604
713
  const local = validateLocalArtifact(artifactContent, {
605
714
  family: localFamily,
606
- requiredSections: validationConfig.requiredSections,
607
- requiredPatterns: validationConfig.requiredPatterns
715
+ taskContent: content,
716
+ artifact: completedArtifact.name
608
717
  });
609
718
  if (!local.ok) {
610
719
  return failed(normalized, {
@@ -726,6 +835,14 @@ function applyTaskEventUnlocked(request, options = {}) {
726
835
  const body = appendActivityEntry(section, { time: metadata.timestamp, step: logStep, agent: normalized.agent, note: eventIdentity.note });
727
836
  const frontmatterSet = { current_step: step, assigned_to: normalized.agent };
728
837
  let frontmatterRemove;
838
+ if (eventIdentity.phase === 'started' && artifactContext) {
839
+ try {
840
+ frontmatterSet.qualification_input_relations = JSON.stringify(qualificationRelationsForStarted(eventIdentity.family, artifactContext.inputs));
841
+ }
842
+ catch (error) {
843
+ return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot capture qualification input relations: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
844
+ }
845
+ }
729
846
  if (eventIdentity.phase === 'started' && eventIdentity.family === 'code') {
730
847
  const planInput = artifactContext?.inputs.find((input) => input.family === 'plan');
731
848
  if (!planInput)
@@ -757,6 +874,8 @@ function applyTaskEventUnlocked(request, options = {}) {
757
874
  else if (eventIdentity.phase === 'completed' && eventIdentity.family.startsWith('review-')) {
758
875
  frontmatterRemove = ['review_input_artifact', 'review_input_sha256'];
759
876
  }
877
+ if (eventIdentity.phase === 'completed')
878
+ frontmatterRemove = [...(frontmatterRemove ?? []), 'qualification_input_relations'];
760
879
  if (eventIdentity.phase === 'started' && normalized.implementationInput)
761
880
  frontmatterSet.last_reviewed_commit = '';
762
881
  const mutations = [
@@ -1,3 +1,4 @@
1
+ import { PUBLIC_CLI_ROUTE_SELECTORS, PUBLIC_CLI_SELECTOR_ALIASES } from '../internal/cli-route-inventory.js';
1
2
  const USAGE = `Usage: ai task <command> [options]
2
3
 
3
4
  Commands:
@@ -30,6 +31,7 @@ Examples:
30
31
  ai task status --task 11
31
32
 
32
33
  Run 'ai task <command> --help' for details.`;
34
+ const TASK_OPERATIONS = PUBLIC_CLI_ROUTE_SELECTORS.task;
33
35
  export async function runTask(args) {
34
36
  const [subcommand, ...rest] = args;
35
37
  if (!subcommand) {
@@ -41,14 +43,20 @@ export async function runTask(args) {
41
43
  process.stdout.write(`${USAGE}\n`);
42
44
  return;
43
45
  }
44
- switch (subcommand) {
46
+ const routeSelector = PUBLIC_CLI_SELECTOR_ALIASES.task[subcommand] ?? subcommand;
47
+ if (!TASK_OPERATIONS.includes(routeSelector)) {
48
+ process.stderr.write(`Unknown task command: ${subcommand}\n\n`);
49
+ process.stdout.write(`${USAGE}\n`);
50
+ process.exitCode = 1;
51
+ return;
52
+ }
53
+ switch (routeSelector) {
45
54
  case 'cat': {
46
55
  const { cat } = await import('./commands/cat.js');
47
56
  cat(rest);
48
57
  break;
49
58
  }
50
- case 'decisions':
51
- case 'd': {
59
+ case 'decisions': {
52
60
  const { decisions } = await import('./commands/decisions.js');
53
61
  decisions(rest);
54
62
  break;