@fitlab-ai/agent-infra 0.9.14 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/README.zh-CN.md +10 -0
- package/bin/internal-cli.sh +45 -0
- package/dist/bin/internal-cli.js +93 -30
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +9 -4
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +39 -0
- package/dist/lib/fs/file-lock.js +111 -0
- package/dist/lib/host-control/audit.js +19 -0
- package/dist/lib/host-control/client.js +142 -0
- package/dist/lib/host-control/command.js +49 -0
- package/dist/lib/host-control/path.js +160 -0
- package/dist/lib/host-control/secure-fs.js +140 -0
- package/dist/lib/host-control/server.js +159 -0
- package/dist/lib/host-control/service.js +112 -0
- package/dist/lib/init.js +11 -2
- package/dist/lib/internal/cli-route-inventory.js +4 -2
- package/dist/lib/internal/codex-lifecycle.js +6 -6
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +331 -0
- package/dist/lib/internal/platform-pr.js +18 -4
- package/dist/lib/internal/release-workflow.js +63 -84
- package/dist/lib/internal/sandbox-control.js +149 -2
- package/dist/lib/internal/task-artifact.js +16 -263
- package/dist/lib/internal/task-create.js +6 -5
- package/dist/lib/internal/task-event.js +37 -37
- package/dist/lib/internal/task-lifecycle.js +2 -2
- package/dist/lib/internal/task-operation-registry.js +101 -4
- package/dist/lib/internal/task-review.js +11 -56
- package/dist/lib/internal/task-validate.js +30 -10
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +4 -1
- package/dist/lib/platform/completion-backfill.js +1 -1
- package/dist/lib/platform/github-data.js +241 -0
- package/dist/lib/platform/github-provider.js +15 -20
- package/dist/lib/platform/issue-comments.js +18 -12
- package/dist/lib/platform/issues.js +2 -180
- package/dist/lib/platform/pr-change-report.js +5 -4
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +117 -33
- package/dist/lib/platform/pull-requests.js +5 -108
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/task-identities.js +2 -9
- package/dist/lib/platform/verification-sync.js +132 -143
- package/dist/lib/pr-review/evidence-grading.js +4 -8
- package/dist/lib/release/workflow.js +1 -1
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/rm.js +19 -1511
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +93 -54
- package/dist/lib/sandbox/control/executor.js +129 -65
- package/dist/lib/sandbox/control/finalization-response.js +27 -0
- package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
- package/dist/lib/sandbox/control/lifecycle.js +36 -2
- package/dist/lib/sandbox/control/native-file-lock.js +6 -98
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +518 -127
- package/dist/lib/sandbox/control/state.js +64 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +89 -0
- package/dist/lib/sandbox/index.js +1 -2
- package/dist/lib/sandbox/recovery.js +28 -4
- package/dist/lib/sandbox/removal.js +822 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-identity.js +17 -1
- package/dist/lib/sandbox/workspace-view.js +13 -14
- package/dist/lib/task/activity-intent.js +2 -1
- package/dist/lib/task/activity-log.js +30 -24
- package/dist/lib/task/artifact-command.js +79 -0
- package/dist/lib/task/artifact-lifecycle.js +30 -28
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +29 -289
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-recovery.js +430 -0
- package/dist/lib/task/artifact-repair-intent.js +134 -29
- package/dist/lib/task/artifact-schema.js +6 -14
- package/dist/lib/task/capabilities.js +12 -19
- package/dist/lib/task/codex-orchestration.js +71 -25
- package/dist/lib/task/commit-identity.js +28 -0
- package/dist/lib/task/commit-operation.js +33 -20
- package/dist/lib/task/control-authority.js +403 -6
- package/dist/lib/task/control-recovery.js +140 -0
- package/dist/lib/task/create-service.js +1 -8
- package/dist/lib/task/create.js +62 -9
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/delegation-receipts.js +64 -3
- package/dist/lib/task/events.js +275 -22
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +8 -14
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/intermediate-cleanup.js +441 -0
- package/dist/lib/task/invalidation.js +18 -65
- package/dist/lib/task/ledger-intents.js +7 -22
- package/dist/lib/task/ledger.js +1 -3
- package/dist/lib/task/lifecycle-recovery.js +549 -0
- package/dist/lib/task/lifecycle.js +31 -4
- package/dist/lib/task/local-artifact-finalization.js +197 -197
- package/dist/lib/task/manual-validation-completion.js +26 -0
- package/dist/lib/task/manual-validation-evidence.js +130 -0
- package/dist/lib/task/manual-validation-receipt.js +105 -0
- package/dist/lib/task/manual-validation-shared.js +40 -0
- package/dist/lib/task/manual-validation-transaction.js +243 -0
- package/dist/lib/task/markdown.js +47 -0
- package/dist/lib/task/orchestration.js +66 -19
- package/dist/lib/task/pr-delivery-fact.js +12 -56
- package/dist/lib/task/qualification-audit.js +20 -34
- package/dist/lib/task/recovery-warning.js +19 -0
- package/dist/lib/task/review-artifacts.js +2 -18
- package/dist/lib/task/review-command.js +26 -0
- package/dist/lib/task/review-finalization.js +175 -216
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -56
- package/dist/lib/task/short-id.js +15 -7
- package/dist/lib/task/task-execution-lock.js +1 -1
- package/dist/lib/task/verification-engine.js +119 -337
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +16 -0
- package/dist/lib/task/workflow-warning-intents.js +0 -16
- package/dist/lib/task/workflow-warnings.js +28 -2
- package/dist/lib/update.js +2 -0
- package/dist/lib/version.js +1 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +10 -4
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
- package/lib/fs/durable-write.ts +30 -0
- package/lib/fs/file-lock.ts +113 -0
- package/lib/host-control/audit.ts +22 -0
- package/lib/host-control/client.ts +172 -0
- package/lib/host-control/command.ts +60 -0
- package/lib/host-control/path.ts +167 -0
- package/lib/host-control/secure-fs.ts +135 -0
- package/lib/host-control/server.ts +180 -0
- package/lib/host-control/service.ts +133 -0
- package/lib/init.ts +15 -2
- package/lib/internal/cli-route-inventory.ts +4 -2
- package/lib/internal/codex-lifecycle.ts +6 -6
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +315 -0
- package/lib/internal/platform-pr.ts +15 -4
- package/lib/internal/release-workflow.ts +74 -82
- package/lib/internal/sandbox-control.ts +162 -1
- package/lib/internal/task-artifact.ts +14 -217
- package/lib/internal/task-create.ts +5 -5
- package/lib/internal/task-event.ts +20 -11
- package/lib/internal/task-lifecycle.ts +4 -3
- package/lib/internal/task-operation-registry.ts +111 -4
- package/lib/internal/task-review.ts +9 -54
- package/lib/internal/task-validate.ts +26 -10
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +5 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +15 -20
- package/lib/platform/issue-comments.ts +21 -14
- package/lib/platform/issues.ts +5 -246
- package/lib/platform/pr-change-report.ts +7 -5
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +123 -35
- package/lib/platform/pull-requests.ts +6 -173
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/task-identities.ts +3 -14
- package/lib/platform/verification-sync.ts +150 -156
- package/lib/pr-review/evidence-grading.ts +4 -9
- package/lib/release/workflow.ts +3 -3
- package/lib/sandbox/commands/create.ts +7 -6
- package/lib/sandbox/commands/rm.ts +21 -1895
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +109 -66
- package/lib/sandbox/control/executor.ts +134 -74
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +45 -3
- package/lib/sandbox/control/native-file-lock.ts +6 -107
- package/lib/sandbox/control/protocol.ts +80 -6
- package/lib/sandbox/control/server.ts +567 -132
- package/lib/sandbox/control/state.ts +86 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +92 -0
- package/lib/sandbox/index.ts +1 -2
- package/lib/sandbox/recovery.ts +28 -5
- package/lib/sandbox/removal.ts +1142 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-identity.ts +19 -3
- package/lib/sandbox/workspace-view.ts +17 -15
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +85 -0
- package/lib/task/artifact-lifecycle.ts +31 -26
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +34 -310
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-recovery.ts +519 -0
- package/lib/task/artifact-repair-intent.ts +162 -33
- package/lib/task/artifact-schema.ts +5 -34
- package/lib/task/capabilities.ts +13 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +37 -20
- package/lib/task/control-authority.ts +540 -10
- package/lib/task/control-recovery.ts +186 -0
- package/lib/task/create-service.ts +1 -10
- package/lib/task/create.ts +63 -8
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/delegation-receipts.ts +73 -2
- package/lib/task/events.ts +309 -24
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +8 -16
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/intermediate-cleanup.ts +493 -0
- package/lib/task/invalidation.ts +18 -62
- package/lib/task/ledger-intents.ts +7 -24
- package/lib/task/ledger.ts +1 -3
- package/lib/task/lifecycle-recovery.ts +691 -0
- package/lib/task/lifecycle.ts +30 -5
- package/lib/task/local-artifact-finalization.ts +244 -234
- package/lib/task/manual-validation-completion.ts +40 -0
- package/lib/task/manual-validation-evidence.ts +209 -0
- package/lib/task/manual-validation-receipt.ts +156 -0
- package/lib/task/manual-validation-shared.ts +50 -0
- package/lib/task/manual-validation-transaction.ts +304 -0
- package/lib/task/markdown.ts +70 -0
- package/lib/task/orchestration.ts +84 -19
- package/lib/task/pr-delivery-fact.ts +15 -62
- package/lib/task/qualification-audit.ts +22 -33
- package/lib/task/recovery-warning.ts +21 -0
- package/lib/task/review-artifacts.ts +1 -20
- package/lib/task/review-command.ts +24 -0
- package/lib/task/review-finalization.ts +183 -292
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -54
- package/lib/task/short-id.ts +27 -5
- package/lib/task/task-execution-lock.ts +1 -1
- package/lib/task/verification-engine.ts +113 -383
- package/lib/task/verification-types.ts +15 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +17 -0
- package/lib/task/workflow-warning-intents.ts +0 -13
- package/lib/task/workflow-warnings.ts +21 -2
- package/lib/update.ts +6 -0
- package/lib/version.ts +1 -15
- package/package.json +6 -6
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +4 -2
- package/templates/.agents/rules/issue-sync.zh-CN.md +4 -3
- package/templates/.agents/rules/local-artifact-repair.en.md +46 -39
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +43 -36
- package/templates/.agents/rules/testing-discipline.en.md +2 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
- package/templates/.agents/scripts/package.json +3 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +33 -21
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +33 -21
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/config/verify.json +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/import-issue/config/verify.json +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +7 -3
- package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
- package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/restore-task/config/verify.json +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
- package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
- package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +20 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +17 -6
- package/templates/.agents/skills/test/SKILL.zh-CN.md +17 -6
- package/templates/.agents/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/import-issue.en.md +1 -1
- package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
- package/templates/.claude/commands/restore-task.en.md +1 -1
- package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
- package/templates/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
|
@@ -10,6 +10,8 @@ import { planInLabelUpdate, validateInLabelMapping } from "./in-label-sync.js";
|
|
|
10
10
|
import { readPrDeliveryFact } from "../task/pr-delivery-fact.js";
|
|
11
11
|
import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.js";
|
|
12
12
|
import { taskIssueIdentity } from "./task-identities.js";
|
|
13
|
+
import { isTaskCommentTooLarge } from "./issue-comments.js";
|
|
14
|
+
import { CONTROL_MARKER_PATTERN, canonicalizeCommentBody, renderSafeCodeFence, sanitizeMarkdownDocument } from "./comment-safety.js";
|
|
13
15
|
const CHECK_TYPE = "platform-sync";
|
|
14
16
|
const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
|
|
15
17
|
const FRONTMATTER_FIELD_MAP = {
|
|
@@ -24,8 +26,6 @@ const OPTION_LOCALIZATION = {
|
|
|
24
26
|
"中": "Medium",
|
|
25
27
|
"低": "Low"
|
|
26
28
|
};
|
|
27
|
-
let activeShared = null;
|
|
28
|
-
let repoRoot = "";
|
|
29
29
|
export function getDefaults() {
|
|
30
30
|
return {
|
|
31
31
|
statusLabels: {
|
|
@@ -45,47 +45,16 @@ export function getDefaults() {
|
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
return activeShared;
|
|
53
|
-
}
|
|
54
|
-
function loadTask(...args) {
|
|
55
|
-
return getShared().loadTask(...args);
|
|
56
|
-
}
|
|
57
|
-
function getCheckedRequirements(...args) {
|
|
58
|
-
return getShared().getCheckedRequirements(...args);
|
|
59
|
-
}
|
|
60
|
-
function normalizeContent(...args) {
|
|
61
|
-
return getShared().normalizeContent(...args);
|
|
62
|
-
}
|
|
63
|
-
function isBlank(...args) {
|
|
64
|
-
return getShared().isBlank(...args);
|
|
65
|
-
}
|
|
66
|
-
function escapeRegExp(...args) {
|
|
67
|
-
return getShared().escapeRegExp(...args);
|
|
68
|
-
}
|
|
69
|
-
function passResult(...args) {
|
|
70
|
-
return getShared().passResult(...args);
|
|
71
|
-
}
|
|
72
|
-
function failResult(...args) {
|
|
73
|
-
return getShared().failResult(...args);
|
|
74
|
-
}
|
|
75
|
-
function blockedResult(...args) {
|
|
76
|
-
return getShared().blockedResult(...args);
|
|
77
|
-
}
|
|
78
|
-
function safeStat(...args) {
|
|
79
|
-
return getShared().safeStat(...args);
|
|
48
|
+
function sanitizeCommentContent(content) {
|
|
49
|
+
const result = sanitizeMarkdownDocument(content, { reservedMarkers: [CONTROL_MARKER_PATTERN] });
|
|
50
|
+
return result.ok ? result.value : null;
|
|
80
51
|
}
|
|
81
52
|
export async function check({ taskDir, config, artifactFile }, shared) {
|
|
82
|
-
|
|
83
|
-
repoRoot = shared.repoRoot;
|
|
84
|
-
const context = await buildSyncContext({ taskDir, config, artifactFile });
|
|
53
|
+
const context = await buildSyncContext({ taskDir, config, artifactFile }, shared);
|
|
85
54
|
if (context.earlyReturn) {
|
|
86
55
|
return context.earlyReturn;
|
|
87
56
|
}
|
|
88
|
-
const remoteData = await fetchRemoteData(context);
|
|
57
|
+
const remoteData = await fetchRemoteData(context, shared);
|
|
89
58
|
if (remoteData.earlyReturn) {
|
|
90
59
|
return remoteData.earlyReturn;
|
|
91
60
|
}
|
|
@@ -108,52 +77,52 @@ export async function check({ taskDir, config, artifactFile }, shared) {
|
|
|
108
77
|
checkMilestone
|
|
109
78
|
];
|
|
110
79
|
for (const subCheck of subChecks) {
|
|
111
|
-
const result = subCheck(context, remoteData);
|
|
80
|
+
const result = subCheck(context, remoteData, shared);
|
|
112
81
|
if (result) {
|
|
113
82
|
return result;
|
|
114
83
|
}
|
|
115
84
|
}
|
|
116
|
-
return passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
|
|
85
|
+
return shared.passResult(CHECK_TYPE, `Platform sync checks passed for Issue ${context.issueNumber || "identity"}`);
|
|
117
86
|
}
|
|
118
|
-
async function buildSyncContext({ taskDir, config, artifactFile }) {
|
|
119
|
-
const task = loadTask(taskDir);
|
|
87
|
+
async function buildSyncContext({ taskDir, config, artifactFile }, shared) {
|
|
88
|
+
const task = shared.loadTask(taskDir);
|
|
120
89
|
if (!task.ok) {
|
|
121
|
-
return { earlyReturn: failResult(CHECK_TYPE, task.message) };
|
|
90
|
+
return { earlyReturn: shared.failResult(CHECK_TYPE, task.message) };
|
|
122
91
|
}
|
|
123
92
|
const issueIdentity = taskIssueIdentity(task.metadata);
|
|
124
93
|
const issueNumber = resourceIdentityNumber(issueIdentity);
|
|
125
94
|
const fact = readPrDeliveryFact(task.metadata);
|
|
126
95
|
if (fact.status === "invalid") {
|
|
127
|
-
return { earlyReturn: failResult(CHECK_TYPE, fact.error.message, "check_failed") };
|
|
96
|
+
return { earlyReturn: shared.failResult(CHECK_TYPE, fact.error.message, "check_failed") };
|
|
128
97
|
}
|
|
129
98
|
const prIdentity = fact.status === "valid" && fact.fact.state === "bound" ? fact.fact.identity.resource : null;
|
|
130
99
|
const prNumber = resourceIdentityNumber(prIdentity);
|
|
131
|
-
if (config.when === "
|
|
132
|
-
return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no
|
|
100
|
+
if (config.when === "platform_issue_identity_exists" && !issueIdentity) {
|
|
101
|
+
return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no platform_issue_identity") };
|
|
133
102
|
}
|
|
134
103
|
if (config.when === "pr_fact_bound" && !prIdentity) {
|
|
135
|
-
return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
|
|
104
|
+
return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
|
|
136
105
|
}
|
|
137
106
|
if (!issueIdentity) {
|
|
138
|
-
return { earlyReturn: passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
|
|
107
|
+
return { earlyReturn: shared.passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
|
|
139
108
|
}
|
|
140
|
-
const loaded = await resolvePlatformProviderContext({ cwd: repoRoot });
|
|
109
|
+
const loaded = await resolvePlatformProviderContext({ cwd: shared.repoRoot });
|
|
141
110
|
const platformContext = loaded.ok ? loaded.value.context : loaded.context;
|
|
142
111
|
if (platformContext.status === "failed") {
|
|
143
|
-
return { earlyReturn: failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
|
|
112
|
+
return { earlyReturn: shared.failResult(CHECK_TYPE, platformContext.error?.message || "Platform context failed", "check_failed") };
|
|
144
113
|
}
|
|
145
114
|
if (platformContext.status === "blocked") {
|
|
146
|
-
return { earlyReturn: blockedResult(CHECK_TYPE, platformContext.error?.message || "Platform context blocked", "network_error") };
|
|
115
|
+
return { earlyReturn: shared.blockedResult(CHECK_TYPE, platformContext.error?.message || "Platform context blocked", "network_error") };
|
|
147
116
|
}
|
|
148
117
|
if (!platformContext.platform.repository) {
|
|
149
118
|
if (platformContext.error?.code === "REMOTE_MISSING" || platformContext.error?.code === "REMOTE_INVALID") {
|
|
150
|
-
return { earlyReturn: blockedResult(CHECK_TYPE, platformContext.error.message, "network_error") };
|
|
119
|
+
return { earlyReturn: shared.blockedResult(CHECK_TYPE, platformContext.error.message, "network_error") };
|
|
151
120
|
}
|
|
152
|
-
return { earlyReturn: passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
|
|
121
|
+
return { earlyReturn: shared.passResult(CHECK_TYPE, `Skipped: ${platformContext.error?.message || "platform unavailable"}`) };
|
|
153
122
|
}
|
|
154
123
|
const expectedValues = resolveExpectedValues(config);
|
|
155
124
|
if (!expectedValues.ok) {
|
|
156
|
-
return { earlyReturn: failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
|
|
125
|
+
return { earlyReturn: shared.failResult(CHECK_TYPE, expectedValues.message, "check_failed") };
|
|
157
126
|
}
|
|
158
127
|
const marker = expectedValues.commentMarker
|
|
159
128
|
? interpolate(expectedValues.commentMarker, taskDir, artifactFile)
|
|
@@ -229,7 +198,7 @@ function resolveDefaultValue({ collection, key, value, configKey }) {
|
|
|
229
198
|
}
|
|
230
199
|
return { ok: true, value: resolvedValue };
|
|
231
200
|
}
|
|
232
|
-
async function fetchRemoteData(context) {
|
|
201
|
+
async function fetchRemoteData(context, shared) {
|
|
233
202
|
const provider = context.provider;
|
|
234
203
|
const operationContext = providerOperationContext(context.loadedContext, context.taskDir);
|
|
235
204
|
const facts = provider?.verification?.fetchRemoteFacts
|
|
@@ -245,8 +214,8 @@ async function fetchRemoteData(context) {
|
|
|
245
214
|
if (!facts.ok) {
|
|
246
215
|
return {
|
|
247
216
|
earlyReturn: facts.error.retryable
|
|
248
|
-
? blockedResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "network_error")
|
|
249
|
-
: failResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "check_failed")
|
|
217
|
+
? shared.blockedResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "network_error")
|
|
218
|
+
: shared.failResult(CHECK_TYPE, providerError(facts.error, "PLATFORM_PROVIDER_OPERATION_FAILED").message, "check_failed")
|
|
250
219
|
};
|
|
251
220
|
}
|
|
252
221
|
const issueSnapshot = facts.value.issue;
|
|
@@ -272,8 +241,8 @@ async function fetchRemoteData(context) {
|
|
|
272
241
|
if (!listed.ok)
|
|
273
242
|
return {
|
|
274
243
|
earlyReturn: listed.error.retryable
|
|
275
|
-
? blockedResult(CHECK_TYPE, listed.error.message, "network_error")
|
|
276
|
-
: failResult(CHECK_TYPE, listed.error.message, "check_failed")
|
|
244
|
+
? shared.blockedResult(CHECK_TYPE, listed.error.message, "network_error")
|
|
245
|
+
: shared.failResult(CHECK_TYPE, listed.error.message, "check_failed")
|
|
277
246
|
};
|
|
278
247
|
prComments = listed.value.map((comment) => ({ id: comment.id, body: comment.body }));
|
|
279
248
|
}
|
|
@@ -281,9 +250,9 @@ async function fetchRemoteData(context) {
|
|
|
281
250
|
let inLabelMapping = {};
|
|
282
251
|
let repositoryLabels = [];
|
|
283
252
|
if (context.config.verify_in_labels_computed && context.hasTriage) {
|
|
284
|
-
const mapping = loadInLabelMapping();
|
|
253
|
+
const mapping = loadInLabelMapping(shared);
|
|
285
254
|
if (!mapping.ok)
|
|
286
|
-
return { earlyReturn: failResult(CHECK_TYPE, mapping.message, "check_failed") };
|
|
255
|
+
return { earlyReturn: shared.failResult(CHECK_TYPE, mapping.message, "check_failed") };
|
|
287
256
|
inLabelMapping = mapping.value;
|
|
288
257
|
if (Object.keys(inLabelMapping).length > 0) {
|
|
289
258
|
const labels = provider?.issues?.listLabels
|
|
@@ -293,8 +262,8 @@ async function fetchRemoteData(context) {
|
|
|
293
262
|
const error = providerError(labels.error, "PLATFORM_PROVIDER_OPERATION_FAILED");
|
|
294
263
|
return {
|
|
295
264
|
earlyReturn: error.retryable
|
|
296
|
-
? blockedResult(CHECK_TYPE, `${error.code}: ${error.message}`, "network_error")
|
|
297
|
-
: failResult(CHECK_TYPE, `${error.code}: ${error.message}`, "check_failed")
|
|
265
|
+
? shared.blockedResult(CHECK_TYPE, `${error.code}: ${error.message}`, "network_error")
|
|
266
|
+
: shared.failResult(CHECK_TYPE, `${error.code}: ${error.message}`, "check_failed")
|
|
298
267
|
};
|
|
299
268
|
}
|
|
300
269
|
repositoryLabels = labels.value;
|
|
@@ -330,7 +299,7 @@ function shouldFetchComments(config) {
|
|
|
330
299
|
|| config.verify_comment_content
|
|
331
300
|
|| config.verify_task_comment_content);
|
|
332
301
|
}
|
|
333
|
-
function checkStatusLabel(context, remoteData) {
|
|
302
|
+
function checkStatusLabel(context, remoteData, shared) {
|
|
334
303
|
if (!context.expectedStatusLabel || !context.hasTriage) {
|
|
335
304
|
return null;
|
|
336
305
|
}
|
|
@@ -341,9 +310,9 @@ function checkStatusLabel(context, remoteData) {
|
|
|
341
310
|
if (labels.includes(context.expectedStatusLabel)) {
|
|
342
311
|
return null;
|
|
343
312
|
}
|
|
344
|
-
return failResult(CHECK_TYPE, `Expected label '${context.expectedStatusLabel}' not found on Issue #${context.issueNumber}`, "check_failed");
|
|
313
|
+
return shared.failResult(CHECK_TYPE, `Expected label '${context.expectedStatusLabel}' not found on Issue #${context.issueNumber}`, "check_failed");
|
|
345
314
|
}
|
|
346
|
-
function checkClosedIssueStatusLabels(context, remoteData) {
|
|
315
|
+
function checkClosedIssueStatusLabels(context, remoteData, shared) {
|
|
347
316
|
if (!context.config.verify_closed_issue_has_no_status_labels) {
|
|
348
317
|
return null;
|
|
349
318
|
}
|
|
@@ -355,9 +324,9 @@ function checkClosedIssueStatusLabels(context, remoteData) {
|
|
|
355
324
|
if (statusLabels.length === 0) {
|
|
356
325
|
return null;
|
|
357
326
|
}
|
|
358
|
-
return failResult(CHECK_TYPE, `Closed Issue #${context.issueNumber} retains status labels: ${statusLabels.join(", ")}`, "check_failed");
|
|
327
|
+
return shared.failResult(CHECK_TYPE, `Closed Issue #${context.issueNumber} retains status labels: ${statusLabels.join(", ")}`, "check_failed");
|
|
359
328
|
}
|
|
360
|
-
function checkCommentMarker(context, remoteData) {
|
|
329
|
+
function checkCommentMarker(context, remoteData, shared) {
|
|
361
330
|
if (!context.marker) {
|
|
362
331
|
return null;
|
|
363
332
|
}
|
|
@@ -365,9 +334,9 @@ function checkCommentMarker(context, remoteData) {
|
|
|
365
334
|
if (comment) {
|
|
366
335
|
return null;
|
|
367
336
|
}
|
|
368
|
-
return failResult(CHECK_TYPE, `Expected comment marker '${context.marker}' not found on Issue #${context.issueNumber}`, "check_failed");
|
|
337
|
+
return shared.failResult(CHECK_TYPE, `Expected comment marker '${context.marker}' not found on Issue #${context.issueNumber}`, "check_failed");
|
|
369
338
|
}
|
|
370
|
-
function checkPrCommentMarker(context, remoteData) {
|
|
339
|
+
function checkPrCommentMarker(context, remoteData, shared) {
|
|
371
340
|
if (!context.prMarker) {
|
|
372
341
|
return null;
|
|
373
342
|
}
|
|
@@ -375,88 +344,110 @@ function checkPrCommentMarker(context, remoteData) {
|
|
|
375
344
|
if (comment) {
|
|
376
345
|
return null;
|
|
377
346
|
}
|
|
378
|
-
return failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
|
|
347
|
+
return shared.failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
|
|
379
348
|
}
|
|
380
|
-
function checkPrCommentLastCommit(context, remoteData) {
|
|
349
|
+
function checkPrCommentLastCommit(context, remoteData, shared) {
|
|
381
350
|
if (!context.config.verify_pr_comment_last_commit_matches_head) {
|
|
382
351
|
return null;
|
|
383
352
|
}
|
|
384
353
|
if (!context.prMarker) {
|
|
385
|
-
return failResult(CHECK_TYPE, "verify_pr_comment_last_commit_matches_head requires expected_pr_comment_marker", "check_failed");
|
|
354
|
+
return shared.failResult(CHECK_TYPE, "verify_pr_comment_last_commit_matches_head requires expected_pr_comment_marker", "check_failed");
|
|
386
355
|
}
|
|
387
356
|
const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
|
|
388
357
|
if (!comment) {
|
|
389
|
-
return failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
|
|
358
|
+
return shared.failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
|
|
390
359
|
}
|
|
391
360
|
const match = String(comment.body || "").match(/<!--\s*last-commit:\s*([0-9a-f]{7,40})\s*-->/i);
|
|
392
361
|
if (!match) {
|
|
393
|
-
return failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing '<!-- last-commit: <sha> -->' metadata`, "check_failed");
|
|
362
|
+
return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing '<!-- last-commit: <sha> -->' metadata`, "check_failed");
|
|
394
363
|
}
|
|
395
364
|
const expectedHead = String(remoteData.prHeadSha || "").trim();
|
|
396
365
|
if (!expectedHead)
|
|
397
|
-
return blockedResult(CHECK_TYPE, "Unable to resolve the PR head SHA", "network_error");
|
|
366
|
+
return shared.blockedResult(CHECK_TYPE, "Unable to resolve the PR head SHA", "network_error");
|
|
398
367
|
const actualHead = match[1].trim();
|
|
399
368
|
if (expectedHead === actualHead) {
|
|
400
369
|
return null;
|
|
401
370
|
}
|
|
402
|
-
return failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment last-commit metadata mismatch: expected ${expectedHead}, got ${actualHead}`, "check_failed");
|
|
371
|
+
return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment last-commit metadata mismatch: expected ${expectedHead}, got ${actualHead}`, "check_failed");
|
|
403
372
|
}
|
|
404
|
-
function checkPrCommentRequiredPatterns(context, remoteData) {
|
|
373
|
+
function checkPrCommentRequiredPatterns(context, remoteData, shared) {
|
|
405
374
|
const patterns = context.config.expected_pr_comment_required_patterns || [];
|
|
406
375
|
if (!Array.isArray(patterns) || patterns.length === 0) {
|
|
407
376
|
return null;
|
|
408
377
|
}
|
|
409
378
|
if (!context.prMarker) {
|
|
410
|
-
return failResult(CHECK_TYPE, "expected_pr_comment_required_patterns requires expected_pr_comment_marker", "check_failed");
|
|
379
|
+
return shared.failResult(CHECK_TYPE, "expected_pr_comment_required_patterns requires expected_pr_comment_marker", "check_failed");
|
|
411
380
|
}
|
|
412
381
|
const comment = findCommentByMarker(remoteData.prComments, context.prMarker);
|
|
413
382
|
if (!comment) {
|
|
414
|
-
return failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
|
|
383
|
+
return shared.failResult(CHECK_TYPE, `Expected PR comment marker '${context.prMarker}' not found on PR #${context.prNumber}`, "check_failed");
|
|
415
384
|
}
|
|
416
385
|
const body = String(comment.body || "");
|
|
417
386
|
for (const pattern of patterns) {
|
|
418
387
|
const regex = new RegExp(pattern, "m");
|
|
419
388
|
if (!regex.test(body)) {
|
|
420
|
-
return failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing required pattern: ${pattern}`, "check_failed");
|
|
389
|
+
return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} summary comment is missing required pattern: ${pattern}`, "check_failed");
|
|
421
390
|
}
|
|
422
391
|
}
|
|
423
392
|
return null;
|
|
424
393
|
}
|
|
425
|
-
function checkCommentContent(context, remoteData) {
|
|
394
|
+
function checkCommentContent(context, remoteData, shared) {
|
|
426
395
|
if (!context.config.verify_comment_content) {
|
|
427
396
|
return null;
|
|
428
397
|
}
|
|
429
398
|
if (!context.marker) {
|
|
430
|
-
return failResult(CHECK_TYPE, "verify_comment_content requires expected_comment_marker", "check_failed");
|
|
399
|
+
return shared.failResult(CHECK_TYPE, "verify_comment_content requires expected_comment_marker", "check_failed");
|
|
431
400
|
}
|
|
432
|
-
if (!context.artifactPath || !safeStat(context.artifactPath)) {
|
|
433
|
-
return failResult(CHECK_TYPE, `Artifact not found for comment verification: ${context.artifactFile || "(missing artifactFile)"}`, "check_failed");
|
|
401
|
+
if (!context.artifactPath || !shared.safeStat(context.artifactPath)) {
|
|
402
|
+
return shared.failResult(CHECK_TYPE, `Artifact not found for comment verification: ${context.artifactFile || "(missing artifactFile)"}`, "check_failed");
|
|
434
403
|
}
|
|
435
404
|
const comment = findCommentByMarker(remoteData.comments, context.marker);
|
|
436
|
-
|
|
437
|
-
|
|
405
|
+
let localContent;
|
|
406
|
+
let commentContent;
|
|
407
|
+
try {
|
|
408
|
+
const localCanonical = canonicalizeCommentBody(fs.readFileSync(context.artifactPath, "utf8"));
|
|
409
|
+
const commentCanonical = canonicalizeCommentBody(extractCommentBody(comment?.body || ""));
|
|
410
|
+
if (!localCanonical.ok) {
|
|
411
|
+
return shared.failResult(CHECK_TYPE, `Comment content cannot be canonicalized for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${localCanonical.error.message}`, "check_failed");
|
|
412
|
+
}
|
|
413
|
+
if (!commentCanonical.ok) {
|
|
414
|
+
return shared.failResult(CHECK_TYPE, `Comment content cannot be canonicalized for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${commentCanonical.error.message}`, "check_failed");
|
|
415
|
+
}
|
|
416
|
+
localContent = shared.normalizeContent(localCanonical.value);
|
|
417
|
+
commentContent = shared.normalizeContent(commentCanonical.value);
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
return shared.failResult(CHECK_TYPE, `Comment content cannot be read for '${path.basename(context.artifactPath, path.extname(context.artifactPath))}': ${error instanceof Error ? error.message : String(error)}`, "check_failed");
|
|
421
|
+
}
|
|
438
422
|
if (localContent === commentContent) {
|
|
439
423
|
return null;
|
|
440
424
|
}
|
|
441
|
-
return failResult(CHECK_TYPE, buildCommentContentMismatchMessage(path.basename(context.artifactPath, path.extname(context.artifactPath)), context.issueNumber, localContent, commentContent), "check_failed");
|
|
425
|
+
return shared.failResult(CHECK_TYPE, buildCommentContentMismatchMessage(path.basename(context.artifactPath, path.extname(context.artifactPath)), context.issueNumber, localContent, commentContent), "check_failed");
|
|
442
426
|
}
|
|
443
|
-
function checkTaskCommentContent(context, remoteData) {
|
|
427
|
+
function checkTaskCommentContent(context, remoteData, shared) {
|
|
444
428
|
if (!context.config.verify_task_comment_content) {
|
|
445
429
|
return null;
|
|
446
430
|
}
|
|
431
|
+
if (isTaskCommentTooLarge(context.task.content)) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
447
434
|
const taskMarker = `<!-- sync-issue:${context.task.metadata.id}:task -->`;
|
|
448
435
|
const comment = findCommentByMarker(remoteData.comments, taskMarker);
|
|
449
436
|
if (!comment) {
|
|
450
|
-
return failResult(CHECK_TYPE, `Expected comment marker '${taskMarker}' not found on Issue #${context.issueNumber}`, "check_failed");
|
|
437
|
+
return shared.failResult(CHECK_TYPE, `Expected comment marker '${taskMarker}' not found on Issue #${context.issueNumber}`, "check_failed");
|
|
438
|
+
}
|
|
439
|
+
const expectedTaskBody = buildExpectedTaskBody(context.task.content, shared);
|
|
440
|
+
if (expectedTaskBody === null) {
|
|
441
|
+
return shared.failResult(CHECK_TYPE, "Task content cannot be rendered safely for comment verification", "check_failed");
|
|
451
442
|
}
|
|
452
|
-
const expectedBody = normalizeContent(
|
|
453
|
-
const commentBody = normalizeContent(extractCommentBody(comment.body || ""));
|
|
443
|
+
const expectedBody = shared.normalizeContent(expectedTaskBody);
|
|
444
|
+
const commentBody = shared.normalizeContent(extractCommentBody(comment.body || ""));
|
|
454
445
|
if (expectedBody === commentBody) {
|
|
455
446
|
return null;
|
|
456
447
|
}
|
|
457
|
-
return failResult(CHECK_TYPE, buildCommentContentMismatchMessage("task", context.issueNumber, expectedBody, commentBody), "check_failed");
|
|
448
|
+
return shared.failResult(CHECK_TYPE, buildCommentContentMismatchMessage("task", context.issueNumber, expectedBody, commentBody), "check_failed");
|
|
458
449
|
}
|
|
459
|
-
function checkPrTypeLabel(context, remoteData) {
|
|
450
|
+
function checkPrTypeLabel(context, remoteData, shared) {
|
|
460
451
|
if (!context.config.verify_pr_type_label || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
|
|
461
452
|
return null;
|
|
462
453
|
}
|
|
@@ -467,9 +458,9 @@ function checkPrTypeLabel(context, remoteData) {
|
|
|
467
458
|
if (remoteData.prLabels.includes(expectedLabel)) {
|
|
468
459
|
return null;
|
|
469
460
|
}
|
|
470
|
-
return failResult(CHECK_TYPE, `Expected type label '${expectedLabel}' not found on PR #${context.prNumber}`, "check_failed");
|
|
461
|
+
return shared.failResult(CHECK_TYPE, `Expected type label '${expectedLabel}' not found on PR #${context.prNumber}`, "check_failed");
|
|
471
462
|
}
|
|
472
|
-
function checkInLabelsMatchPr(context, remoteData) {
|
|
463
|
+
function checkInLabelsMatchPr(context, remoteData, shared) {
|
|
473
464
|
if (!context.config.verify_in_labels_match_pr || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
|
|
474
465
|
return null;
|
|
475
466
|
}
|
|
@@ -482,17 +473,17 @@ function checkInLabelsMatchPr(context, remoteData) {
|
|
|
482
473
|
if (arraysEqual(issueInLabels, prInLabels)) {
|
|
483
474
|
return null;
|
|
484
475
|
}
|
|
485
|
-
return failResult(CHECK_TYPE, `in: labels mismatch — PR #${context.prNumber} has [${formatLabelList(prInLabels)}], Issue #${context.issueNumber} has [${formatLabelList(issueInLabels)}]`, "check_failed");
|
|
476
|
+
return shared.failResult(CHECK_TYPE, `in: labels mismatch — PR #${context.prNumber} has [${formatLabelList(prInLabels)}], Issue #${context.issueNumber} has [${formatLabelList(issueInLabels)}]`, "check_failed");
|
|
486
477
|
}
|
|
487
|
-
function checkInLabelsComputed(context, remoteData) {
|
|
478
|
+
function checkInLabelsComputed(context, remoteData, shared) {
|
|
488
479
|
if (!context.config.verify_in_labels_computed || !context.hasTriage) {
|
|
489
480
|
return null;
|
|
490
481
|
}
|
|
491
|
-
const expectedInLabels = computeExpectedInLabels(context.taskDir, remoteData.repositoryLabels, remoteData.inLabelMapping);
|
|
482
|
+
const expectedInLabels = computeExpectedInLabels(context.taskDir, remoteData.repositoryLabels, remoteData.inLabelMapping, shared);
|
|
492
483
|
if (!expectedInLabels.ok) {
|
|
493
484
|
return expectedInLabels.type === "check_failed"
|
|
494
|
-
? failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
|
|
495
|
-
: blockedResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type);
|
|
485
|
+
? shared.failResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type)
|
|
486
|
+
: shared.blockedResult(CHECK_TYPE, expectedInLabels.message, expectedInLabels.type);
|
|
496
487
|
}
|
|
497
488
|
if (expectedInLabels.mode === "skipped") {
|
|
498
489
|
return null;
|
|
@@ -503,32 +494,32 @@ function checkInLabelsComputed(context, remoteData) {
|
|
|
503
494
|
if (arraysEqual(expectedInLabels.labels, actualInLabels)) {
|
|
504
495
|
return null;
|
|
505
496
|
}
|
|
506
|
-
return failResult(CHECK_TYPE, `Issue #${context.issueNumber} in: labels do not match committed changes: expected [${formatLabelList(expectedInLabels.labels)}], got [${formatLabelList(actualInLabels)}]`, "check_failed");
|
|
497
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} in: labels do not match committed changes: expected [${formatLabelList(expectedInLabels.labels)}], got [${formatLabelList(actualInLabels)}]`, "check_failed");
|
|
507
498
|
}
|
|
508
|
-
function checkSyncedRequirements(context, remoteData) {
|
|
499
|
+
function checkSyncedRequirements(context, remoteData, shared) {
|
|
509
500
|
if (!context.config.sync_checked_requirements || !context.hasTriage) {
|
|
510
501
|
return null;
|
|
511
502
|
}
|
|
512
|
-
const checkedRequirements = getCheckedRequirements(context.task.content);
|
|
503
|
+
const checkedRequirements = shared.getCheckedRequirements(context.task.content);
|
|
513
504
|
if (checkedRequirements.length === 0) {
|
|
514
505
|
return null;
|
|
515
506
|
}
|
|
516
507
|
const issueBody = remoteData.issue.body || "";
|
|
517
|
-
const resolution = resolveRequirementSection(issueBody, requirementSectionAnchors(repoRoot, context.task.metadata.type || "task"));
|
|
508
|
+
const resolution = resolveRequirementSection(issueBody, requirementSectionAnchors(shared.repoRoot, context.task.metadata.type || "task"));
|
|
518
509
|
if (resolution.status === "missing") {
|
|
519
510
|
return null;
|
|
520
511
|
}
|
|
521
512
|
if (resolution.status === "ambiguous") {
|
|
522
|
-
return failResult(CHECK_TYPE, `Issue #${context.issueNumber} requirements section is ambiguous`, "check_failed");
|
|
513
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} requirements section is ambiguous`, "check_failed");
|
|
523
514
|
}
|
|
524
515
|
const requirementBody = issueBody.slice(resolution.bodyStart, resolution.bodyEnd);
|
|
525
516
|
const missingRequirements = checkedRequirements.filter((item) => !hasCheckedRequirement(requirementBody, item));
|
|
526
517
|
if (missingRequirements.length === 0) {
|
|
527
518
|
return null;
|
|
528
519
|
}
|
|
529
|
-
return failResult(CHECK_TYPE, `Issue body is missing checked requirements: ${missingRequirements.join(", ")}`, "check_failed");
|
|
520
|
+
return shared.failResult(CHECK_TYPE, `Issue body is missing checked requirements: ${missingRequirements.join(", ")}`, "check_failed");
|
|
530
521
|
}
|
|
531
|
-
function checkIssueType(context, remoteData) {
|
|
522
|
+
function checkIssueType(context, remoteData, shared) {
|
|
532
523
|
if (!context.config.verify_issue_type || !context.hasPush) {
|
|
533
524
|
return null;
|
|
534
525
|
}
|
|
@@ -539,15 +530,15 @@ function checkIssueType(context, remoteData) {
|
|
|
539
530
|
if (context.repoOwnerType === "User") {
|
|
540
531
|
return null;
|
|
541
532
|
}
|
|
542
|
-
return failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no Issue Type set`, "check_failed");
|
|
533
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no Issue Type set`, "check_failed");
|
|
543
534
|
}
|
|
544
535
|
const expectedType = mapTaskTypeToIssueType(context.task.metadata.type);
|
|
545
536
|
if (expectedType && remoteData.issueType !== expectedType) {
|
|
546
|
-
return failResult(CHECK_TYPE, `Issue #${context.issueNumber} has type '${remoteData.issueType}', expected '${expectedType}' (from task type '${context.task.metadata.type}')`, "check_failed");
|
|
537
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} has type '${remoteData.issueType}', expected '${expectedType}' (from task type '${context.task.metadata.type}')`, "check_failed");
|
|
547
538
|
}
|
|
548
539
|
return null;
|
|
549
540
|
}
|
|
550
|
-
function checkIssueFields(context, remoteData) {
|
|
541
|
+
function checkIssueFields(context, remoteData, shared) {
|
|
551
542
|
if (!context.config.verify_issue_fields || !context.hasPush) {
|
|
552
543
|
return null;
|
|
553
544
|
}
|
|
@@ -556,7 +547,7 @@ function checkIssueFields(context, remoteData) {
|
|
|
556
547
|
}
|
|
557
548
|
for (const [metadataKey, fieldName] of Object.entries(FRONTMATTER_FIELD_MAP)) {
|
|
558
549
|
const expectedRaw = context.task.metadata[metadataKey];
|
|
559
|
-
if (isBlank(expectedRaw) || !remoteData.issueFields.pinnedNames.has(fieldName)) {
|
|
550
|
+
if (shared.isBlank(expectedRaw) || !remoteData.issueFields.pinnedNames.has(fieldName)) {
|
|
560
551
|
continue;
|
|
561
552
|
}
|
|
562
553
|
const actual = remoteData.issueFields.values.get(fieldName);
|
|
@@ -565,40 +556,40 @@ function checkIssueFields(context, remoteData) {
|
|
|
565
556
|
continue;
|
|
566
557
|
}
|
|
567
558
|
if (!actual) {
|
|
568
|
-
return failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is missing, expected '${expected.value}'`, "check_failed");
|
|
559
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is missing, expected '${expected.value}'`, "check_failed");
|
|
569
560
|
}
|
|
570
561
|
if (actual.kind !== expected.kind || actual.value !== expected.value) {
|
|
571
|
-
return failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is '${actual.value}', expected '${expected.value}'`, "check_failed");
|
|
562
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} field '${fieldName}' is '${actual.value}', expected '${expected.value}'`, "check_failed");
|
|
572
563
|
}
|
|
573
564
|
}
|
|
574
565
|
return null;
|
|
575
566
|
}
|
|
576
|
-
function checkPrAssignee(context, remoteData) {
|
|
567
|
+
function checkPrAssignee(context, remoteData, shared) {
|
|
577
568
|
if (!context.config.verify_pr_assignee || !context.hasPush || !context.prNumber) {
|
|
578
569
|
return null;
|
|
579
570
|
}
|
|
580
571
|
if (!remoteData.prAssignees || remoteData.prAssignees.length === 0) {
|
|
581
|
-
return failResult(CHECK_TYPE, `PR #${context.prNumber} has no assignee`, "check_failed");
|
|
572
|
+
return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} has no assignee`, "check_failed");
|
|
582
573
|
}
|
|
583
574
|
return null;
|
|
584
575
|
}
|
|
585
|
-
function checkMilestone(context, remoteData) {
|
|
576
|
+
function checkMilestone(context, remoteData, shared) {
|
|
586
577
|
if (!context.config.verify_milestone || !context.hasTriage) {
|
|
587
578
|
return null;
|
|
588
579
|
}
|
|
589
580
|
if (!remoteData.issue?.milestone?.title) {
|
|
590
|
-
return failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no milestone set`, "check_failed");
|
|
581
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} has no milestone set`, "check_failed");
|
|
591
582
|
}
|
|
592
583
|
if (context.prNumber && remoteData.prMilestone !== undefined && !remoteData.prMilestone?.title) {
|
|
593
|
-
return failResult(CHECK_TYPE, `PR #${context.prNumber} has no milestone set`, "check_failed");
|
|
584
|
+
return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} has no milestone set`, "check_failed");
|
|
594
585
|
}
|
|
595
586
|
if (context.config.verify_milestone_specific) {
|
|
596
587
|
const issueTitle = remoteData.issue.milestone.title;
|
|
597
588
|
if (VERSION_LINE_REGEX.test(issueTitle)) {
|
|
598
|
-
return failResult(CHECK_TYPE, `Issue #${context.issueNumber} milestone '${issueTitle}' is a release line; narrow to a specific version (e.g. ${issueTitle.replace(/\.x$/, ".N")}) before continuing`, "check_failed");
|
|
589
|
+
return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} milestone '${issueTitle}' is a release line; narrow to a specific version (e.g. ${issueTitle.replace(/\.x$/, ".N")}) before continuing`, "check_failed");
|
|
599
590
|
}
|
|
600
591
|
if (context.prNumber && remoteData.prMilestone?.title && VERSION_LINE_REGEX.test(remoteData.prMilestone.title)) {
|
|
601
|
-
return failResult(CHECK_TYPE, `PR #${context.prNumber} milestone '${remoteData.prMilestone.title}' is a release line; narrow to a specific version before continuing`, "check_failed");
|
|
592
|
+
return shared.failResult(CHECK_TYPE, `PR #${context.prNumber} milestone '${remoteData.prMilestone.title}' is a release line; narrow to a specific version before continuing`, "check_failed");
|
|
602
593
|
}
|
|
603
594
|
}
|
|
604
595
|
return null;
|
|
@@ -643,37 +634,38 @@ function extractCommentBody(commentBody) {
|
|
|
643
634
|
}
|
|
644
635
|
return lines.slice(start, end).join("\n");
|
|
645
636
|
}
|
|
646
|
-
function buildExpectedTaskBody(taskContent) {
|
|
637
|
+
function buildExpectedTaskBody(taskContent, shared) {
|
|
647
638
|
const frontmatterMatch = taskContent.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
648
639
|
if (!frontmatterMatch) {
|
|
649
|
-
return taskContent.trim();
|
|
640
|
+
return sanitizeCommentContent(taskContent.trim());
|
|
641
|
+
}
|
|
642
|
+
const body = sanitizeCommentContent(taskContent.slice(frontmatterMatch[0].length).trim());
|
|
643
|
+
if (body === null) {
|
|
644
|
+
return null;
|
|
650
645
|
}
|
|
651
|
-
const body = taskContent.slice(frontmatterMatch[0].length).trim();
|
|
652
646
|
return [
|
|
653
|
-
buildTaskFrontmatterSummary(),
|
|
647
|
+
buildTaskFrontmatterSummary(shared),
|
|
654
648
|
"",
|
|
655
|
-
"
|
|
656
|
-
frontmatterMatch[0].trim(),
|
|
657
|
-
"```",
|
|
649
|
+
renderSafeCodeFence(frontmatterMatch[0].trim(), "yaml"),
|
|
658
650
|
"",
|
|
659
651
|
"</details>",
|
|
660
652
|
"",
|
|
661
653
|
body
|
|
662
654
|
].join("\n").trim();
|
|
663
655
|
}
|
|
664
|
-
function buildTaskFrontmatterSummary() {
|
|
665
|
-
const language = loadProjectLanguage();
|
|
656
|
+
function buildTaskFrontmatterSummary(shared) {
|
|
657
|
+
const language = loadProjectLanguage(shared);
|
|
666
658
|
if (language === "en" || language === "en-US") {
|
|
667
659
|
return "<details><summary>Metadata (frontmatter)</summary>";
|
|
668
660
|
}
|
|
669
661
|
return "<details><summary>元数据 (frontmatter)</summary>";
|
|
670
662
|
}
|
|
671
|
-
function loadProjectLanguage() {
|
|
663
|
+
function loadProjectLanguage(shared) {
|
|
672
664
|
const override = process.env.VALIDATE_ARTIFACT_LANGUAGE;
|
|
673
|
-
if (!isBlank(override)) {
|
|
665
|
+
if (!shared.isBlank(override)) {
|
|
674
666
|
return String(override).trim();
|
|
675
667
|
}
|
|
676
|
-
const configPath = path.join(repoRoot, ".agents", ".airc.json");
|
|
668
|
+
const configPath = path.join(shared.repoRoot, ".agents", ".airc.json");
|
|
677
669
|
if (!fs.existsSync(configPath)) {
|
|
678
670
|
return "";
|
|
679
671
|
}
|
|
@@ -788,8 +780,8 @@ function arraysEqual(left, right) {
|
|
|
788
780
|
function formatLabelList(labels) {
|
|
789
781
|
return labels.length > 0 ? labels.join(", ") : "none";
|
|
790
782
|
}
|
|
791
|
-
function computeExpectedInLabels(taskDir, repositoryLabels
|
|
792
|
-
const task = loadTask(taskDir);
|
|
783
|
+
function computeExpectedInLabels(taskDir, repositoryLabels, mappingOverride, shared) {
|
|
784
|
+
const task = shared.loadTask(taskDir);
|
|
793
785
|
if (!task.ok) {
|
|
794
786
|
return task;
|
|
795
787
|
}
|
|
@@ -820,8 +812,8 @@ function computeExpectedInLabels(taskDir, repositoryLabels = [], mappingOverride
|
|
|
820
812
|
}
|
|
821
813
|
return { ok: true, labels: planned.target, mode: "mapped" };
|
|
822
814
|
}
|
|
823
|
-
function loadInLabelMapping() {
|
|
824
|
-
const configPath = path.join(repoRoot, ".agents", ".airc.json");
|
|
815
|
+
function loadInLabelMapping(shared) {
|
|
816
|
+
const configPath = path.join(shared.repoRoot, ".agents", ".airc.json");
|
|
825
817
|
if (!fs.existsSync(configPath)) {
|
|
826
818
|
return { ok: true, value: {} };
|
|
827
819
|
}
|
|
@@ -852,12 +844,12 @@ function gitText(args, cwd) {
|
|
|
852
844
|
}
|
|
853
845
|
}
|
|
854
846
|
function resolvePrHeadSha(context) {
|
|
855
|
-
const fallback = () =>
|
|
847
|
+
const fallback = () => gitText(["rev-parse", "HEAD"], context.taskDir);
|
|
856
848
|
const branch = String(context.task?.metadata?.branch || "").trim();
|
|
857
849
|
if (!branch) {
|
|
858
850
|
return fallback();
|
|
859
851
|
}
|
|
860
|
-
const worktreeList =
|
|
852
|
+
const worktreeList = gitText(["worktree", "list", "--porcelain"], context.taskDir);
|
|
861
853
|
if (!worktreeList.ok) {
|
|
862
854
|
return fallback();
|
|
863
855
|
}
|
|
@@ -865,7 +857,7 @@ function resolvePrHeadSha(context) {
|
|
|
865
857
|
if (!matchedWorktree) {
|
|
866
858
|
return fallback();
|
|
867
859
|
}
|
|
868
|
-
const headInWorktree =
|
|
860
|
+
const headInWorktree = gitText(["rev-parse", "HEAD"], matchedWorktree);
|
|
869
861
|
if (!headInWorktree.ok) {
|
|
870
862
|
return fallback();
|
|
871
863
|
}
|
|
@@ -888,9 +880,6 @@ function findWorktreeForBranch(porcelainOutput, branch) {
|
|
|
888
880
|
}
|
|
889
881
|
return null;
|
|
890
882
|
}
|
|
891
|
-
function withRetry(operation) {
|
|
892
|
-
return operation();
|
|
893
|
-
}
|
|
894
883
|
function interpolate(template, taskDir, artifactFile) {
|
|
895
884
|
const artifactStem = artifactFile ? path.basename(artifactFile, path.extname(artifactFile)) : "";
|
|
896
885
|
return template
|