@fitlab-ai/agent-infra 0.9.9 → 0.9.11
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.
- package/README.md +13 -3
- package/README.zh-CN.md +13 -3
- package/bin/cli.ts +17 -4
- package/dist/bin/cli.js +19 -4
- package/dist/bin/internal-cli.js +55 -4
- package/dist/lib/agent-client.js +0 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +17 -10
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +11 -31
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +15 -33
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +72 -11
- package/dist/lib/agent-clients/adapters/index.js +3 -1
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
- package/dist/lib/agent-clients/adapters/traecli.js +65 -0
- package/dist/lib/agent-clients/config.js +14 -99
- package/dist/lib/agent-clients/reconcile.js +1 -11
- package/dist/lib/agent-clients/schema.js +5 -4
- package/dist/lib/agent-clients/tokens.js +7 -17
- package/dist/lib/agent-clients/types.js +2 -1
- package/dist/lib/decide.js +14 -30
- package/dist/lib/defaults.json +9 -0
- package/dist/lib/init.js +2 -2
- package/dist/lib/internal/codex-lifecycle.js +2 -2
- package/dist/lib/internal/codex-sandbox-controller.js +4 -3
- package/dist/lib/internal/release-workflow.js +0 -1
- package/dist/lib/internal/sandbox-control.js +2 -2
- package/dist/lib/internal/task-finalization.js +19 -9
- package/dist/lib/internal/task-ledger.js +8 -2
- package/dist/lib/internal/task-lifecycle.js +17 -93
- package/dist/lib/internal/task-orchestration.js +30 -216
- package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +77 -43
- package/dist/lib/merge.js +205 -103
- package/dist/lib/process-data/normalize.js +2 -9
- package/dist/lib/run/host.js +46 -16
- package/dist/lib/run/tui.js +5 -2
- package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
- package/dist/lib/sandbox/commands/create.js +3 -3
- package/dist/lib/sandbox/commands/enter.js +18 -4
- package/dist/lib/sandbox/config.js +3 -12
- package/dist/lib/sandbox/control/controller-registration.js +4 -2
- package/dist/lib/sandbox/control/executor.js +135 -40
- package/dist/lib/sandbox/control/lifecycle.js +29 -20
- package/dist/lib/sandbox/control/server.js +18 -16
- package/dist/lib/sandbox/mounts.js +3 -2
- package/dist/lib/sandbox/recovery.js +123 -22
- package/dist/lib/sandbox/workspace-identity.js +14 -0
- package/dist/lib/sandbox/workspace-view.js +8 -9
- package/dist/lib/self-update.js +278 -0
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +4 -2
- package/dist/lib/task/codex-orchestration.js +32 -35
- package/dist/lib/task/command-options.js +1 -3
- package/dist/lib/task/commands/cat.js +0 -5
- package/dist/lib/task/commands/decisions.js +76 -92
- package/dist/lib/task/commands/files.js +6 -6
- package/dist/lib/task/commands/grep.js +25 -61
- package/dist/lib/task/commands/issue-body.js +6 -6
- package/dist/lib/task/commands/log.js +25 -10
- package/dist/lib/task/commands/show.js +5 -6
- package/dist/lib/task/commands/status.js +8 -8
- package/dist/lib/task/commit-operation.js +21 -25
- package/dist/lib/task/commit-policy.js +1 -1
- package/dist/lib/task/control-authority.js +431 -0
- package/dist/lib/task/create.js +6 -1
- package/dist/lib/task/current-contract.js +35 -0
- package/dist/lib/task/decision-details.js +263 -0
- package/dist/lib/task/decision-intents.js +5 -2
- package/dist/lib/task/delegation-receipts.js +8 -7
- package/dist/lib/task/events.js +11 -5
- package/dist/lib/task/finalization.js +72 -46
- package/dist/lib/task/index.js +19 -29
- package/dist/lib/task/ledger-intents.js +8 -9
- package/dist/lib/task/ledger.js +13 -6
- package/dist/lib/task/lifecycle.js +16 -0
- package/dist/lib/task/orchestration.js +51 -14
- package/dist/lib/task/review-artifacts.js +31 -12
- package/dist/lib/task/review-finalization.js +19 -6
- package/dist/lib/task/verification-engine.js +38 -18
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/workspace-snapshot.js +5 -11
- package/dist/lib/task/write.js +11 -1
- package/dist/lib/update.js +6 -37
- package/dist/lib/version.js +5 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +0 -1
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +25 -10
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +16 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +16 -35
- package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
- package/lib/agent-clients/adapters/index.ts +3 -1
- package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
- package/lib/agent-clients/adapters/traecli.ts +66 -0
- package/lib/agent-clients/config.ts +14 -144
- package/lib/agent-clients/reconcile.ts +1 -12
- package/lib/agent-clients/schema.ts +5 -4
- package/lib/agent-clients/tokens.ts +7 -20
- package/lib/agent-clients/types.ts +2 -1
- package/lib/decide.ts +12 -22
- package/lib/defaults.json +9 -0
- package/lib/init.ts +2 -2
- package/lib/internal/codex-lifecycle.ts +2 -2
- package/lib/internal/codex-sandbox-controller.ts +4 -3
- package/lib/internal/release-workflow.ts +0 -1
- package/lib/internal/sandbox-control.ts +2 -2
- package/lib/internal/task-finalization.ts +27 -13
- package/lib/internal/task-ledger.ts +8 -2
- package/lib/internal/task-lifecycle.ts +33 -76
- package/lib/internal/task-orchestration.ts +33 -190
- package/lib/{task/commands/validate.ts → internal/task-validate.ts} +77 -41
- package/lib/merge.ts +236 -120
- package/lib/process-data/normalize.ts +2 -8
- package/lib/run/host.ts +63 -14
- package/lib/run/tui.ts +5 -3
- package/lib/sandbox/agent-client-reconciler.ts +5 -15
- package/lib/sandbox/commands/create.ts +4 -4
- package/lib/sandbox/commands/enter.ts +24 -4
- package/lib/sandbox/config.ts +2 -14
- package/lib/sandbox/control/container-identity.ts +2 -2
- package/lib/sandbox/control/controller-registration.ts +8 -3
- package/lib/sandbox/control/executor.ts +156 -53
- package/lib/sandbox/control/lifecycle.ts +34 -28
- package/lib/sandbox/control/protocol.ts +1 -3
- package/lib/sandbox/control/server.ts +15 -12
- package/lib/sandbox/control/state.ts +7 -7
- package/lib/sandbox/mounts.ts +4 -3
- package/lib/sandbox/recovery.ts +162 -34
- package/lib/sandbox/tools.ts +1 -1
- package/lib/sandbox/workspace-identity.ts +28 -0
- package/lib/sandbox/workspace-view.ts +14 -10
- package/lib/self-update.ts +341 -0
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +6 -1
- package/lib/task/artifact-lifecycle.ts +5 -3
- package/lib/task/codex-orchestration.ts +40 -45
- package/lib/task/command-options.ts +1 -2
- package/lib/task/commands/cat.ts +0 -4
- package/lib/task/commands/decisions.ts +80 -75
- package/lib/task/commands/files.ts +6 -6
- package/lib/task/commands/grep.ts +23 -51
- package/lib/task/commands/issue-body.ts +6 -6
- package/lib/task/commands/log.ts +22 -10
- package/lib/task/commands/show.ts +5 -6
- package/lib/task/commands/status.ts +8 -8
- package/lib/task/commit-operation.ts +25 -30
- package/lib/task/commit-policy.ts +1 -2
- package/lib/task/control-authority.ts +563 -0
- package/lib/task/create.ts +6 -1
- package/lib/task/current-contract.ts +49 -0
- package/lib/task/decision-details.ts +340 -0
- package/lib/task/decision-intents.ts +7 -2
- package/lib/task/delegation-receipts.ts +8 -6
- package/lib/task/events.ts +10 -5
- package/lib/task/finalization.ts +84 -42
- package/lib/task/index.ts +19 -28
- package/lib/task/ledger-intents.ts +6 -8
- package/lib/task/ledger.ts +17 -7
- package/lib/task/lifecycle.ts +20 -0
- package/lib/task/orchestration.ts +42 -16
- package/lib/task/review-artifacts.ts +53 -15
- package/lib/task/review-finalization.ts +33 -7
- package/lib/task/verification-engine.ts +33 -21
- package/lib/task/verification-review.ts +1 -1
- package/lib/task/workspace-snapshot.ts +4 -13
- package/lib/task/write.ts +20 -1
- package/lib/update.ts +6 -40
- package/lib/version.ts +6 -1
- package/package.json +1 -1
- package/templates/.agents/QUICKSTART.en.md +2 -3
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -3
- package/templates/.agents/README.en.md +5 -3
- package/templates/.agents/README.zh-CN.md +5 -3
- package/templates/.agents/rules/README.en.md +1 -0
- package/templates/.agents/rules/README.zh-CN.md +1 -0
- package/templates/.agents/rules/compatibility-policy.en.md +40 -0
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +40 -0
- package/templates/.agents/rules/create-issue.github.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
- package/templates/.agents/rules/human-decision-context.en.md +9 -7
- package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
- package/templates/.agents/rules/issue-fields.github.en.md +1 -1
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.github.en.md +1 -1
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -2
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
- package/templates/.agents/rules/milestone-inference.github.en.md +1 -1
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +1 -1
- package/templates/.agents/rules/next-step-output.en.md +3 -3
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
- package/templates/.agents/rules/pr-sync.en.md +2 -2
- package/templates/.agents/rules/pr-sync.github.en.md +3 -2
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +3 -1
- package/templates/.agents/rules/pr-sync.zh-CN.md +2 -1
- package/templates/.agents/rules/release-commands.github.en.md +6 -0
- package/templates/.agents/rules/release-commands.github.zh-CN.md +6 -0
- package/templates/.agents/rules/review-handshake.en.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/review-method.en.md +1 -0
- package/templates/.agents/rules/review-method.zh-CN.md +1 -0
- package/templates/.agents/rules/task-management.en.md +1 -2
- package/templates/.agents/rules/task-management.zh-CN.md +1 -2
- package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/commit/SKILL.en.md +27 -7
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -5
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/create-pr/SKILL.en.md +15 -11
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +15 -11
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +46 -0
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +46 -0
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
- package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
- package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -4
- package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -3
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +9 -6
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +9 -6
- package/templates/.agents/skills/review-plan/SKILL.en.md +5 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +7 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +6 -2
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +159 -104
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/analyze-task.en.md +1 -1
- package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.claude/commands/block-task.en.md +1 -1
- package/templates/.claude/commands/block-task.zh-CN.md +1 -1
- package/templates/.claude/commands/cancel-task.en.md +1 -1
- package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
- package/templates/.claude/commands/check-task.en.md +1 -1
- package/templates/.claude/commands/check-task.zh-CN.md +1 -1
- package/templates/.claude/commands/code-task.en.md +1 -1
- package/templates/.claude/commands/code-task.zh-CN.md +1 -1
- package/templates/.claude/commands/commit.en.md +1 -1
- package/templates/.claude/commands/commit.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-task.en.md +1 -1
- package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
- package/templates/.claude/commands/create-pr.en.md +1 -1
- package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
- package/templates/.claude/commands/plan-task.en.md +1 -1
- package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
- package/templates/.claude/commands/review-analysis.en.md +1 -1
- package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.claude/commands/review-code.en.md +1 -1
- package/templates/.claude/commands/review-code.zh-CN.md +1 -1
- package/templates/.claude/commands/review-plan.en.md +1 -1
- package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/run-task.en.md +1 -1
- package/templates/.claude/commands/run-task.zh-CN.md +1 -1
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.traecli/commands/update-agent-infra.en.md +7 -0
- package/templates/.traecli/commands/update-agent-infra.zh-CN.md +7 -0
- package/dist/lib/builtin-tuis.js +0 -40
- package/lib/builtin-tuis.ts +0 -56
package/lib/task/finalization.ts
CHANGED
|
@@ -85,6 +85,13 @@ type TaskFinalizationStep = Readonly<{
|
|
|
85
85
|
pendingSteps?: readonly string[];
|
|
86
86
|
}>;
|
|
87
87
|
|
|
88
|
+
type TaskCommentSyncOutcome = Readonly<{
|
|
89
|
+
receipt: TaskFinalizationReceipt;
|
|
90
|
+
step: TaskFinalizationStep;
|
|
91
|
+
changed: boolean;
|
|
92
|
+
error: FinalizationError | null;
|
|
93
|
+
}>;
|
|
94
|
+
|
|
88
95
|
type TaskFinalizationResult = Readonly<{
|
|
89
96
|
status: 'completed' | 'failed' | 'blocked';
|
|
90
97
|
changed: boolean;
|
|
@@ -390,11 +397,18 @@ function mutationPatch(current: TaskFinalizationReceipt, mutation: FinalizationM
|
|
|
390
397
|
}
|
|
391
398
|
if (mutation.scope === 'verification') {
|
|
392
399
|
if (mutation.operation === 'succeeded') {
|
|
400
|
+
const resolvedTaskWarning = current.warnings.some((warning) => warning.step === 'verification' && warning.status === 'open');
|
|
393
401
|
const warnings = resolveStepWarnings(current, 'verification');
|
|
394
|
-
return {
|
|
402
|
+
return {
|
|
403
|
+
verification: 'done',
|
|
404
|
+
taskComment: resolvedTaskWarning ? 'pending' : current.taskComment,
|
|
405
|
+
warningProjection: warnings.length > 0 ? 'pending' : 'done',
|
|
406
|
+
warnings,
|
|
407
|
+
lastError: null
|
|
408
|
+
};
|
|
395
409
|
}
|
|
396
410
|
const warnings = replaceWarning(current, warningFromError('verification', mutation.error), 'open');
|
|
397
|
-
return { verification: 'pending', warningProjection: 'pending', warnings, lastError: mutation.error };
|
|
411
|
+
return { verification: 'pending', taskComment: 'pending', warningProjection: 'pending', warnings, lastError: mutation.error };
|
|
398
412
|
}
|
|
399
413
|
return mutation.operation === 'succeeded'
|
|
400
414
|
? { warningProjection: 'done', lastError: null }
|
|
@@ -472,6 +486,56 @@ function reconcileWarningProjection(repoRoot: string, taskId: string, receipt: T
|
|
|
472
486
|
catch { return receipt; }
|
|
473
487
|
}
|
|
474
488
|
|
|
489
|
+
function syncPendingTaskComment(input: {
|
|
490
|
+
repoRoot: string;
|
|
491
|
+
taskId: string;
|
|
492
|
+
agent: string;
|
|
493
|
+
receipt: TaskFinalizationReceipt;
|
|
494
|
+
commentSync: typeof syncPlatformComment;
|
|
495
|
+
consumedCapabilities: Set<string>;
|
|
496
|
+
}): TaskCommentSyncOutcome {
|
|
497
|
+
const { repoRoot, taskId, agent, commentSync, consumedCapabilities } = input;
|
|
498
|
+
let receipt = input.receipt;
|
|
499
|
+
if (receipt.taskComment !== 'pending') {
|
|
500
|
+
return {
|
|
501
|
+
receipt,
|
|
502
|
+
step: { status: receipt.taskComment === 'skipped' ? 'skipped' : 'no-op', changed: false, error: null },
|
|
503
|
+
changed: false,
|
|
504
|
+
error: null
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
try {
|
|
508
|
+
const result = commentSync(taskId, { kind: 'task', agent, cwd: repoRoot });
|
|
509
|
+
const step = commentStep(result);
|
|
510
|
+
if (result.status === 'applied' || result.status === 'no-op') {
|
|
511
|
+
const skipped = result.error?.code === 'ISSUE_NOT_LINKED';
|
|
512
|
+
const capability = issueCapability(receipt, 'task-comment');
|
|
513
|
+
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
514
|
+
scope: 'task-comment', operation: 'succeeded', state: skipped ? 'skipped' : 'done'
|
|
515
|
+
}, consumedCapabilities);
|
|
516
|
+
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
517
|
+
return { receipt, step: skipped ? { ...step, status: 'skipped' } : step, changed: result.changed, error: null };
|
|
518
|
+
}
|
|
519
|
+
const detail = step.error ?? { code: 'COMMENT_SYNC_FAILED', message: 'task comment synchronization failed', retryable: true };
|
|
520
|
+
const capability = issueCapability(receipt, 'task-comment');
|
|
521
|
+
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
522
|
+
scope: 'task-comment', operation: 'failed', error: detail
|
|
523
|
+
}, consumedCapabilities);
|
|
524
|
+
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
525
|
+
return { receipt, step, changed: result.changed, error: detail };
|
|
526
|
+
} catch (error) {
|
|
527
|
+
const detail = errorOf(error, 'COMMENT_SYNC_FAILED', true);
|
|
528
|
+
try {
|
|
529
|
+
const capability = issueCapability(receipt, 'task-comment');
|
|
530
|
+
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
531
|
+
scope: 'task-comment', operation: 'failed', error: detail
|
|
532
|
+
}, consumedCapabilities);
|
|
533
|
+
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
534
|
+
} catch { /* preserve the primary error */ }
|
|
535
|
+
return { receipt, step: { status: 'blocked', changed: false, error: detail }, changed: false, error: detail };
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
475
539
|
function terminalResult(
|
|
476
540
|
taskId: string,
|
|
477
541
|
receipt: TaskFinalizationReceipt,
|
|
@@ -603,46 +667,15 @@ function applyUnderLock(
|
|
|
603
667
|
}
|
|
604
668
|
|
|
605
669
|
let taskComment: TaskFinalizationStep | null = null;
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
}
|
|
615
|
-
const result = commentSync(taskId, { kind: 'task', agent: request.agent, cwd: repoRoot });
|
|
616
|
-
taskComment = commentStep(result);
|
|
617
|
-
if (result.status === 'applied' || result.status === 'no-op') {
|
|
618
|
-
const skipped = result.error?.code === 'ISSUE_NOT_LINKED';
|
|
619
|
-
const capability = issueCapability(receipt, 'task-comment');
|
|
620
|
-
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
621
|
-
scope: 'task-comment', operation: 'succeeded', state: skipped ? 'skipped' : 'done'
|
|
622
|
-
}, consumedCapabilities);
|
|
623
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
624
|
-
taskComment = skipped ? { ...taskComment, status: 'skipped' } : taskComment;
|
|
625
|
-
changed = changed || result.changed;
|
|
626
|
-
} else {
|
|
627
|
-
const detail = taskComment.error ?? { code: 'COMMENT_SYNC_FAILED', message: 'task comment synchronization failed', retryable: true };
|
|
628
|
-
const capability = issueCapability(receipt, 'task-comment');
|
|
629
|
-
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
630
|
-
scope: 'task-comment', operation: 'failed', error: detail
|
|
631
|
-
}, consumedCapabilities);
|
|
632
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
633
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification: null }, changed, taskComment.error);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
} catch (error) {
|
|
637
|
-
const detail = errorOf(error, 'COMMENT_SYNC_FAILED', true);
|
|
638
|
-
try {
|
|
639
|
-
const capability = issueCapability(receipt, 'task-comment');
|
|
640
|
-
receipt = applyFinalizationReceiptMutationUnderLock(repoRoot, receipt, capability, {
|
|
641
|
-
scope: 'task-comment', operation: 'failed', error: detail
|
|
642
|
-
}, consumedCapabilities);
|
|
643
|
-
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
644
|
-
} catch { /* preserve the primary error */ }
|
|
645
|
-
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment: { status: 'blocked', changed: false, error: detail }, verification: null }, changed, detail);
|
|
670
|
+
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
671
|
+
const initialComment = syncPendingTaskComment({
|
|
672
|
+
repoRoot, taskId, agent: request.agent, receipt, commentSync, consumedCapabilities
|
|
673
|
+
});
|
|
674
|
+
receipt = initialComment.receipt;
|
|
675
|
+
taskComment = initialComment.step;
|
|
676
|
+
changed = changed || initialComment.changed;
|
|
677
|
+
if (initialComment.error) {
|
|
678
|
+
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification: null }, changed, initialComment.error);
|
|
646
679
|
}
|
|
647
680
|
|
|
648
681
|
let verification: TaskFinalizationStep | null = null;
|
|
@@ -662,6 +695,15 @@ function applyUnderLock(
|
|
|
662
695
|
scope: 'verification', operation: 'succeeded'
|
|
663
696
|
}, consumedCapabilities);
|
|
664
697
|
receipt = reconcileWarningProjection(repoRoot, taskId, receipt, consumedCapabilities);
|
|
698
|
+
const finalComment = syncPendingTaskComment({
|
|
699
|
+
repoRoot, taskId, agent: request.agent, receipt, commentSync, consumedCapabilities
|
|
700
|
+
});
|
|
701
|
+
receipt = finalComment.receipt;
|
|
702
|
+
if (finalComment.step.status !== 'no-op') taskComment = finalComment.step;
|
|
703
|
+
changed = changed || finalComment.changed;
|
|
704
|
+
if (finalComment.error) {
|
|
705
|
+
return terminalResult(taskId, receipt, { lifecycle: lifecycleResult, taskComment, verification }, changed, finalComment.error);
|
|
706
|
+
}
|
|
665
707
|
} else {
|
|
666
708
|
const detail = verification.error ?? { code: 'VERIFY_FAILED', message: 'verification failed', retryable: true };
|
|
667
709
|
const capability = issueCapability(receipt, 'verification');
|
package/lib/task/index.ts
CHANGED
|
@@ -1,36 +1,33 @@
|
|
|
1
1
|
const USAGE = `Usage: ai task <command> [options]
|
|
2
2
|
|
|
3
3
|
Commands:
|
|
4
|
-
cat [--task <ref>] <artifact | N>
|
|
5
|
-
decisions, d [--task <ref>] [-i
|
|
6
|
-
files [--task <ref>]
|
|
7
|
-
grep
|
|
8
|
-
issue-body [--task <ref>] [--template] Print a deterministic Issue body from task.md
|
|
9
|
-
log [--task <ref>]
|
|
4
|
+
cat [--task <ref> | -t <ref>] <artifact | N> Print a task artifact (by name or number)
|
|
5
|
+
decisions, d [--task <ref> | -t <ref>] [--item <selector> | -i <selector>] List review decisions, or show one item's detail
|
|
6
|
+
files [--task <ref> | -t <ref>] List artifacts in a task dir (numbered)
|
|
7
|
+
grep [--current | --task <ref> | -t <ref>] <pattern> Literal search in the current or selected task
|
|
8
|
+
issue-body [--task <ref> | -t <ref>] [--template <path>] Print a deterministic Issue body from task.md
|
|
9
|
+
log [--task <ref> | -t <ref>] Render a task's activity log as a timeline
|
|
10
10
|
ls [--all | --blocked | --completed] List tasks (default: active)
|
|
11
|
-
show [--task <ref>]
|
|
12
|
-
status [--task <ref>]
|
|
13
|
-
validate <ref> [options] -- <command> Run host validation in snapshot or inplace scope
|
|
11
|
+
show [--task <ref> | -t <ref>] Print a task.md
|
|
12
|
+
status [--task <ref> | -t <ref>] Aggregated status view (metadata / artifacts / git / platform)
|
|
14
13
|
|
|
15
14
|
Examples:
|
|
16
|
-
ai task cat 11 analysis
|
|
17
15
|
ai task cat analysis
|
|
18
|
-
ai task cat 11
|
|
19
|
-
ai task decisions 11
|
|
16
|
+
ai task cat --task 11 analysis
|
|
17
|
+
ai task decisions --task 11
|
|
20
18
|
ai task decisions --item 1
|
|
21
|
-
ai task d 11 PL-3 --format markdown
|
|
22
|
-
ai task files 11
|
|
19
|
+
ai task d --task 11 --item PL-3 --format markdown
|
|
20
|
+
ai task files --task 11
|
|
23
21
|
ai task grep resolveArtifact
|
|
24
22
|
ai task grep resolveArtifact --current
|
|
25
|
-
ai task grep resolveArtifact 11
|
|
26
|
-
ai task issue-body 11
|
|
27
|
-
ai task issue-body 11 --template .github/ISSUE_TEMPLATE/05_other.yml
|
|
28
|
-
ai task log 11
|
|
23
|
+
ai task grep resolveArtifact --task 11
|
|
24
|
+
ai task issue-body --task 11
|
|
25
|
+
ai task issue-body --task 11 --template .github/ISSUE_TEMPLATE/05_other.yml
|
|
26
|
+
ai task log --task 11
|
|
29
27
|
ai task ls
|
|
30
|
-
ai task show 11
|
|
31
|
-
ai task show TASK-20260612-162737
|
|
32
|
-
ai task status 11
|
|
33
|
-
ai task validate 11 -- npm test
|
|
28
|
+
ai task show --task 11
|
|
29
|
+
ai task show --task TASK-20260612-162737
|
|
30
|
+
ai task status --task 11
|
|
34
31
|
|
|
35
32
|
Run 'ai task <command> --help' for details.`;
|
|
36
33
|
|
|
@@ -95,12 +92,6 @@ export async function runTask(args: string[]): Promise<void> {
|
|
|
95
92
|
status(rest);
|
|
96
93
|
break;
|
|
97
94
|
}
|
|
98
|
-
case 'validate': {
|
|
99
|
-
const { validate } = await import('./commands/validate.ts');
|
|
100
|
-
const exitCode = await validate(rest);
|
|
101
|
-
if (exitCode !== 0) process.exitCode = exitCode;
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
95
|
default:
|
|
105
96
|
process.stderr.write(`Unknown task command: ${subcommand}\n\n`);
|
|
106
97
|
process.stdout.write(`${USAGE}\n`);
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
parseImplementationInputs,
|
|
8
8
|
renderImplementationInputs
|
|
9
9
|
} from './implementation-inputs.ts';
|
|
10
|
-
import { LEDGER_COLUMNS, LEDGER_HEADINGS, nextHdId,
|
|
10
|
+
import { LEDGER_COLUMNS, LEDGER_HEADINGS, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, nextHdId, parseLedgerDocument, validateLedgerRows } from './ledger.ts';
|
|
11
11
|
import type { LedgerRow, ReviewStage } from './ledger.ts';
|
|
12
12
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
13
13
|
import { writeTask } from './write.ts';
|
|
@@ -61,6 +61,7 @@ function normalizedEvidence(value: string): string {
|
|
|
61
61
|
|
|
62
62
|
function ledgerReadErrorCode(error: unknown): string {
|
|
63
63
|
const code = error && typeof error === 'object' && 'code' in error ? String(error.code) : '';
|
|
64
|
+
if (code === LEDGER_SECTION_MISSING_CODE) return code;
|
|
64
65
|
return code === 'TABLE_DUPLICATE_KEY' ? 'LEDGER_DUPLICATE_ID' : 'LEDGER_DOCUMENT_INVALID';
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -96,12 +97,7 @@ function rowMutation(row: LedgerRow): TaskMutation {
|
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
function ledgerSectionMutation(content: string): TaskMutation[] {
|
|
99
|
-
|
|
100
|
-
const english = /^##\s+Activity Log\s*$/m.test(content);
|
|
101
|
-
return [{
|
|
102
|
-
kind: 'section', aliases: LEDGER_HEADINGS, heading: english ? 'Review Disagreement Ledger' : '审查分歧账本',
|
|
103
|
-
body: `| ${LEDGER_COLUMNS.join(' | ')} |\n|----|-------|-------|----------|--------|----------|`
|
|
104
|
-
}];
|
|
100
|
+
return [];
|
|
105
101
|
}
|
|
106
102
|
|
|
107
103
|
function implementationInputMutation(content: string, rows: Parameters<typeof renderImplementationInputs>[0]): TaskMutation {
|
|
@@ -127,7 +123,9 @@ function applyLedgerIntent(intent: LedgerIntent, options: TaskWriteOptions = {})
|
|
|
127
123
|
let rows: LedgerRow[];
|
|
128
124
|
try {
|
|
129
125
|
content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
130
|
-
|
|
126
|
+
const ledger = parseLedgerDocument(content);
|
|
127
|
+
if (!ledger.present) return failed(intent, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, resolved.taskId);
|
|
128
|
+
rows = ledger.rows as LedgerRow[];
|
|
131
129
|
} catch (error) {
|
|
132
130
|
return failed(intent, ledgerReadErrorCode(error), error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
133
131
|
}
|
package/lib/task/ledger.ts
CHANGED
|
@@ -20,6 +20,10 @@ type LedgerRow = {
|
|
|
20
20
|
/** Zero-based line within the normalized ledger section body. */
|
|
21
21
|
sourceLine: number;
|
|
22
22
|
};
|
|
23
|
+
type LedgerDocument = {
|
|
24
|
+
present: boolean;
|
|
25
|
+
rows: readonly LedgerRow[];
|
|
26
|
+
};
|
|
23
27
|
|
|
24
28
|
type ReviewStage = 'analysis' | 'plan' | 'code';
|
|
25
29
|
type ReviewSeverity = 'blocker' | 'major' | 'minor';
|
|
@@ -40,6 +44,8 @@ const LEDGER_TERMINAL = new Set(['confirmed', 'closed', 'human-decided']);
|
|
|
40
44
|
// Statuses that represent a decision row awaiting or carrying a human ruling.
|
|
41
45
|
const HUMAN_DECISION_STATUSES = new Set(['needs-human-decision', 'human-decided']);
|
|
42
46
|
const FINDING_STATUSES = new Set(['open', 'accepted', 'adjusted', 'refuted', 'cannot-judge', 'confirmed', 'needs-human-decision', 'closed', 'human-decided']);
|
|
47
|
+
const LEDGER_SECTION_MISSING_CODE = 'LEDGER_SECTION_MISSING';
|
|
48
|
+
const LEDGER_SECTION_MISSING_MESSAGE = 'review disagreement ledger section is missing';
|
|
43
49
|
|
|
44
50
|
function isReviewStage(stage: string): stage is ReviewStage {
|
|
45
51
|
return stage === 'analysis' || stage === 'plan' || stage === 'code';
|
|
@@ -48,12 +54,16 @@ function isReviewStage(stage: string): stage is ReviewStage {
|
|
|
48
54
|
// Parse all rows of the disagreement ledger table. Skips the heading, the
|
|
49
55
|
// header row (`| id | ... |`) and the `|---|` separator; ignores non-`|` lines.
|
|
50
56
|
// Rows with fewer than 6 columns are skipped (mirrors the JS gate parser).
|
|
51
|
-
function
|
|
57
|
+
function parseLedgerDocument(content: string): LedgerDocument {
|
|
52
58
|
const table = parseTable(content, { sectionAliases: LEDGER_HEADINGS, columns: LEDGER_COLUMNS });
|
|
53
|
-
return
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
if (!table) return { present: false, rows: [] };
|
|
60
|
+
return {
|
|
61
|
+
present: true,
|
|
62
|
+
rows: table.rows.map(({ values, sourceLine }) => ({
|
|
63
|
+
id: values.id!, stage: values.stage!, round: values.round!, severity: values.severity!,
|
|
64
|
+
status: values.status!, evidence: values.evidence!, sourceLine
|
|
65
|
+
}))
|
|
66
|
+
};
|
|
57
67
|
}
|
|
58
68
|
|
|
59
69
|
function validateLedgerRows(rows: readonly LedgerRow[]): LedgerValidationError | null {
|
|
@@ -130,5 +140,5 @@ function nextHdId(rows: readonly LedgerRow[]): string {
|
|
|
130
140
|
return `HD-${max + 1}`;
|
|
131
141
|
}
|
|
132
142
|
|
|
133
|
-
export { LEDGER_HEADINGS, LEDGER_COLUMNS,
|
|
134
|
-
export type { LedgerRow, ReviewStage, ReviewSeverity, LedgerValidationError, LedgerStageStatus };
|
|
143
|
+
export { LEDGER_HEADINGS, LEDGER_COLUMNS, parseLedgerDocument, nextHdId, isReviewStage, validateLedgerRows, summarizeLedgerStage, LEDGER_TERMINAL, HUMAN_DECISION_STATUSES, LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE };
|
|
144
|
+
export type { LedgerRow, LedgerDocument, ReviewStage, ReviewSeverity, LedgerValidationError, LedgerStageStatus };
|
package/lib/task/lifecycle.ts
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
} from './short-id.ts';
|
|
13
13
|
import { captureTaskWriteMetadata, writeTask } from './write.ts';
|
|
14
14
|
import type { TaskFileSystem, TaskMutation, TaskOperationSummary, TaskWriteMetadata } from './write.ts';
|
|
15
|
+
import { validateCurrentTaskContract } from './current-contract.ts';
|
|
16
|
+
import { isValidAgentInfraVersion } from '../version.ts';
|
|
15
17
|
|
|
16
18
|
const lifecycleIntentCatalog = [
|
|
17
19
|
'block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore'
|
|
@@ -126,6 +128,14 @@ function failed(request: Pick<TaskLifecycleRequest, 'taskRef' | 'intent'>, error
|
|
|
126
128
|
};
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
export function taskLifecycleFailure(
|
|
132
|
+
request: Pick<TaskLifecycleRequest, 'taskRef' | 'intent'>,
|
|
133
|
+
error: LifecycleError,
|
|
134
|
+
taskId: string | null = null
|
|
135
|
+
): TaskLifecycleResult {
|
|
136
|
+
return failed(request, error, { taskId });
|
|
137
|
+
}
|
|
138
|
+
|
|
129
139
|
function normalizedRequest(request: TaskLifecycleRequest): TaskLifecycleRequest | LifecycleError {
|
|
130
140
|
if (!request || typeof request !== 'object' || !lifecycleIntentCatalog.includes(request.intent as TaskLifecycleIntent)) {
|
|
131
141
|
return { code: 'LIFECYCLE_PAYLOAD_INVALID', message: 'intent is not in the lifecycle catalog' };
|
|
@@ -385,6 +395,10 @@ function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLif
|
|
|
385
395
|
catch (error) { return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: error instanceof Error ? error.message : String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath }); }
|
|
386
396
|
if (frontmatter.id !== taskId) return failed(request, { code: 'LIFECYCLE_IDENTITY_INVALID', message: 'task.md id does not match its lifecycle task id' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
387
397
|
if (!locateActivityLog(content) && !allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING')) return failed(request, { code: 'LIFECYCLE_LOG_MISSING', message: 'task has no unique Activity Log section' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
398
|
+
if (spec.target === 'active') {
|
|
399
|
+
const contract = validateCurrentTaskContract(content);
|
|
400
|
+
if (!contract.ok) return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: contract.message }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
401
|
+
}
|
|
388
402
|
const registryRequest = executeShortIdCommand({
|
|
389
403
|
operation: 'list', activeDir: path.join(repoRoot, '.agents', 'workspace', 'active'),
|
|
390
404
|
shortIdLength: configuredShortIdLength(repoRoot)
|
|
@@ -408,6 +422,9 @@ function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLif
|
|
|
408
422
|
try { journal = parseJournal(io.readFileSync(initialJournalPath)); }
|
|
409
423
|
catch (error) { return failed(request, { code: 'LIFECYCLE_JOURNAL_INVALID', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath }); }
|
|
410
424
|
if (journal.taskId !== taskId || journal.intent !== request.intent) return failed(request, { code: 'LIFECYCLE_INTENT_CONFLICT', message: 'an in-progress lifecycle journal belongs to a different request' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
425
|
+
if (spec.target === 'active' && !isValidAgentInfraVersion(journal.metadata.agentInfraVersion)) {
|
|
426
|
+
return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: `journal metadata agentInfraVersion must be a valid v-prefixed semver (received ${journal.metadata.agentInfraVersion})` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
427
|
+
}
|
|
411
428
|
if (request.dryRun) {
|
|
412
429
|
if (!journal.failure) return failed(request, { code: 'LIFECYCLE_JOURNAL_INVALID', message: 'existing lifecycle journal has no durable failure evidence for a read-only probe' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
413
430
|
return failed(request, journal.failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath, completedSteps: journal.completedSteps, pendingSteps: STEPS.filter((step) => !journal.completedSteps.includes(step)) });
|
|
@@ -418,6 +435,9 @@ function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLif
|
|
|
418
435
|
let metadata: TaskWriteMetadata;
|
|
419
436
|
try { metadata = (options.metadataProvider ?? captureTaskWriteMetadata)(); }
|
|
420
437
|
catch (error) { return failed(request, { code: 'LIFECYCLE_METADATA_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath }); }
|
|
438
|
+
if (spec.target === 'active' && !isValidAgentInfraVersion(metadata.agentInfraVersion)) {
|
|
439
|
+
return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: `metadata agentInfraVersion must be a valid v-prefixed semver (received ${metadata.agentInfraVersion})` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
440
|
+
}
|
|
421
441
|
journal = { version: 1, taskId, intent: request.intent, intentDigest: digest, sourceState, targetState: spec.target, metadata, completedSteps: [] };
|
|
422
442
|
if (request.dryRun) {
|
|
423
443
|
const plannedMutations = mutationsFor(request, content, metadata, restoredFiles, allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'));
|
|
@@ -3,9 +3,11 @@ import path from 'node:path';
|
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
|
|
5
5
|
import { parseTypedTaskFrontmatter } from './frontmatter.ts';
|
|
6
|
-
import {
|
|
6
|
+
import { validateCurrentTaskContract } from './current-contract.ts';
|
|
7
|
+
import { parseReviewSummary, resolveCanonicalVerdict } from './review-artifacts.ts';
|
|
7
8
|
import { inspectArtifactDirectory } from './artifact-lifecycle.ts';
|
|
8
|
-
import {
|
|
9
|
+
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
|
|
10
|
+
import type { LedgerDocument } from './ledger.ts';
|
|
9
11
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
10
12
|
import {
|
|
11
13
|
activateDelegation,
|
|
@@ -119,6 +121,7 @@ type OrchestrationResult = Readonly<{
|
|
|
119
121
|
taskId: string | null;
|
|
120
122
|
run: OrchestrationRun | null;
|
|
121
123
|
next: OrchestrationNext | null;
|
|
124
|
+
warnings?: readonly Readonly<{ code: string; message: string; action: string }>[];
|
|
122
125
|
error: Readonly<{
|
|
123
126
|
code: string;
|
|
124
127
|
message: string;
|
|
@@ -181,7 +184,7 @@ function orchestrationPath(taskDir: string): string {
|
|
|
181
184
|
return path.join(taskDir, 'orchestration.json');
|
|
182
185
|
}
|
|
183
186
|
|
|
184
|
-
const ORCHESTRATION_STATE_INVALID_MESSAGE = 'orchestration.json does not match the current runtime structure;
|
|
187
|
+
const ORCHESTRATION_STATE_INVALID_MESSAGE = 'orchestration.json does not match the current runtime structure; the file was left unchanged; rebuild the sandbox or manually repair the state before retrying';
|
|
185
188
|
|
|
186
189
|
class OrchestrationStateError extends Error {
|
|
187
190
|
readonly code = 'ORCHESTRATION_STATE_INVALID';
|
|
@@ -529,14 +532,20 @@ function hasReviewAfterArtifact(taskDir: string, family: string, reviewFamily: s
|
|
|
529
532
|
&& review.reviewedInput?.name === artifact.latest.name;
|
|
530
533
|
}
|
|
531
534
|
|
|
532
|
-
function latestReviewApproved(taskDir: string, family: 'review-analysis' | 'review-plan' | 'review-code'): boolean {
|
|
535
|
+
function latestReviewApproved(taskDir: string, family: 'review-analysis' | 'review-plan' | 'review-code'): { approved: boolean; error: string | null } {
|
|
533
536
|
const round = highestRound(taskDir, family);
|
|
534
|
-
if (round === 0) return false;
|
|
535
|
-
|
|
536
|
-
|
|
537
|
+
if (round === 0) return { approved: false, error: null };
|
|
538
|
+
let content: string;
|
|
539
|
+
try { content = fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8'); }
|
|
540
|
+
catch (error) { return { approved: false, error: String(error) }; }
|
|
541
|
+
const parsed = parseReviewSummary(content);
|
|
542
|
+
if (!parsed.ok) return { approved: false, error: `${parsed.code}: ${parsed.message}` };
|
|
543
|
+
const verdict = resolveCanonicalVerdict(parsed.summary);
|
|
544
|
+
if (!verdict.ok) return { approved: false, error: `${verdict.code}: ${verdict.message}` };
|
|
545
|
+
return { approved: verdict.verdict === 'Approved', error: null };
|
|
537
546
|
}
|
|
538
547
|
|
|
539
|
-
function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedModel' | 'requestedReasoningEffort'> | null {
|
|
548
|
+
function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedModel' | 'requestedReasoningEffort'> | { error: string } | null {
|
|
540
549
|
const analysisRound = highestRound(taskDir, 'analysis');
|
|
541
550
|
const analysisReviewRound = highestRound(taskDir, 'review-analysis');
|
|
542
551
|
const planRound = highestRound(taskDir, 'plan');
|
|
@@ -551,7 +560,9 @@ function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedMode
|
|
|
551
560
|
const round = analysisReviewRound + 1;
|
|
552
561
|
return { action: 'review-analysis', role: 'reviewer', stage: 'review-analysis', round, artifact: artifactName('review-analysis', round) };
|
|
553
562
|
}
|
|
554
|
-
|
|
563
|
+
const analysisVerdict = latestReviewApproved(taskDir, 'review-analysis');
|
|
564
|
+
if (analysisVerdict.error) return { error: analysisVerdict.error };
|
|
565
|
+
if (!analysisVerdict.approved) {
|
|
555
566
|
const round = analysisRound + 1;
|
|
556
567
|
return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
|
|
557
568
|
}
|
|
@@ -563,7 +574,9 @@ function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedMode
|
|
|
563
574
|
const round = planReviewRound + 1;
|
|
564
575
|
return { action: 'review-plan', role: 'reviewer', stage: 'review-plan', round, artifact: artifactName('review-plan', round) };
|
|
565
576
|
}
|
|
566
|
-
|
|
577
|
+
const planVerdict = latestReviewApproved(taskDir, 'review-plan');
|
|
578
|
+
if (planVerdict.error) return { error: planVerdict.error };
|
|
579
|
+
if (!planVerdict.approved) {
|
|
567
580
|
const round = planRound + 1;
|
|
568
581
|
return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
|
|
569
582
|
}
|
|
@@ -575,7 +588,9 @@ function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedMode
|
|
|
575
588
|
const round = codeReviewRound + 1;
|
|
576
589
|
return { action: 'review-code', role: 'reviewer', stage: 'review-code', round, artifact: artifactName('review-code', round) };
|
|
577
590
|
}
|
|
578
|
-
|
|
591
|
+
const codeVerdict = latestReviewApproved(taskDir, 'review-code');
|
|
592
|
+
if (codeVerdict.error) return { error: codeVerdict.error };
|
|
593
|
+
if (!codeVerdict.approved) {
|
|
579
594
|
const round = codeRound + 1;
|
|
580
595
|
return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
|
|
581
596
|
}
|
|
@@ -598,12 +613,21 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
598
613
|
return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
|
|
599
614
|
}
|
|
600
615
|
let metadata: ReturnType<typeof parseTypedTaskFrontmatter>;
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
return failed('
|
|
616
|
+
let currentLedger: LedgerDocument | null = null;
|
|
617
|
+
if (resolved.state === 'active') {
|
|
618
|
+
const contract = validateCurrentTaskContract(content);
|
|
619
|
+
if (!contract.ok) return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', contract.message, resolved.taskId);
|
|
620
|
+
metadata = contract.metadata;
|
|
621
|
+
currentLedger = contract.ledger;
|
|
622
|
+
} else {
|
|
623
|
+
try {
|
|
624
|
+
metadata = parseTypedTaskFrontmatter(content);
|
|
625
|
+
} catch (error) {
|
|
626
|
+
return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
627
|
+
}
|
|
605
628
|
}
|
|
606
629
|
const routed = routeFromFacts(resolved.taskDir);
|
|
630
|
+
if (routed && 'error' in routed) return failed('ORCHESTRATION_REVIEW_INVALID', routed.error, resolved.taskId);
|
|
607
631
|
if (!routed) return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
|
|
608
632
|
const run = readRun(resolved.taskDir);
|
|
609
633
|
const policy = run ? rolePolicy(run, routed.role) : null;
|
|
@@ -621,7 +645,9 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
621
645
|
if (!review.ok || review.summary.manualValidation === null) {
|
|
622
646
|
return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
|
|
623
647
|
}
|
|
624
|
-
const
|
|
648
|
+
const ledger = currentLedger ?? parseLedgerDocument(content);
|
|
649
|
+
if (!ledger.present) return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
|
|
650
|
+
const rows = ledger.rows;
|
|
625
651
|
const ledgerError = validateLedgerRows(rows);
|
|
626
652
|
if (ledgerError) return failed('ORCHESTRATION_LEDGER_INVALID', ledgerError.message, resolved.taskId);
|
|
627
653
|
if (!summarizeLedgerStage(rows, 'code').canAdvance) {
|
|
@@ -18,6 +18,21 @@ type ReviewSummaryErrorCode =
|
|
|
18
18
|
| 'REVIEW_SUMMARY_NOT_FOUND'
|
|
19
19
|
| 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
|
|
20
20
|
| 'REVIEW_SUMMARY_COUNT_MISMATCH';
|
|
21
|
+
type VerdictSemanticErrorCode =
|
|
22
|
+
| 'REVIEW_VERDICT_FINDING_MISMATCH'
|
|
23
|
+
| 'REVIEW_FINDING_COUNTS_NOT_FINALIZED';
|
|
24
|
+
type CanonicalVerdictResult =
|
|
25
|
+
| { ok: true; verdict: ReviewVerdict }
|
|
26
|
+
| { ok: false; verdict: null; code: VerdictSemanticErrorCode; message: string };
|
|
27
|
+
type PathVerdictErrorCode =
|
|
28
|
+
| 'REVIEW_ARTIFACT_NOT_FOUND'
|
|
29
|
+
| 'REVIEW_ARTIFACT_READ_FAILED'
|
|
30
|
+
| 'REVIEW_SUMMARY_NOT_FOUND'
|
|
31
|
+
| 'REVIEW_SUMMARY_PLACEHOLDER_INVALID'
|
|
32
|
+
| VerdictSemanticErrorCode;
|
|
33
|
+
type PathVerdictResult =
|
|
34
|
+
| { ok: true; verdict: ReviewVerdict }
|
|
35
|
+
| { ok: false; verdict: null; code: PathVerdictErrorCode; message: string };
|
|
21
36
|
type ReviewSummaryParseResult =
|
|
22
37
|
| { ok: true; summary: ReviewSummary; findingsStart: number; findingsEnd: number }
|
|
23
38
|
| { ok: false; code: ReviewSummaryErrorCode; message: string };
|
|
@@ -203,28 +218,46 @@ function finalizeReviewSummaryContent(
|
|
|
203
218
|
};
|
|
204
219
|
}
|
|
205
220
|
|
|
206
|
-
function
|
|
207
|
-
|
|
208
|
-
|
|
221
|
+
function resolveCanonicalVerdict(summary: ReviewSummary): CanonicalVerdictResult {
|
|
222
|
+
if (summary.counts === null) {
|
|
223
|
+
return {
|
|
224
|
+
ok: false,
|
|
225
|
+
verdict: null,
|
|
226
|
+
code: 'REVIEW_FINDING_COUNTS_NOT_FINALIZED',
|
|
227
|
+
message: 'review summary finding counts are not finalized'
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (summary.verdict === 'Approved' && !equalCounts(summary.counts, { blocker: 0, major: 0, minor: 0 })) {
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
verdict: null,
|
|
234
|
+
code: 'REVIEW_VERDICT_FINDING_MISMATCH',
|
|
235
|
+
message: 'Approved verdict requires zero finalized findings'
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
return { ok: true, verdict: summary.verdict };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function parseVerdict(reviewPath: string): PathVerdictResult {
|
|
209
242
|
if (!fs.existsSync(reviewPath)) {
|
|
210
|
-
return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
|
|
243
|
+
return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_NOT_FOUND', message: `Review artifact not found: ${path.basename(reviewPath)}` };
|
|
244
|
+
}
|
|
245
|
+
let content: string;
|
|
246
|
+
try {
|
|
247
|
+
content = fs.readFileSync(reviewPath, 'utf8');
|
|
248
|
+
} catch (error) {
|
|
249
|
+
return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_READ_FAILED', message: `Review artifact could not be read: ${String(error)}` };
|
|
211
250
|
}
|
|
212
|
-
const parsed = parseReviewSummary(
|
|
251
|
+
const parsed = parseReviewSummary(content);
|
|
213
252
|
if (!parsed.ok) {
|
|
214
253
|
return {
|
|
215
254
|
ok: false,
|
|
216
255
|
verdict: null,
|
|
256
|
+
code: parsed.code === 'REVIEW_SUMMARY_PLACEHOLDER_INVALID' ? parsed.code : 'REVIEW_SUMMARY_NOT_FOUND',
|
|
217
257
|
message: `cannot parse review summary in ${path.basename(reviewPath)}: ${parsed.message}`
|
|
218
258
|
};
|
|
219
259
|
}
|
|
220
|
-
|
|
221
|
-
if (verdict !== 'Approved') return { ok: true, verdict };
|
|
222
|
-
return {
|
|
223
|
-
ok: true,
|
|
224
|
-
verdict: counts && equalCounts(counts, { blocker: 0, major: 0, minor: 0 })
|
|
225
|
-
? 'Approved'
|
|
226
|
-
: 'Approved-with-issues'
|
|
227
|
-
};
|
|
260
|
+
return resolveCanonicalVerdict(parsed.summary);
|
|
228
261
|
}
|
|
229
262
|
|
|
230
263
|
export {
|
|
@@ -236,13 +269,18 @@ export {
|
|
|
236
269
|
maxRound,
|
|
237
270
|
normalizeVerdict,
|
|
238
271
|
parseReviewSummary,
|
|
239
|
-
parseVerdict
|
|
272
|
+
parseVerdict,
|
|
273
|
+
resolveCanonicalVerdict
|
|
240
274
|
};
|
|
241
275
|
export type {
|
|
276
|
+
CanonicalVerdictResult,
|
|
277
|
+
PathVerdictErrorCode,
|
|
278
|
+
PathVerdictResult,
|
|
242
279
|
ReviewFindingCounts,
|
|
243
280
|
ReviewSummary,
|
|
244
281
|
ReviewSummaryErrorCode,
|
|
245
282
|
ReviewSummaryFinalizeResult,
|
|
246
283
|
ReviewSummaryParseResult,
|
|
247
|
-
ReviewVerdict
|
|
284
|
+
ReviewVerdict,
|
|
285
|
+
VerdictSemanticErrorCode
|
|
248
286
|
};
|