@fitlab-ai/agent-infra 0.9.10 → 0.9.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -9
- package/README.zh-CN.md +19 -9
- 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 +71 -10
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
- package/dist/lib/agent-clients/adapters/traecli.js +7 -8
- 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 +5 -15
- package/dist/lib/decide.js +14 -30
- package/dist/lib/defaults.json +8 -0
- package/dist/lib/git/workflow.js +43 -1
- package/dist/lib/init.js +3 -2
- package/dist/lib/internal/git-workflow.js +8 -1
- package/dist/lib/internal/platform-pr.js +8 -2
- package/dist/lib/internal/release-workflow.js +0 -1
- package/dist/lib/internal/task-delivery.js +59 -0
- 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/internal/task-validate.js +2 -1
- package/dist/lib/merge.js +205 -103
- package/dist/lib/platform/github-client.js +7 -1
- package/dist/lib/platform/pr-checks.js +8 -5
- package/dist/lib/platform/pr-summary.js +12 -8
- package/dist/lib/platform/pull-requests.js +393 -91
- package/dist/lib/platform/verification-required.js +13 -2
- package/dist/lib/platform/verification-sync.js +9 -4
- package/dist/lib/pr-review/evidence-grading.js +12 -4
- package/dist/lib/process-data/normalize.js +2 -9
- package/dist/lib/run/host.js +46 -16
- 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 +8 -12
- package/dist/lib/sandbox/control/executor.js +131 -39
- package/dist/lib/sandbox/control/lifecycle.js +55 -24
- 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/process-state.js +2 -2
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +6 -4
- package/dist/lib/task/codex-orchestration.js +1 -1
- 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-intent.js +100 -0
- package/dist/lib/task/commit-operation.js +181 -91
- package/dist/lib/task/commit-policy.js +1 -1
- package/dist/lib/task/control-authority.js +431 -0
- package/dist/lib/task/create.js +39 -8
- package/dist/lib/task/current-contract.js +35 -0
- package/dist/lib/task/decision-details.js +218 -0
- package/dist/lib/task/decision-intents.js +5 -2
- package/dist/lib/task/delivery-target.js +97 -0
- package/dist/lib/task/delivery.js +153 -0
- package/dist/lib/task/events.js +11 -5
- package/dist/lib/task/human-override.js +5 -2
- package/dist/lib/task/index.js +19 -20
- package/dist/lib/task/ledger-intents.js +8 -9
- package/dist/lib/task/ledger.js +13 -6
- package/dist/lib/task/lifecycle.js +122 -9
- package/dist/lib/task/orchestration.js +105 -75
- package/dist/lib/task/pr-delivery-fact.js +182 -0
- package/dist/lib/task/review-artifacts.js +31 -12
- package/dist/lib/task/review-finalization.js +13 -4
- package/dist/lib/task/review-fingerprint.js +8 -0
- package/dist/lib/task/verification-engine.js +86 -39
- package/dist/lib/task/verification-review.js +2 -2
- package/dist/lib/task/workspace-snapshot.js +5 -11
- package/dist/lib/task/write.js +11 -1
- package/dist/lib/update.js +13 -35
- 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 +105 -10
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
- package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
- package/lib/agent-clients/adapters/traecli.ts +7 -8
- 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 +5 -18
- package/lib/decide.ts +12 -22
- package/lib/defaults.json +8 -0
- package/lib/git/workflow.ts +38 -1
- package/lib/init.ts +5 -2
- package/lib/internal/git-workflow.ts +7 -1
- package/lib/internal/platform-pr.ts +8 -1
- package/lib/internal/release-workflow.ts +0 -1
- package/lib/internal/task-delivery.ts +53 -0
- 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/internal/task-validate.ts +3 -2
- package/lib/merge.ts +236 -120
- package/lib/platform/github-client.ts +8 -1
- package/lib/platform/pr-checks.ts +7 -5
- package/lib/platform/pr-summary.ts +10 -7
- package/lib/platform/pull-requests.ts +425 -87
- package/lib/platform/verification-required.ts +12 -2
- package/lib/platform/verification-sync.ts +9 -4
- package/lib/pr-review/evidence-grading.ts +7 -4
- package/lib/process-data/normalize.ts +2 -8
- package/lib/run/host.ts +63 -14
- 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 +9 -14
- package/lib/sandbox/control/container-identity.ts +2 -2
- package/lib/sandbox/control/executor.ts +148 -51
- package/lib/sandbox/control/lifecycle.ts +59 -31
- 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/process-state.ts +2 -2
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +6 -1
- package/lib/task/artifact-lifecycle.ts +7 -5
- package/lib/task/codex-orchestration.ts +2 -1
- 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-intent.ts +137 -0
- package/lib/task/commit-operation.ts +161 -79
- package/lib/task/commit-policy.ts +1 -2
- package/lib/task/control-authority.ts +563 -0
- package/lib/task/create.ts +40 -10
- package/lib/task/current-contract.ts +49 -0
- package/lib/task/decision-details.ts +280 -0
- package/lib/task/decision-intents.ts +7 -2
- package/lib/task/delivery-target.ts +130 -0
- package/lib/task/delivery.ts +160 -0
- package/lib/task/events.ts +10 -5
- package/lib/task/human-override.ts +5 -2
- package/lib/task/index.ts +19 -20
- package/lib/task/ledger-intents.ts +6 -8
- package/lib/task/ledger.ts +17 -7
- package/lib/task/lifecycle.ts +113 -4
- package/lib/task/orchestration.ts +102 -80
- package/lib/task/pr-delivery-fact.ts +254 -0
- package/lib/task/review-artifacts.ts +53 -15
- package/lib/task/review-finalization.ts +21 -5
- package/lib/task/review-fingerprint.ts +10 -0
- package/lib/task/verification-engine.ts +78 -46
- package/lib/task/verification-review.ts +3 -1
- package/lib/task/workspace-snapshot.ts +4 -13
- package/lib/task/write.ts +20 -1
- package/lib/update.ts +14 -38
- package/lib/version.ts +6 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +5 -3
- package/templates/.agents/README.zh-CN.md +5 -3
- package/templates/.agents/rules/README.en.md +4 -2
- package/templates/.agents/rules/README.zh-CN.md +4 -2
- package/templates/.agents/rules/compatibility-policy.en.md +5 -4
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
- package/templates/.agents/rules/create-issue.en.md +11 -2
- package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
- 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 → issue-fields.en.md} +1 -1
- package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +26 -5
- package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
- package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
- package/templates/.agents/rules/milestone-inference.en.md +12 -2
- package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
- 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 +23 -14
- package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
- package/templates/.agents/rules/release-commands.en.md +42 -3
- package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
- 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/security-alerts.en.md +17 -2
- package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
- package/templates/.agents/rules/sync-content-generation.en.md +8 -0
- package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
- package/templates/.agents/rules/task-management.en.md +2 -3
- package/templates/.agents/rules/task-management.zh-CN.md +2 -3
- package/templates/.agents/scripts/security-alerts.github.sh +151 -0
- package/templates/.agents/scripts/security-alerts.sh +31 -0
- 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 +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/commit/SKILL.en.md +30 -10
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
- package/templates/.agents/skills/commit/config/verify.json +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +13 -13
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
- 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 +2 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/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/init-labels/SKILL.en.md +9 -10
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
- 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 +8 -5
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
- 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-analysis/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
- 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 +2 -1
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
- 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/review-plan/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
- 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 +144 -109
- package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
- package/templates/.agents/templates/task.en.md +6 -2
- package/templates/.agents/templates/task.zh-CN.md +6 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/feature-development.en.yaml +9 -8
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/refactoring.en.yaml +9 -8
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
- 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/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
- package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
- package/dist/lib/builtin-tuis.js +0 -40
- package/lib/builtin-tuis.ts +0 -56
- package/templates/.agents/rules/create-issue.github.en.md +0 -14
- package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
- package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
- package/templates/.agents/rules/issue-sync.github.en.md +0 -40
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
- package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
- package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
- package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
- package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
- package/templates/.agents/rules/pr-sync.github.en.md +0 -43
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
- package/templates/.agents/rules/release-commands.github.en.md +0 -38
- package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
- package/templates/.agents/rules/security-alerts.github.en.md +0 -43
- package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { parseTaskFrontmatter } from './frontmatter.ts';
|
|
6
|
+
import { resolveTaskRef } from './resolve-ref.ts';
|
|
7
|
+
import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead, validateBaseRef, validateRemote } from './delivery-target.ts';
|
|
8
|
+
import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead, findAuthoritativeReviewCodeArtifact } from './review-fingerprint.ts';
|
|
9
|
+
import { captureTaskWriteMetadata, writeTask } from './write.ts';
|
|
10
|
+
import { withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
11
|
+
|
|
12
|
+
export type DeliveryState = 'absent' | 'same' | 'known-old' | 'unknown-drift';
|
|
13
|
+
|
|
14
|
+
export type DeliveryClassification = Readonly<{
|
|
15
|
+
state: DeliveryState;
|
|
16
|
+
shouldPush: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
|
|
19
|
+
export type TaskBranchDeliveryResult = Readonly<{
|
|
20
|
+
status: 'applied' | 'no-op' | 'failed' | 'blocked' | 'planned';
|
|
21
|
+
changed: boolean;
|
|
22
|
+
taskId: string | null;
|
|
23
|
+
remoteHead: string | null;
|
|
24
|
+
localHead: string | null;
|
|
25
|
+
state: DeliveryState | null;
|
|
26
|
+
error: { code: string; message: string; retryable: boolean } | null;
|
|
27
|
+
}>;
|
|
28
|
+
|
|
29
|
+
function classifyDeliveryState(localHead: string, remoteHead: string | null, lastDelivered: string | null): DeliveryClassification {
|
|
30
|
+
if (remoteHead === null) return { state: 'absent', shouldPush: true };
|
|
31
|
+
if (remoteHead === localHead) return { state: 'same', shouldPush: false };
|
|
32
|
+
if (lastDelivered && remoteHead === lastDelivered) return { state: 'known-old', shouldPush: true };
|
|
33
|
+
return { state: 'unknown-drift', shouldPush: false };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function git(cwd: string, args: readonly string[]): { status: number | null; stdout: string; stderr: string } {
|
|
37
|
+
const result = spawnSync('git', [...args], { cwd, encoding: 'utf8' });
|
|
38
|
+
return { status: result.status, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function gitValue(cwd: string, args: readonly string[]): string | null {
|
|
42
|
+
const result = git(cwd, args);
|
|
43
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function validateBranch(cwd: string, branch: string): boolean {
|
|
47
|
+
return git(cwd, ['check-ref-format', `refs/heads/${branch}`]).status === 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function validateReviewTargetCompatibility(
|
|
51
|
+
repoRoot: string,
|
|
52
|
+
taskDir: string,
|
|
53
|
+
target: { remote: string; baseRef: string },
|
|
54
|
+
reviewedHead: string
|
|
55
|
+
): { ok: true } | { ok: false; code: string; message: string } {
|
|
56
|
+
const artifact = findAuthoritativeReviewCodeArtifact(taskDir);
|
|
57
|
+
if (!artifact.ok || !artifact.path) return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: 'A current review-code artifact is required before delivery' };
|
|
58
|
+
let content: string;
|
|
59
|
+
try { content = fs.readFileSync(artifact.path, 'utf8'); }
|
|
60
|
+
catch (error) { return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: error instanceof Error ? error.message : String(error) }; }
|
|
61
|
+
const savedReviewedHead = extractReviewedHead(content) || extractReviewBaseline(content);
|
|
62
|
+
const savedTargetHead = extractReviewTargetHead(content);
|
|
63
|
+
const savedDiffBase = extractReviewDiffBase(content);
|
|
64
|
+
if (!savedReviewedHead || !savedTargetHead || !savedDiffBase) {
|
|
65
|
+
return { ok: false, code: 'DELIVERY_REVIEW_TARGET_INVALID', message: 'review-code must save reviewed head, target head, and diff base before delivery' };
|
|
66
|
+
}
|
|
67
|
+
if (savedReviewedHead !== reviewedHead) {
|
|
68
|
+
return { ok: false, code: 'DELIVERY_REVIEW_REQUIRED', message: 'review-code reviewed head does not match last_reviewed_commit' };
|
|
69
|
+
}
|
|
70
|
+
const saved = resolveDiffBase(repoRoot, reviewedHead, savedTargetHead);
|
|
71
|
+
if (!saved.ok || saved.diffBase !== savedDiffBase) {
|
|
72
|
+
return { ok: false, code: 'DELIVERY_REVIEW_TARGET_INVALID', message: saved.ok ? 'saved review diff base does not match saved target head' : saved.message };
|
|
73
|
+
}
|
|
74
|
+
const currentTarget = resolveTargetHead(repoRoot, target);
|
|
75
|
+
if (!currentTarget.ok) return { ok: false, code: currentTarget.code, message: currentTarget.message };
|
|
76
|
+
const current = resolveDiffBase(repoRoot, reviewedHead, currentTarget.head!);
|
|
77
|
+
if (!current.ok) return { ok: false, code: current.code, message: current.message };
|
|
78
|
+
if (current.diffBase !== savedDiffBase) {
|
|
79
|
+
return { ok: false, code: 'DELIVERY_REVIEW_TARGET_CHANGED', message: 'current delivery target changed the reviewed diff base; re-run review-code' };
|
|
80
|
+
}
|
|
81
|
+
return { ok: true };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function failure(taskId: string | null, code: string, message: string, retryable = false, status: 'failed' | 'blocked' = retryable ? 'blocked' : 'failed'): TaskBranchDeliveryResult {
|
|
85
|
+
return { status, changed: false, taskId, remoteHead: null, localHead: null, state: null, error: { code, message, retryable } };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function deliverUnlocked(
|
|
89
|
+
taskId: string,
|
|
90
|
+
taskDir: string,
|
|
91
|
+
repoRoot: string,
|
|
92
|
+
options: { agent: string; remote?: string; baseRef?: string; dryRun?: boolean }
|
|
93
|
+
): TaskBranchDeliveryResult {
|
|
94
|
+
let frontmatter: ReturnType<typeof parseTaskFrontmatter>;
|
|
95
|
+
try { frontmatter = parseTaskFrontmatter(fs.readFileSync(path.join(taskDir, 'task.md'), 'utf8')); }
|
|
96
|
+
catch (error) { return failure(taskId, 'TASK_CONTEXT_UNREADABLE', error instanceof Error ? error.message : String(error)); }
|
|
97
|
+
if (frontmatter.status !== 'active') return failure(taskId, 'TASK_STATE_INVALID', 'task branch delivery requires an active task');
|
|
98
|
+
const branch = frontmatter.branch ?? '';
|
|
99
|
+
if (!branch) return failure(taskId, 'DELIVERY_BRANCH_MISSING', 'task branch is missing');
|
|
100
|
+
if (!validateBranch(repoRoot, branch)) return failure(taskId, 'DELIVERY_BRANCH_INVALID', 'task branch is invalid');
|
|
101
|
+
if (!frontmatter.delivery_remote || !frontmatter.delivery_base_ref) {
|
|
102
|
+
return failure(taskId, 'DELIVERY_TARGET_MISSING', 'task delivery target is not bound; recreate or repair task metadata before delivery');
|
|
103
|
+
}
|
|
104
|
+
const target = resolveDeliveryTarget(repoRoot, {
|
|
105
|
+
remote: frontmatter.delivery_remote,
|
|
106
|
+
baseRef: frontmatter.delivery_base_ref
|
|
107
|
+
}, {
|
|
108
|
+
...(options.remote === undefined ? {} : { remote: options.remote }),
|
|
109
|
+
...(options.baseRef === undefined ? {} : { baseRef: options.baseRef })
|
|
110
|
+
});
|
|
111
|
+
if (!target.ok) return failure(taskId, target.code, target.message);
|
|
112
|
+
if (!validateRemote(target.value.remote) || !validateBaseRef(target.value.baseRef)) return failure(taskId, 'DELIVERY_TARGET_INVALID', 'task delivery target is invalid');
|
|
113
|
+
const localHead = gitValue(repoRoot, ['rev-parse', 'HEAD']);
|
|
114
|
+
if (!localHead) return failure(taskId, 'DELIVERY_LOCAL_HEAD_UNAVAILABLE', 'Unable to resolve local HEAD');
|
|
115
|
+
const status = git(repoRoot, ['status', '--porcelain=v1']);
|
|
116
|
+
if (status.status !== 0) return failure(taskId, 'DELIVERY_GIT_INSPECT_FAILED', status.stderr.trim() || 'Unable to inspect Git state');
|
|
117
|
+
if (status.stdout.trim()) return failure(taskId, 'DELIVERY_WORKTREE_DIRTY', 'Working tree must be clean before branch delivery');
|
|
118
|
+
const reviewed = frontmatter.last_reviewed_commit ?? '';
|
|
119
|
+
if (reviewed !== localHead) return failure(taskId, 'DELIVERY_REVIEW_REQUIRED', 'Local HEAD must equal last_reviewed_commit before delivery');
|
|
120
|
+
const reviewTarget = validateReviewTargetCompatibility(repoRoot, taskDir, target.value, reviewed);
|
|
121
|
+
if (!reviewTarget.ok) return failure(taskId, reviewTarget.code, reviewTarget.message, reviewTarget.code.includes('UNAVAILABLE'), reviewTarget.code.includes('UNAVAILABLE') ? 'blocked' : 'failed');
|
|
122
|
+
const ref = `refs/heads/${branch}`;
|
|
123
|
+
const remoteOutput = git(repoRoot, ['ls-remote', '--refs', target.value.remote, ref]);
|
|
124
|
+
if (remoteOutput.status !== 0) return failure(taskId, 'DELIVERY_REMOTE_UNAVAILABLE', remoteOutput.stderr.trim() || 'Unable to inspect the delivery remote', true);
|
|
125
|
+
const remoteHead = remoteOutput.stdout.trim().split(/\s+/)[0] || null;
|
|
126
|
+
const classification = classifyDeliveryState(localHead, remoteHead, frontmatter.delivery_remote_head ?? null);
|
|
127
|
+
if (classification.state === 'unknown-drift') return failure(taskId, 'DELIVERY_REMOTE_DRIFT', `Remote branch ${branch} has an unexpected head; refusing to overwrite it`);
|
|
128
|
+
if (options.dryRun) return { status: 'planned', changed: classification.shouldPush, taskId, remoteHead, localHead, state: classification.state, error: null };
|
|
129
|
+
if (classification.shouldPush) {
|
|
130
|
+
const args = classification.state === 'known-old'
|
|
131
|
+
? ['push', `--force-with-lease=${ref}:${frontmatter.delivery_remote_head}`, target.value.remote, `HEAD:${ref}`]
|
|
132
|
+
: ['push', `--force-with-lease=${ref}:`, target.value.remote, `HEAD:${ref}`];
|
|
133
|
+
const pushed = git(repoRoot, args);
|
|
134
|
+
if (pushed.status !== 0) return failure(taskId, 'DELIVERY_PUSH_FAILED', pushed.stderr.trim() || 'Task branch delivery was rejected', true, 'blocked');
|
|
135
|
+
}
|
|
136
|
+
const verified = gitValue(repoRoot, ['ls-remote', '--refs', target.value.remote, ref])?.split(/\s+/)[0] ?? null;
|
|
137
|
+
if (verified !== localHead) return failure(taskId, 'DELIVERY_REMOTE_VERIFY_FAILED', `Expected remote head ${localHead}, received ${verified ?? 'unavailable'}`, true, 'blocked');
|
|
138
|
+
const written = writeTask({
|
|
139
|
+
taskRef: taskId,
|
|
140
|
+
expectedState: 'active',
|
|
141
|
+
mutations: [{ kind: 'frontmatter', set: { delivery_remote: target.value.remote, delivery_base_ref: target.value.baseRef, delivery_remote_head: localHead, assigned_to: options.agent } }]
|
|
142
|
+
}, { repoRoot, metadataProvider: captureTaskWriteMetadata });
|
|
143
|
+
if (written.status === 'failed') return failure(taskId, 'DELIVERY_TASK_SYNC_FAILED', written.error.message, true, 'blocked');
|
|
144
|
+
return { status: classification.shouldPush ? 'applied' : 'no-op', changed: classification.shouldPush || written.changed, taskId, remoteHead: localHead, localHead, state: classification.state, error: null };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function deliverTaskBranch(taskRef: string, options: { repoRoot?: string; agent: string; remote?: string; baseRef?: string; dryRun?: boolean }): TaskBranchDeliveryResult {
|
|
148
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
149
|
+
if (!resolved.ok) return failure(resolved.taskId, resolved.code, resolved.message);
|
|
150
|
+
try {
|
|
151
|
+
return withRepositoryMutationLock(resolved.repoRoot, () => withTaskExecutionLock(
|
|
152
|
+
resolved.repoRoot, resolved.taskId, 'task-delivery',
|
|
153
|
+
() => deliverUnlocked(resolved.taskId, resolved.taskDir, resolved.repoRoot, options)
|
|
154
|
+
));
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return failure(resolved.taskId, 'DELIVERY_LOCK_FAILED', error instanceof Error ? error.message : String(error), true, 'blocked');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export { classifyDeliveryState, deliverTaskBranch };
|
package/lib/task/events.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
|
|
3
|
-
import { appendActivityEntry, locateActivityLog, pairEntries } from './activity-log.ts';
|
|
3
|
+
import { appendActivityEntry, locateActivityLog, pairEntries, startedBackedRows } from './activity-log.ts';
|
|
4
4
|
import {
|
|
5
5
|
buildArtifactLinkSection,
|
|
6
6
|
artifactName,
|
|
@@ -21,9 +21,9 @@ import {
|
|
|
21
21
|
parseImplementationInputs,
|
|
22
22
|
renderImplementationInputs
|
|
23
23
|
} from './implementation-inputs.ts';
|
|
24
|
-
import {
|
|
24
|
+
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
|
|
25
25
|
import type { ReviewStage } from './ledger.ts';
|
|
26
|
-
import { parseReviewSummary } from './review-artifacts.ts';
|
|
26
|
+
import { parseReviewSummary, resolveCanonicalVerdict } from './review-artifacts.ts';
|
|
27
27
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
28
28
|
import { findSectionHeading } from './sections.ts';
|
|
29
29
|
import { validateLifecycleExecution } from './lifecycle-execution.ts';
|
|
@@ -50,6 +50,7 @@ type TaskEventErrorCode =
|
|
|
50
50
|
| 'EVENT_UNKNOWN' | 'EVENT_PAYLOAD_INVALID' | 'EVENT_TRANSITION_INVALID'
|
|
51
51
|
| 'EVENT_LOG_MISSING' | 'EVENT_START_MISSING' | 'EVENT_ALREADY_COMPLETED'
|
|
52
52
|
| 'EVENT_LOG_CONFLICT' | 'EVENT_ARTIFACT_CONFLICT' | 'EVENT_FINDING_COUNT_MISMATCH'
|
|
53
|
+
| 'EVENT_VERDICT_INVALID'
|
|
53
54
|
| 'EVENT_ORCHESTRATION_COMMIT_FAILED'
|
|
54
55
|
| ArtifactErrorCode | TaskWriteErrorCode;
|
|
55
56
|
type TaskEventRequest = {
|
|
@@ -151,7 +152,9 @@ function validateReviewFindingCounts(
|
|
|
151
152
|
): TaskEventError | null {
|
|
152
153
|
const stage = REVIEW_LEDGER_STAGES[family];
|
|
153
154
|
if (!stage || !artifactPath) return null;
|
|
154
|
-
const
|
|
155
|
+
const ledger = parseLedgerDocument(content);
|
|
156
|
+
if (!ledger.present) return { code: 'EVENT_FINDING_COUNT_MISMATCH', message: `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}` };
|
|
157
|
+
const rows = ledger.rows;
|
|
155
158
|
const invalid = validateLedgerRows(rows);
|
|
156
159
|
if (invalid) return { code: 'TASK_DOCUMENT_INVALID', message: `${invalid.code}: ${invalid.message}` };
|
|
157
160
|
const expected = summarizeLedgerStage(rows, stage).unresolvedFindingCounts;
|
|
@@ -173,6 +176,8 @@ function validateReviewFindingCounts(
|
|
|
173
176
|
message: parsed.ok ? 'review summary finding counts are not finalized' : parsed.message
|
|
174
177
|
};
|
|
175
178
|
}
|
|
179
|
+
const canonical = resolveCanonicalVerdict(parsed.summary);
|
|
180
|
+
if (!canonical.ok) return { code: 'EVENT_VERDICT_INVALID', message: `${canonical.code}: ${canonical.message}` };
|
|
176
181
|
const report = parsed.summary.counts;
|
|
177
182
|
const reportVerdict = parsed.summary.verdict === 'Approved'
|
|
178
183
|
? 'approved'
|
|
@@ -370,7 +375,7 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
370
375
|
catch (error) { return failed(request, { code: 'TASK_DOCUMENT_INVALID', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath }); }
|
|
371
376
|
const section = locateActivityLog(content);
|
|
372
377
|
if (!section) return failed(request, { code: 'EVENT_LOG_MISSING', message: 'task has no unique Activity Log section' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
373
|
-
const rows = pairEntries(section.entries);
|
|
378
|
+
const rows = startedBackedRows(pairEntries(section.entries));
|
|
374
379
|
let normalized = request;
|
|
375
380
|
let artifactContext: ArtifactContextResult | null = null;
|
|
376
381
|
const initialParts = eventParts(request.event);
|
|
@@ -24,6 +24,7 @@ import { verifyTaskEvent } from './verification.ts';
|
|
|
24
24
|
import { verifyInProcess } from './verification-engine.ts';
|
|
25
25
|
import { canonicalTimestamp, writeTask } from './write.ts';
|
|
26
26
|
import type { TaskFileSystem, TaskWriteMetadata, TaskWriteResult } from './write.ts';
|
|
27
|
+
import { readPrDeliveryFact } from './pr-delivery-fact.ts';
|
|
27
28
|
|
|
28
29
|
type Eligibility = 'eligible' | 'repair-only' | 'runtime-recovery-only' | 'never-overridable';
|
|
29
30
|
type OutcomeEffect = 'apply-target' | 'retry-same-intent' | 'record-only' | 'no-write';
|
|
@@ -614,7 +615,8 @@ function resourceForSnapshot(
|
|
|
614
615
|
return number === null ? null : { kind: 'issue', number };
|
|
615
616
|
}
|
|
616
617
|
if (policy.producerId === 'platform.pull-request') {
|
|
617
|
-
const
|
|
618
|
+
const fact = frontmatter ? readPrDeliveryFact(frontmatter) : null;
|
|
619
|
+
const number = options.probePullRequestNumber ?? (fact?.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null);
|
|
618
620
|
return number === null ? null : { kind: 'pull-request', number };
|
|
619
621
|
}
|
|
620
622
|
return null;
|
|
@@ -705,7 +707,8 @@ function probePlatformFailure(
|
|
|
705
707
|
return error('OVERRIDE_DOCUMENT_INVALID', cause instanceof Error ? cause.message : String(cause));
|
|
706
708
|
}
|
|
707
709
|
const issueNumber = options.probeIssueNumber ?? toPositiveInteger(frontmatter.issue_number);
|
|
708
|
-
const
|
|
710
|
+
const fact = readPrDeliveryFact(frontmatter);
|
|
711
|
+
const pullRequestNumber = options.probePullRequestNumber ?? (fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null);
|
|
709
712
|
const operation = policy.producerId === 'platform.issue'
|
|
710
713
|
? issueNumber === null
|
|
711
714
|
? null
|
package/lib/task/index.ts
CHANGED
|
@@ -1,34 +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>]
|
|
11
|
+
show [--task <ref> | -t <ref>] Print a task.md
|
|
12
|
+
status [--task <ref> | -t <ref>] Aggregated status view (metadata / artifacts / git / platform)
|
|
13
13
|
|
|
14
14
|
Examples:
|
|
15
|
-
ai task cat 11 analysis
|
|
16
15
|
ai task cat analysis
|
|
17
|
-
ai task cat 11
|
|
18
|
-
ai task decisions 11
|
|
16
|
+
ai task cat --task 11 analysis
|
|
17
|
+
ai task decisions --task 11
|
|
19
18
|
ai task decisions --item 1
|
|
20
|
-
ai task d 11 PL-3 --format markdown
|
|
21
|
-
ai task files 11
|
|
19
|
+
ai task d --task 11 --item PL-3 --format markdown
|
|
20
|
+
ai task files --task 11
|
|
22
21
|
ai task grep resolveArtifact
|
|
23
22
|
ai task grep resolveArtifact --current
|
|
24
|
-
ai task grep resolveArtifact 11
|
|
25
|
-
ai task issue-body 11
|
|
26
|
-
ai task issue-body 11 --template .github/ISSUE_TEMPLATE/05_other.yml
|
|
27
|
-
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
|
|
28
27
|
ai task ls
|
|
29
|
-
ai task show 11
|
|
30
|
-
ai task show TASK-20260612-162737
|
|
31
|
-
ai task status 11
|
|
28
|
+
ai task show --task 11
|
|
29
|
+
ai task show --task TASK-20260612-162737
|
|
30
|
+
ai task status --task 11
|
|
32
31
|
|
|
33
32
|
Run 'ai task <command> --help' for details.`;
|
|
34
33
|
|
|
@@ -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,12 +12,15 @@ 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'
|
|
18
20
|
] as const;
|
|
19
21
|
const lifecycleFailureCatalog = [
|
|
20
22
|
'LIFECYCLE_DIRECTORY_RENAME_FAILED',
|
|
23
|
+
'LIFECYCLE_DIRECTORY_COPY_FAILED',
|
|
21
24
|
'LIFECYCLE_DOCUMENT_INVALID',
|
|
22
25
|
'LIFECYCLE_FINAL_STATE_INVALID',
|
|
23
26
|
'LIFECYCLE_IDENTITY_CONFLICT',
|
|
@@ -126,6 +129,14 @@ function failed(request: Pick<TaskLifecycleRequest, 'taskRef' | 'intent'>, error
|
|
|
126
129
|
};
|
|
127
130
|
}
|
|
128
131
|
|
|
132
|
+
export function taskLifecycleFailure(
|
|
133
|
+
request: Pick<TaskLifecycleRequest, 'taskRef' | 'intent'>,
|
|
134
|
+
error: LifecycleError,
|
|
135
|
+
taskId: string | null = null
|
|
136
|
+
): TaskLifecycleResult {
|
|
137
|
+
return failed(request, error, { taskId });
|
|
138
|
+
}
|
|
139
|
+
|
|
129
140
|
function normalizedRequest(request: TaskLifecycleRequest): TaskLifecycleRequest | LifecycleError {
|
|
130
141
|
if (!request || typeof request !== 'object' || !lifecycleIntentCatalog.includes(request.intent as TaskLifecycleIntent)) {
|
|
131
142
|
return { code: 'LIFECYCLE_PAYLOAD_INVALID', message: 'intent is not in the lifecycle catalog' };
|
|
@@ -326,6 +337,62 @@ function parseJournal(content: string): LifecycleJournal {
|
|
|
326
337
|
return value as LifecycleJournal;
|
|
327
338
|
}
|
|
328
339
|
|
|
340
|
+
type DirectoryEntry = Readonly<{ relativePath: string; kind: 'file' | 'directory'; size: number; digest: string }>;
|
|
341
|
+
|
|
342
|
+
function directoryManifest(root: string): DirectoryEntry[] {
|
|
343
|
+
const entries: DirectoryEntry[] = [];
|
|
344
|
+
const visit = (current: string, relative = '') => {
|
|
345
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
|
|
346
|
+
const relativePath = relative ? path.join(relative, entry.name) : entry.name;
|
|
347
|
+
const absolute = path.join(current, entry.name);
|
|
348
|
+
const stat = fs.lstatSync(absolute);
|
|
349
|
+
if (stat.isSymbolicLink()) throw new Error(`symbolic link is not allowed in lifecycle directory: ${relativePath}`);
|
|
350
|
+
if (entry.isDirectory()) {
|
|
351
|
+
entries.push({ relativePath, kind: 'directory', size: 0, digest: '' });
|
|
352
|
+
visit(absolute, relativePath);
|
|
353
|
+
} else if (entry.isFile()) {
|
|
354
|
+
const digest = createHash('sha256').update(fs.readFileSync(absolute)).digest('hex');
|
|
355
|
+
entries.push({ relativePath, kind: 'file', size: stat.size, digest });
|
|
356
|
+
} else {
|
|
357
|
+
throw new Error(`unsupported lifecycle directory entry: ${relativePath}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
visit(root);
|
|
362
|
+
return entries;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function copyDirectoryVerified(source: string, target: string): void {
|
|
366
|
+
if (fs.existsSync(target)) throw new Error(`target directory already exists: ${target}`);
|
|
367
|
+
const copy = (currentSource: string, currentTarget: string) => {
|
|
368
|
+
fs.mkdirSync(currentTarget);
|
|
369
|
+
for (const entry of fs.readdirSync(currentSource, { withFileTypes: true })) {
|
|
370
|
+
const sourcePath = path.join(currentSource, entry.name);
|
|
371
|
+
const targetPath = path.join(currentTarget, entry.name);
|
|
372
|
+
const stat = fs.lstatSync(sourcePath);
|
|
373
|
+
if (stat.isSymbolicLink()) throw new Error(`symbolic link is not allowed in lifecycle directory: ${entry.name}`);
|
|
374
|
+
if (entry.isDirectory()) copy(sourcePath, targetPath);
|
|
375
|
+
else if (entry.isFile()) fs.copyFileSync(sourcePath, targetPath);
|
|
376
|
+
else throw new Error(`unsupported lifecycle directory entry: ${entry.name}`);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
copy(source, target);
|
|
380
|
+
const sourceManifest = directoryManifest(source);
|
|
381
|
+
const targetManifest = directoryManifest(target);
|
|
382
|
+
if (JSON.stringify(sourceManifest) !== JSON.stringify(targetManifest)) {
|
|
383
|
+
throw new Error('copied lifecycle directory failed manifest verification');
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function removeCopiedSource(source: string, target: string): void {
|
|
388
|
+
const sourceManifest = directoryManifest(source);
|
|
389
|
+
const targetManifest = directoryManifest(target);
|
|
390
|
+
if (JSON.stringify(sourceManifest) !== JSON.stringify(targetManifest)) {
|
|
391
|
+
throw new Error('lifecycle source and target diverged before source cleanup');
|
|
392
|
+
}
|
|
393
|
+
fs.rmSync(source, { recursive: true, force: false });
|
|
394
|
+
}
|
|
395
|
+
|
|
329
396
|
function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLifecycleOptions = {}): TaskLifecycleResult {
|
|
330
397
|
const normalized = normalizedRequest(requestInput);
|
|
331
398
|
if ('code' in normalized) return failed(requestInput, normalized);
|
|
@@ -376,7 +443,7 @@ function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLif
|
|
|
376
443
|
if (!spec.sources.includes(sourceState) && !existingJournal && !allowsManualOverride(options, 'LIFECYCLE_SOURCE_INVALID')) {
|
|
377
444
|
return failed(request, { code: 'LIFECYCLE_SOURCE_INVALID', message: `${request.intent} is not allowed from ${sourceState}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
378
445
|
}
|
|
379
|
-
if (sourcePath !== targetPath && fs.existsSync(targetPath)) {
|
|
446
|
+
if (sourcePath !== targetPath && fs.existsSync(targetPath) && !existingJournal) {
|
|
380
447
|
return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
381
448
|
}
|
|
382
449
|
const content = io.readFileSync(currentTaskFile);
|
|
@@ -385,6 +452,10 @@ function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLif
|
|
|
385
452
|
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
453
|
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
454
|
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 });
|
|
455
|
+
if (spec.target === 'active') {
|
|
456
|
+
const contract = validateCurrentTaskContract(content);
|
|
457
|
+
if (!contract.ok) return failed(request, { code: 'LIFECYCLE_DOCUMENT_INVALID', message: contract.message }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
458
|
+
}
|
|
388
459
|
const registryRequest = executeShortIdCommand({
|
|
389
460
|
operation: 'list', activeDir: path.join(repoRoot, '.agents', 'workspace', 'active'),
|
|
390
461
|
shortIdLength: configuredShortIdLength(repoRoot)
|
|
@@ -408,6 +479,9 @@ function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLif
|
|
|
408
479
|
try { journal = parseJournal(io.readFileSync(initialJournalPath)); }
|
|
409
480
|
catch (error) { return failed(request, { code: 'LIFECYCLE_JOURNAL_INVALID', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath }); }
|
|
410
481
|
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 });
|
|
482
|
+
if (spec.target === 'active' && !isValidAgentInfraVersion(journal.metadata.agentInfraVersion)) {
|
|
483
|
+
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 });
|
|
484
|
+
}
|
|
411
485
|
if (request.dryRun) {
|
|
412
486
|
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
487
|
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 +492,9 @@ function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLif
|
|
|
418
492
|
let metadata: TaskWriteMetadata;
|
|
419
493
|
try { metadata = (options.metadataProvider ?? captureTaskWriteMetadata)(); }
|
|
420
494
|
catch (error) { return failed(request, { code: 'LIFECYCLE_METADATA_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath }); }
|
|
495
|
+
if (spec.target === 'active' && !isValidAgentInfraVersion(metadata.agentInfraVersion)) {
|
|
496
|
+
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 });
|
|
497
|
+
}
|
|
421
498
|
journal = { version: 1, taskId, intent: request.intent, intentDigest: digest, sourceState, targetState: spec.target, metadata, completedSteps: [] };
|
|
422
499
|
if (request.dryRun) {
|
|
423
500
|
const plannedMutations = mutationsFor(request, content, metadata, restoredFiles, allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'));
|
|
@@ -478,13 +555,45 @@ function applyTaskLifecycle(requestInput: TaskLifecycleRequest, options: TaskLif
|
|
|
478
555
|
if (fs.existsSync(targetPath)) return failed(request, { code: 'LIFECYCLE_TARGET_CONFLICT', message: `target directory already exists: ${targetPath}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
479
556
|
try { (options.directoryRenameSync ?? io.renameSync)(sourcePath, targetPath); }
|
|
480
557
|
catch (error) {
|
|
558
|
+
if ((error as { code?: string }).code === 'EXDEV') {
|
|
559
|
+
try {
|
|
560
|
+
copyDirectoryVerified(sourcePath, targetPath);
|
|
561
|
+
completed.add('directory-moved');
|
|
562
|
+
journal.completedSteps = [...completed];
|
|
563
|
+
const copiedJournalPath = path.join(targetPath, '.task-lifecycle.json');
|
|
564
|
+
writeJournal(initialJournalPath, journal, io);
|
|
565
|
+
writeJournal(copiedJournalPath, journal, io);
|
|
566
|
+
removeCopiedSource(sourcePath, targetPath);
|
|
567
|
+
journalPath = copiedJournalPath;
|
|
568
|
+
} catch (copyError) {
|
|
569
|
+
if (fs.existsSync(targetPath)) fs.rmSync(targetPath, { recursive: true, force: false });
|
|
570
|
+
const failure = { code: 'LIFECYCLE_DIRECTORY_COPY_FAILED', message: String(copyError) } as const;
|
|
571
|
+
rememberJournalFailure(initialJournalPath, journal, io, failure);
|
|
572
|
+
return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
573
|
+
}
|
|
574
|
+
} else {
|
|
481
575
|
const failure = { code: 'LIFECYCLE_DIRECTORY_RENAME_FAILED', message: String(error) } as const;
|
|
482
576
|
rememberJournalFailure(journalPath, journal, io, failure);
|
|
483
577
|
return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (completed.has('directory-moved') && !fs.existsSync(sourcePath)) {
|
|
581
|
+
journalPath = path.join(targetPath, '.task-lifecycle.json');
|
|
582
|
+
} else if (!completed.has('directory-moved')) {
|
|
583
|
+
completed.add('directory-moved'); journal.completedSteps = [...completed]; journalPath = path.join(targetPath, '.task-lifecycle.json');
|
|
584
|
+
try { writeJournal(journalPath, journal, io); }
|
|
585
|
+
catch (error) { return failed(request, { code: 'LIFECYCLE_JOURNAL_WRITE_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, directory: { effect: 'move', changed: true }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion }); }
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
if (completed.has('directory-moved') && sourcePath !== targetPath && fs.existsSync(sourcePath) && fs.existsSync(targetPath)) {
|
|
589
|
+
try {
|
|
590
|
+
removeCopiedSource(sourcePath, targetPath);
|
|
591
|
+
journalPath = path.join(targetPath, '.task-lifecycle.json');
|
|
592
|
+
} catch (error) {
|
|
593
|
+
const failure = { code: 'LIFECYCLE_DIRECTORY_COPY_FAILED', message: String(error) } as const;
|
|
594
|
+
rememberJournalFailure(journalPath, journal, io, failure);
|
|
595
|
+
return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
484
596
|
}
|
|
485
|
-
completed.add('directory-moved'); journal.completedSteps = [...completed]; journalPath = path.join(targetPath, '.task-lifecycle.json');
|
|
486
|
-
try { writeJournal(journalPath, journal, io); }
|
|
487
|
-
catch (error) { return failed(request, { code: 'LIFECYCLE_JOURNAL_WRITE_FAILED', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, directory: { effect: 'move', changed: true }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion }); }
|
|
488
597
|
}
|
|
489
598
|
let shortId: TaskLifecycleResult['shortId'] = { effect: 'unchanged', shortId: null, changed: false };
|
|
490
599
|
if (!completed.has('registry-committed')) {
|