@fitlab-ai/agent-infra 0.11.6 → 0.11.8
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/dist/bin/internal-cli.js +8 -2
- package/dist/lib/agent-clients/adapters/codex.js +1 -1
- package/dist/lib/agent-clients/config.js +11 -3
- package/dist/lib/defaults.json +6 -3
- package/dist/lib/git/primary-worktree.js +58 -0
- package/dist/lib/internal/cli-route-inventory.js +3 -0
- package/dist/lib/internal/platform-pr-review.js +20 -3
- package/dist/lib/internal/task-operation-registry.js +3 -1
- package/dist/lib/internal/task-platform-recovery.js +56 -0
- package/dist/lib/merge.js +25 -5
- package/dist/lib/platform/completion-backfill.js +2 -1
- package/dist/lib/platform/in-label-sync.js +12 -1
- package/dist/lib/platform/issue-comments.js +58 -4
- package/dist/lib/platform/issues.js +105 -5
- package/dist/lib/platform/operation-coordinator.js +74 -0
- package/dist/lib/platform/pr-review.js +107 -1
- package/dist/lib/platform/pr-summary.js +79 -57
- package/dist/lib/platform/pull-requests.js +125 -7
- package/dist/lib/sandbox/commands/create.js +6 -3
- package/dist/lib/sandbox/commands/ls.js +4 -3
- package/dist/lib/sandbox/commands/rm.js +1 -1
- package/dist/lib/sandbox/config.js +22 -4
- package/dist/lib/sandbox/control/server.js +6 -2
- package/dist/lib/sandbox/git-metadata.js +50 -0
- package/dist/lib/sandbox/index.js +1 -1
- package/dist/lib/sandbox/mounts.js +12 -0
- package/dist/lib/sandbox/recovery.js +11 -0
- package/dist/lib/sandbox/removal.js +58 -20
- package/dist/lib/sandbox/tools.js +29 -7
- package/dist/lib/server/config.js +7 -27
- package/dist/lib/server/daemon.js +4 -3
- package/dist/lib/server/process-control.js +4 -2
- package/dist/lib/server/runner.js +4 -3
- package/dist/lib/task/activity-intent.js +4 -3
- package/dist/lib/task/artifact-lifecycle.js +42 -10
- package/dist/lib/task/artifact-receipts.js +62 -22
- package/dist/lib/task/capabilities.js +22 -4
- package/dist/lib/task/completion-facts.js +56 -0
- package/dist/lib/task/create-service.js +11 -5
- package/dist/lib/task/create.js +60 -79
- package/dist/lib/task/events.js +268 -195
- package/dist/lib/task/finalization-artifacts.js +4 -1
- package/dist/lib/task/finalization-handoff.js +2 -1
- package/dist/lib/task/finalization.js +157 -24
- package/dist/lib/task/invalidation-command.js +4 -2
- package/dist/lib/task/invalidation.js +38 -9
- package/dist/lib/task/lifecycle.js +38 -2
- package/dist/lib/task/local-artifact-finalization.js +17 -3
- package/dist/lib/task/platform-operation-journal.js +152 -0
- package/dist/lib/task/platform-operation-recovery.js +459 -0
- package/dist/lib/task/qualification-audit.js +23 -105
- package/dist/lib/task/review-finalization.js +7 -0
- package/dist/lib/task/review-identity.js +69 -0
- package/dist/lib/task/sections.js +6 -5
- package/dist/lib/task/task-execution-lock.js +6 -3
- package/dist/lib/task/verification-engine.js +14 -63
- package/dist/lib/task/verification.js +35 -38
- package/dist/lib/task/workflow-warning-intents.js +7 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex.ts +1 -1
- package/lib/agent-clients/config.ts +9 -3
- package/lib/defaults.json +6 -3
- package/lib/git/primary-worktree.ts +62 -0
- package/lib/internal/cli-route-inventory.ts +2 -0
- package/lib/internal/platform-pr-review.ts +14 -3
- package/lib/internal/task-operation-registry.ts +3 -1
- package/lib/internal/task-platform-recovery.ts +53 -0
- package/lib/merge.ts +26 -6
- package/lib/platform/completion-backfill.ts +2 -1
- package/lib/platform/in-label-sync.ts +12 -0
- package/lib/platform/issue-comments.ts +59 -3
- package/lib/platform/issues.ts +103 -5
- package/lib/platform/operation-coordinator.ts +96 -0
- package/lib/platform/pr-review.ts +113 -1
- package/lib/platform/pr-summary.ts +22 -1
- package/lib/platform/pull-requests.ts +132 -8
- package/lib/sandbox/commands/create.ts +7 -8
- package/lib/sandbox/commands/ls.ts +4 -3
- package/lib/sandbox/commands/rm.ts +1 -1
- package/lib/sandbox/config.ts +23 -5
- package/lib/sandbox/control/server.ts +5 -2
- package/lib/sandbox/git-metadata.ts +66 -0
- package/lib/sandbox/index.ts +1 -1
- package/lib/sandbox/mounts.ts +14 -0
- package/lib/sandbox/recovery.ts +13 -0
- package/lib/sandbox/removal.ts +57 -19
- package/lib/sandbox/tool-types.ts +1 -1
- package/lib/sandbox/tools.ts +36 -7
- package/lib/server/config.ts +7 -29
- package/lib/server/daemon.ts +5 -3
- package/lib/server/process-control.ts +4 -2
- package/lib/server/runner.ts +7 -4
- package/lib/server/server.schema.json +0 -4
- package/lib/task/activity-intent.ts +4 -3
- package/lib/task/artifact-lifecycle.ts +35 -11
- package/lib/task/artifact-receipts.ts +55 -15
- package/lib/task/capabilities.ts +17 -3
- package/lib/task/completion-facts.ts +66 -0
- package/lib/task/create-service.ts +12 -6
- package/lib/task/create.ts +54 -69
- package/lib/task/events.ts +248 -182
- package/lib/task/finalization-artifacts.ts +4 -1
- package/lib/task/finalization-handoff.ts +2 -1
- package/lib/task/finalization.ts +154 -21
- package/lib/task/invalidation-command.ts +4 -2
- package/lib/task/invalidation.ts +35 -10
- package/lib/task/lifecycle.ts +35 -3
- package/lib/task/local-artifact-finalization.ts +13 -3
- package/lib/task/platform-operation-journal.ts +230 -0
- package/lib/task/platform-operation-recovery.ts +435 -0
- package/lib/task/qualification-audit.ts +21 -114
- package/lib/task/review-finalization.ts +7 -0
- package/lib/task/review-identity.ts +85 -0
- package/lib/task/sections.ts +6 -6
- package/lib/task/task-execution-lock.ts +7 -3
- package/lib/task/verification-engine.ts +13 -86
- package/lib/task/verification.ts +34 -38
- package/lib/task/workflow-warning-intents.ts +9 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +19 -17
- package/templates/.agents/README.zh-CN.md +18 -16
- package/templates/.agents/rules/README.en.md +2 -1
- package/templates/.agents/rules/README.zh-CN.md +2 -1
- package/templates/.agents/rules/decision-qualification.en.md +4 -3
- package/templates/.agents/rules/decision-qualification.zh-CN.md +4 -3
- package/templates/.agents/rules/task-management.en.md +12 -0
- package/templates/.agents/rules/task-management.zh-CN.md +12 -0
- package/templates/.agents/rules/validation-output.en.md +19 -0
- package/templates/.agents/rules/validation-output.zh-CN.md +19 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -0
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -2
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +5 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/reference/report-template.en.md +4 -8
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +4 -8
- package/templates/.agents/skills/commit/SKILL.en.md +1 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -0
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-task/SKILL.en.md +2 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -0
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -0
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +2 -0
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +3 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
- 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/review-analysis/SKILL.en.md +3 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +4 -8
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +4 -8
- package/templates/.agents/skills/review-code/SKILL.en.md +13 -12
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -12
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -3
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -3
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +23 -46
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +27 -48
- package/templates/.agents/skills/review-code/reference/report-template.en.md +8 -11
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +8 -11
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +2 -2
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.en.md +3 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +5 -9
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +5 -9
- package/templates/.agents/skills/review-pr/SKILL.en.md +13 -11
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +13 -11
- package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +3 -3
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -0
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +19 -6
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -0
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +1 -1
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
3
4
|
import { execFileSync } from 'node:child_process';
|
|
4
5
|
import { buildDefaultBody } from '../task/commands/issue-body.js';
|
|
5
6
|
import { extractTitle, parseTaskFrontmatter } from '../task/frontmatter.js';
|
|
@@ -10,10 +11,11 @@ import { writeTask } from '../task/write.js';
|
|
|
10
11
|
import { resolvePlatformProviderContext } from './context.js';
|
|
11
12
|
import { DEFAULT_REQUIREMENT_SECTION_ANCHORS, chooseMilestone, desiredIssueType, normalizeOption, planIssueMetadata } from './issue-metadata.js';
|
|
12
13
|
import { platformResult } from './types.js';
|
|
13
|
-
import { labelDelta, planInLabelUpdate, syncLabelDelta, validateInLabelMapping, validateRepositoryLabelPayload } from './in-label-sync.js';
|
|
14
|
+
import { labelDelta, inLabelMappingDigest, planInLabelUpdate, syncLabelDelta, validateInLabelMapping, validateRepositoryLabelPayload } from './in-label-sync.js';
|
|
14
15
|
import { providerError, providerOperationContext, providerStatus, providerResourceIdentity, providerResourceToken, unsupportedProviderOperation } from './provider-bridge.js';
|
|
15
16
|
import { resourceIdentityEquals, resourceIdentityNumber, resourceIdentityString, serializeResourceIdentity } from './resource-identity.js';
|
|
16
17
|
import { taskIssueIdentity, taskIssueIdentityError } from './task-identities.js';
|
|
18
|
+
import { coordinatePlatformWrite } from './operation-coordinator.js';
|
|
17
19
|
function result(status, taskId, issueNumber, overrides = {}) {
|
|
18
20
|
return {
|
|
19
21
|
...platformResult(status),
|
|
@@ -247,7 +249,7 @@ async function createPlatformIssue(taskRef, options) {
|
|
|
247
249
|
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
248
250
|
operations: [{ name: 'issue:create', status: 'planned', reasonCode: null }], error: null
|
|
249
251
|
});
|
|
250
|
-
{
|
|
252
|
+
const execute = async () => {
|
|
251
253
|
const created = base.provider.issues?.create
|
|
252
254
|
? await base.provider.issues.create({
|
|
253
255
|
context: providerOperationContext(base.loadedContext),
|
|
@@ -290,7 +292,7 @@ async function createPlatformIssue(taskRef, options) {
|
|
|
290
292
|
return result('failed', base.resolved.taskId, null, {
|
|
291
293
|
platform: base.context.platform, capabilities: base.context.capabilities,
|
|
292
294
|
resource: { kind: 'issue', number: issueNumber, identity: inspected.value.identity || createdIdentity },
|
|
293
|
-
error: { code: 'ISSUE_CREATED_BIND_FAILED', message: written.error.message, retryable:
|
|
295
|
+
error: { code: 'ISSUE_CREATED_BIND_FAILED', message: written.error.message, retryable: true }
|
|
294
296
|
});
|
|
295
297
|
return result('applied', base.resolved.taskId, issueNumber, {
|
|
296
298
|
changed: true,
|
|
@@ -301,7 +303,26 @@ async function createPlatformIssue(taskRef, options) {
|
|
|
301
303
|
issue: normalizeProviderIssue(inspected.value, repository, issueNumber),
|
|
302
304
|
error: null
|
|
303
305
|
});
|
|
304
|
-
}
|
|
306
|
+
};
|
|
307
|
+
const issueCreate = { title: payload.title, bodyDigest: createHash('sha256').update(payload.body).digest('hex') };
|
|
308
|
+
const operation = {
|
|
309
|
+
kind: 'issue-create',
|
|
310
|
+
target: `task:${base.resolved.taskId}`,
|
|
311
|
+
expectedDigest: createHash('sha256').update(JSON.stringify({
|
|
312
|
+
taskId: base.resolved.taskId,
|
|
313
|
+
title: payload.title,
|
|
314
|
+
body: createHash('sha256').update(payload.body).digest('hex'),
|
|
315
|
+
labels: payload.labels || [], assignees: payload.assignees,
|
|
316
|
+
milestone: typeof payload.milestone === 'string' ? payload.milestone : null
|
|
317
|
+
})).digest('hex')
|
|
318
|
+
};
|
|
319
|
+
return coordinatePlatformWrite({
|
|
320
|
+
operation: { taskRef: base.resolved.taskId, cwd: base.resolved.repoRoot, ...operation, issueCreate, dependency: 'required' },
|
|
321
|
+
agent: options.agent,
|
|
322
|
+
execute,
|
|
323
|
+
block: (error) => result('blocked', base.resolved.taskId, null, { platform: base.context.platform, capabilities: base.context.capabilities, error }),
|
|
324
|
+
persistenceFailure: (error) => result('failed', base.resolved.taskId, null, { error })
|
|
325
|
+
});
|
|
305
326
|
}
|
|
306
327
|
async function bindPlatformIssue(taskRef, options) {
|
|
307
328
|
const base = await resolvedContext(taskRef, options);
|
|
@@ -435,7 +456,7 @@ function desiredFieldValues(frontmatter, fields) {
|
|
|
435
456
|
}
|
|
436
457
|
return desired;
|
|
437
458
|
}
|
|
438
|
-
async function
|
|
459
|
+
async function syncPlatformIssueImpl(taskRef, options) {
|
|
439
460
|
const base = await resolvedContext(taskRef, options);
|
|
440
461
|
if (!base.ok)
|
|
441
462
|
return base.output;
|
|
@@ -582,5 +603,84 @@ async function syncPlatformIssue(taskRef, options) {
|
|
|
582
603
|
operations: plan.operations.map((operation) => operation.status === 'planned' ? { ...operation, status: 'applied' } : operation), error: null
|
|
583
604
|
});
|
|
584
605
|
}
|
|
606
|
+
async function syncPlatformIssue(taskRef, options) {
|
|
607
|
+
const resolved = resolveTaskRef(taskRef, options.cwd ? { repoRoot: options.cwd } : {});
|
|
608
|
+
if (!resolved.ok || options.dryRun)
|
|
609
|
+
return syncPlatformIssueImpl(taskRef, options);
|
|
610
|
+
let frontmatter;
|
|
611
|
+
let taskContent;
|
|
612
|
+
try {
|
|
613
|
+
taskContent = fs.readFileSync(resolved.taskMdPath, 'utf8');
|
|
614
|
+
frontmatter = parseTaskFrontmatter(taskContent);
|
|
615
|
+
}
|
|
616
|
+
catch {
|
|
617
|
+
return syncPlatformIssueImpl(taskRef, options);
|
|
618
|
+
}
|
|
619
|
+
const identity = taskIssueIdentity(frontmatter);
|
|
620
|
+
if (!identity || ![
|
|
621
|
+
options.requirements, options.issueType, options.fields, options.status !== undefined,
|
|
622
|
+
options.assignees !== undefined, options.milestone !== undefined,
|
|
623
|
+
options.inLabels !== undefined, options.state !== undefined
|
|
624
|
+
].some(Boolean))
|
|
625
|
+
return syncPlatformIssueImpl(taskRef, options);
|
|
626
|
+
let fromDiffFiles;
|
|
627
|
+
let mappingDigest;
|
|
628
|
+
if (options.inLabels === 'from-diff') {
|
|
629
|
+
const taskBase = typeof frontmatter.delivery_base_ref === 'string' ? frontmatter.delivery_base_ref.trim() : '';
|
|
630
|
+
if (taskBase && (!options.base || options.base === taskBase)) {
|
|
631
|
+
try {
|
|
632
|
+
fromDiffFiles = execFileSync('git', ['diff', `${taskBase}...HEAD`, '--name-only'], {
|
|
633
|
+
cwd: resolved.repoRoot, encoding: 'utf8'
|
|
634
|
+
}).trim().split(/\r?\n/).filter(Boolean).sort();
|
|
635
|
+
const config = JSON.parse(fs.readFileSync(path.join(resolved.repoRoot, '.agents', '.airc.json'), 'utf8'));
|
|
636
|
+
const mapping = inLabelMappingDigest(config.labels?.in);
|
|
637
|
+
if (!mapping.ok)
|
|
638
|
+
return result('failed', resolved.taskId, resourceIdentityNumber(identity), {
|
|
639
|
+
error: { code: mapping.error.code, message: mapping.error.message, retryable: false }
|
|
640
|
+
});
|
|
641
|
+
mappingDigest = mapping.digest;
|
|
642
|
+
}
|
|
643
|
+
catch {
|
|
644
|
+
return result('failed', resolved.taskId, resourceIdentityNumber(identity), {
|
|
645
|
+
error: { code: 'IN_LABEL_SYNC_EVIDENCE_UNAVAILABLE', message: 'Unable to derive the current changed-file set for Issue metadata recovery', retryable: false }
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const issueMetadata = {
|
|
651
|
+
requirements: options.requirements === true,
|
|
652
|
+
issueType: options.issueType === true,
|
|
653
|
+
fields: options.fields === true,
|
|
654
|
+
...(options.status !== undefined ? { status: options.status } : {}),
|
|
655
|
+
...(options.assignees !== undefined ? { assignees: options.assignees } : {}),
|
|
656
|
+
...(options.milestone !== undefined ? { milestone: options.milestone } : {}),
|
|
657
|
+
...(options.inLabels ? { inLabels: options.inLabels } : {}),
|
|
658
|
+
...(options.base ? { base: options.base } : {}),
|
|
659
|
+
...(fromDiffFiles ? { fromDiffFiles } : {}),
|
|
660
|
+
...(mappingDigest ? { inLabelMappingDigest: mappingDigest } : {}),
|
|
661
|
+
...(options.state ? { state: options.state } : {}),
|
|
662
|
+
...(options.closeReason ? { closeReason: options.closeReason } : {})
|
|
663
|
+
};
|
|
664
|
+
const operation = {
|
|
665
|
+
kind: 'issue-metadata',
|
|
666
|
+
target: JSON.stringify(identity),
|
|
667
|
+
expectedDigest: createHash('sha256').update(JSON.stringify({
|
|
668
|
+
...issueMetadata,
|
|
669
|
+
task: resolved.taskId,
|
|
670
|
+
taskContent: createHash('sha256').update(taskContent).digest('hex')
|
|
671
|
+
})).digest('hex')
|
|
672
|
+
};
|
|
673
|
+
const dependency = options.dependency ?? (options.state === 'closed' && options.closeReason === 'not_planned' ? 'required' : 'deferred');
|
|
674
|
+
const execute = () => syncPlatformIssueImpl(taskRef, options);
|
|
675
|
+
if (options.skipQueue)
|
|
676
|
+
return execute();
|
|
677
|
+
return coordinatePlatformWrite({
|
|
678
|
+
operation: { taskRef: resolved.taskId, cwd: resolved.repoRoot, ...operation, issueMetadata, dependency },
|
|
679
|
+
agent: options.agent,
|
|
680
|
+
execute,
|
|
681
|
+
block: (error) => result('blocked', resolved.taskId, resourceIdentityNumber(identity), { error }),
|
|
682
|
+
persistenceFailure: (error) => result('failed', resolved.taskId, resourceIdentityNumber(identity), { error })
|
|
683
|
+
});
|
|
684
|
+
}
|
|
585
685
|
export { bindPlatformIssue, createPlatformIssue, inspectPlatformIssue, requirementSectionAnchors, syncPlatformIssue };
|
|
586
686
|
//# sourceMappingURL=issues.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { recoverPlatformOperations } from '../task/platform-operation-recovery.js';
|
|
2
|
+
import { recordPlatformOperation } from '../task/platform-operation-journal.js';
|
|
3
|
+
async function coordinatePlatformWrite(input) {
|
|
4
|
+
let operationId;
|
|
5
|
+
try {
|
|
6
|
+
const queued = recordPlatformOperation({ ...input.operation, state: 'queued' });
|
|
7
|
+
operationId = queued.id;
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
const value = error;
|
|
11
|
+
return input.persistenceFailure({
|
|
12
|
+
code: value.code || 'PLATFORM_OPERATION_JOURNAL_WRITE_FAILED',
|
|
13
|
+
message: value.message || 'Unable to queue platform operation',
|
|
14
|
+
retryable: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const selection = input.operation.kind === 'issue-metadata' && input.operation.issueMetadata?.state === 'closed'
|
|
18
|
+
? 'all' : input.operation.dependency;
|
|
19
|
+
const drained = input.drain
|
|
20
|
+
? await input.drain(input.operation.taskRef, operationId, input.agent, input.operation.cwd)
|
|
21
|
+
: await recoverPlatformOperations(input.operation.taskRef, selection, {
|
|
22
|
+
agent: input.agent,
|
|
23
|
+
client: input.client,
|
|
24
|
+
cwd: input.operation.cwd,
|
|
25
|
+
excludeId: operationId
|
|
26
|
+
});
|
|
27
|
+
if (drained.status !== 'applied' && drained.status !== 'no-op') {
|
|
28
|
+
return input.block(drained.error ?? {
|
|
29
|
+
code: 'PLATFORM_OPERATION_QUEUE_BLOCKED',
|
|
30
|
+
message: 'A previous platform operation is still unresolved; the current write remains queued',
|
|
31
|
+
retryable: true
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
recordPlatformOperation({ ...input.operation, state: 'pending' });
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const value = error;
|
|
39
|
+
return input.persistenceFailure({
|
|
40
|
+
code: value.code || 'PLATFORM_OPERATION_JOURNAL_WRITE_FAILED',
|
|
41
|
+
message: value.message || 'Unable to mark platform operation as started',
|
|
42
|
+
retryable: true
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
let output;
|
|
46
|
+
try {
|
|
47
|
+
output = await input.execute();
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const value = error;
|
|
51
|
+
output = input.block({ code: value.code || 'PLATFORM_WRITE_FAILED', message: value.message || String(error), retryable: true });
|
|
52
|
+
}
|
|
53
|
+
const succeeded = (output.status === 'applied' || output.status === 'no-op')
|
|
54
|
+
&& !output.error && !(output.warnings?.length);
|
|
55
|
+
const state = succeeded ? 'succeeded' : output.status === 'failed' && output.error?.retryable === false ? 'failed' : 'unknown';
|
|
56
|
+
try {
|
|
57
|
+
recordPlatformOperation({
|
|
58
|
+
...input.operation,
|
|
59
|
+
state,
|
|
60
|
+
lastCode: output.error?.code ?? output.warnings?.[0]?.code ?? null
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
const value = error;
|
|
65
|
+
return input.persistenceFailure({
|
|
66
|
+
code: value.code || 'PLATFORM_OPERATION_JOURNAL_WRITE_FAILED',
|
|
67
|
+
message: value.message || 'Unable to persist platform operation outcome',
|
|
68
|
+
retryable: true
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return output;
|
|
72
|
+
}
|
|
73
|
+
export { coordinatePlatformWrite };
|
|
74
|
+
//# sourceMappingURL=operation-coordinator.js.map
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { coordinatePlatformWrite } from './operation-coordinator.js';
|
|
1
5
|
import { resolvePlatformProviderContext } from './context.js';
|
|
2
6
|
import { platformResult } from './types.js';
|
|
3
7
|
import { providerError, providerOperationContext, providerStatus, providerResourceToken, unsupportedProviderOperation } from './provider-bridge.js';
|
|
4
|
-
import { isResourceIdentity, resourceIdentityNumber, reviewMarker as resourceReviewMarker } from './resource-identity.js';
|
|
8
|
+
import { isResourceIdentity, resourceIdentityEquals, resourceIdentityNumber, reviewMarker as resourceReviewMarker } from './resource-identity.js';
|
|
9
|
+
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
5
10
|
const REVIEW_EVENTS = ['COMMENT', 'APPROVE', 'REQUEST_CHANGES'];
|
|
6
11
|
// Marker-safe scope: a task id (`TASK-YYYYMMDD-HHMMSS`) or a bare PR number
|
|
7
12
|
// (`pr{N}`). Any other value (e.g. one containing `\r\n` or `-->`) would break
|
|
@@ -19,6 +24,34 @@ export function reviewedCommitMarker(sha) {
|
|
|
19
24
|
function normalizeBody(body) {
|
|
20
25
|
return String(body || '').replace(/\r\n/g, '\n').replace(/\n+$/, '\n');
|
|
21
26
|
}
|
|
27
|
+
function canonicalReviewBody(body) {
|
|
28
|
+
return normalizeBody(body).replace(/\n$/u, '');
|
|
29
|
+
}
|
|
30
|
+
function reviewArtifactName(round, artifactFile) {
|
|
31
|
+
return path.basename(artifactFile) === artifactFile
|
|
32
|
+
&& (artifactFile === 'pr-review.md' && round === 1 || artifactFile === `pr-review-r${round}.md`);
|
|
33
|
+
}
|
|
34
|
+
export function readReviewBodyFile(taskRef, round, artifactFile, bodyDigest, cwd) {
|
|
35
|
+
if (!/^TASK-\d{8}-\d{6}$/u.test(taskRef) || path.basename(artifactFile) !== artifactFile
|
|
36
|
+
|| !reviewArtifactName(round, artifactFile)) {
|
|
37
|
+
throw Object.assign(new Error('Pull-request review artifact identity is invalid'), { code: 'PLATFORM_OPERATION_PAYLOAD_INVALID' });
|
|
38
|
+
}
|
|
39
|
+
const task = resolveTaskRef(taskRef, cwd ? { repoRoot: cwd } : {});
|
|
40
|
+
if (!task.ok)
|
|
41
|
+
throw Object.assign(new Error(task.message), { code: 'PLATFORM_OPERATION_PAYLOAD_INVALID' });
|
|
42
|
+
const bodyFile = artifactFile.replace(/^pr-review/u, 'pr-review-body');
|
|
43
|
+
const report = fs.readFileSync(path.join(task.taskDir, artifactFile), 'utf8');
|
|
44
|
+
const bodyReferences = [...report.matchAll(/^\*\*(?:正文文件|Body file)\*\*(?::|:)\s*`([^`]+)`[^\r\n]*$/gmu)]
|
|
45
|
+
.map((match) => match[1]);
|
|
46
|
+
if (bodyReferences.length !== 1 || bodyReferences[0] !== bodyFile) {
|
|
47
|
+
throw Object.assign(new Error('Pull-request review artifact does not reference its canonical body file'), { code: 'PLATFORM_OPERATION_PAYLOAD_INVALID' });
|
|
48
|
+
}
|
|
49
|
+
const body = canonicalReviewBody(fs.readFileSync(path.join(task.taskDir, bodyFile), 'utf8'));
|
|
50
|
+
if (!body || createHash('sha256').update(body).digest('hex') !== bodyDigest) {
|
|
51
|
+
throw Object.assign(new Error('Canonical pull-request review body does not match its persisted digest'), { code: 'PLATFORM_OPERATION_PAYLOAD_INVALID' });
|
|
52
|
+
}
|
|
53
|
+
return body;
|
|
54
|
+
}
|
|
22
55
|
function firstLine(body) {
|
|
23
56
|
return normalizeBody(String(body || '')).split('\n', 1)[0] || '';
|
|
24
57
|
}
|
|
@@ -74,6 +107,13 @@ export async function publishPrReview(options) {
|
|
|
74
107
|
if (!hasUsableContext(context)) {
|
|
75
108
|
return platformResult(context.status, { platform: context.platform, capabilities: context.capabilities, error: context.error });
|
|
76
109
|
}
|
|
110
|
+
const providerScopeId = loaded.ok ? loaded.value.snapshot.scope.id : null;
|
|
111
|
+
if (options.expectedProviderScopeId && providerScopeId !== options.expectedProviderScopeId) {
|
|
112
|
+
return platformResult('failed', {
|
|
113
|
+
platform: context.platform, capabilities: context.capabilities,
|
|
114
|
+
error: { code: 'PLATFORM_OPERATION_IDENTITY_MISMATCH', message: 'Current provider scope differs from the persisted operation target', retryable: false }
|
|
115
|
+
});
|
|
116
|
+
}
|
|
77
117
|
const identity = loaded.ok ? (() => { try {
|
|
78
118
|
return providerResourceToken(loaded.value.provider, 'pull-request', String(options.prNumber));
|
|
79
119
|
}
|
|
@@ -87,6 +127,13 @@ export async function publishPrReview(options) {
|
|
|
87
127
|
});
|
|
88
128
|
}
|
|
89
129
|
const identityNumber = resourceIdentityNumber(identity);
|
|
130
|
+
if (options.expectedResource && !resourceIdentityEquals(identity, options.expectedResource)) {
|
|
131
|
+
return platformResult('failed', {
|
|
132
|
+
platform: context.platform, capabilities: context.capabilities,
|
|
133
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
134
|
+
error: { code: 'PLATFORM_OPERATION_IDENTITY_MISMATCH', message: 'Current pull request identity differs from the persisted operation target', retryable: false }
|
|
135
|
+
});
|
|
136
|
+
}
|
|
90
137
|
if (!REVIEW_EVENTS.includes(options.event)) {
|
|
91
138
|
return platformResult('failed', {
|
|
92
139
|
platform: context.platform, capabilities: context.capabilities,
|
|
@@ -101,6 +148,65 @@ export async function publishPrReview(options) {
|
|
|
101
148
|
error: { code: 'REVIEW_IDENTITY_INVALID', message: 'review scope must be a task id or pr{N}; commitSha is required', retryable: false }
|
|
102
149
|
});
|
|
103
150
|
}
|
|
151
|
+
if (/^TASK-\d{8}-\d{6}$/u.test(options.identity.scope) && !options.skipQueue && !options.dryRun) {
|
|
152
|
+
if (!options.agent)
|
|
153
|
+
return platformResult('failed', {
|
|
154
|
+
platform: context.platform, capabilities: context.capabilities,
|
|
155
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
156
|
+
error: { code: 'PR_REVIEW_AGENT_REQUIRED', message: 'task-scoped review publication requires an agent token', retryable: false }
|
|
157
|
+
});
|
|
158
|
+
if (!options.recoveryArtifact || !reviewArtifactName(options.identity.round, options.recoveryArtifact))
|
|
159
|
+
return platformResult('failed', {
|
|
160
|
+
platform: context.platform, capabilities: context.capabilities,
|
|
161
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
162
|
+
error: { code: 'PR_REVIEW_ARTIFACT_REQUIRED', message: 'task-scoped review publication requires its canonical review artifact', retryable: false }
|
|
163
|
+
});
|
|
164
|
+
const body = canonicalReviewBody(options.body);
|
|
165
|
+
const bodyDigest = createHash('sha256').update(body).digest('hex');
|
|
166
|
+
try {
|
|
167
|
+
if (readReviewBodyFile(options.identity.scope, options.identity.round, options.recoveryArtifact, bodyDigest, options.cwd) !== body)
|
|
168
|
+
throw new Error('body mismatch');
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return platformResult('failed', {
|
|
172
|
+
platform: context.platform, capabilities: context.capabilities,
|
|
173
|
+
resource: { kind: 'pull-request', number: identityNumber, identity },
|
|
174
|
+
error: { code: 'PR_REVIEW_ARTIFACT_MISMATCH', message: 'Review body must match the canonical review body file', retryable: false }
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const pullRequestReview = {
|
|
178
|
+
prNumber: String(options.prNumber),
|
|
179
|
+
resource: identity,
|
|
180
|
+
providerScopeId: providerScopeId,
|
|
181
|
+
scope: options.identity.scope,
|
|
182
|
+
round: options.identity.round,
|
|
183
|
+
commitSha: options.identity.commitSha,
|
|
184
|
+
event: options.event,
|
|
185
|
+
artifactFile: options.recoveryArtifact,
|
|
186
|
+
bodyDigest
|
|
187
|
+
};
|
|
188
|
+
return coordinatePlatformWrite({
|
|
189
|
+
operation: {
|
|
190
|
+
taskRef: options.identity.scope,
|
|
191
|
+
cwd: options.cwd || process.cwd(),
|
|
192
|
+
kind: 'pull-request-review',
|
|
193
|
+
target: JSON.stringify(identity),
|
|
194
|
+
expectedDigest: createHash('sha256').update(JSON.stringify(pullRequestReview)).digest('hex'),
|
|
195
|
+
dependency: 'deferred',
|
|
196
|
+
pullRequestReview
|
|
197
|
+
},
|
|
198
|
+
agent: options.agent,
|
|
199
|
+
execute: () => publishPrReview({ ...options, expectedProviderScopeId: providerScopeId, skipQueue: true }),
|
|
200
|
+
block: (error) => platformResult('blocked', {
|
|
201
|
+
platform: context.platform, capabilities: context.capabilities,
|
|
202
|
+
resource: { kind: 'pull-request', number: identityNumber, identity }, error
|
|
203
|
+
}),
|
|
204
|
+
persistenceFailure: (error) => platformResult('failed', {
|
|
205
|
+
platform: context.platform, capabilities: context.capabilities,
|
|
206
|
+
resource: { kind: 'pull-request', number: identityNumber, identity }, error
|
|
207
|
+
})
|
|
208
|
+
});
|
|
209
|
+
}
|
|
104
210
|
const marker = reviewMarker({ ...options.identity, resource: identity });
|
|
105
211
|
const listed = await listPrReviews(options.prNumber, { cwd: options.cwd, client: options.client });
|
|
106
212
|
if (listed.status === 'failed' || listed.status === 'blocked')
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
3
4
|
import { enumerateArtifacts } from '../task/artifacts.js';
|
|
4
5
|
import { parseTypedTaskFrontmatter } from '../task/frontmatter.js';
|
|
5
6
|
import { resolveTaskRef } from '../task/resolve-ref.js';
|
|
@@ -9,6 +10,7 @@ import { resolvePlatformProviderContext } from './context.js';
|
|
|
9
10
|
import { normalizeCommentContent } from './issue-comments.js';
|
|
10
11
|
import { CONTROL_MARKER_PATTERN, sanitizeMarkdownDocument, splitDocumentPlaceholder } from './comment-safety.js';
|
|
11
12
|
import { platformResult } from './types.js';
|
|
13
|
+
import { coordinatePlatformWrite } from './operation-coordinator.js';
|
|
12
14
|
import { providerError, providerOperationContext, providerStatus, providerResourceToken, resourceIdentityNumber, unsupportedProviderOperation } from './provider-bridge.js';
|
|
13
15
|
import { resourceIdentityEquals } from './resource-identity.js';
|
|
14
16
|
import { buildPrChangeReport, CANONICAL_REPORT_PLACEHOLDER, readPrChangeReport, replaceCanonicalReportPlaceholder, runMechanicalChangeReport, taskIntentDigest, validateMechanicalReport, validatePrecheckCandidate, writePrChangeReportAtomic } from './pr-change-report.js';
|
|
@@ -609,74 +611,94 @@ async function syncPullRequestSummary(taskRef, options) {
|
|
|
609
611
|
operations: [{ name: `summary:${reconciliation.action}`, status: 'planned', reasonCode: null }],
|
|
610
612
|
result: null, warnings: [], ...info
|
|
611
613
|
};
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
context: providerOperationContext(loaded.value),
|
|
618
|
-
parent: prIdentity,
|
|
619
|
-
body: desired,
|
|
620
|
-
...(existingComment ? { existingComment } : {}),
|
|
621
|
-
mutation: { idempotencyKey: `pr-summary:${resolved.taskId}` }
|
|
622
|
-
})
|
|
623
|
-
: unsupportedProviderOperation(loaded.value.provider, 'comments.write');
|
|
624
|
-
if (!written.ok)
|
|
625
|
-
return fail(providerStatus(written.error) === 'blocked' ? 'blocked' : 'failed', context, providerError(written.error, 'PLATFORM_PROVIDER_OPERATION_FAILED'), prNumber);
|
|
626
|
-
const after = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
|
|
627
|
-
const id = /^\d+$/.test(written.value.remoteId) ? Number(written.value.remoteId) : written.value.remoteId;
|
|
628
|
-
const compensateWrittenSummary = async () => reconciliation.action === 'create'
|
|
629
|
-
? written.value.remoteId && loaded.value.provider.comments?.delete
|
|
630
|
-
? await loaded.value.provider.comments.delete({
|
|
631
|
-
context: providerOperationContext(loaded.value),
|
|
632
|
-
parent: prIdentity,
|
|
633
|
-
comment: providerResourceToken(loaded.value.provider, 'comment', written.value.remoteId),
|
|
634
|
-
mutation: { idempotencyKey: `pr-summary-delete:${resolved.taskId}:${written.value.remoteId}` }
|
|
635
|
-
})
|
|
636
|
-
: { ok: false, error: { code: 'PR_SUMMARY_RACE_COMPENSATION_FAILED', message: 'Created summary comment has no valid id', retryable: true } }
|
|
637
|
-
: reconciliation.commentId && previousBody !== null && loaded.value.provider.comments?.write
|
|
614
|
+
const publish = async () => {
|
|
615
|
+
const existingComment = reconciliation.commentId === null
|
|
616
|
+
? undefined
|
|
617
|
+
: providerResourceToken(loaded.value.provider, 'comment', String(reconciliation.commentId));
|
|
618
|
+
const written = loaded.value.provider.comments?.write
|
|
638
619
|
? await loaded.value.provider.comments.write({
|
|
639
620
|
context: providerOperationContext(loaded.value),
|
|
640
621
|
parent: prIdentity,
|
|
641
|
-
body:
|
|
642
|
-
existingComment
|
|
643
|
-
mutation: { idempotencyKey: `pr-summary
|
|
622
|
+
body: desired,
|
|
623
|
+
...(existingComment ? { existingComment } : {}),
|
|
624
|
+
mutation: { idempotencyKey: `pr-summary:${resolved.taskId}` }
|
|
644
625
|
})
|
|
645
|
-
:
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
626
|
+
: unsupportedProviderOperation(loaded.value.provider, 'comments.write');
|
|
627
|
+
if (!written.ok)
|
|
628
|
+
return fail(providerStatus(written.error) === 'blocked' ? 'blocked' : 'failed', context, providerError(written.error, 'PLATFORM_PROVIDER_OPERATION_FAILED'), prNumber);
|
|
629
|
+
const after = await inspectBoundPullRequest(context, resolved.repoRoot, prIdentity, loaded.value);
|
|
630
|
+
const id = /^\d+$/.test(written.value.remoteId) ? Number(written.value.remoteId) : written.value.remoteId;
|
|
631
|
+
const compensateWrittenSummary = async () => reconciliation.action === 'create'
|
|
632
|
+
? written.value.remoteId && loaded.value.provider.comments?.delete
|
|
633
|
+
? await loaded.value.provider.comments.delete({
|
|
634
|
+
context: providerOperationContext(loaded.value),
|
|
635
|
+
parent: prIdentity,
|
|
636
|
+
comment: providerResourceToken(loaded.value.provider, 'comment', written.value.remoteId),
|
|
637
|
+
mutation: { idempotencyKey: `pr-summary-delete:${resolved.taskId}:${written.value.remoteId}` }
|
|
638
|
+
})
|
|
639
|
+
: { ok: false, error: { code: 'PR_SUMMARY_RACE_COMPENSATION_FAILED', message: 'Created summary comment has no valid id', retryable: true } }
|
|
640
|
+
: reconciliation.commentId && previousBody !== null && loaded.value.provider.comments?.write
|
|
641
|
+
? await loaded.value.provider.comments.write({
|
|
642
|
+
context: providerOperationContext(loaded.value),
|
|
643
|
+
parent: prIdentity,
|
|
644
|
+
body: previousBody,
|
|
645
|
+
existingComment: providerResourceToken(loaded.value.provider, 'comment', String(reconciliation.commentId)),
|
|
646
|
+
mutation: { idempotencyKey: `pr-summary-restore:${resolved.taskId}` }
|
|
647
|
+
})
|
|
648
|
+
: { ok: false, error: { code: 'PR_SUMMARY_RACE_COMPENSATION_FAILED', message: 'Previous summary body is unavailable for restoration', retryable: true } };
|
|
649
|
+
if (!after.ok) {
|
|
650
|
+
const compensated = await compensateWrittenSummary();
|
|
651
|
+
if (!compensated.ok)
|
|
652
|
+
return fail('blocked', context, {
|
|
653
|
+
code: 'PR_SUMMARY_RACE_COMPENSATION_FAILED',
|
|
654
|
+
message: `Summary post-write verification failed and compensation failed: ${compensated.error.message}`,
|
|
655
|
+
retryable: true
|
|
656
|
+
}, prNumber);
|
|
649
657
|
return fail('blocked', context, {
|
|
650
|
-
code: '
|
|
651
|
-
message: `Summary
|
|
658
|
+
code: 'PR_SUMMARY_POSTWRITE_VERIFY_FAILED',
|
|
659
|
+
message: `Summary was written but pull-request verification failed: ${after.error?.message || 'unknown verification error'}; the previous summary was restored or the new comment was deleted`,
|
|
652
660
|
retryable: true
|
|
653
661
|
}, prNumber);
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
662
|
+
}
|
|
663
|
+
if (!sameIdentity(initial.value, after.value)) {
|
|
664
|
+
const compensated = await compensateWrittenSummary();
|
|
665
|
+
if (!compensated.ok)
|
|
666
|
+
return fail('blocked', context, {
|
|
667
|
+
code: 'PR_SUMMARY_RACE_COMPENSATION_FAILED',
|
|
668
|
+
message: `Pull-request head changed and summary compensation failed: ${compensated.error.message}`,
|
|
669
|
+
retryable: true
|
|
670
|
+
}, prNumber);
|
|
663
671
|
return fail('blocked', context, {
|
|
664
|
-
code: '
|
|
665
|
-
message:
|
|
672
|
+
code: 'PR_SUMMARY_HEAD_RACE',
|
|
673
|
+
message: 'Pull-request head changed while publishing; the previous summary was restored or the new comment was deleted; rerun report and summary sync',
|
|
666
674
|
retryable: true
|
|
667
675
|
}, prNumber);
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
676
|
+
}
|
|
677
|
+
return {
|
|
678
|
+
...basePlatformResult('applied', context, resolved.taskId, prNumber),
|
|
679
|
+
comment: { kind: 'summary', marker: summaryMarker(resolved.taskId), ids: Number.isInteger(id) ? [id] : [], parts: 1 },
|
|
680
|
+
operations: [{ name: `summary:${reconciliation.action}`, status: 'applied', reasonCode: null }],
|
|
681
|
+
result: null, warnings: [], ...info
|
|
682
|
+
};
|
|
683
|
+
};
|
|
684
|
+
const operation = {
|
|
685
|
+
taskRef: resolved.taskId,
|
|
686
|
+
cwd: resolved.repoRoot,
|
|
687
|
+
kind: 'pull-request-summary',
|
|
688
|
+
target: JSON.stringify(prIdentity),
|
|
689
|
+
expectedDigest: createHash('sha256').update(desired).digest('hex'),
|
|
690
|
+
dependency: 'deferred',
|
|
691
|
+
pullRequestSummary: { body: options.body, changeReportFile: options.changeReportFile }
|
|
679
692
|
};
|
|
693
|
+
if (options.skipQueue)
|
|
694
|
+
return publish();
|
|
695
|
+
return coordinatePlatformWrite({
|
|
696
|
+
operation,
|
|
697
|
+
agent: options.agent,
|
|
698
|
+
execute: publish,
|
|
699
|
+
block: (error) => fail('blocked', context, error, prNumber),
|
|
700
|
+
persistenceFailure: (error) => fail('failed', context, error, prNumber)
|
|
701
|
+
});
|
|
680
702
|
};
|
|
681
703
|
return await (options.lockAlreadyHeld
|
|
682
704
|
? execute()
|