@fitlab-ai/agent-infra 0.9.11 → 0.9.13
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 -13
- package/README.zh-CN.md +13 -13
- package/dist/bin/internal-cli.js +29 -19
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/defaults.json +4 -0
- package/dist/lib/git/workflow.js +43 -1
- package/dist/lib/init.js +1 -0
- package/dist/lib/internal/git-workflow.js +8 -1
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +9 -9
- package/dist/lib/internal/platform-context.js +2 -2
- package/dist/lib/internal/platform-issue.js +8 -9
- package/dist/lib/internal/platform-pr-review.js +10 -10
- package/dist/lib/internal/platform-pr.js +68 -23
- package/dist/lib/internal/platform-release-notes.js +3 -3
- package/dist/lib/internal/pr-review-grade.js +11 -9
- package/dist/lib/internal/release-workflow.js +4 -4
- package/dist/lib/internal/sandbox-control.js +30 -4
- package/dist/lib/internal/task-activity.js +3 -3
- package/dist/lib/internal/task-artifact.js +76 -24
- package/dist/lib/internal/task-create.js +2 -2
- package/dist/lib/internal/task-delivery.js +59 -0
- package/dist/lib/internal/task-event.js +6 -3
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-invalidation.js +60 -0
- package/dist/lib/internal/task-ledger.js +9 -6
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-override.js +4 -4
- package/dist/lib/internal/task-review.js +3 -3
- package/dist/lib/internal/task-verify.js +6 -6
- package/dist/lib/internal/task-warning.js +3 -3
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +8 -2
- package/dist/lib/platform/github-provider.js +903 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +94 -41
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +551 -0
- package/dist/lib/platform/pr-checks.js +124 -106
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +523 -49
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +487 -0
- package/dist/lib/platform/provider-contract.js +95 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +504 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +1050 -502
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +22 -12
- package/dist/lib/platform/verification-sync.js +118 -239
- package/dist/lib/pr-review/evidence-grading.js +30 -9
- package/dist/lib/run/index.js +63 -23
- package/dist/lib/sandbox/config.js +5 -0
- package/dist/lib/sandbox/control/client.js +96 -15
- package/dist/lib/sandbox/control/executor.js +3 -3
- package/dist/lib/sandbox/control/lifecycle.js +29 -7
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +398 -25
- package/dist/lib/sandbox/control/state.js +204 -1
- package/dist/lib/server/process-state.js +2 -2
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +35 -7
- package/dist/lib/task/capabilities.js +239 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-intent.js +100 -0
- package/dist/lib/task/commit-operation.js +207 -102
- package/dist/lib/task/control-authority.js +8 -5
- package/dist/lib/task/create-service.js +8 -8
- package/dist/lib/task/create.js +33 -7
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +3 -48
- package/dist/lib/task/delivery-target.js +97 -0
- package/dist/lib/task/delivery.js +153 -0
- package/dist/lib/task/events.js +297 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +61 -36
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/lifecycle.js +106 -9
- package/dist/lib/task/local-artifact-finalization.js +374 -0
- package/dist/lib/task/orchestration.js +100 -151
- package/dist/lib/task/pr-delivery-fact.js +183 -0
- package/dist/lib/task/review-finalization.js +4 -8
- package/dist/lib/task/review-fingerprint.js +32 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-engine.js +89 -38
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/update.js +9 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- 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/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/defaults.json +4 -0
- package/lib/git/workflow.ts +38 -1
- package/lib/init.ts +4 -1
- package/lib/internal/git-workflow.ts +7 -1
- package/lib/internal/platform-checks.ts +3 -3
- package/lib/internal/platform-comment.ts +8 -8
- package/lib/internal/platform-context.ts +2 -2
- package/lib/internal/platform-issue.ts +7 -8
- package/lib/internal/platform-pr-review.ts +9 -9
- package/lib/internal/platform-pr.ts +53 -21
- package/lib/internal/platform-release-notes.ts +3 -3
- package/lib/internal/pr-review-grade.ts +10 -8
- package/lib/internal/release-workflow.ts +4 -4
- package/lib/internal/sandbox-control.ts +27 -3
- package/lib/internal/task-activity.ts +3 -3
- package/lib/internal/task-artifact.ts +65 -22
- package/lib/internal/task-create.ts +2 -2
- package/lib/internal/task-delivery.ts +53 -0
- package/lib/internal/task-event.ts +6 -3
- package/lib/internal/task-finalization.ts +3 -3
- package/lib/internal/task-invalidation.ts +39 -0
- package/lib/internal/task-ledger.ts +11 -8
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-override.ts +6 -6
- package/lib/internal/task-review.ts +3 -3
- package/lib/internal/task-verify.ts +6 -6
- package/lib/internal/task-warning.ts +3 -3
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +10 -3
- package/lib/platform/github-provider.ts +910 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +97 -47
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +645 -0
- package/lib/platform/pr-checks.ts +129 -107
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +562 -52
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +536 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +529 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +1126 -505
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +21 -13
- package/lib/platform/verification-sync.ts +118 -293
- package/lib/pr-review/evidence-grading.ts +31 -11
- package/lib/run/index.ts +57 -22
- package/lib/sandbox/config.ts +7 -0
- package/lib/sandbox/control/client.ts +105 -15
- package/lib/sandbox/control/executor.ts +4 -4
- package/lib/sandbox/control/lifecycle.ts +30 -8
- package/lib/sandbox/control/protocol.ts +113 -0
- package/lib/sandbox/control/server.ts +431 -23
- package/lib/sandbox/control/state.ts +251 -1
- package/lib/server/process-state.ts +2 -2
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +34 -8
- package/lib/task/capabilities.ts +262 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-intent.ts +137 -0
- package/lib/task/commit-operation.ts +186 -88
- package/lib/task/control-authority.ts +11 -8
- package/lib/task/create-service.ts +8 -8
- package/lib/task/create.ts +34 -9
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +8 -64
- package/lib/task/delivery-target.ts +130 -0
- package/lib/task/delivery.ts +160 -0
- package/lib/task/events.ts +296 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +73 -46
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/lifecycle.ts +93 -4
- package/lib/task/local-artifact-finalization.ts +563 -0
- package/lib/task/orchestration.ts +110 -146
- package/lib/task/pr-delivery-fact.ts +237 -0
- package/lib/task/review-finalization.ts +4 -14
- package/lib/task/review-fingerprint.ts +33 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-engine.ts +95 -43
- package/lib/task/verification-review.ts +2 -0
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +11 -1
- package/lib/version.ts +15 -1
- package/package.json +7 -1
- package/templates/.agents/rules/README.en.md +4 -2
- package/templates/.agents/rules/README.zh-CN.md +4 -2
- 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/issue-pr-commands.en.md +4 -17
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +28 -5
- package/templates/.agents/rules/issue-sync.zh-CN.md +64 -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 +53 -0
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +53 -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/pr-sync.en.md +28 -13
- package/templates/.agents/rules/pr-sync.zh-CN.md +39 -5
- 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/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 +1 -1
- package/templates/.agents/rules/task-management.zh-CN.md +1 -1
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -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 +14 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -3
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +32 -10
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +35 -13
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/commit/SKILL.en.md +3 -3
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -3
- 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/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +4 -4
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +4 -4
- 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 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -4
- 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 +5 -4
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +5 -4
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -13
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +30 -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 +11 -10
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +11 -11
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/create-task/SKILL.en.md +1 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -0
- 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/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 +14 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +11 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +6 -5
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +6 -5
- 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 +10 -9
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +10 -9
- 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 +6 -5
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +6 -5
- 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 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +4 -0
- package/templates/.agents/skills/watch-pr/SKILL.en.md +6 -6
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +23 -2
- package/templates/.agents/templates/task.zh-CN.md +23 -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/.github/workflows/pr-label.yml +7 -26
- 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 -44
- package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -44
- package/templates/.agents/rules/security-alerts.github.en.md +0 -43
- package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
- /package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +0 -0
- /package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +0 -0
|
@@ -11,10 +11,13 @@ import { appendActivityEntry, locateActivityLog } from './activity-log.ts';
|
|
|
11
11
|
import { captureTaskWriteMetadata, writeTask } from './write.ts';
|
|
12
12
|
import type { TaskMutation } from './write.ts';
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
checkpointIntentDigest,
|
|
15
|
+
readCheckpointIntent,
|
|
16
|
+
removeCheckpointIntent,
|
|
17
|
+
sameCheckpointIntent,
|
|
18
|
+
updateCheckpointIntent,
|
|
19
|
+
writeCheckpointIntent
|
|
20
|
+
} from './commit-intent.ts';
|
|
18
21
|
|
|
19
22
|
type CommitExecutionMode = 'direct' | 'orchestrated';
|
|
20
23
|
|
|
@@ -27,7 +30,13 @@ type CommitOperationInput = Readonly<{
|
|
|
27
30
|
taskRef?: string;
|
|
28
31
|
agent?: string;
|
|
29
32
|
mode?: CommitExecutionMode;
|
|
30
|
-
|
|
33
|
+
round?: number;
|
|
34
|
+
delivery?: Readonly<{
|
|
35
|
+
mode: 'local' | 'push';
|
|
36
|
+
remote: string;
|
|
37
|
+
refs: readonly string[];
|
|
38
|
+
}> | Readonly<{ mode: 'local' }>;
|
|
39
|
+
push?: Readonly<{
|
|
31
40
|
remote: string;
|
|
32
41
|
refs: readonly string[];
|
|
33
42
|
}>;
|
|
@@ -55,6 +64,7 @@ type BoundTask = Readonly<{
|
|
|
55
64
|
}>;
|
|
56
65
|
|
|
57
66
|
const HEAD_REF = /^refs\/heads\/(.+)$/;
|
|
67
|
+
type CommitDelivery = Readonly<{ mode: 'local' } | { mode: 'push'; remote: string; refs: readonly string[] }>;
|
|
58
68
|
|
|
59
69
|
function isCommitExecutionMode(value: unknown): value is CommitExecutionMode {
|
|
60
70
|
return value === 'direct' || value === 'orchestrated';
|
|
@@ -68,6 +78,12 @@ function gitText(repoRoot: string, args: readonly string[]): string {
|
|
|
68
78
|
}).trim();
|
|
69
79
|
}
|
|
70
80
|
|
|
81
|
+
function effectiveDelivery(input: CommitOperationInput): CommitDelivery | null {
|
|
82
|
+
if (input.delivery) return input.delivery;
|
|
83
|
+
if (input.push) return { mode: 'push', remote: input.push.remote, refs: input.push.refs };
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
71
87
|
function canonicalRepositoryRoot(cwd: string): string {
|
|
72
88
|
return gitText(cwd, ['rev-parse', '--show-toplevel']);
|
|
73
89
|
}
|
|
@@ -156,7 +172,8 @@ function validateCommon(input: CommitOperationInput, branch: string | null): Rea
|
|
|
156
172
|
if (!input.message.trim()) return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit message is required' };
|
|
157
173
|
if (!/^[a-f0-9]{40,64}$/.test(input.expectedHead)) return { code: 'GIT_HEAD_EXPECTATION_REQUIRED', message: 'A valid expected HEAD is required' };
|
|
158
174
|
if (!/^[a-f0-9]{40,64}$/.test(input.expectedTree)) return { code: 'GIT_TREE_EXPECTATION_REQUIRED', message: 'A valid expected tree is required' };
|
|
159
|
-
|
|
175
|
+
const delivery = effectiveDelivery(input);
|
|
176
|
+
if (!delivery) return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Commit delivery requires a delivery policy' };
|
|
160
177
|
if (input.paths.some((candidate) => !candidate || candidate.startsWith('/') || candidate.split(/[\\/]/).includes('..'))) {
|
|
161
178
|
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit paths must stay inside the repository' };
|
|
162
179
|
}
|
|
@@ -164,42 +181,16 @@ function validateCommon(input: CommitOperationInput, branch: string | null): Rea
|
|
|
164
181
|
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Sensitive paths cannot be committed' };
|
|
165
182
|
}
|
|
166
183
|
if (branch !== null && branch === '') return { code: 'GIT_BRANCH_INVALID', message: 'A named branch is required' };
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
184
|
+
if (delivery.mode === 'push') {
|
|
185
|
+
if (!delivery.remote || delivery.refs.length !== 1) return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push requires one remote and one ref' };
|
|
186
|
+
const ref = delivery.refs[0] ?? '';
|
|
187
|
+
if (!HEAD_REF.test(ref) || HEAD_REF.exec(ref)?.[1] !== branch) {
|
|
188
|
+
return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push ref must be the current full heads ref' };
|
|
189
|
+
}
|
|
171
190
|
}
|
|
172
191
|
return null;
|
|
173
192
|
}
|
|
174
193
|
|
|
175
|
-
function validateOrchestrated(
|
|
176
|
-
input: CommitOperationInput,
|
|
177
|
-
task: BoundTask
|
|
178
|
-
): { plan: OrchestrationStageCompletion } | { error: Readonly<{ code: string; message: string }> } {
|
|
179
|
-
if (!input.agent) return { error: { code: 'ORCHESTRATION_PROVENANCE_MISMATCH', message: 'orchestrated commit requires an agent' } };
|
|
180
|
-
const planned = planOrchestrationStageCompletion(task.taskId, {
|
|
181
|
-
stage: 'commit',
|
|
182
|
-
round: 1,
|
|
183
|
-
artifact: 'commit',
|
|
184
|
-
role: 'executor',
|
|
185
|
-
agent: input.agent
|
|
186
|
-
}, { repoRoot: task.repoRoot });
|
|
187
|
-
const run = planned.result.run;
|
|
188
|
-
if (
|
|
189
|
-
!planned.plan
|
|
190
|
-
|| !run
|
|
191
|
-
|| run.status !== 'running'
|
|
192
|
-
|| !run.commitAuthorization.issuedAt
|
|
193
|
-
|| run.commitAuthorization.consumedAt
|
|
194
|
-
) return {
|
|
195
|
-
error: {
|
|
196
|
-
code: planned.result.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH',
|
|
197
|
-
message: planned.result.error?.message ?? 'orchestrated commit requires one matching activated commit delegation'
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
return { plan: planned.plan };
|
|
201
|
-
}
|
|
202
|
-
|
|
203
194
|
function syncTaskCommit(
|
|
204
195
|
input: CommitOperationInput,
|
|
205
196
|
task: BoundTask,
|
|
@@ -219,7 +210,18 @@ function syncTaskCommit(
|
|
|
219
210
|
);
|
|
220
211
|
const metadata = captureTaskWriteMetadata();
|
|
221
212
|
const commitNote = gitText(task.repoRoot, ['show', '-s', '--format=%h %s', committedHead]);
|
|
222
|
-
|
|
213
|
+
let matchingCommitIndex = -1;
|
|
214
|
+
for (let index = activity.entries.length - 1; index >= 0; index -= 1) {
|
|
215
|
+
const entry = activity.entries[index]!;
|
|
216
|
+
if (entry.step === 'Commit' && entry.note === commitNote) {
|
|
217
|
+
matchingCommitIndex = index;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const reviewedAfterCommit = matchingCommitIndex >= 0 && activity.entries
|
|
222
|
+
.slice(matchingCommitIndex + 1)
|
|
223
|
+
.some((entry) => /^Review Code \(Round \d+\)$/.test(entry.step));
|
|
224
|
+
const alreadyLogged = matchingCommitIndex >= 0 && !reviewedAfterCommit;
|
|
223
225
|
const mutations: TaskMutation[] = [];
|
|
224
226
|
if (!alreadyLogged) mutations.push({
|
|
225
227
|
kind: 'section',
|
|
@@ -227,7 +229,10 @@ function syncTaskCommit(
|
|
|
227
229
|
heading: activity.heading,
|
|
228
230
|
body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Commit', agent: input.agent!, note: commitNote })
|
|
229
231
|
});
|
|
230
|
-
const frontmatter: Record<string, string> = {
|
|
232
|
+
const frontmatter: Record<string, string> = {
|
|
233
|
+
assigned_to: input.agent!,
|
|
234
|
+
checkpoint_commit: committedHead
|
|
235
|
+
};
|
|
231
236
|
mutations.push({ kind: 'frontmatter', set: frontmatter });
|
|
232
237
|
if (mutations.length === 0) return null;
|
|
233
238
|
const written = writeTask({
|
|
@@ -256,29 +261,133 @@ function syncTaskCommit(
|
|
|
256
261
|
}
|
|
257
262
|
}
|
|
258
263
|
|
|
264
|
+
function checkpointChildMatches(task: BoundTask, intent: NonNullable<ReturnType<typeof readCheckpointIntent>>, head: string): boolean {
|
|
265
|
+
try {
|
|
266
|
+
const parent = gitText(task.repoRoot, ['rev-parse', `${head}^`]);
|
|
267
|
+
const tree = gitText(task.repoRoot, ['rev-parse', `${head}^{tree}`]);
|
|
268
|
+
const message = gitText(task.repoRoot, ['show', '-s', '--format=%s', head]);
|
|
269
|
+
const changed = gitText(task.repoRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', head]).split('\n').filter(Boolean).sort();
|
|
270
|
+
return parent === intent.expectedHead
|
|
271
|
+
&& tree === intent.expectedTree
|
|
272
|
+
&& message === intent.message
|
|
273
|
+
&& JSON.stringify(changed) === JSON.stringify([...intent.paths].sort());
|
|
274
|
+
} catch {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function checkpointIdentity(input: CommitOperationInput, task: BoundTask, delivery: CommitDelivery): Parameters<typeof checkpointIntentDigest>[0] {
|
|
280
|
+
if (delivery.mode !== 'local') throw new Error('checkpoint identity requires local delivery');
|
|
281
|
+
return {
|
|
282
|
+
taskId: task.taskId,
|
|
283
|
+
branch: task.branch,
|
|
284
|
+
mode: 'local',
|
|
285
|
+
expectedHead: input.expectedHead,
|
|
286
|
+
expectedTree: input.expectedTree,
|
|
287
|
+
paths: [...input.paths],
|
|
288
|
+
message: input.message,
|
|
289
|
+
round: input.round ?? 1
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function checkpointTimestamp(): string {
|
|
294
|
+
return new Date().toISOString();
|
|
295
|
+
}
|
|
296
|
+
|
|
259
297
|
function executeUnlocked(input: CommitOperationInput, task: BoundTask | null, mode: CommitExecutionMode): CommitOperationResult {
|
|
260
298
|
const inspected = inspectGitWorkflow(input.cwd);
|
|
261
299
|
if (!inspected.snapshot) return failure(input, mode, task?.taskId ?? null, 'GIT_INSPECT_FAILED', 'Unable to inspect Git repository');
|
|
300
|
+
if (mode === 'orchestrated') return failure(
|
|
301
|
+
input,
|
|
302
|
+
mode,
|
|
303
|
+
task?.taskId ?? null,
|
|
304
|
+
'ORCHESTRATED_COMMIT_REMOVED',
|
|
305
|
+
'Orchestrated commit is no longer a lifecycle stage; use local checkpoint delivery from code-task',
|
|
306
|
+
'blocked',
|
|
307
|
+
inspected.snapshot
|
|
308
|
+
);
|
|
262
309
|
const branch = inspected.snapshot.branch;
|
|
263
310
|
const commonError = validateCommon(input, branch);
|
|
264
311
|
if (commonError) return failure(input, mode, task?.taskId ?? null, commonError.code, commonError.message, 'blocked', inspected.snapshot);
|
|
265
312
|
if (task && task.state !== 'active') return failure(input, mode, task.taskId, 'TASK_STATE_INVALID', 'task-bound commit requires an active task', 'blocked', inspected.snapshot);
|
|
266
313
|
if (task && task.branch !== branch) return failure(input, mode, task.taskId, 'GIT_BRANCH_MISMATCH', `Task branch '${task.branch}' does not match current branch '${branch}'`, 'blocked', inspected.snapshot);
|
|
267
314
|
if (task && !input.agent) return failure(input, mode, task.taskId, 'COMMIT_AGENT_REQUIRED', 'task-bound commit requires an agent', 'blocked', inspected.snapshot);
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
315
|
+
const delivery = effectiveDelivery(input)!;
|
|
316
|
+
if (delivery.mode === 'local' && !task) return failure(input, mode, null, 'CHECKPOINT_TASK_REQUIRED', 'local checkpoint delivery requires a task context', 'blocked', inspected.snapshot);
|
|
317
|
+
let pendingIntent: NonNullable<ReturnType<typeof readCheckpointIntent>> | null = null;
|
|
318
|
+
let checkpointCommittedHead: string | null = null;
|
|
319
|
+
const committed = delivery.mode === 'local'
|
|
320
|
+
? (() => {
|
|
321
|
+
const identity = checkpointIdentity(input, task!, delivery);
|
|
322
|
+
try {
|
|
323
|
+
pendingIntent = readCheckpointIntent(task!.repoRoot, task!.taskId);
|
|
324
|
+
} catch (error) {
|
|
325
|
+
return {
|
|
326
|
+
status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
|
|
327
|
+
error: { code: 'COMMIT_INTENT_INVALID', message: error instanceof Error ? error.message : String(error) }
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
if (pendingIntent && !sameCheckpointIntent(pendingIntent, identity)) return {
|
|
331
|
+
status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
|
|
332
|
+
error: { code: 'COMMIT_INTENT_CONFLICT', message: 'A different checkpoint intent is still pending for this task' }
|
|
333
|
+
};
|
|
334
|
+
if (!pendingIntent) {
|
|
335
|
+
const timestamp = checkpointTimestamp();
|
|
336
|
+
pendingIntent = {
|
|
337
|
+
version: 1, ...identity, digest: checkpointIntentDigest(identity), state: 'prepared',
|
|
338
|
+
committedHead: null, createdAt: timestamp, updatedAt: timestamp
|
|
339
|
+
};
|
|
340
|
+
try { writeCheckpointIntent(task!.repoRoot, pendingIntent); }
|
|
341
|
+
catch (error) {
|
|
342
|
+
return {
|
|
343
|
+
status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
|
|
344
|
+
error: { code: 'COMMIT_INTENT_WRITE_FAILED', message: error instanceof Error ? error.message : String(error) }
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (pendingIntent.state !== 'prepared') {
|
|
349
|
+
checkpointCommittedHead = pendingIntent.committedHead;
|
|
350
|
+
return {
|
|
351
|
+
status: 'no-op' as const, changed: false, snapshot: inspected.snapshot,
|
|
352
|
+
operations: [{ name: 'commit', status: 'no-op' as const }], error: null
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
if (inspected.snapshot.head !== identity.expectedHead && !checkpointChildMatches(task!, pendingIntent, inspected.snapshot.head)) return {
|
|
356
|
+
status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
|
|
357
|
+
error: { code: 'COMMIT_INTENT_HEAD_CONFLICT', message: 'Current HEAD does not match the pending checkpoint intent' }
|
|
358
|
+
};
|
|
359
|
+
if (inspected.snapshot.head !== identity.expectedHead) {
|
|
360
|
+
checkpointCommittedHead = inspected.snapshot.head;
|
|
361
|
+
pendingIntent = updateCheckpointIntent(pendingIntent, {
|
|
362
|
+
state: 'committed', committedHead: checkpointCommittedHead, updatedAt: checkpointTimestamp()
|
|
363
|
+
});
|
|
364
|
+
writeCheckpointIntent(task!.repoRoot, pendingIntent);
|
|
365
|
+
return {
|
|
366
|
+
status: 'no-op' as const, changed: false, snapshot: inspected.snapshot,
|
|
367
|
+
operations: [{ name: 'commit', status: 'no-op' as const }], error: null
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (input.paths.length === 0) return {
|
|
371
|
+
status: 'failed' as const, changed: false, snapshot: inspected.snapshot, operations: [],
|
|
372
|
+
error: { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Local checkpoint requires explicit paths' }
|
|
373
|
+
};
|
|
374
|
+
const created = commitExplicitPaths({
|
|
375
|
+
cwd: input.cwd, paths: input.paths, message: input.message,
|
|
376
|
+
expectedHead: identity.expectedHead, expectedTree: identity.expectedTree
|
|
377
|
+
});
|
|
378
|
+
if (created.status === 'applied' && created.snapshot?.head) {
|
|
379
|
+
checkpointCommittedHead = created.snapshot.head;
|
|
380
|
+
pendingIntent = updateCheckpointIntent(pendingIntent, {
|
|
381
|
+
state: 'committed', committedHead: checkpointCommittedHead, updatedAt: checkpointTimestamp()
|
|
382
|
+
});
|
|
383
|
+
writeCheckpointIntent(task!.repoRoot, pendingIntent);
|
|
384
|
+
} else if (created.status === 'no-op') {
|
|
385
|
+
removeCheckpointIntent(task!.repoRoot, task!.taskId);
|
|
386
|
+
pendingIntent = null;
|
|
387
|
+
}
|
|
388
|
+
return created;
|
|
389
|
+
})()
|
|
390
|
+
: input.paths.length === 0
|
|
282
391
|
? (() => {
|
|
283
392
|
if (inspected.snapshot.worktree.length > 0 || inspected.snapshot.staged.length > 0) return {
|
|
284
393
|
status: 'failed' as const,
|
|
@@ -327,53 +436,42 @@ function executeUnlocked(input: CommitOperationInput, task: BoundTask | null, mo
|
|
|
327
436
|
};
|
|
328
437
|
|
|
329
438
|
const warnings: OperationWarning[] = [];
|
|
330
|
-
if (orchestrationCompletion) {
|
|
331
|
-
try {
|
|
332
|
-
commitOrchestrationStageCompletion(orchestrationCompletion);
|
|
333
|
-
} catch (error) {
|
|
334
|
-
warnings.push(warning(
|
|
335
|
-
'ORCHESTRATION_COMPLETION_FAILED',
|
|
336
|
-
error instanceof Error ? error.message : String(error),
|
|
337
|
-
true,
|
|
338
|
-
'orchestration',
|
|
339
|
-
task?.taskId ?? 'orchestration',
|
|
340
|
-
'ACTION_REQUIRED'
|
|
341
|
-
));
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
439
|
let status: CommitOperationResult['status'] = committed.status;
|
|
345
440
|
let result: CommitOperationResult['result'] = committed.status === 'applied' ? 'committed' : 'no_op';
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
true,
|
|
361
|
-
'push',
|
|
362
|
-
target,
|
|
363
|
-
'ACTION_REQUIRED'
|
|
364
|
-
));
|
|
441
|
+
if (delivery.mode === 'push') {
|
|
442
|
+
const target = `${delivery.remote}:${delivery.refs[0]}`;
|
|
443
|
+
const decision = commitPushDecision({ branch }, target);
|
|
444
|
+
if (!decision.shouldPush) {
|
|
445
|
+
warnings.push(decision.warning!);
|
|
446
|
+
} else {
|
|
447
|
+
const pushed = pushGitRefs({
|
|
448
|
+
cwd: input.cwd, remote: delivery.remote, refs: delivery.refs,
|
|
449
|
+
expectedSha: committed.snapshot?.head ?? gitText(input.cwd, ['rev-parse', 'HEAD'])
|
|
450
|
+
});
|
|
451
|
+
if (pushed.status !== 'applied') warnings.push(warning(
|
|
452
|
+
'COMMIT_PUSH_FAILED', pushed.error?.message ?? 'Git push failed', true, 'push', target, 'ACTION_REQUIRED'
|
|
453
|
+
));
|
|
454
|
+
}
|
|
365
455
|
}
|
|
366
456
|
if (warnings.length > 0) {
|
|
367
457
|
status = 'applied';
|
|
368
458
|
result = 'committed_with_warnings';
|
|
369
459
|
}
|
|
370
460
|
if (task && (committed.status === 'applied' || committed.status === 'no-op')) {
|
|
371
|
-
const
|
|
461
|
+
const committedHead = checkpointCommittedHead ?? committed.snapshot?.head ?? gitText(task.repoRoot, ['rev-parse', 'HEAD']);
|
|
462
|
+
const taskWarning = syncTaskCommit(input, task, committedHead);
|
|
372
463
|
if (taskWarning) {
|
|
373
464
|
warnings.push(taskWarning);
|
|
374
465
|
status = 'applied';
|
|
375
466
|
result = 'committed_with_warnings';
|
|
376
467
|
}
|
|
468
|
+
if (delivery.mode === 'local' && !taskWarning && pendingIntent && checkpointCommittedHead) {
|
|
469
|
+
const synced = updateCheckpointIntent(pendingIntent, {
|
|
470
|
+
state: 'synced', committedHead: checkpointCommittedHead, updatedAt: checkpointTimestamp()
|
|
471
|
+
});
|
|
472
|
+
writeCheckpointIntent(task.repoRoot, synced);
|
|
473
|
+
removeCheckpointIntent(task.repoRoot, task.taskId);
|
|
474
|
+
}
|
|
377
475
|
}
|
|
378
476
|
return {
|
|
379
477
|
status,
|
|
@@ -206,15 +206,15 @@ function lifecycleTaskId(request: TaskLifecycleControlRequest, repoRoot: string)
|
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
-
function applyLifecycleWithAuthority(
|
|
209
|
+
async function applyLifecycleWithAuthority(
|
|
210
210
|
context: TaskControlExecutionContext,
|
|
211
211
|
request: TaskLifecycleControlRequest
|
|
212
|
-
): TaskLifecycleResult & { humanOverride?: unknown } {
|
|
212
|
+
): Promise<TaskLifecycleResult & { humanOverride?: unknown }> {
|
|
213
213
|
const conflict = overrideDryRunConflict(request as unknown as Record<string, unknown>);
|
|
214
214
|
if (conflict) {
|
|
215
215
|
return taskLifecycleFailure(request, { code: 'LIFECYCLE_PAYLOAD_INVALID', message: conflict.message });
|
|
216
216
|
}
|
|
217
|
-
const execute = (): TaskLifecycleResult & { humanOverride?: unknown } => {
|
|
217
|
+
const execute = async (): Promise<TaskLifecycleResult & { humanOverride?: unknown }> => {
|
|
218
218
|
const lifecycleResult = applyTaskLifecycle(request, { repoRoot: context.repoRoot });
|
|
219
219
|
if (lifecycleResult.status !== 'failed' || !request.overrideTicket) return lifecycleResult;
|
|
220
220
|
if (!request.overrideTarget || !request.overrideScope) {
|
|
@@ -223,7 +223,7 @@ function applyLifecycleWithAuthority(
|
|
|
223
223
|
error: { code: 'OVERRIDE_PAYLOAD_INVALID', message: 'override ticket requires --override-target and --override-scope' }
|
|
224
224
|
};
|
|
225
225
|
}
|
|
226
|
-
const override = consumeHumanOverride({
|
|
226
|
+
const override = await consumeHumanOverride({
|
|
227
227
|
taskRef: request.taskRef,
|
|
228
228
|
ticketId: request.overrideTicket,
|
|
229
229
|
failureId: failureId('lifecycle.apply', lifecycleResult.error?.code ?? 'LIFECYCLE_FAILED'),
|
|
@@ -238,9 +238,9 @@ function applyLifecycleWithAuthority(
|
|
|
238
238
|
return { ...lifecycleResult, ...override, humanOverride: override, error: null };
|
|
239
239
|
};
|
|
240
240
|
const taskId = lifecycleTaskId(request, context.repoRoot);
|
|
241
|
-
if (!taskId) return execute();
|
|
241
|
+
if (!taskId) return await execute();
|
|
242
242
|
try {
|
|
243
|
-
return withTaskExecutionLock(
|
|
243
|
+
return await withTaskExecutionLock(
|
|
244
244
|
context.repoRoot,
|
|
245
245
|
taskId,
|
|
246
246
|
`task-lifecycle.${request.intent}`,
|
|
@@ -322,11 +322,11 @@ function orchestrationFailure(error: unknown): OrchestrationResult {
|
|
|
322
322
|
export function dispatchTaskControlOperation(
|
|
323
323
|
context: TaskControlExecutionContext,
|
|
324
324
|
operation: Extract<TaskControlOperation, { family: 'task-lifecycle' }>
|
|
325
|
-
): TaskLifecycleResult
|
|
325
|
+
): Promise<TaskLifecycleResult & { humanOverride?: unknown }>;
|
|
326
326
|
export function dispatchTaskControlOperation(
|
|
327
327
|
context: TaskControlExecutionContext,
|
|
328
328
|
operation: Extract<TaskControlOperation, { family: 'task-finalization' }>
|
|
329
|
-
): TaskFinalizationResult
|
|
329
|
+
): Promise<TaskFinalizationResult>;
|
|
330
330
|
export function dispatchTaskControlOperation(
|
|
331
331
|
context: TaskControlExecutionContext,
|
|
332
332
|
operation: Extract<TaskControlOperation, { family: 'task-orchestration' }>
|
|
@@ -345,6 +345,9 @@ export function dispatchTaskControlOperation(
|
|
|
345
345
|
operationTaskId(context, operation.request.taskRef);
|
|
346
346
|
return applyTaskFinalization(operation.request, {
|
|
347
347
|
repoRoot: context.repoRoot,
|
|
348
|
+
...(context.source === 'sandbox-executor' ? {
|
|
349
|
+
controlBinding: { generation: context.generation, requestId: context.requestId }
|
|
350
|
+
} : {}),
|
|
348
351
|
preflight: (request, options) => verifyTaskEvent(
|
|
349
352
|
{ ...request, event: 'complete-task.hard-preflight' }, options
|
|
350
353
|
)
|
|
@@ -42,8 +42,8 @@ const DEFAULT_DEPENDENCIES: TaskCreateDependencies = {
|
|
|
42
42
|
addWarning: applyWorkflowWarningIntent
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
function verifyCreatedTask(repoRoot: string, taskId: string): { operation: TaskCreateOperation; error: TaskCreateResult['error'] } {
|
|
46
|
-
const verification = verifyTaskEvent(
|
|
45
|
+
async function verifyCreatedTask(repoRoot: string, taskId: string): Promise<{ operation: TaskCreateOperation; error: TaskCreateResult['error'] }> {
|
|
46
|
+
const verification = await verifyTaskEvent(
|
|
47
47
|
{ taskRef: taskId, event: 'create-task.completed' },
|
|
48
48
|
{ repoRoot }
|
|
49
49
|
);
|
|
@@ -78,7 +78,7 @@ function issueFromTask(repoRoot: string, taskId: string): { number: number; url:
|
|
|
78
78
|
return match ? { number: Number(match[1]), url: '' } : null;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResult {
|
|
81
|
+
async function createTask(value: unknown, options: CreateTaskOptions): Promise<TaskCreateResult> {
|
|
82
82
|
const dependencies = { ...DEFAULT_DEPENDENCIES, ...options.dependencies };
|
|
83
83
|
let candidate: TaskCreateCandidateV1;
|
|
84
84
|
let local;
|
|
@@ -101,13 +101,13 @@ function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResul
|
|
|
101
101
|
name: 'task:local', status: local.status, reasonCode: null
|
|
102
102
|
}];
|
|
103
103
|
let issue = issueFromTask(options.repoRoot, local.task.id);
|
|
104
|
-
const created = dependencies.createIssue(local.task.id, { cwd: options.repoRoot, agent: candidate.agent });
|
|
104
|
+
const created = await dependencies.createIssue(local.task.id, { cwd: options.repoRoot, agent: candidate.agent });
|
|
105
105
|
operations.push(...platformOperations('platform-create', created.operations));
|
|
106
106
|
if (created.issue) issue = { number: created.issue.number, url: created.issue.url };
|
|
107
107
|
|
|
108
108
|
let platformFailure: PlatformResult | null = created.status === 'failed' || created.status === 'blocked' ? created : null;
|
|
109
109
|
if (!platformFailure && (created.task.issueNumber || issue)) {
|
|
110
|
-
const synced = dependencies.syncIssue(local.task.id, {
|
|
110
|
+
const synced = await dependencies.syncIssue(local.task.id, {
|
|
111
111
|
cwd: options.repoRoot,
|
|
112
112
|
agent: candidate.agent,
|
|
113
113
|
status: 'waiting-for-triage',
|
|
@@ -120,7 +120,7 @@ function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResul
|
|
|
120
120
|
if (synced.issue) issue = { number: synced.issue.number, url: synced.issue.url };
|
|
121
121
|
if (synced.status === 'failed' || synced.status === 'blocked') platformFailure = synced;
|
|
122
122
|
if (!platformFailure) {
|
|
123
|
-
const commented = dependencies.syncComment(local.task.id, { cwd: options.repoRoot, kind: 'task', agent: candidate.agent });
|
|
123
|
+
const commented = await dependencies.syncComment(local.task.id, { cwd: options.repoRoot, kind: 'task', agent: candidate.agent });
|
|
124
124
|
operations.push(...platformOperations('platform-comment', commented.operations));
|
|
125
125
|
if (commented.status === 'failed' || commented.status === 'blocked') platformFailure = commented;
|
|
126
126
|
}
|
|
@@ -133,7 +133,7 @@ function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResul
|
|
|
133
133
|
code: 'ISSUE_CREATE_FAILED', target: 'issue', message: `${error.code}: ${error.message}`,
|
|
134
134
|
action: 'Fix platform authentication or connectivity, then retry Issue creation for this task.'
|
|
135
135
|
}, { repoRoot: options.repoRoot });
|
|
136
|
-
const verified = verifyCreatedTask(options.repoRoot, local.task.id);
|
|
136
|
+
const verified = await verifyCreatedTask(options.repoRoot, local.task.id);
|
|
137
137
|
operations.push(verified.operation);
|
|
138
138
|
if (verified.error) return {
|
|
139
139
|
status: verified.error.retryable ? 'blocked' : 'failed', changed: local.changed,
|
|
@@ -150,7 +150,7 @@ function createTask(value: unknown, options: CreateTaskOptions): TaskCreateResul
|
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
const verified = verifyCreatedTask(options.repoRoot, local.task.id);
|
|
153
|
+
const verified = await verifyCreatedTask(options.repoRoot, local.task.id);
|
|
154
154
|
operations.push(verified.operation);
|
|
155
155
|
if (verified.error) return {
|
|
156
156
|
status: verified.error.retryable ? 'blocked' : 'failed', changed: local.changed,
|
package/lib/task/create.ts
CHANGED
|
@@ -5,6 +5,8 @@ import path from 'node:path';
|
|
|
5
5
|
import { isValidAgentInfraVersion, VERSION } from '../version.ts';
|
|
6
6
|
import { loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.ts';
|
|
7
7
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
8
|
+
import { readDeliveryDefaults, validateBaseRef, validateRemote } from './delivery-target.ts';
|
|
9
|
+
import { buildUnboundFact, encodePrDeliveryFact } from './pr-delivery-fact.ts';
|
|
8
10
|
|
|
9
11
|
const AGENTS = ['claude', 'codex', 'antigravity', 'opencode', 'cursor'] as const;
|
|
10
12
|
const TYPES = ['feature', 'bugfix', 'refactor', 'docs', 'chore'] as const;
|
|
@@ -30,6 +32,8 @@ type TaskCreateCandidateV1 = Readonly<{
|
|
|
30
32
|
priority: TaskPriority;
|
|
31
33
|
effort: TaskEffort;
|
|
32
34
|
description: string;
|
|
35
|
+
deliveryRemote?: string;
|
|
36
|
+
deliveryBaseRef?: string;
|
|
33
37
|
taskInput: Readonly<Record<typeof TASK_INPUT_KEYS[number], readonly string[]>>;
|
|
34
38
|
}>;
|
|
35
39
|
|
|
@@ -49,12 +53,14 @@ function payloadError(message: string): never {
|
|
|
49
53
|
throw new Error(`TASK_CREATE_PAYLOAD_INVALID: ${message}`);
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
function exactKeys(value: Record<string, unknown>, expected: readonly string[], label: string): void {
|
|
56
|
+
function exactKeys(value: Record<string, unknown>, expected: readonly string[], label: string, optional: readonly string[] = []): void {
|
|
53
57
|
const actual = Object.keys(value).sort();
|
|
54
58
|
const wanted = [...expected].sort();
|
|
55
|
-
|
|
59
|
+
const allowed = new Set([...expected, ...optional]);
|
|
60
|
+
if (actual.some((key) => !allowed.has(key))) {
|
|
56
61
|
payloadError(`${label} has unknown or missing fields`);
|
|
57
62
|
}
|
|
63
|
+
if (expected.some((key) => !actual.includes(key))) payloadError(`${label} has unknown or missing fields`);
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
function cleanString(value: unknown, label: string, maxBytes: number, oneLine = false): string {
|
|
@@ -78,7 +84,7 @@ function validateTaskCreateCandidate(value: unknown): TaskCreateCandidateV1 {
|
|
|
78
84
|
exactKeys(candidate, [
|
|
79
85
|
'version', 'idempotencyKey', 'agent', 'title', 'type', 'branchSlug',
|
|
80
86
|
'priority', 'effort', 'description', 'taskInput'
|
|
81
|
-
], 'candidate');
|
|
87
|
+
], 'candidate', ['deliveryRemote', 'deliveryBaseRef']);
|
|
82
88
|
if (candidate.version !== 1) payloadError('version must be 1');
|
|
83
89
|
const idempotencyKey = cleanString(candidate.idempotencyKey, 'idempotencyKey', 64, true);
|
|
84
90
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(idempotencyKey)) {
|
|
@@ -91,6 +97,14 @@ function validateTaskCreateCandidate(value: unknown): TaskCreateCandidateV1 {
|
|
|
91
97
|
payloadError('branchSlug must be lowercase ASCII kebab-case with length 3-64');
|
|
92
98
|
}
|
|
93
99
|
const description = cleanString(candidate.description, 'description', 8 * 1024);
|
|
100
|
+
const deliveryRemote = candidate.deliveryRemote === undefined
|
|
101
|
+
? undefined
|
|
102
|
+
: cleanString(candidate.deliveryRemote, 'deliveryRemote', 200, true);
|
|
103
|
+
const deliveryBaseRef = candidate.deliveryBaseRef === undefined
|
|
104
|
+
? undefined
|
|
105
|
+
: cleanString(candidate.deliveryBaseRef, 'deliveryBaseRef', 200, true);
|
|
106
|
+
if (deliveryRemote !== undefined && !validateRemote(deliveryRemote)) payloadError('deliveryRemote is invalid');
|
|
107
|
+
if (deliveryBaseRef !== undefined && !validateBaseRef(deliveryBaseRef)) payloadError('deliveryBaseRef is invalid');
|
|
94
108
|
if (!candidate.taskInput || typeof candidate.taskInput !== 'object' || Array.isArray(candidate.taskInput)) {
|
|
95
109
|
payloadError('taskInput must be an object');
|
|
96
110
|
}
|
|
@@ -111,6 +125,8 @@ function validateTaskCreateCandidate(value: unknown): TaskCreateCandidateV1 {
|
|
|
111
125
|
priority: enumValue(candidate.priority, PRIORITIES, 'priority'),
|
|
112
126
|
effort: enumValue(candidate.effort, EFFORTS, 'effort'),
|
|
113
127
|
description,
|
|
128
|
+
...(deliveryRemote === undefined ? {} : { deliveryRemote }),
|
|
129
|
+
...(deliveryBaseRef === undefined ? {} : { deliveryBaseRef }),
|
|
114
130
|
taskInput
|
|
115
131
|
};
|
|
116
132
|
}
|
|
@@ -127,6 +143,8 @@ function canonicalTaskCreateCandidate(candidate: TaskCreateCandidateV1): string
|
|
|
127
143
|
priority: valid.priority,
|
|
128
144
|
effort: valid.effort,
|
|
129
145
|
description: valid.description,
|
|
146
|
+
deliveryRemote: valid.deliveryRemote ?? null,
|
|
147
|
+
deliveryBaseRef: valid.deliveryBaseRef ?? null,
|
|
130
148
|
taskInput: Object.fromEntries(TASK_INPUT_KEYS.map((key) => [key, valid.taskInput[key]]))
|
|
131
149
|
});
|
|
132
150
|
}
|
|
@@ -170,6 +188,7 @@ function renderTask(params: Readonly<{
|
|
|
170
188
|
candidate: TaskCreateCandidateV1;
|
|
171
189
|
taskId: string;
|
|
172
190
|
project: string;
|
|
191
|
+
delivery: { remote: string; baseRef: string };
|
|
173
192
|
timestamp: string;
|
|
174
193
|
agentInfraVersion: string;
|
|
175
194
|
keyDigest: string;
|
|
@@ -194,7 +213,11 @@ function renderTask(params: Readonly<{
|
|
|
194
213
|
'target_date:',
|
|
195
214
|
'current_step: requirement-analysis',
|
|
196
215
|
`assigned_to: ${candidate.agent}`,
|
|
197
|
-
|
|
216
|
+
`pr_delivery_fact: ${JSON.stringify(encodePrDeliveryFact(buildUnboundFact()))}`,
|
|
217
|
+
`delivery_remote: ${candidate.deliveryRemote ?? params.delivery.remote}`,
|
|
218
|
+
`delivery_base_ref: ${candidate.deliveryBaseRef ?? params.delivery.baseRef}`,
|
|
219
|
+
'checkpoint_commit:',
|
|
220
|
+
'delivery_remote_head:',
|
|
198
221
|
`task_create_key_digest: ${params.keyDigest}`,
|
|
199
222
|
`task_create_candidate_digest: ${params.candidateDigest}`,
|
|
200
223
|
'---'
|
|
@@ -219,12 +242,14 @@ function renderTask(params: Readonly<{
|
|
|
219
242
|
return `${content.trimEnd()}\n`;
|
|
220
243
|
}
|
|
221
244
|
|
|
222
|
-
function readProject(repoRoot: string): string {
|
|
223
|
-
const config = JSON.parse(fs.readFileSync(path.join(repoRoot, '.agents', '.airc.json'), 'utf8')) as { project?: unknown };
|
|
245
|
+
function readProject(repoRoot: string): { project: string; delivery: { remote: string; baseRef: string } } {
|
|
246
|
+
const config = JSON.parse(fs.readFileSync(path.join(repoRoot, '.agents', '.airc.json'), 'utf8')) as { project?: unknown; delivery?: { remote?: unknown; baseRef?: unknown } };
|
|
224
247
|
if (typeof config.project !== 'string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(config.project)) {
|
|
225
248
|
throw new Error('TASK_CREATE_CONFIG_INVALID: project is missing or invalid');
|
|
226
249
|
}
|
|
227
|
-
|
|
250
|
+
const delivery = readDeliveryDefaults(repoRoot);
|
|
251
|
+
if (!delivery.ok) throw new Error(`TASK_CREATE_CONFIG_INVALID: ${delivery.message}`);
|
|
252
|
+
return { project: config.project, delivery: delivery.value };
|
|
228
253
|
}
|
|
229
254
|
|
|
230
255
|
function assertRealDirectory(directory: string, code: string): void {
|
|
@@ -305,7 +330,7 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
|
|
|
305
330
|
ensureRealDirectory(path.join(repoRoot, '.agents'), 'TASK_CREATE_WORKSPACE_INVALID');
|
|
306
331
|
ensureRealDirectory(workspaceRoot, 'TASK_CREATE_WORKSPACE_INVALID');
|
|
307
332
|
ensureRealDirectory(activeRoot, 'TASK_CREATE_WORKSPACE_INVALID');
|
|
308
|
-
const
|
|
333
|
+
const projectConfig = readProject(repoRoot);
|
|
309
334
|
const canonical = canonicalTaskCreateCandidate(candidate);
|
|
310
335
|
const candidateDigest = sha256(canonical);
|
|
311
336
|
const keyDigest = sha256(candidate.idempotencyKey);
|
|
@@ -346,7 +371,7 @@ function createLocalTask(value: unknown, options: LocalTaskCreateOptions): Local
|
|
|
346
371
|
const templateStat = fs.lstatSync(templatePath);
|
|
347
372
|
if (!templateStat.isFile() || templateStat.isSymbolicLink()) throw new Error('TASK_CREATE_TEMPLATE_INVALID');
|
|
348
373
|
const rendered = renderTask({
|
|
349
|
-
template: fs.readFileSync(templatePath, 'utf8'), candidate, taskId, project, timestamp,
|
|
374
|
+
template: fs.readFileSync(templatePath, 'utf8'), candidate, taskId, project: projectConfig.project, delivery: projectConfig.delivery, timestamp,
|
|
350
375
|
agentInfraVersion, keyDigest, candidateDigest
|
|
351
376
|
});
|
|
352
377
|
const temporary = path.join(workspaceRoot, `.task-create.tmp.${process.pid}.${randomUUID()}`);
|
|
@@ -2,6 +2,8 @@ import { isValidAgentInfraVersion } from '../version.ts';
|
|
|
2
2
|
import { parseTypedTaskFrontmatter } from './frontmatter.ts';
|
|
3
3
|
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, validateLedgerRows } from './ledger.ts';
|
|
4
4
|
import type { LedgerDocument } from './ledger.ts';
|
|
5
|
+
import { parseInvalidationDocument } from './invalidation.ts';
|
|
6
|
+
import type { InvalidationDocument } from './invalidation.ts';
|
|
5
7
|
|
|
6
8
|
type CurrentTaskContractErrorCode = 'TASK_DOCUMENT_INVALID' | 'TASK_CURRENT_CONTRACT_INVALID';
|
|
7
9
|
type CurrentTaskContractResult =
|
|
@@ -9,6 +11,7 @@ type CurrentTaskContractResult =
|
|
|
9
11
|
ok: true;
|
|
10
12
|
metadata: ReturnType<typeof parseTypedTaskFrontmatter>;
|
|
11
13
|
ledger: LedgerDocument;
|
|
14
|
+
invalidation: InvalidationDocument;
|
|
12
15
|
}
|
|
13
16
|
| {
|
|
14
17
|
ok: false;
|
|
@@ -42,7 +45,9 @@ function validateCurrentTaskContract(content: string): CurrentTaskContractResult
|
|
|
42
45
|
}
|
|
43
46
|
const invalid = validateLedgerRows(ledger.rows);
|
|
44
47
|
if (invalid) return { ok: false, code: 'TASK_CURRENT_CONTRACT_INVALID', message: `${invalid.code}: ${invalid.message}` };
|
|
45
|
-
|
|
48
|
+
const invalidation = parseInvalidationDocument(content);
|
|
49
|
+
if (!invalidation.ok) return { ok: false, code: 'TASK_CURRENT_CONTRACT_INVALID', message: `${invalidation.code}: ${invalidation.message}` };
|
|
50
|
+
return { ok: true, metadata, ledger, invalidation: invalidation.document };
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
export { validateCurrentTaskContract };
|