@fitlab-ai/agent-infra 0.9.12 → 0.9.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/README.zh-CN.md +11 -11
- package/dist/bin/internal-cli.js +24 -19
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +9 -9
- package/dist/lib/internal/platform-context.js +2 -2
- package/dist/lib/internal/platform-issue.js +8 -9
- package/dist/lib/internal/platform-pr-review.js +10 -10
- package/dist/lib/internal/platform-pr.js +63 -24
- package/dist/lib/internal/platform-release-notes.js +3 -3
- package/dist/lib/internal/pr-review-grade.js +11 -9
- package/dist/lib/internal/release-workflow.js +4 -4
- package/dist/lib/internal/sandbox-control.js +30 -4
- package/dist/lib/internal/task-activity.js +3 -3
- package/dist/lib/internal/task-artifact.js +76 -24
- package/dist/lib/internal/task-create.js +2 -2
- package/dist/lib/internal/task-event.js +6 -3
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-invalidation.js +60 -0
- package/dist/lib/internal/task-ledger.js +9 -6
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-override.js +4 -4
- package/dist/lib/internal/task-review.js +3 -3
- package/dist/lib/internal/task-verify.js +6 -6
- package/dist/lib/internal/task-warning.js +3 -3
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +1 -1
- package/dist/lib/platform/github-provider.js +903 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +94 -41
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +551 -0
- package/dist/lib/platform/pr-checks.js +119 -104
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +520 -50
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +487 -0
- package/dist/lib/platform/provider-contract.js +95 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +504 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +783 -537
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +11 -12
- package/dist/lib/platform/verification-sync.js +112 -238
- package/dist/lib/pr-review/evidence-grading.js +19 -6
- package/dist/lib/run/index.js +63 -23
- package/dist/lib/sandbox/control/client.js +96 -15
- package/dist/lib/sandbox/control/executor.js +3 -3
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +398 -25
- package/dist/lib/sandbox/control/state.js +204 -1
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +33 -5
- package/dist/lib/task/capabilities.js +239 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-operation.js +12 -1
- package/dist/lib/task/control-authority.js +8 -5
- package/dist/lib/task/create-service.js +8 -8
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +1 -1
- package/dist/lib/task/events.js +297 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +58 -36
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/local-artifact-finalization.js +374 -0
- package/dist/lib/task/orchestration.js +49 -93
- package/dist/lib/task/pr-delivery-fact.js +102 -101
- package/dist/lib/task/review-fingerprint.js +24 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-engine.js +43 -16
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/init.ts +1 -1
- package/lib/internal/platform-checks.ts +3 -3
- package/lib/internal/platform-comment.ts +8 -8
- package/lib/internal/platform-context.ts +2 -2
- package/lib/internal/platform-issue.ts +7 -8
- package/lib/internal/platform-pr-review.ts +9 -9
- package/lib/internal/platform-pr.ts +47 -22
- package/lib/internal/platform-release-notes.ts +3 -3
- package/lib/internal/pr-review-grade.ts +10 -8
- package/lib/internal/release-workflow.ts +4 -4
- package/lib/internal/sandbox-control.ts +27 -3
- package/lib/internal/task-activity.ts +3 -3
- package/lib/internal/task-artifact.ts +65 -22
- package/lib/internal/task-create.ts +2 -2
- package/lib/internal/task-event.ts +6 -3
- package/lib/internal/task-finalization.ts +3 -3
- package/lib/internal/task-invalidation.ts +39 -0
- package/lib/internal/task-ledger.ts +11 -8
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-override.ts +6 -6
- package/lib/internal/task-review.ts +3 -3
- package/lib/internal/task-verify.ts +6 -6
- package/lib/internal/task-warning.ts +3 -3
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +2 -2
- package/lib/platform/github-provider.ts +910 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +97 -47
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +645 -0
- package/lib/platform/pr-checks.ts +126 -106
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +560 -53
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +536 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +529 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +826 -543
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +11 -13
- package/lib/platform/verification-sync.ts +112 -292
- package/lib/pr-review/evidence-grading.ts +25 -8
- package/lib/run/index.ts +57 -22
- package/lib/sandbox/control/client.ts +105 -15
- package/lib/sandbox/control/executor.ts +4 -4
- package/lib/sandbox/control/protocol.ts +113 -0
- package/lib/sandbox/control/server.ts +431 -23
- package/lib/sandbox/control/state.ts +251 -1
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +32 -6
- package/lib/task/capabilities.ts +262 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-operation.ts +12 -1
- package/lib/task/control-authority.ts +11 -8
- package/lib/task/create-service.ts +8 -8
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +6 -2
- package/lib/task/events.ts +296 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +70 -46
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/local-artifact-finalization.ts +563 -0
- package/lib/task/orchestration.ts +52 -85
- package/lib/task/pr-delivery-fact.ts +131 -148
- package/lib/task/review-fingerprint.ts +23 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-engine.ts +52 -17
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +1 -1
- package/lib/version.ts +15 -1
- package/package.json +7 -1
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
- package/templates/.agents/rules/local-artifact-repair.en.md +9 -2
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +9 -2
- package/templates/.agents/rules/pr-sync.en.md +10 -4
- package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
- package/templates/.agents/rules/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/skills/analyze-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/code-task/SKILL.en.md +27 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +29 -10
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +21 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +10 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.en.md +4 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +4 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +4 -2
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +18 -1
- package/templates/.agents/templates/task.zh-CN.md +18 -1
- package/templates/.github/workflows/pr-label.yml +7 -26
|
@@ -3,14 +3,16 @@ import path from "node:path";
|
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import { resolvePlatformContext } from "./context.ts";
|
|
6
|
+
import { resolvePlatformProviderContext } from "./context.ts";
|
|
8
7
|
import { hasCheckedRequirement, resolveRequirementSection } from "./issue-metadata.ts";
|
|
9
|
-
import {
|
|
10
|
-
import { listRemoteComments } from "./issue-comments.ts";
|
|
8
|
+
import { requirementSectionAnchors } from "./issues.ts";
|
|
11
9
|
import { taskTypeLabel } from "./metadata-labels.ts";
|
|
10
|
+
import { planInLabelUpdate, validateInLabelMapping, validateRepositoryLabelPayload } from "./in-label-sync.ts";
|
|
12
11
|
import { inspectGitHubPullRequest } from "./pull-requests.ts";
|
|
12
|
+
import { createGitHubClient } from "./github-client.ts";
|
|
13
13
|
import { readPrDeliveryFact } from "../task/pr-delivery-fact.ts";
|
|
14
|
+
import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.ts";
|
|
15
|
+
import { taskIssueIdentity } from "./task-identities.ts";
|
|
14
16
|
|
|
15
17
|
const CHECK_TYPE = "platform-sync";
|
|
16
18
|
const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
|
|
@@ -29,7 +31,6 @@ const OPTION_LOCALIZATION: Record<string, string> = {
|
|
|
29
31
|
|
|
30
32
|
let activeShared: any = null;
|
|
31
33
|
let repoRoot = "";
|
|
32
|
-
const githubClient = createGitHubClient();
|
|
33
34
|
|
|
34
35
|
export function getDefaults(): any {
|
|
35
36
|
return {
|
|
@@ -95,23 +96,15 @@ function safeStat(...args: any[]): any {
|
|
|
95
96
|
return getShared().safeStat(...args);
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
function
|
|
99
|
-
return getShared().parseIssueNumber(...args);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function parsePrNumber(...args: any[]): any {
|
|
103
|
-
return getShared().parsePrNumber(...args);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function check({ taskDir, config, artifactFile }: any, shared: any): any {
|
|
99
|
+
export async function check({ taskDir, config, artifactFile }: any, shared: any): Promise<any> {
|
|
107
100
|
activeShared = shared;
|
|
108
101
|
repoRoot = shared.repoRoot;
|
|
109
|
-
const context = buildSyncContext({ taskDir, config, artifactFile });
|
|
102
|
+
const context = await buildSyncContext({ taskDir, config, artifactFile });
|
|
110
103
|
if (context.earlyReturn) {
|
|
111
104
|
return context.earlyReturn;
|
|
112
105
|
}
|
|
113
106
|
|
|
114
|
-
const remoteData = fetchRemoteData(context);
|
|
107
|
+
const remoteData = await fetchRemoteData(context);
|
|
115
108
|
if (remoteData.earlyReturn) {
|
|
116
109
|
return remoteData.earlyReturn;
|
|
117
110
|
}
|
|
@@ -142,33 +135,36 @@ export function check({ taskDir, config, artifactFile }: any, shared: any): any
|
|
|
142
135
|
}
|
|
143
136
|
}
|
|
144
137
|
|
|
145
|
-
return passResult(CHECK_TYPE, `
|
|
138
|
+
return passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
|
|
146
139
|
}
|
|
147
140
|
|
|
148
|
-
function buildSyncContext({ taskDir, config, artifactFile }: any): any {
|
|
141
|
+
async function buildSyncContext({ taskDir, config, artifactFile }: any): Promise<any> {
|
|
149
142
|
const task = loadTask(taskDir);
|
|
150
143
|
if (!task.ok) {
|
|
151
144
|
return { earlyReturn: failResult(CHECK_TYPE, task.message) };
|
|
152
145
|
}
|
|
153
146
|
|
|
154
|
-
const
|
|
147
|
+
const issueIdentity = taskIssueIdentity(task.metadata);
|
|
148
|
+
const issueNumber = resourceIdentityNumber(issueIdentity);
|
|
155
149
|
const fact = readPrDeliveryFact(task.metadata);
|
|
156
150
|
if (fact.status === "invalid") {
|
|
157
151
|
return { earlyReturn: failResult(CHECK_TYPE, fact.error.message, "check_failed") };
|
|
158
152
|
}
|
|
159
|
-
const
|
|
160
|
-
|
|
153
|
+
const prIdentity = fact.status === "valid" && fact.fact.state === "bound" ? fact.fact.identity.resource : null;
|
|
154
|
+
const prNumber = resourceIdentityNumber(prIdentity);
|
|
155
|
+
if (config.when === "issue_number_exists" && !issueIdentity) {
|
|
161
156
|
return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
|
|
162
157
|
}
|
|
163
|
-
if (config.when === "pr_fact_bound" && !
|
|
158
|
+
if (config.when === "pr_fact_bound" && !prIdentity) {
|
|
164
159
|
return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
|
|
165
160
|
}
|
|
166
161
|
|
|
167
|
-
if (!
|
|
162
|
+
if (!issueIdentity) {
|
|
168
163
|
return { earlyReturn: passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
|
|
169
164
|
}
|
|
170
165
|
|
|
171
|
-
const
|
|
166
|
+
const loaded = await resolvePlatformProviderContext({ cwd: repoRoot });
|
|
167
|
+
const platformContext = loaded.ok ? loaded.value.context : loaded.context;
|
|
172
168
|
if (platformContext.status === "failed") {
|
|
173
169
|
return { earlyReturn: failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
|
|
174
170
|
}
|
|
@@ -181,7 +177,6 @@ function buildSyncContext({ taskDir, config, artifactFile }: any): any {
|
|
|
181
177
|
}
|
|
182
178
|
return { earlyReturn: passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
|
|
183
179
|
}
|
|
184
|
-
const repoOwnerType = detectRepoOwnerType(platformContext.platform.repository, taskDir);
|
|
185
180
|
const expectedValues = resolveExpectedValues(config);
|
|
186
181
|
if (!expectedValues.ok) {
|
|
187
182
|
return { earlyReturn: failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
|
|
@@ -201,15 +196,20 @@ function buildSyncContext({ taskDir, config, artifactFile }: any): any {
|
|
|
201
196
|
config,
|
|
202
197
|
artifactFile,
|
|
203
198
|
artifactPath,
|
|
199
|
+
issueIdentity,
|
|
200
|
+
prIdentity,
|
|
204
201
|
issueNumber,
|
|
205
202
|
prNumber,
|
|
206
203
|
upstreamRepo: platformContext.platform.repository,
|
|
207
|
-
repoOwnerType,
|
|
204
|
+
repoOwnerType: String(loaded.ok ? loaded.value.snapshot.metadata?.ownerType || "unknown" : "unknown"),
|
|
208
205
|
hasTriage: platformContext.capabilities.triage,
|
|
209
206
|
hasPush: platformContext.capabilities.push,
|
|
210
207
|
expectedStatusLabel: expectedValues.statusLabel,
|
|
211
208
|
marker,
|
|
212
|
-
prMarker
|
|
209
|
+
prMarker,
|
|
210
|
+
provider: loaded.ok ? loaded.value.provider : null,
|
|
211
|
+
providerType: loaded.ok ? loaded.value.providerType : null,
|
|
212
|
+
loadedContext: loaded.ok ? loaded.value : null
|
|
213
213
|
};
|
|
214
214
|
}
|
|
215
215
|
|
|
@@ -266,170 +266,68 @@ function resolveDefaultValue({ collection, key, value, configKey }: any): any {
|
|
|
266
266
|
return { ok: true, value: resolvedValue };
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
function fetchRemoteData(context: any): any {
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
milestone: sharedIssue.value.milestone ? { title: sharedIssue.value.milestone } : null
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
: {
|
|
287
|
-
ok: false,
|
|
288
|
-
type: sharedIssue.error.retryable ? "network_error" : "check_failed",
|
|
289
|
-
message: sharedIssue.error.message
|
|
290
|
-
};
|
|
291
|
-
if (!issueResult.ok) {
|
|
269
|
+
async function fetchRemoteData(context: any): Promise<any> {
|
|
270
|
+
const provider = context.provider;
|
|
271
|
+
const operationContext = providerOperationContext(context.loadedContext, context.taskDir);
|
|
272
|
+
const facts = provider?.verification?.fetchRemoteFacts
|
|
273
|
+
? await provider.verification.fetchRemoteFacts({
|
|
274
|
+
context: operationContext,
|
|
275
|
+
taskId: context.task?.metadata?.id || context.task?.id || "",
|
|
276
|
+
...(context.issueIdentity ? { issue: context.issueIdentity } : {}),
|
|
277
|
+
...(context.prIdentity ? { changeRequest: context.prIdentity } : {}),
|
|
278
|
+
includeComments: shouldFetchComments(context.config),
|
|
279
|
+
includeFields: Boolean(context.config.verify_issue_fields)
|
|
280
|
+
})
|
|
281
|
+
: unsupportedProviderOperation(provider, "verification.fetchRemoteFacts");
|
|
282
|
+
if (!facts.ok) {
|
|
292
283
|
return {
|
|
293
|
-
earlyReturn:
|
|
294
|
-
?
|
|
295
|
-
:
|
|
284
|
+
earlyReturn: facts.error.retryable
|
|
285
|
+
? blockedResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "network_error")
|
|
286
|
+
: failResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "check_failed")
|
|
296
287
|
};
|
|
297
288
|
}
|
|
298
|
-
|
|
299
|
-
const issue =
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
let comments = null;
|
|
307
|
-
if (shouldFetchComments(context.config)) {
|
|
308
|
-
const commentsResult = withRetry(() => ghPaginatedJson([
|
|
309
|
-
"api",
|
|
310
|
-
"--paginate",
|
|
311
|
-
"--slurp",
|
|
312
|
-
`repos/${context.upstreamRepo}/issues/${context.issueNumber}/comments?per_page=100`
|
|
313
|
-
], context.taskDir));
|
|
314
|
-
|
|
315
|
-
if (!commentsResult.ok) {
|
|
316
|
-
return {
|
|
317
|
-
earlyReturn: commentsResult.type === "check_failed"
|
|
318
|
-
? failResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
|
|
319
|
-
: blockedResult(CHECK_TYPE, commentsResult.message, commentsResult.type)
|
|
320
|
-
};
|
|
289
|
+
const issueSnapshot = facts.value.issue;
|
|
290
|
+
const issue = issueSnapshot
|
|
291
|
+
? {
|
|
292
|
+
state: issueSnapshot.state.toUpperCase(),
|
|
293
|
+
labels: issueSnapshot.labels.map((name: string) => ({ name })),
|
|
294
|
+
body: issueSnapshot.body,
|
|
295
|
+
milestone: issueSnapshot.milestone ? { title: issueSnapshot.milestone } : null
|
|
321
296
|
}
|
|
322
|
-
|
|
323
|
-
|
|
297
|
+
: null;
|
|
298
|
+
let issueFields: any;
|
|
299
|
+
if (context.config.verify_issue_fields && issueSnapshot?.issueType) {
|
|
300
|
+
const fieldKinds = new Map(issueSnapshot.issueType.fields.map((field: { name: string; kind: string }) => [field.name, field.kind]));
|
|
301
|
+
issueFields = {
|
|
302
|
+
pinnedNames: new Set(issueSnapshot.issueType.fields.map((field: { name: string }) => field.name)),
|
|
303
|
+
values: new Map(Object.entries(issueSnapshot.fields).map(([name, value]) => [name, { kind: fieldKinds.get(name) || (typeof value === "number" ? "number" : "single-select"), value }]))
|
|
304
|
+
};
|
|
324
305
|
}
|
|
325
|
-
|
|
326
306
|
let prComments = null;
|
|
327
|
-
if (context.prMarker) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
githubClient,
|
|
336
|
-
context.upstreamRepo,
|
|
337
|
-
context.prNumber,
|
|
338
|
-
context.taskDir
|
|
339
|
-
);
|
|
340
|
-
|
|
341
|
-
if (!prCommentsResult.ok) {
|
|
342
|
-
return {
|
|
343
|
-
earlyReturn: prCommentsResult.error.retryable
|
|
344
|
-
? blockedResult(CHECK_TYPE, prCommentsResult.error.message, "network_error")
|
|
345
|
-
: failResult(CHECK_TYPE, prCommentsResult.error.message, "check_failed")
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
prComments = prCommentsResult.value;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
let issueType;
|
|
353
|
-
if (context.config.verify_issue_type && context.hasPush) {
|
|
354
|
-
const issueTypeResult = withRetry(() => ghText([
|
|
355
|
-
"api",
|
|
356
|
-
`repos/${context.upstreamRepo}/issues/${context.issueNumber}`,
|
|
357
|
-
"--jq",
|
|
358
|
-
".type.name // empty"
|
|
359
|
-
], context.taskDir));
|
|
360
|
-
|
|
361
|
-
if (issueTypeResult.ok) {
|
|
362
|
-
issueType = issueTypeResult.value || null;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
let issueFields;
|
|
367
|
-
if (context.config.verify_issue_fields && context.hasPush) {
|
|
368
|
-
const [owner, name] = context.upstreamRepo.split("/");
|
|
369
|
-
const issueFieldsResult = withRetry(() => ghJson([
|
|
370
|
-
"api",
|
|
371
|
-
"graphql",
|
|
372
|
-
"-f",
|
|
373
|
-
`query=${ISSUE_FIELDS_QUERY}`,
|
|
374
|
-
"-F",
|
|
375
|
-
`owner=${owner}`,
|
|
376
|
-
"-F",
|
|
377
|
-
`name=${name}`,
|
|
378
|
-
"-F",
|
|
379
|
-
`number=${context.issueNumber}`
|
|
380
|
-
], context.taskDir));
|
|
381
|
-
|
|
382
|
-
if (issueFieldsResult.ok) {
|
|
383
|
-
issueFields = normalizeIssueFields(issueFieldsResult.value);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
let prLabels = null;
|
|
388
|
-
let prMilestone;
|
|
389
|
-
let prAssignees;
|
|
390
|
-
let prHeadSha;
|
|
391
|
-
if (((context.config.verify_in_labels_match_pr && context.hasTriage)
|
|
392
|
-
|| (context.config.verify_pr_type_label && context.hasTriage)
|
|
393
|
-
|| (context.config.verify_milestone && context.hasTriage)
|
|
394
|
-
|| (context.config.verify_pr_assignee && context.hasPush)
|
|
395
|
-
|| context.config.verify_pr_comment_last_commit_matches_head) && context.prNumber) {
|
|
396
|
-
const prResult = inspectGitHubPullRequest(
|
|
397
|
-
githubClient,
|
|
398
|
-
context.upstreamRepo,
|
|
399
|
-
context.prNumber,
|
|
400
|
-
context.taskDir
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
if (!prResult.ok) {
|
|
404
|
-
return {
|
|
405
|
-
earlyReturn: prResult.error.retryable
|
|
406
|
-
? blockedResult(CHECK_TYPE, prResult.error.message, "network_error")
|
|
407
|
-
: failResult(CHECK_TYPE, prResult.error.message, "check_failed")
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
prLabels = (context.config.verify_in_labels_match_pr || context.config.verify_pr_type_label)
|
|
412
|
-
? prResult.value.labels
|
|
413
|
-
: null;
|
|
414
|
-
prMilestone = context.config.verify_milestone
|
|
415
|
-
? prResult.value.milestone ? { title: prResult.value.milestone } : null
|
|
416
|
-
: undefined;
|
|
417
|
-
prAssignees = context.config.verify_pr_assignee
|
|
418
|
-
? prResult.value.assignees
|
|
419
|
-
: undefined;
|
|
420
|
-
prHeadSha = prResult.value.head.sha;
|
|
307
|
+
if (context.prMarker && context.prIdentity && shouldFetchComments(context.config) && provider?.comments?.list) {
|
|
308
|
+
const listed = await provider.comments.list({ context: operationContext, parent: context.prIdentity });
|
|
309
|
+
if (!listed.ok) return {
|
|
310
|
+
earlyReturn: listed.error.retryable
|
|
311
|
+
? blockedResult(CHECK_TYPE, listed.error.message, "network_error")
|
|
312
|
+
: failResult(CHECK_TYPE, listed.error.message, "check_failed")
|
|
313
|
+
};
|
|
314
|
+
prComments = listed.value.map((comment: { id: string; body: string }) => ({ id: comment.id, body: comment.body }));
|
|
421
315
|
}
|
|
422
|
-
|
|
316
|
+
const changeRequest = facts.value.changeRequest;
|
|
423
317
|
return {
|
|
424
318
|
issue,
|
|
425
|
-
comments,
|
|
319
|
+
comments: facts.value.comments.map((comment: { id: string; body: string }) => ({ id: comment.id, body: comment.body })),
|
|
426
320
|
prComments,
|
|
427
|
-
prLabels,
|
|
428
|
-
issueType
|
|
321
|
+
prLabels: changeRequest?.labels || null,
|
|
322
|
+
issueType: issueSnapshot
|
|
323
|
+
? (issueSnapshot.issueType ? issueSnapshot.issueType.name : null)
|
|
324
|
+
: undefined,
|
|
429
325
|
issueFields,
|
|
430
|
-
prMilestone
|
|
431
|
-
|
|
432
|
-
|
|
326
|
+
prMilestone: changeRequest
|
|
327
|
+
? (changeRequest.milestone ? { title: changeRequest.milestone } : null)
|
|
328
|
+
: undefined,
|
|
329
|
+
prAssignees: changeRequest?.assignees,
|
|
330
|
+
prHeadSha: changeRequest?.headSha
|
|
433
331
|
};
|
|
434
332
|
}
|
|
435
333
|
|
|
@@ -449,14 +347,6 @@ function shouldFetchComments(config: any): any {
|
|
|
449
347
|
);
|
|
450
348
|
}
|
|
451
349
|
|
|
452
|
-
function flattenComments(value: any): any {
|
|
453
|
-
if (!Array.isArray(value)) {
|
|
454
|
-
return [];
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return value.flatMap((page) => Array.isArray(page) ? page : []);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
350
|
function checkStatusLabel(context: any, remoteData: any): any {
|
|
461
351
|
if (!context.expectedStatusLabel || !context.hasTriage) {
|
|
462
352
|
return null;
|
|
@@ -715,7 +605,7 @@ function checkInLabelsComputed(context: any, remoteData: any): any {
|
|
|
715
605
|
return null;
|
|
716
606
|
}
|
|
717
607
|
|
|
718
|
-
const expectedInLabels = computeExpectedInLabels(context.taskDir);
|
|
608
|
+
const expectedInLabels = computeExpectedInLabels(context.taskDir, context.upstreamRepo);
|
|
719
609
|
if (!expectedInLabels.ok) {
|
|
720
610
|
return expectedInLabels.type === "check_failed"
|
|
721
611
|
? failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
|
|
@@ -1052,8 +942,6 @@ function mapTaskTypeToIssueType(taskType: any): any {
|
|
|
1052
942
|
return mapping[taskType] || "Task";
|
|
1053
943
|
}
|
|
1054
944
|
|
|
1055
|
-
const ISSUE_FIELDS_QUERY = `query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){issue(number:$number){issueType{name pinnedFields{__typename ... on IssueFieldSingleSelect{id name} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}} issueFieldValues(first:50){nodes{__typename ... on IssueFieldSingleSelectValue{name optionId field{... on IssueFieldSingleSelect{name}}} ... on IssueFieldDateValue{value field{... on IssueFieldDate{name}}} ... on IssueFieldTextValue{value field{... on IssueFieldText{name}}} ... on IssueFieldNumberValue{value field{... on IssueFieldNumber{name}}}}}}}}`;
|
|
1056
|
-
|
|
1057
945
|
function normalizeIssueFields(payload: any): any {
|
|
1058
946
|
const issue = payload?.data?.repository?.issue;
|
|
1059
947
|
const pinnedFields = Array.isArray(issue?.issueType?.pinnedFields)
|
|
@@ -1127,10 +1015,18 @@ function formatLabelList(labels: any): any {
|
|
|
1127
1015
|
return labels.length > 0 ? labels.join(", ") : "none";
|
|
1128
1016
|
}
|
|
1129
1017
|
|
|
1130
|
-
function computeExpectedInLabels(taskDir: any): any {
|
|
1131
|
-
const
|
|
1018
|
+
function computeExpectedInLabels(taskDir: any, repository: any): any {
|
|
1019
|
+
const task = loadTask(taskDir);
|
|
1020
|
+
if (!task.ok) {
|
|
1021
|
+
return task;
|
|
1022
|
+
}
|
|
1023
|
+
const baseRef = String(task.metadata?.delivery_base_ref || "").trim();
|
|
1024
|
+
if (!baseRef) {
|
|
1025
|
+
return { ok: false, type: "check_failed", message: "Task has no delivery_base_ref for in-label evidence" };
|
|
1026
|
+
}
|
|
1027
|
+
const changedFilesResult = gitText(["diff", `${baseRef}...HEAD`, "--name-only"], taskDir);
|
|
1132
1028
|
if (!changedFilesResult.ok) {
|
|
1133
|
-
return changedFilesResult;
|
|
1029
|
+
return { ...changedFilesResult, type: "network_error" };
|
|
1134
1030
|
}
|
|
1135
1031
|
|
|
1136
1032
|
const changedFiles = String(changedFilesResult.value || "")
|
|
@@ -1143,55 +1039,32 @@ function computeExpectedInLabels(taskDir: any): any {
|
|
|
1143
1039
|
return mapping;
|
|
1144
1040
|
}
|
|
1145
1041
|
|
|
1146
|
-
if (Object.keys(mapping.value ?? {}).length
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
for (const file of changedFiles) {
|
|
1150
|
-
for (const [label, prefixes] of Object.entries(mapping.value ?? {}) as Array<[string, string[]]>) {
|
|
1151
|
-
if (prefixes.some((prefix: any) => file.startsWith(prefix))) {
|
|
1152
|
-
labels.add(`in: ${label}`);
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
return { ok: true, labels: Array.from(labels).sort(), mode: "mapped" };
|
|
1042
|
+
if (Object.keys(mapping.value ?? {}).length === 0) {
|
|
1043
|
+
return { ok: true, labels: [], mode: "mapped" };
|
|
1158
1044
|
}
|
|
1159
1045
|
|
|
1160
|
-
const
|
|
1161
|
-
|
|
1162
|
-
"
|
|
1163
|
-
|
|
1164
|
-
"200",
|
|
1165
|
-
"--json",
|
|
1166
|
-
"name"
|
|
1167
|
-
], taskDir));
|
|
1046
|
+
const client = createGitHubClient();
|
|
1047
|
+
const repoLabelsResult = withRetry(() => client.json([
|
|
1048
|
+
"api", "--paginate", "--slurp", `repos/${repository}/labels?per_page=100`
|
|
1049
|
+
], { cwd: taskDir }));
|
|
1168
1050
|
if (!repoLabelsResult.ok) {
|
|
1169
1051
|
return repoLabelsResult;
|
|
1170
1052
|
}
|
|
1171
1053
|
|
|
1172
|
-
const
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
);
|
|
1176
|
-
|
|
1177
|
-
if (repoInLabels.size === 0) {
|
|
1178
|
-
return { ok: true, labels: [], mode: "fallback" };
|
|
1054
|
+
const repositoryLabels = validateRepositoryLabelPayload(repoLabelsResult.value);
|
|
1055
|
+
if (!repositoryLabels.ok) {
|
|
1056
|
+
return { ok: false, type: "check_failed", message: repositoryLabels.error.message };
|
|
1179
1057
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
const candidate = `in: ${topLevel}`;
|
|
1189
|
-
if (repoInLabels.has(candidate)) {
|
|
1190
|
-
labels.add(candidate);
|
|
1191
|
-
}
|
|
1058
|
+
const planned = planInLabelUpdate({
|
|
1059
|
+
changedFiles,
|
|
1060
|
+
currentLabels: [],
|
|
1061
|
+
mapping: mapping.value ?? {},
|
|
1062
|
+
repositoryLabels: new Set(repositoryLabels.value)
|
|
1063
|
+
});
|
|
1064
|
+
if (planned.error) {
|
|
1065
|
+
return { ok: false, type: "check_failed", message: planned.error.message };
|
|
1192
1066
|
}
|
|
1193
|
-
|
|
1194
|
-
return { ok: true, labels: Array.from(labels).sort(), mode: "fallback" };
|
|
1067
|
+
return { ok: true, labels: planned.target, mode: "mapped" };
|
|
1195
1068
|
}
|
|
1196
1069
|
|
|
1197
1070
|
function loadInLabelMapping(): any {
|
|
@@ -1202,26 +1075,10 @@ function loadInLabelMapping(): any {
|
|
|
1202
1075
|
|
|
1203
1076
|
try {
|
|
1204
1077
|
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
1205
|
-
const mapping = config?.labels?.in;
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
const normalized: Record<string, string[]> = {};
|
|
1211
|
-
for (const [label, prefixes] of Object.entries(mapping)) {
|
|
1212
|
-
if (!Array.isArray(prefixes)) {
|
|
1213
|
-
continue;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
const cleaned = prefixes
|
|
1217
|
-
.map((value) => String(value || "").trim())
|
|
1218
|
-
.filter(Boolean);
|
|
1219
|
-
if (cleaned.length > 0) {
|
|
1220
|
-
normalized[label] = cleaned;
|
|
1221
|
-
}
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
return { ok: true, value: normalized };
|
|
1078
|
+
const mapping = validateInLabelMapping(config?.labels?.in);
|
|
1079
|
+
return mapping.ok
|
|
1080
|
+
? { ok: true, value: mapping.value }
|
|
1081
|
+
: { ok: false, type: "check_failed", message: mapping.error.message };
|
|
1225
1082
|
} catch (error: any) {
|
|
1226
1083
|
return { ok: false, type: "check_failed", message: `Unable to parse .agents/.airc.json: ${error.message}` };
|
|
1227
1084
|
}
|
|
@@ -1229,43 +1086,6 @@ function loadInLabelMapping(): any {
|
|
|
1229
1086
|
|
|
1230
1087
|
// === GitHub API ===
|
|
1231
1088
|
|
|
1232
|
-
function detectRepoOwnerType(upstreamRepo: any, taskDir: any): any {
|
|
1233
|
-
const ownerTypeResult = withRetry(() => ghText([
|
|
1234
|
-
"api",
|
|
1235
|
-
`repos/${upstreamRepo}`,
|
|
1236
|
-
"--jq",
|
|
1237
|
-
".owner.type // empty"
|
|
1238
|
-
], taskDir));
|
|
1239
|
-
|
|
1240
|
-
if (!ownerTypeResult.ok) {
|
|
1241
|
-
return "unknown";
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
return ownerTypeResult.value || "unknown";
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
function ghJson(args: any, cwd: any): any {
|
|
1248
|
-
return mapClientResult(githubClient.json(args, { cwd }));
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
function ghText(args: any, cwd: any): any {
|
|
1252
|
-
return mapClientResult(githubClient.text(args, { cwd }));
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
function ghPaginatedJson(args: any, cwd: any): any {
|
|
1256
|
-
return ghJson(args, cwd);
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
function mapClientResult(result: any): any {
|
|
1260
|
-
if (result.ok) return result;
|
|
1261
|
-
const checkFailed = ["RESOURCE_NOT_FOUND", "PERMISSION_DENIED", "PLATFORM_REQUEST_INVALID"].includes(result.error.code);
|
|
1262
|
-
return {
|
|
1263
|
-
ok: false,
|
|
1264
|
-
type: checkFailed ? "check_failed" : "network_error",
|
|
1265
|
-
message: result.error.message
|
|
1266
|
-
};
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
1089
|
function gitText(args: any, cwd: any): any {
|
|
1270
1090
|
try {
|
|
1271
1091
|
const value = execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
|
|
@@ -3,6 +3,9 @@ import path from 'node:path';
|
|
|
3
3
|
|
|
4
4
|
import { parseTypedTaskFrontmatter } from '../task/frontmatter.ts';
|
|
5
5
|
import { readPrDeliveryFact } from '../task/pr-delivery-fact.ts';
|
|
6
|
+
import { resourceIdentityEquals, resourceIdentityNumber } from '../platform/resource-identity.ts';
|
|
7
|
+
import type { ResourceIdentity } from '../platform/resource-identity.ts';
|
|
8
|
+
import { taskIssueIdentity } from '../platform/task-identities.ts';
|
|
6
9
|
|
|
7
10
|
// ---------------------------------------------------------------------------
|
|
8
11
|
// Host resolution (PL-4): PR -> unique / ambiguous / none.
|
|
@@ -13,10 +16,12 @@ export type HostCandidate = {
|
|
|
13
16
|
taskDir: string;
|
|
14
17
|
issueNumber: number | null;
|
|
15
18
|
prNumber: number | null;
|
|
19
|
+
issueIdentity?: ResourceIdentity;
|
|
20
|
+
prIdentity?: ResourceIdentity;
|
|
16
21
|
};
|
|
17
22
|
|
|
18
23
|
export type HostResolution =
|
|
19
|
-
| { kind: 'unique'; taskId: string; taskDir: string; issueNumber: number; prNumber: number }
|
|
24
|
+
| { kind: 'unique'; taskId: string; taskDir: string; issueNumber: number; prNumber: number; issueIdentity?: ResourceIdentity; prIdentity?: ResourceIdentity }
|
|
20
25
|
| { kind: 'ambiguous'; candidates: Array<{ taskId: string; issueNumber: number }> }
|
|
21
26
|
| { kind: 'none' };
|
|
22
27
|
|
|
@@ -66,7 +71,8 @@ export function extractClosingIssueNumbers(body: string): number[] {
|
|
|
66
71
|
* a verified fact identity hit takes priority over the issue-number reverse lookup.
|
|
67
72
|
*/
|
|
68
73
|
export function collectHostCandidates(input: {
|
|
69
|
-
prNumber
|
|
74
|
+
prNumber?: number;
|
|
75
|
+
prIdentity?: ResourceIdentity;
|
|
70
76
|
closingIssues: number[];
|
|
71
77
|
workspaceRoot: string;
|
|
72
78
|
}): HostCandidate[] {
|
|
@@ -90,13 +96,22 @@ export function collectHostCandidates(input: {
|
|
|
90
96
|
}
|
|
91
97
|
let frontmatter: ReturnType<typeof parseTypedTaskFrontmatter>;
|
|
92
98
|
try { frontmatter = parseTypedTaskFrontmatter(content); } catch { continue; }
|
|
93
|
-
const
|
|
99
|
+
const issueIdentity = taskIssueIdentity(frontmatter);
|
|
100
|
+
const issueNumber = resourceIdentityNumber(issueIdentity) ?? toPositiveNumber(frontmatter.issue_number);
|
|
94
101
|
const fact = readPrDeliveryFact(frontmatter);
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
const prIdentity = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.resource : null;
|
|
103
|
+
const prNumber = resourceIdentityNumber(prIdentity);
|
|
104
|
+
const matchesPr = input.prIdentity
|
|
105
|
+
? Boolean(prIdentity && resourceIdentityEquals(prIdentity, input.prIdentity))
|
|
106
|
+
: input.prNumber !== undefined && prNumber === input.prNumber;
|
|
107
|
+
const identityDetails = {
|
|
108
|
+
...(issueIdentity && issueIdentity.kind !== 'number' ? { issueIdentity } : {}),
|
|
109
|
+
...(prIdentity && prIdentity.kind !== 'number' ? { prIdentity } : {})
|
|
110
|
+
};
|
|
111
|
+
if (matchesPr) {
|
|
112
|
+
candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber, ...identityDetails });
|
|
98
113
|
} else if (issueNumber !== null && input.closingIssues.includes(issueNumber)) {
|
|
99
|
-
candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber });
|
|
114
|
+
candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber, ...identityDetails });
|
|
100
115
|
}
|
|
101
116
|
}
|
|
102
117
|
return candidates;
|
|
@@ -116,7 +131,9 @@ export function resolveHostFromCandidates(candidates: HostCandidate[]): HostReso
|
|
|
116
131
|
taskId: candidate.taskId,
|
|
117
132
|
taskDir: candidate.taskDir,
|
|
118
133
|
issueNumber: candidate.issueNumber ?? 0,
|
|
119
|
-
prNumber: candidate.prNumber ?? 0
|
|
134
|
+
prNumber: candidate.prNumber ?? 0,
|
|
135
|
+
...(candidate.issueIdentity ? { issueIdentity: candidate.issueIdentity } : {}),
|
|
136
|
+
...(candidate.prIdentity ? { prIdentity: candidate.prIdentity } : {})
|
|
120
137
|
};
|
|
121
138
|
}
|
|
122
139
|
return {
|