@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
|
@@ -16,7 +16,9 @@ import { platformResult } from './types.ts';
|
|
|
16
16
|
import type { PlatformResult } from './types.ts';
|
|
17
17
|
import type { OperationWarning } from '../task/operation-outcome.ts';
|
|
18
18
|
import { providerError, providerOperationContext, providerStatus, providerResourceToken, resourceIdentityNumber, unsupportedProviderOperation } from './provider-bridge.ts';
|
|
19
|
-
import
|
|
19
|
+
import { resourceIdentityEquals } from './resource-identity.ts';
|
|
20
|
+
import type { ResourceIdentity } from './resource-identity.ts';
|
|
21
|
+
import type { PlatformChangeRequestSnapshot } from './snapshots.ts';
|
|
20
22
|
import type { ChangeRequestSnapshot } from './provider-contract.ts';
|
|
21
23
|
import {
|
|
22
24
|
buildPrChangeReport,
|
|
@@ -33,6 +35,8 @@ import type {
|
|
|
33
35
|
MechanicalChangeReport,
|
|
34
36
|
PrChangeReport
|
|
35
37
|
} from './pr-change-report.ts';
|
|
38
|
+
import { manualValidationFinalSummaryProjectionMatches } from '../task/manual-validation-receipt.ts';
|
|
39
|
+
import { readManualValidationCompletion } from '../task/manual-validation-completion.ts';
|
|
36
40
|
|
|
37
41
|
type SummaryComment = { id: number | string; body: string };
|
|
38
42
|
type ChangeReportState = 'ready' | 'missing' | 'stale' | 'invalid';
|
|
@@ -50,9 +54,21 @@ type PullRequestSummaryResult = PlatformResult & {
|
|
|
50
54
|
};
|
|
51
55
|
type PullRequestPrimaryResult = 'pr_created' | 'pr_reused' | 'no_op';
|
|
52
56
|
type SummaryOptions = { cwd?: string; client?: PlatformClient; runtimeVersion?: string };
|
|
57
|
+
type ManualValidationSummaryOptions = {
|
|
58
|
+
phase: 'pending' | 'final';
|
|
59
|
+
transactionId?: string;
|
|
60
|
+
receiptDigest?: string;
|
|
61
|
+
prHeadSha?: string;
|
|
62
|
+
authority?: 'coordinator';
|
|
63
|
+
};
|
|
53
64
|
type ReportWriteResult = PlatformResult & {
|
|
54
65
|
report: { path: string; status: 'written' | 'no-op' | 'planned'; precheckVerdict: 'clear' | 'needs-review'; nextAction: 'watch-pr' | 'review-code' } | null;
|
|
55
66
|
};
|
|
67
|
+
type SummaryCommentStateResult = Omit<PlatformResult, 'comment'> & {
|
|
68
|
+
task: { id: string | null; prNumber: number | null };
|
|
69
|
+
pullRequest: PlatformChangeRequestSnapshot | null;
|
|
70
|
+
comment: SummaryComment | null;
|
|
71
|
+
};
|
|
56
72
|
|
|
57
73
|
function summaryMarker(taskId: string): string {
|
|
58
74
|
return `<!-- sync-pr:${taskId}:summary -->`;
|
|
@@ -88,12 +104,17 @@ function taskReportPath(taskDir: string): string {
|
|
|
88
104
|
return path.join(taskDir, 'pr-change-report.json');
|
|
89
105
|
}
|
|
90
106
|
|
|
91
|
-
function readBoundTaskSnapshot(taskMdPath: string): {
|
|
107
|
+
function readBoundTaskSnapshot(taskMdPath: string): {
|
|
108
|
+
taskContent: string;
|
|
109
|
+
boundFact: Extract<PrDeliveryFact, { state: 'bound' }> | null;
|
|
110
|
+
factError: { code: string; message: string } | null;
|
|
111
|
+
} {
|
|
92
112
|
const taskContent = fs.readFileSync(taskMdPath, 'utf8');
|
|
93
113
|
const frontmatter = parseTypedTaskFrontmatter(taskContent);
|
|
94
114
|
const fact = readPrDeliveryFact(frontmatter);
|
|
95
115
|
const boundFact = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact : null;
|
|
96
|
-
|
|
116
|
+
const factError = fact.status === 'invalid' ? { code: fact.error.code, message: fact.error.message } : null;
|
|
117
|
+
return { taskContent, boundFact, factError };
|
|
97
118
|
}
|
|
98
119
|
|
|
99
120
|
function boundPrNumberForWarning(taskMdPath: string): number | null {
|
|
@@ -175,7 +196,7 @@ function inspectionError(error: { code: string; message: string; retryable: bool
|
|
|
175
196
|
async function inspectBoundPullRequest(
|
|
176
197
|
context: PlatformResult,
|
|
177
198
|
repoRoot: string,
|
|
178
|
-
|
|
199
|
+
prIdentity: ResourceIdentity,
|
|
179
200
|
loadedContext?: LoadedContext
|
|
180
201
|
): Promise<{ ok: true; value: PlatformChangeRequestSnapshot } | { ok: false; error: PlatformResult['error']; status: PlatformResult['status'] }> {
|
|
181
202
|
if (!context.platform.repository) {
|
|
@@ -185,16 +206,15 @@ async function inspectBoundPullRequest(
|
|
|
185
206
|
? { ok: true as const, value: loadedContext }
|
|
186
207
|
: await resolvePlatformProviderContext({ cwd: repoRoot });
|
|
187
208
|
if (!loaded.ok) return { ok: false, status: loaded.context.status, error: loaded.context.error };
|
|
188
|
-
const target = providerResourceToken(loaded.value.provider, 'pull-request', String(prNumber));
|
|
189
209
|
const inspected = loaded.value.provider.changeRequests?.inspect
|
|
190
|
-
? await loaded.value.provider.changeRequests.inspect({ context: providerOperationContext(loaded.value), target })
|
|
210
|
+
? await loaded.value.provider.changeRequests.inspect({ context: providerOperationContext(loaded.value), target: prIdentity })
|
|
191
211
|
: unsupportedProviderOperation(loaded.value.provider, 'changeRequests.inspect');
|
|
192
212
|
if (!inspected.ok) {
|
|
193
213
|
const error = providerError(inspected.error, 'PLATFORM_PROVIDER_OPERATION_FAILED');
|
|
194
214
|
return { ok: false, status: inspectionError(error), error };
|
|
195
215
|
}
|
|
196
216
|
const remote: ChangeRequestSnapshot = inspected.value;
|
|
197
|
-
const number = remote.number ?? resourceIdentityNumber(remote.identity) ??
|
|
217
|
+
const number = remote.number ?? resourceIdentityNumber(remote.identity) ?? resourceIdentityNumber(prIdentity) ?? 0;
|
|
198
218
|
const head = remote.head || { repository: context.platform.repository, ref: '', sha: remote.headSha || '' };
|
|
199
219
|
const base = remote.base || { repository: context.platform.repository, ref: '', sha: remote.baseSha || '' };
|
|
200
220
|
return {
|
|
@@ -202,6 +222,7 @@ async function inspectBoundPullRequest(
|
|
|
202
222
|
value: {
|
|
203
223
|
repository: context.platform.repository,
|
|
204
224
|
number,
|
|
225
|
+
identity: remote.identity ?? prIdentity,
|
|
205
226
|
nodeId: remote.id,
|
|
206
227
|
url: remote.displayUrl || '',
|
|
207
228
|
state: remote.state === 'closed' ? 'closed' : 'open',
|
|
@@ -223,21 +244,22 @@ async function inspectBoundPullRequest(
|
|
|
223
244
|
function sameIdentity(left: PlatformChangeRequestSnapshot, right: PlatformChangeRequestSnapshot): boolean {
|
|
224
245
|
return JSON.stringify({
|
|
225
246
|
repository: left.repository,
|
|
226
|
-
|
|
247
|
+
identity: left.identity ?? null,
|
|
227
248
|
base: left.base,
|
|
228
249
|
head: left.head
|
|
229
250
|
}) === JSON.stringify({
|
|
230
251
|
repository: right.repository,
|
|
231
|
-
|
|
252
|
+
identity: right.identity ?? null,
|
|
232
253
|
base: right.base,
|
|
233
254
|
head: right.head
|
|
234
255
|
});
|
|
235
256
|
}
|
|
236
257
|
|
|
237
258
|
function identityFromSnapshot(snapshot: PlatformChangeRequestSnapshot) {
|
|
259
|
+
if (!snapshot.identity) throw new Error('pull-request snapshot has no canonical identity');
|
|
238
260
|
return {
|
|
239
261
|
repository: snapshot.repository,
|
|
240
|
-
|
|
262
|
+
resource: snapshot.identity,
|
|
241
263
|
base: { ...snapshot.base },
|
|
242
264
|
head: { ...snapshot.head }
|
|
243
265
|
};
|
|
@@ -272,7 +294,7 @@ function currentReportCheck(
|
|
|
272
294
|
const digest = taskIntentDigest(taskContent);
|
|
273
295
|
if (!digest.ok) return { ok: false, error: platformError(digest.error) };
|
|
274
296
|
const reportIdentity = report.identity;
|
|
275
|
-
if (reportIdentity.repository !== snapshot.repository || reportIdentity.
|
|
297
|
+
if (reportIdentity.repository !== snapshot.repository || !resourceIdentityEquals(reportIdentity.resource, snapshot.identity) ||
|
|
276
298
|
JSON.stringify(reportIdentity.base) !== JSON.stringify(snapshot.base) || JSON.stringify(reportIdentity.head) !== JSON.stringify(snapshot.head)) return {
|
|
277
299
|
ok: false,
|
|
278
300
|
error: { code: 'PR_CHANGE_REPORT_STALE', message: 'Change report identity does not match the authoritative pull request snapshot', retryable: false }
|
|
@@ -329,14 +351,22 @@ async function summaryContext(taskRef: string, options: SummaryOptions = {}): Pr
|
|
|
329
351
|
};
|
|
330
352
|
const taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
331
353
|
const frontmatter = parseTypedTaskFrontmatter(taskContent);
|
|
332
|
-
const fact = readPrDeliveryFact(frontmatter, options.runtimeVersion);
|
|
333
|
-
const prNumber = fact.status === 'valid' && fact.fact.state === 'bound' ? resourceIdentityNumber(fact.fact.identity.resource) : null;
|
|
334
354
|
const reportPath = taskReportPath(resolved.taskDir);
|
|
355
|
+
const fact = readPrDeliveryFact(frontmatter, options.runtimeVersion);
|
|
356
|
+
if (fact.status === 'invalid') return {
|
|
357
|
+
...platformResult('failed', { error: { code: fact.error.code, message: fact.error.message, retryable: false } }),
|
|
358
|
+
task: { id: resolved.taskId, prNumber: null },
|
|
359
|
+
pullRequest: null,
|
|
360
|
+
changeReport: { status: 'invalid', path: reportPath, taskIntentSha256: null, reason: fact.error.message },
|
|
361
|
+
artifacts: []
|
|
362
|
+
};
|
|
363
|
+
const prIdentity = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
364
|
+
const prNumber = resourceIdentityNumber(prIdentity);
|
|
335
365
|
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
336
366
|
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
337
367
|
let pullRequest: PlatformChangeRequestSnapshot | null = null;
|
|
338
|
-
if (loaded.ok &&
|
|
339
|
-
const inspected = await inspectBoundPullRequest(context, resolved.repoRoot,
|
|
368
|
+
if (loaded.ok && prIdentity && context.platform.repository && ['no-op', 'degraded'].includes(context.status)) {
|
|
369
|
+
const inspected = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
|
|
340
370
|
if (inspected.ok) pullRequest = inspected.value;
|
|
341
371
|
}
|
|
342
372
|
const report = summaryReportStatus(reportPath, pullRequest, taskContent, resolved.repoRoot);
|
|
@@ -354,6 +384,24 @@ async function summaryContext(taskRef: string, options: SummaryOptions = {}): Pr
|
|
|
354
384
|
};
|
|
355
385
|
}
|
|
356
386
|
|
|
387
|
+
async function summaryCommentState(taskRef: string, options: SummaryOptions = {}): Promise<SummaryCommentStateResult> {
|
|
388
|
+
const context = await summaryContext(taskRef, options);
|
|
389
|
+
const prIdentity = context.pullRequest?.identity;
|
|
390
|
+
if (!prIdentity) return { ...context, comment: null };
|
|
391
|
+
const loaded = await resolvePlatformProviderContext({ cwd: options.cwd, client: options.client });
|
|
392
|
+
if (!loaded.ok || !loaded.value.provider.comments?.list) return { ...context, comment: null };
|
|
393
|
+
const listed = await loaded.value.provider.comments.list({
|
|
394
|
+
context: providerOperationContext(loaded.value),
|
|
395
|
+
parent: prIdentity
|
|
396
|
+
});
|
|
397
|
+
if (!listed.ok) return { ...context, comment: null };
|
|
398
|
+
const matches = listed.value.filter((comment) => comment.body.includes(summaryMarker(context.task.id!)));
|
|
399
|
+
return {
|
|
400
|
+
...context,
|
|
401
|
+
comment: matches.length === 1 ? { id: /^\d+$/u.test(matches[0]!.id) ? Number(matches[0]!.id) : matches[0]!.id, body: matches[0]!.body } : null
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
357
405
|
type ReportWriteOptions = {
|
|
358
406
|
agent: string;
|
|
359
407
|
mechanicalFile: string;
|
|
@@ -366,24 +414,38 @@ type ReportWriteOptions = {
|
|
|
366
414
|
async function reportWrite(taskRef: string, options: ReportWriteOptions): Promise<ReportWriteResult> {
|
|
367
415
|
const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
|
|
368
416
|
if (!resolved.ok) return { ...platformResult('failed', { error: { code: resolved.code, message: resolved.message, retryable: false } }), report: null };
|
|
417
|
+
let preflight: ReturnType<typeof readBoundTaskSnapshot>;
|
|
418
|
+
try {
|
|
419
|
+
preflight = readBoundTaskSnapshot(resolved.taskMdPath);
|
|
420
|
+
} catch (error) {
|
|
421
|
+
return {
|
|
422
|
+
...platformResult('failed', { error: { code: 'PR_CHANGE_REPORT_FAILED', message: error instanceof Error ? error.message : String(error), retryable: false } }),
|
|
423
|
+
report: null
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (preflight.factError) return {
|
|
427
|
+
...platformResult('failed', { error: { code: preflight.factError.code, message: preflight.factError.message, retryable: false } }),
|
|
428
|
+
report: null
|
|
429
|
+
};
|
|
369
430
|
const loaded = await resolvePlatformProviderContext({ cwd: resolved.repoRoot, client: options.client });
|
|
370
431
|
const context = loaded.ok ? loaded.value.context : loaded.context;
|
|
371
432
|
if (!loaded.ok || !context.platform.repository || !['no-op', 'degraded'].includes(context.status)) return { ...context, report: null };
|
|
372
433
|
let knownPrNumber: number | null = null;
|
|
373
434
|
try {
|
|
374
435
|
return await withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, async () => {
|
|
375
|
-
const { taskContent, boundFact } = readBoundTaskSnapshot(resolved.taskMdPath);
|
|
376
|
-
if (
|
|
377
|
-
...basePlatformResult('failed', context, resolved.taskId, null, { code:
|
|
436
|
+
const { taskContent, boundFact, factError } = readBoundTaskSnapshot(resolved.taskMdPath);
|
|
437
|
+
if (factError) return {
|
|
438
|
+
...basePlatformResult('failed', context, resolved.taskId, null, { code: factError.code, message: factError.message, retryable: false }),
|
|
378
439
|
report: null
|
|
379
440
|
};
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NUMBER_INVALID', message: 'Bound pull request has no numeric identifier', retryable: false }),
|
|
441
|
+
if (!boundFact) return {
|
|
442
|
+
...basePlatformResult('failed', context, resolved.taskId, null, { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false }),
|
|
383
443
|
report: null
|
|
384
444
|
};
|
|
445
|
+
const boundPrIdentity = boundFact.identity.resource;
|
|
446
|
+
const boundPrNumber = resourceIdentityNumber(boundPrIdentity);
|
|
385
447
|
knownPrNumber = boundPrNumber;
|
|
386
|
-
const initial = await inspectBoundPullRequest(context, resolved.repoRoot,
|
|
448
|
+
const initial = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrIdentity, loaded.value);
|
|
387
449
|
if (!initial.ok) return { ...basePlatformResult(initial.status, context, resolved.taskId, boundPrNumber, initial.error), report: null };
|
|
388
450
|
const digest = taskIntentDigest(taskContent);
|
|
389
451
|
if (!digest.ok) return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, platformError(digest.error)), report: null };
|
|
@@ -407,7 +469,7 @@ async function reportWrite(taskRef: string, options: ReportWriteOptions): Promis
|
|
|
407
469
|
};
|
|
408
470
|
const built = buildPrChangeReport(identityFromSnapshot(initial.value), digest.value.sha256, mechanical.value, candidate.value);
|
|
409
471
|
if (!built.ok) return { ...basePlatformResult('failed', context, resolved.taskId, boundPrNumber, platformError(built.error)), report: null };
|
|
410
|
-
const final = await inspectBoundPullRequest(context, resolved.repoRoot,
|
|
472
|
+
const final = await inspectBoundPullRequest(context, resolved.repoRoot, boundPrIdentity, loaded.value);
|
|
411
473
|
if (!final.ok) return { ...basePlatformResult(final.status, context, resolved.taskId, boundPrNumber, final.error), report: null };
|
|
412
474
|
if (!sameIdentity(initial.value, final.value)) return {
|
|
413
475
|
...basePlatformResult('blocked', context, resolved.taskId, boundPrNumber, { code: 'PR_CHANGE_REPORT_HEAD_RACE', message: 'Pull request identity changed while generating the change report', retryable: true }),
|
|
@@ -450,7 +512,7 @@ async function reportWrite(taskRef: string, options: ReportWriteOptions): Promis
|
|
|
450
512
|
|
|
451
513
|
async function syncPullRequestSummary(
|
|
452
514
|
taskRef: string,
|
|
453
|
-
options: { agent: string; body: string; changeReportFile?: string; cwd?: string; client?: PlatformClient; dryRun?: boolean; strict?: boolean; primaryResult: PullRequestPrimaryResult; runtimeVersion?: string }
|
|
515
|
+
options: { agent: string; body: string; changeReportFile?: string; cwd?: string; client?: PlatformClient; dryRun?: boolean; strict?: boolean; primaryResult: PullRequestPrimaryResult; runtimeVersion?: string; manualValidation?: ManualValidationSummaryOptions; lockAlreadyHeld?: boolean }
|
|
454
516
|
): Promise<PullRequestSummaryResult> {
|
|
455
517
|
const warningResult = warningResultForPrimary(options.primaryResult);
|
|
456
518
|
let knownPrNumber: number | null = null;
|
|
@@ -503,22 +565,44 @@ async function syncPullRequestSummary(
|
|
|
503
565
|
return preserveFailure(context);
|
|
504
566
|
}
|
|
505
567
|
try {
|
|
506
|
-
|
|
507
|
-
const { taskContent, boundFact } = readBoundTaskSnapshot(resolved.taskMdPath);
|
|
568
|
+
const execute = async (): Promise<PullRequestSummaryResult> => {
|
|
569
|
+
const { taskContent, boundFact, factError } = readBoundTaskSnapshot(resolved.taskMdPath);
|
|
570
|
+
if (factError) return {
|
|
571
|
+
...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: factError.code, message: factError.message, retryable: false } }),
|
|
572
|
+
result: null, warnings: []
|
|
573
|
+
};
|
|
508
574
|
if (!boundFact) return {
|
|
509
575
|
...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: 'PR_NOT_LINKED', message: 'Task has no verified bound pull request', retryable: false } }),
|
|
510
576
|
result: null, warnings: []
|
|
511
577
|
};
|
|
512
578
|
const prIdentity = boundFact.identity.resource;
|
|
513
579
|
const boundPrNumber = resourceIdentityNumber(prIdentity);
|
|
514
|
-
if (!boundPrNumber) return {
|
|
515
|
-
...platformResult('failed', { resource: { kind: 'pull-request', number: null }, error: { code: 'PR_NUMBER_INVALID', message: 'Bound pull request has no numeric identifier', retryable: false } }),
|
|
516
|
-
result: null, warnings: []
|
|
517
|
-
};
|
|
518
580
|
knownPrNumber = boundPrNumber;
|
|
519
581
|
const prNumber = boundPrNumber;
|
|
520
|
-
const initial = await inspectBoundPullRequest(context, resolved.repoRoot,
|
|
582
|
+
const initial = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
|
|
521
583
|
if (!initial.ok) return fail(initial.status, context, initial.error);
|
|
584
|
+
const manual = options.manualValidation;
|
|
585
|
+
const hasFinalManualValidation = /###\s+✅\s+(?:Manual Validation Passed|人工验证已通过)/u.test(options.body);
|
|
586
|
+
if (hasFinalManualValidation && (!manual || manual.phase !== 'final')) return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'final manual-validation summary requires the transaction coordinator', retryable: false }, prNumber);
|
|
587
|
+
if (manual && (manual.phase === 'pending' ? hasFinalManualValidation : !hasFinalManualValidation)) return fail('failed', context, { code: 'MANUAL_VALIDATION_SUMMARY_PHASE_INVALID', message: 'manual-validation summary phase does not match the requested writer phase', retryable: false }, prNumber);
|
|
588
|
+
if (manual) {
|
|
589
|
+
if (prNumber === null) return fail('failed', context, { code: 'MANUAL_VALIDATION_PR_NUMBER_REQUIRED', message: 'manual-validation summary requires a numeric pull-request identity', retryable: false }, prNumber);
|
|
590
|
+
if (manual.phase === 'final' && (!manual.transactionId || !manual.receiptDigest || !manual.prHeadSha || manual.prHeadSha !== initial.value.head.sha)) return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'final manual-validation summary requires transaction, receipt, and current head identity', retryable: false }, prNumber);
|
|
591
|
+
if (manual.phase === 'final') {
|
|
592
|
+
if (manual.authority !== 'coordinator') return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_REQUIRED', message: 'final manual-validation summary requires coordinator authority', retryable: false }, prNumber);
|
|
593
|
+
const completion = readManualValidationCompletion(resolved.taskDir, {
|
|
594
|
+
transactionId: manual.transactionId,
|
|
595
|
+
taskId: resolved.taskId,
|
|
596
|
+
prNumber,
|
|
597
|
+
prHeadSha: initial.value.head.sha,
|
|
598
|
+
receiptDigest: manual.receiptDigest
|
|
599
|
+
});
|
|
600
|
+
if (!completion.ok) return fail('failed', context, platformError(completion.error), prNumber);
|
|
601
|
+
const { receipt, transaction } = completion.value;
|
|
602
|
+
if (transaction.phase !== 'final-promotion-in-progress' || !transaction.eventAppended) return fail('failed', context, { code: 'MANUAL_VALIDATION_TRANSACTION_PHASE_INVALID', message: 'final summary requires a receipt-backed promotion intent', retryable: false }, prNumber);
|
|
603
|
+
if (!manualValidationFinalSummaryProjectionMatches(options.body, receipt)) return fail('failed', context, { code: 'MANUAL_VALIDATION_SUMMARY_PROJECTION_INVALID', message: 'final summary body does not match the canonical receipt projection', retryable: false }, prNumber);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
522
606
|
const expectedReportPath = taskReportPath(resolved.taskDir);
|
|
523
607
|
const suppliedReportPath = path.resolve(resolved.repoRoot, options.changeReportFile!);
|
|
524
608
|
if (suppliedReportPath !== expectedReportPath) return fail('failed', context, { code: 'PR_CHANGE_REPORT_PATH_INVALID', message: 'summary-sync must consume the task-bound pr-change-report.json', retryable: false });
|
|
@@ -569,7 +653,7 @@ async function syncPullRequestSummary(
|
|
|
569
653
|
? listed.value.find((comment) => comment.id === reconciliation.commentId)?.body ?? null
|
|
570
654
|
: null;
|
|
571
655
|
const info = { precheckVerdict: report.value.precheck.verdict, nextAction: report.value.precheck.route } as const;
|
|
572
|
-
const beforeWrite = await inspectBoundPullRequest(context, resolved.repoRoot,
|
|
656
|
+
const beforeWrite = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
|
|
573
657
|
if (!beforeWrite.ok) return fail(beforeWrite.status, context, beforeWrite.error, prNumber);
|
|
574
658
|
if (!sameIdentity(initial.value, beforeWrite.value)) return fail('blocked', context, {
|
|
575
659
|
code: 'PR_SUMMARY_HEAD_RACE',
|
|
@@ -599,7 +683,7 @@ async function syncPullRequestSummary(
|
|
|
599
683
|
})
|
|
600
684
|
: unsupportedProviderOperation(loaded.value.provider, 'comments.write');
|
|
601
685
|
if (!written.ok) return fail(providerStatus(written.error) === 'blocked' ? 'blocked' : 'failed', context, providerError(written.error, 'PLATFORM_PROVIDER_OPERATION_FAILED'), prNumber);
|
|
602
|
-
const after = await inspectBoundPullRequest(context, resolved.repoRoot,
|
|
686
|
+
const after = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
|
|
603
687
|
const id = /^\d+$/.test(written.value.remoteId) ? Number(written.value.remoteId) : written.value.remoteId;
|
|
604
688
|
const compensateWrittenSummary = async () => reconciliation.action === 'create'
|
|
605
689
|
? written.value.remoteId && loaded.value.provider.comments?.delete
|
|
@@ -651,7 +735,10 @@ async function syncPullRequestSummary(
|
|
|
651
735
|
operations: [{ name: `summary:${reconciliation.action}`, status: 'applied', reasonCode: null }],
|
|
652
736
|
result: null, warnings: [], ...info
|
|
653
737
|
};
|
|
654
|
-
}
|
|
738
|
+
};
|
|
739
|
+
return await (options.lockAlreadyHeld
|
|
740
|
+
? execute()
|
|
741
|
+
: withTaskExecutionLock(resolved.repoRoot, resolved.taskId, options.agent, execute));
|
|
655
742
|
} catch (error) {
|
|
656
743
|
const lockError = error instanceof TaskExecutionLockError
|
|
657
744
|
? { code: error.code, message: error.message, retryable: error.code === 'ORCHESTRATION_LOCK_BUSY' }
|
|
@@ -664,9 +751,10 @@ export {
|
|
|
664
751
|
buildPullRequestSummary,
|
|
665
752
|
reconcileSummaryComment,
|
|
666
753
|
reportWrite,
|
|
754
|
+
summaryCommentState,
|
|
667
755
|
summaryContext,
|
|
668
756
|
summaryMarker,
|
|
669
757
|
syncPullRequestSummary,
|
|
670
758
|
warningResultForPrimary
|
|
671
759
|
};
|
|
672
|
-
export type { PullRequestSummaryResult, ReportWriteOptions, ReportWriteResult, SummaryContextResult };
|
|
760
|
+
export type { ManualValidationSummaryOptions, PullRequestSummaryResult, ReportWriteOptions, ReportWriteResult, SummaryCommentStateResult, SummaryContextResult };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizePullRequest, type RemotePullRequest } from './github-data.ts';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { execFileSync } from 'node:child_process';
|
|
@@ -6,7 +7,7 @@ import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
|
|
|
6
7
|
import { resolveTaskRef } from '../task/resolve-ref.ts';
|
|
7
8
|
import { extractSection } from '../task/sections.ts';
|
|
8
9
|
import { captureTaskWriteMetadata, writeTask } from '../task/write.ts';
|
|
9
|
-
import type { PlatformChangeRequestSnapshot } from './
|
|
10
|
+
import type { PlatformChangeRequestSnapshot } from './snapshots.ts';
|
|
10
11
|
import { resolvePlatformProviderContext } from './context.ts';
|
|
11
12
|
import type { PlatformClient } from './context.ts';
|
|
12
13
|
import { inspectPlatformIssue } from './issues.ts';
|
|
@@ -61,7 +62,6 @@ type PullRequestResult = PlatformResult & {
|
|
|
61
62
|
effect: 'no-op' | 'applied' | 'unknown';
|
|
62
63
|
}>;
|
|
63
64
|
};
|
|
64
|
-
type GitHubClient = PlatformClient;
|
|
65
65
|
type InspectionOptions = { cwd?: string; client?: PlatformClient; runtimeVersion?: string };
|
|
66
66
|
type SharedOptions = { cwd?: string; client?: PlatformClient; runtimeVersion?: string };
|
|
67
67
|
type CreateOptions = SharedOptions & {
|
|
@@ -93,54 +93,6 @@ type ExternalPullRequestResult = PullRequestResult & {
|
|
|
93
93
|
|
|
94
94
|
type CreatePhase = 'before-post' | 'post-dispatched' | 'post-accepted';
|
|
95
95
|
|
|
96
|
-
type RemotePullRequest = {
|
|
97
|
-
number?: number;
|
|
98
|
-
node_id?: string;
|
|
99
|
-
html_url?: string;
|
|
100
|
-
state?: string;
|
|
101
|
-
title?: string;
|
|
102
|
-
body?: string | null;
|
|
103
|
-
draft?: boolean;
|
|
104
|
-
pull_request?: unknown;
|
|
105
|
-
head?: { ref?: string; sha?: string; repo?: { full_name?: string } | null };
|
|
106
|
-
base?: { ref?: string; sha?: string; repo?: { full_name?: string } | null };
|
|
107
|
-
merged_at?: string | null;
|
|
108
|
-
merge_commit_sha?: string | null;
|
|
109
|
-
labels?: Array<string | { name?: string }>;
|
|
110
|
-
assignees?: Array<{ login?: string }>;
|
|
111
|
-
milestone?: { title?: string } | null;
|
|
112
|
-
mergeable?: boolean | null;
|
|
113
|
-
mergeable_state?: string | null;
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
type ClosingPullRequestNode = {
|
|
117
|
-
number?: number;
|
|
118
|
-
id?: string;
|
|
119
|
-
url?: string;
|
|
120
|
-
state?: string;
|
|
121
|
-
title?: string;
|
|
122
|
-
body?: string | null;
|
|
123
|
-
isDraft?: boolean;
|
|
124
|
-
headRefName?: string;
|
|
125
|
-
headRefOid?: string;
|
|
126
|
-
headRepository?: { nameWithOwner?: string } | null;
|
|
127
|
-
baseRefName?: string;
|
|
128
|
-
baseRefOid?: string;
|
|
129
|
-
baseRepository?: { nameWithOwner?: string } | null;
|
|
130
|
-
mergedAt?: string | null;
|
|
131
|
-
mergeCommit?: { oid?: string } | null;
|
|
132
|
-
labels?: { nodes?: Array<{ name?: string }> };
|
|
133
|
-
assignees?: { nodes?: Array<{ login?: string }> };
|
|
134
|
-
milestone?: { title?: string } | null;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
type ClosingPullRequestPage = {
|
|
138
|
-
data?: { repository?: { issue?: { closedByPullRequestsReferences?: {
|
|
139
|
-
nodes?: ClosingPullRequestNode[];
|
|
140
|
-
pageInfo?: { hasNextPage?: boolean; endCursor?: string | null };
|
|
141
|
-
} } } };
|
|
142
|
-
};
|
|
143
|
-
|
|
144
96
|
function warningResultForPrimary(primaryResult: PullRequestPrimaryResult): NonNullable<PullRequestResult['result']> {
|
|
145
97
|
if (primaryResult === 'pr_created') return 'pr_created_with_warnings';
|
|
146
98
|
if (primaryResult === 'pr_reused') return 'pr_reused_with_warnings';
|
|
@@ -189,57 +141,6 @@ const PRECONDITION_NOT_CREATED: CreationOutcome = {
|
|
|
189
141
|
kind: 'not-created', reason: 'precondition-failed', createdByCurrentOperation: false
|
|
190
142
|
};
|
|
191
143
|
|
|
192
|
-
function normalizePullRequest(remote: RemotePullRequest, repository: string): PullRequestSnapshot | null {
|
|
193
|
-
const number = Number(remote.number);
|
|
194
|
-
const headRepository = remote.head?.repo?.full_name;
|
|
195
|
-
const baseRepository = remote.base?.repo?.full_name;
|
|
196
|
-
if (!Number.isInteger(number) || number <= 0 || !remote.node_id || !remote.html_url ||
|
|
197
|
-
!remote.head?.ref || !remote.head.sha || !headRepository || !remote.base?.ref || !baseRepository) return null;
|
|
198
|
-
const mergeabilityDetail = remote.mergeable_state?.trim().toLowerCase() || null;
|
|
199
|
-
const mergeability = remote.mergeable === false
|
|
200
|
-
? { state: 'conflicting' as const, detail: mergeabilityDetail }
|
|
201
|
-
: remote.mergeable === true && mergeabilityDetail !== 'dirty'
|
|
202
|
-
? { state: 'mergeable' as const, detail: mergeabilityDetail }
|
|
203
|
-
: { state: 'unknown' as const, detail: mergeabilityDetail };
|
|
204
|
-
return {
|
|
205
|
-
repository,
|
|
206
|
-
number,
|
|
207
|
-
nodeId: remote.node_id,
|
|
208
|
-
url: remote.html_url,
|
|
209
|
-
state: remote.state === 'closed' ? 'closed' : 'open',
|
|
210
|
-
title: remote.title || '',
|
|
211
|
-
body: remote.body || '',
|
|
212
|
-
draft: Boolean(remote.draft),
|
|
213
|
-
head: { repository: headRepository, ref: remote.head.ref, sha: remote.head.sha },
|
|
214
|
-
base: { repository: baseRepository, ref: remote.base.ref, sha: remote.base.sha || '' },
|
|
215
|
-
mergedAt: remote.merged_at || null,
|
|
216
|
-
mergeCommitSha: remote.merge_commit_sha || null,
|
|
217
|
-
labels: (remote.labels || []).map((label) => typeof label === 'string' ? label : label.name || '').filter(Boolean).sort(),
|
|
218
|
-
assignees: (remote.assignees || []).map((assignee) => assignee.login || '').filter(Boolean).sort(),
|
|
219
|
-
milestone: remote.milestone?.title || null,
|
|
220
|
-
mergeability
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function normalizeClosingPullRequest(node: ClosingPullRequestNode, repository: string): PullRequestSnapshot | null {
|
|
225
|
-
return normalizePullRequest({
|
|
226
|
-
number: node.number,
|
|
227
|
-
node_id: node.id,
|
|
228
|
-
html_url: node.url,
|
|
229
|
-
state: node.state === 'OPEN' ? 'open' : 'closed',
|
|
230
|
-
title: node.title,
|
|
231
|
-
body: node.body,
|
|
232
|
-
draft: node.isDraft,
|
|
233
|
-
head: { ref: node.headRefName, sha: node.headRefOid, repo: node.headRepository ? { full_name: node.headRepository.nameWithOwner } : null },
|
|
234
|
-
base: { ref: node.baseRefName, sha: node.baseRefOid, repo: node.baseRepository ? { full_name: node.baseRepository.nameWithOwner } : null },
|
|
235
|
-
merged_at: node.mergedAt,
|
|
236
|
-
merge_commit_sha: node.mergeCommit?.oid,
|
|
237
|
-
labels: node.labels?.nodes,
|
|
238
|
-
assignees: node.assignees?.nodes,
|
|
239
|
-
milestone: node.milestone
|
|
240
|
-
}, repository);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
144
|
function repositoryKey(repository: string): string {
|
|
244
145
|
return repository.trim().toLowerCase();
|
|
245
146
|
}
|
|
@@ -329,51 +230,6 @@ function selectExternalPullRequest(
|
|
|
329
230
|
return { status: 'selected', source: 'unique', selected: eligible[0]!, candidates, eligible };
|
|
330
231
|
}
|
|
331
232
|
|
|
332
|
-
const CLOSING_PULL_REQUESTS_QUERY = `query($owner:String!,$name:String!,$issue:Int!,$cursor:String){repository(owner:$owner,name:$name){issue(number:$issue){closedByPullRequestsReferences(first:100,after:$cursor){nodes{number id url state title body isDraft headRefName headRefOid headRepository{nameWithOwner} baseRefName baseRefOid baseRepository{nameWithOwner} mergedAt mergeCommit{oid} labels(first:100){nodes{name}} assignees(first:100){nodes{login}} milestone{title}} pageInfo{hasNextPage endCursor}}}}}`;
|
|
333
|
-
|
|
334
|
-
function inspectGitHubIssueClosingChangeRequests(
|
|
335
|
-
client: GitHubClient,
|
|
336
|
-
repository: string,
|
|
337
|
-
issueNumber: number,
|
|
338
|
-
cwd: string
|
|
339
|
-
) {
|
|
340
|
-
const [owner, name] = repository.split('/');
|
|
341
|
-
if (!owner || !name || !Number.isInteger(issueNumber) || issueNumber <= 0) return {
|
|
342
|
-
ok: false as const,
|
|
343
|
-
error: { code: 'PR_IDENTITY_INVALID', message: 'Repository or Issue identity is invalid', retryable: false }
|
|
344
|
-
};
|
|
345
|
-
const candidates: PullRequestSnapshot[] = [];
|
|
346
|
-
let cursor: string | null = null;
|
|
347
|
-
for (;;) {
|
|
348
|
-
const args = [
|
|
349
|
-
'api', 'graphql', '-f', `query=${CLOSING_PULL_REQUESTS_QUERY}`,
|
|
350
|
-
'-F', `owner=${owner}`, '-F', `name=${name}`, '-F', `issue=${issueNumber}`
|
|
351
|
-
];
|
|
352
|
-
if (cursor) args.push('-F', `cursor=${cursor}`);
|
|
353
|
-
const response = client.json<ClosingPullRequestPage>(args, { cwd });
|
|
354
|
-
if (!response.ok) return response;
|
|
355
|
-
const connection = response.value?.data?.repository?.issue?.closedByPullRequestsReferences;
|
|
356
|
-
if (!connection || !Array.isArray(connection.nodes) || !connection.pageInfo) return {
|
|
357
|
-
ok: false as const,
|
|
358
|
-
error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request response is incomplete', retryable: false }
|
|
359
|
-
};
|
|
360
|
-
for (const node of connection.nodes) {
|
|
361
|
-
const normalized = normalizeClosingPullRequest(node, repository);
|
|
362
|
-
if (!normalized) return {
|
|
363
|
-
ok: false as const,
|
|
364
|
-
error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request identity is incomplete', retryable: false }
|
|
365
|
-
};
|
|
366
|
-
candidates.push(normalized);
|
|
367
|
-
}
|
|
368
|
-
if (!connection.pageInfo.hasNextPage) return { ok: true as const, value: candidates };
|
|
369
|
-
if (!connection.pageInfo.endCursor || connection.pageInfo.endCursor === cursor) return {
|
|
370
|
-
ok: false as const,
|
|
371
|
-
error: { code: 'PR_IDENTITY_INVALID', message: 'Closing pull request pagination cursor is invalid', retryable: false }
|
|
372
|
-
};
|
|
373
|
-
cursor = connection.pageInfo.endCursor;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
233
|
function expectedHead(repository: string, head: string): { repository: string; ref: string } {
|
|
378
234
|
const colon = head.indexOf(':');
|
|
379
235
|
if (colon === -1) return { repository, ref: head };
|
|
@@ -520,7 +376,7 @@ async function resolvedContext(taskRef: string, options: InspectionOptions) {
|
|
|
520
376
|
}) };
|
|
521
377
|
const fact = factRead.status === 'valid' ? factRead.fact : null;
|
|
522
378
|
let issueIdentity: ResourceIdentity | null;
|
|
523
|
-
try { issueIdentity = taskIssueIdentity(frontmatter
|
|
379
|
+
try { issueIdentity = taskIssueIdentity(frontmatter); }
|
|
524
380
|
catch (error) { return { ok: false as const, output: result('failed', resolved.taskId, null, null, { error: { ...taskIssueIdentityError(error), retryable: false } }) }; }
|
|
525
381
|
const issueNumber = resourceIdentityNumber(issueIdentity);
|
|
526
382
|
const prIdentity = fact?.state === 'bound' ? fact.identity.resource : null;
|
|
@@ -606,15 +462,6 @@ async function inspectExternalPullRequest(
|
|
|
606
462
|
});
|
|
607
463
|
}
|
|
608
464
|
|
|
609
|
-
function inspectGitHubPullRequest(client: GitHubClient, repository: string, number: number, cwd: string) {
|
|
610
|
-
const fetched = client.json<RemotePullRequest>(['api', `repos/${repository}/pulls/${number}`], { cwd });
|
|
611
|
-
if (!fetched.ok) return fetched;
|
|
612
|
-
const pullRequest = normalizePullRequest(fetched.value, repository);
|
|
613
|
-
return pullRequest
|
|
614
|
-
? { ok: true as const, value: pullRequest }
|
|
615
|
-
: { ok: false as const, error: { code: 'PR_IDENTITY_INVALID', message: 'Remote resource is not a valid pull request', retryable: false } };
|
|
616
|
-
}
|
|
617
|
-
|
|
618
465
|
async function inspectPlatformPullRequest(taskRef: string, options: InspectionOptions = {}): Promise<PullRequestResult> {
|
|
619
466
|
const base = await resolvedContext(taskRef, options);
|
|
620
467
|
if (!base.ok) return base.output;
|
|
@@ -1094,7 +941,7 @@ async function resolveExternalPullRequest(taskRef: string, options: ResolveExter
|
|
|
1094
941
|
if (!base.issueIdentity) return externalResult(result('failed', base.resolved.taskId, null, base.prNumber, {
|
|
1095
942
|
platform: base.context.platform,
|
|
1096
943
|
capabilities: base.context.capabilities,
|
|
1097
|
-
error: { code: 'EXTERNAL_DELIVERY_ISSUE_REQUIRED', message: 'External delivery requires a valid
|
|
944
|
+
error: { code: 'EXTERNAL_DELIVERY_ISSUE_REQUIRED', message: 'External delivery requires a valid platform issue identity', retryable: false }
|
|
1098
945
|
}));
|
|
1099
946
|
const inspected = base.provider.changeRequests?.listClosing
|
|
1100
947
|
? await base.provider.changeRequests.listClosing({
|
|
@@ -1583,7 +1430,7 @@ function skipPlatformPullRequestFactUnlocked(
|
|
|
1583
1430
|
});
|
|
1584
1431
|
}
|
|
1585
1432
|
let issueIdentity: ResourceIdentity | null;
|
|
1586
|
-
try { issueIdentity = taskIssueIdentity(frontmatter
|
|
1433
|
+
try { issueIdentity = taskIssueIdentity(frontmatter); }
|
|
1587
1434
|
catch (error) { return result('failed', resolved.taskId, null, null, { error: { ...taskIssueIdentityError(error), retryable: false } }); }
|
|
1588
1435
|
const issueNumber = resourceIdentityNumber(issueIdentity);
|
|
1589
1436
|
const existing = readPrDeliveryFact(frontmatter, options.runtimeVersion);
|
|
@@ -1656,20 +1503,6 @@ async function skipPlatformPullRequestFact(taskRef: string, options: SkipFactOpt
|
|
|
1656
1503
|
}
|
|
1657
1504
|
}
|
|
1658
1505
|
|
|
1659
|
-
export {
|
|
1660
|
-
bindPlatformPullRequest,
|
|
1661
|
-
createPlatformPullRequest,
|
|
1662
|
-
inspectGitHubPullRequest,
|
|
1663
|
-
inspectGitHubIssueClosingChangeRequests,
|
|
1664
|
-
inspectPlatformPullRequest,
|
|
1665
|
-
inspectPlatformPullRequestByNumber,
|
|
1666
|
-
normalizePullRequest,
|
|
1667
|
-
resolveExternalPullRequest,
|
|
1668
|
-
skipPlatformPullRequestFact,
|
|
1669
|
-
selectExternalPullRequest,
|
|
1670
|
-
selectPullRequest,
|
|
1671
|
-
syncPlatformPullRequest,
|
|
1672
|
-
syncPlatformPullRequestInLabels
|
|
1673
|
-
};
|
|
1506
|
+
export { bindPlatformPullRequest, createPlatformPullRequest, inspectPlatformPullRequest, inspectPlatformPullRequestByNumber, resolveExternalPullRequest, skipPlatformPullRequestFact, selectExternalPullRequest, selectPullRequest, syncPlatformPullRequest, syncPlatformPullRequestInLabels };
|
|
1674
1507
|
export type { BindOptions, CreateOptions, ExternalPullRequestResult, ExternalPullRequestSelection, PullRequestPrimaryResult, PullRequestResult, PullRequestSnapshot, ResolveExternalOptions, SkipFactOptions, SyncOptions };
|
|
1675
1508
|
export { warningResultForPrimary };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ResourceIdentity } from './resource-identity.ts';
|
|
2
|
+
|
|
3
|
+
export type PlatformIssueSnapshot = {
|
|
4
|
+
repository: string;
|
|
5
|
+
number: number;
|
|
6
|
+
identity?: ResourceIdentity;
|
|
7
|
+
databaseId: number | null;
|
|
8
|
+
nodeId: string;
|
|
9
|
+
url: string;
|
|
10
|
+
state: 'open' | 'closed';
|
|
11
|
+
title: string;
|
|
12
|
+
body: string;
|
|
13
|
+
labels: string[];
|
|
14
|
+
assignees: string[];
|
|
15
|
+
milestone: string | null;
|
|
16
|
+
issueType: string | null;
|
|
17
|
+
fields: Record<string, string | number | null>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type PlatformMergeability = {
|
|
21
|
+
state: 'mergeable' | 'conflicting' | 'unknown';
|
|
22
|
+
detail: string | null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type PlatformChangeRequestSnapshot = {
|
|
26
|
+
repository: string;
|
|
27
|
+
number: number;
|
|
28
|
+
nodeId: string;
|
|
29
|
+
url: string;
|
|
30
|
+
state: 'open' | 'closed';
|
|
31
|
+
title: string;
|
|
32
|
+
body: string;
|
|
33
|
+
draft: boolean;
|
|
34
|
+
head: { repository: string; ref: string; sha: string };
|
|
35
|
+
base: { repository: string; ref: string; sha: string };
|
|
36
|
+
mergedAt: string | null;
|
|
37
|
+
mergeCommitSha: string | null;
|
|
38
|
+
labels: string[];
|
|
39
|
+
assignees: string[];
|
|
40
|
+
milestone: string | null;
|
|
41
|
+
mergeability?: PlatformMergeability;
|
|
42
|
+
identity?: ResourceIdentity;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type PlatformCheckSnapshot = {
|
|
46
|
+
name: string;
|
|
47
|
+
bucket: 'pass' | 'fail' | 'pending' | 'cancel';
|
|
48
|
+
workflow?: string | null;
|
|
49
|
+
conclusion?: string | null;
|
|
50
|
+
detailsUrl?: string | null;
|
|
51
|
+
startedAt?: string | null;
|
|
52
|
+
completedAt?: string | null;
|
|
53
|
+
};
|