@fitlab-ai/agent-infra 0.9.11 → 0.9.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/README.zh-CN.md +13 -13
- package/dist/bin/internal-cli.js +29 -19
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/defaults.json +4 -0
- package/dist/lib/git/workflow.js +43 -1
- package/dist/lib/init.js +1 -0
- package/dist/lib/internal/git-workflow.js +8 -1
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +9 -9
- package/dist/lib/internal/platform-context.js +2 -2
- package/dist/lib/internal/platform-issue.js +8 -9
- package/dist/lib/internal/platform-pr-review.js +10 -10
- package/dist/lib/internal/platform-pr.js +68 -23
- package/dist/lib/internal/platform-release-notes.js +3 -3
- package/dist/lib/internal/pr-review-grade.js +11 -9
- package/dist/lib/internal/release-workflow.js +4 -4
- package/dist/lib/internal/sandbox-control.js +30 -4
- package/dist/lib/internal/task-activity.js +3 -3
- package/dist/lib/internal/task-artifact.js +76 -24
- package/dist/lib/internal/task-create.js +2 -2
- package/dist/lib/internal/task-delivery.js +59 -0
- package/dist/lib/internal/task-event.js +6 -3
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-invalidation.js +60 -0
- package/dist/lib/internal/task-ledger.js +9 -6
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-override.js +4 -4
- package/dist/lib/internal/task-review.js +3 -3
- package/dist/lib/internal/task-verify.js +6 -6
- package/dist/lib/internal/task-warning.js +3 -3
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +8 -2
- package/dist/lib/platform/github-provider.js +903 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +94 -41
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +551 -0
- package/dist/lib/platform/pr-checks.js +124 -106
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +523 -49
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +487 -0
- package/dist/lib/platform/provider-contract.js +95 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +504 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +1050 -502
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +22 -12
- package/dist/lib/platform/verification-sync.js +118 -239
- package/dist/lib/pr-review/evidence-grading.js +30 -9
- package/dist/lib/run/index.js +63 -23
- package/dist/lib/sandbox/config.js +5 -0
- package/dist/lib/sandbox/control/client.js +96 -15
- package/dist/lib/sandbox/control/executor.js +3 -3
- package/dist/lib/sandbox/control/lifecycle.js +29 -7
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +398 -25
- package/dist/lib/sandbox/control/state.js +204 -1
- package/dist/lib/server/process-state.js +2 -2
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +35 -7
- package/dist/lib/task/capabilities.js +239 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-intent.js +100 -0
- package/dist/lib/task/commit-operation.js +207 -102
- package/dist/lib/task/control-authority.js +8 -5
- package/dist/lib/task/create-service.js +8 -8
- package/dist/lib/task/create.js +33 -7
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +3 -48
- package/dist/lib/task/delivery-target.js +97 -0
- package/dist/lib/task/delivery.js +153 -0
- package/dist/lib/task/events.js +297 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +61 -36
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/lifecycle.js +106 -9
- package/dist/lib/task/local-artifact-finalization.js +374 -0
- package/dist/lib/task/orchestration.js +100 -151
- package/dist/lib/task/pr-delivery-fact.js +183 -0
- package/dist/lib/task/review-finalization.js +4 -8
- package/dist/lib/task/review-fingerprint.js +32 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-engine.js +89 -38
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/update.js +9 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/defaults.json +4 -0
- package/lib/git/workflow.ts +38 -1
- package/lib/init.ts +4 -1
- package/lib/internal/git-workflow.ts +7 -1
- package/lib/internal/platform-checks.ts +3 -3
- package/lib/internal/platform-comment.ts +8 -8
- package/lib/internal/platform-context.ts +2 -2
- package/lib/internal/platform-issue.ts +7 -8
- package/lib/internal/platform-pr-review.ts +9 -9
- package/lib/internal/platform-pr.ts +53 -21
- package/lib/internal/platform-release-notes.ts +3 -3
- package/lib/internal/pr-review-grade.ts +10 -8
- package/lib/internal/release-workflow.ts +4 -4
- package/lib/internal/sandbox-control.ts +27 -3
- package/lib/internal/task-activity.ts +3 -3
- package/lib/internal/task-artifact.ts +65 -22
- package/lib/internal/task-create.ts +2 -2
- package/lib/internal/task-delivery.ts +53 -0
- package/lib/internal/task-event.ts +6 -3
- package/lib/internal/task-finalization.ts +3 -3
- package/lib/internal/task-invalidation.ts +39 -0
- package/lib/internal/task-ledger.ts +11 -8
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-override.ts +6 -6
- package/lib/internal/task-review.ts +3 -3
- package/lib/internal/task-verify.ts +6 -6
- package/lib/internal/task-warning.ts +3 -3
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +10 -3
- package/lib/platform/github-provider.ts +910 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +97 -47
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +645 -0
- package/lib/platform/pr-checks.ts +129 -107
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +562 -52
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +536 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +529 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +1126 -505
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +21 -13
- package/lib/platform/verification-sync.ts +118 -293
- package/lib/pr-review/evidence-grading.ts +31 -11
- package/lib/run/index.ts +57 -22
- package/lib/sandbox/config.ts +7 -0
- package/lib/sandbox/control/client.ts +105 -15
- package/lib/sandbox/control/executor.ts +4 -4
- package/lib/sandbox/control/lifecycle.ts +30 -8
- package/lib/sandbox/control/protocol.ts +113 -0
- package/lib/sandbox/control/server.ts +431 -23
- package/lib/sandbox/control/state.ts +251 -1
- package/lib/server/process-state.ts +2 -2
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +34 -8
- package/lib/task/capabilities.ts +262 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-intent.ts +137 -0
- package/lib/task/commit-operation.ts +186 -88
- package/lib/task/control-authority.ts +11 -8
- package/lib/task/create-service.ts +8 -8
- package/lib/task/create.ts +34 -9
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +8 -64
- package/lib/task/delivery-target.ts +130 -0
- package/lib/task/delivery.ts +160 -0
- package/lib/task/events.ts +296 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +73 -46
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/lifecycle.ts +93 -4
- package/lib/task/local-artifact-finalization.ts +563 -0
- package/lib/task/orchestration.ts +110 -146
- package/lib/task/pr-delivery-fact.ts +237 -0
- package/lib/task/review-finalization.ts +4 -14
- package/lib/task/review-fingerprint.ts +33 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-engine.ts +95 -43
- package/lib/task/verification-review.ts +2 -0
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +11 -1
- package/lib/version.ts +15 -1
- package/package.json +7 -1
- package/templates/.agents/rules/README.en.md +4 -2
- package/templates/.agents/rules/README.zh-CN.md +4 -2
- package/templates/.agents/rules/create-issue.en.md +11 -2
- package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
- package/templates/.agents/rules/issue-pr-commands.en.md +4 -17
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +28 -5
- package/templates/.agents/rules/issue-sync.zh-CN.md +64 -5
- package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
- package/templates/.agents/rules/local-artifact-repair.en.md +53 -0
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +53 -0
- package/templates/.agents/rules/milestone-inference.en.md +12 -2
- package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
- package/templates/.agents/rules/pr-sync.en.md +28 -13
- package/templates/.agents/rules/pr-sync.zh-CN.md +39 -5
- package/templates/.agents/rules/release-commands.en.md +42 -3
- package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
- package/templates/.agents/rules/security-alerts.en.md +17 -2
- package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
- package/templates/.agents/rules/sync-content-generation.en.md +8 -0
- package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
- package/templates/.agents/rules/task-management.en.md +1 -1
- package/templates/.agents/rules/task-management.zh-CN.md +1 -1
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/scripts/security-alerts.github.sh +151 -0
- package/templates/.agents/scripts/security-alerts.sh +31 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +14 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -3
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +32 -10
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +35 -13
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/commit/SKILL.en.md +3 -3
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/config/verify.json +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +4 -4
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +4 -4
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +5 -4
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +5 -4
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -13
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +30 -13
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.en.md +11 -10
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +11 -11
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/create-task/SKILL.en.md +1 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
- package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
- package/templates/.agents/skills/plan-task/SKILL.en.md +14 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +11 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +6 -5
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +6 -5
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +10 -9
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +10 -9
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +6 -5
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +6 -5
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +4 -0
- package/templates/.agents/skills/watch-pr/SKILL.en.md +6 -6
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +23 -2
- package/templates/.agents/templates/task.zh-CN.md +23 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/feature-development.en.yaml +9 -8
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
- package/templates/.agents/workflows/refactoring.en.yaml +9 -8
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
- package/templates/.github/workflows/pr-label.yml +7 -26
- package/templates/.agents/rules/create-issue.github.en.md +0 -14
- package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
- package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
- package/templates/.agents/rules/issue-sync.github.en.md +0 -40
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
- package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
- package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
- package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
- package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
- package/templates/.agents/rules/pr-sync.github.en.md +0 -43
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
- package/templates/.agents/rules/release-commands.github.en.md +0 -44
- package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -44
- package/templates/.agents/rules/security-alerts.github.en.md +0 -43
- package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
- /package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +0 -0
- /package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +0 -0
|
@@ -19,6 +19,9 @@ import { applyWorkflowWarningIntent } from './workflow-warning-intents.js';
|
|
|
19
19
|
import { verifyTaskEvent } from './verification.js';
|
|
20
20
|
import { verifyInProcess } from './verification-engine.js';
|
|
21
21
|
import { canonicalTimestamp, writeTask } from './write.js';
|
|
22
|
+
import { readPrDeliveryFact } from './pr-delivery-fact.js';
|
|
23
|
+
import { resourceIdentity, resourceIdentityNumber } from '../platform/provider-bridge.js';
|
|
24
|
+
import { taskIssueIdentity } from '../platform/task-identities.js';
|
|
22
25
|
const LIFECYCLE_PRODUCER = 'lifecycle.apply';
|
|
23
26
|
const HUMAN_OVERRIDE_SECTION_ALIASES = ['人工豁免账本', 'Human Override Ledger'];
|
|
24
27
|
const HUMAN_OVERRIDE_COLUMNS = [
|
|
@@ -401,12 +404,20 @@ function snapshotEvidence(taskId, failureIdValue, target, content, frontmatter,
|
|
|
401
404
|
}
|
|
402
405
|
function resourceForSnapshot(policy, options, frontmatter) {
|
|
403
406
|
if (policy.producerId === 'platform.issue') {
|
|
404
|
-
const
|
|
405
|
-
|
|
407
|
+
const identity = options.probeIssueNumber !== undefined
|
|
408
|
+
? resourceIdentity(options.probeIssueNumber)
|
|
409
|
+
: taskIssueIdentity((frontmatter || {})) || (() => {
|
|
410
|
+
const number = toPositiveInteger(frontmatter?.issue_number);
|
|
411
|
+
return number === null ? null : resourceIdentity(number);
|
|
412
|
+
})();
|
|
413
|
+
return identity ? { kind: 'issue', identity, number: resourceIdentityNumber(identity) } : null;
|
|
406
414
|
}
|
|
407
415
|
if (policy.producerId === 'platform.pull-request') {
|
|
408
|
-
const
|
|
409
|
-
|
|
416
|
+
const fact = frontmatter ? readPrDeliveryFact(frontmatter) : null;
|
|
417
|
+
const identity = options.probePullRequestNumber !== undefined
|
|
418
|
+
? resourceIdentity(options.probePullRequestNumber)
|
|
419
|
+
: fact?.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
420
|
+
return identity ? { kind: 'pull-request', identity, number: resourceIdentityNumber(identity) } : null;
|
|
410
421
|
}
|
|
411
422
|
return null;
|
|
412
423
|
}
|
|
@@ -459,7 +470,7 @@ function probeResult(policy, result, state) {
|
|
|
459
470
|
function probeNotObserved(policy) {
|
|
460
471
|
return error('OVERRIDE_FAILURE_NOT_PRESENT', `producer ${policy.producerId} did not produce ${policy.code}`);
|
|
461
472
|
}
|
|
462
|
-
function probePlatformFailure(taskRef, policy, resolved, options) {
|
|
473
|
+
async function probePlatformFailure(taskRef, policy, resolved, options) {
|
|
463
474
|
let frontmatter;
|
|
464
475
|
try {
|
|
465
476
|
frontmatter = parseTypedTaskFrontmatter(fs.readFileSync(path.join(resolved.taskDir, 'task.md'), 'utf8'));
|
|
@@ -467,29 +478,40 @@ function probePlatformFailure(taskRef, policy, resolved, options) {
|
|
|
467
478
|
catch (cause) {
|
|
468
479
|
return error('OVERRIDE_DOCUMENT_INVALID', cause instanceof Error ? cause.message : String(cause));
|
|
469
480
|
}
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
481
|
+
const issueIdentity = options.probeIssueNumber !== undefined
|
|
482
|
+
? resourceIdentity(options.probeIssueNumber)
|
|
483
|
+
: taskIssueIdentity(frontmatter) || (() => {
|
|
484
|
+
const number = toPositiveInteger(frontmatter.issue_number);
|
|
485
|
+
return number === null ? null : resourceIdentity(number);
|
|
486
|
+
})();
|
|
487
|
+
const fact = readPrDeliveryFact(frontmatter);
|
|
488
|
+
const pullRequestIdentity = options.probePullRequestNumber !== undefined
|
|
489
|
+
? resourceIdentity(options.probePullRequestNumber)
|
|
490
|
+
: fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
491
|
+
const token = (identity) => identity ? String(identity.value) : null;
|
|
492
|
+
const issueToken = token(issueIdentity);
|
|
493
|
+
const pullRequestToken = token(pullRequestIdentity);
|
|
494
|
+
const operation = await (policy.producerId === 'platform.issue'
|
|
495
|
+
? issueToken === null
|
|
474
496
|
? null
|
|
475
497
|
: bindPlatformIssue(taskRef, {
|
|
476
|
-
issue:
|
|
498
|
+
issue: issueToken,
|
|
477
499
|
agent: options.probeOperator ?? 'local-operator',
|
|
478
500
|
dryRun: true,
|
|
479
501
|
cwd: resolved.repoRoot,
|
|
480
502
|
...(options.platformClient ? { client: options.platformClient } : {})
|
|
481
503
|
})
|
|
482
|
-
:
|
|
504
|
+
: pullRequestToken === null
|
|
483
505
|
? null
|
|
484
506
|
: bindPlatformPullRequest(taskRef, {
|
|
485
|
-
pr:
|
|
507
|
+
pr: pullRequestToken,
|
|
486
508
|
agent: options.probeOperator ?? 'local-operator',
|
|
487
509
|
dryRun: true,
|
|
488
510
|
cwd: resolved.repoRoot,
|
|
489
511
|
...(options.platformClient ? { client: options.platformClient } : {})
|
|
490
|
-
});
|
|
512
|
+
}));
|
|
491
513
|
if (!operation)
|
|
492
|
-
return error('OVERRIDE_PROBE_INPUT_MISSING', `${policy.producerId} requires a bound issue or pull request
|
|
514
|
+
return error('OVERRIDE_PROBE_INPUT_MISSING', `${policy.producerId} requires a bound issue or pull request identity`);
|
|
493
515
|
if (operation.status !== 'failed' && operation.status !== 'blocked')
|
|
494
516
|
return probeNotObserved(policy);
|
|
495
517
|
if (!operation.error)
|
|
@@ -527,6 +549,9 @@ function probeTaskEventFailure(taskRef, policy, resolved) {
|
|
|
527
549
|
event: candidate.event,
|
|
528
550
|
agent: 'codex',
|
|
529
551
|
dryRun: true,
|
|
552
|
+
initiator: 'model',
|
|
553
|
+
requestId: `override-probe:${taskRef}:${candidate.event}`,
|
|
554
|
+
reasonCode: 'user-request',
|
|
530
555
|
...(candidate.artifact ? { artifact: candidate.artifact } : {})
|
|
531
556
|
}, { repoRoot: resolved.repoRoot, lockAlreadyHeld: true });
|
|
532
557
|
const matched = probeResult(policy, result, resolved.state);
|
|
@@ -535,8 +560,8 @@ function probeTaskEventFailure(taskRef, policy, resolved) {
|
|
|
535
560
|
}
|
|
536
561
|
return probeNotObserved(policy);
|
|
537
562
|
}
|
|
538
|
-
function probeVerificationFailure(taskRef, policy, resolved) {
|
|
539
|
-
const result = verifyTaskEvent({
|
|
563
|
+
async function probeVerificationFailure(taskRef, policy, resolved) {
|
|
564
|
+
const result = await verifyTaskEvent({
|
|
540
565
|
taskRef,
|
|
541
566
|
event: 'review-code.completed',
|
|
542
567
|
artifact: policy.code === 'VERIFY_ARTIFACT_INVALID' ? 'invalid-artifact.md' : 'review-code.md'
|
|
@@ -559,7 +584,7 @@ function latestArtifact(taskDir, family) {
|
|
|
559
584
|
});
|
|
560
585
|
return candidates[0];
|
|
561
586
|
}
|
|
562
|
-
function probeVerificationEngineFailure(policy, resolved) {
|
|
587
|
+
async function probeVerificationEngineFailure(policy, resolved) {
|
|
563
588
|
const candidates = [
|
|
564
589
|
{ skillName: 'review-code', family: 'review-code' },
|
|
565
590
|
{ skillName: 'review-plan', family: 'review-plan' },
|
|
@@ -574,7 +599,7 @@ function probeVerificationEngineFailure(policy, resolved) {
|
|
|
574
599
|
if (candidate.family && !artifactFile)
|
|
575
600
|
continue;
|
|
576
601
|
try {
|
|
577
|
-
const payload = verifyInProcess({
|
|
602
|
+
const payload = await verifyInProcess({
|
|
578
603
|
mode: 'gate',
|
|
579
604
|
skillName: candidate.skillName,
|
|
580
605
|
taskDir: resolved.taskDir,
|
|
@@ -598,7 +623,7 @@ function probeVerificationEngineFailure(policy, resolved) {
|
|
|
598
623
|
}
|
|
599
624
|
return probeNotObserved(policy);
|
|
600
625
|
}
|
|
601
|
-
function probeGenericFailure(taskRef, policy, resolved, options = {}) {
|
|
626
|
+
async function probeGenericFailure(taskRef, policy, resolved, options = {}) {
|
|
602
627
|
switch (policy.producerId) {
|
|
603
628
|
case 'task.resolve':
|
|
604
629
|
return probeNotObserved(policy);
|
|
@@ -607,9 +632,9 @@ function probeGenericFailure(taskRef, policy, resolved, options = {}) {
|
|
|
607
632
|
case 'task-event':
|
|
608
633
|
return probeTaskEventFailure(taskRef, policy, resolved);
|
|
609
634
|
case 'task-verify':
|
|
610
|
-
return probeVerificationFailure(taskRef, policy, resolved);
|
|
635
|
+
return await probeVerificationFailure(taskRef, policy, resolved);
|
|
611
636
|
case 'verification-engine':
|
|
612
|
-
return probeVerificationEngineFailure(policy, resolved);
|
|
637
|
+
return await probeVerificationEngineFailure(policy, resolved);
|
|
613
638
|
case 'activity-intent':
|
|
614
639
|
return probeResult(policy, applyPrReviewActivityIntent({
|
|
615
640
|
kind: 'pr-review-start', taskRef, agent: 'codex', artifact: 'pr-review.md', head: '0'.repeat(40), dryRun: true
|
|
@@ -645,7 +670,7 @@ function probeGenericFailure(taskRef, policy, resolved, options = {}) {
|
|
|
645
670
|
}
|
|
646
671
|
case 'platform.issue':
|
|
647
672
|
case 'platform.pull-request':
|
|
648
|
-
return probePlatformFailure(taskRef, policy, resolved, options);
|
|
673
|
+
return await probePlatformFailure(taskRef, policy, resolved, options);
|
|
649
674
|
default:
|
|
650
675
|
return error('OVERRIDE_PROBE_INPUT_MISSING', `no producer probe is registered for ${policy.producerId}`);
|
|
651
676
|
}
|
|
@@ -653,7 +678,7 @@ function probeGenericFailure(taskRef, policy, resolved, options = {}) {
|
|
|
653
678
|
function isProducerFailure(value) {
|
|
654
679
|
return 'observedFacts' in value && 'state' in value && Array.isArray(value.observedFacts);
|
|
655
680
|
}
|
|
656
|
-
function captureFailureSnapshot(taskRef, failureIdValue, target, options = {}) {
|
|
681
|
+
async function captureFailureSnapshot(taskRef, failureIdValue, target, options = {}) {
|
|
657
682
|
const policy = policyFor(failureIdValue);
|
|
658
683
|
if (!policy)
|
|
659
684
|
return error('OVERRIDE_FAILURE_UNKNOWN', `failure policy '${failureIdValue}' is not registered`);
|
|
@@ -676,7 +701,7 @@ function captureFailureSnapshot(taskRef, failureIdValue, target, options = {}) {
|
|
|
676
701
|
catch { /* probe reports an unconfirmed identity */ }
|
|
677
702
|
const producer = policy.producerId === LIFECYCLE_PRODUCER
|
|
678
703
|
? probeLifecycleFailure(taskRef, policy, resolved, options)
|
|
679
|
-
: probeGenericFailure(taskRef, policy, resolved, options);
|
|
704
|
+
: await probeGenericFailure(taskRef, policy, resolved, options);
|
|
680
705
|
if (!isProducerFailure(producer))
|
|
681
706
|
return producer;
|
|
682
707
|
const digest = computeFailureDigest(snapshotEvidence(resolved.taskId, failureIdValue, target, content, frontmatter, producer, resourceForSnapshot(policy, options, frontmatter)));
|
|
@@ -733,7 +758,7 @@ function validateResourceNumbers(request) {
|
|
|
733
758
|
}
|
|
734
759
|
return null;
|
|
735
760
|
}
|
|
736
|
-
function issueHumanOverride(request, options = {}) {
|
|
761
|
+
async function issueHumanOverride(request, options = {}) {
|
|
737
762
|
const invalid = validateCommon(request);
|
|
738
763
|
if (invalid)
|
|
739
764
|
return failedIssue(invalid.code, invalid.message);
|
|
@@ -762,7 +787,7 @@ function issueHumanOverride(request, options = {}) {
|
|
|
762
787
|
return failedIssue(document.code, document.message, resolved.taskId);
|
|
763
788
|
if (document.frontmatter.id !== resolved.taskId)
|
|
764
789
|
return failedIssue('OVERRIDE_DOCUMENT_INVALID', 'task frontmatter id does not match resolved task', resolved.taskId);
|
|
765
|
-
const snapshotResult = captureFailureSnapshot(request.taskRef, request.failureId, request.target, {
|
|
790
|
+
const snapshotResult = await captureFailureSnapshot(request.taskRef, request.failureId, request.target, {
|
|
766
791
|
...options,
|
|
767
792
|
probeIntent: request.intent,
|
|
768
793
|
probeOperator: request.operator,
|
|
@@ -888,14 +913,14 @@ function lifecycleRequestFor(taskRef, intent, operator, reason, values = {}) {
|
|
|
888
913
|
return error('OVERRIDE_EFFECT_INPUT_MISSING', 'restore requires the original staging directory and issue number');
|
|
889
914
|
return { taskRef, intent: 'restore', agent: operator, stagingDir, issueNumber };
|
|
890
915
|
}
|
|
891
|
-
function applyOutcomeEffect(request, row, snapshot, outcome, options) {
|
|
916
|
+
async function applyOutcomeEffect(request, row, snapshot, outcome, options) {
|
|
892
917
|
if (outcome.effect === 'no-write' || outcome.effect === 'record-only')
|
|
893
918
|
return null;
|
|
894
919
|
const intent = outcome.target === 'safe-close' ? 'cancel' : row.intent || request.intent;
|
|
895
920
|
if (!intent && outcome.target === 'continue-local') {
|
|
896
921
|
if (!options.effectExecutor)
|
|
897
922
|
return error('OVERRIDE_EFFECT_EXECUTOR_MISSING', 'the producer must supply an effect executor for a continue-local outcome');
|
|
898
|
-
return options.effectExecutor({
|
|
923
|
+
return await options.effectExecutor({
|
|
899
924
|
failureId: request.failureId,
|
|
900
925
|
operator: row.operator ?? 'local-operator',
|
|
901
926
|
reason: row.reason ?? snapshot.message
|
|
@@ -988,7 +1013,7 @@ function snapshotFromTicket(taskId, row) {
|
|
|
988
1013
|
source: 'producer-result'
|
|
989
1014
|
};
|
|
990
1015
|
}
|
|
991
|
-
function consumeHumanOverride(request, options = {}) {
|
|
1016
|
+
async function consumeHumanOverride(request, options = {}) {
|
|
992
1017
|
const values = [request.taskRef, request.ticketId, request.failureId, request.target, request.scope];
|
|
993
1018
|
if (values.some((value) => !singleLine(value))) {
|
|
994
1019
|
return failedConsume('OVERRIDE_PAYLOAD_INVALID', 'consume fields must be non-empty single-line values', request.ticketId || null);
|
|
@@ -1040,7 +1065,7 @@ function consumeHumanOverride(request, options = {}) {
|
|
|
1040
1065
|
snapshot = recoverySnapshot;
|
|
1041
1066
|
}
|
|
1042
1067
|
else {
|
|
1043
|
-
const snapshotResult = captureFailureSnapshot(request.taskRef, request.failureId, request.target, {
|
|
1068
|
+
const snapshotResult = await captureFailureSnapshot(request.taskRef, request.failureId, request.target, {
|
|
1044
1069
|
...options,
|
|
1045
1070
|
probeIntent: row.values.intent ? row.values.intent : request.intent,
|
|
1046
1071
|
probeOperator: row.values.operator,
|
|
@@ -1094,7 +1119,7 @@ function consumeHumanOverride(request, options = {}) {
|
|
|
1094
1119
|
return failedConsume('OVERRIDE_WRITE_INVALID', 'override writes cannot be planned', request.ticketId, resolved.taskId);
|
|
1095
1120
|
effectRow = { ...row.values, status: 'consuming', effect: outcome.effect, result: outcome.result, context_id: outcome.contextId, residual: `effect pending: ${outcome.residual}` };
|
|
1096
1121
|
}
|
|
1097
|
-
const effectError = applyOutcomeEffect(request, effectRow, snapshot, outcome, options);
|
|
1122
|
+
const effectError = await applyOutcomeEffect(request, effectRow, snapshot, outcome, options);
|
|
1098
1123
|
if (effectError)
|
|
1099
1124
|
return failedConsume(effectError.code, effectError.message, request.ticketId, resolved.taskId);
|
|
1100
1125
|
const after = resolveTask(request.taskRef, options);
|
|
@@ -1134,7 +1159,7 @@ function diagnoseHumanOverride(failure, target) {
|
|
|
1134
1159
|
error: null
|
|
1135
1160
|
};
|
|
1136
1161
|
}
|
|
1137
|
-
function diagnoseHumanOverrideForTask(taskRef, failure, target, options = {}) {
|
|
1162
|
+
async function diagnoseHumanOverrideForTask(taskRef, failure, target, options = {}) {
|
|
1138
1163
|
const resolved = resolveTask(taskRef, options);
|
|
1139
1164
|
if (!resolved.ok) {
|
|
1140
1165
|
return {
|
|
@@ -1173,12 +1198,12 @@ function diagnoseHumanOverrideForTask(taskRef, failure, target, options = {}) {
|
|
|
1173
1198
|
error: error('OVERRIDE_FAILURE_UNKNOWN', `failure policy '${failure}' is not registered`)
|
|
1174
1199
|
};
|
|
1175
1200
|
}
|
|
1176
|
-
const probes = policies
|
|
1201
|
+
const probes = await Promise.all(policies
|
|
1177
1202
|
.filter((policy) => !target || policy.targets.includes(target))
|
|
1178
|
-
.map((policy) => {
|
|
1203
|
+
.map(async (policy) => {
|
|
1179
1204
|
const producer = policy.producerId === LIFECYCLE_PRODUCER
|
|
1180
1205
|
? probeLifecycleFailure(taskRef, policy, resolved, options)
|
|
1181
|
-
: probeGenericFailure(taskRef, policy, resolved, options);
|
|
1206
|
+
: await probeGenericFailure(taskRef, policy, resolved, options);
|
|
1182
1207
|
const observed = isProducerFailure(producer);
|
|
1183
1208
|
return {
|
|
1184
1209
|
failureId: policy.id,
|
|
@@ -1192,7 +1217,7 @@ function diagnoseHumanOverrideForTask(taskRef, failure, target, options = {}) {
|
|
|
1192
1217
|
message: observed ? producer.message : producer.message,
|
|
1193
1218
|
errorCode: observed ? null : producer.code
|
|
1194
1219
|
};
|
|
1195
|
-
});
|
|
1220
|
+
}));
|
|
1196
1221
|
const blockedBy = probes.filter((probe) => probe.status === 'observed');
|
|
1197
1222
|
return {
|
|
1198
1223
|
status: 'ready',
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { parseArtifactReceipts, sha256File } from './artifact-receipts.js';
|
|
4
|
+
import { invalidationMutation, parseInvalidationDocument, reconcileInvalidation } from './invalidation.js';
|
|
5
|
+
import { resolveTaskRef } from './resolve-ref.js';
|
|
6
|
+
import { writeTask } from './write.js';
|
|
7
|
+
function reconcileTaskInvalidation(taskRef, options = {}) {
|
|
8
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
9
|
+
if (!resolved.ok)
|
|
10
|
+
return { status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: 0, error: { code: resolved.code, message: resolved.message } };
|
|
11
|
+
let content;
|
|
12
|
+
try {
|
|
13
|
+
content = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
return { status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: 0, error: { code: 'TASK_READ_FAILED', message: error instanceof Error ? error.message : String(error) } };
|
|
17
|
+
}
|
|
18
|
+
const parsed = parseInvalidationDocument(content);
|
|
19
|
+
if (!parsed.ok)
|
|
20
|
+
return { status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: 0, error: { code: parsed.code, message: parsed.message } };
|
|
21
|
+
if (!parsed.present)
|
|
22
|
+
return { status: 'no-op', changed: false, taskId: resolved.taskId, processed: 0, remaining: 0, error: null };
|
|
23
|
+
const beforePending = parsed.document.targets.filter((target) => target.status !== 'completed').length;
|
|
24
|
+
let receipts;
|
|
25
|
+
try {
|
|
26
|
+
receipts = parseArtifactReceipts(content).rows;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (parsed.document.targets.some((target) => target.status !== 'completed' && target.targetKind === 'receipt')) {
|
|
30
|
+
return {
|
|
31
|
+
status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending,
|
|
32
|
+
error: { code: 'INVALIDATION_TARGET_HASH_CONFLICT', message: error instanceof Error ? error.message : String(error) }
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
receipts = [];
|
|
36
|
+
}
|
|
37
|
+
for (const target of parsed.document.targets) {
|
|
38
|
+
if (target.status === 'completed')
|
|
39
|
+
continue;
|
|
40
|
+
if (target.targetKind === 'receipt') {
|
|
41
|
+
const receipt = receipts.find((candidate) => candidate.output === target.targetArtifact);
|
|
42
|
+
if (!receipt || receipt.inputSha256 !== target.targetSha256) {
|
|
43
|
+
return {
|
|
44
|
+
status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending,
|
|
45
|
+
error: { code: 'INVALIDATION_TARGET_HASH_CONFLICT', message: `invalidation receipt target '${target.targetArtifact}' changed before reconcile` }
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const targetPath = path.join(resolved.taskDir, target.targetArtifact);
|
|
51
|
+
try {
|
|
52
|
+
const stat = fs.lstatSync(targetPath);
|
|
53
|
+
if (!stat.isFile() || stat.isSymbolicLink() || sha256File(targetPath) !== target.targetSha256) {
|
|
54
|
+
return {
|
|
55
|
+
status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending,
|
|
56
|
+
error: { code: 'INVALIDATION_TARGET_HASH_CONFLICT', message: `invalidation target '${target.targetArtifact}' changed before reconcile` }
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return {
|
|
62
|
+
status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending,
|
|
63
|
+
error: { code: 'INVALIDATION_TARGET_HASH_CONFLICT', message: `invalidation target '${target.targetArtifact}' is unavailable before reconcile` }
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const reconciled = reconcileInvalidation(parsed.document, (options.now ?? (() => new Date().toISOString()))(), options.maxTargets);
|
|
68
|
+
if (!reconciled.ok)
|
|
69
|
+
return { status: 'failed', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending, error: { code: reconciled.code, message: reconciled.message } };
|
|
70
|
+
if (!reconciled.changed)
|
|
71
|
+
return { status: 'no-op', changed: false, taskId: resolved.taskId, processed: 0, remaining: beforePending, error: null };
|
|
72
|
+
const result = writeTask({
|
|
73
|
+
taskRef, expectedState: resolved.state, dryRun: options.dryRun,
|
|
74
|
+
mutations: [invalidationMutation(content, reconciled.document)]
|
|
75
|
+
}, { ...options, invalidationContext: 'reconcile', taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state } });
|
|
76
|
+
if (result.status === 'failed')
|
|
77
|
+
return { status: 'failed', changed: false, taskId: resolved.taskId, processed: beforePending - reconciled.document.targets.filter((target) => target.status !== 'completed').length, remaining: reconciled.document.targets.filter((target) => target.status !== 'completed').length, error: result.error };
|
|
78
|
+
return {
|
|
79
|
+
status: result.status, changed: result.changed, taskId: resolved.taskId,
|
|
80
|
+
processed: beforePending - reconciled.document.targets.filter((target) => target.status !== 'completed').length,
|
|
81
|
+
remaining: reconciled.document.targets.filter((target) => target.status !== 'completed').length,
|
|
82
|
+
error: null
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export { reconcileTaskInvalidation };
|
|
86
|
+
//# sourceMappingURL=invalidation-command.js.map
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
const INVALIDATION_HEADINGS = ['产物失效记录', 'Artifact Invalidation'];
|
|
3
|
+
const OPERATION_COLUMNS = [
|
|
4
|
+
'operation_id', 'source_family', 'source_artifact', 'source_round', 'source_sha256',
|
|
5
|
+
'status', 'processed', 'total', 'created_at', 'updated_at', 'completed_at', 'error'
|
|
6
|
+
];
|
|
7
|
+
const TARGET_COLUMNS = [
|
|
8
|
+
'target_id', 'operation_id', 'target_kind', 'target_family', 'target_artifact',
|
|
9
|
+
'target_round', 'target_sha256', 'status', 'reason_code', 'updated_at'
|
|
10
|
+
];
|
|
11
|
+
function invalid(message) {
|
|
12
|
+
return { ok: false, code: 'TASK_INVALIDATION_INVALID', message };
|
|
13
|
+
}
|
|
14
|
+
function hashIdentity(value) {
|
|
15
|
+
return createHash('sha256').update(JSON.stringify(value)).digest('hex').slice(0, 16);
|
|
16
|
+
}
|
|
17
|
+
function operationIdFor(source) {
|
|
18
|
+
return `INV-${hashIdentity([source.sourceFamily, source.sourceArtifact, source.sourceRound, source.sourceSha256])}`;
|
|
19
|
+
}
|
|
20
|
+
function targetIdFor(operationId, target) {
|
|
21
|
+
return `INV-T-${hashIdentity([operationId, target.targetKind, target.targetFamily, target.targetArtifact, target.targetRound, target.targetSha256])}`;
|
|
22
|
+
}
|
|
23
|
+
function createInvalidationOperation(source, targets = []) {
|
|
24
|
+
const operationId = source.operationId ?? operationIdFor(source);
|
|
25
|
+
return {
|
|
26
|
+
operationId, sourceFamily: source.sourceFamily, sourceArtifact: source.sourceArtifact,
|
|
27
|
+
sourceRound: source.sourceRound, sourceSha256: source.sourceSha256,
|
|
28
|
+
status: 'pending', processed: 0, total: targets.length,
|
|
29
|
+
createdAt: source.createdAt, updatedAt: source.updatedAt, completedAt: '', error: ''
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function lineCells(line) {
|
|
33
|
+
const first = line.indexOf('|');
|
|
34
|
+
const last = line.lastIndexOf('|');
|
|
35
|
+
if (first < 0 || first === last || line.slice(0, first).trim() || line.slice(last + 1).trim())
|
|
36
|
+
return null;
|
|
37
|
+
const inner = line.slice(first + 1, last);
|
|
38
|
+
const cells = [];
|
|
39
|
+
let start = 0;
|
|
40
|
+
let escaped = false;
|
|
41
|
+
for (let index = 0; index < inner.length; index += 1) {
|
|
42
|
+
const char = inner[index];
|
|
43
|
+
if (char === '|' && !escaped) {
|
|
44
|
+
cells.push(inner.slice(start, index));
|
|
45
|
+
start = index + 1;
|
|
46
|
+
}
|
|
47
|
+
escaped = char === '\\' && !escaped;
|
|
48
|
+
if (char !== '\\')
|
|
49
|
+
escaped = false;
|
|
50
|
+
}
|
|
51
|
+
cells.push(inner.slice(start));
|
|
52
|
+
return cells.map((cell) => cell.replace(/\\([\\|])/g, '$1').trim());
|
|
53
|
+
}
|
|
54
|
+
function isSeparator(cells, count) {
|
|
55
|
+
return cells.length === count && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
|
|
56
|
+
}
|
|
57
|
+
function sectionBody(content) {
|
|
58
|
+
const heading = INVALIDATION_HEADINGS.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
|
|
59
|
+
const match = new RegExp(`^##\\s+(${heading})\\s*$`, 'm').exec(content);
|
|
60
|
+
if (!match)
|
|
61
|
+
return null;
|
|
62
|
+
const start = (match.index ?? 0) + match[0].length;
|
|
63
|
+
const rest = content.slice(start);
|
|
64
|
+
const end = rest.search(/^##\s+/m);
|
|
65
|
+
return rest.slice(0, end < 0 ? rest.length : end);
|
|
66
|
+
}
|
|
67
|
+
function parseTable(body, heading, columns) {
|
|
68
|
+
const marker = new RegExp(`^###\\s+${heading}\\s*$`, 'm').exec(body);
|
|
69
|
+
if (!marker)
|
|
70
|
+
return null;
|
|
71
|
+
const start = (marker.index ?? 0) + marker[0].length;
|
|
72
|
+
const rest = body.slice(start);
|
|
73
|
+
const end = rest.search(/^###\s+/m);
|
|
74
|
+
const lines = rest.slice(0, end < 0 ? rest.length : end).split(/\r?\n/).filter((line) => line.trim());
|
|
75
|
+
const header = lines.length > 0 ? lineCells(lines[0]) : null;
|
|
76
|
+
const separator = lines.length > 1 ? lineCells(lines[1]) : null;
|
|
77
|
+
if (!header || !separator || header.length !== columns.length || !header.every((value, index) => value === columns[index]) || !isSeparator(separator, columns.length))
|
|
78
|
+
return false;
|
|
79
|
+
const rows = [];
|
|
80
|
+
for (const line of lines.slice(2)) {
|
|
81
|
+
const cells = lineCells(line);
|
|
82
|
+
if (!cells || cells.length !== columns.length)
|
|
83
|
+
return false;
|
|
84
|
+
rows.push(Object.fromEntries(columns.map((column, index) => [column, cells[index]])));
|
|
85
|
+
}
|
|
86
|
+
return rows;
|
|
87
|
+
}
|
|
88
|
+
function required(value, field) {
|
|
89
|
+
if (!value)
|
|
90
|
+
throw new Error(`${field} is required`);
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
function status(value, field) {
|
|
94
|
+
if (value !== 'pending' && value !== 'in-progress' && value !== 'completed')
|
|
95
|
+
throw new Error(`${field} has an invalid status`);
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
function numberValue(value, field, allowZero = true) {
|
|
99
|
+
const parsed = Number(value);
|
|
100
|
+
if (!Number.isInteger(parsed) || (allowZero ? parsed < 0 : parsed < 1))
|
|
101
|
+
throw new Error(`${field} must be an integer`);
|
|
102
|
+
return parsed;
|
|
103
|
+
}
|
|
104
|
+
function parseInvalidationDocument(content) {
|
|
105
|
+
const body = sectionBody(content);
|
|
106
|
+
if (body === null)
|
|
107
|
+
return { ok: true, present: false, document: { operations: [], targets: [] } };
|
|
108
|
+
try {
|
|
109
|
+
const operationRows = parseTable(body, 'Operations', OPERATION_COLUMNS);
|
|
110
|
+
const targetRows = parseTable(body, 'Targets', TARGET_COLUMNS);
|
|
111
|
+
if (operationRows === null || targetRows === null || operationRows === false || targetRows === false) {
|
|
112
|
+
return invalid('invalidation section must contain Operations and Targets tables');
|
|
113
|
+
}
|
|
114
|
+
const operations = operationRows.map((row) => ({
|
|
115
|
+
operationId: required(row.operation_id, 'operation_id'), sourceFamily: required(row.source_family, 'source_family'),
|
|
116
|
+
sourceArtifact: required(row.source_artifact, 'source_artifact'), sourceRound: numberValue(row.source_round, 'source_round', false),
|
|
117
|
+
sourceSha256: required(row.source_sha256, 'source_sha256'), status: status(row.status, 'operation status'),
|
|
118
|
+
processed: numberValue(row.processed, 'processed'), total: numberValue(row.total, 'total'),
|
|
119
|
+
createdAt: required(row.created_at, 'created_at'), updatedAt: required(row.updated_at, 'updated_at'),
|
|
120
|
+
completedAt: row.completed_at ?? '', error: row.error ?? ''
|
|
121
|
+
}));
|
|
122
|
+
const targets = targetRows.map((row) => ({
|
|
123
|
+
targetId: required(row.target_id, 'target_id'), operationId: required(row.operation_id, 'operation_id'),
|
|
124
|
+
targetKind: row.target_kind, targetFamily: required(row.target_family, 'target_family'),
|
|
125
|
+
targetArtifact: required(row.target_artifact, 'target_artifact'), targetRound: numberValue(row.target_round, 'target_round', false),
|
|
126
|
+
targetSha256: required(row.target_sha256, 'target_sha256'), status: status(row.status, 'target status'),
|
|
127
|
+
reasonCode: required(row.reason_code, 'reason_code'), updatedAt: required(row.updated_at, 'updated_at')
|
|
128
|
+
}));
|
|
129
|
+
const operationIds = new Set();
|
|
130
|
+
for (const operation of operations) {
|
|
131
|
+
if (operationIds.has(operation.operationId))
|
|
132
|
+
throw new Error(`duplicate operation '${operation.operationId}'`);
|
|
133
|
+
operationIds.add(operation.operationId);
|
|
134
|
+
if (operation.operationId !== operationIdFor(operation))
|
|
135
|
+
throw new Error(`operation '${operation.operationId}' has a non-canonical identity`);
|
|
136
|
+
if (operation.processed > operation.total)
|
|
137
|
+
throw new Error(`operation '${operation.operationId}' progress is invalid`);
|
|
138
|
+
if (!/^[a-f0-9]{64}$/.test(operation.sourceSha256))
|
|
139
|
+
throw new Error(`operation '${operation.operationId}' source hash is invalid`);
|
|
140
|
+
}
|
|
141
|
+
const targetIds = new Set();
|
|
142
|
+
for (const target of targets) {
|
|
143
|
+
if (!['artifact', 'receipt', 'approval', 'reviewed-snapshot'].includes(target.targetKind))
|
|
144
|
+
throw new Error(`target '${target.targetId}' kind is invalid`);
|
|
145
|
+
if (targetIds.has(target.targetId))
|
|
146
|
+
throw new Error(`duplicate target '${target.targetId}'`);
|
|
147
|
+
targetIds.add(target.targetId);
|
|
148
|
+
if (!operationIds.has(target.operationId))
|
|
149
|
+
throw new Error(`target '${target.targetId}' references an unknown operation`);
|
|
150
|
+
if (target.targetId !== targetIdFor(target.operationId, target))
|
|
151
|
+
throw new Error(`target '${target.targetId}' has a non-canonical identity`);
|
|
152
|
+
if (!/^[a-f0-9]{64}$/.test(target.targetSha256))
|
|
153
|
+
throw new Error(`target '${target.targetId}' hash is invalid`);
|
|
154
|
+
}
|
|
155
|
+
for (const operation of operations) {
|
|
156
|
+
const scoped = targets.filter((target) => target.operationId === operation.operationId);
|
|
157
|
+
const completed = scoped.filter((target) => target.status === 'completed').length;
|
|
158
|
+
if (operation.total !== scoped.length || operation.processed !== completed)
|
|
159
|
+
throw new Error(`operation '${operation.operationId}' progress does not match its targets`);
|
|
160
|
+
if (operation.status === 'completed' && (completed !== scoped.length || !operation.completedAt))
|
|
161
|
+
throw new Error(`completed operation '${operation.operationId}' has incomplete targets`);
|
|
162
|
+
if (operation.status === 'pending' && completed !== 0)
|
|
163
|
+
throw new Error(`pending operation '${operation.operationId}' has completed targets`);
|
|
164
|
+
if (operation.status === 'in-progress' && (completed === scoped.length || (completed === 0 && !scoped.some((target) => target.status === 'in-progress'))))
|
|
165
|
+
throw new Error(`in-progress operation '${operation.operationId}' has invalid progress`);
|
|
166
|
+
}
|
|
167
|
+
return { ok: true, present: true, document: { operations, targets } };
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return invalid(error instanceof Error ? error.message : String(error));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function cell(value) {
|
|
174
|
+
return String(value).replace(/\\/g, '\\\\').replace(/\|/g, '\\|').replace(/[\r\n]/g, ' ');
|
|
175
|
+
}
|
|
176
|
+
function table(columns, rows) {
|
|
177
|
+
const header = `| ${columns.join(' | ')} |`;
|
|
178
|
+
const separator = `| ${columns.map(() => '---').join(' | ')} |`;
|
|
179
|
+
return [header, separator, ...rows.map((row) => `| ${row.map(cell).join(' | ')} |`)].join('\n');
|
|
180
|
+
}
|
|
181
|
+
function renderInvalidation(document) {
|
|
182
|
+
const operations = [...document.operations].sort((left, right) => left.operationId.localeCompare(right.operationId));
|
|
183
|
+
const targets = [...document.targets].sort((left, right) => left.targetId.localeCompare(right.targetId));
|
|
184
|
+
return [
|
|
185
|
+
'### Operations', '',
|
|
186
|
+
table(OPERATION_COLUMNS, operations.map((row) => [
|
|
187
|
+
row.operationId, row.sourceFamily, row.sourceArtifact, row.sourceRound, row.sourceSha256,
|
|
188
|
+
row.status, row.processed, row.total, row.createdAt, row.updatedAt, row.completedAt, row.error
|
|
189
|
+
])), '',
|
|
190
|
+
'### Targets', '',
|
|
191
|
+
table(TARGET_COLUMNS, targets.map((row) => [
|
|
192
|
+
row.targetId, row.operationId, row.targetKind, row.targetFamily, row.targetArtifact,
|
|
193
|
+
row.targetRound, row.targetSha256, row.status, row.reasonCode, row.updatedAt
|
|
194
|
+
]))
|
|
195
|
+
].join('\n');
|
|
196
|
+
}
|
|
197
|
+
function reconcileInvalidation(document, now, maxTargets = Number.POSITIVE_INFINITY) {
|
|
198
|
+
const nextTargets = document.targets.map((target) => {
|
|
199
|
+
if (target.status === 'completed' || maxTargets <= 0)
|
|
200
|
+
return target;
|
|
201
|
+
maxTargets -= 1;
|
|
202
|
+
return { ...target, status: 'completed', updatedAt: now };
|
|
203
|
+
});
|
|
204
|
+
const changed = nextTargets.some((target, index) => target !== document.targets[index]);
|
|
205
|
+
const nextOperations = document.operations.map((operation) => {
|
|
206
|
+
const scoped = nextTargets.filter((target) => target.operationId === operation.operationId);
|
|
207
|
+
const processed = scoped.filter((target) => target.status === 'completed').length;
|
|
208
|
+
const total = scoped.length;
|
|
209
|
+
const complete = total === processed;
|
|
210
|
+
const nextStatus = complete ? 'completed' : processed > 0 ? 'in-progress' : 'pending';
|
|
211
|
+
if (operation.status === nextStatus && operation.processed === processed && operation.total === total && (!complete || operation.completedAt))
|
|
212
|
+
return operation;
|
|
213
|
+
return {
|
|
214
|
+
...operation, status: nextStatus, processed, total, updatedAt: changed ? now : operation.updatedAt,
|
|
215
|
+
completedAt: complete ? (operation.completedAt || now) : ''
|
|
216
|
+
};
|
|
217
|
+
});
|
|
218
|
+
const operationChanged = nextOperations.some((operation, index) => operation !== document.operations[index]);
|
|
219
|
+
return { ok: true, changed: changed || operationChanged, document: { operations: nextOperations, targets: nextTargets } };
|
|
220
|
+
}
|
|
221
|
+
function upsertInvalidation(document, operation, targets) {
|
|
222
|
+
const existing = document.operations.find((candidate) => candidate.operationId === operation.operationId);
|
|
223
|
+
if (existing && JSON.stringify(existing) !== JSON.stringify(operation)) {
|
|
224
|
+
return { ok: false, code: 'INVALIDATION_IDENTITY_CONFLICT', message: `operation '${operation.operationId}' identity conflicts with existing state` };
|
|
225
|
+
}
|
|
226
|
+
const existingTargets = document.targets.filter((target) => target.operationId === operation.operationId);
|
|
227
|
+
for (const target of targets) {
|
|
228
|
+
const old = existingTargets.find((candidate) => candidate.targetId === target.targetId);
|
|
229
|
+
if (old && JSON.stringify(old) !== JSON.stringify(target)) {
|
|
230
|
+
return { ok: false, code: 'INVALIDATION_IDENTITY_CONFLICT', message: `target '${target.targetId}' identity conflicts with existing state` };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (existing)
|
|
234
|
+
return { ok: true, changed: false, document };
|
|
235
|
+
return {
|
|
236
|
+
ok: true, changed: true,
|
|
237
|
+
document: { operations: [...document.operations, operation], targets: [...document.targets, ...targets] }
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function invalidationBlocks(document) {
|
|
241
|
+
return document.operations.some((operation) => operation.status !== 'completed')
|
|
242
|
+
|| document.targets.some((target) => target.status !== 'completed');
|
|
243
|
+
}
|
|
244
|
+
function isArtifactInvalidated(document, family, artifact) {
|
|
245
|
+
return document.targets.some((target) => target.status === 'completed'
|
|
246
|
+
&& target.targetKind === 'artifact'
|
|
247
|
+
&& target.targetFamily === family
|
|
248
|
+
&& target.targetArtifact === artifact);
|
|
249
|
+
}
|
|
250
|
+
function invalidationMutation(content, document) {
|
|
251
|
+
const existing = sectionBody(content) !== null;
|
|
252
|
+
return {
|
|
253
|
+
kind: 'section',
|
|
254
|
+
aliases: INVALIDATION_HEADINGS,
|
|
255
|
+
heading: existing && /^##\s+Artifact Invalidation\s*$/m.test(content) ? INVALIDATION_HEADINGS[1] : INVALIDATION_HEADINGS[0],
|
|
256
|
+
body: renderInvalidation(document)
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
export { INVALIDATION_HEADINGS, OPERATION_COLUMNS, TARGET_COLUMNS, createInvalidationOperation, isArtifactInvalidated, invalidationBlocks, invalidationMutation, operationIdFor, parseInvalidationDocument, reconcileInvalidation, renderInvalidation, targetIdFor, upsertInvalidation };
|
|
260
|
+
//# sourceMappingURL=invalidation.js.map
|