@fitlab-ai/agent-infra 0.9.13 → 0.9.15
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 +10 -0
- package/README.zh-CN.md +10 -0
- package/bin/cli.ts +26 -9
- package/bin/internal-cli.sh +45 -0
- package/dist/bin/cli.js +173 -157
- package/dist/bin/internal-cli.js +126 -42
- package/dist/lib/agent-client.js +5 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +37 -0
- package/dist/lib/fs/file-lock.js +111 -0
- package/dist/lib/host-control/audit.js +19 -0
- package/dist/lib/host-control/client.js +142 -0
- package/dist/lib/host-control/command.js +49 -0
- package/dist/lib/host-control/path.js +160 -0
- package/dist/lib/host-control/secure-fs.js +140 -0
- package/dist/lib/host-control/server.js +159 -0
- package/dist/lib/host-control/service.js +112 -0
- package/dist/lib/init.js +11 -2
- package/dist/lib/internal/agent-client.js +5 -2
- package/dist/lib/internal/cli-route-inventory.js +154 -0
- package/dist/lib/internal/codex-lifecycle.js +15 -12
- package/dist/lib/internal/codex-sandbox-controller.js +8 -2
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/git-workflow.js +10 -6
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +322 -0
- package/dist/lib/internal/platform-checks.js +16 -4
- package/dist/lib/internal/platform-comment.js +16 -4
- package/dist/lib/internal/platform-context.js +4 -1
- package/dist/lib/internal/platform-issue.js +12 -4
- package/dist/lib/internal/platform-metadata.js +88 -0
- package/dist/lib/internal/platform-pr-review.js +16 -3
- package/dist/lib/internal/platform-pr.js +42 -14
- package/dist/lib/internal/platform-release-notes.js +6 -3
- package/dist/lib/internal/platform-security.js +93 -0
- package/dist/lib/internal/pr-review-grade.js +14 -3
- package/dist/lib/internal/release-workflow.js +82 -89
- package/dist/lib/internal/sandbox-control.js +171 -7
- package/dist/lib/internal/task-activity.js +13 -4
- package/dist/lib/internal/task-artifact.js +19 -107
- package/dist/lib/internal/task-context.js +4 -1
- package/dist/lib/internal/task-create.js +48 -14
- package/dist/lib/internal/task-delivery.js +4 -1
- package/dist/lib/internal/task-event.js +38 -35
- package/dist/lib/internal/task-finalization.js +4 -1
- package/dist/lib/internal/task-invalidation.js +4 -1
- package/dist/lib/internal/task-ledger.js +12 -1
- package/dist/lib/internal/task-lifecycle.js +4 -1
- package/dist/lib/internal/task-operation-registry.js +519 -0
- package/dist/lib/internal/task-orchestration.js +12 -0
- package/dist/lib/internal/task-override.js +10 -3
- package/dist/lib/internal/task-qualification.js +90 -0
- package/dist/lib/internal/task-review.js +10 -52
- package/dist/lib/internal/task-short-id.js +11 -1
- package/dist/lib/internal/task-snapshot.js +4 -1
- package/dist/lib/internal/task-validate.js +37 -12
- package/dist/lib/internal/task-verify.js +5 -1
- package/dist/lib/internal/task-warning.js +8 -1
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +229 -0
- package/dist/lib/platform/completion-backfill.js +1 -1
- package/dist/lib/platform/github-data.js +241 -0
- package/dist/lib/platform/github-provider.js +248 -26
- package/dist/lib/platform/issue-comments.js +146 -34
- package/dist/lib/platform/issues.js +1 -179
- package/dist/lib/platform/pr-change-report.js +6 -12
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +83 -10
- package/dist/lib/platform/provider-contract.d.ts +67 -1
- package/dist/lib/platform/provider-contract.js +6 -2
- package/dist/lib/platform/provider-validation.js +76 -3
- package/dist/lib/platform/pull-requests.js +117 -275
- package/dist/lib/platform/repository-metadata.js +377 -0
- package/dist/lib/platform/security-alerts.js +155 -0
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/verification-sync.js +154 -162
- package/dist/lib/process-data/index.js +7 -0
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/run/index.js +5 -0
- package/dist/lib/sandbox/commands/control-status.js +22 -0
- package/dist/lib/sandbox/commands/create.js +39 -8
- package/dist/lib/sandbox/commands/ls.js +5 -0
- package/dist/lib/sandbox/commands/rm.js +3 -807
- package/dist/lib/sandbox/commands/show.js +16 -0
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +125 -12
- package/dist/lib/sandbox/control/completed-reentry.js +85 -0
- package/dist/lib/sandbox/control/container-identity.js +28 -31
- package/dist/lib/sandbox/control/executor.js +233 -34
- package/dist/lib/sandbox/control/finalization-response.js +27 -0
- package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
- package/dist/lib/sandbox/control/lifecycle.js +656 -128
- package/dist/lib/sandbox/control/native-file-lock.js +85 -0
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +720 -79
- package/dist/lib/sandbox/control/state.js +63 -3
- package/dist/lib/sandbox/control/task-view.js +183 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +99 -0
- package/dist/lib/sandbox/engines/authority.js +289 -0
- package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
- package/dist/lib/sandbox/index.js +7 -2
- package/dist/lib/sandbox/recovery.js +49 -5
- package/dist/lib/sandbox/removal.js +1717 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-view.js +15 -15
- package/dist/lib/sandbox/worktree-safety.js +4 -1
- package/dist/lib/server/index.js +8 -0
- package/dist/lib/task/activity-intent.js +2 -1
- package/dist/lib/task/activity-log.js +30 -24
- package/dist/lib/task/artifact-command.js +101 -0
- package/dist/lib/task/artifact-lifecycle.js +71 -40
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +474 -0
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-repair-intent.js +130 -0
- package/dist/lib/task/artifact-schema.js +127 -0
- package/dist/lib/task/capabilities.js +83 -19
- package/dist/lib/task/codex-orchestration.js +71 -25
- package/dist/lib/task/commit-identity.js +28 -0
- package/dist/lib/task/commit-operation.js +2 -16
- package/dist/lib/task/control-authority.js +375 -5
- package/dist/lib/task/control-recovery.js +128 -0
- package/dist/lib/task/create-service.js +103 -4
- package/dist/lib/task/create.js +80 -2
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/events.js +306 -76
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +3 -6
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/index.js +11 -3
- package/dist/lib/task/intermediate-cleanup.js +383 -0
- package/dist/lib/task/invalidation.js +18 -65
- package/dist/lib/task/ledger-intents.js +7 -22
- package/dist/lib/task/ledger.js +1 -3
- package/dist/lib/task/lifecycle.js +19 -1
- package/dist/lib/task/local-artifact-finalization.js +190 -290
- package/dist/lib/task/manual-validation-completion.js +26 -0
- package/dist/lib/task/manual-validation-evidence.js +130 -0
- package/dist/lib/task/manual-validation-receipt.js +101 -0
- package/dist/lib/task/manual-validation-shared.js +40 -0
- package/dist/lib/task/manual-validation-transaction.js +243 -0
- package/dist/lib/task/markdown.js +47 -0
- package/dist/lib/task/orchestration.js +206 -69
- package/dist/lib/task/qualification-audit.js +476 -0
- package/dist/lib/task/qualification-confirmation.js +109 -0
- package/dist/lib/task/qualification-intents.js +148 -0
- package/dist/lib/task/review-artifacts.js +23 -40
- package/dist/lib/task/review-command.js +25 -0
- package/dist/lib/task/review-finalization.js +146 -97
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -54
- package/dist/lib/task/verification-config.js +11 -0
- package/dist/lib/task/verification-engine.js +170 -327
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +17 -0
- package/dist/lib/task/workflow-warning-intents.js +0 -16
- package/dist/lib/task/workflow-warnings.js +28 -2
- package/dist/lib/update.js +2 -0
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +9 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
- package/lib/fs/durable-write.ts +29 -0
- package/lib/fs/file-lock.ts +113 -0
- package/lib/host-control/audit.ts +22 -0
- package/lib/host-control/client.ts +172 -0
- package/lib/host-control/command.ts +60 -0
- package/lib/host-control/path.ts +167 -0
- package/lib/host-control/secure-fs.ts +135 -0
- package/lib/host-control/server.ts +180 -0
- package/lib/host-control/service.ts +133 -0
- package/lib/init.ts +15 -2
- package/lib/internal/agent-client.ts +4 -2
- package/lib/internal/cli-route-inventory.ts +142 -0
- package/lib/internal/codex-lifecycle.ts +14 -12
- package/lib/internal/codex-sandbox-controller.ts +7 -2
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/git-workflow.ts +9 -6
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +306 -0
- package/lib/internal/platform-checks.ts +12 -4
- package/lib/internal/platform-comment.ts +12 -4
- package/lib/internal/platform-context.ts +3 -1
- package/lib/internal/platform-issue.ts +11 -4
- package/lib/internal/platform-metadata.ts +70 -0
- package/lib/internal/platform-pr-review.ts +14 -3
- package/lib/internal/platform-pr.ts +38 -14
- package/lib/internal/platform-release-notes.ts +5 -3
- package/lib/internal/platform-security.ts +83 -0
- package/lib/internal/pr-review-grade.ts +10 -3
- package/lib/internal/release-workflow.ts +91 -87
- package/lib/internal/sandbox-control.ts +181 -6
- package/lib/internal/task-activity.ts +12 -4
- package/lib/internal/task-artifact.ts +15 -79
- package/lib/internal/task-context.ts +3 -1
- package/lib/internal/task-create.ts +76 -15
- package/lib/internal/task-delivery.ts +3 -1
- package/lib/internal/task-event.ts +21 -10
- package/lib/internal/task-finalization.ts +3 -1
- package/lib/internal/task-invalidation.ts +3 -1
- package/lib/internal/task-ledger.ts +11 -1
- package/lib/internal/task-lifecycle.ts +3 -1
- package/lib/internal/task-operation-registry.ts +590 -0
- package/lib/internal/task-orchestration.ts +12 -0
- package/lib/internal/task-override.ts +9 -3
- package/lib/internal/task-qualification.ts +61 -0
- package/lib/internal/task-review.ts +7 -50
- package/lib/internal/task-short-id.ts +10 -1
- package/lib/internal/task-snapshot.ts +3 -1
- package/lib/internal/task-validate.ts +32 -12
- package/lib/internal/task-verify.ts +4 -1
- package/lib/internal/task-warning.ts +7 -1
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +255 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +244 -27
- package/lib/platform/issue-comments.ts +159 -35
- package/lib/platform/issues.ts +4 -245
- package/lib/platform/pr-change-report.ts +6 -12
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +88 -11
- package/lib/platform/provider-contract.ts +78 -3
- package/lib/platform/provider-validation.ts +85 -3
- package/lib/platform/pull-requests.ts +129 -348
- package/lib/platform/repository-metadata.ts +453 -0
- package/lib/platform/security-alerts.ts +191 -0
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/verification-sync.ts +171 -177
- package/lib/process-data/index.ts +7 -0
- package/lib/release/workflow.ts +3 -3
- package/lib/run/index.ts +5 -0
- package/lib/sandbox/commands/control-status.ts +26 -0
- package/lib/sandbox/commands/create.ts +53 -12
- package/lib/sandbox/commands/ls.ts +7 -0
- package/lib/sandbox/commands/rm.ts +3 -996
- package/lib/sandbox/commands/show.ts +16 -0
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +150 -13
- package/lib/sandbox/control/completed-reentry.ts +98 -0
- package/lib/sandbox/control/container-identity.ts +27 -43
- package/lib/sandbox/control/executor.ts +262 -37
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +672 -18
- package/lib/sandbox/control/native-file-lock.ts +108 -0
- package/lib/sandbox/control/protocol.ts +80 -8
- package/lib/sandbox/control/server.ts +779 -82
- package/lib/sandbox/control/state.ts +84 -3
- package/lib/sandbox/control/task-view.ts +240 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +102 -0
- package/lib/sandbox/engines/authority.ts +365 -0
- package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
- package/lib/sandbox/index.ts +9 -2
- package/lib/sandbox/recovery.ts +49 -6
- package/lib/sandbox/removal.ts +2144 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-view.ts +26 -18
- package/lib/sandbox/worktree-safety.ts +4 -1
- package/lib/server/index.ts +10 -0
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +104 -0
- package/lib/task/artifact-lifecycle.ts +66 -39
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +580 -0
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-repair-intent.ts +152 -0
- package/lib/task/artifact-schema.ts +163 -0
- package/lib/task/capabilities.ts +82 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +2 -16
- package/lib/task/control-authority.ts +513 -6
- package/lib/task/control-recovery.ts +174 -0
- package/lib/task/create-service.ts +131 -6
- package/lib/task/create.ts +83 -1
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/events.ts +292 -75
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +3 -8
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/index.ts +16 -3
- package/lib/task/intermediate-cleanup.ts +455 -0
- package/lib/task/invalidation.ts +18 -62
- package/lib/task/ledger-intents.ts +7 -24
- package/lib/task/ledger.ts +1 -3
- package/lib/task/lifecycle.ts +17 -1
- package/lib/task/local-artifact-finalization.ts +223 -388
- package/lib/task/manual-validation-completion.ts +40 -0
- package/lib/task/manual-validation-evidence.ts +209 -0
- package/lib/task/manual-validation-receipt.ts +152 -0
- package/lib/task/manual-validation-shared.ts +50 -0
- package/lib/task/manual-validation-transaction.ts +304 -0
- package/lib/task/markdown.ts +70 -0
- package/lib/task/orchestration.ts +191 -68
- package/lib/task/qualification-audit.ts +588 -0
- package/lib/task/qualification-confirmation.ts +117 -0
- package/lib/task/qualification-intents.ts +150 -0
- package/lib/task/review-artifacts.ts +21 -41
- package/lib/task/review-command.ts +23 -0
- package/lib/task/review-finalization.ts +154 -153
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -52
- package/lib/task/verification-config.ts +12 -0
- package/lib/task/verification-engine.ts +165 -370
- package/lib/task/verification-types.ts +17 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +18 -0
- package/lib/task/workflow-warning-intents.ts +0 -13
- package/lib/task/workflow-warnings.ts +21 -2
- package/lib/update.ts +6 -0
- package/package.json +7 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/README.en.md +2 -0
- package/templates/.agents/rules/README.zh-CN.md +2 -0
- package/templates/.agents/rules/decision-qualification.en.md +22 -0
- package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
- package/templates/.agents/rules/evidence-reporting.en.md +28 -0
- package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
- package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
- package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
- package/templates/.agents/rules/security-alerts.en.md +8 -8
- package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
- package/templates/.agents/rules/testing-discipline.en.md +2 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
- package/templates/.agents/scripts/package.json +3 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
- 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 +14 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +37 -21
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +37 -21
- 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/report-template.en.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +10 -23
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -23
- package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -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 +8 -8
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
- package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
- package/templates/.agents/skills/release/SKILL.en.md +7 -3
- package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
- package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +25 -4
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +25 -4
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +5 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +5 -1
- 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/test/SKILL.en.md +29 -7
- package/templates/.agents/skills/test/SKILL.zh-CN.md +29 -7
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
- package/templates/.agents/templates/task.en.md +6 -0
- package/templates/.agents/templates/task.zh-CN.md +6 -0
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
- package/templates/.agents/scripts/security-alerts.github.sh +0 -151
- package/templates/.agents/scripts/security-alerts.sh +0 -31
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
package/lib/task/events.ts
CHANGED
|
@@ -4,14 +4,13 @@ import path from 'node:path';
|
|
|
4
4
|
import { appendActivityEntry, locateActivityLog, pairEntries, startedBackedRows } from './activity-log.ts';
|
|
5
5
|
import {
|
|
6
6
|
buildArtifactLinkSection,
|
|
7
|
-
artifactName,
|
|
8
7
|
inspectArtifactDirectory,
|
|
9
8
|
parseReviewedInputReference,
|
|
10
9
|
parseCodePlanInputReference,
|
|
11
|
-
parseArtifactName,
|
|
12
10
|
resolveArtifactContext,
|
|
13
11
|
validateCompletedArtifact
|
|
14
12
|
} from './artifact-lifecycle.ts';
|
|
13
|
+
import { artifactName, parseArtifactName } from './artifact-name.ts';
|
|
15
14
|
import type { ArtifactContextResult, ArtifactErrorCode, ArtifactFamily, ArtifactIdentity } from './artifact-lifecycle.ts';
|
|
16
15
|
import { ArtifactReceiptError, parseArtifactReceipts, sha256File, upsertArtifactReceipt } from './artifact-receipts.ts';
|
|
17
16
|
import type { ArtifactReceipt } from './artifact-receipts.ts';
|
|
@@ -35,19 +34,28 @@ import { captureTaskWriteMetadata, writeTask } from './write.ts';
|
|
|
35
34
|
import type { TaskOperationSummary, TaskWriteErrorCode, TaskWriteOptions } from './write.ts';
|
|
36
35
|
import { allowsManualOverride } from './guard-override.ts';
|
|
37
36
|
import {
|
|
38
|
-
LOCAL_ARTIFACT_REQUIRED_PATTERNS,
|
|
39
|
-
LOCAL_ARTIFACT_REQUIRED_SECTIONS,
|
|
40
37
|
consumeLocalArtifactFinalizationIntent,
|
|
41
38
|
readLocalArtifactFinalizationIntent,
|
|
42
39
|
validateLocalArtifact
|
|
43
40
|
} from './local-artifact-finalization.ts';
|
|
41
|
+
import { writeArtifactRepairIntent } from './artifact-repair-intent.ts';
|
|
44
42
|
import type { LocalArtifactFamily, LocalArtifactFinalizationIntent } from './local-artifact-finalization.ts';
|
|
45
|
-
import { loadVerificationConfig } from './verification-config.ts';
|
|
46
43
|
import { buildLifecycleFacts, canStart } from './capabilities.ts';
|
|
47
44
|
import type { ExplicitTrigger, LifecycleAction, TriggerInitiator, TriggerReason } from './capabilities.ts';
|
|
48
45
|
import { createInvalidationOperation, invalidationMutation, parseInvalidationDocument, targetIdFor, upsertInvalidation } from './invalidation.ts';
|
|
49
46
|
import type { InvalidationTargetKind } from './invalidation.ts';
|
|
50
47
|
import { consumeReworkIntents, parseReworkIntentDocument, reworkIntentMutation, supersedeReworkIntents } from './rework-intent.ts';
|
|
48
|
+
import { ARTIFACT_FAMILIES, expectedQualificationRelations, parseQualificationAudit, parseTaskQualification, upstreamArtifactDigest, validateQualificationAudit } from './qualification-audit.ts';
|
|
49
|
+
import type { QualificationAudit, UpstreamRelation } from './qualification-audit.ts';
|
|
50
|
+
import { getArtifactSchema } from './artifact-schema.ts';
|
|
51
|
+
import { inspectArtifactContract } from './artifact-operations.ts';
|
|
52
|
+
import {
|
|
53
|
+
consumeLifecycleRecoveryAttestation,
|
|
54
|
+
lifecycleRecoveryAttestationDigest,
|
|
55
|
+
validateLifecycleRecoveryAttestation,
|
|
56
|
+
type LifecycleRecoveryAttestationV1
|
|
57
|
+
} from './control-authority.ts';
|
|
58
|
+
import { readManualValidationCompletion } from './manual-validation-completion.ts';
|
|
51
59
|
|
|
52
60
|
const eventCatalog = [
|
|
53
61
|
'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
|
|
@@ -68,6 +76,10 @@ type TaskEventErrorCode =
|
|
|
68
76
|
| 'EVENT_LOG_CONFLICT' | 'EVENT_ARTIFACT_CONFLICT' | 'EVENT_FINDING_COUNT_MISMATCH'
|
|
69
77
|
| 'EVENT_VERDICT_INVALID'
|
|
70
78
|
| 'EVENT_ORCHESTRATION_COMMIT_FAILED'
|
|
79
|
+
| 'MANUAL_VALIDATION_RECEIPT_MISSING' | 'MANUAL_VALIDATION_RECEIPT_INVALID'
|
|
80
|
+
| 'MANUAL_VALIDATION_RECEIPT_IDENTITY_MISMATCH'
|
|
81
|
+
| 'MANUAL_VALIDATION_TRANSACTION_MISSING' | 'MANUAL_VALIDATION_TRANSACTION_INVALID'
|
|
82
|
+
| 'MANUAL_VALIDATION_TRANSACTION_IDENTITY_MISMATCH' | 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID'
|
|
71
83
|
| ArtifactErrorCode | TaskWriteErrorCode;
|
|
72
84
|
type TaskEventRequest = {
|
|
73
85
|
taskRef: string; event: TaskEventName | string; agent: string; dryRun?: boolean; orchestrated?: boolean;
|
|
@@ -79,11 +91,14 @@ type TaskEventRequest = {
|
|
|
79
91
|
verdict?: Verdict; blockers?: number; major?: number; minor?: number;
|
|
80
92
|
manualValidation?: number; filesModified?: number; testsPassed?: number;
|
|
81
93
|
summaryResult?: string;
|
|
94
|
+
transactionId?: string; receiptDigest?: string; prHeadSha?: string;
|
|
82
95
|
};
|
|
83
96
|
type TaskEventError = { code: TaskEventErrorCode; message: string };
|
|
84
97
|
type TaskEventOptions = TaskWriteOptions & {
|
|
85
98
|
commitOrchestrationCompletion?: (plan: OrchestrationStageCompletion) => void;
|
|
86
99
|
lockAlreadyHeld?: boolean;
|
|
100
|
+
lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
|
|
101
|
+
deferLifecycleRecoveryConsumption?: boolean;
|
|
87
102
|
};
|
|
88
103
|
type TaskEventResult = {
|
|
89
104
|
status: 'planned' | 'applied' | 'no-op' | 'failed'; changed: boolean;
|
|
@@ -111,36 +126,12 @@ const SCHEMAS: Record<TaskEventName, { required?: string[]; optional?: string[]
|
|
|
111
126
|
'code.completed': { required: ['artifact', 'artifactSha256', 'semanticDigest'], optional: ['round', 'fixFor', 'implementationInput', 'filesModified', 'testsPassed', 'blockers', 'major', 'minor', 'manualValidation', 'orchestrated'] },
|
|
112
127
|
'review-code.started': { optional: ['round'] },
|
|
113
128
|
'review-code.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round', 'orchestrated'] },
|
|
114
|
-
'manual-validation.started': { optional: ['round'] },
|
|
115
|
-
'manual-validation.completed': { required: ['artifact', 'summaryResult'], optional: ['round'] },
|
|
129
|
+
'manual-validation.started': { optional: ['round', 'transactionId'] },
|
|
130
|
+
'manual-validation.completed': { required: ['artifact', 'summaryResult', 'transactionId', 'receiptDigest', 'prHeadSha'], optional: ['round'] },
|
|
116
131
|
'validation-run.started': { optional: ['round'] },
|
|
117
132
|
'validation-run.completed': { required: ['artifact'], optional: ['round'] }
|
|
118
133
|
};
|
|
119
134
|
|
|
120
|
-
function localArtifactValidationConfig(repoRoot: string, family: LocalArtifactFamily) {
|
|
121
|
-
const fallback = {
|
|
122
|
-
requiredSections: LOCAL_ARTIFACT_REQUIRED_SECTIONS[family],
|
|
123
|
-
requiredPatterns: LOCAL_ARTIFACT_REQUIRED_PATTERNS
|
|
124
|
-
};
|
|
125
|
-
const skillName = family === 'analysis' ? 'analyze-task' : family === 'plan' ? 'plan-task' : 'code-task';
|
|
126
|
-
try {
|
|
127
|
-
const artifact = loadVerificationConfig(repoRoot, skillName).checks.artifact;
|
|
128
|
-
if (!artifact || typeof artifact !== 'object' || Array.isArray(artifact)) return fallback;
|
|
129
|
-
const sections = artifact.required_sections;
|
|
130
|
-
const patterns = artifact.required_patterns;
|
|
131
|
-
return {
|
|
132
|
-
requiredSections: Array.isArray(sections)
|
|
133
|
-
? sections.filter((value): value is string => typeof value === 'string')
|
|
134
|
-
: fallback.requiredSections,
|
|
135
|
-
requiredPatterns: Array.isArray(patterns)
|
|
136
|
-
? patterns.filter((value): value is string => typeof value === 'string')
|
|
137
|
-
: fallback.requiredPatterns
|
|
138
|
-
};
|
|
139
|
-
} catch {
|
|
140
|
-
return fallback;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
135
|
function validateTaskEventRequest(request: TaskEventRequest): TaskEventError | null {
|
|
145
136
|
if (!eventCatalog.includes(request.event as TaskEventName)) return { code: 'EVENT_UNKNOWN', message: `unknown task event '${request.event}'` };
|
|
146
137
|
if (!request.taskRef || !request.agent) return { code: 'EVENT_PAYLOAD_INVALID', message: 'taskRef and agent are required' };
|
|
@@ -169,12 +160,23 @@ function validateTaskEventRequest(request: TaskEventRequest): TaskEventError | n
|
|
|
169
160
|
const forbidden = fix ? ['filesModified', 'testsPassed'] : ['fixFor', 'blockers', 'major', 'minor', 'manualValidation'];
|
|
170
161
|
if (modeRequired.some((key) => request[key as keyof TaskEventRequest] === undefined) || forbidden.some((key) => request[key as keyof TaskEventRequest] !== undefined)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'code.completed requires either initial or fix completion payload' };
|
|
171
162
|
}
|
|
172
|
-
if (request.fixFor &&
|
|
163
|
+
if (request.fixFor && parseArtifactName(request.fixFor)?.family !== 'review-code') return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor must reference a canonical review-code artifact' };
|
|
173
164
|
if (request.implementationInput && !/^II-[1-9]\d*$/.test(request.implementationInput)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'implementationInput must be a canonical II-N id' };
|
|
174
165
|
if (request.artifactSha256 !== undefined && !/^[0-9a-f]{64}$/i.test(request.artifactSha256)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'artifactSha256 must be a 64-character hexadecimal digest' };
|
|
175
166
|
if (request.semanticDigest !== undefined && !/^[0-9a-f]{64}$/i.test(request.semanticDigest)) return { code: 'EVENT_PAYLOAD_INVALID', message: 'semanticDigest must be a 64-character hexadecimal digest' };
|
|
176
167
|
if (request.fixFor && request.implementationInput) return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor and implementationInput are mutually exclusive' };
|
|
177
168
|
if (request.summaryResult !== undefined && (!request.summaryResult.trim() || /[\r\n]/.test(request.summaryResult))) return { code: 'EVENT_PAYLOAD_INVALID', message: 'summaryResult must be a non-empty single line' };
|
|
169
|
+
if (request.event === 'manual-validation.completed') {
|
|
170
|
+
for (const [name, value, pattern] of [
|
|
171
|
+
['transactionId', request.transactionId, /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u],
|
|
172
|
+
['receiptDigest', request.receiptDigest, /^[a-f0-9]{64}$/u],
|
|
173
|
+
['prHeadSha', request.prHeadSha, /^[a-f0-9]{40}$/u]
|
|
174
|
+
] as const) if (!value || !pattern.test(value)) return { code: 'EVENT_PAYLOAD_INVALID', message: `manual-validation.completed requires a valid ${name}` };
|
|
175
|
+
}
|
|
176
|
+
if (request.event === 'manual-validation.started' && request.transactionId !== undefined
|
|
177
|
+
&& !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(request.transactionId)) {
|
|
178
|
+
return { code: 'EVENT_PAYLOAD_INVALID', message: 'manual-validation.started requires a valid transactionId' };
|
|
179
|
+
}
|
|
178
180
|
if (request.event !== 'analyze.awaiting-input' && /\.(?:started|completed)$/.test(request.event)) {
|
|
179
181
|
if (!request.initiator || !request.requestId || !request.reasonCode) {
|
|
180
182
|
return { code: 'EVENT_TRIGGER_REQUIRED', message: `${request.event} requires --initiator, --request-id, and --reason-code` };
|
|
@@ -277,7 +279,9 @@ function identity(request: TaskEventRequest) {
|
|
|
277
279
|
if (family === 'manual-validation') {
|
|
278
280
|
return {
|
|
279
281
|
family, phase, action: spec.label,
|
|
280
|
-
note: phase === 'started'
|
|
282
|
+
note: phase === 'started'
|
|
283
|
+
? `started${request.transactionId ? `; transaction=${request.transactionId}` : ''}`
|
|
284
|
+
: `Manual validation passed → ${request.artifact}; ${request.summaryResult}; transaction=${request.transactionId}; receipt=${request.receiptDigest}; head=${request.prHeadSha}`,
|
|
281
285
|
target: null
|
|
282
286
|
} as const;
|
|
283
287
|
}
|
|
@@ -326,6 +330,28 @@ function normalizeStarted(request: TaskEventRequest, repoRoot: string): { reques
|
|
|
326
330
|
return { request: { ...request, round, artifact: context.next.name, fixFor: expectedFix, implementationInput: expectedImplementation }, context };
|
|
327
331
|
}
|
|
328
332
|
|
|
333
|
+
function validateManualValidationCompletion(
|
|
334
|
+
taskDir: string,
|
|
335
|
+
taskId: string,
|
|
336
|
+
request: TaskEventRequest,
|
|
337
|
+
artifactPath: string
|
|
338
|
+
): TaskEventError | null {
|
|
339
|
+
const completion = readManualValidationCompletion(taskDir, {
|
|
340
|
+
transactionId: request.transactionId,
|
|
341
|
+
taskId,
|
|
342
|
+
prHeadSha: request.prHeadSha,
|
|
343
|
+
artifact: request.artifact,
|
|
344
|
+
receiptDigest: request.receiptDigest
|
|
345
|
+
});
|
|
346
|
+
if (!completion.ok) return completion.error;
|
|
347
|
+
const { receipt, transaction } = completion.value;
|
|
348
|
+
if (!['receipt-committed', 'final-promotion-in-progress', 'committed'].includes(transaction.phase)) {
|
|
349
|
+
return { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'manual validation completion requires the receipt-backed transaction phase' };
|
|
350
|
+
}
|
|
351
|
+
if (sha256File(artifactPath) !== receipt.artifactSha256) return { code: 'MANUAL_VALIDATION_RECEIPT_INVALID', message: 'manual-validation artifact digest does not match the receipt' };
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
|
|
329
355
|
function openStartedIdentity(rows: ReturnType<typeof pairEntries>, family: EventFamily) {
|
|
330
356
|
const spec = FAMILY[family];
|
|
331
357
|
if (family === 'manual-validation') {
|
|
@@ -347,6 +373,25 @@ function reviewInputFamily(family: EventFamily): ArtifactFamily {
|
|
|
347
373
|
return family === 'review-analysis' ? 'analysis' : family === 'review-plan' ? 'plan' : 'code';
|
|
348
374
|
}
|
|
349
375
|
|
|
376
|
+
function relationForStartedInput(family: EventFamily, input: ArtifactIdentity): UpstreamRelation['relation'] {
|
|
377
|
+
if (family.startsWith('review-')) {
|
|
378
|
+
return input.family === reviewInputFamily(family) ? 'reviewed-input' : 'approval-context';
|
|
379
|
+
}
|
|
380
|
+
const consumer = family === 'analyze' ? 'analysis' : family;
|
|
381
|
+
if ((consumer === 'plan' && input.family === 'analysis') || (consumer === 'code' && input.family === 'plan')) return 'required-input';
|
|
382
|
+
return 'review-context';
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function qualificationRelationsForStarted(family: EventFamily, inputs: readonly ArtifactIdentity[]): UpstreamRelation[] {
|
|
386
|
+
return inputs.map((input) => ({
|
|
387
|
+
upstreamFamily: input.family as UpstreamRelation['upstreamFamily'],
|
|
388
|
+
upstreamArtifact: input.name,
|
|
389
|
+
upstreamRound: input.round,
|
|
390
|
+
upstreamSha256: sha256File(input.path),
|
|
391
|
+
relation: relationForStartedInput(family, input)
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
|
|
350
395
|
function lifecycleAction(family: EventFamily): LifecycleAction {
|
|
351
396
|
return family === 'analyze' ? 'analysis' : family;
|
|
352
397
|
}
|
|
@@ -381,49 +426,118 @@ function invalidationMutationForCompletion(
|
|
|
381
426
|
code: ['review-code']
|
|
382
427
|
};
|
|
383
428
|
const sourceFamily = family as 'analyze' | 'plan' | 'code';
|
|
429
|
+
const sourceArtifactFamily = sourceFamily === 'analyze' ? 'analysis' : sourceFamily;
|
|
384
430
|
let receipts: readonly ArtifactReceipt[] = [];
|
|
385
431
|
try {
|
|
386
432
|
receipts = parseArtifactReceipts(content).rows;
|
|
387
433
|
} catch (error) {
|
|
388
434
|
return { error: error instanceof Error ? error.message : String(error) };
|
|
389
435
|
}
|
|
390
|
-
const
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return
|
|
397
|
-
})
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
436
|
+
const inventory = fs.readdirSync(taskDir).flatMap((name) => {
|
|
437
|
+
const identity = parseArtifactName(name);
|
|
438
|
+
if (!identity || !(ARTIFACT_FAMILIES as readonly string[]).includes(identity.family)) return [];
|
|
439
|
+
try {
|
|
440
|
+
const stat = fs.lstatSync(path.join(taskDir, name));
|
|
441
|
+
if (!stat.isFile() || stat.isSymbolicLink()) return [];
|
|
442
|
+
return [{ family: identity.family, name, round: identity.round, sha256: sha256File(path.join(taskDir, name)) }];
|
|
443
|
+
} catch (error) {
|
|
444
|
+
throw new Error(`cannot inspect invalidation artifact '${name}': ${error instanceof Error ? error.message : String(error)}`);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
const taskQualification = parseTaskQualification(content);
|
|
448
|
+
if (!taskQualification.ok) return { error: taskQualification.message };
|
|
449
|
+
const nodeMap = new Map(inventory.map((node) => [`${node.family}/${node.name}`, node]));
|
|
450
|
+
const audits = new Map<string, QualificationAudit>();
|
|
451
|
+
let taskInputChanged = false;
|
|
452
|
+
let graphUsable = taskQualification.qualification.present;
|
|
453
|
+
if (graphUsable) {
|
|
454
|
+
for (const node of inventory) {
|
|
455
|
+
const artifactContent = fs.readFileSync(path.join(taskDir, node.name), 'utf8');
|
|
456
|
+
const parsedAudit = parseQualificationAudit(artifactContent);
|
|
457
|
+
if (!parsedAudit.ok || !parsedAudit.audit.present || !parsedAudit.audit.snapshot) { graphUsable = false; break; }
|
|
458
|
+
audits.set(`${node.family}/${node.name}`, parsedAudit.audit);
|
|
459
|
+
if (parsedAudit.audit.snapshot.taskInputDigest !== taskQualification.qualification.taskInputDigest) taskInputChanged = true;
|
|
460
|
+
if (parsedAudit.audit.snapshot.nonConstraintInputDigest !== taskQualification.qualification.nonConstraintInputDigest) {
|
|
461
|
+
graphUsable = false;
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
if (parsedAudit.audit.snapshot.upstreamArtifactDigest !== upstreamArtifactDigest(parsedAudit.audit.upstreamRelations)) {
|
|
465
|
+
graphUsable = false;
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
for (const relation of parsedAudit.audit.upstreamRelations) {
|
|
469
|
+
const upstream = nodeMap.get(`${relation.upstreamFamily}/${relation.upstreamArtifact}`);
|
|
470
|
+
if (!upstream || upstream.round !== relation.upstreamRound || upstream.sha256 !== relation.upstreamSha256) { graphUsable = false; break; }
|
|
471
|
+
}
|
|
472
|
+
if (!graphUsable) break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const selected = new Set<string>();
|
|
476
|
+
if (graphUsable) {
|
|
477
|
+
const changedConstraints = new Set<string>();
|
|
478
|
+
for (const audit of audits.values()) {
|
|
479
|
+
for (const dependency of audit.constraintDependencies) {
|
|
480
|
+
const current = taskQualification.qualification.constraints.find((row) => row.constraintId === dependency.constraintId);
|
|
481
|
+
if (!current || current.digest !== dependency.constraintDigest) changedConstraints.add(dependency.constraintId);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const currentCandidates = new Map(taskQualification.qualification.candidates.map((candidate) => [candidate.candidateId, candidate]));
|
|
485
|
+
const candidateSnapshotMatches = [...audits.values()].every((audit) => audit.candidateQualifications.length === currentCandidates.size && audit.candidateQualifications.every((row) => {
|
|
486
|
+
const current = currentCandidates.get(row.candidateId);
|
|
487
|
+
return Boolean(current && current.status === row.status && current.impact === row.impact
|
|
488
|
+
&& current.evidence === row.evidence && current.constraintIds.join(',') === row.constraintIds.join(','));
|
|
489
|
+
}));
|
|
490
|
+
// A task-input digest also covers candidates. A narrowed invalidation graph
|
|
491
|
+
// is safe only when the old audit rows show that the candidate projection
|
|
492
|
+
// stayed unchanged and the change is explained by at least one constraint.
|
|
493
|
+
if (taskInputChanged && (changedConstraints.size === 0 || !candidateSnapshotMatches)) graphUsable = false;
|
|
494
|
+
for (const node of inventory) {
|
|
495
|
+
const audit = audits.get(`${node.family}/${node.name}`)!;
|
|
496
|
+
if (audit.constraintDependencies.some((dependency) => changedConstraints.has(dependency.constraintId))) selected.add(`${node.family}/${node.name}`);
|
|
497
|
+
if (audit.upstreamRelations.some((relation) => relation.upstreamFamily === sourceArtifactFamily)) selected.add(`${node.family}/${node.name}`);
|
|
498
|
+
}
|
|
499
|
+
// Relation rows point from a consumer to its actual upstream artifact. Walk
|
|
500
|
+
// that reverse edge so review-only seeds and multi-hop consumers receive the
|
|
501
|
+
// same derived targets as ordinary executor seeds.
|
|
502
|
+
let changed = true;
|
|
503
|
+
while (changed) {
|
|
504
|
+
changed = false;
|
|
505
|
+
for (const node of inventory) {
|
|
506
|
+
const key = `${node.family}/${node.name}`;
|
|
507
|
+
const audit = audits.get(key)!;
|
|
508
|
+
if (audit.upstreamRelations.some((relation) => selected.has(`${relation.upstreamFamily}/${relation.upstreamArtifact}`)) && !selected.has(key)) {
|
|
509
|
+
selected.add(key);
|
|
510
|
+
changed = true;
|
|
417
511
|
}
|
|
418
|
-
return shapes.map((targetShape) => ({
|
|
419
|
-
...targetShape,
|
|
420
|
-
targetId: targetIdFor('pending', targetShape), operationId: 'pending', status: 'pending' as const,
|
|
421
|
-
reasonCode: 'upstream-replaced', updatedAt: timestamp
|
|
422
|
-
}));
|
|
423
|
-
} catch (error) {
|
|
424
|
-
throw new Error(`cannot inspect invalidation target '${name}': ${error instanceof Error ? error.message : String(error)}`);
|
|
425
512
|
}
|
|
426
|
-
}
|
|
513
|
+
}
|
|
514
|
+
// A relation cycle cannot establish a safe downstream closure.
|
|
515
|
+
const visiting = new Set<string>();
|
|
516
|
+
const visited = new Set<string>();
|
|
517
|
+
const hasCycle = (key: string): boolean => {
|
|
518
|
+
if (visiting.has(key)) return true;
|
|
519
|
+
if (visited.has(key)) return false;
|
|
520
|
+
visiting.add(key);
|
|
521
|
+
const cycle = audits.get(key)?.upstreamRelations.some((relation) => hasCycle(`${relation.upstreamFamily}/${relation.upstreamArtifact}`)) ?? false;
|
|
522
|
+
visiting.delete(key); visited.add(key);
|
|
523
|
+
return cycle;
|
|
524
|
+
};
|
|
525
|
+
if ([...nodeMap.keys()].some(hasCycle)) graphUsable = false;
|
|
526
|
+
}
|
|
527
|
+
const reasonCode = graphUsable ? 'qualification-changed' : 'upstream-replaced';
|
|
528
|
+
const targetNodes = (graphUsable
|
|
529
|
+
? inventory.filter((node) => selected.has(`${node.family}/${node.name}`))
|
|
530
|
+
: inventory.filter((node) => downstream[sourceFamily].includes(node.family)))
|
|
531
|
+
.filter((node) => !(node.family === FAMILY[family].artifact && node.name === artifact.name));
|
|
532
|
+
const targets = targetNodes.flatMap((node) => {
|
|
533
|
+
const shapes: Array<{ targetKind: InvalidationTargetKind; targetFamily: string; targetArtifact: string; targetRound: number; targetSha256: string }> = [
|
|
534
|
+
{ targetKind: 'artifact', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: node.sha256 }
|
|
535
|
+
];
|
|
536
|
+
const receipt = receipts.find((candidate) => candidate.output === node.name);
|
|
537
|
+
if (receipt) shapes.push({ targetKind: 'receipt', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: receipt.inputSha256 });
|
|
538
|
+
if (node.family.startsWith('review-')) shapes.push({ targetKind: 'approval', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: node.sha256 });
|
|
539
|
+
if (node.family === 'review-code') shapes.push({ targetKind: 'reviewed-snapshot', targetFamily: node.family, targetArtifact: node.name, targetRound: node.round, targetSha256: node.sha256 });
|
|
540
|
+
return shapes.map((targetShape) => ({ ...targetShape, targetId: targetIdFor('pending', targetShape), operationId: 'pending', status: 'pending' as const, reasonCode, updatedAt: timestamp }));
|
|
427
541
|
});
|
|
428
542
|
if (targets.length === 0) return { mutation: null };
|
|
429
543
|
const source = {
|
|
@@ -561,6 +675,10 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
561
675
|
if (openIdentity && 'conflict' in openIdentity) return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'artifact family has more than one open started event' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
562
676
|
if (openIdentity) {
|
|
563
677
|
if (openIdentity.row.agent !== request.agent) return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
678
|
+
if (initialParts.family === 'manual-validation' && request.transactionId !== undefined) {
|
|
679
|
+
const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(openIdentity.row.note)?.[1];
|
|
680
|
+
if (existingTransactionId !== undefined && existingTransactionId !== request.transactionId) return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
681
|
+
}
|
|
564
682
|
if (request.round !== undefined && request.round !== openIdentity.round) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `round ${request.round} conflicts with open round ${openIdentity.round}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
565
683
|
if (request.fixFor !== undefined && request.fixFor !== openIdentity.fixFor) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `fixFor '${request.fixFor}' conflicts with open event` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
566
684
|
if (request.implementationInput !== openIdentity.implementationInput) return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with open event` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
@@ -576,6 +694,23 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
576
694
|
if (identity?.family === FAMILY[initialParts.family].artifact) normalized = { ...request, round: identity.round };
|
|
577
695
|
}
|
|
578
696
|
const eventIdentity = identity(normalized);
|
|
697
|
+
const lifecycleAuthority = options.lifecycleRecoveryAttestation ?? null;
|
|
698
|
+
if (lifecycleAuthority) {
|
|
699
|
+
try { validateLifecycleRecoveryAttestation(lifecycleAuthority); }
|
|
700
|
+
catch (error) {
|
|
701
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
702
|
+
}
|
|
703
|
+
const expectedFamily = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
|
|
704
|
+
if (eventIdentity.phase !== 'completed'
|
|
705
|
+
|| lifecycleAuthority.phase !== 'task-event.completed'
|
|
706
|
+
|| lifecycleAuthority.taskId !== resolved.taskId
|
|
707
|
+
|| lifecycleAuthority.family !== expectedFamily
|
|
708
|
+
|| lifecycleAuthority.artifact !== normalized.artifact
|
|
709
|
+
|| lifecycleAuthority.round !== normalized.round
|
|
710
|
+
|| (normalized.requestId !== undefined && lifecycleAuthority.lifecycleRequestId !== normalized.requestId)) {
|
|
711
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'lifecycle completion authority does not match the event tuple' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
712
|
+
}
|
|
713
|
+
}
|
|
579
714
|
const currentStep = typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '';
|
|
580
715
|
const matchingRows = rows.filter((item) => item.step === eventIdentity.action);
|
|
581
716
|
const manual = eventIdentity.family === 'manual-validation';
|
|
@@ -587,6 +722,12 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
587
722
|
let localFinalizationIntent: LocalArtifactFinalizationIntent | null = null;
|
|
588
723
|
if (eventIdentity.phase === 'started' && row) {
|
|
589
724
|
if (row.agent !== normalized.agent) return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
725
|
+
if (manual && normalized.transactionId !== undefined) {
|
|
726
|
+
const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(row.note)?.[1];
|
|
727
|
+
if (existingTransactionId !== undefined && existingTransactionId !== normalized.transactionId) {
|
|
728
|
+
return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
729
|
+
}
|
|
730
|
+
}
|
|
590
731
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, row.started, frontmatter, artifactContext);
|
|
591
732
|
}
|
|
592
733
|
if (eventIdentity.phase === 'started' && !manual && completedRows.length > 0) return failed(normalized, { code: 'EVENT_ALREADY_COMPLETED', message: 'event identity is already completed' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
@@ -598,6 +739,39 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
598
739
|
const validated = validateCompletedArtifact(resolved.taskDir, FAMILY[eventIdentity.family].artifact, normalized.artifact!, normalized.round);
|
|
599
740
|
if (!validated.ok) return failed(normalized, validated.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
600
741
|
completedArtifact = validated.artifact;
|
|
742
|
+
if (eventIdentity.family === 'manual-validation') {
|
|
743
|
+
const validationError = validateManualValidationCompletion(
|
|
744
|
+
resolved.taskDir,
|
|
745
|
+
resolved.taskId,
|
|
746
|
+
normalized,
|
|
747
|
+
completedArtifact.path
|
|
748
|
+
);
|
|
749
|
+
if (validationError) return failed(normalized, validationError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
750
|
+
}
|
|
751
|
+
if (eventIdentity.family.startsWith('review-')) {
|
|
752
|
+
let reviewContent: string;
|
|
753
|
+
try { reviewContent = fs.readFileSync(completedArtifact.path, 'utf8'); }
|
|
754
|
+
catch (error) {
|
|
755
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot read qualification audit from ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
756
|
+
}
|
|
757
|
+
const schema = getArtifactSchema(eventIdentity.family);
|
|
758
|
+
const structure = schema ? inspectArtifactContract(reviewContent, schema) : null;
|
|
759
|
+
if (structure && !structure.ok) {
|
|
760
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `shared artifact structure invalid: ${structure.diagnostics.map((item) => `${item.code}: ${item.message}`).join('; ')}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
761
|
+
}
|
|
762
|
+
const expected = expectedQualificationRelations(content, eventIdentity.family as 'review-analysis' | 'review-plan' | 'review-code');
|
|
763
|
+
if (!expected.ok) {
|
|
764
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `${expected.code}: ${expected.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
765
|
+
}
|
|
766
|
+
const qualification = validateQualificationAudit(content, reviewContent, {
|
|
767
|
+
family: eventIdentity.family as 'review-analysis' | 'review-plan' | 'review-code',
|
|
768
|
+
artifact: completedArtifact.name,
|
|
769
|
+
expectedUpstreamRelations: expected.relations
|
|
770
|
+
});
|
|
771
|
+
if (!qualification.ok) {
|
|
772
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `${qualification.code}: ${qualification.message}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
773
|
+
}
|
|
774
|
+
}
|
|
601
775
|
if (eventIdentity.family === 'analyze' || eventIdentity.family === 'plan' || eventIdentity.family === 'code') {
|
|
602
776
|
const localFamily = eventIdentity.family === 'analyze'
|
|
603
777
|
? 'analysis'
|
|
@@ -607,11 +781,10 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
607
781
|
catch (error) {
|
|
608
782
|
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot read ${completedArtifact.name}: ${String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
609
783
|
}
|
|
610
|
-
const validationConfig = localArtifactValidationConfig(resolved.repoRoot, localFamily);
|
|
611
784
|
const local = validateLocalArtifact(artifactContent, {
|
|
612
785
|
family: localFamily,
|
|
613
|
-
|
|
614
|
-
|
|
786
|
+
taskContent: content,
|
|
787
|
+
artifact: completedArtifact.name
|
|
615
788
|
});
|
|
616
789
|
if (!local.ok) {
|
|
617
790
|
return failed(normalized, {
|
|
@@ -642,7 +815,7 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
642
815
|
message: `cannot read local finalizer provenance for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
643
816
|
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
644
817
|
}
|
|
645
|
-
if (!localFinalizationIntent || !['passed', 'consumed'].includes(localFinalizationIntent.state)) {
|
|
818
|
+
if (!localFinalizationIntent || !['passed', 'consumed', 'finalize-ready', 'commit-started'].includes(localFinalizationIntent.state)) {
|
|
646
819
|
return failed(normalized, {
|
|
647
820
|
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
648
821
|
message: `local finalizer provenance is missing or incomplete for ${completedArtifact.name}`
|
|
@@ -654,6 +827,18 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
654
827
|
message: `local finalizer provenance does not match ${completedArtifact.name}`
|
|
655
828
|
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
656
829
|
}
|
|
830
|
+
if (localFinalizationIntent.recoveryOperationId !== null) {
|
|
831
|
+
if (!lifecycleAuthority
|
|
832
|
+
|| localFinalizationIntent.recoveryOperationId !== lifecycleAuthority.operationId
|
|
833
|
+
|| localFinalizationIntent.requestId !== lifecycleAuthority.lifecycleRequestId
|
|
834
|
+
|| (localFinalizationIntent.phase !== 'artifact.finalize-local'
|
|
835
|
+
&& localFinalizationIntent.authorityDigest !== lifecycleRecoveryAttestationDigest(lifecycleAuthority))) {
|
|
836
|
+
return failed(normalized, {
|
|
837
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
838
|
+
message: `local finalizer provenance does not match lifecycle completion authority for ${completedArtifact.name}`
|
|
839
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
840
|
+
}
|
|
841
|
+
}
|
|
657
842
|
}
|
|
658
843
|
}
|
|
659
844
|
if (eventIdentity.phase === 'waiting' && section.entries.some((entry) => entry.step === eventIdentity.action && entry.note === eventIdentity.note)) {
|
|
@@ -730,6 +915,13 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
730
915
|
const body = appendActivityEntry(section, { time: metadata.timestamp, step: logStep, agent: normalized.agent, note: eventIdentity.note });
|
|
731
916
|
const frontmatterSet: Record<string, string> = { current_step: step, assigned_to: normalized.agent };
|
|
732
917
|
let frontmatterRemove: string[] | undefined;
|
|
918
|
+
if (eventIdentity.phase === 'started' && artifactContext) {
|
|
919
|
+
try {
|
|
920
|
+
frontmatterSet.qualification_input_relations = JSON.stringify(qualificationRelationsForStarted(eventIdentity.family, artifactContext.inputs));
|
|
921
|
+
} catch (error) {
|
|
922
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot capture qualification input relations: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
923
|
+
}
|
|
924
|
+
}
|
|
733
925
|
if (eventIdentity.phase === 'started' && eventIdentity.family === 'code') {
|
|
734
926
|
const planInput = artifactContext?.inputs.find((input) => input.family === 'plan');
|
|
735
927
|
if (!planInput) return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'code.started plan input context is unavailable' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
@@ -754,6 +946,7 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
754
946
|
} else if (eventIdentity.phase === 'completed' && eventIdentity.family.startsWith('review-')) {
|
|
755
947
|
frontmatterRemove = ['review_input_artifact', 'review_input_sha256'];
|
|
756
948
|
}
|
|
949
|
+
if (eventIdentity.phase === 'completed') frontmatterRemove = [...(frontmatterRemove ?? []), 'qualification_input_relations'];
|
|
757
950
|
if (eventIdentity.phase === 'started' && normalized.implementationInput) frontmatterSet.last_reviewed_commit = '';
|
|
758
951
|
const mutations: Parameters<typeof writeTask>[0]['mutations'][number][] = [
|
|
759
952
|
{ kind: 'frontmatter', set: frontmatterSet, remove: frontmatterRemove }
|
|
@@ -791,13 +984,26 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
791
984
|
});
|
|
792
985
|
}
|
|
793
986
|
mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
|
|
794
|
-
if (!normalized.dryRun && localFinalizationIntent && completedArtifact
|
|
987
|
+
if (!normalized.dryRun && localFinalizationIntent && completedArtifact) {
|
|
795
988
|
try {
|
|
796
|
-
|
|
989
|
+
if (lifecycleAuthority && localFinalizationIntent.state === 'passed') {
|
|
990
|
+
const expectedIntent = localFinalizationIntent;
|
|
991
|
+
localFinalizationIntent = {
|
|
992
|
+
...localFinalizationIntent,
|
|
993
|
+
state: 'commit-started',
|
|
994
|
+
phase: 'task-event.completed',
|
|
995
|
+
authorityDigest: lifecycleRecoveryAttestationDigest(lifecycleAuthority),
|
|
996
|
+
requestId: lifecycleAuthority.lifecycleRequestId,
|
|
997
|
+
updatedAt: Date.now()
|
|
998
|
+
};
|
|
999
|
+
writeArtifactRepairIntent(resolved.repoRoot, localFinalizationIntent, { expected: expectedIntent });
|
|
1000
|
+
} else if (!lifecycleAuthority && localFinalizationIntent.state === 'passed') {
|
|
1001
|
+
localFinalizationIntent = consumeLocalArtifactFinalizationIntent(resolved.repoRoot, localFinalizationIntent);
|
|
1002
|
+
}
|
|
797
1003
|
} catch (error) {
|
|
798
1004
|
return failed(normalized, {
|
|
799
1005
|
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
800
|
-
message: `local finalizer provenance could not be
|
|
1006
|
+
message: `local finalizer provenance could not be advanced before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
801
1007
|
}, {
|
|
802
1008
|
taskId: resolved.taskId,
|
|
803
1009
|
taskMdPath: resolved.taskMdPath,
|
|
@@ -813,6 +1019,17 @@ function applyTaskEventUnlocked(request: TaskEventRequest, options: TaskEventOpt
|
|
|
813
1019
|
&& ['analyze', 'plan', 'code'].includes(eventIdentity.family);
|
|
814
1020
|
const result = writeTask({ taskRef: normalized.taskRef, expectedState: stateOverride ? resolved.state : 'active', dryRun: normalized.dryRun, mutations }, { ...options, invalidationContext: sourceCompletion ? 'source-completion' : 'standard', metadataProvider: () => metadata });
|
|
815
1021
|
if (result.status === 'failed') return failed(normalized, result.error, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
|
|
1022
|
+
if (!normalized.dryRun && lifecycleAuthority && localFinalizationIntent && completedArtifact) {
|
|
1023
|
+
try {
|
|
1024
|
+
if (!options.deferLifecycleRecoveryConsumption) consumeLifecycleRecoveryAttestation(lifecycleAuthority);
|
|
1025
|
+
writeArtifactRepairIntent(resolved.repoRoot, { ...localFinalizationIntent, state: 'consumed', updatedAt: Date.now() }, { expected: localFinalizationIntent });
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
return failed(normalized, {
|
|
1028
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
1029
|
+
message: `task completion was written but lifecycle recovery consumption is incomplete: ${error instanceof Error ? error.message : String(error)}`
|
|
1030
|
+
}, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
816
1033
|
if (!normalized.dryRun && orchestrationCompletion) {
|
|
817
1034
|
try {
|
|
818
1035
|
(options.commitOrchestrationCompletion ?? commitOrchestrationStageCompletion)(orchestrationCompletion);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TaskFinalizationReceipt } from './finalization.ts';
|
|
2
|
+
|
|
3
|
+
export type TaskControlBinding = NonNullable<TaskFinalizationReceipt['controlBinding']>;
|
|
4
|
+
export type TaskControlBindingEvidence = (taskId: string, binding: TaskControlBinding) => 'terminal' | 'pending' | 'mismatch';
|
|
5
|
+
|
|
6
|
+
export function taskFinalizationReceiptState(receipt: TaskFinalizationReceipt): 'pending' | 'unresolved' | 'complete' {
|
|
7
|
+
if (receipt.lifecycle !== 'done' || receipt.taskComment === 'pending'
|
|
8
|
+
|| receipt.verification === 'pending' || receipt.warningProjection !== 'done') return 'pending';
|
|
9
|
+
if (receipt.lastError !== null || receipt.warnings.some((warning) => warning.status === 'open')) return 'unresolved';
|
|
10
|
+
return 'complete';
|
|
11
|
+
}
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
4
4
|
|
|
5
|
+
import { parseArtifactName } from './artifact-name.ts';
|
|
5
6
|
import { locateActivityLog } from './activity-log.ts';
|
|
6
7
|
import { applyPrReviewActivityIntent } from './activity-intent.ts';
|
|
7
8
|
import { applyHumanDecision } from './decision-intents.ts';
|
|
@@ -814,14 +815,8 @@ async function probeVerificationFailure(taskRef: string, policy: FailurePolicy,
|
|
|
814
815
|
function latestArtifact(taskDir: string, family: string): string | undefined {
|
|
815
816
|
let names: string[];
|
|
816
817
|
try { names = fs.readdirSync(taskDir); } catch { return undefined; }
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
));
|
|
820
|
-
candidates.sort((a, b) => {
|
|
821
|
-
const round = (name: string) => Number(/-r(\d+)\.md$/.exec(name)?.[1] ?? 1);
|
|
822
|
-
return round(b) - round(a);
|
|
823
|
-
});
|
|
824
|
-
return candidates[0];
|
|
818
|
+
return names.map(parseArtifactName).filter((identity) => identity !== null)
|
|
819
|
+
.filter((identity) => identity.family === family).sort((left, right) => right.round - left.round)[0]?.name;
|
|
825
820
|
}
|
|
826
821
|
|
|
827
822
|
async function probeVerificationEngineFailure(policy: FailurePolicy, resolved: ProbeTask): Promise<ProducerFailure | OverrideError> {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseArtifactName } from './artifact-name.ts';
|
|
1
2
|
import { parseTable } from './sections.ts';
|
|
2
3
|
|
|
3
4
|
const SECTION_ALIASES = ['实现输入', 'Implementation Inputs'] as const;
|
|
@@ -6,7 +7,6 @@ const COLUMNS = [
|
|
|
6
7
|
'decided_at', 'status', 'consumed_by'
|
|
7
8
|
] as const;
|
|
8
9
|
const TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/;
|
|
9
|
-
const ARTIFACT_RE = /^code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/;
|
|
10
10
|
|
|
11
11
|
type ImplementationInputStatus = 'declared' | 'pending' | 'not-required' | 'consumed';
|
|
12
12
|
type ImplementationInput = {
|
|
@@ -36,7 +36,7 @@ function validateInput(row: ImplementationInput): void {
|
|
|
36
36
|
(row.status === 'declared' && row.decidedAt === '' && row.consumedBy === '') ||
|
|
37
37
|
(hasValidTimestamp && row.needsImplementation && row.status === 'pending' && row.consumedBy === '') ||
|
|
38
38
|
(hasValidTimestamp && !row.needsImplementation && row.status === 'not-required' && row.consumedBy === '') ||
|
|
39
|
-
(hasValidTimestamp && row.needsImplementation && row.status === 'consumed' &&
|
|
39
|
+
(hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && parseArtifactName(row.consumedBy)?.family === 'code');
|
|
40
40
|
if (!valid) throw new Error(`implementation input ${row.id} has an invalid state combination`);
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -143,7 +143,7 @@ function selectPendingImplementationInput(
|
|
|
143
143
|
function consumeImplementationInput(
|
|
144
144
|
rows: readonly ImplementationInput[], id: string, artifact: string
|
|
145
145
|
): ImplementationInput[] {
|
|
146
|
-
if (
|
|
146
|
+
if (parseArtifactName(artifact)?.family !== 'code') throw new Error(`implementation input artifact '${artifact}' is invalid`);
|
|
147
147
|
let found = false;
|
|
148
148
|
const next = rows.map((row) => {
|
|
149
149
|
if (row.id !== id) return row;
|