@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
|
@@ -36,7 +36,6 @@ type DecisionDetailResolution =
|
|
|
36
36
|
type DecisionDetailDuplicate = {
|
|
37
37
|
id: string;
|
|
38
38
|
blocks: DecisionDetailBlock[];
|
|
39
|
-
repairable: boolean;
|
|
40
39
|
};
|
|
41
40
|
|
|
42
41
|
type DecisionDetailInspection =
|
|
@@ -49,15 +48,6 @@ type DecisionDetailInspection =
|
|
|
49
48
|
duplicates: DecisionDetailDuplicate[];
|
|
50
49
|
};
|
|
51
50
|
|
|
52
|
-
type DecisionDetailRepair =
|
|
53
|
-
| { ok: true; changed: boolean; content: string }
|
|
54
|
-
| {
|
|
55
|
-
ok: false;
|
|
56
|
-
code: 'DECISION_DETAIL_AMBIGUOUS';
|
|
57
|
-
message: string;
|
|
58
|
-
content: string;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
51
|
const DECISION_ID = /^(AN|PL|CD|HD)-[1-9]\d*(?=$|[\s::])/i;
|
|
62
52
|
const HEADING = /^ {0,3}(#{1,6})[ \t]+(.+?)\s*$/;
|
|
63
53
|
const FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})(.*)$/;
|
|
@@ -172,32 +162,8 @@ function resolveDecisionDetail(content: string, rowId: string, anchor: string):
|
|
|
172
162
|
return { status: 'found', block: matches[0]! };
|
|
173
163
|
}
|
|
174
164
|
|
|
175
|
-
const INFORMAL_SHORT_HEADING = /^(?:AN|PL|CD|HD)-[1-9]\d*[::]\s*(?:上一轮)?简短复核$/i;
|
|
176
|
-
const INFORMAL_SHORT_FRAGMENT = /^简短(?:结论|说明)$/;
|
|
177
|
-
|
|
178
|
-
function isInformalShortBlock(block: DecisionDetailBlock): boolean {
|
|
179
|
-
if (block.canonical || block.content.length > 600) return false;
|
|
180
|
-
if (!INFORMAL_SHORT_HEADING.test(block.heading)) return false;
|
|
181
|
-
const scanned = scanVisibleMarkdown(block.content);
|
|
182
|
-
const bodyLines = scanned.lines
|
|
183
|
-
.filter((line) => !scanned.headings.some((heading) => heading.start === line.start) && line.text.trim())
|
|
184
|
-
.map((line) => line.text);
|
|
185
|
-
if (bodyLines.length === 0 || bodyLines.length > 2) return false;
|
|
186
|
-
const fragments = bodyLines.map((line) => line.match(/^\s*[-*]\s+([^\s].*?)\s*$/)?.[1] ?? null);
|
|
187
|
-
if (fragments.some((fragment) => fragment === null)) return false;
|
|
188
|
-
return fragments.every((fragment) => INFORMAL_SHORT_FRAGMENT.test(fragment!));
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function hasDirectPrecedingAnchor(block: DecisionDetailBlock, scanned: VisibleMarkdown): boolean {
|
|
192
|
-
return scanned.anchors.some((anchor) => (
|
|
193
|
-
anchor.end < block.start
|
|
194
|
-
&& !scanned.lines.some((line) => line.start > anchor.end && line.start < block.start && line.text.trim())
|
|
195
|
-
));
|
|
196
|
-
}
|
|
197
|
-
|
|
198
165
|
function inspectDecisionDetailDuplicates(content: string): DecisionDetailInspection {
|
|
199
166
|
const blocks = parseDecisionDetailBlocks(content);
|
|
200
|
-
const scanned = scanVisibleMarkdown(content);
|
|
201
167
|
const grouped = new Map<string, DecisionDetailBlock[]>();
|
|
202
168
|
for (const block of blocks) {
|
|
203
169
|
const key = block.id.toUpperCase();
|
|
@@ -208,16 +174,12 @@ function inspectDecisionDetailDuplicates(content: string): DecisionDetailInspect
|
|
|
208
174
|
const duplicates: DecisionDetailDuplicate[] = [];
|
|
209
175
|
for (const group of grouped.values()) {
|
|
210
176
|
if (group.length < 2) continue;
|
|
211
|
-
|
|
212
|
-
const repairable = canonical.length === 1 && group.every((block) => (
|
|
213
|
-
block.canonical || (!hasDirectPrecedingAnchor(block, scanned) && isInformalShortBlock(block))
|
|
214
|
-
));
|
|
215
|
-
duplicates.push({ id: canonical[0]?.id ?? group[0]!.id, blocks: group, repairable });
|
|
177
|
+
duplicates.push({ id: group[0]!.id, blocks: group });
|
|
216
178
|
}
|
|
217
179
|
if (duplicates.length === 0) return { ok: true, blocks };
|
|
218
180
|
const summary = duplicates.map((duplicate) => {
|
|
219
181
|
const types = duplicate.blocks.map((block) => block.canonical ? 'canonical' : 'informal').join(', ');
|
|
220
|
-
return `${duplicate.id} (${duplicate.blocks.length} blocks: ${types}
|
|
182
|
+
return `${duplicate.id} (${duplicate.blocks.length} blocks: ${types})`;
|
|
221
183
|
}).join(', ');
|
|
222
184
|
return {
|
|
223
185
|
ok: false,
|
|
@@ -228,26 +190,6 @@ function inspectDecisionDetailDuplicates(content: string): DecisionDetailInspect
|
|
|
228
190
|
};
|
|
229
191
|
}
|
|
230
192
|
|
|
231
|
-
function repairDecisionDetailDuplicates(content: string): DecisionDetailRepair {
|
|
232
|
-
const inspection = inspectDecisionDetailDuplicates(content);
|
|
233
|
-
if (inspection.ok) return { ok: true, changed: false, content };
|
|
234
|
-
const unsafe = inspection.duplicates.filter((duplicate) => !duplicate.repairable);
|
|
235
|
-
if (unsafe.length > 0) {
|
|
236
|
-
return {
|
|
237
|
-
ok: false,
|
|
238
|
-
code: 'DECISION_DETAIL_AMBIGUOUS',
|
|
239
|
-
message: `Cannot safely repair duplicate decision detail ids: ${unsafe.map((duplicate) => duplicate.id).join(', ')}`,
|
|
240
|
-
content
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
const removals = inspection.duplicates.flatMap((duplicate) => duplicate.blocks.filter((block) => !block.canonical));
|
|
244
|
-
let repaired = content;
|
|
245
|
-
for (const block of removals.sort((left, right) => right.start - left.start)) {
|
|
246
|
-
repaired = repaired.slice(0, block.start) + repaired.slice(block.end);
|
|
247
|
-
}
|
|
248
|
-
return { ok: true, changed: repaired !== content, content: repaired };
|
|
249
|
-
}
|
|
250
|
-
|
|
251
193
|
type FieldKey = 'decision' | 'why' | 'impact' | 'risk' | 'analogy' | 'recommend' | 'reason' | 'implementation';
|
|
252
194
|
|
|
253
195
|
const FIELD_KEYS: Record<string, FieldKey> = {
|
|
@@ -328,13 +270,15 @@ export {
|
|
|
328
270
|
normalizeDecisionDetailForDisplay,
|
|
329
271
|
parseDecisionDetailBlocks,
|
|
330
272
|
parseDecisionEvidence,
|
|
331
|
-
|
|
332
|
-
|
|
273
|
+
resolveDecisionDetail,
|
|
274
|
+
scanVisibleMarkdown
|
|
333
275
|
};
|
|
334
276
|
export type {
|
|
335
277
|
DecisionDetailBlock,
|
|
336
278
|
DecisionDetailDuplicate,
|
|
337
279
|
DecisionDetailInspection,
|
|
338
|
-
|
|
339
|
-
|
|
280
|
+
DecisionDetailResolution,
|
|
281
|
+
SourceLine,
|
|
282
|
+
VisibleHeading,
|
|
283
|
+
VisibleMarkdown
|
|
340
284
|
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
export type DeliveryTarget = Readonly<{
|
|
6
|
+
remote: string;
|
|
7
|
+
baseRef: string;
|
|
8
|
+
}>;
|
|
9
|
+
|
|
10
|
+
export type DeliveryTargetBindingInput = Readonly<{
|
|
11
|
+
defaults?: Partial<DeliveryTarget>;
|
|
12
|
+
existing?: Partial<DeliveryTarget>;
|
|
13
|
+
explicit?: Partial<DeliveryTarget>;
|
|
14
|
+
}>;
|
|
15
|
+
|
|
16
|
+
export type DeliveryTargetResult =
|
|
17
|
+
| { ok: true; value: DeliveryTarget }
|
|
18
|
+
| { ok: false; code: string; message: string };
|
|
19
|
+
|
|
20
|
+
export type DiffBaseResult =
|
|
21
|
+
| { ok: true; diffBase: string }
|
|
22
|
+
| { ok: false; code: string; message: string };
|
|
23
|
+
|
|
24
|
+
const REMOTE_RE = /^(?!-)(?!.*\.\.)(?!.*[~^:?*\\\s])[A-Za-z0-9._/-]+$/;
|
|
25
|
+
const REF_RE = /^(?!-)(?!.*\.\.)(?!.*(?:^|\/)\.)(?!.*[~^:?*\\\s])[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*$/;
|
|
26
|
+
|
|
27
|
+
function validateRemote(value: unknown): value is string {
|
|
28
|
+
return typeof value === 'string' && value.length > 0 && REMOTE_RE.test(value);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validateBaseRef(value: unknown): value is string {
|
|
32
|
+
return typeof value === 'string' && value.length > 0 && !value.startsWith('refs/') && REF_RE.test(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function invalid(field: string, value: unknown): DeliveryTargetResult {
|
|
36
|
+
return { ok: false, code: 'DELIVERY_TARGET_INVALID', message: `${field} is invalid: ${String(value ?? '')}` };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function bindDeliveryTarget(input: DeliveryTargetBindingInput): DeliveryTargetResult {
|
|
40
|
+
const remote = input.existing?.remote ?? input.explicit?.remote ?? input.defaults?.remote;
|
|
41
|
+
const baseRef = input.existing?.baseRef ?? input.explicit?.baseRef ?? input.defaults?.baseRef;
|
|
42
|
+
if (input.existing?.remote !== undefined && input.explicit?.remote !== undefined && input.existing.remote !== input.explicit.remote) {
|
|
43
|
+
return { ok: false, code: 'DELIVERY_TARGET_CONFLICT', message: 'Explicit delivery remote does not match the task binding' };
|
|
44
|
+
}
|
|
45
|
+
if (input.existing?.baseRef !== undefined && input.explicit?.baseRef !== undefined && input.existing.baseRef !== input.explicit.baseRef) {
|
|
46
|
+
return { ok: false, code: 'DELIVERY_TARGET_CONFLICT', message: 'Explicit delivery base ref does not match the task binding' };
|
|
47
|
+
}
|
|
48
|
+
if (!validateRemote(remote)) return invalid('delivery remote', remote);
|
|
49
|
+
if (!validateBaseRef(baseRef)) return invalid('delivery base ref', baseRef);
|
|
50
|
+
return { ok: true, value: { remote, baseRef } };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readDeliveryDefaults(repoRoot: string): DeliveryTargetResult {
|
|
54
|
+
try {
|
|
55
|
+
const configPath = path.join(repoRoot, '.agents', '.airc.json');
|
|
56
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8')) as Record<string, unknown>;
|
|
57
|
+
if (!config.delivery || typeof config.delivery !== 'object' || Array.isArray(config.delivery)) {
|
|
58
|
+
return { ok: false, code: 'DELIVERY_CONFIG_INVALID', message: 'delivery config must declare remote and baseRef' };
|
|
59
|
+
}
|
|
60
|
+
const delivery = config.delivery as Record<string, unknown>;
|
|
61
|
+
if (typeof delivery.remote !== 'string' || typeof delivery.baseRef !== 'string') {
|
|
62
|
+
return { ok: false, code: 'DELIVERY_CONFIG_INVALID', message: 'delivery config must declare remote and baseRef' };
|
|
63
|
+
}
|
|
64
|
+
return bindDeliveryTarget({
|
|
65
|
+
defaults: {
|
|
66
|
+
remote: delivery.remote,
|
|
67
|
+
baseRef: delivery.baseRef
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
} catch (error) {
|
|
71
|
+
return { ok: false, code: 'DELIVERY_CONFIG_INVALID', message: error instanceof Error ? error.message : String(error) };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveDeliveryTarget(
|
|
76
|
+
repoRoot: string,
|
|
77
|
+
existing: Partial<DeliveryTarget> = {},
|
|
78
|
+
explicit: Partial<DeliveryTarget> = {}
|
|
79
|
+
): DeliveryTargetResult {
|
|
80
|
+
const defaults = readDeliveryDefaults(repoRoot);
|
|
81
|
+
if (!defaults.ok) return defaults;
|
|
82
|
+
return bindDeliveryTarget({ defaults: defaults.value, existing, explicit });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function gitText(repoRoot: string, args: readonly string[]): string {
|
|
86
|
+
return execFileSync('git', [...args], {
|
|
87
|
+
cwd: repoRoot,
|
|
88
|
+
encoding: 'utf8',
|
|
89
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
90
|
+
}).trim();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function resolveTargetHead(repoRoot: string, target: DeliveryTarget): DeliveryTargetResult & { head?: string } {
|
|
94
|
+
try {
|
|
95
|
+
const output = gitText(repoRoot, ['ls-remote', '--refs', target.remote, `refs/heads/${target.baseRef}`]);
|
|
96
|
+
const head = output.split(/\s+/)[0] ?? '';
|
|
97
|
+
if (!/^[a-f0-9]{40}$/i.test(head)) {
|
|
98
|
+
return { ok: false, code: 'DELIVERY_TARGET_UNAVAILABLE', message: `Delivery target ${target.remote}/${target.baseRef} is unavailable` };
|
|
99
|
+
}
|
|
100
|
+
try { gitText(repoRoot, ['cat-file', '-e', `${head}^{commit}`]); }
|
|
101
|
+
catch {
|
|
102
|
+
return { ok: false, code: 'DELIVERY_TARGET_OBJECT_MISSING', message: `Delivery target commit ${head} is not available locally` };
|
|
103
|
+
}
|
|
104
|
+
return { ok: true, value: target, head };
|
|
105
|
+
} catch (error) {
|
|
106
|
+
return { ok: false, code: 'DELIVERY_TARGET_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function resolveDiffBase(repoRoot: string, reviewedHead: string, targetHead: string): DiffBaseResult {
|
|
111
|
+
try {
|
|
112
|
+
const diffBase = gitText(repoRoot, ['merge-base', reviewedHead, targetHead]);
|
|
113
|
+
if (!/^[a-f0-9]{40}$/i.test(diffBase)) {
|
|
114
|
+
return { ok: false, code: 'DELIVERY_DIFF_BASE_UNAVAILABLE', message: 'Git returned an invalid diff base' };
|
|
115
|
+
}
|
|
116
|
+
return { ok: true, diffBase };
|
|
117
|
+
} catch (error) {
|
|
118
|
+
return { ok: false, code: 'DELIVERY_DIFF_BASE_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export {
|
|
123
|
+
bindDeliveryTarget,
|
|
124
|
+
readDeliveryDefaults,
|
|
125
|
+
resolveDeliveryTarget,
|
|
126
|
+
resolveTargetHead,
|
|
127
|
+
resolveDiffBase,
|
|
128
|
+
validateBaseRef,
|
|
129
|
+
validateRemote
|
|
130
|
+
};
|
|
@@ -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 };
|