@fitlab-ai/agent-infra 0.9.10 → 0.9.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -9
- package/README.zh-CN.md +19 -9
- package/bin/cli.ts +17 -4
- package/dist/bin/cli.js +19 -4
- package/dist/bin/internal-cli.js +55 -4
- package/dist/lib/agent-client.js +0 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
- package/dist/lib/agent-clients/adapters/traecli.js +7 -8
- package/dist/lib/agent-clients/config.js +14 -99
- package/dist/lib/agent-clients/reconcile.js +1 -11
- package/dist/lib/agent-clients/schema.js +5 -4
- package/dist/lib/agent-clients/tokens.js +5 -15
- package/dist/lib/decide.js +14 -30
- package/dist/lib/defaults.json +8 -0
- package/dist/lib/git/workflow.js +43 -1
- package/dist/lib/init.js +3 -2
- package/dist/lib/internal/git-workflow.js +8 -1
- package/dist/lib/internal/platform-pr.js +8 -2
- package/dist/lib/internal/release-workflow.js +0 -1
- package/dist/lib/internal/task-delivery.js +59 -0
- package/dist/lib/internal/task-finalization.js +19 -9
- package/dist/lib/internal/task-ledger.js +8 -2
- package/dist/lib/internal/task-lifecycle.js +17 -93
- package/dist/lib/internal/task-orchestration.js +30 -216
- package/dist/lib/internal/task-validate.js +2 -1
- package/dist/lib/merge.js +205 -103
- package/dist/lib/platform/github-client.js +7 -1
- package/dist/lib/platform/pr-checks.js +8 -5
- package/dist/lib/platform/pr-summary.js +12 -8
- package/dist/lib/platform/pull-requests.js +393 -91
- package/dist/lib/platform/verification-required.js +13 -2
- package/dist/lib/platform/verification-sync.js +9 -4
- package/dist/lib/pr-review/evidence-grading.js +12 -4
- package/dist/lib/process-data/normalize.js +2 -9
- package/dist/lib/run/host.js +46 -16
- package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
- package/dist/lib/sandbox/commands/create.js +3 -3
- package/dist/lib/sandbox/commands/enter.js +18 -4
- package/dist/lib/sandbox/config.js +8 -12
- package/dist/lib/sandbox/control/executor.js +131 -39
- package/dist/lib/sandbox/control/lifecycle.js +55 -24
- package/dist/lib/sandbox/control/server.js +18 -16
- package/dist/lib/sandbox/mounts.js +3 -2
- package/dist/lib/sandbox/recovery.js +123 -22
- package/dist/lib/sandbox/workspace-identity.js +14 -0
- package/dist/lib/sandbox/workspace-view.js +8 -9
- package/dist/lib/self-update.js +278 -0
- package/dist/lib/server/process-state.js +2 -2
- package/dist/lib/server/protocol.js +5 -5
- package/dist/lib/task/activity-log.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +6 -4
- package/dist/lib/task/codex-orchestration.js +1 -1
- package/dist/lib/task/command-options.js +1 -3
- package/dist/lib/task/commands/cat.js +0 -5
- package/dist/lib/task/commands/decisions.js +76 -92
- package/dist/lib/task/commands/files.js +6 -6
- package/dist/lib/task/commands/grep.js +25 -61
- package/dist/lib/task/commands/issue-body.js +6 -6
- package/dist/lib/task/commands/log.js +25 -10
- package/dist/lib/task/commands/show.js +5 -6
- package/dist/lib/task/commands/status.js +8 -8
- package/dist/lib/task/commit-intent.js +100 -0
- package/dist/lib/task/commit-operation.js +181 -91
- package/dist/lib/task/commit-policy.js +1 -1
- package/dist/lib/task/control-authority.js +431 -0
- package/dist/lib/task/create.js +39 -8
- package/dist/lib/task/current-contract.js +35 -0
- package/dist/lib/task/decision-details.js +218 -0
- package/dist/lib/task/decision-intents.js +5 -2
- package/dist/lib/task/delivery-target.js +97 -0
- package/dist/lib/task/delivery.js +153 -0
- package/dist/lib/task/events.js +11 -5
- package/dist/lib/task/human-override.js +5 -2
- package/dist/lib/task/index.js +19 -20
- package/dist/lib/task/ledger-intents.js +8 -9
- package/dist/lib/task/ledger.js +13 -6
- package/dist/lib/task/lifecycle.js +122 -9
- package/dist/lib/task/orchestration.js +105 -75
- package/dist/lib/task/pr-delivery-fact.js +182 -0
- package/dist/lib/task/review-artifacts.js +31 -12
- package/dist/lib/task/review-finalization.js +13 -4
- package/dist/lib/task/review-fingerprint.js +8 -0
- package/dist/lib/task/verification-engine.js +86 -39
- package/dist/lib/task/verification-review.js +2 -2
- package/dist/lib/task/workspace-snapshot.js +5 -11
- package/dist/lib/task/write.js +11 -1
- package/dist/lib/update.js +13 -35
- package/dist/lib/version.js +5 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +0 -1
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
- package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
- package/lib/agent-clients/adapters/traecli.ts +7 -8
- package/lib/agent-clients/config.ts +14 -144
- package/lib/agent-clients/reconcile.ts +1 -12
- package/lib/agent-clients/schema.ts +5 -4
- package/lib/agent-clients/tokens.ts +5 -18
- package/lib/decide.ts +12 -22
- package/lib/defaults.json +8 -0
- package/lib/git/workflow.ts +38 -1
- package/lib/init.ts +5 -2
- package/lib/internal/git-workflow.ts +7 -1
- package/lib/internal/platform-pr.ts +8 -1
- package/lib/internal/release-workflow.ts +0 -1
- package/lib/internal/task-delivery.ts +53 -0
- package/lib/internal/task-finalization.ts +27 -13
- package/lib/internal/task-ledger.ts +8 -2
- package/lib/internal/task-lifecycle.ts +33 -76
- package/lib/internal/task-orchestration.ts +33 -190
- package/lib/internal/task-validate.ts +3 -2
- package/lib/merge.ts +236 -120
- package/lib/platform/github-client.ts +8 -1
- package/lib/platform/pr-checks.ts +7 -5
- package/lib/platform/pr-summary.ts +10 -7
- package/lib/platform/pull-requests.ts +425 -87
- package/lib/platform/verification-required.ts +12 -2
- package/lib/platform/verification-sync.ts +9 -4
- package/lib/pr-review/evidence-grading.ts +7 -4
- package/lib/process-data/normalize.ts +2 -8
- package/lib/run/host.ts +63 -14
- package/lib/sandbox/agent-client-reconciler.ts +5 -15
- package/lib/sandbox/commands/create.ts +4 -4
- package/lib/sandbox/commands/enter.ts +24 -4
- package/lib/sandbox/config.ts +9 -14
- package/lib/sandbox/control/container-identity.ts +2 -2
- package/lib/sandbox/control/executor.ts +148 -51
- package/lib/sandbox/control/lifecycle.ts +59 -31
- package/lib/sandbox/control/protocol.ts +1 -3
- package/lib/sandbox/control/server.ts +15 -12
- package/lib/sandbox/control/state.ts +7 -7
- package/lib/sandbox/mounts.ts +4 -3
- package/lib/sandbox/recovery.ts +162 -34
- package/lib/sandbox/tools.ts +1 -1
- package/lib/sandbox/workspace-identity.ts +28 -0
- package/lib/sandbox/workspace-view.ts +14 -10
- package/lib/self-update.ts +341 -0
- package/lib/server/process-state.ts +2 -2
- package/lib/server/protocol.ts +5 -5
- package/lib/task/activity-log.ts +6 -1
- package/lib/task/artifact-lifecycle.ts +7 -5
- package/lib/task/codex-orchestration.ts +2 -1
- package/lib/task/command-options.ts +1 -2
- package/lib/task/commands/cat.ts +0 -4
- package/lib/task/commands/decisions.ts +80 -75
- package/lib/task/commands/files.ts +6 -6
- package/lib/task/commands/grep.ts +23 -51
- package/lib/task/commands/issue-body.ts +6 -6
- package/lib/task/commands/log.ts +22 -10
- package/lib/task/commands/show.ts +5 -6
- package/lib/task/commands/status.ts +8 -8
- package/lib/task/commit-intent.ts +137 -0
- package/lib/task/commit-operation.ts +161 -79
- package/lib/task/commit-policy.ts +1 -2
- package/lib/task/control-authority.ts +563 -0
- package/lib/task/create.ts +40 -10
- package/lib/task/current-contract.ts +49 -0
- package/lib/task/decision-details.ts +280 -0
- package/lib/task/decision-intents.ts +7 -2
- package/lib/task/delivery-target.ts +130 -0
- package/lib/task/delivery.ts +160 -0
- package/lib/task/events.ts +10 -5
- package/lib/task/human-override.ts +5 -2
- package/lib/task/index.ts +19 -20
- package/lib/task/ledger-intents.ts +6 -8
- package/lib/task/ledger.ts +17 -7
- package/lib/task/lifecycle.ts +113 -4
- package/lib/task/orchestration.ts +102 -80
- package/lib/task/pr-delivery-fact.ts +254 -0
- package/lib/task/review-artifacts.ts +53 -15
- package/lib/task/review-finalization.ts +21 -5
- package/lib/task/review-fingerprint.ts +10 -0
- package/lib/task/verification-engine.ts +78 -46
- package/lib/task/verification-review.ts +3 -1
- package/lib/task/workspace-snapshot.ts +4 -13
- package/lib/task/write.ts +20 -1
- package/lib/update.ts +14 -38
- package/lib/version.ts +6 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +5 -3
- package/templates/.agents/README.zh-CN.md +5 -3
- package/templates/.agents/rules/README.en.md +4 -2
- package/templates/.agents/rules/README.zh-CN.md +4 -2
- package/templates/.agents/rules/compatibility-policy.en.md +5 -4
- package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
- package/templates/.agents/rules/create-issue.en.md +11 -2
- package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
- package/templates/.agents/rules/human-decision-context.en.md +9 -7
- package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
- package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +1 -1
- package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +26 -5
- package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
- package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
- package/templates/.agents/rules/milestone-inference.en.md +12 -2
- package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
- package/templates/.agents/rules/next-step-output.en.md +3 -3
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
- package/templates/.agents/rules/pr-sync.en.md +23 -14
- package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
- package/templates/.agents/rules/release-commands.en.md +42 -3
- package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
- package/templates/.agents/rules/review-handshake.en.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/security-alerts.en.md +17 -2
- package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
- package/templates/.agents/rules/sync-content-generation.en.md +8 -0
- package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
- package/templates/.agents/rules/task-management.en.md +2 -3
- package/templates/.agents/rules/task-management.zh-CN.md +2 -3
- package/templates/.agents/scripts/security-alerts.github.sh +151 -0
- package/templates/.agents/scripts/security-alerts.sh +31 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/commit/SKILL.en.md +30 -10
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
- package/templates/.agents/skills/commit/config/verify.json +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +13 -13
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
- package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
- package/templates/.agents/skills/create-task/SKILL.en.md +2 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
- package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +8 -5
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +144 -109
- package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
- package/templates/.agents/templates/task.en.md +6 -2
- package/templates/.agents/templates/task.zh-CN.md +6 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/feature-development.en.yaml +9 -8
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/refactoring.en.yaml +9 -8
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
- package/templates/.claude/commands/analyze-task.en.md +1 -1
- package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
- package/templates/.claude/commands/block-task.en.md +1 -1
- package/templates/.claude/commands/block-task.zh-CN.md +1 -1
- package/templates/.claude/commands/cancel-task.en.md +1 -1
- package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
- package/templates/.claude/commands/check-task.en.md +1 -1
- package/templates/.claude/commands/check-task.zh-CN.md +1 -1
- package/templates/.claude/commands/code-task.en.md +1 -1
- package/templates/.claude/commands/code-task.zh-CN.md +1 -1
- package/templates/.claude/commands/commit.en.md +1 -1
- package/templates/.claude/commands/commit.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
- package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/complete-task.en.md +1 -1
- package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
- package/templates/.claude/commands/create-pr.en.md +1 -1
- package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
- package/templates/.claude/commands/plan-task.en.md +1 -1
- package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
- package/templates/.claude/commands/review-analysis.en.md +1 -1
- package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
- package/templates/.claude/commands/review-code.en.md +1 -1
- package/templates/.claude/commands/review-code.zh-CN.md +1 -1
- package/templates/.claude/commands/review-plan.en.md +1 -1
- package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/templates/.claude/commands/run-task.en.md +1 -1
- package/templates/.claude/commands/run-task.zh-CN.md +1 -1
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
- package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
- package/dist/lib/builtin-tuis.js +0 -40
- package/lib/builtin-tuis.ts +0 -56
- package/templates/.agents/rules/create-issue.github.en.md +0 -14
- package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
- package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
- package/templates/.agents/rules/issue-sync.github.en.md +0 -40
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
- package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
- package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
- package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
- package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
- package/templates/.agents/rules/pr-sync.github.en.md +0 -43
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
- package/templates/.agents/rules/release-commands.github.en.md +0 -38
- package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
- package/templates/.agents/rules/security-alerts.github.en.md +0 -43
- package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
const PR_DELIVERY_FACT_KEY = 'pr_delivery_fact';
|
|
2
|
+
function factError(message) {
|
|
3
|
+
return Object.assign(new Error(`PR_DELIVERY_FACT_INVALID: ${message}`), { code: 'PR_DELIVERY_FACT_INVALID' });
|
|
4
|
+
}
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function exactKeys(value, keys, label) {
|
|
9
|
+
const actual = Object.keys(value).sort();
|
|
10
|
+
const expected = [...keys].sort();
|
|
11
|
+
if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
|
|
12
|
+
throw factError(`${label} has an invalid field set`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function text(value, label) {
|
|
16
|
+
if (typeof value !== 'string' || !value || value.trim() !== value)
|
|
17
|
+
throw factError(`${label} must be a non-empty string`);
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function nullableText(value, label) {
|
|
21
|
+
return value === null ? null : text(value, label);
|
|
22
|
+
}
|
|
23
|
+
function positiveNumber(value, label) {
|
|
24
|
+
if (!Number.isSafeInteger(value) || value <= 0)
|
|
25
|
+
throw factError(`${label} must be a positive integer`);
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function optionalTimestamp(value, label) {
|
|
29
|
+
const result = nullableText(value, label);
|
|
30
|
+
if (result !== null && Number.isNaN(Date.parse(result)))
|
|
31
|
+
throw factError(`${label} must be a valid timestamp`);
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function parseIdentity(value) {
|
|
35
|
+
if (!isRecord(value))
|
|
36
|
+
throw factError('identity must be an object');
|
|
37
|
+
exactKeys(value, ['repository', 'number', 'nodeId', 'url', 'head', 'base'], 'identity');
|
|
38
|
+
const head = parseRef(value.head, 'head');
|
|
39
|
+
const base = parseRef(value.base, 'base');
|
|
40
|
+
return {
|
|
41
|
+
repository: text(value.repository, 'identity.repository'),
|
|
42
|
+
number: positiveNumber(value.number, 'identity.number'),
|
|
43
|
+
nodeId: text(value.nodeId, 'identity.nodeId'),
|
|
44
|
+
url: text(value.url, 'identity.url'),
|
|
45
|
+
head,
|
|
46
|
+
base
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function parseRef(value, label) {
|
|
50
|
+
if (!isRecord(value))
|
|
51
|
+
throw factError(`${label} must be an object`);
|
|
52
|
+
exactKeys(value, ['repository', 'ref', 'sha'], label);
|
|
53
|
+
return {
|
|
54
|
+
repository: text(value.repository, `${label}.repository`),
|
|
55
|
+
ref: text(value.ref, `${label}.ref`),
|
|
56
|
+
sha: text(value.sha, `${label}.sha`)
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function parseFact(value) {
|
|
60
|
+
if (!isRecord(value))
|
|
61
|
+
throw factError('fact must be an object');
|
|
62
|
+
if (value.version !== 1 || typeof value.state !== 'string')
|
|
63
|
+
throw factError('version or state is invalid');
|
|
64
|
+
if (value.state === 'unbound') {
|
|
65
|
+
exactKeys(value, ['version', 'state', 'reason'], 'unbound fact');
|
|
66
|
+
if (value.reason !== 'initial')
|
|
67
|
+
throw factError('unbound reason is invalid');
|
|
68
|
+
return { version: 1, state: 'unbound', reason: value.reason };
|
|
69
|
+
}
|
|
70
|
+
if (value.state === 'skipped') {
|
|
71
|
+
exactKeys(value, ['version', 'state', 'reason', 'decidedAt'], 'skipped fact');
|
|
72
|
+
if (value.reason !== 'explicit')
|
|
73
|
+
throw factError('skipped reason is invalid');
|
|
74
|
+
const decidedAt = text(value.decidedAt, 'skipped.decidedAt');
|
|
75
|
+
if (Number.isNaN(Date.parse(decidedAt)))
|
|
76
|
+
throw factError('skipped.decidedAt must be a valid timestamp');
|
|
77
|
+
return { version: 1, state: 'skipped', reason: 'explicit', decidedAt };
|
|
78
|
+
}
|
|
79
|
+
if (value.state !== 'bound')
|
|
80
|
+
throw factError('state is invalid');
|
|
81
|
+
exactKeys(value, ['version', 'state', 'identity', 'binding', 'provenance'], 'bound fact');
|
|
82
|
+
if (!isRecord(value.binding))
|
|
83
|
+
throw factError('binding must be an object');
|
|
84
|
+
exactKeys(value.binding, ['status', 'source', 'verifiedAt', 'issueNumber', 'remoteState', 'mergedAt', 'mergeCommitSha'], 'binding');
|
|
85
|
+
if (value.binding.status !== 'verified')
|
|
86
|
+
throw factError('binding status is invalid');
|
|
87
|
+
const sources = ['created', 'reused', 'explicit-bind', 'external-unique', 'external-explicit'];
|
|
88
|
+
if (!sources.includes(value.binding.source))
|
|
89
|
+
throw factError('binding source is invalid');
|
|
90
|
+
const verifiedAt = text(value.binding.verifiedAt, 'binding.verifiedAt');
|
|
91
|
+
if (Number.isNaN(Date.parse(verifiedAt)))
|
|
92
|
+
throw factError('binding.verifiedAt must be a valid timestamp');
|
|
93
|
+
const issueNumber = value.binding.issueNumber === null ? null : positiveNumber(value.binding.issueNumber, 'binding.issueNumber');
|
|
94
|
+
if (value.binding.remoteState !== 'open' && value.binding.remoteState !== 'closed')
|
|
95
|
+
throw factError('binding.remoteState is invalid');
|
|
96
|
+
const mergedAt = optionalTimestamp(value.binding.mergedAt, 'binding.mergedAt');
|
|
97
|
+
const mergeCommitSha = nullableText(value.binding.mergeCommitSha, 'binding.mergeCommitSha');
|
|
98
|
+
if (Boolean(mergedAt) !== Boolean(mergeCommitSha))
|
|
99
|
+
throw factError('mergedAt and mergeCommitSha must be paired');
|
|
100
|
+
if (!isRecord(value.provenance))
|
|
101
|
+
throw factError('provenance must be an object');
|
|
102
|
+
exactKeys(value.provenance, ['establishedBy'], 'provenance');
|
|
103
|
+
const provenance = ['create-post', 'reuse', 'explicit-bind', 'external-unique', 'external-explicit'];
|
|
104
|
+
if (!provenance.includes(value.provenance.establishedBy))
|
|
105
|
+
throw factError('provenance.establishedBy is invalid');
|
|
106
|
+
if (value.provenance.establishedBy !== provenanceForSource(value.binding.source)) {
|
|
107
|
+
throw factError('binding.source and provenance.establishedBy do not match');
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
version: 1,
|
|
111
|
+
state: 'bound',
|
|
112
|
+
identity: parseIdentity(value.identity),
|
|
113
|
+
binding: {
|
|
114
|
+
status: 'verified',
|
|
115
|
+
source: value.binding.source,
|
|
116
|
+
verifiedAt,
|
|
117
|
+
issueNumber,
|
|
118
|
+
remoteState: value.binding.remoteState,
|
|
119
|
+
mergedAt,
|
|
120
|
+
mergeCommitSha
|
|
121
|
+
},
|
|
122
|
+
provenance: { establishedBy: value.provenance.establishedBy }
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function decodePrDeliveryFact(value) {
|
|
126
|
+
if (typeof value === 'string') {
|
|
127
|
+
try {
|
|
128
|
+
return parseFact(JSON.parse(value));
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
throw error instanceof Error && error.message.startsWith('PR_DELIVERY_FACT_INVALID:') ? error : factError('value is not valid JSON');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return parseFact(value);
|
|
135
|
+
}
|
|
136
|
+
function encodePrDeliveryFact(fact) {
|
|
137
|
+
return JSON.stringify(parseFact(fact));
|
|
138
|
+
}
|
|
139
|
+
function readPrDeliveryFact(metadata) {
|
|
140
|
+
if (!Object.hasOwn(metadata, PR_DELIVERY_FACT_KEY) || metadata[PR_DELIVERY_FACT_KEY] === '')
|
|
141
|
+
return { status: 'missing', fact: null };
|
|
142
|
+
try {
|
|
143
|
+
return { status: 'valid', fact: decodePrDeliveryFact(metadata[PR_DELIVERY_FACT_KEY]) };
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return { status: 'invalid', fact: null, error: error instanceof Error ? error : factError(String(error)) };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function buildUnboundFact() {
|
|
150
|
+
return { version: 1, state: 'unbound', reason: 'initial' };
|
|
151
|
+
}
|
|
152
|
+
function buildSkippedFact(decidedAt) {
|
|
153
|
+
return parseFact({ version: 1, state: 'skipped', reason: 'explicit', decidedAt });
|
|
154
|
+
}
|
|
155
|
+
function provenanceForSource(source) {
|
|
156
|
+
return source === 'created' ? 'create-post' : source === 'reused' ? 'reuse' : source;
|
|
157
|
+
}
|
|
158
|
+
function buildBoundFact(input) {
|
|
159
|
+
return parseFact({
|
|
160
|
+
version: 1,
|
|
161
|
+
state: 'bound',
|
|
162
|
+
identity: input.identity,
|
|
163
|
+
binding: {
|
|
164
|
+
status: 'verified',
|
|
165
|
+
source: input.source,
|
|
166
|
+
verifiedAt: input.verifiedAt,
|
|
167
|
+
issueNumber: input.issueNumber ?? null,
|
|
168
|
+
remoteState: input.remoteState,
|
|
169
|
+
mergedAt: input.mergedAt ?? null,
|
|
170
|
+
mergeCommitSha: input.mergeCommitSha ?? null
|
|
171
|
+
},
|
|
172
|
+
provenance: { establishedBy: provenanceForSource(input.source) }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function factFrontmatterMutation(fact) {
|
|
176
|
+
return { set: { [PR_DELIVERY_FACT_KEY]: encodePrDeliveryFact(fact) } };
|
|
177
|
+
}
|
|
178
|
+
function identityFromPullRequest(pullRequest) {
|
|
179
|
+
return parseIdentity(pullRequest);
|
|
180
|
+
}
|
|
181
|
+
export { PR_DELIVERY_FACT_KEY, buildBoundFact, buildSkippedFact, buildUnboundFact, decodePrDeliveryFact, encodePrDeliveryFact, factFrontmatterMutation, identityFromPullRequest, readPrDeliveryFact };
|
|
182
|
+
//# sourceMappingURL=pr-delivery-fact.js.map
|
|
@@ -159,27 +159,46 @@ function finalizeReviewSummaryContent(content, counts) {
|
|
|
159
159
|
content: content.slice(0, parsed.findingsStart) + line + content.slice(parsed.findingsEnd)
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
|
+
function resolveCanonicalVerdict(summary) {
|
|
163
|
+
if (summary.counts === null) {
|
|
164
|
+
return {
|
|
165
|
+
ok: false,
|
|
166
|
+
verdict: null,
|
|
167
|
+
code: 'REVIEW_FINDING_COUNTS_NOT_FINALIZED',
|
|
168
|
+
message: 'review summary finding counts are not finalized'
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (summary.verdict === 'Approved' && !equalCounts(summary.counts, { blocker: 0, major: 0, minor: 0 })) {
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
verdict: null,
|
|
175
|
+
code: 'REVIEW_VERDICT_FINDING_MISMATCH',
|
|
176
|
+
message: 'Approved verdict requires zero finalized findings'
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return { ok: true, verdict: summary.verdict };
|
|
180
|
+
}
|
|
162
181
|
function parseVerdict(reviewPath) {
|
|
163
182
|
if (!fs.existsSync(reviewPath)) {
|
|
164
|
-
return { ok: false, verdict: null, message: `Review artifact not found: ${path.basename(reviewPath)}` };
|
|
183
|
+
return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_NOT_FOUND', message: `Review artifact not found: ${path.basename(reviewPath)}` };
|
|
184
|
+
}
|
|
185
|
+
let content;
|
|
186
|
+
try {
|
|
187
|
+
content = fs.readFileSync(reviewPath, 'utf8');
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
return { ok: false, verdict: null, code: 'REVIEW_ARTIFACT_READ_FAILED', message: `Review artifact could not be read: ${String(error)}` };
|
|
165
191
|
}
|
|
166
|
-
const parsed = parseReviewSummary(
|
|
192
|
+
const parsed = parseReviewSummary(content);
|
|
167
193
|
if (!parsed.ok) {
|
|
168
194
|
return {
|
|
169
195
|
ok: false,
|
|
170
196
|
verdict: null,
|
|
197
|
+
code: parsed.code === 'REVIEW_SUMMARY_PLACEHOLDER_INVALID' ? parsed.code : 'REVIEW_SUMMARY_NOT_FOUND',
|
|
171
198
|
message: `cannot parse review summary in ${path.basename(reviewPath)}: ${parsed.message}`
|
|
172
199
|
};
|
|
173
200
|
}
|
|
174
|
-
|
|
175
|
-
if (verdict !== 'Approved')
|
|
176
|
-
return { ok: true, verdict };
|
|
177
|
-
return {
|
|
178
|
-
ok: true,
|
|
179
|
-
verdict: counts && equalCounts(counts, { blocker: 0, major: 0, minor: 0 })
|
|
180
|
-
? 'Approved'
|
|
181
|
-
: 'Approved-with-issues'
|
|
182
|
-
};
|
|
201
|
+
return resolveCanonicalVerdict(parsed.summary);
|
|
183
202
|
}
|
|
184
|
-
export { artifactName, equalCounts, escapeRegExp, extractSection, finalizeReviewSummaryContent, maxRound, normalizeVerdict, parseReviewSummary, parseVerdict };
|
|
203
|
+
export { artifactName, equalCounts, escapeRegExp, extractSection, finalizeReviewSummaryContent, maxRound, normalizeVerdict, parseReviewSummary, parseVerdict, resolveCanonicalVerdict };
|
|
185
204
|
//# sourceMappingURL=review-artifacts.js.map
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import { locateActivityLog, pairEntries } from './activity-log.js';
|
|
4
|
+
import { locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
|
|
5
5
|
import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
6
|
-
import {
|
|
6
|
+
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
7
7
|
import { finalizeReviewSummaryContent } from './review-artifacts.js';
|
|
8
|
+
import { inspectDecisionDetailDuplicates } from './decision-details.js';
|
|
8
9
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
9
10
|
import { validateLifecycleExecution } from './lifecycle-execution.js';
|
|
10
11
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
@@ -40,7 +41,7 @@ function openReviewRound(content, action, round) {
|
|
|
40
41
|
if (!section)
|
|
41
42
|
return false;
|
|
42
43
|
const expected = `${action} (Round ${round})`;
|
|
43
|
-
return pairEntries(section.entries).filter((row) => (row.step === expected &&
|
|
44
|
+
return startedBackedRows(pairEntries(section.entries)).filter((row) => (row.step === expected && !row.done)).length === 1;
|
|
44
45
|
}
|
|
45
46
|
function cleanupTemp(fileSystem, tempPath) {
|
|
46
47
|
try {
|
|
@@ -103,7 +104,11 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
|
|
|
103
104
|
}
|
|
104
105
|
let rows;
|
|
105
106
|
try {
|
|
106
|
-
|
|
107
|
+
const ledger = parseLedgerDocument(taskContent);
|
|
108
|
+
if (!ledger.present) {
|
|
109
|
+
return failed(request, 'REVIEW_LEDGER_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
|
|
110
|
+
}
|
|
111
|
+
rows = ledger.rows;
|
|
107
112
|
}
|
|
108
113
|
catch (error) {
|
|
109
114
|
return failed(request, 'REVIEW_LEDGER_INVALID', String(error), resolved.taskId);
|
|
@@ -113,6 +118,10 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
|
|
|
113
118
|
return failed(request, 'REVIEW_LEDGER_INVALID', `${ledgerError.code}: ${ledgerError.message}`, resolved.taskId);
|
|
114
119
|
}
|
|
115
120
|
const stageStatus = summarizeLedgerStage(rows, stage);
|
|
121
|
+
const detailInspection = inspectDecisionDetailDuplicates(artifactContent);
|
|
122
|
+
if (!detailInspection.ok) {
|
|
123
|
+
return failed(request, 'REVIEW_DECISION_DETAIL_INVALID', `${detailInspection.code}: ${detailInspection.message}`, resolved.taskId, stageStatus);
|
|
124
|
+
}
|
|
116
125
|
const transformed = finalizeReviewSummaryContent(artifactContent, stageStatus.unresolvedFindingCounts);
|
|
117
126
|
if (!transformed.ok) {
|
|
118
127
|
return failed(request, transformed.code, transformed.message, resolved.taskId, stageStatus);
|
|
@@ -44,6 +44,14 @@ export function extractReviewBaseline(content) {
|
|
|
44
44
|
const match = String(content).match(/^[-*]?\s*\*\*(?:审查基线提交|Review Baseline Commit)\*\*[::]\s*(.*?)\s*$/m);
|
|
45
45
|
return match ? match[1].trim().replace(/`/g, "") : "";
|
|
46
46
|
}
|
|
47
|
+
export function extractReviewTargetHead(content) {
|
|
48
|
+
const match = String(content).match(/^[-*]?\s*\*\*(?:审查目标提交|审查目标 HEAD|Review Target Commit|Review Target Head)\*\*[::]\s*(.*?)\s*$/m);
|
|
49
|
+
return match ? match[1].trim().replace(/`/g, "") : "";
|
|
50
|
+
}
|
|
51
|
+
export function extractReviewedHead(content) {
|
|
52
|
+
const match = String(content).match(/^[-*]?\s*\*\*(?:审查已检视提交|已检视提交|Reviewed Head Commit|Reviewed Head)\*\*[::]\s*(.*?)\s*$/m);
|
|
53
|
+
return match ? match[1].trim().replace(/`/g, "") : "";
|
|
54
|
+
}
|
|
47
55
|
export function extractReviewDiffBase(content) {
|
|
48
56
|
const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异基线|Reviewed Diff Base)\*\*[::]\s*(.*?)\s*$/m);
|
|
49
57
|
return match ? match[1].trim().replace(/`/g, "") : "";
|
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { extractReviewBaseline, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from "./verification-review.js";
|
|
7
|
+
import { extractReviewBaseline, extractReviewTargetHead, extractReviewedHead, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from "./verification-review.js";
|
|
8
8
|
import { check as checkPlatformSync } from "../platform/verification-sync.js";
|
|
9
9
|
import { check as checkRequiredChecks } from "../platform/verification-required.js";
|
|
10
10
|
import { inspectPlatformPullRequest } from "../platform/pull-requests.js";
|
|
@@ -12,11 +12,15 @@ import { resolveMaterializedReviewedHeadRelation } from "../platform/change-requ
|
|
|
12
12
|
import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.js";
|
|
13
13
|
import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.js";
|
|
14
14
|
import { parseTypedTaskFrontmatter } from "./frontmatter.js";
|
|
15
|
-
import {
|
|
15
|
+
import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from "./ledger.js";
|
|
16
|
+
import { isValidAgentInfraVersion } from "../version.js";
|
|
16
17
|
import { equalCounts, parseReviewSummary } from "./review-artifacts.js";
|
|
18
|
+
import { inspectDecisionDetailDuplicates } from "./decision-details.js";
|
|
17
19
|
import { loadVerificationConfig } from "./verification-config.js";
|
|
18
20
|
import { snapshotReview } from "../git/review-snapshot.js";
|
|
19
21
|
import { OrchestrationStateError, readRun } from "./orchestration.js";
|
|
22
|
+
import { resolveDeliveryTarget } from "./delivery-target.js";
|
|
23
|
+
import { readPrDeliveryFact } from "./pr-delivery-fact.js";
|
|
20
24
|
const TASK_ENUMS = {
|
|
21
25
|
type: ["feature", "bugfix", "refactor", "docs", "chore"],
|
|
22
26
|
workflow: ["feature-development", "bug-fix", "refactoring"],
|
|
@@ -34,7 +38,6 @@ const DEFAULT_REQUIRED_FIELDS = [
|
|
|
34
38
|
"assigned_to"
|
|
35
39
|
];
|
|
36
40
|
const DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?$/;
|
|
37
|
-
const AGENT_INFRA_VERSION_PATTERN = /^v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
38
41
|
const ACTIVITY_LOG_PATTERN = /^- (\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:[+-]\d{2}:\d{2})?) — \*\*(.+?)\*\* by (.+?) — (.+)$/;
|
|
39
42
|
// Start markers (action suffixed with ` [started]`) are excluded from the
|
|
40
43
|
// "latest action" / freshness computation so a step's in-flight marker never
|
|
@@ -89,6 +92,8 @@ function runCheck(type, context, shared) {
|
|
|
89
92
|
return checkTaskMeta(context);
|
|
90
93
|
case "artifact":
|
|
91
94
|
return checkArtifact(context);
|
|
95
|
+
case "decision-details":
|
|
96
|
+
return checkDecisionDetails(context);
|
|
92
97
|
case "implementation-input":
|
|
93
98
|
return checkImplementationInput(context);
|
|
94
99
|
case "activity-log":
|
|
@@ -173,13 +178,12 @@ function validateCleanCompletionEvidence(run) {
|
|
|
173
178
|
|| !shaPattern.test(evidence.headTree)
|
|
174
179
|
|| !shaPattern.test(evidence.worktreeTree)
|
|
175
180
|
|| !shaPattern.test(evidence.lastReviewedCommit)
|
|
176
|
-
|| !Number.isInteger(evidence.prNumber)
|
|
177
|
-
|| evidence.
|
|
178
|
-
|| !shaPattern.test(evidence.prHead)) {
|
|
181
|
+
|| !(evidence.prNumber === null || (Number.isInteger(evidence.prNumber) && evidence.prNumber > 0))
|
|
182
|
+
|| !(evidence.prHead === null || shaPattern.test(evidence.prHead))) {
|
|
179
183
|
return 'Clean completion evidence has an invalid structure';
|
|
180
184
|
}
|
|
181
185
|
if (evidence.head !== evidence.lastReviewedCommit
|
|
182
|
-
|| evidence.head !== evidence.prHead
|
|
186
|
+
|| (evidence.prHead !== null && evidence.head !== evidence.prHead)
|
|
183
187
|
|| evidence.headTree !== evidence.worktreeTree) {
|
|
184
188
|
return 'Clean completion evidence does not bind matching commit and tree identities';
|
|
185
189
|
}
|
|
@@ -248,10 +252,8 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
248
252
|
if (evidenceError)
|
|
249
253
|
return failResult('orchestration-evidence', evidenceError);
|
|
250
254
|
}
|
|
251
|
-
else if (run.status === 'completed'
|
|
252
|
-
|
|
253
|
-
|| run.receipts.at(-1)?.status !== 'consumed')) {
|
|
254
|
-
return failResult('orchestration-evidence', 'Completed run requires a final consumed commit receipt');
|
|
255
|
+
else if (run.status === 'completed') {
|
|
256
|
+
return failResult('orchestration-evidence', 'Completed run requires reviewed-head-clean completion evidence');
|
|
255
257
|
}
|
|
256
258
|
if (run.pendingDelegation && !['prepared', 'activated', 'stage-completed'].includes(run.pendingDelegation.status)) {
|
|
257
259
|
return failResult('orchestration-evidence', `Pending receipt has unsafe status '${run.pendingDelegation.status}'`);
|
|
@@ -352,18 +354,17 @@ function checkTaskMeta({ taskDir, config }) {
|
|
|
352
354
|
const metadata = task.metadata;
|
|
353
355
|
const requiredFields = config.required_fields || DEFAULT_REQUIRED_FIELDS;
|
|
354
356
|
const missingFields = requiredFields.filter((field) => isBlank(metadata[field]));
|
|
355
|
-
const
|
|
357
|
+
const currentTask = metadata.status === 'active';
|
|
358
|
+
const blockingMissingFields = missingFields.filter((field) => field !== "agent_infra_version" || currentTask);
|
|
356
359
|
const warnings = [];
|
|
357
|
-
if (missingFields.includes("agent_infra_version")) {
|
|
360
|
+
if (missingFields.includes("agent_infra_version") && !currentTask) {
|
|
358
361
|
warnings.push("field 'agent_infra_version' missing — historical task or skipped version stamp");
|
|
359
362
|
}
|
|
360
363
|
if (blockingMissingFields.length > 0) {
|
|
361
364
|
return failResult("task-meta", `Missing required fields: ${blockingMissingFields.join(", ")}`);
|
|
362
365
|
}
|
|
363
|
-
if (!
|
|
364
|
-
metadata.agent_infra_version
|
|
365
|
-
!AGENT_INFRA_VERSION_PATTERN.test(metadata.agent_infra_version)) {
|
|
366
|
-
return failResult("task-meta", `Invalid agent_infra_version: ${metadata.agent_infra_version}`);
|
|
366
|
+
if (currentTask && !isValidAgentInfraVersion(metadata.agent_infra_version)) {
|
|
367
|
+
return failResult("task-meta", `Invalid agent_infra_version: ${String(metadata.agent_infra_version ?? 'missing')}`);
|
|
367
368
|
}
|
|
368
369
|
const invalidDates = ["created_at", "updated_at", "completed_at", "blocked_at", "cancelled_at"]
|
|
369
370
|
.filter((field) => !isBlank(metadata[field]) && !DATE_TIME_PATTERN.test(metadata[field]));
|
|
@@ -432,19 +433,20 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
|
|
|
432
433
|
return failResult('required-pr-delivery', 'Project configuration is invalid');
|
|
433
434
|
}
|
|
434
435
|
const flow = projectConfig.prFlow;
|
|
435
|
-
const
|
|
436
|
-
|
|
436
|
+
const factRead = readPrDeliveryFact(task.metadata);
|
|
437
|
+
if (factRead.status === 'invalid')
|
|
438
|
+
return failResult('required-pr-delivery', factRead.error.message);
|
|
439
|
+
const fact = factRead.status === 'valid' ? factRead.fact : null;
|
|
440
|
+
const status = fact?.state || 'unbound';
|
|
441
|
+
const prNumber = fact?.state === 'bound' ? fact.identity.number : null;
|
|
437
442
|
if (flow === 'disabled')
|
|
438
443
|
return passResult('required-pr-delivery', 'Pull request delivery is disabled by project policy');
|
|
439
|
-
if (flow === 'required' && (status !== '
|
|
444
|
+
if (flow === 'required' && (status !== 'bound' || prNumber === null)) {
|
|
440
445
|
return failResult('required-pr-delivery', 'Project policy requires a bound pull request before completion');
|
|
441
446
|
}
|
|
442
|
-
if (flow !== 'required' && status === '
|
|
447
|
+
if (flow !== 'required' && status === 'unbound') {
|
|
443
448
|
return failResult('required-pr-delivery', 'Pull request delivery is pending; create a PR or explicitly skip it');
|
|
444
449
|
}
|
|
445
|
-
if (status === 'created' && prNumber === null) {
|
|
446
|
-
return failResult('required-pr-delivery', 'Task claims a created pull request without a valid pr_number');
|
|
447
|
-
}
|
|
448
450
|
if (flow === 'required') {
|
|
449
451
|
if (mode === 'gate') {
|
|
450
452
|
return passResult('required-pr-delivery', 'Bound pull request shape is valid; merged-state inspection is reserved for hard preflight');
|
|
@@ -456,13 +458,15 @@ function checkRequiredPrDelivery({ taskDir, repositoryRoot, mode }) {
|
|
|
456
458
|
const pullRequest = inspected.pullRequest;
|
|
457
459
|
const repository = inspected.platform.repository?.toLowerCase() || '';
|
|
458
460
|
const taskBranch = String(task.metadata.branch || '').trim();
|
|
461
|
+
const deliveryBaseRef = String(task.metadata.delivery_base_ref || projectConfig.delivery?.baseRef || '').trim();
|
|
459
462
|
if (inspected.status !== 'no-op' || !pullRequest || pullRequest.number !== prNumber) {
|
|
460
463
|
return failResult('required-pr-delivery', 'Bound pull request identity could not be verified');
|
|
461
464
|
}
|
|
462
465
|
if (!repository
|
|
463
466
|
|| pullRequest.repository.toLowerCase() !== repository
|
|
464
467
|
|| pullRequest.base.repository.toLowerCase() !== repository
|
|
465
|
-
|| (taskBranch && pullRequest.head.ref !== taskBranch)
|
|
468
|
+
|| (taskBranch && pullRequest.head.ref !== taskBranch)
|
|
469
|
+
|| (deliveryBaseRef && pullRequest.base.ref !== deliveryBaseRef)) {
|
|
466
470
|
return failResult('required-pr-delivery', 'Bound pull request identity does not match the task');
|
|
467
471
|
}
|
|
468
472
|
if (pullRequest.state !== 'closed' || !pullRequest.mergedAt || !pullRequest.mergeCommitSha) {
|
|
@@ -527,6 +531,21 @@ function checkArtifact({ taskDir, config, artifactFile }) {
|
|
|
527
531
|
}
|
|
528
532
|
return passResult("artifact", `${path.basename(artifactPath)} passed (${requiredSections.length} sections)`);
|
|
529
533
|
}
|
|
534
|
+
function checkDecisionDetails({ taskDir, artifactFile }) {
|
|
535
|
+
const resolvedArtifact = resolveArtifactPath(taskDir, "", artifactFile);
|
|
536
|
+
if (!resolvedArtifact.ok)
|
|
537
|
+
return failResult("decision-details", resolvedArtifact.message);
|
|
538
|
+
const artifactPath = resolvedArtifact.path;
|
|
539
|
+
const stat = safeStat(artifactPath);
|
|
540
|
+
if (!stat?.isFile())
|
|
541
|
+
return failResult("decision-details", `Artifact not found: ${path.basename(artifactPath)}`);
|
|
542
|
+
if (stat.size === 0)
|
|
543
|
+
return passResult("decision-details", `${path.basename(artifactPath)} contains no decision details`);
|
|
544
|
+
const inspection = inspectDecisionDetailDuplicates(fs.readFileSync(artifactPath, "utf8"));
|
|
545
|
+
if (!inspection.ok)
|
|
546
|
+
return failResult("decision-details", `${path.basename(artifactPath)}: ${inspection.message}`);
|
|
547
|
+
return passResult("decision-details", `${path.basename(artifactPath)} has no duplicate decision detail ids`);
|
|
548
|
+
}
|
|
530
549
|
function checkReviewSummary({ taskDir, config, artifactFile }) {
|
|
531
550
|
const stage = String(config.stage || "");
|
|
532
551
|
if (!["analysis", "plan", "code"].includes(stage)) {
|
|
@@ -547,7 +566,10 @@ function checkReviewSummary({ taskDir, config, artifactFile }) {
|
|
|
547
566
|
return failResult("review-summary", task.message);
|
|
548
567
|
let rows;
|
|
549
568
|
try {
|
|
550
|
-
|
|
569
|
+
const ledger = parseLedgerDocument(task.content);
|
|
570
|
+
if (!ledger.present)
|
|
571
|
+
return failResult("review-summary", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
|
|
572
|
+
rows = ledger.rows;
|
|
551
573
|
}
|
|
552
574
|
catch (error) {
|
|
553
575
|
return failResult("review-summary", `Invalid disagreement ledger: ${String(error)}`);
|
|
@@ -817,13 +839,12 @@ function checkReviewLedger({ taskDir, config }) {
|
|
|
817
839
|
if (!task.ok) {
|
|
818
840
|
return failResult("review-ledger", task.message);
|
|
819
841
|
}
|
|
820
|
-
const section = getSectionContent(task.content, LEDGER_SECTION_NAMES);
|
|
821
|
-
if (!section.trim()) {
|
|
822
|
-
return passResult("review-ledger", "No disagreement ledger section; treated as no open disagreements");
|
|
823
|
-
}
|
|
824
842
|
let rows;
|
|
825
843
|
try {
|
|
826
|
-
|
|
844
|
+
const ledger = parseLedgerDocument(task.content);
|
|
845
|
+
if (!ledger.present)
|
|
846
|
+
return failResult("review-ledger", `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`);
|
|
847
|
+
rows = ledger.rows.map((row) => [
|
|
827
848
|
row.id, row.stage, row.round, row.severity, row.status, row.evidence
|
|
828
849
|
]);
|
|
829
850
|
}
|
|
@@ -947,9 +968,13 @@ function checkPostReviewCommit({ taskDir, config }) {
|
|
|
947
968
|
const task = loadTask(taskDir);
|
|
948
969
|
if (!task.ok)
|
|
949
970
|
return failResult("post-review-commit", task.message);
|
|
971
|
+
const factRead = readPrDeliveryFact(task.metadata);
|
|
972
|
+
if (factRead.status === 'invalid')
|
|
973
|
+
return failResult("post-review-commit", factRead.error.message);
|
|
974
|
+
const fact = factRead.status === 'valid' ? factRead.fact : null;
|
|
950
975
|
const lastReviewedCommit = task.ok ? (task.metadata.last_reviewed_commit || "").trim() : "";
|
|
951
976
|
const globs = resolvePostReviewGlobs(config, loadPostReviewConfig(repoRoot));
|
|
952
|
-
const hasPullRequest =
|
|
977
|
+
const hasPullRequest = fact?.state === 'bound';
|
|
953
978
|
let inspected = null;
|
|
954
979
|
if (hasPullRequest) {
|
|
955
980
|
inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: gitRoot });
|
|
@@ -1068,7 +1093,11 @@ function formatPostReviewExemptions(exemptions) {
|
|
|
1068
1093
|
}
|
|
1069
1094
|
function resolvePostReviewExemption(content) {
|
|
1070
1095
|
try {
|
|
1071
|
-
const
|
|
1096
|
+
const ledger = parseLedgerDocument(content);
|
|
1097
|
+
if (!ledger.present) {
|
|
1098
|
+
return { ok: false, code: "POST_REVIEW_EXEMPTION_INVALID", message: `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}` };
|
|
1099
|
+
}
|
|
1100
|
+
const rows = ledger.rows;
|
|
1072
1101
|
const invalid = validateLedgerRows(rows);
|
|
1073
1102
|
if (invalid) {
|
|
1074
1103
|
return {
|
|
@@ -1101,28 +1130,46 @@ function checkReviewFact({ taskDir, artifactFile }) {
|
|
|
1101
1130
|
}
|
|
1102
1131
|
const content = fs.readFileSync(resolvedArtifact.path, "utf8");
|
|
1103
1132
|
const verdict = parseReviewVerdict(content);
|
|
1104
|
-
const
|
|
1133
|
+
const reviewTargetHead = extractReviewTargetHead(content);
|
|
1134
|
+
const reviewReviewedHead = extractReviewedHead(content);
|
|
1105
1135
|
const reviewDiffBase = extractReviewDiffBase(content);
|
|
1106
1136
|
const reviewedFingerprint = extractReviewDiffFingerprint(content);
|
|
1107
1137
|
const reviewedTree = extractReviewedSnapshotTree(content);
|
|
1108
1138
|
if (!["通过", "需要修改", "拒绝", "Approved", "Changes Requested", "Rejected"].includes(verdict)) {
|
|
1109
1139
|
return failResult("review-fact", `Unsupported review verdict '${verdict}'`);
|
|
1110
1140
|
}
|
|
1141
|
+
const deliveryRemote = String(task.metadata.delivery_remote || "").trim();
|
|
1142
|
+
const deliveryBaseRef = String(task.metadata.delivery_base_ref || "").trim();
|
|
1143
|
+
if (!deliveryRemote || !deliveryBaseRef) {
|
|
1144
|
+
return failResult("review-fact", "Task delivery target is not bound; re-run task branch preparation before review-code");
|
|
1145
|
+
}
|
|
1146
|
+
if (!reviewTargetHead || !reviewReviewedHead || !reviewDiffBase) {
|
|
1147
|
+
return failResult("review-fact", "Review fact must record target head, reviewed head, and diff base");
|
|
1148
|
+
}
|
|
1111
1149
|
let gitRoot;
|
|
1112
1150
|
let head;
|
|
1113
1151
|
let baseline;
|
|
1152
|
+
let targetHead;
|
|
1114
1153
|
let diffBase;
|
|
1115
1154
|
try {
|
|
1116
1155
|
gitRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
1117
1156
|
head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
1118
|
-
|
|
1119
|
-
|
|
1157
|
+
const target = resolveDeliveryTarget(gitRoot, { remote: deliveryRemote, baseRef: deliveryBaseRef });
|
|
1158
|
+
if (!target.ok)
|
|
1159
|
+
throw new Error(target.message);
|
|
1160
|
+
const reviewedRef = reviewReviewedHead;
|
|
1161
|
+
baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewedRef}^{commit}`], { encoding: "utf8" }).trim();
|
|
1162
|
+
targetHead = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewTargetHead}^{commit}`], { encoding: "utf8" }).trim();
|
|
1163
|
+
const computed = execFileSync("git", ["-C", gitRoot, "merge-base", baseline, targetHead], { encoding: "utf8" }).trim();
|
|
1164
|
+
diffBase = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewDiffBase}^{commit}`], { encoding: "utf8" }).trim();
|
|
1165
|
+
if (diffBase !== computed)
|
|
1166
|
+
throw new Error('saved review diff base does not match merge-base(reviewed head, target head)');
|
|
1120
1167
|
}
|
|
1121
1168
|
catch {
|
|
1122
|
-
return blockedResult("review-fact", `Unable to resolve review
|
|
1169
|
+
return blockedResult("review-fact", `Unable to resolve saved review commits in the task repository; re-run review-code`);
|
|
1123
1170
|
}
|
|
1124
1171
|
if (baseline !== head) {
|
|
1125
|
-
return failResult("review-fact", `
|
|
1172
|
+
return failResult("review-fact", `Reviewed head ${baseline.slice(0, 8)} does not match current HEAD ${head.slice(0, 8)}; re-run review-code`);
|
|
1126
1173
|
}
|
|
1127
1174
|
let actualSnapshot;
|
|
1128
1175
|
try {
|
|
@@ -1147,7 +1194,7 @@ function checkReviewFact({ taskDir, artifactFile }) {
|
|
|
1147
1194
|
const lastReviewedCommit = String(task.metadata.last_reviewed_commit || "").trim();
|
|
1148
1195
|
const cleanSnapshot = actualSnapshot.tree === execFileSync("git", ["-C", gitRoot, "rev-parse", `${baseline}^{tree}`], { encoding: "utf8" }).trim();
|
|
1149
1196
|
if (cleanSnapshot && lastReviewedCommit !== baseline) {
|
|
1150
|
-
return failResult("review-fact", `Approved clean review must set task last_reviewed_commit to
|
|
1197
|
+
return failResult("review-fact", `Approved clean review must set task last_reviewed_commit to reviewed head ${baseline.slice(0, 8)}`);
|
|
1151
1198
|
}
|
|
1152
1199
|
if (!cleanSnapshot && lastReviewedCommit) {
|
|
1153
1200
|
return failResult("review-fact", "Approved review with uncommitted changes must remain unanchored until commit");
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { extractReviewBaseline, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from './review-fingerprint.js';
|
|
2
|
-
export {
|
|
1
|
+
export { extractReviewBaseline, extractReviewTargetHead, extractReviewedHead, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from './review-fingerprint.js';
|
|
2
|
+
export { parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
3
3
|
//# sourceMappingURL=verification-review.js.map
|
|
@@ -26,12 +26,6 @@ function captureWorktreeTree(repoRoot, forcedPath) {
|
|
|
26
26
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
-
function captureLegacyWorkspaceSnapshot(repoRoot, taskId) {
|
|
30
|
-
const workspaceRelative = taskId
|
|
31
|
-
? `.agents/workspace/active/${taskId}`
|
|
32
|
-
: '.agents/workspace/active';
|
|
33
|
-
return captureWorktreeTree(repoRoot, workspaceRelative);
|
|
34
|
-
}
|
|
35
29
|
function taskFileFingerprints(stateRoot, taskId) {
|
|
36
30
|
const activeRoot = path.join(stateRoot, '.agents', 'workspace', 'active');
|
|
37
31
|
const roots = taskId ? [path.join(activeRoot, taskId)] : [activeRoot];
|
|
@@ -46,6 +40,8 @@ function taskFileFingerprints(stateRoot, taskId) {
|
|
|
46
40
|
}
|
|
47
41
|
if (!stat.isFile() && !stat.isSymbolicLink())
|
|
48
42
|
return;
|
|
43
|
+
if (relative.startsWith('.agents/workspace/active/') && path.posix.basename(relative) === 'orchestration.json')
|
|
44
|
+
return;
|
|
49
45
|
const content = stat.isSymbolicLink()
|
|
50
46
|
? Buffer.from(fs.readlinkSync(absolute))
|
|
51
47
|
: fs.readFileSync(absolute);
|
|
@@ -81,13 +77,11 @@ function decodeWorkspaceFingerprint(value) {
|
|
|
81
77
|
throw new Error(`ORCHESTRATION_SNAPSHOT_FAILED: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
78
|
}
|
|
83
79
|
}
|
|
84
|
-
function captureWorkspaceSnapshot(
|
|
85
|
-
if (typeof contextOrRoot === 'string')
|
|
86
|
-
return captureLegacyWorkspaceSnapshot(contextOrRoot, taskId ?? null);
|
|
80
|
+
function captureWorkspaceSnapshot(context) {
|
|
87
81
|
return encodeWorkspaceFingerprint({
|
|
88
82
|
version: 2,
|
|
89
|
-
gitTree: captureWorktreeTree(
|
|
90
|
-
taskFiles: taskFileFingerprints(
|
|
83
|
+
gitTree: captureWorktreeTree(context.gitRoot, null),
|
|
84
|
+
taskFiles: taskFileFingerprints(context.stateRoot, context.taskId)
|
|
91
85
|
});
|
|
92
86
|
}
|
|
93
87
|
function captureRepositorySnapshot(repoRoot) {
|