@fitlab-ai/agent-infra 0.9.0 → 0.9.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/README.md +22 -5
- package/README.zh-CN.md +21 -5
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +20 -0
- package/dist/lib/agent-client.js +163 -0
- package/dist/lib/agent-clients/adapter.js +294 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
- package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
- package/dist/lib/agent-clients/adapters/codex.js +54 -0
- package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
- package/dist/lib/agent-clients/adapters/index.js +20 -0
- package/dist/lib/agent-clients/adapters/opencode.js +64 -0
- package/dist/lib/agent-clients/config.js +177 -0
- package/dist/lib/agent-clients/custom-tuis.js +99 -0
- package/dist/lib/agent-clients/invocation.js +9 -0
- package/dist/lib/agent-clients/next-steps.js +38 -0
- package/dist/lib/agent-clients/project-assets.js +45 -0
- package/dist/lib/agent-clients/reconcile.js +235 -0
- package/dist/lib/agent-clients/registry.js +72 -0
- package/dist/lib/agent-clients/schema.js +57 -0
- package/dist/lib/agent-clients/types.js +29 -0
- package/dist/lib/builtin-tuis.js +9 -14
- package/dist/lib/defaults.json +85 -73
- package/dist/lib/git/review-snapshot.js +25 -10
- package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
- package/dist/lib/init.js +47 -91
- package/dist/lib/internal/agent-client.js +117 -0
- package/dist/lib/internal/git-workflow.js +1 -1
- package/dist/lib/internal/platform-release-notes.js +53 -0
- package/dist/lib/internal/release-workflow.js +175 -17
- package/dist/lib/internal/task-orchestration.js +151 -0
- package/dist/lib/internal/task-review.js +65 -0
- package/dist/lib/merge.js +2 -1
- package/dist/lib/platform/adapters.js +38 -1
- package/dist/lib/platform/change-request-git-evidence.js +113 -0
- package/dist/lib/platform/github-release-notes.js +96 -0
- package/dist/lib/platform/issues.js +4 -2
- package/dist/lib/platform/merged-pr-equivalence.js +84 -0
- package/dist/lib/platform/pr-checks.js +43 -15
- package/dist/lib/platform/pull-requests.js +113 -7
- package/dist/lib/platform/release-notes.js +143 -0
- package/dist/lib/platform/releases.js +1 -1
- package/dist/lib/platform/verification-required.js +31 -9
- package/dist/lib/prompt.js +15 -3
- package/dist/lib/release/channels.js +4 -1
- package/dist/lib/release/workflow.js +4 -2
- package/dist/lib/remove-dir.js +25 -0
- package/dist/lib/run/tui.js +8 -2
- package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
- package/dist/lib/sandbox/build-proxy.js +63 -0
- package/dist/lib/sandbox/clipboard/linux.js +2 -1
- package/dist/lib/sandbox/commands/create.js +133 -189
- package/dist/lib/sandbox/commands/enter.js +17 -1
- package/dist/lib/sandbox/commands/prune.js +2 -2
- package/dist/lib/sandbox/commands/rebuild.js +24 -7
- package/dist/lib/sandbox/commands/rm.js +3 -2
- package/dist/lib/sandbox/config.js +21 -4
- package/dist/lib/sandbox/constants.js +3 -0
- package/dist/lib/sandbox/dockerfile.js +2 -1
- package/dist/lib/sandbox/image-build.js +2 -1
- package/dist/lib/sandbox/managed-fs.js +10 -5
- package/dist/lib/sandbox/recovery.js +52 -36
- package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
- package/dist/lib/sandbox/runtimes/base.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/java17.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/java21.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/node20.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/node22.dockerfile +8 -1
- package/dist/lib/sandbox/runtimes/python3.dockerfile +8 -1
- package/dist/lib/sandbox/shell.js +3 -2
- package/dist/lib/sandbox/tool-types.js +8 -0
- package/dist/lib/sandbox/tools.js +2 -126
- package/dist/lib/task/artifact-lifecycle.js +1 -49
- package/dist/lib/task/commands/decisions.js +50 -20
- package/dist/lib/task/commands/issue-body.js +10 -5
- package/dist/lib/task/delegation-receipts.js +107 -0
- package/dist/lib/task/events.js +101 -2
- package/dist/lib/task/issue-form.js +2 -0
- package/dist/lib/task/orchestration.js +420 -0
- package/dist/lib/task/review-artifacts.js +143 -59
- package/dist/lib/task/review-finalization.js +166 -0
- package/dist/lib/task/review-fingerprint.js +13 -0
- package/dist/lib/task/verification-checks.js +2 -1
- package/dist/lib/task/verification-engine.js +168 -23
- package/dist/lib/task/verification-review.js +1 -1
- package/dist/lib/task/verification.js +4 -2
- package/dist/lib/task/workspace-snapshot.js +34 -0
- package/dist/lib/update.js +42 -161
- package/lib/agent-client.ts +214 -0
- package/lib/agent-clients/adapter.ts +461 -0
- package/lib/agent-clients/adapters/claude-code.ts +70 -0
- package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
- package/lib/agent-clients/adapters/codex.ts +58 -0
- package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
- package/lib/agent-clients/adapters/index.ts +27 -0
- package/lib/agent-clients/adapters/opencode.ts +66 -0
- package/lib/agent-clients/config.ts +278 -0
- package/lib/agent-clients/custom-tuis.ts +149 -0
- package/lib/agent-clients/invocation.ts +19 -0
- package/lib/agent-clients/next-steps.ts +72 -0
- package/lib/agent-clients/project-assets.ts +96 -0
- package/lib/agent-clients/reconcile.ts +324 -0
- package/lib/agent-clients/registry.ts +144 -0
- package/lib/agent-clients/schema.ts +62 -0
- package/lib/agent-clients/types.ts +78 -0
- package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
- package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
- package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
- package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
- package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
- package/lib/builtin-tuis.ts +16 -15
- package/lib/defaults.json +24 -12
- package/lib/git/review-snapshot.ts +39 -9
- package/lib/git/reviewed-commit-equivalence.ts +135 -0
- package/lib/init.ts +53 -121
- package/lib/internal/agent-client.ts +131 -0
- package/lib/internal/git-workflow.ts +1 -1
- package/lib/internal/platform-release-notes.ts +55 -0
- package/lib/internal/release-workflow.ts +172 -18
- package/lib/internal/task-orchestration.ts +132 -0
- package/lib/internal/task-review.ts +65 -0
- package/lib/merge.ts +2 -1
- package/lib/platform/adapters.ts +142 -3
- package/lib/platform/change-request-git-evidence.ts +159 -0
- package/lib/platform/github-release-notes.ts +132 -0
- package/lib/platform/issues.ts +6 -4
- package/lib/platform/merged-pr-equivalence.ts +107 -0
- package/lib/platform/pr-checks.ts +57 -28
- package/lib/platform/pull-requests.ts +140 -25
- package/lib/platform/release-notes.ts +167 -0
- package/lib/platform/releases.ts +1 -1
- package/lib/platform/verification-required.ts +37 -9
- package/lib/prompt.ts +16 -3
- package/lib/release/channels.ts +4 -1
- package/lib/release/workflow.ts +4 -3
- package/lib/remove-dir.ts +28 -0
- package/lib/run/tui.ts +9 -1
- package/lib/sandbox/agent-client-reconciler.ts +378 -0
- package/lib/sandbox/build-proxy.ts +94 -0
- package/lib/sandbox/clipboard/linux.ts +2 -1
- package/lib/sandbox/commands/create.ts +188 -203
- package/lib/sandbox/commands/enter.ts +23 -1
- package/lib/sandbox/commands/prune.ts +2 -2
- package/lib/sandbox/commands/rebuild.ts +29 -7
- package/lib/sandbox/commands/rm.ts +3 -2
- package/lib/sandbox/config.ts +31 -4
- package/lib/sandbox/constants.ts +6 -0
- package/lib/sandbox/dockerfile.ts +2 -1
- package/lib/sandbox/image-build.ts +4 -1
- package/lib/sandbox/managed-fs.ts +18 -5
- package/lib/sandbox/recovery.ts +98 -67
- package/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
- package/lib/sandbox/runtimes/base.dockerfile +8 -1
- package/lib/sandbox/runtimes/java17.dockerfile +8 -1
- package/lib/sandbox/runtimes/java21.dockerfile +8 -1
- package/lib/sandbox/runtimes/node20.dockerfile +8 -1
- package/lib/sandbox/runtimes/node22.dockerfile +8 -1
- package/lib/sandbox/runtimes/python3.dockerfile +8 -1
- package/lib/sandbox/shell.ts +4 -2
- package/lib/sandbox/tool-types.ts +143 -0
- package/lib/sandbox/tools.ts +5 -157
- package/lib/task/artifact-lifecycle.ts +1 -38
- package/lib/task/commands/decisions.ts +53 -20
- package/lib/task/commands/issue-body.ts +10 -5
- package/lib/task/delegation-receipts.ts +170 -0
- package/lib/task/events.ts +111 -3
- package/lib/task/issue-form.ts +4 -0
- package/lib/task/orchestration.ts +543 -0
- package/lib/task/review-artifacts.ts +201 -69
- package/lib/task/review-finalization.ts +275 -0
- package/lib/task/review-fingerprint.ts +15 -1
- package/lib/task/verification-checks.ts +2 -1
- package/lib/task/verification-engine.ts +190 -25
- package/lib/task/verification-review.ts +2 -0
- package/lib/task/verification.ts +6 -3
- package/lib/task/workspace-snapshot.ts +36 -0
- package/lib/update.ts +48 -184
- package/package.json +2 -2
- package/templates/.agents/README.en.md +21 -20
- package/templates/.agents/README.zh-CN.md +21 -20
- package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
- package/templates/.agents/rules/README.en.md +2 -0
- package/templates/.agents/rules/README.zh-CN.md +2 -0
- package/templates/.agents/rules/commit-and-pr.en.md +1 -0
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
- package/templates/.agents/rules/human-decision-context.en.md +14 -12
- package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
- package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
- package/templates/.agents/rules/next-step-output.en.md +16 -0
- package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
- package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
- package/templates/.agents/rules/release-commands.en.md +2 -2
- package/templates/.agents/rules/release-commands.github.en.md +15 -39
- package/templates/.agents/rules/release-commands.github.zh-CN.md +14 -38
- package/templates/.agents/rules/release-commands.zh-CN.md +2 -2
- package/templates/.agents/rules/review-handshake.en.md +4 -3
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -3
- package/templates/.agents/rules/review-method.en.md +113 -0
- package/templates/.agents/rules/review-method.zh-CN.md +98 -0
- package/templates/.agents/rules/testing-discipline.en.md +20 -2
- package/templates/.agents/rules/testing-discipline.zh-CN.md +20 -2
- package/templates/.agents/skills/analyze-task/SKILL.en.md +9 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +9 -6
- package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
- package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
- package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
- package/templates/.agents/skills/commit/SKILL.en.md +11 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
- 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 +0 -1
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
- package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -38
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +16 -37
- package/templates/.agents/skills/create-task/SKILL.en.md +15 -11
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +15 -11
- package/templates/.agents/skills/create-task/reference/context-capture.en.md +29 -0
- package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +29 -0
- package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/release/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
- package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
- package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
- package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
- package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
- package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
- package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
- package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
- package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
- package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
- package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
- package/templates/.agents/skills/test/SKILL.en.md +4 -4
- package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
- package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
- package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
- package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
- package/templates/.agents/templates/task.en.md +18 -0
- package/templates/.agents/templates/task.zh-CN.md +18 -0
- package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
- package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
- package/templates/.claude/commands/run-task.en.md +8 -0
- package/templates/.claude/commands/run-task.zh-CN.md +8 -0
- package/templates/.claude/settings.json +24 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
- package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
- package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
- package/templates/.opencode/commands/run-task.en.md +11 -0
- package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { locateActivityLog, pairEntries } from './activity-log.js';
|
|
5
|
+
import { parseArtifactName, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
6
|
+
import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
7
|
+
import { finalizeReviewSummaryContent } from './review-artifacts.js';
|
|
8
|
+
import { resolveTaskRef } from './resolve-ref.js';
|
|
9
|
+
import { validateOrchestrationStage } from './orchestration.js';
|
|
10
|
+
const STAGES = {
|
|
11
|
+
analysis: { family: 'review-analysis', action: 'Review Analysis' },
|
|
12
|
+
plan: { family: 'review-plan', action: 'Review Plan' },
|
|
13
|
+
code: { family: 'review-code', action: 'Review Code' }
|
|
14
|
+
};
|
|
15
|
+
const DEFAULT_FILE_SYSTEM = {
|
|
16
|
+
readFileSync: (file) => fs.readFileSync(file, 'utf8'),
|
|
17
|
+
statModeSync: (file) => fs.statSync(file).mode,
|
|
18
|
+
writeFileSync: (file, content, mode) => fs.writeFileSync(file, content, { encoding: 'utf8', flag: 'wx', mode }),
|
|
19
|
+
renameSync: (from, to) => fs.renameSync(from, to),
|
|
20
|
+
unlinkSync: (file) => fs.unlinkSync(file)
|
|
21
|
+
};
|
|
22
|
+
function failed(request, code, message, taskId = null, stageStatus = null) {
|
|
23
|
+
return {
|
|
24
|
+
status: 'failed',
|
|
25
|
+
changed: false,
|
|
26
|
+
intent: 'finalize-summary',
|
|
27
|
+
requestRef: request.taskRef,
|
|
28
|
+
taskId,
|
|
29
|
+
stage: request.stage,
|
|
30
|
+
artifact: request.artifact,
|
|
31
|
+
stageStatus,
|
|
32
|
+
operations: [],
|
|
33
|
+
error: { code, message }
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function openReviewRound(content, action, round) {
|
|
37
|
+
const section = locateActivityLog(content);
|
|
38
|
+
if (!section)
|
|
39
|
+
return false;
|
|
40
|
+
const expected = `${action} (Round ${round})`;
|
|
41
|
+
return pairEntries(section.entries).filter((row) => (row.step === expected && Boolean(row.started) && !row.done)).length === 1;
|
|
42
|
+
}
|
|
43
|
+
function cleanupTemp(fileSystem, tempPath) {
|
|
44
|
+
try {
|
|
45
|
+
fileSystem.unlinkSync(tempPath);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Best-effort cleanup preserves the primary failure.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function finalizeReviewSummary(request, options = {}) {
|
|
52
|
+
const stage = request.stage;
|
|
53
|
+
const spec = STAGES[stage];
|
|
54
|
+
if (!spec)
|
|
55
|
+
return failed(request, 'REVIEW_STAGE_INVALID', `unsupported review stage '${request.stage}'`);
|
|
56
|
+
if (!request.taskRef || !request.artifact) {
|
|
57
|
+
return failed(request, 'REVIEW_PAYLOAD_INVALID', 'taskRef, stage, and artifact are required');
|
|
58
|
+
}
|
|
59
|
+
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
60
|
+
if (!resolved.ok)
|
|
61
|
+
return failed(request, resolved.code, resolved.message, resolved.taskId);
|
|
62
|
+
if (resolved.state !== 'active') {
|
|
63
|
+
return failed(request, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
64
|
+
}
|
|
65
|
+
const parsedArtifact = parseArtifactName(request.artifact);
|
|
66
|
+
if (!parsedArtifact || parsedArtifact.family !== spec.family) {
|
|
67
|
+
return failed(request, 'REVIEW_ARTIFACT_IDENTITY_INVALID', `artifact '${request.artifact}' does not match ${spec.family}`, resolved.taskId);
|
|
68
|
+
}
|
|
69
|
+
const validated = validateCompletedArtifact(resolved.taskDir, spec.family, request.artifact, parsedArtifact.round);
|
|
70
|
+
if (!validated.ok) {
|
|
71
|
+
const code = validated.error.code === 'ARTIFACT_NOT_REGULAR'
|
|
72
|
+
? 'REVIEW_ARTIFACT_NOT_REGULAR'
|
|
73
|
+
: 'REVIEW_ARTIFACT_IDENTITY_INVALID';
|
|
74
|
+
return failed(request, code, validated.error.message, resolved.taskId);
|
|
75
|
+
}
|
|
76
|
+
const fileSystem = { ...DEFAULT_FILE_SYSTEM, ...options.fileSystem };
|
|
77
|
+
let taskContent;
|
|
78
|
+
let artifactContent;
|
|
79
|
+
try {
|
|
80
|
+
taskContent = fileSystem.readFileSync(resolved.taskMdPath);
|
|
81
|
+
artifactContent = fileSystem.readFileSync(validated.artifact.path);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return failed(request, 'REVIEW_ARTIFACT_NOT_REGULAR', String(error), resolved.taskId);
|
|
85
|
+
}
|
|
86
|
+
if (!openReviewRound(taskContent, spec.action, parsedArtifact.round)) {
|
|
87
|
+
return failed(request, 'REVIEW_ARTIFACT_IDENTITY_INVALID', `${request.artifact} does not have one matching open started review event`, resolved.taskId);
|
|
88
|
+
}
|
|
89
|
+
const provenance = validateOrchestrationStage(request.taskRef, {
|
|
90
|
+
stage: spec.family,
|
|
91
|
+
round: parsedArtifact.round,
|
|
92
|
+
artifact: request.artifact,
|
|
93
|
+
role: 'reviewer'
|
|
94
|
+
}, { repoRoot: options.repoRoot });
|
|
95
|
+
if (provenance.status === 'failed' || provenance.status === 'paused') {
|
|
96
|
+
return failed(request, 'REVIEW_PROVENANCE_INVALID', provenance.error?.message ?? provenance.run?.pause?.message ?? 'orchestration provenance validation failed', resolved.taskId);
|
|
97
|
+
}
|
|
98
|
+
let rows;
|
|
99
|
+
try {
|
|
100
|
+
rows = parseLedger(taskContent);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return failed(request, 'REVIEW_LEDGER_INVALID', String(error), resolved.taskId);
|
|
104
|
+
}
|
|
105
|
+
const ledgerError = validateLedgerRows(rows);
|
|
106
|
+
if (ledgerError) {
|
|
107
|
+
return failed(request, 'REVIEW_LEDGER_INVALID', `${ledgerError.code}: ${ledgerError.message}`, resolved.taskId);
|
|
108
|
+
}
|
|
109
|
+
const stageStatus = summarizeLedgerStage(rows, stage);
|
|
110
|
+
const transformed = finalizeReviewSummaryContent(artifactContent, stageStatus.unresolvedFindingCounts);
|
|
111
|
+
if (!transformed.ok) {
|
|
112
|
+
return failed(request, transformed.code, transformed.message, resolved.taskId, stageStatus);
|
|
113
|
+
}
|
|
114
|
+
if (!transformed.changed) {
|
|
115
|
+
return {
|
|
116
|
+
...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
|
|
117
|
+
status: 'no-op',
|
|
118
|
+
error: null
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const operations = [{ kind: 'artifact', artifact: request.artifact, operation: 'update' }];
|
|
122
|
+
if (request.dryRun) {
|
|
123
|
+
return {
|
|
124
|
+
...failed(request, 'REVIEW_ARTIFACT_CONFLICT', '', resolved.taskId, stageStatus),
|
|
125
|
+
status: 'planned',
|
|
126
|
+
changed: true,
|
|
127
|
+
operations,
|
|
128
|
+
error: null
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const tempPath = path.join(resolved.taskDir, `.${request.artifact}.tmp-${process.pid}-${(options.randomSuffix ?? randomUUID)()}`);
|
|
132
|
+
let mode;
|
|
133
|
+
try {
|
|
134
|
+
mode = fileSystem.statModeSync(validated.artifact.path);
|
|
135
|
+
fileSystem.writeFileSync(tempPath, transformed.content, mode);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
cleanupTemp(fileSystem, tempPath);
|
|
139
|
+
return failed(request, 'REVIEW_TEMP_WRITE_FAILED', String(error), resolved.taskId, stageStatus);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
if (fileSystem.readFileSync(validated.artifact.path) !== artifactContent) {
|
|
143
|
+
cleanupTemp(fileSystem, tempPath);
|
|
144
|
+
return failed(request, 'REVIEW_ARTIFACT_CONFLICT', `${request.artifact} changed during finalization`, resolved.taskId, stageStatus);
|
|
145
|
+
}
|
|
146
|
+
fileSystem.renameSync(tempPath, validated.artifact.path);
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
cleanupTemp(fileSystem, tempPath);
|
|
150
|
+
return failed(request, 'REVIEW_RENAME_FAILED', String(error), resolved.taskId, stageStatus);
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
status: 'applied',
|
|
154
|
+
changed: true,
|
|
155
|
+
intent: 'finalize-summary',
|
|
156
|
+
requestRef: request.taskRef,
|
|
157
|
+
taskId: resolved.taskId,
|
|
158
|
+
stage,
|
|
159
|
+
artifact: request.artifact,
|
|
160
|
+
stageStatus,
|
|
161
|
+
operations,
|
|
162
|
+
error: null
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
export { finalizeReviewSummary };
|
|
166
|
+
//# sourceMappingURL=review-finalization.js.map
|
|
@@ -5,6 +5,15 @@ import { artifactName, maxRound } from "./review-artifacts.js";
|
|
|
5
5
|
// covers ALL tracked changes by default. This default denylist is EMPTY —
|
|
6
6
|
// projects opt into exclusions explicitly via review.post_review_exclude_globs.
|
|
7
7
|
export const DEFAULT_POST_REVIEW_EXCLUDES = [];
|
|
8
|
+
export function loadPostReviewConfig(repoRoot) {
|
|
9
|
+
try {
|
|
10
|
+
const config = JSON.parse(fs.readFileSync(path.join(repoRoot, ".agents", ".airc.json"), "utf8"));
|
|
11
|
+
return config.review && typeof config.review === "object" ? config.review : {};
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
8
17
|
export function resolvePostReviewGlobs(config = {}, reviewConfig = {}) {
|
|
9
18
|
const projectExcludes = [
|
|
10
19
|
...(Array.isArray(reviewConfig.post_review_exclude_globs) ? reviewConfig.post_review_exclude_globs : []),
|
|
@@ -31,6 +40,10 @@ export function extractReviewBaseline(content) {
|
|
|
31
40
|
const match = String(content).match(/^[-*]?\s*\*\*(?:审查基线提交|Review Baseline Commit)\*\*[::]\s*(.*?)\s*$/m);
|
|
32
41
|
return match ? match[1].trim().replace(/`/g, "") : "";
|
|
33
42
|
}
|
|
43
|
+
export function extractReviewDiffBase(content) {
|
|
44
|
+
const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异基线|Reviewed Diff Base)\*\*[::]\s*(.*?)\s*$/m);
|
|
45
|
+
return match ? match[1].trim().replace(/`/g, "") : "";
|
|
46
|
+
}
|
|
34
47
|
export function extractReviewDiffFingerprint(content) {
|
|
35
48
|
const match = String(content).match(/^[-*]?\s*\*\*(?:审查差异指纹|Reviewed Diff Fingerprint)\*\*[::]\s*(.*?)\s*$/m);
|
|
36
49
|
return match ? match[1].trim().replace(/`/g, "") : "";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const LOCAL_VERIFICATION_CHECKS = [
|
|
2
|
-
'task-meta', 'artifact', 'implementation-input', 'activity-log', 'completion-checklist'
|
|
2
|
+
'task-meta', 'artifact', 'implementation-input', 'activity-log', 'completion-checklist',
|
|
3
|
+
'orchestration-state'
|
|
3
4
|
];
|
|
4
5
|
function isLocalVerificationCheck(value) {
|
|
5
6
|
return LOCAL_VERIFICATION_CHECKS.includes(value);
|
|
@@ -4,11 +4,16 @@ import path from "node:path";
|
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { extractReviewBaseline, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, parseReviewVerdict, resolvePostReviewGlobs } from "./verification-review.js";
|
|
7
|
+
import { extractReviewBaseline, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from "./verification-review.js";
|
|
8
8
|
import { check as checkPlatformSync } from "../platform/verification-sync.js";
|
|
9
9
|
import { check as checkRequiredChecks } from "../platform/verification-required.js";
|
|
10
|
+
import { inspectPlatformPullRequest } from "../platform/pull-requests.js";
|
|
11
|
+
import { resolveMaterializedReviewedHeadRelation } from "../platform/change-request-git-evidence.js";
|
|
12
|
+
import { resolveReviewedHeadRelation } from "../platform/merged-pr-equivalence.js";
|
|
13
|
+
import { resolveLocalReviewedCommitRelation } from "../git/reviewed-commit-equivalence.js";
|
|
10
14
|
import { parseTypedTaskFrontmatter } from "./frontmatter.js";
|
|
11
|
-
import { parseLedger } from "./ledger.js";
|
|
15
|
+
import { parseLedger, summarizeLedgerStage, validateLedgerRows } from "./ledger.js";
|
|
16
|
+
import { equalCounts, parseReviewSummary } from "./review-artifacts.js";
|
|
12
17
|
import { loadVerificationConfig } from "./verification-config.js";
|
|
13
18
|
import { snapshotReview } from "../git/review-snapshot.js";
|
|
14
19
|
const TASK_ENUMS = {
|
|
@@ -91,10 +96,14 @@ function runCheck(type, context, shared) {
|
|
|
91
96
|
return checkCompletionChecklist(context);
|
|
92
97
|
case "review-ledger":
|
|
93
98
|
return checkReviewLedger(context);
|
|
99
|
+
case "review-summary":
|
|
100
|
+
return checkReviewSummary(context);
|
|
94
101
|
case "review-fact":
|
|
95
102
|
return checkReviewFact(context);
|
|
96
103
|
case "post-review-commit":
|
|
97
104
|
return checkPostReviewCommit(context);
|
|
105
|
+
case "orchestration-state":
|
|
106
|
+
return checkOrchestrationState(context);
|
|
98
107
|
default: {
|
|
99
108
|
const adapter = PLATFORM_ADAPTERS[type];
|
|
100
109
|
if (!adapter) {
|
|
@@ -107,6 +116,34 @@ function runCheck(type, context, shared) {
|
|
|
107
116
|
}
|
|
108
117
|
}
|
|
109
118
|
}
|
|
119
|
+
function checkOrchestrationState({ taskDir }) {
|
|
120
|
+
const file = path.join(taskDir, 'orchestration.json');
|
|
121
|
+
const stat = safeStat(file);
|
|
122
|
+
if (!stat?.isFile())
|
|
123
|
+
return failResult('orchestration-state', 'orchestration.json is missing');
|
|
124
|
+
let run;
|
|
125
|
+
try {
|
|
126
|
+
run = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
return failResult('orchestration-state', `Invalid orchestration.json: ${String(error)}`);
|
|
130
|
+
}
|
|
131
|
+
if (run.schemaVersion !== 1 || !['paused', 'completed'].includes(run.status)) {
|
|
132
|
+
return failResult('orchestration-state', `Expected paused or completed run, received '${run.status}'`);
|
|
133
|
+
}
|
|
134
|
+
if (run.status === 'paused' && (!run.pause?.code || !run.pause?.message)) {
|
|
135
|
+
return failResult('orchestration-state', 'Paused run requires a stable pause code and message');
|
|
136
|
+
}
|
|
137
|
+
if (run.status === 'completed') {
|
|
138
|
+
if (run.pendingDelegation !== null || !run.commitAuthorization?.consumedAt) {
|
|
139
|
+
return failResult('orchestration-state', 'Completed run must consume commit authorization and clear pending delegation');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else if (run.pendingDelegation?.status === 'sealed') {
|
|
143
|
+
return failResult('orchestration-state', 'Paused run has a sealed delegation that could still advance');
|
|
144
|
+
}
|
|
145
|
+
return passResult('orchestration-state', `Orchestration run is safely ${run.status}`);
|
|
146
|
+
}
|
|
110
147
|
// === Check Functions ===
|
|
111
148
|
function checkTaskMeta({ taskDir, config }) {
|
|
112
149
|
const task = loadTask(taskDir);
|
|
@@ -210,19 +247,6 @@ function loadProjectName() {
|
|
|
210
247
|
return "";
|
|
211
248
|
}
|
|
212
249
|
}
|
|
213
|
-
function loadReviewConfig() {
|
|
214
|
-
const configPath = path.join(repoRoot, ".agents", ".airc.json");
|
|
215
|
-
if (!fs.existsSync(configPath)) {
|
|
216
|
-
return {};
|
|
217
|
-
}
|
|
218
|
-
try {
|
|
219
|
-
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
220
|
-
return config.review && typeof config.review === "object" ? config.review : {};
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
return {};
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
250
|
function checkArtifact({ taskDir, config, artifactFile }) {
|
|
227
251
|
const resolvedArtifact = resolveArtifactPath(taskDir, config.file_pattern, artifactFile);
|
|
228
252
|
if (!resolvedArtifact.ok) {
|
|
@@ -250,6 +274,40 @@ function checkArtifact({ taskDir, config, artifactFile }) {
|
|
|
250
274
|
}
|
|
251
275
|
return passResult("artifact", `${path.basename(artifactPath)} passed (${requiredSections.length} sections)`);
|
|
252
276
|
}
|
|
277
|
+
function checkReviewSummary({ taskDir, config, artifactFile }) {
|
|
278
|
+
const stage = String(config.stage || "");
|
|
279
|
+
if (!["analysis", "plan", "code"].includes(stage)) {
|
|
280
|
+
return failResult("review-summary", `Invalid review stage '${stage}'`);
|
|
281
|
+
}
|
|
282
|
+
const resolvedArtifact = resolveArtifactPath(taskDir, "", artifactFile);
|
|
283
|
+
if (!resolvedArtifact.ok || !safeStat(resolvedArtifact.path)?.isFile()) {
|
|
284
|
+
return failResult("review-summary", resolvedArtifact.message || "Review artifact is missing");
|
|
285
|
+
}
|
|
286
|
+
const parsed = parseReviewSummary(fs.readFileSync(resolvedArtifact.path, "utf8"));
|
|
287
|
+
if (!parsed.ok)
|
|
288
|
+
return failResult("review-summary", `${parsed.code}: ${parsed.message}`);
|
|
289
|
+
if (!parsed.summary.counts) {
|
|
290
|
+
return failResult("review-summary", "Review summary finding counts are not finalized");
|
|
291
|
+
}
|
|
292
|
+
const task = loadTask(taskDir);
|
|
293
|
+
if (!task.ok)
|
|
294
|
+
return failResult("review-summary", task.message);
|
|
295
|
+
let rows;
|
|
296
|
+
try {
|
|
297
|
+
rows = parseLedger(task.content);
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
return failResult("review-summary", `Invalid disagreement ledger: ${String(error)}`);
|
|
301
|
+
}
|
|
302
|
+
const invalid = validateLedgerRows(rows);
|
|
303
|
+
if (invalid)
|
|
304
|
+
return failResult("review-summary", `${invalid.code}: ${invalid.message}`);
|
|
305
|
+
const expected = summarizeLedgerStage(rows, stage).unresolvedFindingCounts;
|
|
306
|
+
if (!equalCounts(parsed.summary.counts, expected)) {
|
|
307
|
+
return failResult("review-summary", `Review summary counts ${JSON.stringify(parsed.summary.counts)} do not match ledger ${JSON.stringify(expected)}`);
|
|
308
|
+
}
|
|
309
|
+
return passResult("review-summary", `${path.basename(resolvedArtifact.path)} finding counts match the ${stage} ledger`);
|
|
310
|
+
}
|
|
253
311
|
function checkImplementationInput({ taskDir, artifactFile }) {
|
|
254
312
|
const task = loadTask(taskDir);
|
|
255
313
|
if (!task.ok)
|
|
@@ -495,7 +553,7 @@ function resolveReviewSetting(config, key, fallback) {
|
|
|
495
553
|
if (config && config[key] !== undefined && config[key] !== null) {
|
|
496
554
|
return config[key];
|
|
497
555
|
}
|
|
498
|
-
const reviewConfig =
|
|
556
|
+
const reviewConfig = loadPostReviewConfig(repoRoot);
|
|
499
557
|
if (reviewConfig[key] !== undefined && reviewConfig[key] !== null) {
|
|
500
558
|
return reviewConfig[key];
|
|
501
559
|
}
|
|
@@ -579,8 +637,42 @@ function checkPostReviewCommit({ taskDir, config }) {
|
|
|
579
637
|
return blockedResult("post-review-commit", "git unavailable or task directory is not inside a git repository");
|
|
580
638
|
}
|
|
581
639
|
const task = loadTask(taskDir);
|
|
582
|
-
|
|
640
|
+
if (!task.ok)
|
|
641
|
+
return failResult("post-review-commit", task.message);
|
|
583
642
|
const lastReviewedCommit = task.ok ? (task.metadata.last_reviewed_commit || "").trim() : "";
|
|
643
|
+
const globs = resolvePostReviewGlobs(config, loadPostReviewConfig(repoRoot));
|
|
644
|
+
const hasPullRequest = Number(task.metadata.pr_number) > 0;
|
|
645
|
+
let inspected = null;
|
|
646
|
+
if (hasPullRequest) {
|
|
647
|
+
inspected = inspectPlatformPullRequest(task.metadata.id, { cwd: gitRoot });
|
|
648
|
+
if (!inspected.pullRequest) {
|
|
649
|
+
const message = inspected.error?.message || "PR merge snapshot is unavailable";
|
|
650
|
+
const code = inspected.error?.code || "PR_INSPECTION_INVALID";
|
|
651
|
+
return inspected.status === "blocked"
|
|
652
|
+
? blockedResult("post-review-commit", message, code)
|
|
653
|
+
: failResult("post-review-commit", message, code);
|
|
654
|
+
}
|
|
655
|
+
if (inspected.pullRequest.state === "closed" &&
|
|
656
|
+
inspected.pullRequest.mergedAt &&
|
|
657
|
+
inspected.pullRequest.mergeCommitSha) {
|
|
658
|
+
const relation = resolveMaterializedReviewedHeadRelation({
|
|
659
|
+
cwd: gitRoot,
|
|
660
|
+
platformType: inspected.platform.type,
|
|
661
|
+
lastReviewedCommit,
|
|
662
|
+
pullRequest: inspected.pullRequest
|
|
663
|
+
});
|
|
664
|
+
if (relation.status === "merged-equivalent") {
|
|
665
|
+
return passResult("post-review-commit", `Reviewed PR head is content-equivalent to squash merge ${relation.mergeCommit.slice(0, 8)} on target ${relation.comparisonHead.slice(0, 8)}`);
|
|
666
|
+
}
|
|
667
|
+
if (relation.status === "blocked") {
|
|
668
|
+
return blockedResult("post-review-commit", relation.message, relation.code);
|
|
669
|
+
}
|
|
670
|
+
if (relation.status === "strict") {
|
|
671
|
+
return failResult("post-review-commit", "Merged PR evidence did not establish squash merge equivalence", "PR_MERGE_IDENTITY_INVALID");
|
|
672
|
+
}
|
|
673
|
+
return failResult("post-review-commit", relation.message, relation.code);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
584
676
|
const baselineSource = resolvePostReviewBaseline({
|
|
585
677
|
gitRoot,
|
|
586
678
|
lastReviewedCommit,
|
|
@@ -590,7 +682,6 @@ function checkPostReviewCommit({ taskDir, config }) {
|
|
|
590
682
|
return baselineSource.result;
|
|
591
683
|
}
|
|
592
684
|
const sha = baselineSource.sha;
|
|
593
|
-
const globs = resolvePostReviewGlobs(config, loadReviewConfig());
|
|
594
685
|
let commits;
|
|
595
686
|
try {
|
|
596
687
|
const out = execFileSync("git", ["-C", gitRoot, "rev-list", `${sha}..HEAD`, "--", ...globs], { encoding: "utf8" });
|
|
@@ -602,18 +693,63 @@ function checkPostReviewCommit({ taskDir, config }) {
|
|
|
602
693
|
if (commits.length === 0) {
|
|
603
694
|
return passResult("post-review-commit", `No post-review commits to code/rule paths since ${sha.slice(0, 8)}`);
|
|
604
695
|
}
|
|
605
|
-
let
|
|
696
|
+
let localHead = "";
|
|
606
697
|
try {
|
|
607
|
-
|
|
698
|
+
localHead = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
608
699
|
}
|
|
609
700
|
catch {
|
|
610
|
-
return
|
|
701
|
+
return blockedResult("post-review-commit", "Unable to resolve the local HEAD");
|
|
702
|
+
}
|
|
703
|
+
if (hasPullRequest) {
|
|
704
|
+
if (inspected?.pullRequest) {
|
|
705
|
+
const relation = resolveReviewedHeadRelation({
|
|
706
|
+
gitRoot,
|
|
707
|
+
comparisonHead: localHead,
|
|
708
|
+
lastReviewedCommit: sha,
|
|
709
|
+
pullRequest: inspected.pullRequest
|
|
710
|
+
});
|
|
711
|
+
if (relation.status === "merged-equivalent") {
|
|
712
|
+
return passResult("post-review-commit", `Reviewed PR head is content-equivalent to squash merge ${relation.mergeCommit.slice(0, 8)}`);
|
|
713
|
+
}
|
|
714
|
+
if (relation.status === "blocked") {
|
|
715
|
+
return blockedResult("post-review-commit", relation.message, relation.code);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
else if (commits.length === 1) {
|
|
720
|
+
const relation = resolveLocalReviewedCommitRelation({
|
|
721
|
+
gitRoot,
|
|
722
|
+
localHead,
|
|
723
|
+
lastReviewedCommit: sha,
|
|
724
|
+
rewrittenCommit: commits[0],
|
|
725
|
+
pathspecs: globs
|
|
726
|
+
});
|
|
727
|
+
if (relation.status === "local-equivalent") {
|
|
728
|
+
return passResult("post-review-commit", `Reviewed commit is content-equivalent to local rewrite ${relation.rewrittenCommit.slice(0, 8)}`);
|
|
729
|
+
}
|
|
730
|
+
if (relation.status === "blocked") {
|
|
731
|
+
return blockedResult("post-review-commit", relation.message);
|
|
732
|
+
}
|
|
611
733
|
}
|
|
612
|
-
|
|
734
|
+
const exemption = resolvePostReviewExemption(task.content);
|
|
735
|
+
if (!exemption.ok)
|
|
736
|
+
return failResult("post-review-commit", exemption.message);
|
|
737
|
+
if (exemption.exempt) {
|
|
613
738
|
return passResult("post-review-commit", `${commits.length} post-review commit(s) covered by a human-decided exemption`);
|
|
614
739
|
}
|
|
615
740
|
return failResult("post-review-commit", `${commits.length} commit(s) to code/rule paths after reviewed commit ${sha.slice(0, 8)}; re-run review-code or record a human-decided exemption`);
|
|
616
741
|
}
|
|
742
|
+
function resolvePostReviewExemption(content) {
|
|
743
|
+
try {
|
|
744
|
+
return {
|
|
745
|
+
ok: true,
|
|
746
|
+
exempt: parseLedger(content).some((row) => row.stage === POST_REVIEW_COMMIT_STAGE && row.status === "human-decided")
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
catch {
|
|
750
|
+
return { ok: false, message: "Invalid disagreement ledger" };
|
|
751
|
+
}
|
|
752
|
+
}
|
|
617
753
|
function checkReviewFact({ taskDir, artifactFile }) {
|
|
618
754
|
const resolvedArtifact = resolveArtifactPath(taskDir, "review-code.md|review-code-r{N}.md", artifactFile);
|
|
619
755
|
if (!resolvedArtifact.ok) {
|
|
@@ -626,6 +762,7 @@ function checkReviewFact({ taskDir, artifactFile }) {
|
|
|
626
762
|
const content = fs.readFileSync(resolvedArtifact.path, "utf8");
|
|
627
763
|
const verdict = parseReviewVerdict(content);
|
|
628
764
|
const reviewBaseline = extractReviewBaseline(content);
|
|
765
|
+
const reviewDiffBase = extractReviewDiffBase(content);
|
|
629
766
|
const reviewedFingerprint = extractReviewDiffFingerprint(content);
|
|
630
767
|
const reviewedTree = extractReviewedSnapshotTree(content);
|
|
631
768
|
if (!["通过", "需要修改", "拒绝", "Approved", "Changes Requested", "Rejected"].includes(verdict)) {
|
|
@@ -634,10 +771,12 @@ function checkReviewFact({ taskDir, artifactFile }) {
|
|
|
634
771
|
let gitRoot;
|
|
635
772
|
let head;
|
|
636
773
|
let baseline;
|
|
774
|
+
let diffBase;
|
|
637
775
|
try {
|
|
638
776
|
gitRoot = execFileSync("git", ["-C", taskDir, "rev-parse", "--show-toplevel"], { encoding: "utf8" }).trim();
|
|
639
777
|
head = execFileSync("git", ["-C", gitRoot, "rev-parse", "HEAD"], { encoding: "utf8" }).trim();
|
|
640
778
|
baseline = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewBaseline}^{commit}`], { encoding: "utf8" }).trim();
|
|
779
|
+
diffBase = execFileSync("git", ["-C", gitRoot, "rev-parse", `${reviewDiffBase || reviewBaseline}^{commit}`], { encoding: "utf8" }).trim();
|
|
641
780
|
}
|
|
642
781
|
catch {
|
|
643
782
|
return blockedResult("review-fact", `Unable to resolve review baseline '${reviewBaseline}' in the task repository; re-run review-code`);
|
|
@@ -647,7 +786,13 @@ function checkReviewFact({ taskDir, artifactFile }) {
|
|
|
647
786
|
}
|
|
648
787
|
let actualSnapshot;
|
|
649
788
|
try {
|
|
650
|
-
actualSnapshot = snapshotReview({
|
|
789
|
+
actualSnapshot = snapshotReview({
|
|
790
|
+
cwd: gitRoot,
|
|
791
|
+
mode: "worktree",
|
|
792
|
+
baseline,
|
|
793
|
+
diffBase,
|
|
794
|
+
globs: resolvePostReviewGlobs({}, loadPostReviewConfig(repoRoot))
|
|
795
|
+
});
|
|
651
796
|
}
|
|
652
797
|
catch {
|
|
653
798
|
return blockedResult("review-fact", `Unable to recompute reviewed diff fingerprint from baseline ${baseline.slice(0, 8)}; re-run review-code`);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { extractReviewBaseline, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, parseReviewVerdict, resolvePostReviewGlobs } from './review-fingerprint.js';
|
|
1
|
+
export { extractReviewBaseline, extractReviewDiffBase, extractReviewDiffFingerprint, extractReviewedSnapshotTree, findAuthoritativeReviewCodeArtifact, loadPostReviewConfig, parseReviewVerdict, resolvePostReviewGlobs } from './review-fingerprint.js';
|
|
2
2
|
export { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
3
3
|
//# sourceMappingURL=verification-review.js.map
|
|
@@ -19,14 +19,16 @@ const VERIFICATION_CATALOG = {
|
|
|
19
19
|
'block-task.completed': gate('block-task', 'blocked'),
|
|
20
20
|
'cancel-task.completed': gate('cancel-task', 'completed'),
|
|
21
21
|
'commit.completed': gate('commit', 'active'),
|
|
22
|
-
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['review-ledger', 'post-review-commit', '
|
|
22
|
+
'complete-task.preflight': { skill: 'complete-task', expectedState: 'active', mode: 'checks', checks: ['review-ledger', 'post-review-commit', 'platform-sync-preflight'] },
|
|
23
23
|
'complete-task.completed': gate('complete-task', 'completed'),
|
|
24
24
|
'create-pr.completed': gate('create-pr', 'active'),
|
|
25
25
|
'create-task.completed': gate('create-task', 'active'),
|
|
26
26
|
'import-codescan.completed': gate('import-codescan', 'active'),
|
|
27
27
|
'import-dependabot.completed': gate('import-dependabot', 'active'),
|
|
28
28
|
'import-issue.completed': gate('import-issue', 'active'),
|
|
29
|
-
'watch-pr.completed': gate('watch-pr', 'active')
|
|
29
|
+
'watch-pr.completed': gate('watch-pr', 'active'),
|
|
30
|
+
'run-task.paused': { skill: 'run-task', expectedState: 'active', mode: 'checks', checks: ['orchestration-state'] },
|
|
31
|
+
'run-task.completed': { skill: 'run-task', expectedState: 'active', mode: 'checks', checks: ['orchestration-state'] }
|
|
30
32
|
};
|
|
31
33
|
function failure(request, code, message, extra = {}) {
|
|
32
34
|
return {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
function git(repoRoot, indexFile, args) {
|
|
6
|
+
const env = indexFile ? { ...process.env, GIT_INDEX_FILE: indexFile } : process.env;
|
|
7
|
+
const result = spawnSync('git', args, { cwd: repoRoot, env, encoding: 'utf8' });
|
|
8
|
+
if (result.status !== 0) {
|
|
9
|
+
throw new Error(String(result.stderr || result.stdout || `git ${args[0]} failed`).trim());
|
|
10
|
+
}
|
|
11
|
+
return result.stdout;
|
|
12
|
+
}
|
|
13
|
+
function captureWorkspaceSnapshot(repoRoot) {
|
|
14
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-infra-orchestration-index-'));
|
|
15
|
+
const indexFile = path.join(tempDir, 'index');
|
|
16
|
+
try {
|
|
17
|
+
git(repoRoot, indexFile, ['read-tree', 'HEAD']);
|
|
18
|
+
git(repoRoot, indexFile, ['add', '-A', '--', ':/']);
|
|
19
|
+
const workspace = path.join(repoRoot, '.agents', 'workspace', 'active');
|
|
20
|
+
if (fs.existsSync(workspace)) {
|
|
21
|
+
git(repoRoot, indexFile, ['add', '-f', '--', '.agents/workspace/active']);
|
|
22
|
+
}
|
|
23
|
+
return git(repoRoot, indexFile, ['write-tree']).trim();
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function diffWorkspaceSnapshots(repoRoot, before, after) {
|
|
30
|
+
const output = git(repoRoot, null, ['diff', '--name-only', '-z', before, after]);
|
|
31
|
+
return output.split('\0').filter(Boolean);
|
|
32
|
+
}
|
|
33
|
+
export { captureWorkspaceSnapshot, diffWorkspaceSnapshots };
|
|
34
|
+
//# sourceMappingURL=workspace-snapshot.js.map
|