@fitlab-ai/agent-infra 0.9.10 → 0.9.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -9
- package/README.zh-CN.md +19 -9
- package/bin/cli.ts +17 -4
- package/dist/bin/cli.js +19 -4
- package/dist/bin/internal-cli.js +55 -4
- package/dist/lib/agent-client.js +0 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
- package/dist/lib/agent-clients/adapters/traecli.js +7 -8
- package/dist/lib/agent-clients/config.js +14 -99
- package/dist/lib/agent-clients/reconcile.js +1 -11
- package/dist/lib/agent-clients/schema.js +5 -4
- package/dist/lib/agent-clients/tokens.js +5 -15
- package/dist/lib/decide.js +14 -30
- package/dist/lib/defaults.json +8 -0
- package/dist/lib/git/workflow.js +43 -1
- package/dist/lib/init.js +3 -2
- package/dist/lib/internal/git-workflow.js +8 -1
- package/dist/lib/internal/platform-pr.js +8 -2
- package/dist/lib/internal/release-workflow.js +0 -1
- package/dist/lib/internal/task-delivery.js +59 -0
- package/dist/lib/internal/task-finalization.js +19 -9
- package/dist/lib/internal/task-ledger.js +8 -2
- package/dist/lib/internal/task-lifecycle.js +17 -93
- package/dist/lib/internal/task-orchestration.js +30 -216
- package/dist/lib/internal/task-validate.js +2 -1
- package/dist/lib/merge.js +205 -103
- package/dist/lib/platform/github-client.js +7 -1
- package/dist/lib/platform/pr-checks.js +8 -5
- package/dist/lib/platform/pr-summary.js +12 -8
- package/dist/lib/platform/pull-requests.js +393 -91
- package/dist/lib/platform/verification-required.js +13 -2
- package/dist/lib/platform/verification-sync.js +9 -4
- package/dist/lib/pr-review/evidence-grading.js +12 -4
- package/dist/lib/process-data/normalize.js +2 -9
- package/dist/lib/run/host.js +46 -16
- package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
- package/dist/lib/sandbox/commands/create.js +3 -3
- package/dist/lib/sandbox/commands/enter.js +18 -4
- package/dist/lib/sandbox/config.js +8 -12
- package/dist/lib/sandbox/control/executor.js +131 -39
- package/dist/lib/sandbox/control/lifecycle.js +55 -24
- package/dist/lib/sandbox/control/server.js +18 -16
- package/dist/lib/sandbox/mounts.js +3 -2
- package/dist/lib/sandbox/recovery.js +123 -22
- package/dist/lib/sandbox/workspace-identity.js +14 -0
- package/dist/lib/sandbox/workspace-view.js +8 -9
- package/dist/lib/self-update.js +278 -0
- package/dist/lib/server/process-state.js +2 -2
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +6 -4
- package/dist/lib/task/codex-orchestration.js +1 -1
- package/dist/lib/task/command-options.js +1 -3
- package/dist/lib/task/commands/cat.js +0 -5
- package/dist/lib/task/commands/decisions.js +76 -92
- package/dist/lib/task/commands/files.js +6 -6
- package/dist/lib/task/commands/grep.js +25 -61
- package/dist/lib/task/commands/issue-body.js +6 -6
- package/dist/lib/task/commands/log.js +25 -10
- package/dist/lib/task/commands/show.js +5 -6
- package/dist/lib/task/commands/status.js +8 -8
- package/dist/lib/task/commit-intent.js +100 -0
- package/dist/lib/task/commit-operation.js +181 -91
- package/dist/lib/task/commit-policy.js +1 -1
- package/dist/lib/task/control-authority.js +431 -0
- package/dist/lib/task/create.js +39 -8
- package/dist/lib/task/current-contract.js +35 -0
- package/dist/lib/task/decision-details.js +218 -0
- package/dist/lib/task/decision-intents.js +5 -2
- package/dist/lib/task/delivery-target.js +97 -0
- package/dist/lib/task/delivery.js +153 -0
- package/dist/lib/task/events.js +11 -5
- package/dist/lib/task/human-override.js +5 -2
- package/dist/lib/task/index.js +19 -20
- package/dist/lib/task/ledger-intents.js +8 -9
- package/dist/lib/task/ledger.js +13 -6
- package/dist/lib/task/lifecycle.js +122 -9
- package/dist/lib/task/orchestration.js +105 -75
- package/dist/lib/task/pr-delivery-fact.js +182 -0
- package/dist/lib/task/review-artifacts.js +31 -12
- package/dist/lib/task/review-finalization.js +13 -4
- package/dist/lib/task/review-fingerprint.js +8 -0
- package/dist/lib/task/verification-engine.js +86 -39
- package/dist/lib/task/verification-review.js +2 -2
- package/dist/lib/task/workspace-snapshot.js +5 -11
- package/dist/lib/task/write.js +11 -1
- package/dist/lib/update.js +13 -35
- package/dist/lib/version.js +5 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +0 -1
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
- package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
- package/lib/agent-clients/adapters/traecli.ts +7 -8
- package/lib/agent-clients/config.ts +14 -144
- package/lib/agent-clients/reconcile.ts +1 -12
- package/lib/agent-clients/schema.ts +5 -4
- package/lib/agent-clients/tokens.ts +5 -18
- package/lib/decide.ts +12 -22
- package/lib/defaults.json +8 -0
- package/lib/git/workflow.ts +38 -1
- package/lib/init.ts +5 -2
- package/lib/internal/git-workflow.ts +7 -1
- package/lib/internal/platform-pr.ts +8 -1
- package/lib/internal/release-workflow.ts +0 -1
- package/lib/internal/task-delivery.ts +53 -0
- package/lib/internal/task-finalization.ts +27 -13
- package/lib/internal/task-ledger.ts +8 -2
- package/lib/internal/task-lifecycle.ts +33 -76
- package/lib/internal/task-orchestration.ts +33 -190
- package/lib/internal/task-validate.ts +3 -2
- package/lib/merge.ts +236 -120
- package/lib/platform/github-client.ts +8 -1
- package/lib/platform/pr-checks.ts +7 -5
- package/lib/platform/pr-summary.ts +10 -7
- package/lib/platform/pull-requests.ts +425 -87
- package/lib/platform/verification-required.ts +12 -2
- package/lib/platform/verification-sync.ts +9 -4
- package/lib/pr-review/evidence-grading.ts +7 -4
- package/lib/process-data/normalize.ts +2 -8
- package/lib/run/host.ts +63 -14
- package/lib/sandbox/agent-client-reconciler.ts +5 -15
- package/lib/sandbox/commands/create.ts +4 -4
- package/lib/sandbox/commands/enter.ts +24 -4
- package/lib/sandbox/config.ts +9 -14
- package/lib/sandbox/control/container-identity.ts +2 -2
- package/lib/sandbox/control/executor.ts +148 -51
- package/lib/sandbox/control/lifecycle.ts +59 -31
- package/lib/sandbox/control/protocol.ts +1 -3
- package/lib/sandbox/control/server.ts +15 -12
- package/lib/sandbox/control/state.ts +7 -7
- package/lib/sandbox/mounts.ts +4 -3
- package/lib/sandbox/recovery.ts +162 -34
- package/lib/sandbox/tools.ts +1 -1
- package/lib/sandbox/workspace-identity.ts +28 -0
- package/lib/sandbox/workspace-view.ts +14 -10
- package/lib/self-update.ts +341 -0
- package/lib/server/process-state.ts +2 -2
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +6 -1
- package/lib/task/artifact-lifecycle.ts +7 -5
- package/lib/task/codex-orchestration.ts +2 -1
- package/lib/task/command-options.ts +1 -2
- package/lib/task/commands/cat.ts +0 -4
- package/lib/task/commands/decisions.ts +80 -75
- package/lib/task/commands/files.ts +6 -6
- package/lib/task/commands/grep.ts +23 -51
- package/lib/task/commands/issue-body.ts +6 -6
- package/lib/task/commands/log.ts +22 -10
- package/lib/task/commands/show.ts +5 -6
- package/lib/task/commands/status.ts +8 -8
- package/lib/task/commit-intent.ts +137 -0
- package/lib/task/commit-operation.ts +161 -79
- package/lib/task/commit-policy.ts +1 -2
- package/lib/task/control-authority.ts +563 -0
- package/lib/task/create.ts +40 -10
- package/lib/task/current-contract.ts +49 -0
- package/lib/task/decision-details.ts +280 -0
- package/lib/task/decision-intents.ts +7 -2
- package/lib/task/delivery-target.ts +130 -0
- package/lib/task/delivery.ts +160 -0
- package/lib/task/events.ts +10 -5
- package/lib/task/human-override.ts +5 -2
- package/lib/task/index.ts +19 -20
- package/lib/task/ledger-intents.ts +6 -8
- package/lib/task/ledger.ts +17 -7
- package/lib/task/lifecycle.ts +113 -4
- package/lib/task/orchestration.ts +102 -80
- package/lib/task/pr-delivery-fact.ts +254 -0
- package/lib/task/review-artifacts.ts +53 -15
- package/lib/task/review-finalization.ts +21 -5
- package/lib/task/review-fingerprint.ts +10 -0
- package/lib/task/verification-engine.ts +78 -46
- package/lib/task/verification-review.ts +3 -1
- package/lib/task/workspace-snapshot.ts +4 -13
- package/lib/task/write.ts +20 -1
- package/lib/update.ts +14 -38
- package/lib/version.ts +6 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +5 -3
- package/templates/.agents/README.zh-CN.md +5 -3
- package/templates/.agents/rules/README.en.md +4 -2
- package/templates/.agents/rules/README.zh-CN.md +4 -2
- package/templates/.agents/rules/compatibility-policy.en.md +5 -4
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
- package/templates/.agents/rules/create-issue.en.md +11 -2
- package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
- package/templates/.agents/rules/human-decision-context.en.md +9 -7
- package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
- package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +1 -1
- package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +26 -5
- package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
- package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
- package/templates/.agents/rules/milestone-inference.en.md +12 -2
- package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
- package/templates/.agents/rules/next-step-output.en.md +3 -3
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
- package/templates/.agents/rules/pr-sync.en.md +23 -14
- package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
- package/templates/.agents/rules/release-commands.en.md +42 -3
- package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
- package/templates/.agents/rules/review-handshake.en.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/security-alerts.en.md +17 -2
- package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
- package/templates/.agents/rules/sync-content-generation.en.md +8 -0
- package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
- package/templates/.agents/rules/task-management.en.md +2 -3
- package/templates/.agents/rules/task-management.zh-CN.md +2 -3
- package/templates/.agents/scripts/security-alerts.github.sh +151 -0
- package/templates/.agents/scripts/security-alerts.sh +31 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/commit/SKILL.en.md +30 -10
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
- package/templates/.agents/skills/commit/config/verify.json +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +13 -13
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
- package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
- package/templates/.agents/skills/create-task/SKILL.en.md +2 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +8 -5
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +144 -109
- package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
- package/templates/.agents/templates/task.en.md +6 -2
- package/templates/.agents/templates/task.zh-CN.md +6 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/feature-development.en.yaml +9 -8
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/refactoring.en.yaml +9 -8
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
- package/templates/.claude/commands/analyze-task.en.md +1 -1
- package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.claude/commands/block-task.en.md +1 -1
- package/templates/.claude/commands/block-task.zh-CN.md +1 -1
- package/templates/.claude/commands/cancel-task.en.md +1 -1
- package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
- package/templates/.claude/commands/check-task.en.md +1 -1
- package/templates/.claude/commands/check-task.zh-CN.md +1 -1
- package/templates/.claude/commands/code-task.en.md +1 -1
- package/templates/.claude/commands/code-task.zh-CN.md +1 -1
- package/templates/.claude/commands/commit.en.md +1 -1
- package/templates/.claude/commands/commit.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-task.en.md +1 -1
- package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
- package/templates/.claude/commands/create-pr.en.md +1 -1
- package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
- package/templates/.claude/commands/plan-task.en.md +1 -1
- package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
- package/templates/.claude/commands/review-analysis.en.md +1 -1
- package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.claude/commands/review-code.en.md +1 -1
- package/templates/.claude/commands/review-code.zh-CN.md +1 -1
- package/templates/.claude/commands/review-plan.en.md +1 -1
- package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/run-task.en.md +1 -1
- package/templates/.claude/commands/run-task.zh-CN.md +1 -1
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
- package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
- package/dist/lib/builtin-tuis.js +0 -40
- package/lib/builtin-tuis.ts +0 -56
- package/templates/.agents/rules/create-issue.github.en.md +0 -14
- package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
- package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
- package/templates/.agents/rules/issue-sync.github.en.md +0 -40
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
- package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
- package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
- package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
- package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
- package/templates/.agents/rules/pr-sync.github.en.md +0 -43
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
- package/templates/.agents/rules/release-commands.github.en.md +0 -38
- package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
- package/templates/.agents/rules/security-alerts.github.en.md +0 -43
- package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
|
@@ -3,9 +3,11 @@ import path from 'node:path';
|
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
|
|
5
5
|
import { parseTypedTaskFrontmatter } from './frontmatter.ts';
|
|
6
|
-
import {
|
|
6
|
+
import { validateCurrentTaskContract } from './current-contract.ts';
|
|
7
|
+
import { parseReviewSummary, resolveCanonicalVerdict } from './review-artifacts.ts';
|
|
7
8
|
import { inspectArtifactDirectory } from './artifact-lifecycle.ts';
|
|
8
|
-
import {
|
|
9
|
+
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
|
|
10
|
+
import type { LedgerDocument } from './ledger.ts';
|
|
9
11
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
10
12
|
import {
|
|
11
13
|
activateDelegation,
|
|
@@ -27,7 +29,6 @@ import type {
|
|
|
27
29
|
} from './delegation-receipts.ts';
|
|
28
30
|
import { normalizeAgentClients } from '../agent-clients/config.ts';
|
|
29
31
|
import { resolveAgentRuntimeStoreRoot } from '../runtime/agent-runtime.ts';
|
|
30
|
-
import { inspectPlatformPullRequest } from '../platform/pull-requests.ts';
|
|
31
32
|
import {
|
|
32
33
|
getAgentClientCapability,
|
|
33
34
|
getAgentClientDelegationEvidence,
|
|
@@ -50,6 +51,8 @@ import { assertGitRepositoryBinding } from '../git/worktree-identity.ts';
|
|
|
50
51
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
51
52
|
import { hasActiveCodexLifecycleEvidence } from '../agent-clients/adapters/codex-lifecycle/store.ts';
|
|
52
53
|
import type { CodexCapabilityProvenanceDetail } from '../agent-clients/adapters/codex-lifecycle/capability-store.ts';
|
|
54
|
+
import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead } from './review-fingerprint.ts';
|
|
55
|
+
import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './delivery-target.ts';
|
|
53
56
|
|
|
54
57
|
type OrchestrationStatus = 'running' | 'paused' | 'completed';
|
|
55
58
|
type ModelPolicySource = Readonly<{
|
|
@@ -82,8 +85,8 @@ type CleanCompletionEvidence = Readonly<{
|
|
|
82
85
|
headTree: string;
|
|
83
86
|
worktreeTree: string;
|
|
84
87
|
lastReviewedCommit: string;
|
|
85
|
-
prNumber: number;
|
|
86
|
-
prHead: string;
|
|
88
|
+
prNumber: number | null;
|
|
89
|
+
prHead: string | null;
|
|
87
90
|
}>;
|
|
88
91
|
type OrchestrationRun = Readonly<{
|
|
89
92
|
taskId: string;
|
|
@@ -271,9 +274,8 @@ function isCompletionEvidence(value: unknown): value is CleanCompletionEvidence
|
|
|
271
274
|
&& exactText(value.headTree)
|
|
272
275
|
&& exactText(value.worktreeTree)
|
|
273
276
|
&& exactText(value.lastReviewedCommit)
|
|
274
|
-
&& Number.isSafeInteger(value.prNumber)
|
|
275
|
-
&& (value.
|
|
276
|
-
&& exactText(value.prHead);
|
|
277
|
+
&& (value.prNumber === null || (Number.isSafeInteger(value.prNumber) && (value.prNumber as number) > 0))
|
|
278
|
+
&& (value.prHead === null || exactText(value.prHead));
|
|
277
279
|
}
|
|
278
280
|
|
|
279
281
|
const ORCHESTRATION_RUN_KEYS = [
|
|
@@ -521,6 +523,44 @@ function artifactName(family: string, round: number): string {
|
|
|
521
523
|
return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
|
|
522
524
|
}
|
|
523
525
|
|
|
526
|
+
function validateSavedCurrentDiffBase(
|
|
527
|
+
repoRoot: string,
|
|
528
|
+
metadata: ReturnType<typeof parseTypedTaskFrontmatter>,
|
|
529
|
+
taskDir: string,
|
|
530
|
+
reviewedHead: string
|
|
531
|
+
): Readonly<{ code: string; message: string }> | null {
|
|
532
|
+
const reviewRound = highestRound(taskDir, 'review-code');
|
|
533
|
+
const reviewContent = fs.readFileSync(path.join(taskDir, artifactName('review-code', reviewRound)), 'utf8');
|
|
534
|
+
const savedTargetHead = extractReviewTargetHead(reviewContent);
|
|
535
|
+
const savedDiffBase = extractReviewDiffBase(reviewContent);
|
|
536
|
+
const savedReviewedHead = extractReviewedHead(reviewContent) || extractReviewBaseline(reviewContent);
|
|
537
|
+
if (!savedTargetHead && !savedDiffBase && !savedReviewedHead) return null;
|
|
538
|
+
if (savedReviewedHead && savedReviewedHead !== reviewedHead) {
|
|
539
|
+
return { code: 'ORCHESTRATION_REVIEWED_HEAD_MISMATCH', message: 'review-code saved reviewed head does not match last_reviewed_commit' };
|
|
540
|
+
}
|
|
541
|
+
if (!savedTargetHead || !savedDiffBase) {
|
|
542
|
+
return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: 'review-code must save target head and diff base together' };
|
|
543
|
+
}
|
|
544
|
+
const savedBase = resolveDiffBase(repoRoot, reviewedHead, savedTargetHead);
|
|
545
|
+
if (!savedBase.ok) return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: savedBase.message };
|
|
546
|
+
if (savedBase.diffBase !== savedDiffBase) {
|
|
547
|
+
return { code: 'ORCHESTRATION_REVIEW_DIFF_BASE_MISMATCH', message: 'saved review diff base does not match reviewed head and saved target head' };
|
|
548
|
+
}
|
|
549
|
+
const target = resolveDeliveryTarget(repoRoot, {
|
|
550
|
+
remote: typeof metadata.delivery_remote === 'string' ? metadata.delivery_remote : undefined,
|
|
551
|
+
baseRef: typeof metadata.delivery_base_ref === 'string' ? metadata.delivery_base_ref : undefined
|
|
552
|
+
});
|
|
553
|
+
if (!target.ok) return { code: target.code, message: target.message };
|
|
554
|
+
const currentTarget = resolveTargetHead(repoRoot, target.value);
|
|
555
|
+
if (!currentTarget.ok) return { code: currentTarget.code, message: currentTarget.message };
|
|
556
|
+
const currentBase = resolveDiffBase(repoRoot, reviewedHead, currentTarget.head!);
|
|
557
|
+
if (!currentBase.ok) return { code: currentBase.code, message: currentBase.message };
|
|
558
|
+
if (currentBase.diffBase !== savedDiffBase) {
|
|
559
|
+
return { code: 'ORCHESTRATION_REVIEW_TARGET_CHANGED', message: 'current delivery target changed the reviewed diff base; re-run review-code' };
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
|
|
524
564
|
function hasReviewAfterArtifact(taskDir: string, family: string, reviewFamily: string): boolean {
|
|
525
565
|
const artifact = inspectArtifactDirectory(taskDir, family as 'analysis' | 'plan' | 'code');
|
|
526
566
|
const review = inspectArtifactDirectory(taskDir, reviewFamily as 'review-analysis' | 'review-plan' | 'review-code');
|
|
@@ -530,14 +570,20 @@ function hasReviewAfterArtifact(taskDir: string, family: string, reviewFamily: s
|
|
|
530
570
|
&& review.reviewedInput?.name === artifact.latest.name;
|
|
531
571
|
}
|
|
532
572
|
|
|
533
|
-
function latestReviewApproved(taskDir: string, family: 'review-analysis' | 'review-plan' | 'review-code'): boolean {
|
|
573
|
+
function latestReviewApproved(taskDir: string, family: 'review-analysis' | 'review-plan' | 'review-code'): { approved: boolean; error: string | null } {
|
|
534
574
|
const round = highestRound(taskDir, family);
|
|
535
|
-
if (round === 0) return false;
|
|
536
|
-
|
|
537
|
-
|
|
575
|
+
if (round === 0) return { approved: false, error: null };
|
|
576
|
+
let content: string;
|
|
577
|
+
try { content = fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8'); }
|
|
578
|
+
catch (error) { return { approved: false, error: String(error) }; }
|
|
579
|
+
const parsed = parseReviewSummary(content);
|
|
580
|
+
if (!parsed.ok) return { approved: false, error: `${parsed.code}: ${parsed.message}` };
|
|
581
|
+
const verdict = resolveCanonicalVerdict(parsed.summary);
|
|
582
|
+
if (!verdict.ok) return { approved: false, error: `${verdict.code}: ${verdict.message}` };
|
|
583
|
+
return { approved: verdict.verdict === 'Approved', error: null };
|
|
538
584
|
}
|
|
539
585
|
|
|
540
|
-
function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedModel' | 'requestedReasoningEffort'> | null {
|
|
586
|
+
function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedModel' | 'requestedReasoningEffort'> | { error: string } | null {
|
|
541
587
|
const analysisRound = highestRound(taskDir, 'analysis');
|
|
542
588
|
const analysisReviewRound = highestRound(taskDir, 'review-analysis');
|
|
543
589
|
const planRound = highestRound(taskDir, 'plan');
|
|
@@ -552,7 +598,9 @@ function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedMode
|
|
|
552
598
|
const round = analysisReviewRound + 1;
|
|
553
599
|
return { action: 'review-analysis', role: 'reviewer', stage: 'review-analysis', round, artifact: artifactName('review-analysis', round) };
|
|
554
600
|
}
|
|
555
|
-
|
|
601
|
+
const analysisVerdict = latestReviewApproved(taskDir, 'review-analysis');
|
|
602
|
+
if (analysisVerdict.error) return { error: analysisVerdict.error };
|
|
603
|
+
if (!analysisVerdict.approved) {
|
|
556
604
|
const round = analysisRound + 1;
|
|
557
605
|
return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
|
|
558
606
|
}
|
|
@@ -564,7 +612,9 @@ function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedMode
|
|
|
564
612
|
const round = planReviewRound + 1;
|
|
565
613
|
return { action: 'review-plan', role: 'reviewer', stage: 'review-plan', round, artifact: artifactName('review-plan', round) };
|
|
566
614
|
}
|
|
567
|
-
|
|
615
|
+
const planVerdict = latestReviewApproved(taskDir, 'review-plan');
|
|
616
|
+
if (planVerdict.error) return { error: planVerdict.error };
|
|
617
|
+
if (!planVerdict.approved) {
|
|
568
618
|
const round = planRound + 1;
|
|
569
619
|
return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
|
|
570
620
|
}
|
|
@@ -576,7 +626,9 @@ function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedMode
|
|
|
576
626
|
const round = codeReviewRound + 1;
|
|
577
627
|
return { action: 'review-code', role: 'reviewer', stage: 'review-code', round, artifact: artifactName('review-code', round) };
|
|
578
628
|
}
|
|
579
|
-
|
|
629
|
+
const codeVerdict = latestReviewApproved(taskDir, 'review-code');
|
|
630
|
+
if (codeVerdict.error) return { error: codeVerdict.error };
|
|
631
|
+
if (!codeVerdict.approved) {
|
|
580
632
|
const round = codeRound + 1;
|
|
581
633
|
return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
|
|
582
634
|
}
|
|
@@ -599,12 +651,21 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
599
651
|
return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
|
|
600
652
|
}
|
|
601
653
|
let metadata: ReturnType<typeof parseTypedTaskFrontmatter>;
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
return failed('
|
|
654
|
+
let currentLedger: LedgerDocument | null = null;
|
|
655
|
+
if (resolved.state === 'active') {
|
|
656
|
+
const contract = validateCurrentTaskContract(content);
|
|
657
|
+
if (!contract.ok) return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', contract.message, resolved.taskId);
|
|
658
|
+
metadata = contract.metadata;
|
|
659
|
+
currentLedger = contract.ledger;
|
|
660
|
+
} else {
|
|
661
|
+
try {
|
|
662
|
+
metadata = parseTypedTaskFrontmatter(content);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
665
|
+
}
|
|
606
666
|
}
|
|
607
667
|
const routed = routeFromFacts(resolved.taskDir);
|
|
668
|
+
if (routed && 'error' in routed) return failed('ORCHESTRATION_REVIEW_INVALID', routed.error, resolved.taskId);
|
|
608
669
|
if (!routed) return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
|
|
609
670
|
const run = readRun(resolved.taskDir);
|
|
610
671
|
const policy = run ? rolePolicy(run, routed.role) : null;
|
|
@@ -622,7 +683,9 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
622
683
|
if (!review.ok || review.summary.manualValidation === null) {
|
|
623
684
|
return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
|
|
624
685
|
}
|
|
625
|
-
const
|
|
686
|
+
const ledger = currentLedger ?? parseLedgerDocument(content);
|
|
687
|
+
if (!ledger.present) return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
|
|
688
|
+
const rows = ledger.rows;
|
|
626
689
|
const ledgerError = validateLedgerRows(rows);
|
|
627
690
|
if (ledgerError) return failed('ORCHESTRATION_LEDGER_INVALID', ledgerError.message, resolved.taskId);
|
|
628
691
|
if (!summarizeLedgerStage(rows, 'code').canAdvance) {
|
|
@@ -631,29 +694,8 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
631
694
|
if (run?.status === 'completed') {
|
|
632
695
|
return { status: 'completed', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
633
696
|
}
|
|
634
|
-
|
|
635
|
-
let config: unknown;
|
|
636
|
-
try {
|
|
637
|
-
config = JSON.parse(fs.readFileSync(path.join(resolved.repoRoot, '.agents', '.airc.json'), 'utf8'));
|
|
638
|
-
} catch (error) {
|
|
639
|
-
return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
640
|
-
}
|
|
641
|
-
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
642
|
-
return failed('ORCHESTRATION_CONFIG_INVALID', 'project configuration must be an object', resolved.taskId);
|
|
643
|
-
}
|
|
644
|
-
const projectConfig = config as Record<string, unknown>;
|
|
645
|
-
if (!Object.hasOwn(projectConfig, 'prFlow') || projectConfig.prFlow === 'disabled') {
|
|
646
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
647
|
-
}
|
|
648
|
-
if (projectConfig.prFlow !== 'required') {
|
|
649
|
-
return failed('ORCHESTRATION_CONFIG_INVALID', "project configuration 'prFlow' must be 'required' or 'disabled'", resolved.taskId);
|
|
650
|
-
}
|
|
651
|
-
const prNumber = Number(metadata.pr_number);
|
|
652
|
-
if (!Number.isInteger(prNumber) || prNumber <= 0) {
|
|
653
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
654
|
-
}
|
|
655
697
|
if (!run) {
|
|
656
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
698
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
657
699
|
}
|
|
658
700
|
if (run.status !== 'running' || run.pendingDelegation !== null) {
|
|
659
701
|
return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'clean completion requires an idle running orchestration', resolved.taskId);
|
|
@@ -667,35 +709,19 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
667
709
|
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
668
710
|
}
|
|
669
711
|
if (before.headTree !== before.worktreeTree) {
|
|
670
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
712
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
671
713
|
}
|
|
672
714
|
const lastReviewedCommit = String(metadata.last_reviewed_commit ?? '');
|
|
673
715
|
if (before.head !== lastReviewedCommit) {
|
|
674
716
|
return failed('ORCHESTRATION_REVIEWED_HEAD_MISMATCH', 'local HEAD does not match last_reviewed_commit', resolved.taskId);
|
|
675
717
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
return failed('ORCHESTRATION_PR_INSPECTION_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
684
|
-
}
|
|
685
|
-
if (inspection.status === 'blocked') {
|
|
686
|
-
return failed('ORCHESTRATION_PR_INSPECTION_BLOCKED', inspection.error?.message ?? 'pull request inspection is blocked', resolved.taskId);
|
|
687
|
-
}
|
|
688
|
-
if (inspection.status === 'failed') {
|
|
689
|
-
return failed('ORCHESTRATION_PR_INSPECTION_FAILED', inspection.error?.message ?? 'pull request inspection failed', resolved.taskId);
|
|
690
|
-
}
|
|
691
|
-
const inspectedPrNumber = inspection.task?.prNumber ?? inspection.prNumber;
|
|
692
|
-
const prHead = inspection.pullRequest?.head?.sha;
|
|
693
|
-
if (typeof prHead !== 'string') {
|
|
694
|
-
return failed('ORCHESTRATION_PR_INSPECTION_INVALID', 'pull request inspection returned incomplete identity evidence', resolved.taskId);
|
|
695
|
-
}
|
|
696
|
-
if (inspectedPrNumber !== prNumber || prHead !== before.head) {
|
|
697
|
-
return failed('ORCHESTRATION_PR_HEAD_MISMATCH', 'pull request identity does not match the reviewed local HEAD', resolved.taskId);
|
|
698
|
-
}
|
|
718
|
+
const diffBaseError = validateSavedCurrentDiffBase(
|
|
719
|
+
resolved.repoRoot,
|
|
720
|
+
metadata,
|
|
721
|
+
resolved.taskDir,
|
|
722
|
+
lastReviewedCommit
|
|
723
|
+
);
|
|
724
|
+
if (diffBaseError) return failed(diffBaseError.code, diffBaseError.message, resolved.taskId);
|
|
699
725
|
|
|
700
726
|
let after: RepositorySnapshot;
|
|
701
727
|
try {
|
|
@@ -704,7 +730,7 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
704
730
|
return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
705
731
|
}
|
|
706
732
|
if (after.headTree !== after.worktreeTree) {
|
|
707
|
-
return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
|
|
733
|
+
return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
708
734
|
}
|
|
709
735
|
if (after.head !== before.head || after.headTree !== before.headTree) {
|
|
710
736
|
return failed('ORCHESTRATION_REPOSITORY_CHANGED', 'repository changed while clean completion evidence was collected', resolved.taskId);
|
|
@@ -718,8 +744,8 @@ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {})
|
|
|
718
744
|
headTree: after.headTree,
|
|
719
745
|
worktreeTree: after.worktreeTree,
|
|
720
746
|
lastReviewedCommit,
|
|
721
|
-
prNumber,
|
|
722
|
-
prHead
|
|
747
|
+
prNumber: null,
|
|
748
|
+
prHead: null
|
|
723
749
|
};
|
|
724
750
|
const completed = withUpdatedRun(run, {
|
|
725
751
|
status: 'completed',
|
|
@@ -827,10 +853,7 @@ function prepareOrchestrationDelegationUnlocked(
|
|
|
827
853
|
}
|
|
828
854
|
const updated = withUpdatedRun(run, {
|
|
829
855
|
nextStage: next.stage,
|
|
830
|
-
pendingDelegation: receipt
|
|
831
|
-
commitAuthorization: next.stage === 'commit' && run.commitAuthorization.issuedAt === null
|
|
832
|
-
? { issuedAt: (options.now ?? (() => new Date().toISOString()))(), consumedAt: null }
|
|
833
|
-
: run.commitAuthorization
|
|
856
|
+
pendingDelegation: receipt
|
|
834
857
|
});
|
|
835
858
|
saveRun(resolved.taskDir, updated);
|
|
836
859
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
|
|
@@ -1435,16 +1458,15 @@ function advanceOrchestration(taskRef: string, options: OrchestrationOptions = {
|
|
|
1435
1458
|
if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1436
1459
|
const result = consumeDelegation(run.pendingDelegation, { now: options.now });
|
|
1437
1460
|
if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
1438
|
-
|
|
1461
|
+
if (result.receipt.stage === 'commit') {
|
|
1462
|
+
return failed('ORCHESTRATED_COMMIT_REMOVED', 'Orchestrated commit is no longer a lifecycle stage; use local checkpoint delivery from code-task', resolved.taskId);
|
|
1463
|
+
}
|
|
1439
1464
|
const updated = withUpdatedRun(run, {
|
|
1440
|
-
status:
|
|
1465
|
+
status: 'running',
|
|
1441
1466
|
nextStage: null,
|
|
1442
1467
|
stepCount: run.stepCount + 1,
|
|
1443
1468
|
pendingDelegation: null,
|
|
1444
|
-
receipts: Object.freeze([...run.receipts, result.receipt])
|
|
1445
|
-
commitAuthorization: completed
|
|
1446
|
-
? { ...run.commitAuthorization, consumedAt: (options.now ?? (() => new Date().toISOString()))() }
|
|
1447
|
-
: run.commitAuthorization
|
|
1469
|
+
receipts: Object.freeze([...run.receipts, result.receipt])
|
|
1448
1470
|
});
|
|
1449
1471
|
saveRun(resolved.taskDir, updated);
|
|
1450
1472
|
return { status: updated.status, changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
const PR_DELIVERY_FACT_KEY = 'pr_delivery_fact';
|
|
2
|
+
|
|
3
|
+
type PrDeliveryIdentity = {
|
|
4
|
+
repository: string;
|
|
5
|
+
number: number;
|
|
6
|
+
nodeId: string;
|
|
7
|
+
url: string;
|
|
8
|
+
head: { repository: string; ref: string; sha: string };
|
|
9
|
+
base: { repository: string; ref: string; sha: string };
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type PrDeliveryBindingSource =
|
|
13
|
+
| 'created'
|
|
14
|
+
| 'reused'
|
|
15
|
+
| 'explicit-bind'
|
|
16
|
+
| 'external-unique'
|
|
17
|
+
| 'external-explicit';
|
|
18
|
+
|
|
19
|
+
type PrDeliveryProvenance =
|
|
20
|
+
| 'create-post'
|
|
21
|
+
| 'reuse'
|
|
22
|
+
| 'explicit-bind'
|
|
23
|
+
| 'external-unique'
|
|
24
|
+
| 'external-explicit';
|
|
25
|
+
|
|
26
|
+
type PrDeliveryFact =
|
|
27
|
+
| { version: 1; state: 'unbound'; reason: 'initial' }
|
|
28
|
+
| { version: 1; state: 'skipped'; reason: 'explicit'; decidedAt: string }
|
|
29
|
+
| {
|
|
30
|
+
version: 1;
|
|
31
|
+
state: 'bound';
|
|
32
|
+
identity: PrDeliveryIdentity;
|
|
33
|
+
binding: {
|
|
34
|
+
status: 'verified';
|
|
35
|
+
source: PrDeliveryBindingSource;
|
|
36
|
+
verifiedAt: string;
|
|
37
|
+
issueNumber: number | null;
|
|
38
|
+
remoteState: 'open' | 'closed';
|
|
39
|
+
mergedAt: string | null;
|
|
40
|
+
mergeCommitSha: string | null;
|
|
41
|
+
};
|
|
42
|
+
provenance: { establishedBy: PrDeliveryProvenance };
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type CreationOutcome =
|
|
46
|
+
| { kind: 'planned'; action: 'create' | 'reuse'; createdByCurrentOperation: false }
|
|
47
|
+
| { kind: 'not-created'; reason: 'precondition-failed' | 'post-rejected'; createdByCurrentOperation: false }
|
|
48
|
+
| { kind: 'created'; createdByCurrentOperation: true }
|
|
49
|
+
| { kind: 'reused'; createdByCurrentOperation: false }
|
|
50
|
+
| { kind: 'no-op'; createdByCurrentOperation: false }
|
|
51
|
+
| { kind: 'unknown'; errorCode: 'PR_CREATE_OUTCOME_UNKNOWN' };
|
|
52
|
+
|
|
53
|
+
type PrDeliveryFactReadResult =
|
|
54
|
+
| { status: 'valid'; fact: PrDeliveryFact }
|
|
55
|
+
| { status: 'missing'; fact: null }
|
|
56
|
+
| { status: 'invalid'; fact: null; error: Error };
|
|
57
|
+
|
|
58
|
+
function factError(message: string): Error & { code: string } {
|
|
59
|
+
return Object.assign(new Error(`PR_DELIVERY_FACT_INVALID: ${message}`), { code: 'PR_DELIVERY_FACT_INVALID' });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
63
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function exactKeys(value: Record<string, unknown>, keys: readonly string[], label: string): void {
|
|
67
|
+
const actual = Object.keys(value).sort();
|
|
68
|
+
const expected = [...keys].sort();
|
|
69
|
+
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
|
|
70
|
+
throw factError(`${label} has an invalid field set`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function text(value: unknown, label: string): string {
|
|
75
|
+
if (typeof value !== 'string' || !value || value.trim() !== value) throw factError(`${label} must be a non-empty string`);
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function nullableText(value: unknown, label: string): string | null {
|
|
80
|
+
return value === null ? null : text(value, label);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function positiveNumber(value: unknown, label: string): number {
|
|
84
|
+
if (!Number.isSafeInteger(value) || (value as number) <= 0) throw factError(`${label} must be a positive integer`);
|
|
85
|
+
return value as number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function optionalTimestamp(value: unknown, label: string): string | null {
|
|
89
|
+
const result = nullableText(value, label);
|
|
90
|
+
if (result !== null && Number.isNaN(Date.parse(result))) throw factError(`${label} must be a valid timestamp`);
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parseIdentity(value: unknown): PrDeliveryIdentity {
|
|
95
|
+
if (!isRecord(value)) throw factError('identity must be an object');
|
|
96
|
+
exactKeys(value, ['repository', 'number', 'nodeId', 'url', 'head', 'base'], 'identity');
|
|
97
|
+
const head = parseRef(value.head, 'head');
|
|
98
|
+
const base = parseRef(value.base, 'base');
|
|
99
|
+
return {
|
|
100
|
+
repository: text(value.repository, 'identity.repository'),
|
|
101
|
+
number: positiveNumber(value.number, 'identity.number'),
|
|
102
|
+
nodeId: text(value.nodeId, 'identity.nodeId'),
|
|
103
|
+
url: text(value.url, 'identity.url'),
|
|
104
|
+
head,
|
|
105
|
+
base
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function parseRef(value: unknown, label: string): { repository: string; ref: string; sha: string } {
|
|
110
|
+
if (!isRecord(value)) throw factError(`${label} must be an object`);
|
|
111
|
+
exactKeys(value, ['repository', 'ref', 'sha'], label);
|
|
112
|
+
return {
|
|
113
|
+
repository: text(value.repository, `${label}.repository`),
|
|
114
|
+
ref: text(value.ref, `${label}.ref`),
|
|
115
|
+
sha: text(value.sha, `${label}.sha`)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function parseFact(value: unknown): PrDeliveryFact {
|
|
120
|
+
if (!isRecord(value)) throw factError('fact must be an object');
|
|
121
|
+
if (value.version !== 1 || typeof value.state !== 'string') throw factError('version or state is invalid');
|
|
122
|
+
if (value.state === 'unbound') {
|
|
123
|
+
exactKeys(value, ['version', 'state', 'reason'], 'unbound fact');
|
|
124
|
+
if (value.reason !== 'initial') throw factError('unbound reason is invalid');
|
|
125
|
+
return { version: 1, state: 'unbound', reason: value.reason };
|
|
126
|
+
}
|
|
127
|
+
if (value.state === 'skipped') {
|
|
128
|
+
exactKeys(value, ['version', 'state', 'reason', 'decidedAt'], 'skipped fact');
|
|
129
|
+
if (value.reason !== 'explicit') throw factError('skipped reason is invalid');
|
|
130
|
+
const decidedAt = text(value.decidedAt, 'skipped.decidedAt');
|
|
131
|
+
if (Number.isNaN(Date.parse(decidedAt))) throw factError('skipped.decidedAt must be a valid timestamp');
|
|
132
|
+
return { version: 1, state: 'skipped', reason: 'explicit', decidedAt };
|
|
133
|
+
}
|
|
134
|
+
if (value.state !== 'bound') throw factError('state is invalid');
|
|
135
|
+
exactKeys(value, ['version', 'state', 'identity', 'binding', 'provenance'], 'bound fact');
|
|
136
|
+
if (!isRecord(value.binding)) throw factError('binding must be an object');
|
|
137
|
+
exactKeys(value.binding, ['status', 'source', 'verifiedAt', 'issueNumber', 'remoteState', 'mergedAt', 'mergeCommitSha'], 'binding');
|
|
138
|
+
if (value.binding.status !== 'verified') throw factError('binding status is invalid');
|
|
139
|
+
const sources: readonly PrDeliveryBindingSource[] = ['created', 'reused', 'explicit-bind', 'external-unique', 'external-explicit'];
|
|
140
|
+
if (!sources.includes(value.binding.source as PrDeliveryBindingSource)) throw factError('binding source is invalid');
|
|
141
|
+
const verifiedAt = text(value.binding.verifiedAt, 'binding.verifiedAt');
|
|
142
|
+
if (Number.isNaN(Date.parse(verifiedAt))) throw factError('binding.verifiedAt must be a valid timestamp');
|
|
143
|
+
const issueNumber = value.binding.issueNumber === null ? null : positiveNumber(value.binding.issueNumber, 'binding.issueNumber');
|
|
144
|
+
if (value.binding.remoteState !== 'open' && value.binding.remoteState !== 'closed') throw factError('binding.remoteState is invalid');
|
|
145
|
+
const mergedAt = optionalTimestamp(value.binding.mergedAt, 'binding.mergedAt');
|
|
146
|
+
const mergeCommitSha = nullableText(value.binding.mergeCommitSha, 'binding.mergeCommitSha');
|
|
147
|
+
if (Boolean(mergedAt) !== Boolean(mergeCommitSha)) throw factError('mergedAt and mergeCommitSha must be paired');
|
|
148
|
+
if (!isRecord(value.provenance)) throw factError('provenance must be an object');
|
|
149
|
+
exactKeys(value.provenance, ['establishedBy'], 'provenance');
|
|
150
|
+
const provenance: readonly PrDeliveryProvenance[] = ['create-post', 'reuse', 'explicit-bind', 'external-unique', 'external-explicit'];
|
|
151
|
+
if (!provenance.includes(value.provenance.establishedBy as PrDeliveryProvenance)) throw factError('provenance.establishedBy is invalid');
|
|
152
|
+
if (value.provenance.establishedBy !== provenanceForSource(value.binding.source as PrDeliveryBindingSource)) {
|
|
153
|
+
throw factError('binding.source and provenance.establishedBy do not match');
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
version: 1,
|
|
157
|
+
state: 'bound',
|
|
158
|
+
identity: parseIdentity(value.identity),
|
|
159
|
+
binding: {
|
|
160
|
+
status: 'verified',
|
|
161
|
+
source: value.binding.source as PrDeliveryBindingSource,
|
|
162
|
+
verifiedAt,
|
|
163
|
+
issueNumber,
|
|
164
|
+
remoteState: value.binding.remoteState,
|
|
165
|
+
mergedAt,
|
|
166
|
+
mergeCommitSha
|
|
167
|
+
},
|
|
168
|
+
provenance: { establishedBy: value.provenance.establishedBy as PrDeliveryProvenance }
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function decodePrDeliveryFact(value: unknown): PrDeliveryFact {
|
|
173
|
+
if (typeof value === 'string') {
|
|
174
|
+
try { return parseFact(JSON.parse(value)); }
|
|
175
|
+
catch (error) { throw error instanceof Error && error.message.startsWith('PR_DELIVERY_FACT_INVALID:') ? error : factError('value is not valid JSON'); }
|
|
176
|
+
}
|
|
177
|
+
return parseFact(value);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function encodePrDeliveryFact(fact: PrDeliveryFact): string {
|
|
181
|
+
return JSON.stringify(parseFact(fact));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function readPrDeliveryFact(metadata: Record<string, unknown>): PrDeliveryFactReadResult {
|
|
185
|
+
if (!Object.hasOwn(metadata, PR_DELIVERY_FACT_KEY) || metadata[PR_DELIVERY_FACT_KEY] === '') return { status: 'missing', fact: null };
|
|
186
|
+
try { return { status: 'valid', fact: decodePrDeliveryFact(metadata[PR_DELIVERY_FACT_KEY]) }; }
|
|
187
|
+
catch (error) { return { status: 'invalid', fact: null, error: error instanceof Error ? error : factError(String(error)) }; }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function buildUnboundFact(): PrDeliveryFact {
|
|
191
|
+
return { version: 1, state: 'unbound', reason: 'initial' };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function buildSkippedFact(decidedAt: string): PrDeliveryFact {
|
|
195
|
+
return parseFact({ version: 1, state: 'skipped', reason: 'explicit', decidedAt });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function provenanceForSource(source: PrDeliveryBindingSource): PrDeliveryProvenance {
|
|
199
|
+
return source === 'created' ? 'create-post' : source === 'reused' ? 'reuse' : source;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function buildBoundFact(input: {
|
|
203
|
+
identity: PrDeliveryIdentity;
|
|
204
|
+
source: PrDeliveryBindingSource;
|
|
205
|
+
verifiedAt: string;
|
|
206
|
+
issueNumber?: number | null;
|
|
207
|
+
remoteState: 'open' | 'closed';
|
|
208
|
+
mergedAt?: string | null;
|
|
209
|
+
mergeCommitSha?: string | null;
|
|
210
|
+
}): PrDeliveryFact {
|
|
211
|
+
return parseFact({
|
|
212
|
+
version: 1,
|
|
213
|
+
state: 'bound',
|
|
214
|
+
identity: input.identity,
|
|
215
|
+
binding: {
|
|
216
|
+
status: 'verified',
|
|
217
|
+
source: input.source,
|
|
218
|
+
verifiedAt: input.verifiedAt,
|
|
219
|
+
issueNumber: input.issueNumber ?? null,
|
|
220
|
+
remoteState: input.remoteState,
|
|
221
|
+
mergedAt: input.mergedAt ?? null,
|
|
222
|
+
mergeCommitSha: input.mergeCommitSha ?? null
|
|
223
|
+
},
|
|
224
|
+
provenance: { establishedBy: provenanceForSource(input.source) }
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function factFrontmatterMutation(fact: PrDeliveryFact): { set: { [PR_DELIVERY_FACT_KEY]: string } } {
|
|
229
|
+
return { set: { [PR_DELIVERY_FACT_KEY]: encodePrDeliveryFact(fact) } };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function identityFromPullRequest(pullRequest: PrDeliveryIdentity): PrDeliveryIdentity {
|
|
233
|
+
return parseIdentity(pullRequest);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export {
|
|
237
|
+
PR_DELIVERY_FACT_KEY,
|
|
238
|
+
buildBoundFact,
|
|
239
|
+
buildSkippedFact,
|
|
240
|
+
buildUnboundFact,
|
|
241
|
+
decodePrDeliveryFact,
|
|
242
|
+
encodePrDeliveryFact,
|
|
243
|
+
factFrontmatterMutation,
|
|
244
|
+
identityFromPullRequest,
|
|
245
|
+
readPrDeliveryFact
|
|
246
|
+
};
|
|
247
|
+
export type {
|
|
248
|
+
CreationOutcome,
|
|
249
|
+
PrDeliveryBindingSource,
|
|
250
|
+
PrDeliveryFact,
|
|
251
|
+
PrDeliveryFactReadResult,
|
|
252
|
+
PrDeliveryIdentity,
|
|
253
|
+
PrDeliveryProvenance
|
|
254
|
+
};
|