@fitlab-ai/agent-infra 0.11.0 → 0.11.2
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/lib/internal/cli-route-inventory.js +1 -1
- package/dist/lib/internal/manual-validation.js +6 -2
- package/dist/lib/internal/platform-comment.js +59 -0
- package/dist/lib/internal/sandbox-control.js +4 -131
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +1 -0
- package/dist/lib/internal/task-review.js +5 -5
- package/dist/lib/internal/task-validate.js +0 -6
- package/dist/lib/platform/comment-safety.js +7 -1
- package/dist/lib/platform/github-provider.js +13 -3
- package/dist/lib/platform/issue-comments.js +120 -13
- package/dist/lib/platform/issues.js +3 -4
- package/dist/lib/platform/provider-contract.d.ts +2 -0
- package/dist/lib/platform/provider-validation.js +7 -2
- package/dist/lib/platform/verification-sync.js +161 -156
- package/dist/lib/sandbox/control/executor.js +0 -38
- package/dist/lib/sandbox/control/finalization-response.js +10 -2
- package/dist/lib/sandbox/control/task-view.js +3 -1
- package/dist/lib/sandbox/control/workflow-executor.js +32 -41
- package/dist/lib/task/artifact-command.js +6 -6
- package/dist/lib/task/artifact-lifecycle.js +15 -52
- package/dist/lib/task/artifact-operations.js +27 -1
- package/dist/lib/task/capabilities.js +0 -2
- package/dist/lib/task/codex-orchestration.js +10 -208
- package/dist/lib/task/commit-operation.js +3 -18
- package/dist/lib/task/control-authority.js +1 -8
- package/dist/lib/task/delegation-receipts.js +14 -76
- package/dist/lib/task/events.js +147 -278
- package/dist/lib/task/finalization-state.js +2 -1
- package/dist/lib/task/finalization.js +273 -49
- package/dist/lib/task/gate-policy.js +94 -0
- package/dist/lib/task/lifecycle-execution.js +2 -11
- package/dist/lib/task/local-artifact-finalization.js +31 -245
- package/dist/lib/task/orchestration.js +8 -24
- package/dist/lib/task/review-artifacts.js +12 -6
- package/dist/lib/task/review-command.js +0 -1
- package/dist/lib/task/review-finalization.js +47 -218
- package/dist/lib/task/verification-engine.js +28 -17
- package/dist/lib/task/verification.js +25 -9
- package/dist/lib/task/workflow-command.js +4 -1
- package/dist/lib/task/workflow-dispatch.js +55 -3
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/internal/cli-route-inventory.ts +1 -1
- package/lib/internal/manual-validation.ts +6 -2
- package/lib/internal/platform-comment.ts +38 -0
- package/lib/internal/sandbox-control.ts +3 -145
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-operation-registry.ts +1 -0
- package/lib/internal/task-review.ts +5 -5
- package/lib/internal/task-validate.ts +0 -5
- package/lib/platform/comment-safety.ts +8 -0
- package/lib/platform/github-provider.ts +11 -2
- package/lib/platform/issue-comments.ts +132 -13
- package/lib/platform/issues.ts +3 -4
- package/lib/platform/provider-contract.ts +2 -0
- package/lib/platform/provider-validation.ts +6 -2
- package/lib/platform/verification-sync.ts +178 -179
- package/lib/sandbox/control/executor.ts +0 -44
- package/lib/sandbox/control/finalization-response.ts +10 -2
- package/lib/sandbox/control/task-view.ts +3 -1
- package/lib/sandbox/control/workflow-executor.ts +31 -48
- package/lib/task/artifact-command.ts +6 -7
- package/lib/task/artifact-lifecycle.ts +12 -49
- package/lib/task/artifact-operations.ts +27 -1
- package/lib/task/capabilities.ts +0 -1
- package/lib/task/codex-orchestration.ts +8 -241
- package/lib/task/commit-operation.ts +3 -23
- package/lib/task/control-authority.ts +1 -8
- package/lib/task/delegation-receipts.ts +12 -79
- package/lib/task/events.ts +155 -287
- package/lib/task/finalization-state.ts +2 -1
- package/lib/task/finalization.ts +277 -57
- package/lib/task/gate-policy.ts +133 -0
- package/lib/task/lifecycle-execution.ts +10 -21
- package/lib/task/local-artifact-finalization.ts +29 -285
- package/lib/task/orchestration.ts +12 -41
- package/lib/task/review-artifacts.ts +8 -2
- package/lib/task/review-command.ts +0 -1
- package/lib/task/review-finalization.ts +46 -200
- package/lib/task/verification-engine.ts +24 -14
- package/lib/task/verification-types.ts +7 -0
- package/lib/task/verification.ts +25 -9
- package/lib/task/workflow-command.ts +4 -1
- package/lib/task/workflow-dispatch.ts +46 -3
- package/package.json +2 -2
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +5 -8
- package/templates/.agents/rules/lifecycle-orchestration.en.md +7 -7
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +7 -7
- package/templates/.agents/rules/local-artifact-repair.en.md +14 -52
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -52
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -10
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/block-task/config/verify.json +1 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -12
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -12
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +11 -0
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +11 -0
- package/templates/.agents/skills/commit/config/verify.json +1 -5
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +6 -6
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +6 -6
- 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 +3 -14
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +3 -14
- package/templates/.agents/skills/create-pr/config/verify.json +1 -10
- package/templates/.agents/skills/create-task/config/verify.json +1 -4
- package/templates/.agents/skills/import-issue/config/verify.json +1 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -10
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -4
- 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 -10
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -10
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/review-code/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +2 -0
- 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 -10
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -10
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -8
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -8
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +38 -13
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +38 -13
- package/templates/.agents/skills/run-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { VerificationShared } from "../task/verification-types.ts";
|
|
2
|
+
import { normalizeVerificationRecord, platformAuditPolicy, type PlatformAuditId } from "../task/gate-policy.ts";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import process from "node:process";
|
|
@@ -12,7 +13,7 @@ import { planInLabelUpdate, validateInLabelMapping } from "./in-label-sync.ts";
|
|
|
12
13
|
import { readPrDeliveryFact } from "../task/pr-delivery-fact.ts";
|
|
13
14
|
import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.ts";
|
|
14
15
|
import { taskIssueIdentity } from "./task-identities.ts";
|
|
15
|
-
import { renderTaskCommentResult } from "./issue-comments.ts";
|
|
16
|
+
import { MARKERS, renderTaskCommentResult } from "./issue-comments.ts";
|
|
16
17
|
import {
|
|
17
18
|
CONTROL_MARKER_PATTERN,
|
|
18
19
|
canonicalizeCommentBody,
|
|
@@ -60,8 +61,23 @@ function sanitizeCommentContent(content: string): string | null {
|
|
|
60
61
|
return result.ok ? result.value : null;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
function infoResult(message: string): any {
|
|
65
|
+
return {
|
|
66
|
+
type: CHECK_TYPE,
|
|
67
|
+
status: 'pass',
|
|
68
|
+
message,
|
|
69
|
+
classification: 'info',
|
|
70
|
+
reason: 'NOT_APPLICABLE',
|
|
71
|
+
action: 'No action required'
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function hardResult(result: any): any {
|
|
76
|
+
return { ...result, classification: 'hard' };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function check({ taskDir, config, artifactFile, skillName }: any, shared: VerificationShared): Promise<any> {
|
|
80
|
+
const context = await buildSyncContext({ taskDir, config, artifactFile, skillName }, shared);
|
|
65
81
|
if (context.earlyReturn) {
|
|
66
82
|
return context.earlyReturn;
|
|
67
83
|
}
|
|
@@ -72,35 +88,36 @@ export async function check({ taskDir, config, artifactFile }: any, shared: Veri
|
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
const subChecks = [
|
|
75
|
-
checkClosedIssueStatusLabels,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
91
|
+
['closed-status-labels', checkClosedIssueStatusLabels], ['status-label', checkStatusLabel],
|
|
92
|
+
['comment-marker', checkCommentMarker], ['pr-comment-marker', checkPrCommentMarker],
|
|
93
|
+
['pr-comment-last-commit', checkPrCommentLastCommit], ['pr-comment-content', checkPrCommentRequiredPatterns],
|
|
94
|
+
['comment-content', checkCommentContent], ['task-comment-content', checkTaskCommentContent],
|
|
95
|
+
['in-labels-computed', checkInLabelsComputed], ['pr-type-label', checkPrTypeLabel],
|
|
96
|
+
['in-labels-match-pr', checkInLabelsMatchPr], ['pr-assignee', checkPrAssignee],
|
|
97
|
+
['requirements', checkSyncedRequirements], ['issue-type', checkIssueType],
|
|
98
|
+
['issue-fields', checkIssueFields], ['milestone', checkMilestone]
|
|
99
|
+
] as const;
|
|
100
|
+
const results = subChecks.map(([id, subCheck]) => {
|
|
101
|
+
const audit = platformAuditPolicy(id as PlatformAuditId, context);
|
|
102
|
+
const result = subCheck({ ...context, audit }, remoteData, shared) ?? infoResult(`Platform audit '${id}' was not evaluated`);
|
|
103
|
+
return { ...result, checkId: `platform.${id}`, classification: result.classification ?? audit.classification };
|
|
104
|
+
});
|
|
105
|
+
const blocking = results.map(normalizeVerificationRecord).find((result) => result.effectiveStatus !== 'pass');
|
|
106
|
+
return {
|
|
107
|
+
...(blocking
|
|
108
|
+
? {
|
|
109
|
+
type: CHECK_TYPE,
|
|
110
|
+
status: blocking.effectiveStatus,
|
|
111
|
+
message: blocking.message,
|
|
112
|
+
...(blocking.fail_type ? { fail_type: blocking.fail_type } : {}),
|
|
113
|
+
classification: 'hard'
|
|
114
|
+
}
|
|
115
|
+
: shared.passResult(CHECK_TYPE, `Platform sync audits completed for Issue ${context.issueNumber || "identity"}`)),
|
|
116
|
+
subchecks: results
|
|
117
|
+
};
|
|
101
118
|
}
|
|
102
119
|
|
|
103
|
-
async function buildSyncContext({ taskDir, config, artifactFile }: any, shared: VerificationShared): Promise<any> {
|
|
120
|
+
async function buildSyncContext({ taskDir, config, artifactFile, skillName }: any, shared: VerificationShared): Promise<any> {
|
|
104
121
|
const task = shared.loadTask(taskDir);
|
|
105
122
|
if (!task.ok) {
|
|
106
123
|
return { earlyReturn: shared.failResult(CHECK_TYPE, task.message) };
|
|
@@ -139,7 +156,8 @@ async function buildSyncContext({ taskDir, config, artifactFile }: any, shared:
|
|
|
139
156
|
}
|
|
140
157
|
return { earlyReturn: shared.passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
|
|
141
158
|
}
|
|
142
|
-
const
|
|
159
|
+
const effectiveSkillName = skillName || config.skillName || "code-task";
|
|
160
|
+
const expectedValues = resolveExpectedValues(effectiveSkillName, artifactFile);
|
|
143
161
|
if (!expectedValues.ok) {
|
|
144
162
|
return { earlyReturn: shared.failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
|
|
145
163
|
}
|
|
@@ -156,6 +174,7 @@ async function buildSyncContext({ taskDir, config, artifactFile }: any, shared:
|
|
|
156
174
|
task,
|
|
157
175
|
taskDir,
|
|
158
176
|
config,
|
|
177
|
+
skillName: effectiveSkillName,
|
|
159
178
|
artifactFile,
|
|
160
179
|
artifactPath,
|
|
161
180
|
issueIdentity,
|
|
@@ -174,59 +193,21 @@ async function buildSyncContext({ taskDir, config, artifactFile }: any, shared:
|
|
|
174
193
|
};
|
|
175
194
|
}
|
|
176
195
|
|
|
177
|
-
function resolveExpectedValues(
|
|
196
|
+
function resolveExpectedValues(skillName: string, artifactFile: string | undefined): any {
|
|
178
197
|
const defaults = getDefaults();
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
});
|
|
185
|
-
if (!statusLabel.ok) {
|
|
186
|
-
return statusLabel;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const commentMarker = resolveDefaultValue({
|
|
190
|
-
collection: defaults.markers,
|
|
191
|
-
key: config.expected_comment_marker_key,
|
|
192
|
-
value: config.expected_comment_marker,
|
|
193
|
-
configKey: "expected_comment_marker_key"
|
|
194
|
-
});
|
|
195
|
-
if (!commentMarker.ok) {
|
|
196
|
-
return commentMarker;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const prCommentMarker = resolveDefaultValue({
|
|
200
|
-
collection: defaults.markers,
|
|
201
|
-
key: config.expected_pr_comment_marker_key,
|
|
202
|
-
value: config.expected_pr_comment_marker,
|
|
203
|
-
configKey: "expected_pr_comment_marker_key"
|
|
204
|
-
});
|
|
205
|
-
if (!prCommentMarker.ok) {
|
|
206
|
-
return prCommentMarker;
|
|
207
|
-
}
|
|
208
|
-
|
|
198
|
+
const statusPolicy = platformAuditPolicy('status-label', { skillName, artifactFile });
|
|
199
|
+
const commentPolicy = platformAuditPolicy('comment-marker', { skillName, artifactFile });
|
|
200
|
+
const prCommentPolicy = ['pr-comment-marker', 'pr-comment-last-commit', 'pr-comment-content']
|
|
201
|
+
.map((id) => platformAuditPolicy(id as PlatformAuditId, { skillName, artifactFile }))
|
|
202
|
+
.find((policy) => policy.enabled && policy.expectedPrCommentMarkerKey);
|
|
209
203
|
return {
|
|
210
204
|
ok: true,
|
|
211
|
-
statusLabel:
|
|
212
|
-
commentMarker:
|
|
213
|
-
prCommentMarker:
|
|
205
|
+
statusLabel: statusPolicy.enabled ? statusPolicy.expectedStatusLabel || null : null,
|
|
206
|
+
commentMarker: commentPolicy.enabled && commentPolicy.expectedCommentMarkerKey ? defaults.markers[commentPolicy.expectedCommentMarkerKey] : null,
|
|
207
|
+
prCommentMarker: prCommentPolicy?.expectedPrCommentMarkerKey ? defaults.markers[prCommentPolicy.expectedPrCommentMarkerKey] : null
|
|
214
208
|
};
|
|
215
209
|
}
|
|
216
210
|
|
|
217
|
-
function resolveDefaultValue({ collection, key, value, configKey }: any): any {
|
|
218
|
-
if (!key) {
|
|
219
|
-
return { ok: true, value: value || null };
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const resolvedValue = collection[key];
|
|
223
|
-
if (!resolvedValue) {
|
|
224
|
-
return { ok: false, message: `Unknown ${configKey}: ${key}` };
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return { ok: true, value: resolvedValue };
|
|
228
|
-
}
|
|
229
|
-
|
|
230
211
|
async function fetchRemoteData(context: any, shared: VerificationShared): Promise<any> {
|
|
231
212
|
const provider = context.provider;
|
|
232
213
|
const operationContext = providerOperationContext(context.loadedContext, context.taskDir);
|
|
@@ -236,8 +217,8 @@ async function fetchRemoteData(context: any, shared: VerificationShared): Promis
|
|
|
236
217
|
taskId: context.task?.metadata?.id || context.task?.id || "",
|
|
237
218
|
...(context.issueIdentity ? { issue: context.issueIdentity } : {}),
|
|
238
219
|
...(context.prIdentity ? { changeRequest: context.prIdentity } : {}),
|
|
239
|
-
includeComments:
|
|
240
|
-
includeFields:
|
|
220
|
+
includeComments: shouldFetchIssueComments(context),
|
|
221
|
+
includeFields: true
|
|
241
222
|
})
|
|
242
223
|
: unsupportedProviderOperation(provider, "verification.fetchRemoteFacts");
|
|
243
224
|
if (!facts.ok) {
|
|
@@ -257,7 +238,7 @@ async function fetchRemoteData(context: any, shared: VerificationShared): Promis
|
|
|
257
238
|
}
|
|
258
239
|
: null;
|
|
259
240
|
let issueFields: any;
|
|
260
|
-
if (
|
|
241
|
+
if (issueSnapshot?.issueType) {
|
|
261
242
|
const fieldKinds = new Map(issueSnapshot.issueType.fields.map((field: { name: string; kind: string }) => [field.name, field.kind]));
|
|
262
243
|
issueFields = {
|
|
263
244
|
pinnedNames: new Set(issueSnapshot.issueType.fields.map((field: { name: string }) => field.name)),
|
|
@@ -265,7 +246,7 @@ async function fetchRemoteData(context: any, shared: VerificationShared): Promis
|
|
|
265
246
|
};
|
|
266
247
|
}
|
|
267
248
|
let prComments = null;
|
|
268
|
-
if (context.prMarker && context.prIdentity &&
|
|
249
|
+
if (context.prMarker && context.prIdentity && shouldFetchPrComments(context) && provider?.comments?.list) {
|
|
269
250
|
const listed = await provider.comments.list({ context: operationContext, parent: context.prIdentity });
|
|
270
251
|
if (!listed.ok) return {
|
|
271
252
|
earlyReturn: listed.error.retryable
|
|
@@ -277,7 +258,7 @@ async function fetchRemoteData(context: any, shared: VerificationShared): Promis
|
|
|
277
258
|
const changeRequest = facts.value.changeRequest;
|
|
278
259
|
let inLabelMapping: Record<string, string[]> = {};
|
|
279
260
|
let repositoryLabels: string[] = [];
|
|
280
|
-
if (context.
|
|
261
|
+
if (platformAuditPolicy('in-labels-computed', context).enabled && context.hasTriage) {
|
|
281
262
|
const mapping = loadInLabelMapping(shared);
|
|
282
263
|
if (!mapping.ok) return { earlyReturn: shared.failResult(CHECK_TYPE, mapping.message, "check_failed") };
|
|
283
264
|
inLabelMapping = mapping.value;
|
|
@@ -298,7 +279,9 @@ async function fetchRemoteData(context: any, shared: VerificationShared): Promis
|
|
|
298
279
|
}
|
|
299
280
|
return {
|
|
300
281
|
issue,
|
|
301
|
-
comments: facts.value.comments.map((comment: { id: string; body: string }) => ({
|
|
282
|
+
comments: facts.value.comments.map((comment: { id: string; body: string; createdSequence: number | null }) => ({
|
|
283
|
+
id: comment.id, body: comment.body, createdSequence: comment.createdSequence
|
|
284
|
+
})),
|
|
302
285
|
prComments,
|
|
303
286
|
prLabels: changeRequest?.labels || null,
|
|
304
287
|
issueType: issueSnapshot
|
|
@@ -319,30 +302,31 @@ function mapTaskTypeToLabel(taskType: any): any {
|
|
|
319
302
|
return taskTypeLabel(taskType);
|
|
320
303
|
}
|
|
321
304
|
|
|
322
|
-
function
|
|
323
|
-
return
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
305
|
+
function hasEnabledAudit(context: any, auditIds: PlatformAuditId[]): boolean {
|
|
306
|
+
return auditIds
|
|
307
|
+
.some((id) => platformAuditPolicy(id as PlatformAuditId, context).enabled);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function shouldFetchIssueComments(context: any): boolean {
|
|
311
|
+
return hasEnabledAudit(context, ['comment-marker', 'comment-content', 'task-comment-content']);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function shouldFetchPrComments(context: any): boolean {
|
|
315
|
+
return hasEnabledAudit(context, ['pr-comment-marker', 'pr-comment-last-commit', 'pr-comment-content']);
|
|
332
316
|
}
|
|
333
317
|
|
|
334
318
|
function checkStatusLabel(context: any, remoteData: any, shared: VerificationShared): any {
|
|
335
319
|
if (!context.expectedStatusLabel || !context.hasTriage) {
|
|
336
|
-
return
|
|
320
|
+
return infoResult('Status label audit is not applicable because no expected label or triage capability is available');
|
|
337
321
|
}
|
|
338
322
|
|
|
339
323
|
if (String(remoteData.issue.state || "").toUpperCase() !== "OPEN") {
|
|
340
|
-
return
|
|
324
|
+
return infoResult('Status label audit is not applicable because the Issue is closed');
|
|
341
325
|
}
|
|
342
326
|
|
|
343
327
|
const labels = extractLabelNames(remoteData.issue.labels);
|
|
344
328
|
if (labels.includes(context.expectedStatusLabel)) {
|
|
345
|
-
return
|
|
329
|
+
return shared.passResult(CHECK_TYPE, `Issue #${context.issueNumber} has expected status label '${context.expectedStatusLabel}'`);
|
|
346
330
|
}
|
|
347
331
|
|
|
348
332
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -352,18 +336,18 @@ function checkStatusLabel(context: any, remoteData: any, shared: VerificationSha
|
|
|
352
336
|
}
|
|
353
337
|
|
|
354
338
|
function checkClosedIssueStatusLabels(context: any, remoteData: any, shared: VerificationShared): any {
|
|
355
|
-
if (!context.
|
|
356
|
-
return
|
|
339
|
+
if (!context.audit.enabled) {
|
|
340
|
+
return infoResult('Closed Issue status-label audit is not enabled for this workflow');
|
|
357
341
|
}
|
|
358
342
|
|
|
359
343
|
if (String(remoteData.issue.state || "").toUpperCase() !== "CLOSED") {
|
|
360
|
-
return
|
|
344
|
+
return infoResult('Closed Issue status-label audit is not applicable because the Issue is open');
|
|
361
345
|
}
|
|
362
346
|
|
|
363
347
|
const statusLabels = extractLabelNames(remoteData.issue.labels)
|
|
364
348
|
.filter((label: any) => label.startsWith("status:"));
|
|
365
349
|
if (statusLabels.length === 0) {
|
|
366
|
-
return
|
|
350
|
+
return shared.passResult(CHECK_TYPE, `Closed Issue #${context.issueNumber} has no status labels`);
|
|
367
351
|
}
|
|
368
352
|
|
|
369
353
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -373,13 +357,28 @@ function checkClosedIssueStatusLabels(context: any, remoteData: any, shared: Ver
|
|
|
373
357
|
}
|
|
374
358
|
|
|
375
359
|
function checkCommentMarker(context: any, remoteData: any, shared: VerificationShared): any {
|
|
376
|
-
if (!context.marker) {
|
|
377
|
-
return
|
|
360
|
+
if (!context.audit.enabled || !context.marker) {
|
|
361
|
+
return infoResult('Issue comment-marker audit is not applicable because no marker is configured');
|
|
378
362
|
}
|
|
379
363
|
|
|
380
364
|
const comment = findCommentByMarker(remoteData.comments, context.marker);
|
|
381
365
|
if (comment) {
|
|
382
|
-
|
|
366
|
+
if (context.marker === MARKERS.summary(context.task.metadata.id)) {
|
|
367
|
+
const managed = remoteData.comments.filter((candidate: any) => {
|
|
368
|
+
const first = String(candidate.body || '').replace(/\r\n/g, '\n').split('\n', 1)[0] || '';
|
|
369
|
+
return first === MARKERS.task(context.task.metadata.id)
|
|
370
|
+
|| first === MARKERS.summary(context.task.metadata.id)
|
|
371
|
+
|| first.startsWith(`<!-- sync-issue:${context.task.metadata.id}:`);
|
|
372
|
+
});
|
|
373
|
+
if (managed.some((candidate: any) => !Number.isSafeInteger(candidate.createdSequence) || candidate.createdSequence < 1)) {
|
|
374
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} summary comment ordering is not provable`, "check_failed");
|
|
375
|
+
}
|
|
376
|
+
const summarySequence = comment.createdSequence;
|
|
377
|
+
if (!managed.every((candidate: any) => summarySequence >= candidate.createdSequence)) {
|
|
378
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} summary comment is not last among task-managed comments`, "check_failed");
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return shared.passResult(CHECK_TYPE, `Issue #${context.issueNumber} has expected comment marker`);
|
|
383
382
|
}
|
|
384
383
|
|
|
385
384
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -389,13 +388,13 @@ function checkCommentMarker(context: any, remoteData: any, shared: VerificationS
|
|
|
389
388
|
}
|
|
390
389
|
|
|
391
390
|
function checkPrCommentMarker(context: any, remoteData: any, shared: VerificationShared): any {
|
|
392
|
-
if (!context.prMarker) {
|
|
393
|
-
return
|
|
391
|
+
if (!context.audit.enabled || !context.prMarker) {
|
|
392
|
+
return infoResult('PR comment-marker audit is not applicable because no marker is configured');
|
|
394
393
|
}
|
|
395
394
|
|
|
396
395
|
const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
|
|
397
396
|
if (comment) {
|
|
398
|
-
return
|
|
397
|
+
return shared.passResult(CHECK_TYPE, `PR #${context.prNumber} has expected comment marker`);
|
|
399
398
|
}
|
|
400
399
|
|
|
401
400
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -405,95 +404,95 @@ function checkPrCommentMarker(context: any, remoteData: any, shared: Verificatio
|
|
|
405
404
|
}
|
|
406
405
|
|
|
407
406
|
function checkPrCommentLastCommit(context: any, remoteData: any, shared: VerificationShared): any {
|
|
408
|
-
if (!context.
|
|
409
|
-
return
|
|
407
|
+
if (!context.audit.enabled) {
|
|
408
|
+
return infoResult('PR last-commit audit is not enabled for this workflow');
|
|
410
409
|
}
|
|
411
410
|
|
|
412
411
|
if (!context.prMarker) {
|
|
413
|
-
return shared.failResult(CHECK_TYPE,
|
|
412
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
414
413
|
"verify_pr_comment_last_commit_matches_head requires expected_pr_comment_marker",
|
|
415
414
|
"check_failed"
|
|
416
|
-
);
|
|
415
|
+
));
|
|
417
416
|
}
|
|
418
417
|
|
|
419
418
|
const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
|
|
420
419
|
if (!comment) {
|
|
421
|
-
return shared.failResult(CHECK_TYPE,
|
|
420
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
422
421
|
`Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`,
|
|
423
422
|
"check_failed"
|
|
424
|
-
);
|
|
423
|
+
));
|
|
425
424
|
}
|
|
426
425
|
|
|
427
426
|
const match = String(comment.body || "").match(/<!--\s*last-commit:\s*([0-9a-f]{7,40})\s*-->/i);
|
|
428
427
|
if (!match) {
|
|
429
|
-
return shared.failResult(CHECK_TYPE,
|
|
428
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
430
429
|
`PR #${context.prNumber} summary comment is missing '<!-- last-commit: <sha> -->' metadata`,
|
|
431
430
|
"check_failed"
|
|
432
|
-
);
|
|
431
|
+
));
|
|
433
432
|
}
|
|
434
433
|
|
|
435
434
|
const expectedHead = String(remoteData.prHeadSha || "").trim();
|
|
436
|
-
if (!expectedHead) return shared.blockedResult(CHECK_TYPE, "Unable to resolve the PR head SHA", "network_error");
|
|
435
|
+
if (!expectedHead) return hardResult(shared.blockedResult(CHECK_TYPE, "Unable to resolve the PR head SHA", "network_error"));
|
|
437
436
|
const actualHead = match[1]!.trim();
|
|
438
437
|
if (expectedHead === actualHead) {
|
|
439
|
-
return
|
|
438
|
+
return shared.passResult(CHECK_TYPE, `PR #${context.prNumber} summary comment last-commit matches HEAD`);
|
|
440
439
|
}
|
|
441
440
|
|
|
442
|
-
return shared.failResult(CHECK_TYPE,
|
|
441
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
443
442
|
`PR #${context.prNumber} summary comment last-commit metadata mismatch: expected ${expectedHead}, got ${actualHead}`,
|
|
444
443
|
"check_failed"
|
|
445
|
-
);
|
|
444
|
+
));
|
|
446
445
|
}
|
|
447
446
|
|
|
448
447
|
function checkPrCommentRequiredPatterns(context: any, remoteData: any, shared: VerificationShared): any {
|
|
449
|
-
const patterns = context.config.expected_pr_comment_required_patterns || [];
|
|
448
|
+
const patterns = context.audit.enabled ? context.config.expected_pr_comment_required_patterns || [] : [];
|
|
450
449
|
if (!Array.isArray(patterns) || patterns.length === 0) {
|
|
451
|
-
return
|
|
450
|
+
return infoResult('PR comment-content audit is not enabled for this workflow');
|
|
452
451
|
}
|
|
453
452
|
|
|
454
453
|
if (!context.prMarker) {
|
|
455
|
-
return shared.failResult(CHECK_TYPE,
|
|
454
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
456
455
|
"expected_pr_comment_required_patterns requires expected_pr_comment_marker",
|
|
457
456
|
"check_failed"
|
|
458
|
-
);
|
|
457
|
+
));
|
|
459
458
|
}
|
|
460
459
|
|
|
461
460
|
const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
|
|
462
461
|
if (!comment) {
|
|
463
|
-
return shared.failResult(CHECK_TYPE,
|
|
462
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
464
463
|
`Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`,
|
|
465
464
|
"check_failed"
|
|
466
|
-
);
|
|
465
|
+
));
|
|
467
466
|
}
|
|
468
467
|
|
|
469
468
|
const body = String(comment.body || "");
|
|
470
469
|
for (const pattern of patterns) {
|
|
471
470
|
const regex = new RegExp(pattern, "m");
|
|
472
471
|
if (!regex.test(body)) {
|
|
473
|
-
return shared.failResult(CHECK_TYPE,
|
|
472
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
474
473
|
`PR #${context.prNumber} summary comment is missing required pattern: ${pattern}`,
|
|
475
474
|
"check_failed"
|
|
476
|
-
);
|
|
475
|
+
));
|
|
477
476
|
}
|
|
478
477
|
}
|
|
479
478
|
|
|
480
|
-
return
|
|
479
|
+
return shared.passResult(CHECK_TYPE, `PR #${context.prNumber} summary comment has all required patterns`);
|
|
481
480
|
}
|
|
482
481
|
|
|
483
482
|
function checkCommentContent(context: any, remoteData: any, shared: VerificationShared): any {
|
|
484
|
-
if (!context.
|
|
485
|
-
return
|
|
483
|
+
if (!context.audit.enabled) {
|
|
484
|
+
return infoResult('Artifact comment-content audit is not enabled for this workflow');
|
|
486
485
|
}
|
|
487
486
|
|
|
488
487
|
if (!context.marker) {
|
|
489
|
-
return shared.failResult(CHECK_TYPE, "
|
|
488
|
+
return hardResult(shared.failResult(CHECK_TYPE, "Artifact comment-content audit requires an expected comment marker", "check_failed"));
|
|
490
489
|
}
|
|
491
490
|
|
|
492
491
|
if (!context.artifactPath || !shared.safeStat(context.artifactPath)) {
|
|
493
|
-
return shared.failResult(CHECK_TYPE,
|
|
492
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
494
493
|
`Artifact not found for comment verification: ${context.artifactFile || "(missing artifactFile)"}`,
|
|
495
494
|
"check_failed"
|
|
496
|
-
);
|
|
495
|
+
));
|
|
497
496
|
}
|
|
498
497
|
|
|
499
498
|
const comment = findCommentByMarker(remoteData.comments, context.marker);
|
|
@@ -503,28 +502,28 @@ function checkCommentContent(context: any, remoteData: any, shared: Verification
|
|
|
503
502
|
const localCanonical = canonicalizeCommentBody(fs.readFileSync(context.artifactPath, "utf8"));
|
|
504
503
|
const commentCanonical = canonicalizeCommentBody(extractCommentBody(comment?.body || ""));
|
|
505
504
|
if (!localCanonical.ok) {
|
|
506
|
-
return shared.failResult(CHECK_TYPE,
|
|
505
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
507
506
|
`Comment content cannot be canonicalized for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${localCanonical.error.message}`,
|
|
508
507
|
"check_failed"
|
|
509
|
-
);
|
|
508
|
+
));
|
|
510
509
|
}
|
|
511
510
|
if (!commentCanonical.ok) {
|
|
512
|
-
return shared.failResult(CHECK_TYPE,
|
|
511
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
513
512
|
`Comment content cannot be canonicalized for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${commentCanonical.error.message}`,
|
|
514
513
|
"check_failed"
|
|
515
|
-
);
|
|
514
|
+
));
|
|
516
515
|
}
|
|
517
516
|
localContent = shared.normalizeContent(localCanonical.value);
|
|
518
517
|
commentContent = shared.normalizeContent(commentCanonical.value);
|
|
519
518
|
} catch (error) {
|
|
520
|
-
return shared.failResult(CHECK_TYPE,
|
|
519
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
521
520
|
`Comment content cannot be read for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${error instanceof Error ? error.message : String(error)}`,
|
|
522
521
|
"check_failed"
|
|
523
|
-
);
|
|
522
|
+
));
|
|
524
523
|
}
|
|
525
524
|
|
|
526
525
|
if (localContent === commentContent) {
|
|
527
|
-
return
|
|
526
|
+
return shared.passResult(CHECK_TYPE, `Artifact comment content matches Issue #${context.issueNumber}`);
|
|
528
527
|
}
|
|
529
528
|
|
|
530
529
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -539,8 +538,8 @@ function checkCommentContent(context: any, remoteData: any, shared: Verification
|
|
|
539
538
|
}
|
|
540
539
|
|
|
541
540
|
function checkTaskCommentContent(context: any, remoteData: any, shared: VerificationShared): any {
|
|
542
|
-
if (!context.
|
|
543
|
-
return
|
|
541
|
+
if (!context.audit.enabled) {
|
|
542
|
+
return infoResult('Task comment-content audit is not enabled for this workflow');
|
|
544
543
|
}
|
|
545
544
|
const taskMarker = `<!-- sync-issue:${context.task.metadata.id}:task -->`;
|
|
546
545
|
const comment = findCommentByMarker(remoteData.comments, taskMarker);
|
|
@@ -559,16 +558,16 @@ function checkTaskCommentContent(context: any, remoteData: any, shared: Verifica
|
|
|
559
558
|
loadProjectLanguage(shared)
|
|
560
559
|
);
|
|
561
560
|
} catch {
|
|
562
|
-
return shared.failResult(CHECK_TYPE, "Task content cannot be rendered safely for comment verification", "check_failed");
|
|
561
|
+
return hardResult(shared.failResult(CHECK_TYPE, "Task content cannot be rendered safely for comment verification", "check_failed"));
|
|
563
562
|
}
|
|
564
563
|
if (renderedTask.byteLength > 60_000) {
|
|
565
|
-
return shared.failResult(CHECK_TYPE, "Task comment exceeds the platform byte limit", "check_failed");
|
|
564
|
+
return hardResult(shared.failResult(CHECK_TYPE, "Task comment exceeds the platform byte limit", "check_failed"));
|
|
566
565
|
}
|
|
567
566
|
const expectedBody = shared.normalizeContent(extractCommentBody(renderedTask.body));
|
|
568
567
|
const commentBody = shared.normalizeContent(extractCommentBody(comment.body || ""));
|
|
569
568
|
|
|
570
569
|
if (expectedBody === commentBody) {
|
|
571
|
-
return
|
|
570
|
+
return shared.passResult(CHECK_TYPE, `Task comment content matches Issue #${context.issueNumber}`);
|
|
572
571
|
}
|
|
573
572
|
|
|
574
573
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -578,17 +577,17 @@ function checkTaskCommentContent(context: any, remoteData: any, shared: Verifica
|
|
|
578
577
|
}
|
|
579
578
|
|
|
580
579
|
function checkPrTypeLabel(context: any, remoteData: any, shared: VerificationShared): any {
|
|
581
|
-
if (!context.
|
|
582
|
-
return
|
|
580
|
+
if (!context.audit.enabled || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
|
|
581
|
+
return infoResult('PR type-label audit is not applicable because the required PR metadata or capability is unavailable');
|
|
583
582
|
}
|
|
584
583
|
|
|
585
584
|
const expectedLabel = mapTaskTypeToLabel(context.task.metadata.type);
|
|
586
585
|
if (!expectedLabel) {
|
|
587
|
-
return
|
|
586
|
+
return infoResult('PR type-label audit is not applicable because the task type has no label mapping');
|
|
588
587
|
}
|
|
589
588
|
|
|
590
589
|
if (remoteData.prLabels.includes(expectedLabel)) {
|
|
591
|
-
return
|
|
590
|
+
return shared.passResult(CHECK_TYPE, `PR #${context.prNumber} has expected type label '${expectedLabel}'`);
|
|
592
591
|
}
|
|
593
592
|
|
|
594
593
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -598,8 +597,8 @@ function checkPrTypeLabel(context: any, remoteData: any, shared: VerificationSha
|
|
|
598
597
|
}
|
|
599
598
|
|
|
600
599
|
function checkInLabelsMatchPr(context: any, remoteData: any, shared: VerificationShared): any {
|
|
601
|
-
if (!context.
|
|
602
|
-
return
|
|
600
|
+
if (!context.audit.enabled || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
|
|
601
|
+
return infoResult('PR in: label audit is not applicable because the required PR metadata or capability is unavailable');
|
|
603
602
|
}
|
|
604
603
|
|
|
605
604
|
const issueInLabels = extractLabelNames(remoteData.issue.labels)
|
|
@@ -610,7 +609,7 @@ function checkInLabelsMatchPr(context: any, remoteData: any, shared: Verificatio
|
|
|
610
609
|
.sort();
|
|
611
610
|
|
|
612
611
|
if (arraysEqual(issueInLabels, prInLabels)) {
|
|
613
|
-
return
|
|
612
|
+
return shared.passResult(CHECK_TYPE, `PR #${context.prNumber} in: labels match Issue #${context.issueNumber}`);
|
|
614
613
|
}
|
|
615
614
|
|
|
616
615
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -620,8 +619,8 @@ function checkInLabelsMatchPr(context: any, remoteData: any, shared: Verificatio
|
|
|
620
619
|
}
|
|
621
620
|
|
|
622
621
|
function checkInLabelsComputed(context: any, remoteData: any, shared: VerificationShared): any {
|
|
623
|
-
if (!context.
|
|
624
|
-
return
|
|
622
|
+
if (!context.audit.enabled || !context.hasTriage) {
|
|
623
|
+
return infoResult('Computed in: label audit is not applicable because it is disabled or triage capability is unavailable');
|
|
625
624
|
}
|
|
626
625
|
|
|
627
626
|
const expectedInLabels = computeExpectedInLabels(context.taskDir, remoteData.repositoryLabels, remoteData.inLabelMapping, shared);
|
|
@@ -632,7 +631,7 @@ function checkInLabelsComputed(context: any, remoteData: any, shared: Verificati
|
|
|
632
631
|
}
|
|
633
632
|
|
|
634
633
|
if (expectedInLabels.mode === "skipped") {
|
|
635
|
-
return
|
|
634
|
+
return infoResult('Computed in: label audit is not applicable because no mapped changed path exists');
|
|
636
635
|
}
|
|
637
636
|
|
|
638
637
|
const actualInLabels = extractLabelNames(remoteData.issue.labels)
|
|
@@ -640,7 +639,7 @@ function checkInLabelsComputed(context: any, remoteData: any, shared: Verificati
|
|
|
640
639
|
.sort();
|
|
641
640
|
|
|
642
641
|
if (arraysEqual(expectedInLabels.labels, actualInLabels)) {
|
|
643
|
-
return
|
|
642
|
+
return shared.passResult(CHECK_TYPE, `Issue #${context.issueNumber} in: labels match committed changes`);
|
|
644
643
|
}
|
|
645
644
|
|
|
646
645
|
return shared.failResult(
|
|
@@ -651,13 +650,13 @@ function checkInLabelsComputed(context: any, remoteData: any, shared: Verificati
|
|
|
651
650
|
}
|
|
652
651
|
|
|
653
652
|
function checkSyncedRequirements(context: any, remoteData: any, shared: VerificationShared): any {
|
|
654
|
-
if (!context.
|
|
655
|
-
return
|
|
653
|
+
if (!context.audit.enabled || !context.hasTriage) {
|
|
654
|
+
return infoResult('Requirements audit is not applicable because it is disabled or triage capability is unavailable');
|
|
656
655
|
}
|
|
657
656
|
|
|
658
657
|
const checkedRequirements = shared.getCheckedRequirements(context.task.content);
|
|
659
658
|
if (checkedRequirements.length === 0) {
|
|
660
|
-
return
|
|
659
|
+
return infoResult('Requirements audit is not applicable because the task has no checked requirements');
|
|
661
660
|
}
|
|
662
661
|
|
|
663
662
|
const issueBody = remoteData.issue.body || "";
|
|
@@ -666,20 +665,20 @@ function checkSyncedRequirements(context: any, remoteData: any, shared: Verifica
|
|
|
666
665
|
requirementSectionAnchors(shared.repoRoot, context.task.metadata.type || "task")
|
|
667
666
|
);
|
|
668
667
|
if (resolution.status === "missing") {
|
|
669
|
-
return
|
|
668
|
+
return infoResult('Requirements audit is not applicable because the Issue has no requirements section');
|
|
670
669
|
}
|
|
671
670
|
if (resolution.status === "ambiguous") {
|
|
672
|
-
return shared.failResult(CHECK_TYPE,
|
|
671
|
+
return hardResult(shared.failResult(CHECK_TYPE,
|
|
673
672
|
`Issue #${context.issueNumber} requirements section is ambiguous`,
|
|
674
673
|
"check_failed"
|
|
675
|
-
);
|
|
674
|
+
));
|
|
676
675
|
}
|
|
677
676
|
const requirementBody = issueBody.slice(resolution.bodyStart, resolution.bodyEnd);
|
|
678
677
|
const missingRequirements = checkedRequirements.filter(
|
|
679
678
|
(item: any) => !hasCheckedRequirement(requirementBody, item)
|
|
680
679
|
);
|
|
681
680
|
if (missingRequirements.length === 0) {
|
|
682
|
-
return
|
|
681
|
+
return shared.passResult(CHECK_TYPE, `Issue #${context.issueNumber} contains all checked requirements`);
|
|
683
682
|
}
|
|
684
683
|
|
|
685
684
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -689,17 +688,17 @@ function checkSyncedRequirements(context: any, remoteData: any, shared: Verifica
|
|
|
689
688
|
}
|
|
690
689
|
|
|
691
690
|
function checkIssueType(context: any, remoteData: any, shared: VerificationShared): any {
|
|
692
|
-
if (!context.
|
|
693
|
-
return
|
|
691
|
+
if (!context.hasPush) {
|
|
692
|
+
return infoResult('Issue Type audit is not applicable because push capability is unavailable');
|
|
694
693
|
}
|
|
695
694
|
|
|
696
695
|
if (remoteData.issueType === undefined) {
|
|
697
|
-
return
|
|
696
|
+
return infoResult('Issue Type audit is not applicable because the provider does not expose Issue Type');
|
|
698
697
|
}
|
|
699
698
|
|
|
700
699
|
if (!remoteData.issueType) {
|
|
701
700
|
if (context.repoOwnerType === "User") {
|
|
702
|
-
return
|
|
701
|
+
return infoResult('Issue Type audit is not applicable for a user-owned repository');
|
|
703
702
|
}
|
|
704
703
|
|
|
705
704
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -716,16 +715,16 @@ function checkIssueType(context: any, remoteData: any, shared: VerificationShare
|
|
|
716
715
|
);
|
|
717
716
|
}
|
|
718
717
|
|
|
719
|
-
return
|
|
718
|
+
return shared.passResult(CHECK_TYPE, `Issue #${context.issueNumber} has the expected Issue Type`);
|
|
720
719
|
}
|
|
721
720
|
|
|
722
721
|
function checkIssueFields(context: any, remoteData: any, shared: VerificationShared): any {
|
|
723
|
-
if (!context.
|
|
724
|
-
return
|
|
722
|
+
if (!context.hasPush) {
|
|
723
|
+
return infoResult('Issue field audit is not applicable because push capability is unavailable');
|
|
725
724
|
}
|
|
726
725
|
|
|
727
726
|
if (remoteData.issueFields === undefined) {
|
|
728
|
-
return
|
|
727
|
+
return infoResult('Issue field audit is not applicable because the provider does not expose Issue fields');
|
|
729
728
|
}
|
|
730
729
|
|
|
731
730
|
for (const [metadataKey, fieldName] of Object.entries(FRONTMATTER_FIELD_MAP)) {
|
|
@@ -755,12 +754,12 @@ function checkIssueFields(context: any, remoteData: any, shared: VerificationSha
|
|
|
755
754
|
}
|
|
756
755
|
}
|
|
757
756
|
|
|
758
|
-
return
|
|
757
|
+
return shared.passResult(CHECK_TYPE, `Issue #${context.issueNumber} fields match task metadata`);
|
|
759
758
|
}
|
|
760
759
|
|
|
761
760
|
function checkPrAssignee(context: any, remoteData: any, shared: VerificationShared): any {
|
|
762
|
-
if (!context.
|
|
763
|
-
return
|
|
761
|
+
if (!context.audit.enabled || !context.hasPush || !context.prNumber) {
|
|
762
|
+
return infoResult('PR assignee audit is not applicable because it is disabled or required PR metadata/capability is unavailable');
|
|
764
763
|
}
|
|
765
764
|
|
|
766
765
|
if (!remoteData.prAssignees || remoteData.prAssignees.length === 0) {
|
|
@@ -770,12 +769,12 @@ function checkPrAssignee(context: any, remoteData: any, shared: VerificationShar
|
|
|
770
769
|
);
|
|
771
770
|
}
|
|
772
771
|
|
|
773
|
-
return
|
|
772
|
+
return shared.passResult(CHECK_TYPE, `PR #${context.prNumber} has an assignee`);
|
|
774
773
|
}
|
|
775
774
|
|
|
776
775
|
function checkMilestone(context: any, remoteData: any, shared: VerificationShared): any {
|
|
777
|
-
if (!context.
|
|
778
|
-
return
|
|
776
|
+
if (!context.hasTriage) {
|
|
777
|
+
return infoResult('Milestone audit is not applicable because triage capability is unavailable');
|
|
779
778
|
}
|
|
780
779
|
|
|
781
780
|
if (!remoteData.issue?.milestone?.title) {
|
|
@@ -792,7 +791,7 @@ function checkMilestone(context: any, remoteData: any, shared: VerificationShare
|
|
|
792
791
|
);
|
|
793
792
|
}
|
|
794
793
|
|
|
795
|
-
if (context.
|
|
794
|
+
if (context.audit.requireSpecificMilestone) {
|
|
796
795
|
const issueTitle = remoteData.issue.milestone.title;
|
|
797
796
|
if (VERSION_LINE_REGEX.test(issueTitle)) {
|
|
798
797
|
return shared.failResult(CHECK_TYPE,
|
|
@@ -808,7 +807,7 @@ function checkMilestone(context: any, remoteData: any, shared: VerificationShare
|
|
|
808
807
|
}
|
|
809
808
|
}
|
|
810
809
|
|
|
811
|
-
return
|
|
810
|
+
return shared.passResult(CHECK_TYPE, `Issue #${context.issueNumber} milestone satisfies the configured policy`);
|
|
812
811
|
}
|
|
813
812
|
|
|
814
813
|
function findCommentByMarker(comments: any, marker: any): any {
|