@fitlab-ai/agent-infra 0.11.3 → 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 (102) hide show
  1. package/README.md +17 -8
  2. package/README.zh-CN.md +17 -8
  3. package/dist/bin/internal-cli.js +1 -1
  4. package/dist/lib/internal/cli-route-inventory.js +1 -1
  5. package/dist/lib/internal/task-artifact.js +1 -1
  6. package/dist/lib/internal/task-finalization.js +41 -1
  7. package/dist/lib/internal/task-ledger.js +13 -5
  8. package/dist/lib/internal/task-operation-registry.js +1 -0
  9. package/dist/lib/platform/completion-backfill.js +63 -17
  10. package/dist/lib/sandbox/control/server.js +26 -143
  11. package/dist/lib/sandbox/task-cleanup.js +13 -20
  12. package/dist/lib/task/artifact-command.js +11 -4
  13. package/dist/lib/task/artifact-lifecycle.js +217 -85
  14. package/dist/lib/task/artifact-operations.js +1 -26
  15. package/dist/lib/task/artifact-receipts.js +4 -1
  16. package/dist/lib/task/artifact-schema.js +1 -0
  17. package/dist/lib/task/artifact-selection.js +25 -0
  18. package/dist/lib/task/capabilities.js +158 -39
  19. package/dist/lib/task/commands/status.js +9 -2
  20. package/dist/lib/task/control-authority.js +3 -2
  21. package/dist/lib/task/decision-intents.js +12 -0
  22. package/dist/lib/task/delivery.js +1 -43
  23. package/dist/lib/task/events.js +80 -64
  24. package/dist/lib/task/finalization.js +301 -143
  25. package/dist/lib/task/ledger-intents.js +157 -15
  26. package/dist/lib/task/lifecycle-path.js +75 -0
  27. package/dist/lib/task/lifecycle.js +10 -0
  28. package/dist/lib/task/local-artifact-finalization.js +1 -20
  29. package/dist/lib/task/orchestration.js +17 -7
  30. package/dist/lib/task/review-finalization.js +3 -16
  31. package/dist/lib/task/rework-intent.js +70 -4
  32. package/dist/lib/task/verification-engine.js +20 -30
  33. package/dist/lib/task/verification.js +1 -0
  34. package/dist/lib/task/workflow-warning-intents.js +21 -6
  35. package/dist/lib/task/workspace-snapshot.js +7 -2
  36. package/dist/lifecycle-build-manifest.json +3 -3
  37. package/lib/internal/cli-route-inventory.ts +1 -1
  38. package/lib/internal/task-artifact.ts +1 -1
  39. package/lib/internal/task-finalization.ts +40 -1
  40. package/lib/internal/task-ledger.ts +12 -5
  41. package/lib/internal/task-operation-registry.ts +1 -0
  42. package/lib/platform/completion-backfill.ts +78 -16
  43. package/lib/sandbox/control/server.ts +21 -146
  44. package/lib/sandbox/task-cleanup.ts +12 -20
  45. package/lib/task/artifact-command.ts +14 -4
  46. package/lib/task/artifact-lifecycle.ts +236 -85
  47. package/lib/task/artifact-operations.ts +1 -26
  48. package/lib/task/artifact-receipts.ts +4 -1
  49. package/lib/task/artifact-schema.ts +1 -0
  50. package/lib/task/artifact-selection.ts +53 -0
  51. package/lib/task/capabilities.ts +154 -40
  52. package/lib/task/commands/status.ts +11 -2
  53. package/lib/task/control-authority.ts +3 -1
  54. package/lib/task/decision-intents.ts +9 -0
  55. package/lib/task/delivery.ts +1 -40
  56. package/lib/task/events.ts +82 -64
  57. package/lib/task/finalization.ts +269 -111
  58. package/lib/task/ledger-intents.ts +155 -15
  59. package/lib/task/lifecycle-path.ts +98 -0
  60. package/lib/task/lifecycle.ts +13 -0
  61. package/lib/task/local-artifact-finalization.ts +2 -25
  62. package/lib/task/orchestration.ts +26 -8
  63. package/lib/task/review-finalization.ts +3 -16
  64. package/lib/task/rework-intent.ts +75 -5
  65. package/lib/task/verification-engine.ts +19 -30
  66. package/lib/task/verification.ts +2 -1
  67. package/lib/task/workflow-warning-intents.ts +18 -6
  68. package/lib/task/workspace-snapshot.ts +3 -2
  69. package/package.json +1 -1
  70. package/templates/.agents/rules/review-handshake.en.md +4 -4
  71. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  72. package/templates/.agents/skills/analyze-task/SKILL.en.md +11 -4
  73. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -4
  74. package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
  75. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
  76. package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
  77. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
  78. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
  79. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
  80. package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
  81. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
  82. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  83. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  84. package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
  85. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
  86. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
  87. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
  88. package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
  89. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
  90. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
  91. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
  92. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
  93. package/templates/.agents/templates/task.en.md +2 -2
  94. package/templates/.agents/templates/task.zh-CN.md +2 -2
  95. package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
  96. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
  97. package/templates/.agents/workflows/feature-development.en.yaml +11 -6
  98. package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
  99. package/templates/.agents/workflows/refactoring.en.yaml +11 -6
  100. package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
  101. package/dist/lib/sandbox/control/finalization-response.js +0 -33
  102. package/lib/sandbox/control/finalization-response.ts +0 -35
@@ -1,4 +1,3 @@
1
- import { finalizationTerminalResponse } from './finalization-response.js';
2
1
  import { completedReentryView } from './completed-reentry.js';
3
2
  import fs from 'node:fs';
4
3
  import path from 'node:path';
@@ -21,7 +20,7 @@ import { nextSandboxControlBackoff } from './timing.js';
21
20
  import { readTaskFinalizationReceipt } from '../../task/finalization.js';
22
21
  import { readCodexControllerRegistration } from './controller-registration.js';
23
22
  import { validateSandboxControlIdentity } from './identity-sentinel.js';
24
- import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
23
+ import { mergeSandboxTaskView, taskViewForManifest } from './task-view.js';
25
24
  import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
26
25
  function appendBrokerAudit(manifest, event, fields = {}) {
27
26
  appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
@@ -125,48 +124,6 @@ export function writeSandboxControlResponse(manifest, response) {
125
124
  }
126
125
  return true;
127
126
  }
128
- function finalizationRecoveryResponse(manifest, requestId, exitCode) {
129
- if (!manifest.taskId || exitCode !== 0)
130
- return { status: 'not-applicable' };
131
- let receipt;
132
- try {
133
- receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
134
- if (!receipt || !receipt.controlBinding
135
- || receipt.controlBinding.generation !== manifest.generation
136
- || receipt.controlBinding.requestId !== requestId)
137
- return { status: 'deferred' };
138
- const resolved = resolveTaskRef(manifest.taskId, { repoRoot: manifest.repoRoot });
139
- if (!resolved.ok || resolved.state !== 'completed' || receipt.lifecycle !== 'done')
140
- return { status: 'deferred' };
141
- }
142
- catch {
143
- return { status: 'deferred' };
144
- }
145
- return { status: 'matched', response: finalizationTerminalResponse(manifest.taskId, requestId, receipt) };
146
- }
147
- function publishFinalizationTaskView(manifest, broker, requestId, state, reasonCode, activeRequestId) {
148
- let view;
149
- try {
150
- const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
151
- const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
152
- view = resolved.ok && resolved.state === 'completed'
153
- ? taskViewAfterFinalization({
154
- taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
155
- })
156
- : {
157
- state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
158
- reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
159
- };
160
- }
161
- catch {
162
- view = {
163
- state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
164
- reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
165
- };
166
- }
167
- writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
168
- return view;
169
- }
170
127
  function payloadReference(payload) {
171
128
  return {
172
129
  version: payload.version,
@@ -329,12 +286,7 @@ export async function recoveryResponse(manifest, manifestPath, request, evidence
329
286
  if (JSON.stringify(payloadTerminal) !== JSON.stringify(terminalResult))
330
287
  return unknown(request.id);
331
288
  }
332
- const finalization = request.family === 'task-finalization' ? finalizationRecoveryResponse(manifest, request.id, 0) : null;
333
- if (evidence.exitCode === 0 && finalization?.status === 'deferred')
334
- return null;
335
- const recovery = finalization
336
- ? { domain: { consistent: finalization.status === 'matched' } }
337
- : await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
289
+ const recovery = await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
338
290
  const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName);
339
291
  const decision = classifySandboxControlRecovery({
340
292
  operation,
@@ -351,23 +303,9 @@ export async function recoveryResponse(manifest, manifestPath, request, evidence
351
303
  return notExecuted(request.id);
352
304
  if (decision.outcome !== 'success' && decision.outcome !== 'failure')
353
305
  return null;
354
- if (request.family === 'task-finalization' && evidence.exitCode === 0) {
355
- if (finalization?.status !== 'matched')
356
- return null;
357
- return finalization.response ?? null;
358
- }
359
306
  return genericRecoveryResponse(request, evidence.exitCode, payload);
360
307
  }
361
- async function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
362
- if (request.family === 'task-finalization' && evidence.exitCode === 0) {
363
- if (!terminalResult)
364
- return { valid: false, payloadReferenced: false };
365
- const expected = await recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
366
- return {
367
- valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
368
- payloadReferenced: false
369
- };
370
- }
308
+ async function terminalMatchesEvidence(manifest, request, response, evidence, payload, payloadInvalid, terminalResult) {
371
309
  if (response.version !== 2 || response.id !== request.id || response.phase !== 'completed'
372
310
  || response.exitCode !== evidence.exitCode || response.error !== null) {
373
311
  return { valid: false, payloadReferenced: false };
@@ -401,7 +339,7 @@ async function terminalMatchesEvidence(manifest, manifestPath, request, response
401
339
  payloadReferenced: false
402
340
  };
403
341
  }
404
- function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
342
+ function publishExecutionResult(manifest, request, result, brokerOwns) {
405
343
  const normalized = sanitizeSandboxControlResult(manifest, result);
406
344
  const terminalResult = writeSandboxControlTerminalResult(manifest, {
407
345
  id: request.id,
@@ -413,46 +351,29 @@ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
413
351
  criticalRequestPhase(manifest, request, 'evidence-written', normalized.exitCode === 0 ? 'success' : 'failure');
414
352
  criticalRequestPhase(manifest, request, 'publish-authorized', normalized.exitCode === 0 ? 'success' : 'failure');
415
353
  }
416
- let terminal = null;
417
- if (request.family === 'task-finalization') {
418
- const finalization = finalizationRecoveryResponse(manifest, request.id, normalized.exitCode);
419
- if (finalization.status === 'deferred')
420
- return false;
421
- terminal = finalization.response ?? null;
422
- }
423
- if (!terminal) {
424
- const inline = {
425
- version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
426
- stdout: normalized.stdout, stderr: normalized.stderr, error: null
427
- };
428
- if (sandboxControlEncodedJsonBytes(inline) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
429
- terminal = inline;
430
- }
354
+ let terminal = {
355
+ version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
356
+ stdout: normalized.stdout, stderr: normalized.stderr, error: null
357
+ };
358
+ if (sandboxControlEncodedJsonBytes(terminal) > SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
431
359
  let payload = null;
432
- if (!terminal) {
433
- try {
434
- payload = createSandboxControlPayload(manifest, request.id, normalized);
435
- const usage = sandboxControlGenerationUsage(manifest);
436
- if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
437
- writeSandboxControlPayload(manifest, request.id, normalized);
438
- }
439
- else {
440
- payload = null;
441
- }
360
+ try {
361
+ payload = createSandboxControlPayload(manifest, request.id, normalized);
362
+ const usage = sandboxControlGenerationUsage(manifest);
363
+ if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
364
+ writeSandboxControlPayload(manifest, request.id, normalized);
442
365
  }
443
- catch {
366
+ else {
444
367
  payload = null;
445
368
  }
446
- terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
447
369
  }
370
+ catch {
371
+ payload = null;
372
+ }
373
+ terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
448
374
  }
449
375
  if (!brokerOwns())
450
376
  return false;
451
- if (request.family === 'task-finalization' && normalized.exitCode === 0) {
452
- const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
453
- if (view.state === 'unknown')
454
- return false;
455
- }
456
377
  const committed = writeSandboxControlResponse(manifest, terminal);
457
378
  if (committed && fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
458
379
  criticalRequestPhase(manifest, request, 'published-committed', normalized.exitCode === 0 ? 'success' : 'failure');
@@ -652,16 +573,11 @@ async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
652
573
  if (terminal && request && resultEvidence) {
653
574
  if (!terminalResult && request.family !== 'task-create')
654
575
  continue;
655
- const reconciliation = await terminalMatchesEvidence(manifest, manifestPath, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
576
+ const reconciliation = await terminalMatchesEvidence(manifest, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
656
577
  if (!reconciliation.valid)
657
578
  continue;
658
579
  terminalReconciled = true;
659
580
  payloadReferenced = reconciliation.payloadReferenced;
660
- if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
661
- const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
662
- if (view.state === 'unknown')
663
- continue;
664
- }
665
581
  }
666
582
  if (!terminalResult && !terminalReconciled) {
667
583
  if (terminal)
@@ -688,11 +604,6 @@ async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
688
604
  const recovered = await recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
689
605
  if (!recovered)
690
606
  continue;
691
- if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
692
- const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
693
- if (view.state === 'unknown')
694
- continue;
695
- }
696
607
  writeSandboxControlResponse(manifest, recovered);
697
608
  const preserveRecoveryEvidence = recovered.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN';
698
609
  if (transitionProtocolActive && !preserveRecoveryEvidence) {
@@ -700,7 +611,7 @@ async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
700
611
  }
701
612
  if (preserveRecoveryEvidence)
702
613
  continue;
703
- payloadReferenced = Boolean(payload) && request.family !== 'task-finalization';
614
+ payloadReferenced = Boolean(payload);
704
615
  }
705
616
  }
706
617
  else {
@@ -794,14 +705,10 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
794
705
  startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
795
706
  }
796
707
  catch {
797
- // Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
798
708
  startupReceipt = {};
799
709
  }
800
710
  }
801
- let taskView = taskViewForManifest({
802
- ...manifest,
803
- receipt: startupReceipt
804
- });
711
+ let taskView = taskViewForManifest({ ...manifest, receipt: startupReceipt });
805
712
  try {
806
713
  const previous = readSandboxControlStatus(manifest.publicStatusDir);
807
714
  if (previous.generation === manifest.generation)
@@ -983,17 +890,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
983
890
  break;
984
891
  let terminalCommitted = false;
985
892
  if (settledExecution.result && settledExecution.resultEvidenceWritten) {
986
- terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
987
- if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
988
- try {
989
- const published = readSandboxControlStatus(manifest.publicStatusDir);
990
- if (published.generation === manifest.generation)
991
- taskView = published.taskView;
992
- }
993
- catch {
994
- taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
995
- }
996
- }
893
+ terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, brokerOwns);
997
894
  }
998
895
  else {
999
896
  terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
@@ -1255,7 +1152,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1255
1152
  }
1256
1153
  }
1257
1154
  if (owned && active.result && active.resultEvidenceWritten) {
1258
- if (publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
1155
+ if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
1259
1156
  if (brokerOwns()) {
1260
1157
  removeAcceptedResponse(manifest, active.request.id);
1261
1158
  fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
@@ -1264,22 +1161,8 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1264
1161
  }
1265
1162
  }
1266
1163
  if (active) {
1267
- const terminationConfirmed = active.prepared.terminate(owned);
1268
- if (owned && brokerOwns() && active.request.family === 'task-finalization') {
1269
- const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
1270
- const view = recovered.status === 'matched'
1271
- ? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
1272
- : null;
1273
- if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
1274
- && writeSandboxControlResponse(manifest, recovered.response)) {
1275
- if (terminationConfirmed && brokerOwns()) {
1276
- removeAcceptedResponse(manifest, active.request.id);
1277
- fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
1278
- active = null;
1279
- }
1280
- }
1281
- }
1282
- if (owned && brokerOwns() && active && active.request.family !== 'task-finalization' && !active.resultEvidenceWritten) {
1164
+ active.prepared.terminate(owned);
1165
+ if (owned && brokerOwns() && active && !active.resultEvidenceWritten) {
1283
1166
  writeSandboxControlResponse(manifest, unknown(active.request.id));
1284
1167
  }
1285
1168
  }
@@ -8,7 +8,6 @@ import { enumerateAllTaskDirs } from '../task/resolve-ref.js';
8
8
  import { resolveSandboxCleanupTarget } from './workspace-identity.js';
9
9
  import { readSandboxControlManifest } from './control/lifecycle.js';
10
10
  import { readJsonFile, readSandboxControlStatus } from './control/state.js';
11
- import { finalizationTerminalResponse } from './control/finalization-response.js';
12
11
  const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
13
12
  function matchingControlManifest(repoRoot, controlRoot, taskId, binding) {
14
13
  if (!controlRootIdentity(controlRoot))
@@ -77,11 +76,9 @@ function canonicalPath(input) {
77
76
  return path.resolve(input);
78
77
  }
79
78
  }
80
- function taskFinalizationReceiptComplete(receipt, taskId, binding) {
79
+ function taskFinalizationReceiptComplete(receipt, taskId) {
81
80
  return receipt.taskId === taskId
82
- && taskFinalizationReceiptState(receipt) === 'complete'
83
- && receipt.controlBinding?.generation === binding.generation
84
- && receipt.controlBinding.requestId === binding.requestId;
81
+ && taskFinalizationReceiptState(receipt) === 'complete';
85
82
  }
86
83
  function terminalControlBindingEvidence(repoRootInput, controlRootInput, taskId, binding) {
87
84
  const repoRoot = canonicalPath(repoRootInput);
@@ -105,26 +102,22 @@ function terminalControlBindingEvidence(repoRootInput, controlRootInput, taskId,
105
102
  return false;
106
103
  }
107
104
  const receipt = readTaskFinalizationReceipt(repoRoot, taskId);
108
- if (!receipt || !taskFinalizationReceiptComplete(receipt, taskId, binding)) {
105
+ if (!receipt || !taskFinalizationReceiptComplete(receipt, taskId)) {
109
106
  return false;
110
107
  }
111
- const status = readSandboxControlStatus(manifest.publicStatusDir);
112
- const viewReceipt = status.taskView.receipt;
113
- const terminalTaskView = (status.taskView.state === 'current' && status.taskView.observedSource === 'completed')
114
- || (status.taskView.state === 'finalized-stale' && status.taskView.observedSource === 'active');
115
- if (status.generation !== binding.generation || status.state !== 'healthy'
116
- || status.activeRequestId !== null || !terminalTaskView || status.taskView.taskId !== taskId
117
- || !viewReceipt || viewReceipt.generation !== binding.generation
118
- || viewReceipt.requestId !== binding.requestId
119
- || viewReceipt.receiptId !== receipt.receiptId
120
- || viewReceipt.revision !== receipt.revision)
121
- return false;
122
108
  const responsePath = path.join(manifest.channelDir, 'responses', `${binding.requestId}.json`);
123
109
  if (!inspectOwnedPath(path.join(controlRoot, 'channel'), responsePath, 'file'))
124
110
  return false;
125
111
  const actual = readJsonFile(responsePath);
126
- const expected = finalizationTerminalResponse(taskId, binding.requestId, receipt);
127
- return JSON.stringify(actual) === JSON.stringify(expected);
112
+ const response = actual;
113
+ if (response.version !== 2 || response.id !== binding.requestId || response.phase !== 'completed'
114
+ || response.exitCode !== 0 || typeof response.stdout !== 'string')
115
+ return false;
116
+ const envelope = JSON.parse(response.stdout);
117
+ const result = envelope.result;
118
+ return envelope.status === 'completed' && envelope.accepted === true
119
+ && result !== null && typeof result === 'object'
120
+ && result.status === 'completed' && result.taskId === taskId;
128
121
  }
129
122
  catch {
130
123
  return false;
@@ -153,7 +146,7 @@ function removedControlBindingEvidence(repoRootInput, journal, taskId, binding)
153
146
  return false;
154
147
  try {
155
148
  const receipt = readTaskFinalizationReceipt(repoRoot, taskId);
156
- return receipt !== null && taskFinalizationReceiptComplete(receipt, taskId, binding);
149
+ return receipt !== null && taskFinalizationReceiptComplete(receipt, taskId);
157
150
  }
158
151
  catch {
159
152
  return false;
@@ -13,7 +13,7 @@ export function parseArtifactCommand(args) {
13
13
  }
14
14
  const fields = {};
15
15
  const allowed = {
16
- inspect: ['--family'],
16
+ inspect: ['--family', '--source-finding', '--source-artifact', '--source-sha256'],
17
17
  init: ['--family', '--artifact', '--locale'],
18
18
  'preflight': ['--family', '--artifact'],
19
19
  'finalize-local': ['--family', '--artifact']
@@ -37,8 +37,10 @@ export function parseArtifactCommand(args) {
37
37
  if (locale !== undefined && locale !== 'zh-CN' && locale !== 'en')
38
38
  throw new Error("option '--locale' must be 'zh-CN' or 'en'");
39
39
  return {
40
- taskRef, operation: operation, family: fields['--family'],
41
- artifact: fields['--artifact'] ?? '', locale
40
+ taskRef, operation: operation, family: fields['--family'] ?? '',
41
+ artifact: fields['--artifact'] ?? '', locale,
42
+ sourceFinding: fields['--source-finding'], sourceArtifact: fields['--source-artifact'],
43
+ sourceSha256: fields['--source-sha256']
42
44
  };
43
45
  }
44
46
  /** Resolve authoritative task metadata separately from a trusted candidate directory. */
@@ -46,7 +48,12 @@ export function executeArtifactCommand(command, options = {}) {
46
48
  const { taskRef, operation, family, artifact, locale } = command;
47
49
  const fail = (code, message) => ({ status: 'failed', changed: false, error: { code, message } });
48
50
  if (operation === 'inspect') {
49
- const result = resolveArtifactContext(taskRef, family, { repoRoot: options.repoRoot });
51
+ const result = resolveArtifactContext(taskRef, family, {
52
+ repoRoot: options.repoRoot,
53
+ sourceFinding: command.sourceFinding,
54
+ sourceArtifact: command.sourceArtifact,
55
+ sourceSha256: command.sourceSha256
56
+ });
50
57
  return family === 'code' && result.codeMode ? {
51
58
  ...result, mode: result.codeMode.mode, code_max: result.codeMode.codeMax, rev_max: result.codeMode.reviewMax,
52
59
  verdict: result.codeMode.verdict, next_round: result.next?.round ?? null, next_artifact: result.next?.name ?? null,