@fitlab-ai/agent-infra 0.9.14 → 0.10.0
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/internal-cli.sh +45 -0
- package/dist/bin/internal-cli.js +93 -30
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +9 -4
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +39 -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/cli-route-inventory.js +4 -2
- package/dist/lib/internal/codex-lifecycle.js +6 -6
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +331 -0
- package/dist/lib/internal/platform-pr.js +18 -4
- package/dist/lib/internal/release-workflow.js +63 -84
- package/dist/lib/internal/sandbox-control.js +149 -2
- package/dist/lib/internal/task-artifact.js +16 -263
- package/dist/lib/internal/task-create.js +6 -5
- package/dist/lib/internal/task-event.js +37 -37
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-operation-registry.js +101 -4
- package/dist/lib/internal/task-review.js +11 -56
- package/dist/lib/internal/task-validate.js +30 -10
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +4 -1
- 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 +15 -20
- package/dist/lib/platform/issue-comments.js +18 -12
- package/dist/lib/platform/issues.js +2 -180
- package/dist/lib/platform/pr-change-report.js +5 -4
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +117 -33
- package/dist/lib/platform/pull-requests.js +5 -108
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/task-identities.js +2 -9
- package/dist/lib/platform/verification-sync.js +132 -143
- package/dist/lib/pr-review/evidence-grading.js +4 -8
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/rm.js +19 -1511
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +93 -54
- package/dist/lib/sandbox/control/executor.js +129 -65
- 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 +36 -2
- package/dist/lib/sandbox/control/native-file-lock.js +6 -98
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +518 -127
- package/dist/lib/sandbox/control/state.js +64 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +89 -0
- package/dist/lib/sandbox/index.js +1 -2
- package/dist/lib/sandbox/recovery.js +28 -4
- package/dist/lib/sandbox/removal.js +822 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-identity.js +17 -1
- package/dist/lib/sandbox/workspace-view.js +13 -14
- 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 +79 -0
- package/dist/lib/task/artifact-lifecycle.js +30 -28
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +29 -289
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-recovery.js +430 -0
- package/dist/lib/task/artifact-repair-intent.js +134 -29
- package/dist/lib/task/artifact-schema.js +6 -14
- package/dist/lib/task/capabilities.js +12 -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 +33 -20
- package/dist/lib/task/control-authority.js +403 -6
- package/dist/lib/task/control-recovery.js +140 -0
- package/dist/lib/task/create-service.js +1 -8
- package/dist/lib/task/create.js +62 -9
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/delegation-receipts.js +64 -3
- package/dist/lib/task/events.js +275 -22
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +8 -14
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/intermediate-cleanup.js +441 -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-recovery.js +549 -0
- package/dist/lib/task/lifecycle.js +31 -4
- package/dist/lib/task/local-artifact-finalization.js +197 -197
- 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 +105 -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 +66 -19
- package/dist/lib/task/pr-delivery-fact.js +12 -56
- package/dist/lib/task/qualification-audit.js +20 -34
- package/dist/lib/task/recovery-warning.js +19 -0
- package/dist/lib/task/review-artifacts.js +2 -18
- package/dist/lib/task/review-command.js +26 -0
- package/dist/lib/task/review-finalization.js +175 -216
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -56
- package/dist/lib/task/short-id.js +15 -7
- package/dist/lib/task/task-execution-lock.js +1 -1
- package/dist/lib/task/verification-engine.js +119 -337
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +16 -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/lib/version.js +1 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +10 -4
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
- package/lib/fs/durable-write.ts +30 -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/cli-route-inventory.ts +4 -2
- package/lib/internal/codex-lifecycle.ts +6 -6
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +315 -0
- package/lib/internal/platform-pr.ts +15 -4
- package/lib/internal/release-workflow.ts +74 -82
- package/lib/internal/sandbox-control.ts +162 -1
- package/lib/internal/task-artifact.ts +14 -217
- package/lib/internal/task-create.ts +5 -5
- package/lib/internal/task-event.ts +20 -11
- package/lib/internal/task-lifecycle.ts +4 -3
- package/lib/internal/task-operation-registry.ts +111 -4
- package/lib/internal/task-review.ts +9 -54
- package/lib/internal/task-validate.ts +26 -10
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +5 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +15 -20
- package/lib/platform/issue-comments.ts +21 -14
- package/lib/platform/issues.ts +5 -246
- package/lib/platform/pr-change-report.ts +7 -5
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +123 -35
- package/lib/platform/pull-requests.ts +6 -173
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/task-identities.ts +3 -14
- package/lib/platform/verification-sync.ts +150 -156
- package/lib/pr-review/evidence-grading.ts +4 -9
- package/lib/release/workflow.ts +3 -3
- package/lib/sandbox/commands/create.ts +7 -6
- package/lib/sandbox/commands/rm.ts +21 -1895
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +109 -66
- package/lib/sandbox/control/executor.ts +134 -74
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +45 -3
- package/lib/sandbox/control/native-file-lock.ts +6 -107
- package/lib/sandbox/control/protocol.ts +80 -6
- package/lib/sandbox/control/server.ts +567 -132
- package/lib/sandbox/control/state.ts +86 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +92 -0
- package/lib/sandbox/index.ts +1 -2
- package/lib/sandbox/recovery.ts +28 -5
- package/lib/sandbox/removal.ts +1142 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-identity.ts +19 -3
- package/lib/sandbox/workspace-view.ts +17 -15
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +85 -0
- package/lib/task/artifact-lifecycle.ts +31 -26
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +34 -310
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-recovery.ts +519 -0
- package/lib/task/artifact-repair-intent.ts +162 -33
- package/lib/task/artifact-schema.ts +5 -34
- package/lib/task/capabilities.ts +13 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +37 -20
- package/lib/task/control-authority.ts +540 -10
- package/lib/task/control-recovery.ts +186 -0
- package/lib/task/create-service.ts +1 -10
- package/lib/task/create.ts +63 -8
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/delegation-receipts.ts +73 -2
- package/lib/task/events.ts +309 -24
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +8 -16
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/intermediate-cleanup.ts +493 -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-recovery.ts +691 -0
- package/lib/task/lifecycle.ts +30 -5
- package/lib/task/local-artifact-finalization.ts +244 -234
- 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 +156 -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 +84 -19
- package/lib/task/pr-delivery-fact.ts +15 -62
- package/lib/task/qualification-audit.ts +22 -33
- package/lib/task/recovery-warning.ts +21 -0
- package/lib/task/review-artifacts.ts +1 -20
- package/lib/task/review-command.ts +24 -0
- package/lib/task/review-finalization.ts +183 -292
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -54
- package/lib/task/short-id.ts +27 -5
- package/lib/task/task-execution-lock.ts +1 -1
- package/lib/task/verification-engine.ts +113 -383
- package/lib/task/verification-types.ts +15 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +17 -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/lib/version.ts +1 -15
- package/package.json +6 -6
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +4 -2
- package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
- 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 +6 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -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 +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -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 +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/config/verify.json +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/import-issue/config/verify.json +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
- 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/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/config/verify.json +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
- package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
- package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
- 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 +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- 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 +17 -6
- package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
- package/templates/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/import-issue.en.md +1 -1
- package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
- package/templates/.claude/commands/restore-task.en.md +1 -1
- package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
package/dist/lib/task/events.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { appendActivityEntry, locateActivityLog, pairEntries, startedBackedRows } from './activity-log.js';
|
|
4
|
-
import { buildArtifactLinkSection,
|
|
4
|
+
import { buildArtifactLinkSection, inspectArtifactDirectory, parseReviewedInputReference, parseCodePlanInputReference, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
5
|
+
import { artifactName, parseArtifactName } from './artifact-name.js';
|
|
5
6
|
import { ArtifactReceiptError, parseArtifactReceipts, sha256File, upsertArtifactReceipt } from './artifact-receipts.js';
|
|
6
7
|
import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
7
8
|
import { consumeImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
|
|
@@ -15,12 +16,16 @@ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-
|
|
|
15
16
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
16
17
|
import { allowsManualOverride } from './guard-override.js';
|
|
17
18
|
import { consumeLocalArtifactFinalizationIntent, readLocalArtifactFinalizationIntent, validateLocalArtifact } from './local-artifact-finalization.js';
|
|
19
|
+
import { writeArtifactRecoveryIntent, readArtifactRecoveryIntent } from './artifact-repair-intent.js';
|
|
20
|
+
import { reconcileArtifactRecovery, recoveryContextFromIntent, sha256Content } from './artifact-recovery.js';
|
|
18
21
|
import { buildLifecycleFacts, canStart } from './capabilities.js';
|
|
19
22
|
import { createInvalidationOperation, invalidationMutation, parseInvalidationDocument, targetIdFor, upsertInvalidation } from './invalidation.js';
|
|
20
23
|
import { consumeReworkIntents, parseReworkIntentDocument, reworkIntentMutation, supersedeReworkIntents } from './rework-intent.js';
|
|
21
24
|
import { ARTIFACT_FAMILIES, expectedQualificationRelations, parseQualificationAudit, parseTaskQualification, upstreamArtifactDigest, validateQualificationAudit } from './qualification-audit.js';
|
|
22
25
|
import { getArtifactSchema } from './artifact-schema.js';
|
|
23
|
-
import { inspectArtifactContract } from './artifact-operations.js';
|
|
26
|
+
import { canonicalSemanticDigest, inspectArtifactContract } from './artifact-operations.js';
|
|
27
|
+
import { consumeLifecycleRecoveryAttestation, lifecycleRecoveryAttestationDigest, validateLifecycleRecoveryAttestation } from './control-authority.js';
|
|
28
|
+
import { readManualValidationCompletion } from './manual-validation-completion.js';
|
|
24
29
|
const eventCatalog = [
|
|
25
30
|
'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
|
|
26
31
|
'review-analysis.started', 'review-analysis.completed',
|
|
@@ -46,8 +51,8 @@ const SCHEMAS = {
|
|
|
46
51
|
'code.completed': { required: ['artifact', 'artifactSha256', 'semanticDigest'], optional: ['round', 'fixFor', 'implementationInput', 'filesModified', 'testsPassed', 'blockers', 'major', 'minor', 'manualValidation', 'orchestrated'] },
|
|
47
52
|
'review-code.started': { optional: ['round'] },
|
|
48
53
|
'review-code.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round', 'orchestrated'] },
|
|
49
|
-
'manual-validation.started': { optional: ['round'] },
|
|
50
|
-
'manual-validation.completed': { required: ['artifact', 'summaryResult'], optional: ['round'] },
|
|
54
|
+
'manual-validation.started': { optional: ['round', 'transactionId'] },
|
|
55
|
+
'manual-validation.completed': { required: ['artifact', 'summaryResult', 'transactionId', 'receiptDigest', 'prHeadSha'], optional: ['round'] },
|
|
51
56
|
'validation-run.started': { optional: ['round'] },
|
|
52
57
|
'validation-run.completed': { required: ['artifact'], optional: ['round'] }
|
|
53
58
|
};
|
|
@@ -92,7 +97,7 @@ function validateTaskEventRequest(request) {
|
|
|
92
97
|
if (modeRequired.some((key) => request[key] === undefined) || forbidden.some((key) => request[key] !== undefined))
|
|
93
98
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'code.completed requires either initial or fix completion payload' };
|
|
94
99
|
}
|
|
95
|
-
if (request.fixFor &&
|
|
100
|
+
if (request.fixFor && parseArtifactName(request.fixFor)?.family !== 'review-code')
|
|
96
101
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor must reference a canonical review-code artifact' };
|
|
97
102
|
if (request.implementationInput && !/^II-[1-9]\d*$/.test(request.implementationInput))
|
|
98
103
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'implementationInput must be a canonical II-N id' };
|
|
@@ -104,6 +109,19 @@ function validateTaskEventRequest(request) {
|
|
|
104
109
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'fixFor and implementationInput are mutually exclusive' };
|
|
105
110
|
if (request.summaryResult !== undefined && (!request.summaryResult.trim() || /[\r\n]/.test(request.summaryResult)))
|
|
106
111
|
return { code: 'EVENT_PAYLOAD_INVALID', message: 'summaryResult must be a non-empty single line' };
|
|
112
|
+
if (request.event === 'manual-validation.completed') {
|
|
113
|
+
for (const [name, value, pattern] of [
|
|
114
|
+
['transactionId', request.transactionId, /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u],
|
|
115
|
+
['receiptDigest', request.receiptDigest, /^[a-f0-9]{64}$/u],
|
|
116
|
+
['prHeadSha', request.prHeadSha, /^[a-f0-9]{40}$/u]
|
|
117
|
+
])
|
|
118
|
+
if (!value || !pattern.test(value))
|
|
119
|
+
return { code: 'EVENT_PAYLOAD_INVALID', message: `manual-validation.completed requires a valid ${name}` };
|
|
120
|
+
}
|
|
121
|
+
if (request.event === 'manual-validation.started' && request.transactionId !== undefined
|
|
122
|
+
&& !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u.test(request.transactionId)) {
|
|
123
|
+
return { code: 'EVENT_PAYLOAD_INVALID', message: 'manual-validation.started requires a valid transactionId' };
|
|
124
|
+
}
|
|
107
125
|
if (request.event !== 'analyze.awaiting-input' && /\.(?:started|completed)$/.test(request.event)) {
|
|
108
126
|
if (!request.initiator || !request.requestId || !request.reasonCode) {
|
|
109
127
|
return { code: 'EVENT_TRIGGER_REQUIRED', message: `${request.event} requires --initiator, --request-id, and --reason-code` };
|
|
@@ -202,7 +220,9 @@ function identity(request) {
|
|
|
202
220
|
if (family === 'manual-validation') {
|
|
203
221
|
return {
|
|
204
222
|
family, phase, action: spec.label,
|
|
205
|
-
note: phase === 'started'
|
|
223
|
+
note: phase === 'started'
|
|
224
|
+
? `started${request.transactionId ? `; transaction=${request.transactionId}` : ''}`
|
|
225
|
+
: `Manual validation passed → ${request.artifact}; ${request.summaryResult}; transaction=${request.transactionId}; receipt=${request.receiptDigest}; head=${request.prHeadSha}`,
|
|
206
226
|
target: null
|
|
207
227
|
};
|
|
208
228
|
}
|
|
@@ -257,6 +277,24 @@ function normalizeStarted(request, repoRoot) {
|
|
|
257
277
|
return { error: { code: 'EVENT_ARTIFACT_CONFLICT', message: `implementationInput '${request.implementationInput ?? ''}' conflicts with artifact context` }, context };
|
|
258
278
|
return { request: { ...request, round, artifact: context.next.name, fixFor: expectedFix, implementationInput: expectedImplementation }, context };
|
|
259
279
|
}
|
|
280
|
+
function validateManualValidationCompletion(taskDir, taskId, request, artifactPath) {
|
|
281
|
+
const completion = readManualValidationCompletion(taskDir, {
|
|
282
|
+
transactionId: request.transactionId,
|
|
283
|
+
taskId,
|
|
284
|
+
prHeadSha: request.prHeadSha,
|
|
285
|
+
artifact: request.artifact,
|
|
286
|
+
receiptDigest: request.receiptDigest
|
|
287
|
+
});
|
|
288
|
+
if (!completion.ok)
|
|
289
|
+
return completion.error;
|
|
290
|
+
const { receipt, transaction } = completion.value;
|
|
291
|
+
if (!['receipt-committed', 'final-promotion-in-progress', 'committed'].includes(transaction.phase)) {
|
|
292
|
+
return { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'manual validation completion requires the receipt-backed transaction phase' };
|
|
293
|
+
}
|
|
294
|
+
if (sha256File(artifactPath) !== receipt.artifactSha256)
|
|
295
|
+
return { code: 'MANUAL_VALIDATION_RECEIPT_INVALID', message: 'manual-validation artifact digest does not match the receipt' };
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
260
298
|
function openStartedIdentity(rows, family) {
|
|
261
299
|
const spec = FAMILY[family];
|
|
262
300
|
if (family === 'manual-validation') {
|
|
@@ -277,6 +315,59 @@ function openStartedIdentity(rows, family) {
|
|
|
277
315
|
function reviewInputFamily(family) {
|
|
278
316
|
return family === 'review-analysis' ? 'analysis' : family === 'review-plan' ? 'plan' : 'code';
|
|
279
317
|
}
|
|
318
|
+
function reconcileReviewCompletionReplay(repoRoot, taskDir, taskId, family, artifact, round) {
|
|
319
|
+
const stage = REVIEW_LEDGER_STAGES[family];
|
|
320
|
+
if (!stage)
|
|
321
|
+
return null;
|
|
322
|
+
const reviewFamily = family;
|
|
323
|
+
try {
|
|
324
|
+
let intent = readArtifactRecoveryIntent(repoRoot, taskId, reviewFamily, artifact);
|
|
325
|
+
if (!intent)
|
|
326
|
+
return null;
|
|
327
|
+
if (intent.state === 'commit-started') {
|
|
328
|
+
const reconciled = reconcileArtifactRecovery(recoveryContextFromIntent(repoRoot, taskDir, intent), { lockAlreadyHeld: true });
|
|
329
|
+
if (reconciled.status !== 'passed' && reconciled.status !== 'consumed') {
|
|
330
|
+
return {
|
|
331
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
332
|
+
message: `review finalizer recovery remains ${reconciled.status} for ${artifact}`
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
intent = reconciled.intent;
|
|
336
|
+
}
|
|
337
|
+
if (!['passed', 'consumed'].includes(intent.state)) {
|
|
338
|
+
return {
|
|
339
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
340
|
+
message: `review finalizer provenance is incomplete for ${artifact}`
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
const validated = validateCompletedArtifact(taskDir, FAMILY[family].artifact, artifact, round);
|
|
344
|
+
if (!validated.ok) {
|
|
345
|
+
return { code: 'EVENT_ARTIFACT_CONFLICT', message: validated.error.message };
|
|
346
|
+
}
|
|
347
|
+
const content = fs.readFileSync(validated.artifact.path, 'utf8');
|
|
348
|
+
const expectedRequestId = `review-finalize:${taskId}:${stage}:${round}`;
|
|
349
|
+
if (intent.round !== round
|
|
350
|
+
|| intent.requestId !== expectedRequestId
|
|
351
|
+
|| intent.finalArtifactSha256 !== sha256Content(content)
|
|
352
|
+
|| intent.finalSemanticDigest !== canonicalSemanticDigest(content)) {
|
|
353
|
+
return {
|
|
354
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
355
|
+
message: `review finalizer provenance does not match ${artifact}`
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
if (intent.state === 'passed') {
|
|
359
|
+
const consumed = { ...intent, state: 'consumed', updatedAt: Date.now() };
|
|
360
|
+
writeArtifactRecoveryIntent(repoRoot, consumed, { expected: intent });
|
|
361
|
+
}
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
return {
|
|
366
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
367
|
+
message: `review finalizer recovery could not be reconciled for ${artifact}: ${error instanceof Error ? error.message : String(error)}`
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
280
371
|
function relationForStartedInput(family, input) {
|
|
281
372
|
if (family.startsWith('review-')) {
|
|
282
373
|
return input.family === reviewInputFamily(family) ? 'reviewed-input' : 'approval-context';
|
|
@@ -623,6 +714,11 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
623
714
|
if (openIdentity) {
|
|
624
715
|
if (openIdentity.row.agent !== request.agent)
|
|
625
716
|
return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
717
|
+
if (initialParts.family === 'manual-validation' && request.transactionId !== undefined) {
|
|
718
|
+
const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(openIdentity.row.note)?.[1];
|
|
719
|
+
if (existingTransactionId !== undefined && existingTransactionId !== request.transactionId)
|
|
720
|
+
return failed(request, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
721
|
+
}
|
|
626
722
|
if (request.round !== undefined && request.round !== openIdentity.round)
|
|
627
723
|
return failed(request, { code: 'EVENT_ARTIFACT_CONFLICT', message: `round ${request.round} conflicts with open round ${openIdentity.round}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
628
724
|
if (request.fixFor !== undefined && request.fixFor !== openIdentity.fixFor)
|
|
@@ -644,6 +740,25 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
644
740
|
normalized = { ...request, round: identity.round };
|
|
645
741
|
}
|
|
646
742
|
const eventIdentity = identity(normalized);
|
|
743
|
+
const lifecycleAuthority = options.lifecycleRecoveryAttestation ?? null;
|
|
744
|
+
if (lifecycleAuthority) {
|
|
745
|
+
try {
|
|
746
|
+
validateLifecycleRecoveryAttestation(lifecycleAuthority);
|
|
747
|
+
}
|
|
748
|
+
catch (error) {
|
|
749
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: error instanceof Error ? error.message : String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
750
|
+
}
|
|
751
|
+
const expectedFamily = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
|
|
752
|
+
if (eventIdentity.phase !== 'completed'
|
|
753
|
+
|| lifecycleAuthority.phase !== 'task-event.completed'
|
|
754
|
+
|| lifecycleAuthority.taskId !== resolved.taskId
|
|
755
|
+
|| lifecycleAuthority.family !== expectedFamily
|
|
756
|
+
|| lifecycleAuthority.artifact !== normalized.artifact
|
|
757
|
+
|| lifecycleAuthority.round !== normalized.round
|
|
758
|
+
|| (normalized.requestId !== undefined && lifecycleAuthority.lifecycleRequestId !== normalized.requestId)) {
|
|
759
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'lifecycle completion authority does not match the event tuple' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
760
|
+
}
|
|
761
|
+
}
|
|
647
762
|
const currentStep = typeof frontmatter.current_step === 'string' ? frontmatter.current_step : '';
|
|
648
763
|
const matchingRows = rows.filter((item) => item.step === eventIdentity.action);
|
|
649
764
|
const manual = eventIdentity.family === 'manual-validation';
|
|
@@ -654,16 +769,30 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
654
769
|
const row = manual ? openRows.at(-1) : matchingRows[0];
|
|
655
770
|
let completedArtifact = null;
|
|
656
771
|
let localFinalizationIntent = null;
|
|
772
|
+
let reviewFinalizationIntent = null;
|
|
773
|
+
let reviewContent = null;
|
|
657
774
|
if (eventIdentity.phase === 'started' && row) {
|
|
658
775
|
if (row.agent !== normalized.agent)
|
|
659
776
|
return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open started event has a different agent' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
777
|
+
if (manual && normalized.transactionId !== undefined) {
|
|
778
|
+
const existingTransactionId = /(?:^|;\s*)transaction=([A-Za-z0-9][A-Za-z0-9._-]{0,127})(?:;|$)/u.exec(row.note)?.[1];
|
|
779
|
+
if (existingTransactionId !== undefined && existingTransactionId !== normalized.transactionId) {
|
|
780
|
+
return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'open manual-validation started event has a different transactionId' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
781
|
+
}
|
|
782
|
+
}
|
|
660
783
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, row.started, frontmatter, artifactContext);
|
|
661
784
|
}
|
|
662
785
|
if (eventIdentity.phase === 'started' && !manual && completedRows.length > 0)
|
|
663
786
|
return failed(normalized, { code: 'EVENT_ALREADY_COMPLETED', message: 'event identity is already completed' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
664
787
|
const done = completedRows.find((item) => item.note === eventIdentity.note);
|
|
665
|
-
if (eventIdentity.phase === 'completed' && done)
|
|
788
|
+
if (eventIdentity.phase === 'completed' && done) {
|
|
789
|
+
if (!normalized.dryRun && eventIdentity.family.startsWith('review-') && normalized.artifact && normalized.round) {
|
|
790
|
+
const recoveryError = reconcileReviewCompletionReplay(resolved.repoRoot, resolved.taskDir, resolved.taskId, eventIdentity.family, normalized.artifact, normalized.round);
|
|
791
|
+
if (recoveryError)
|
|
792
|
+
return failed(normalized, recoveryError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
793
|
+
}
|
|
666
794
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, done.done, frontmatter, artifactContext);
|
|
795
|
+
}
|
|
667
796
|
if (eventIdentity.phase === 'completed' && completedRows.length > 0 && openRows.length === 0)
|
|
668
797
|
return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'event identity is already completed with a different payload' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
669
798
|
if (eventIdentity.phase === 'completed' && !row?.started && !allowsManualOverride(options.manualOverride, 'task-event', 'EVENT_START_MISSING'))
|
|
@@ -673,8 +802,12 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
673
802
|
if (!validated.ok)
|
|
674
803
|
return failed(normalized, validated.error, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
675
804
|
completedArtifact = validated.artifact;
|
|
805
|
+
if (eventIdentity.family === 'manual-validation') {
|
|
806
|
+
const validationError = validateManualValidationCompletion(resolved.taskDir, resolved.taskId, normalized, completedArtifact.path);
|
|
807
|
+
if (validationError)
|
|
808
|
+
return failed(normalized, validationError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
809
|
+
}
|
|
676
810
|
if (eventIdentity.family.startsWith('review-')) {
|
|
677
|
-
let reviewContent;
|
|
678
811
|
try {
|
|
679
812
|
reviewContent = fs.readFileSync(completedArtifact.path, 'utf8');
|
|
680
813
|
}
|
|
@@ -703,6 +836,20 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
703
836
|
const localFamily = eventIdentity.family === 'analyze'
|
|
704
837
|
? 'analysis'
|
|
705
838
|
: eventIdentity.family === 'plan' ? 'plan' : 'code';
|
|
839
|
+
try {
|
|
840
|
+
localFinalizationIntent = readLocalArtifactFinalizationIntent(resolved.repoRoot, resolved.taskId, localFamily, completedArtifact.name);
|
|
841
|
+
if (localFinalizationIntent?.state === 'commit-started') {
|
|
842
|
+
const reconciled = reconcileArtifactRecovery(recoveryContextFromIntent(resolved.repoRoot, resolved.taskDir, localFinalizationIntent), { lockAlreadyHeld: true });
|
|
843
|
+
if (reconciled.status === 'passed' || reconciled.status === 'consumed')
|
|
844
|
+
localFinalizationIntent = reconciled.intent;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
catch (error) {
|
|
848
|
+
return failed(normalized, {
|
|
849
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
850
|
+
message: `local finalizer recovery could not be reconciled for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
851
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
852
|
+
}
|
|
706
853
|
let artifactContent;
|
|
707
854
|
try {
|
|
708
855
|
artifactContent = fs.readFileSync(completedArtifact.path, 'utf8');
|
|
@@ -734,27 +881,29 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
734
881
|
message: `semantic digest does not match finalizer result for ${completedArtifact.name}`
|
|
735
882
|
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
736
883
|
}
|
|
737
|
-
try {
|
|
738
|
-
localFinalizationIntent = readLocalArtifactFinalizationIntent(resolved.repoRoot, resolved.taskId, localFamily, completedArtifact.name);
|
|
739
|
-
}
|
|
740
|
-
catch (error) {
|
|
741
|
-
return failed(normalized, {
|
|
742
|
-
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
743
|
-
message: `cannot read local finalizer provenance for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
744
|
-
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
745
|
-
}
|
|
746
884
|
if (!localFinalizationIntent || !['passed', 'consumed'].includes(localFinalizationIntent.state)) {
|
|
747
885
|
return failed(normalized, {
|
|
748
886
|
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
749
887
|
message: `local finalizer provenance is missing or incomplete for ${completedArtifact.name}`
|
|
750
888
|
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
751
889
|
}
|
|
752
|
-
if (localFinalizationIntent.
|
|
890
|
+
if (localFinalizationIntent.finalArtifactSha256 !== actualSha256 || localFinalizationIntent.finalSemanticDigest !== local.semanticDigest) {
|
|
753
891
|
return failed(normalized, {
|
|
754
892
|
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
755
893
|
message: `local finalizer provenance does not match ${completedArtifact.name}`
|
|
756
894
|
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
757
895
|
}
|
|
896
|
+
if (localFinalizationIntent.authorityDigest !== null) {
|
|
897
|
+
if (!lifecycleAuthority
|
|
898
|
+
|| localFinalizationIntent.recoveryOperationId !== lifecycleAuthority.operationId
|
|
899
|
+
|| localFinalizationIntent.requestId !== lifecycleAuthority.lifecycleRequestId
|
|
900
|
+
|| localFinalizationIntent.authorityDigest !== lifecycleRecoveryAttestationDigest(lifecycleAuthority)) {
|
|
901
|
+
return failed(normalized, {
|
|
902
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
903
|
+
message: `local finalizer provenance does not match lifecycle completion authority for ${completedArtifact.name}`
|
|
904
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
905
|
+
}
|
|
906
|
+
}
|
|
758
907
|
}
|
|
759
908
|
}
|
|
760
909
|
if (eventIdentity.phase === 'waiting' && section.entries.some((entry) => entry.step === eventIdentity.action && entry.note === eventIdentity.note)) {
|
|
@@ -778,6 +927,43 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
778
927
|
const findingCountError = validateReviewFindingCounts(normalized, content, eventIdentity.family, completedArtifact?.path ?? null);
|
|
779
928
|
if (findingCountError)
|
|
780
929
|
return failed(normalized, findingCountError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
930
|
+
if (eventIdentity.phase === 'completed' && eventIdentity.family.startsWith('review-') && completedArtifact && reviewContent !== null) {
|
|
931
|
+
try {
|
|
932
|
+
reviewFinalizationIntent = readArtifactRecoveryIntent(resolved.repoRoot, resolved.taskId, eventIdentity.family, completedArtifact.name);
|
|
933
|
+
if (!normalized.dryRun && reviewFinalizationIntent?.state === 'commit-started') {
|
|
934
|
+
const reconciled = reconcileArtifactRecovery(recoveryContextFromIntent(resolved.repoRoot, resolved.taskDir, reviewFinalizationIntent), { lockAlreadyHeld: true });
|
|
935
|
+
if (reconciled.status === 'passed' || reconciled.status === 'consumed')
|
|
936
|
+
reviewFinalizationIntent = reconciled.intent;
|
|
937
|
+
}
|
|
938
|
+
if (!reviewFinalizationIntent || !['passed', 'consumed'].includes(reviewFinalizationIntent.state)) {
|
|
939
|
+
return failed(normalized, {
|
|
940
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
941
|
+
message: `review finalizer provenance is missing or incomplete for ${completedArtifact.name}`
|
|
942
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
943
|
+
}
|
|
944
|
+
const reviewStage = eventIdentity.family === 'review-analysis'
|
|
945
|
+
? 'analysis'
|
|
946
|
+
: eventIdentity.family === 'review-plan' ? 'plan' : 'code';
|
|
947
|
+
const actualSha256 = sha256File(completedArtifact.path);
|
|
948
|
+
const actualSemanticDigest = canonicalSemanticDigest(reviewContent);
|
|
949
|
+
const expectedRequestId = `review-finalize:${resolved.taskId}:${reviewStage}:${normalized.round}`;
|
|
950
|
+
if (reviewFinalizationIntent.round !== normalized.round
|
|
951
|
+
|| reviewFinalizationIntent.requestId !== expectedRequestId
|
|
952
|
+
|| reviewFinalizationIntent.finalArtifactSha256 !== actualSha256
|
|
953
|
+
|| reviewFinalizationIntent.finalSemanticDigest !== actualSemanticDigest) {
|
|
954
|
+
return failed(normalized, {
|
|
955
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
956
|
+
message: `review finalizer provenance does not match ${completedArtifact.name}`
|
|
957
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
catch (error) {
|
|
961
|
+
return failed(normalized, {
|
|
962
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
963
|
+
message: `review finalizer recovery could not be reconciled for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
964
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
965
|
+
}
|
|
966
|
+
}
|
|
781
967
|
let orchestrationCompletion = null;
|
|
782
968
|
if (eventIdentity.phase === 'completed' && eventIdentity.family !== 'manual-validation' && eventIdentity.family !== 'validation-run') {
|
|
783
969
|
const orchestrationStage = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
|
|
@@ -914,14 +1100,54 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
914
1100
|
});
|
|
915
1101
|
}
|
|
916
1102
|
mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
|
|
917
|
-
if (!normalized.dryRun && localFinalizationIntent && completedArtifact
|
|
1103
|
+
if (!normalized.dryRun && localFinalizationIntent && completedArtifact) {
|
|
1104
|
+
try {
|
|
1105
|
+
if (lifecycleAuthority && localFinalizationIntent.state === 'passed') {
|
|
1106
|
+
const expectedIntent = localFinalizationIntent;
|
|
1107
|
+
localFinalizationIntent = {
|
|
1108
|
+
...localFinalizationIntent,
|
|
1109
|
+
state: 'commit-started',
|
|
1110
|
+
phase: 'task-event.completed',
|
|
1111
|
+
authorityDigest: lifecycleRecoveryAttestationDigest(lifecycleAuthority),
|
|
1112
|
+
requestId: lifecycleAuthority.lifecycleRequestId,
|
|
1113
|
+
updatedAt: Date.now()
|
|
1114
|
+
};
|
|
1115
|
+
writeArtifactRecoveryIntent(resolved.repoRoot, localFinalizationIntent, { expected: expectedIntent });
|
|
1116
|
+
}
|
|
1117
|
+
else if (!lifecycleAuthority && localFinalizationIntent.state === 'passed') {
|
|
1118
|
+
localFinalizationIntent = consumeLocalArtifactFinalizationIntent(resolved.repoRoot, localFinalizationIntent, { lockAlreadyHeld: options.lockAlreadyHeld });
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
catch (error) {
|
|
1122
|
+
return failed(normalized, {
|
|
1123
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
1124
|
+
message: `local finalizer provenance could not be advanced before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
1125
|
+
}, {
|
|
1126
|
+
taskId: resolved.taskId,
|
|
1127
|
+
taskMdPath: resolved.taskMdPath,
|
|
1128
|
+
fromStep: currentStep,
|
|
1129
|
+
toStep: step,
|
|
1130
|
+
action: eventIdentity.action,
|
|
1131
|
+
phase: eventIdentity.phase,
|
|
1132
|
+
artifactContext
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
if (!normalized.dryRun && reviewFinalizationIntent && completedArtifact && reviewFinalizationIntent.state === 'passed') {
|
|
918
1137
|
try {
|
|
919
|
-
|
|
1138
|
+
const expectedIntent = reviewFinalizationIntent;
|
|
1139
|
+
reviewFinalizationIntent = {
|
|
1140
|
+
...reviewFinalizationIntent,
|
|
1141
|
+
state: 'commit-started',
|
|
1142
|
+
phase: 'task-event.completed',
|
|
1143
|
+
updatedAt: Date.now()
|
|
1144
|
+
};
|
|
1145
|
+
writeArtifactRecoveryIntent(resolved.repoRoot, reviewFinalizationIntent, { expected: expectedIntent });
|
|
920
1146
|
}
|
|
921
1147
|
catch (error) {
|
|
922
1148
|
return failed(normalized, {
|
|
923
1149
|
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
924
|
-
message: `
|
|
1150
|
+
message: `review finalizer provenance could not be advanced before task write for ${completedArtifact.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
925
1151
|
}, {
|
|
926
1152
|
taskId: resolved.taskId,
|
|
927
1153
|
taskMdPath: resolved.taskMdPath,
|
|
@@ -938,6 +1164,33 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
938
1164
|
const result = writeTask({ taskRef: normalized.taskRef, expectedState: stateOverride ? resolved.state : 'active', dryRun: normalized.dryRun, mutations }, { ...options, invalidationContext: sourceCompletion ? 'source-completion' : 'standard', metadataProvider: () => metadata });
|
|
939
1165
|
if (result.status === 'failed')
|
|
940
1166
|
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 });
|
|
1167
|
+
if (!normalized.dryRun && lifecycleAuthority && localFinalizationIntent && completedArtifact) {
|
|
1168
|
+
try {
|
|
1169
|
+
if (!options.deferLifecycleRecoveryConsumption)
|
|
1170
|
+
consumeLifecycleRecoveryAttestation(lifecycleAuthority);
|
|
1171
|
+
writeArtifactRecoveryIntent(resolved.repoRoot, { ...localFinalizationIntent, state: 'consumed', updatedAt: Date.now() }, { expected: localFinalizationIntent });
|
|
1172
|
+
}
|
|
1173
|
+
catch (error) {
|
|
1174
|
+
return failed(normalized, {
|
|
1175
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
1176
|
+
message: `task completion was written but lifecycle recovery consumption is incomplete: ${error instanceof Error ? error.message : String(error)}`
|
|
1177
|
+
}, { 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 });
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
if (!normalized.dryRun && reviewFinalizationIntent && completedArtifact) {
|
|
1181
|
+
try {
|
|
1182
|
+
if (reviewFinalizationIntent.state === 'commit-started') {
|
|
1183
|
+
const consumed = { ...reviewFinalizationIntent, state: 'consumed', updatedAt: Date.now() };
|
|
1184
|
+
writeArtifactRecoveryIntent(resolved.repoRoot, consumed, { expected: reviewFinalizationIntent });
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
catch (error) {
|
|
1188
|
+
return failed(normalized, {
|
|
1189
|
+
code: 'EVENT_ARTIFACT_CONFLICT',
|
|
1190
|
+
message: `review finalizer recovery consumption is incomplete: ${error instanceof Error ? error.message : String(error)}`
|
|
1191
|
+
}, { 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 });
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
941
1194
|
if (!normalized.dryRun && orchestrationCompletion) {
|
|
942
1195
|
try {
|
|
943
1196
|
(options.commitOrchestrationCompletion ?? commitOrchestrationStageCompletion)(orchestrationCompletion);
|
|
@@ -984,7 +1237,7 @@ function applyTaskEvent(request, options = {}) {
|
|
|
984
1237
|
if (!resolved.ok)
|
|
985
1238
|
return applyTaskEventUnlocked(request, options);
|
|
986
1239
|
try {
|
|
987
|
-
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, () => applyTaskEventUnlocked(request, options));
|
|
1240
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, () => applyTaskEventUnlocked(request, { ...options, lockAlreadyHeld: true }));
|
|
988
1241
|
}
|
|
989
1242
|
catch (error) {
|
|
990
1243
|
if (!(error instanceof TaskExecutionLockError))
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function taskFinalizationReceiptState(receipt) {
|
|
2
|
+
if (receipt.lifecycle !== 'done' || receipt.taskComment === 'pending'
|
|
3
|
+
|| receipt.verification === 'pending' || receipt.warningProjection !== 'done')
|
|
4
|
+
return 'pending';
|
|
5
|
+
if (receipt.lastError !== null || receipt.warnings.some((warning) => warning.status === 'open'))
|
|
6
|
+
return 'unresolved';
|
|
7
|
+
return 'complete';
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=finalization-state.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { parseArtifactName } from './artifact-name.js';
|
|
4
5
|
import { locateActivityLog } from './activity-log.js';
|
|
5
6
|
import { applyPrReviewActivityIntent } from './activity-intent.js';
|
|
6
7
|
import { applyHumanDecision } from './decision-intents.js';
|
|
@@ -406,10 +407,7 @@ function resourceForSnapshot(policy, options, frontmatter) {
|
|
|
406
407
|
if (policy.producerId === 'platform.issue') {
|
|
407
408
|
const identity = options.probeIssueNumber !== undefined
|
|
408
409
|
? resourceIdentity(options.probeIssueNumber)
|
|
409
|
-
: taskIssueIdentity((frontmatter || {}))
|
|
410
|
-
const number = toPositiveInteger(frontmatter?.issue_number);
|
|
411
|
-
return number === null ? null : resourceIdentity(number);
|
|
412
|
-
})();
|
|
410
|
+
: taskIssueIdentity((frontmatter || {}));
|
|
413
411
|
return identity ? { kind: 'issue', identity, number: resourceIdentityNumber(identity) } : null;
|
|
414
412
|
}
|
|
415
413
|
if (policy.producerId === 'platform.pull-request') {
|
|
@@ -480,11 +478,11 @@ async function probePlatformFailure(taskRef, policy, resolved, options) {
|
|
|
480
478
|
}
|
|
481
479
|
const issueIdentity = options.probeIssueNumber !== undefined
|
|
482
480
|
? resourceIdentity(options.probeIssueNumber)
|
|
483
|
-
: taskIssueIdentity(frontmatter)
|
|
484
|
-
const number = toPositiveInteger(frontmatter.issue_number);
|
|
485
|
-
return number === null ? null : resourceIdentity(number);
|
|
486
|
-
})();
|
|
481
|
+
: taskIssueIdentity(frontmatter);
|
|
487
482
|
const fact = readPrDeliveryFact(frontmatter);
|
|
483
|
+
if (fact.status === 'invalid') {
|
|
484
|
+
return error(fact.error.code, fact.error.message);
|
|
485
|
+
}
|
|
488
486
|
const pullRequestIdentity = options.probePullRequestNumber !== undefined
|
|
489
487
|
? resourceIdentity(options.probePullRequestNumber)
|
|
490
488
|
: fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
@@ -577,12 +575,8 @@ function latestArtifact(taskDir, family) {
|
|
|
577
575
|
catch {
|
|
578
576
|
return undefined;
|
|
579
577
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
const round = (name) => Number(/-r(\d+)\.md$/.exec(name)?.[1] ?? 1);
|
|
583
|
-
return round(b) - round(a);
|
|
584
|
-
});
|
|
585
|
-
return candidates[0];
|
|
578
|
+
return names.map(parseArtifactName).filter((identity) => identity !== null)
|
|
579
|
+
.filter((identity) => identity.family === family).sort((left, right) => right.round - left.round)[0]?.name;
|
|
586
580
|
}
|
|
587
581
|
async function probeVerificationEngineFailure(policy, resolved) {
|
|
588
582
|
const candidates = [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseArtifactName } from './artifact-name.js';
|
|
1
2
|
import { parseTable } from './sections.js';
|
|
2
3
|
const SECTION_ALIASES = ['实现输入', 'Implementation Inputs'];
|
|
3
4
|
const COLUMNS = [
|
|
@@ -5,7 +6,6 @@ const COLUMNS = [
|
|
|
5
6
|
'decided_at', 'status', 'consumed_by'
|
|
6
7
|
];
|
|
7
8
|
const TIMESTAMP_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/;
|
|
8
|
-
const ARTIFACT_RE = /^code(?:-r(?:[2-9]|[1-9]\d+))?\.md$/;
|
|
9
9
|
function validateInput(row) {
|
|
10
10
|
if (!/^II-[1-9]\d*$/.test(row.id))
|
|
11
11
|
throw new Error(`implementation input id '${row.id}' is invalid`);
|
|
@@ -17,7 +17,7 @@ function validateInput(row) {
|
|
|
17
17
|
const valid = (row.status === 'declared' && row.decidedAt === '' && row.consumedBy === '') ||
|
|
18
18
|
(hasValidTimestamp && row.needsImplementation && row.status === 'pending' && row.consumedBy === '') ||
|
|
19
19
|
(hasValidTimestamp && !row.needsImplementation && row.status === 'not-required' && row.consumedBy === '') ||
|
|
20
|
-
(hasValidTimestamp && row.needsImplementation && row.status === 'consumed' &&
|
|
20
|
+
(hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && parseArtifactName(row.consumedBy)?.family === 'code');
|
|
21
21
|
if (!valid)
|
|
22
22
|
throw new Error(`implementation input ${row.id} has an invalid state combination`);
|
|
23
23
|
}
|
|
@@ -122,7 +122,7 @@ function selectPendingImplementationInput(rows) {
|
|
|
122
122
|
})[0] ?? null;
|
|
123
123
|
}
|
|
124
124
|
function consumeImplementationInput(rows, id, artifact) {
|
|
125
|
-
if (
|
|
125
|
+
if (parseArtifactName(artifact)?.family !== 'code')
|
|
126
126
|
throw new Error(`implementation input artifact '${artifact}' is invalid`);
|
|
127
127
|
let found = false;
|
|
128
128
|
const next = rows.map((row) => {
|