@fitlab-ai/agent-infra 0.11.2 → 0.11.4

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 (162) hide show
  1. package/README.md +17 -8
  2. package/README.zh-CN.md +17 -8
  3. package/dist/bin/internal-cli.js +7 -2
  4. package/dist/lib/agent-clients/adapter.js +3 -0
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
  8. package/dist/lib/agent-clients/adapters/codex.js +10 -0
  9. package/dist/lib/internal/cli-route-inventory.js +1 -1
  10. package/dist/lib/internal/manual-validation.js +22 -27
  11. package/dist/lib/internal/sandbox-control.js +1 -43
  12. package/dist/lib/internal/task-artifact.js +1 -1
  13. package/dist/lib/internal/task-finalization.js +41 -1
  14. package/dist/lib/internal/task-ledger.js +13 -5
  15. package/dist/lib/internal/task-lifecycle.js +1 -1
  16. package/dist/lib/internal/task-operation-registry.js +7 -2
  17. package/dist/lib/platform/completion-backfill.js +63 -17
  18. package/dist/lib/platform/issue-comments.js +2 -31
  19. package/dist/lib/platform/pr-summary.js +7 -2
  20. package/dist/lib/sandbox/control/client.js +3 -44
  21. package/dist/lib/sandbox/control/executor.js +14 -79
  22. package/dist/lib/sandbox/control/protocol.js +5 -64
  23. package/dist/lib/sandbox/control/server.js +49 -343
  24. package/dist/lib/sandbox/control/state.js +1 -7
  25. package/dist/lib/sandbox/control/task-view.js +1 -2
  26. package/dist/lib/sandbox/task-cleanup.js +13 -20
  27. package/dist/lib/task/artifact-command.js +11 -4
  28. package/dist/lib/task/artifact-lifecycle.js +217 -85
  29. package/dist/lib/task/artifact-operations.js +1 -26
  30. package/dist/lib/task/artifact-receipts.js +4 -1
  31. package/dist/lib/task/artifact-schema.js +1 -0
  32. package/dist/lib/task/artifact-selection.js +25 -0
  33. package/dist/lib/task/capabilities.js +158 -39
  34. package/dist/lib/task/commands/status.js +9 -2
  35. package/dist/lib/task/control-authority.js +37 -38
  36. package/dist/lib/task/control-recovery.js +3 -42
  37. package/dist/lib/task/decision-intents.js +12 -0
  38. package/dist/lib/task/delegation-receipts.js +2 -2
  39. package/dist/lib/task/delivery.js +1 -43
  40. package/dist/lib/task/events.js +80 -64
  41. package/dist/lib/task/finalization-artifacts.js +1 -3
  42. package/dist/lib/task/finalization-state.js +1 -1
  43. package/dist/lib/task/finalization.js +319 -234
  44. package/dist/lib/task/ledger-intents.js +157 -15
  45. package/dist/lib/task/lifecycle-path.js +75 -0
  46. package/dist/lib/task/lifecycle.js +11 -1
  47. package/dist/lib/task/local-artifact-finalization.js +1 -20
  48. package/dist/lib/task/manual-validation-receipt.js +8 -3
  49. package/dist/lib/task/orchestration.js +29 -13
  50. package/dist/lib/task/review-finalization.js +3 -16
  51. package/dist/lib/task/rework-intent.js +70 -4
  52. package/dist/lib/task/verification-engine.js +20 -30
  53. package/dist/lib/task/verification.js +1 -0
  54. package/dist/lib/task/workflow-warning-intents.js +21 -6
  55. package/dist/lib/task/workspace-snapshot.js +7 -2
  56. package/dist/lifecycle-build-manifest.json +3 -3
  57. package/lib/agent-clients/adapter.ts +46 -0
  58. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
  59. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
  60. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
  61. package/lib/agent-clients/adapters/codex.ts +10 -0
  62. package/lib/internal/cli-route-inventory.ts +1 -1
  63. package/lib/internal/manual-validation.ts +22 -26
  64. package/lib/internal/sandbox-control.ts +0 -42
  65. package/lib/internal/task-artifact.ts +1 -1
  66. package/lib/internal/task-finalization.ts +40 -1
  67. package/lib/internal/task-ledger.ts +12 -5
  68. package/lib/internal/task-lifecycle.ts +2 -2
  69. package/lib/internal/task-operation-registry.ts +8 -4
  70. package/lib/platform/completion-backfill.ts +78 -16
  71. package/lib/platform/issue-comments.ts +1 -35
  72. package/lib/platform/pr-summary.ts +10 -2
  73. package/lib/sandbox/control/client.ts +5 -68
  74. package/lib/sandbox/control/executor.ts +14 -81
  75. package/lib/sandbox/control/protocol.ts +7 -83
  76. package/lib/sandbox/control/server.ts +47 -362
  77. package/lib/sandbox/control/state.ts +1 -9
  78. package/lib/sandbox/control/task-view.ts +1 -2
  79. package/lib/sandbox/task-cleanup.ts +12 -20
  80. package/lib/task/artifact-command.ts +14 -4
  81. package/lib/task/artifact-lifecycle.ts +236 -85
  82. package/lib/task/artifact-operations.ts +1 -26
  83. package/lib/task/artifact-receipts.ts +4 -1
  84. package/lib/task/artifact-schema.ts +1 -0
  85. package/lib/task/artifact-selection.ts +53 -0
  86. package/lib/task/capabilities.ts +154 -40
  87. package/lib/task/commands/status.ts +11 -2
  88. package/lib/task/control-authority.ts +41 -48
  89. package/lib/task/control-recovery.ts +3 -43
  90. package/lib/task/decision-intents.ts +9 -0
  91. package/lib/task/delegation-receipts.ts +2 -2
  92. package/lib/task/delivery.ts +1 -40
  93. package/lib/task/events.ts +82 -64
  94. package/lib/task/finalization-artifacts.ts +1 -3
  95. package/lib/task/finalization-state.ts +1 -1
  96. package/lib/task/finalization.ts +310 -216
  97. package/lib/task/ledger-intents.ts +155 -15
  98. package/lib/task/lifecycle-path.ts +98 -0
  99. package/lib/task/lifecycle.ts +15 -2
  100. package/lib/task/local-artifact-finalization.ts +2 -25
  101. package/lib/task/manual-validation-receipt.ts +8 -2
  102. package/lib/task/orchestration.ts +56 -27
  103. package/lib/task/review-finalization.ts +3 -16
  104. package/lib/task/rework-intent.ts +75 -5
  105. package/lib/task/verification-engine.ts +19 -30
  106. package/lib/task/verification.ts +2 -1
  107. package/lib/task/workflow-warning-intents.ts +18 -6
  108. package/lib/task/workspace-snapshot.ts +3 -2
  109. package/package.json +1 -1
  110. package/templates/.agents/rules/lifecycle-orchestration.en.md +19 -5
  111. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +19 -5
  112. package/templates/.agents/rules/review-handshake.en.md +4 -4
  113. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  114. package/templates/.agents/skills/analyze-task/SKILL.en.md +11 -4
  115. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -4
  116. package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
  117. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
  118. package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
  119. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
  120. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
  121. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
  122. package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
  123. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
  124. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
  125. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  126. package/templates/.agents/skills/complete-task/SKILL.en.md +7 -8
  127. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +7 -8
  128. package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
  129. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
  130. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
  131. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
  132. package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
  133. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
  134. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
  135. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
  136. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
  137. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
  138. package/templates/.agents/skills/run-task/SKILL.en.md +9 -8
  139. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -8
  140. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
  141. package/templates/.agents/templates/task.en.md +2 -2
  142. package/templates/.agents/templates/task.zh-CN.md +2 -2
  143. package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
  144. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
  145. package/templates/.agents/workflows/feature-development.en.yaml +11 -6
  146. package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
  147. package/templates/.agents/workflows/refactoring.en.yaml +11 -6
  148. package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
  149. package/dist/lib/sandbox/control/finalization-response.js +0 -35
  150. package/dist/lib/sandbox/control/task-workflow.js +0 -74
  151. package/dist/lib/sandbox/control/workflow-executor.js +0 -142
  152. package/dist/lib/task/lifecycle-recovery.js +0 -549
  153. package/dist/lib/task/recovery-warning.js +0 -19
  154. package/dist/lib/task/workflow-command.js +0 -21
  155. package/dist/lib/task/workflow-dispatch.js +0 -213
  156. package/lib/sandbox/control/finalization-response.ts +0 -37
  157. package/lib/sandbox/control/task-workflow.ts +0 -95
  158. package/lib/sandbox/control/workflow-executor.ts +0 -156
  159. package/lib/task/lifecycle-recovery.ts +0 -691
  160. package/lib/task/recovery-warning.ts +0 -21
  161. package/lib/task/workflow-command.ts +0 -22
  162. package/lib/task/workflow-dispatch.ts +0 -214
@@ -1,5 +1,9 @@
1
1
  const REWORK_INTENT_HEADINGS = ['返工意图', 'Rework Intent'];
2
2
  const REWORK_INTENT_COLUMNS = [
3
+ 'intent_id', 'finding_id', 'source_artifact', 'source_sha256', 'target',
4
+ 'classification', 'evidence_digest', 'task_fact_digest', 'status', 'declared_at', 'consumed_at'
5
+ ];
6
+ const LEGACY_REWORK_INTENT_COLUMNS = [
3
7
  'intent_id', 'finding_id', 'source_artifact', 'source_sha256', 'target',
4
8
  'status', 'declared_at', 'consumed_at'
5
9
  ];
@@ -55,12 +59,16 @@ function parseReworkIntentDocument(content) {
55
59
  throw new Error(`invalid intent id '${row.intent_id}'`);
56
60
  if (!/^(AN|PL|CD)-[1-9]\d*$/.test(row.finding_id))
57
61
  throw new Error(`invalid finding id '${row.finding_id}'`);
58
- if (!['analysis', 'plan', 'code'].includes(row.target))
62
+ if (!['analysis', 'plan', 'code', 'pause'].includes(row.target))
59
63
  throw new Error(`invalid rework target '${row.target}'`);
64
+ if (!['scope-or-requirement', 'design', 'implementation', 'human-decision', 'insufficient-evidence'].includes(row.classification))
65
+ throw new Error(`invalid rework classification '${row.classification}'`);
60
66
  if (!['pending', 'consumed', 'superseded'].includes(row.status))
61
67
  throw new Error(`invalid rework status '${row.status}'`);
62
68
  if (!/^[a-f0-9]{64}$/.test(row.source_sha256))
63
69
  throw new Error(`invalid source hash for '${row.intent_id}'`);
70
+ if (!/^[a-f0-9]{64}$/.test(row.evidence_digest) || !/^[a-f0-9]{64}$/.test(row.task_fact_digest))
71
+ throw new Error(`invalid semantic digest for '${row.intent_id}'`);
64
72
  if (!row.source_artifact || !row.declared_at)
65
73
  throw new Error(`rework intent '${row.intent_id}' has invalid required fields`);
66
74
  const terminal = row.status === 'consumed' || row.status === 'superseded';
@@ -69,6 +77,8 @@ function parseReworkIntentDocument(content) {
69
77
  return {
70
78
  intentId: row.intent_id, findingId: row.finding_id, sourceArtifact: row.source_artifact,
71
79
  sourceSha256: row.source_sha256, target: row.target,
80
+ classification: row.classification,
81
+ evidenceDigest: row.evidence_digest, taskFactDigest: row.task_fact_digest,
72
82
  status: row.status, declaredAt: row.declared_at, consumedAt: row.consumed_at
73
83
  };
74
84
  });
@@ -91,7 +101,10 @@ function renderReworkIntents(intents) {
91
101
  `| ${REWORK_INTENT_COLUMNS.map(() => '---').join(' | ')} |`,
92
102
  ...intents.map((intent) => `| ${[
93
103
  intent.intentId, intent.findingId, intent.sourceArtifact, intent.sourceSha256,
94
- intent.target, intent.status, intent.declaredAt, intent.consumedAt
104
+ intent.target, intent.classification ?? (intent.target === 'analysis' ? 'scope-or-requirement' : intent.target === 'plan' ? 'design' : intent.target === 'code' ? 'implementation' : 'insufficient-evidence'),
105
+ intent.evidenceDigest ?? requireDigest(`${intent.findingId}|${intent.sourceArtifact}|${intent.sourceSha256}`),
106
+ intent.taskFactDigest ?? requireDigest(`current|${intent.findingId}|${intent.target}`),
107
+ intent.status, intent.declaredAt, intent.consumedAt
95
108
  ].map(escape).join(' | ')} |`)
96
109
  ].join('\n');
97
110
  }
@@ -99,11 +112,63 @@ function upsertReworkIntent(intents, intent) {
99
112
  const existing = intents.find((candidate) => candidate.intentId === intent.intentId);
100
113
  if (!existing)
101
114
  return { changed: true, intents: [...intents, intent] };
102
- const identity = (value) => [value.intentId, value.findingId, value.sourceArtifact, value.sourceSha256, value.target];
115
+ const identity = (value) => [value.intentId, value.findingId, value.sourceArtifact, value.sourceSha256, value.target, value.classification, value.evidenceDigest, value.taskFactDigest];
103
116
  if (JSON.stringify(identity(existing)) !== JSON.stringify(identity(intent)))
104
117
  throw new Error(`intent '${intent.intentId}' identity conflicts with existing state`);
105
118
  return { changed: false, intents };
106
119
  }
120
+ function consumeHumanDecisionReworkIntents(intents, findingId, consumedAt) {
121
+ let changed = false;
122
+ const next = intents.map((intent) => {
123
+ if (intent.status !== 'pending' || intent.target !== 'pause' || intent.classification !== 'human-decision' || intent.findingId !== findingId)
124
+ return intent;
125
+ changed = true;
126
+ return { ...intent, status: 'consumed', consumedAt };
127
+ });
128
+ return { changed, intents: next };
129
+ }
130
+ function parseLegacyReworkIntentDocument(content) {
131
+ // TODO(compat): Remove this explicit decoder once no task contains the eight-column rework-intent header.
132
+ const body = sectionBody(content);
133
+ if (body === null)
134
+ return { ok: false, code: 'TASK_REWORK_INTENT_INVALID', message: 'legacy rework intent section is missing' };
135
+ const lines = body.split(/\r?\n/).filter((line) => line.trim());
136
+ const header = lines[0] ? cells(lines[0]) : null;
137
+ const separator = lines[1] ? cells(lines[1]) : null;
138
+ if (!header || !separator || header.length !== LEGACY_REWORK_INTENT_COLUMNS.length || !header.every((value, index) => value === LEGACY_REWORK_INTENT_COLUMNS[index])) {
139
+ return { ok: false, code: 'TASK_REWORK_INTENT_INVALID', message: 'rework intent section is not the known legacy table' };
140
+ }
141
+ try {
142
+ const intents = lines.slice(2).map((line) => {
143
+ const values = cells(line);
144
+ if (!values || values.length !== LEGACY_REWORK_INTENT_COLUMNS.length)
145
+ throw new Error('legacy rework intent row has the wrong column count');
146
+ const row = Object.fromEntries(LEGACY_REWORK_INTENT_COLUMNS.map((column, index) => [column, values[index]]));
147
+ if (!/^RI-[1-9]\d*$/.test(row.intent_id) || !/^(AN|PL|CD)-[1-9]\d*$/.test(row.finding_id))
148
+ throw new Error('legacy rework intent identity is invalid');
149
+ if (!['analysis', 'plan', 'code'].includes(row.target) || !['pending', 'consumed', 'superseded'].includes(row.status))
150
+ throw new Error('legacy rework intent state is invalid');
151
+ if (!/^[a-f0-9]{64}$/.test(row.source_sha256))
152
+ throw new Error('legacy rework intent source hash is invalid');
153
+ const legacyDigest = (value) => requireDigest(value);
154
+ const classification = row.target === 'analysis' ? 'scope-or-requirement' : row.target === 'plan' ? 'design' : 'implementation';
155
+ return {
156
+ intentId: row.intent_id, findingId: row.finding_id, sourceArtifact: row.source_artifact, sourceSha256: row.source_sha256,
157
+ target: row.target, classification,
158
+ evidenceDigest: legacyDigest(`${row.finding_id}|${row.source_artifact}|${row.source_sha256}`),
159
+ taskFactDigest: legacyDigest(`legacy|${row.finding_id}|${row.target}`),
160
+ status: row.status, declaredAt: row.declared_at, consumedAt: row.consumed_at
161
+ };
162
+ });
163
+ return { ok: true, present: true, intents };
164
+ }
165
+ catch (error) {
166
+ return { ok: false, code: 'TASK_REWORK_INTENT_INVALID', message: error instanceof Error ? error.message : String(error) };
167
+ }
168
+ }
169
+ function requireDigest(value) {
170
+ return createHash('sha256').update(value).digest('hex');
171
+ }
107
172
  function consumeReworkIntents(intents, target, sourceHashes, consumedAt) {
108
173
  let changed = false;
109
174
  const next = intents.map((intent) => {
@@ -134,5 +199,6 @@ function reworkIntentMutation(content, intents) {
134
199
  body: renderReworkIntents(intents)
135
200
  };
136
201
  }
137
- export { REWORK_INTENT_COLUMNS, REWORK_INTENT_HEADINGS, parseReworkIntentDocument, renderReworkIntents, reworkIntentMutation, consumeReworkIntents, supersedeReworkIntents, upsertReworkIntent };
202
+ export { REWORK_INTENT_COLUMNS, REWORK_INTENT_HEADINGS, parseReworkIntentDocument, renderReworkIntents, reworkIntentMutation, consumeReworkIntents, supersedeReworkIntents, upsertReworkIntent, consumeHumanDecisionReworkIntents, parseLegacyReworkIntentDocument };
203
+ import { createHash } from 'node:crypto';
138
204
  //# sourceMappingURL=rework-intent.js.map
@@ -26,7 +26,6 @@ import { OrchestrationStateError, readRun } from "./orchestration.js";
26
26
  import { resolveDeliveryTarget } from "./delivery-target.js";
27
27
  import { readPrDeliveryFact } from "./pr-delivery-fact.js";
28
28
  import { validateLocalArtifact } from "./local-artifact-finalization.js";
29
- import { validateQualificationAudit } from "./qualification-audit.js";
30
29
  import { getArtifactSchema } from "./artifact-schema.js";
31
30
  import { inspectArtifactContract } from "./artifact-operations.js";
32
31
  import { normalizeVerificationRecord } from './gate-policy.js';
@@ -59,7 +58,6 @@ const ACTIVITY_LOG_STARTED_RE = /\s*\[started\]\s*$/;
59
58
  const BRANCH_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
60
59
  // Review disagreement ledger (see .agents/rules/review-handshake.md).
61
60
  const LEDGER_SECTION_NAMES = ["审查分歧账本", "Review Disagreement Ledger"];
62
- const DEFAULT_MAX_HANDSHAKE_ROUNDS = 3;
63
61
  const POST_REVIEW_COMMIT_STAGE = "post-review-commit";
64
62
  const SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
65
63
  const scriptPath = fileURLToPath(import.meta.url);
@@ -441,21 +439,6 @@ function checkArtifact({ taskDir, config, artifactFile, skillName }) {
441
439
  return failResult("artifact", `Artifact is empty: ${path.basename(artifactPath)}`);
442
440
  }
443
441
  const content = fs.readFileSync(artifactPath, "utf8");
444
- let taskContent = "";
445
- const taskPath = path.join(taskDir, "task.md");
446
- if (fs.existsSync(taskPath)) {
447
- try {
448
- taskContent = fs.readFileSync(taskPath, "utf8");
449
- }
450
- catch (error) {
451
- return failResult("artifact", `Cannot read task qualification contract: ${error instanceof Error ? error.message : String(error)}`);
452
- }
453
- }
454
- if (taskContent && /^review-(?:analysis|plan|code)$/.test(skillName)) {
455
- const qualification = validateQualificationAudit(taskContent, content, { artifact: path.basename(artifactPath) });
456
- if (!qualification.ok)
457
- return failResult("artifact", `${path.basename(artifactPath)} has qualification audit errors: ${qualification.code}: ${qualification.message}`);
458
- }
459
442
  const schemaFamily = skillName === "analyze-task"
460
443
  ? "analysis"
461
444
  : skillName === "review-analysis"
@@ -479,9 +462,7 @@ function checkArtifact({ taskDir, config, artifactFile, skillName }) {
479
462
  }
480
463
  if (localFamily) {
481
464
  const local = validateLocalArtifact(content, {
482
- family: localFamily,
483
- ...(taskContent ? { taskContent } : {}),
484
- artifact: path.basename(artifactPath)
465
+ family: localFamily
485
466
  });
486
467
  if (!local.ok) {
487
468
  return failResult("artifact", `${path.basename(artifactPath)} has local structural errors: ${local.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')}`);
@@ -715,11 +696,10 @@ function checkReviewLedger({ taskDir, config, repositoryRoot }) {
715
696
  return passResult("review-ledger", "Disagreement ledger has no entries");
716
697
  }
717
698
  const stageScope = Array.isArray(config.stage_scope) ? config.stage_scope : null;
718
- const maxRounds = Number(resolveReviewSetting(config, "maxHandshakeRounds", DEFAULT_MAX_HANDSHAKE_ROUNDS, repositoryRoot));
719
699
  const problems = [];
720
700
  let inScopeCount = 0;
721
701
  for (const row of rows) {
722
- const { id, stage, round: roundRaw, status, evidence } = row;
702
+ const { id, stage, status, evidence } = row;
723
703
  const stageScoped = stageScope ? stageScope.includes(stage) : true;
724
704
  // post-review-commit exemption rows are consumed by the post-review-commit
725
705
  // check, not enforced here.
@@ -738,13 +718,6 @@ function checkReviewLedger({ taskDir, config, repositoryRoot }) {
738
718
  if (status !== "open" && evidence === "") {
739
719
  problems.push(`${id}: status '${status}' requires evidence`);
740
720
  }
741
- const round = Number.parseInt(roundRaw, 10);
742
- if (Number.isFinite(round) &&
743
- round >= maxRounds &&
744
- !LEDGER_TERMINAL.has(status) &&
745
- status !== "needs-human-decision") {
746
- problems.push(`${id}: round ${round} reached limit ${maxRounds} without convergence; escalate to needs-human-decision`);
747
- }
748
721
  if (!LEDGER_TERMINAL.has(status)) {
749
722
  problems.push(`${id}: unresolved (status '${status}')`);
750
723
  }
@@ -1051,7 +1024,8 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }) {
1051
1024
  let targetHead;
1052
1025
  let diffBase;
1053
1026
  try {
1054
- gitRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
1027
+ const taskRepositoryRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
1028
+ gitRoot = reviewWorktreeForTask(taskRepositoryRoot, String(task.metadata.branch || "").trim()) ?? taskRepositoryRoot;
1055
1029
  head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
1056
1030
  const target = resolveDeliveryTarget(gitRoot, { remote: deliveryRemote, baseRef: deliveryBaseRef });
1057
1031
  if (!target.ok)
@@ -1101,6 +1075,22 @@ function checkReviewFact({ taskDir, artifactFile, repositoryRoot }) {
1101
1075
  }
1102
1076
  return passResult("review-fact", `Review fact valid for ${path.basename(resolvedArtifact.path)} at ${baseline.slice(0, 8)}`);
1103
1077
  }
1078
+ function reviewWorktreeForTask(repositoryRoot, branch) {
1079
+ if (!branch)
1080
+ return null;
1081
+ const expectedRef = `refs/heads/${branch}`;
1082
+ const records = execFileSync("git", ["-C", repositoryRoot, "worktree", "list", "--porcelain"], {
1083
+ encoding: "utf8",
1084
+ stdio: ["pipe", "pipe", "pipe"]
1085
+ }).trim().split(/\r?\n\r?\n/);
1086
+ for (const record of records) {
1087
+ const worktree = /^worktree (.+)$/m.exec(record)?.[1];
1088
+ const branchRef = /^branch (.+)$/m.exec(record)?.[1];
1089
+ if (worktree && branchRef === expectedRef)
1090
+ return worktree;
1091
+ }
1092
+ throw new Error(`Task branch '${branch}' is not checked out in a registered worktree`);
1093
+ }
1104
1094
  function resolvePostReviewBaseline({ gitRoot, lastReviewedCommit, reviewArtifact }) {
1105
1095
  if (SHA_PATTERN.test(lastReviewedCommit) && gitCommitExists(gitRoot, lastReviewedCommit)) {
1106
1096
  return { ok: true, sha: lastReviewedCommit };
@@ -23,6 +23,7 @@ const VERIFICATION_CATALOG = {
23
23
  'commit.completed': gate('commit', 'active'),
24
24
  'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
25
25
  'complete-task.hard-preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['required-pr-delivery'] },
26
+ 'complete-task.prepared': gate('complete-task', 'active'),
26
27
  'complete-task.completed': gate('complete-task', 'completed'),
27
28
  'create-pr.completed': gate('create-pr', 'active'),
28
29
  'create-task.completed': gate('create-task', 'active'),
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import { resolveTaskRef } from './resolve-ref.js';
3
+ import { parseTaskFrontmatter } from './frontmatter.js';
3
4
  import { WORKFLOW_WARNING_COLUMNS, WORKFLOW_WARNING_HEADINGS, WORKFLOW_WARNING_SEVERITIES, WORKFLOW_WARNING_STATUSES, parseWorkflowWarnings } from './workflow-warnings.js';
4
5
  import { captureTaskWriteMetadata, writeTask } from './write.js';
5
6
  import { allowsManualOverride } from './guard-override.js';
@@ -118,8 +119,18 @@ function projectFinalizationWarning(taskRef, warning, options = {}) {
118
119
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
119
120
  if (!resolved.ok)
120
121
  return { status: 'failed', changed: false, error: { code: resolved.code, message: resolved.message } };
121
- if (resolved.state !== 'completed')
122
- return { status: 'failed', changed: false, error: { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected completed` } };
122
+ let prepared = resolved.state === 'completed';
123
+ if (!prepared && resolved.state === 'active') {
124
+ try {
125
+ prepared = parseTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8')).status === 'completed';
126
+ }
127
+ catch {
128
+ prepared = false;
129
+ }
130
+ }
131
+ if (!prepared) {
132
+ return { status: 'failed', changed: false, error: { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected a prepared or completed task` } };
133
+ }
123
134
  let content;
124
135
  let rows;
125
136
  try {
@@ -139,23 +150,27 @@ function projectFinalizationWarning(taskRef, warning, options = {}) {
139
150
  message: warning.message,
140
151
  action: retryHintForWarning(warning),
141
152
  resolvedAt: warning.resolvedAt || (warning.status === 'resolved' ? metadata.timestamp : ''),
142
- resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
153
+ resolution: warning.status === 'resolved'
154
+ ? options.resolution === 'superseded' ? 'Superseded by a later verification observation' : 'Resolved by a successful finalization retry'
155
+ : ''
143
156
  }
144
157
  : {
145
158
  id: nextWarningId(rows), time: metadata.timestamp, step: warning.step,
146
159
  severity: warning.severity, code: warning.code, status: warning.status,
147
160
  target: warning.target, message: warning.message, action: retryHintForWarning(warning),
148
- resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved' ? 'Resolved by a successful finalization retry' : ''
161
+ resolvedAt: warning.resolvedAt || '', resolution: warning.status === 'resolved'
162
+ ? options.resolution === 'superseded' ? 'Superseded by a later verification observation' : 'Resolved by a successful finalization retry'
163
+ : ''
149
164
  };
150
165
  if (existing && JSON.stringify(existing) === JSON.stringify(after))
151
166
  return { status: 'no-op', changed: false, error: null };
152
167
  const written = writeTask({
153
168
  taskRef: resolved.taskId,
154
- expectedState: 'completed',
169
+ expectedState: resolved.state,
155
170
  mutations: [...sectionMutation(content), rowMutation(after)]
156
171
  }, {
157
172
  ...options,
158
- taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: 'completed' },
173
+ taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
159
174
  metadataProvider: () => metadata
160
175
  });
161
176
  if (written.status === 'failed')
@@ -15,7 +15,12 @@ function captureWorktreeTree(repoRoot, forcedPath) {
15
15
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-infra-orchestration-index-'));
16
16
  const indexFile = path.join(tempDir, 'index');
17
17
  try {
18
- git(repoRoot, indexFile, ['read-tree', 'HEAD']);
18
+ try {
19
+ git(repoRoot, indexFile, ['read-tree', 'HEAD']);
20
+ }
21
+ catch {
22
+ git(repoRoot, indexFile, ['read-tree', '--empty']);
23
+ }
19
24
  git(repoRoot, indexFile, ['add', '-A', '--', ':/']);
20
25
  if (forcedPath && fs.existsSync(path.join(repoRoot, forcedPath))) {
21
26
  git(repoRoot, indexFile, ['add', '-f', '--', forcedPath]);
@@ -111,5 +116,5 @@ function diffWorkspaceSnapshots(repoRoot, before, after) {
111
116
  }
112
117
  return [...changed].sort();
113
118
  }
114
- export { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots };
119
+ export { captureRepositorySnapshot, captureWorkspaceSnapshot, captureWorktreeTree, diffWorkspaceSnapshots };
115
120
  //# sourceMappingURL=workspace-snapshot.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "protocolVersion": 3,
3
- "packageVersion": "0.11.2",
4
- "sourceInputHash": "0a1ffa8b90a0d157f9b2aabea6a43f946d68a622bde337befe71059f63b3812b",
5
- "internalExecutableBuildHash": "b6742e1b5d1b997bf7e21e644bfde2bafaa608f7ede7ca308ff61c2cd19da831"
3
+ "packageVersion": "0.11.4",
4
+ "sourceInputHash": "46c0a39f354bff8c80fe19354cdda89a91a4acad060b1f134b6d599bc0ba6806",
5
+ "internalExecutableBuildHash": "1bc9e5755c857ee9f13a702452b2ce7c85da94444dc5a8d1305ae80d5899b001"
6
6
  }
@@ -7,6 +7,10 @@ import type {
7
7
  AgentClientCapabilityMap,
8
8
  AgentClientId
9
9
  } from './types.ts';
10
+ import type {
11
+ OrchestrationOptions,
12
+ OrchestrationResult
13
+ } from '../task/orchestration.ts';
10
14
  import {
11
15
  SANDBOX_HOOK_PHASES
12
16
  } from '../sandbox/tool-types.ts';
@@ -43,6 +47,41 @@ type AgentClientDelegationEvidence = Readonly<{
43
47
  actualReasoningEffort: 'host-event' | 'app-server' | 'spawn-ack' | 'unavailable';
44
48
  }>;
45
49
 
50
+ type AgentClientLifecycleRecoveryRequest = Readonly<{
51
+ taskRef: string;
52
+ intent: 'recover-started';
53
+ agent: string;
54
+ auto: true;
55
+ }>;
56
+
57
+ type AgentClientLifecycleRecoveryResult = Readonly<{
58
+ status: 'applied' | 'no-op' | 'owner-unknown' | 'conflict';
59
+ changed: boolean;
60
+ targetState: 'active';
61
+ requestRef: string;
62
+ intent: 'recover-started';
63
+ taskId: string | null;
64
+ receiptId: string | null;
65
+ childId: string | null;
66
+ error: Readonly<{ code: string; message: string }> | null;
67
+ }>;
68
+
69
+ type AgentClientOrchestrationAdapter = Readonly<{
70
+ prepareDelegation?: (
71
+ taskRef: string,
72
+ input: Readonly<{
73
+ client: AgentClientId;
74
+ requestedModel?: string;
75
+ requestedReasoningEffort?: string;
76
+ }>,
77
+ options?: Readonly<{ repoRoot?: string; orchestrationOptions?: OrchestrationOptions }>
78
+ ) => Promise<OrchestrationResult>;
79
+ recoverStarted?: (
80
+ request: AgentClientLifecycleRecoveryRequest,
81
+ options: Readonly<{ repoRoot: string }>
82
+ ) => AgentClientLifecycleRecoveryResult | Promise<AgentClientLifecycleRecoveryResult>;
83
+ }>;
84
+
46
85
  type AgentClientSeedCommand = Readonly<{
47
86
  templates: Readonly<{
48
87
  en: string;
@@ -75,6 +114,7 @@ type AgentClientAdapter = Readonly<{
75
114
  capabilities: AgentClientCapabilities;
76
115
  modelSelection: AgentClientModelSelectionContext;
77
116
  delegationEvidence: AgentClientDelegationEvidence;
117
+ orchestrationAdapter?: AgentClientOrchestrationAdapter;
78
118
  project: AgentClientProjectDescriptor;
79
119
  sandbox: AgentClientSandboxDescriptor;
80
120
  }>;
@@ -628,6 +668,9 @@ function defineAgentClientAdapter(
628
668
  capabilities: Object.freeze(capabilities),
629
669
  modelSelection: frozenModelSelection,
630
670
  delegationEvidence: Object.freeze({ ...evidence }),
671
+ ...(candidate.orchestrationAdapter === undefined
672
+ ? {}
673
+ : { orchestrationAdapter: Object.freeze({ ...candidate.orchestrationAdapter }) }),
631
674
  project: Object.freeze({
632
675
  ownedPathPrefixes: Object.freeze(paths),
633
676
  ...projectAssets,
@@ -650,6 +693,9 @@ export type {
650
693
  AgentClientCapabilities,
651
694
  AgentClientCustomCommandDescriptor,
652
695
  AgentClientDelegationEvidence,
696
+ AgentClientLifecycleRecoveryRequest,
697
+ AgentClientLifecycleRecoveryResult,
698
+ AgentClientOrchestrationAdapter,
653
699
  AgentClientManifestEntry,
654
700
  AgentClientModelSelectionContext,
655
701
  AgentClientProjectDescriptor,