@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
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
4
4
|
import { locateActivityLog } from './activity-log.js';
|
|
5
5
|
import { parseImplementationInputs, selectPendingImplementationInput } from './implementation-inputs.js';
|
|
6
|
+
import { parseVerdict } from './review-artifacts.js';
|
|
6
7
|
import { extractSection, findSectionHeading } from './sections.js';
|
|
7
8
|
const artifactFamilyCatalog = [
|
|
8
9
|
{ family: 'analysis', sectionAliases: ['分析', 'Analysis'], heading: '分析', labels: ['需求分析报告', 'Requirements Analysis'] },
|
|
@@ -173,24 +174,6 @@ function resolveReviewedInput(taskDir, review, expectedFamily, diagnostics) {
|
|
|
173
174
|
return null;
|
|
174
175
|
}
|
|
175
176
|
}
|
|
176
|
-
function extractMarkdownSection(content, names) {
|
|
177
|
-
const lines = content.split(/\r?\n/);
|
|
178
|
-
const allowed = new Set(names);
|
|
179
|
-
const start = lines.findIndex((line) => {
|
|
180
|
-
const match = /^##\s+(.+?)\s*$/.exec(line.trim());
|
|
181
|
-
return Boolean(match?.[1] && allowed.has(match[1]));
|
|
182
|
-
});
|
|
183
|
-
if (start === -1)
|
|
184
|
-
return '';
|
|
185
|
-
let end = lines.length;
|
|
186
|
-
for (let index = start + 1; index < lines.length; index += 1) {
|
|
187
|
-
if (/^##\s+/.test(lines[index])) {
|
|
188
|
-
end = index;
|
|
189
|
-
break;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return lines.slice(start + 1, end).join('\n');
|
|
193
|
-
}
|
|
194
177
|
function assertWritableInventory(inventory) {
|
|
195
178
|
if (inventory.status === 'failed')
|
|
196
179
|
return inventory.error;
|
|
@@ -333,37 +316,6 @@ function resolveDecisionImplementationInput(inventory, review) {
|
|
|
333
316
|
return { error: error instanceof Error ? error.message : String(error) };
|
|
334
317
|
}
|
|
335
318
|
}
|
|
336
|
-
function normalizeVerdict(value) {
|
|
337
|
-
const normalized = value.trim().toLowerCase();
|
|
338
|
-
if (normalized === '通过' || normalized === 'approved')
|
|
339
|
-
return 'Approved';
|
|
340
|
-
if (normalized === '需要修改' || normalized === 'changes requested')
|
|
341
|
-
return 'Changes Requested';
|
|
342
|
-
if (normalized === '拒绝' || normalized === 'rejected')
|
|
343
|
-
return 'Rejected';
|
|
344
|
-
return null;
|
|
345
|
-
}
|
|
346
|
-
function parseVerdict(file) {
|
|
347
|
-
let content;
|
|
348
|
-
try {
|
|
349
|
-
content = fs.readFileSync(file, 'utf8');
|
|
350
|
-
}
|
|
351
|
-
catch (error) {
|
|
352
|
-
return { ok: false, message: String(error) };
|
|
353
|
-
}
|
|
354
|
-
const summary = extractMarkdownSection(content, ['审查摘要', 'Review Summary']);
|
|
355
|
-
const verdictMatch = /^[-*]?\s*\*\*(?:总体结论|Overall Verdict)\*\*[::]\s*(.+?)\s*$/im.exec(summary);
|
|
356
|
-
const verdict = verdictMatch?.[1] ? normalizeVerdict(verdictMatch[1]) : null;
|
|
357
|
-
if (!verdict)
|
|
358
|
-
return { ok: false, message: `cannot parse verdict in ${path.basename(file)}` };
|
|
359
|
-
if (verdict !== 'Approved')
|
|
360
|
-
return { ok: true, verdict };
|
|
361
|
-
const findings = /^[-*]?\s*\*\*(?:发现(AI 可处理)|Findings \(AI-actionable\))\*\*[::]\s*(.+?)\s*$/im.exec(summary)?.[1];
|
|
362
|
-
const counts = findings?.match(/(\d+)\s*(?:阻塞项|blockers?).*?(\d+)\s*(?:主要|majors?).*?(\d+)\s*(?:次要|minors?)/i);
|
|
363
|
-
if (!counts)
|
|
364
|
-
return { ok: false, message: `cannot parse findings count in ${path.basename(file)}` };
|
|
365
|
-
return { ok: true, verdict: counts.slice(1).some((value) => Number(value) > 0) ? 'Approved-with-issues' : 'Approved' };
|
|
366
|
-
}
|
|
367
319
|
function validateCompletedArtifact(taskDir, family, name, round) {
|
|
368
320
|
if (path.basename(name) !== name)
|
|
369
321
|
return { ok: false, error: { code: 'ARTIFACT_PATH_INVALID', message: 'artifact must be a top-level basename' } };
|
|
@@ -9,8 +9,9 @@ import { extractSubSection } from '../sections.js';
|
|
|
9
9
|
const USAGE = `Usage: ai task decisions [--task <ref> | -t <ref>] [--item <selector> | -i <selector>] [options]
|
|
10
10
|
ai task decisions <ref> [selector] [options]
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
Shows what still needs a maintainer's decision. Without a selector, it lists
|
|
13
|
+
the choices. With a selector, it shows the explanation and how to respond.
|
|
14
|
+
Read-only.
|
|
14
15
|
|
|
15
16
|
Omit <ref> Resolve the unique active task for the current branch.
|
|
16
17
|
<ref> Legacy positional task ref.
|
|
@@ -161,28 +162,29 @@ function findDecisionRecord(id, content) {
|
|
|
161
162
|
function titleOf(row, taskDir) {
|
|
162
163
|
const block = findDetailBlock(row, taskDir);
|
|
163
164
|
if (block) {
|
|
164
|
-
|
|
165
|
+
const title = block
|
|
165
166
|
.split('\n')[0]
|
|
166
167
|
.replace(/^###\s+/, '')
|
|
167
168
|
.replace(/\s*\[needs-human-decision\]\s*$/, '')
|
|
168
169
|
.trim();
|
|
170
|
+
return title.replace(new RegExp(`^${row.id}\\s*[::]\\s*`, 'i'), '').trim();
|
|
169
171
|
}
|
|
170
|
-
return
|
|
172
|
+
return '(explanation unavailable)';
|
|
173
|
+
}
|
|
174
|
+
function trackingOf(row) {
|
|
175
|
+
return `stage=${row.stage} · severity=${row.severity} · status=${row.status} · evidence=${row.evidence}`;
|
|
171
176
|
}
|
|
172
177
|
function renderList(rows, format, taskDir) {
|
|
173
178
|
if (rows.length === 0) {
|
|
174
|
-
process.stdout.write('
|
|
179
|
+
process.stdout.write('No pending decisions.\n');
|
|
175
180
|
return;
|
|
176
181
|
}
|
|
177
|
-
const headers = ['#', '
|
|
182
|
+
const headers = ['#', 'CHOICE', 'WHAT NEEDS A DECISION', 'TRACKING'];
|
|
178
183
|
const data = rows.map((r, i) => [
|
|
179
184
|
String(i + 1),
|
|
180
185
|
r.id,
|
|
181
|
-
r
|
|
182
|
-
r
|
|
183
|
-
r.status,
|
|
184
|
-
r.evidence,
|
|
185
|
-
titleOf(r, taskDir)
|
|
186
|
+
titleOf(r, taskDir),
|
|
187
|
+
trackingOf(r)
|
|
186
188
|
]);
|
|
187
189
|
if (format === 'markdown') {
|
|
188
190
|
const sep = headers.map(() => '---');
|
|
@@ -191,10 +193,28 @@ function renderList(rows, format, taskDir) {
|
|
|
191
193
|
`| ${sep.join(' | ')} |`,
|
|
192
194
|
...data.map((row) => `| ${row.join(' | ')} |`)
|
|
193
195
|
];
|
|
194
|
-
process.stdout.write(`${md.join('\n')}\n`);
|
|
196
|
+
process.stdout.write(`${md.join('\n')}\n\nView one item: ai task decisions <task-ref> <ordinal|ledger-id>\n`);
|
|
195
197
|
return;
|
|
196
198
|
}
|
|
197
|
-
process.stdout.write(`${formatTable(headers, data).join('\n')}\n`);
|
|
199
|
+
process.stdout.write(`${formatTable(headers, data).join('\n')}\n\nView one item: ai task decisions <task-ref> <ordinal|ledger-id>\n`);
|
|
200
|
+
}
|
|
201
|
+
function renderTracking(row, format) {
|
|
202
|
+
if (format === 'markdown') {
|
|
203
|
+
return [
|
|
204
|
+
'**Tracking**',
|
|
205
|
+
`- stage: \`${row.stage}\``,
|
|
206
|
+
`- severity: \`${row.severity}\``,
|
|
207
|
+
`- status: \`${row.status}\``,
|
|
208
|
+
`- evidence: \`${row.evidence}\``
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
return [
|
|
212
|
+
'Tracking:',
|
|
213
|
+
`stage: ${row.stage}`,
|
|
214
|
+
`severity: ${row.severity}`,
|
|
215
|
+
`status: ${row.status}`,
|
|
216
|
+
`evidence: ${row.evidence}`
|
|
217
|
+
];
|
|
198
218
|
}
|
|
199
219
|
function renderDetail(rows, selector, format, taskDir, content) {
|
|
200
220
|
const selected = selectDecisionItem(rows, selector);
|
|
@@ -204,25 +224,35 @@ function renderDetail(rows, selector, format, taskDir, content) {
|
|
|
204
224
|
}
|
|
205
225
|
const r = selected.row;
|
|
206
226
|
const block = findDetailBlock(r, taskDir);
|
|
227
|
+
const title = titleOf(r, taskDir);
|
|
207
228
|
const lines = [];
|
|
229
|
+
const decided = r.status === 'human-decided';
|
|
208
230
|
if (format === 'markdown') {
|
|
209
|
-
lines.push(
|
|
231
|
+
lines.push(`## ${decided ? 'Decision already recorded' : 'Decision needed'}: ${title}`, '');
|
|
232
|
+
if (!decided) {
|
|
233
|
+
lines.push('**How to record your choice**', `\`ai decide <task-ref> ${r.id} <your choice and rationale>\``, '');
|
|
234
|
+
}
|
|
235
|
+
lines.push('**Original context**', '');
|
|
210
236
|
}
|
|
211
237
|
else {
|
|
212
|
-
lines.push(`${
|
|
238
|
+
lines.push(`${decided ? 'Decision already recorded' : 'Decision needed'}: ${title}`, '');
|
|
239
|
+
if (!decided) {
|
|
240
|
+
lines.push('How to record your choice:', `ai decide <task-ref> ${r.id} <your choice and rationale>`, '');
|
|
241
|
+
}
|
|
242
|
+
lines.push('Original context:');
|
|
213
243
|
}
|
|
214
244
|
if (block) {
|
|
215
245
|
lines.push(block);
|
|
216
246
|
}
|
|
217
247
|
else {
|
|
218
|
-
lines.push(
|
|
248
|
+
lines.push(`This older task does not include a full explanation for ${r.id}.`, 'Use the tracking reference below to find the original context.');
|
|
219
249
|
}
|
|
220
|
-
if (
|
|
250
|
+
if (decided) {
|
|
221
251
|
const record = findDecisionRecord(r.id, content);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
252
|
+
lines.push('', format === 'markdown' ? '**Recorded choice**' : 'Recorded choice:');
|
|
253
|
+
lines.push(...(record.length ? record : ['No separate ruling record was found.']));
|
|
225
254
|
}
|
|
255
|
+
lines.push('', ...renderTracking(r, format));
|
|
226
256
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
227
257
|
}
|
|
228
258
|
function decisions(args = []) {
|
|
@@ -10,11 +10,12 @@ Print a deterministic Issue body extracted from a task's task.md.
|
|
|
10
10
|
Omit <ref> Resolve the unique active task for the current branch.
|
|
11
11
|
<ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
|
|
12
12
|
--template <path> Render the final body for the given GitHub Issue Form (scenario A);
|
|
13
|
-
without it, print the default '描述 + 需求' body (scenario B).
|
|
13
|
+
without it, print the default '任务输入 + 描述 + 需求' body (scenario B).
|
|
14
14
|
|
|
15
|
-
Only the task title, '## 描述' and '## 需求' sections are ever
|
|
16
|
-
task.md
|
|
15
|
+
Only the task title, '## 任务输入', '## 描述' and '## 需求' sections are ever
|
|
16
|
+
emitted; the rest of task.md is never written to the body.
|
|
17
17
|
`;
|
|
18
|
+
const TASK_INPUT_ALIASES = ['任务输入', 'Task Input'];
|
|
18
19
|
const DESCRIPTION_ALIASES = ['描述', 'Description'];
|
|
19
20
|
const REQUIREMENTS_ALIASES = ['需求', 'Requirements'];
|
|
20
21
|
/**
|
|
@@ -22,17 +23,21 @@ const REQUIREMENTS_ALIASES = ['需求', 'Requirements'];
|
|
|
22
23
|
* task.md actually uses, with empty sections falling back to `N/A`.
|
|
23
24
|
*/
|
|
24
25
|
function buildDefaultBody(content) {
|
|
26
|
+
const taskInputHeading = findSectionHeading(content, TASK_INPUT_ALIASES);
|
|
25
27
|
const descHeading = findSectionHeading(content, DESCRIPTION_ALIASES);
|
|
26
28
|
const reqHeading = findSectionHeading(content, REQUIREMENTS_ALIASES);
|
|
29
|
+
const taskInput = extractSection(content, TASK_INPUT_ALIASES) || PLACEHOLDER;
|
|
27
30
|
const description = extractSection(content, DESCRIPTION_ALIASES) || PLACEHOLDER;
|
|
28
31
|
const requirements = extractSection(content, REQUIREMENTS_ALIASES) || PLACEHOLDER;
|
|
29
|
-
return `## ${descHeading}\n\n${description}\n\n## ${reqHeading}\n\n${requirements}\n`;
|
|
32
|
+
return `## ${taskInputHeading}\n\n${taskInput}\n\n## ${descHeading}\n\n${description}\n\n## ${reqHeading}\n\n${requirements}\n`;
|
|
30
33
|
}
|
|
31
34
|
function readTaskFields(content) {
|
|
32
35
|
return {
|
|
33
36
|
title: extractTitle(content),
|
|
34
37
|
description: extractSection(content, DESCRIPTION_ALIASES),
|
|
35
|
-
requirements: extractSection(content, REQUIREMENTS_ALIASES)
|
|
38
|
+
requirements: extractSection(content, REQUIREMENTS_ALIASES),
|
|
39
|
+
taskInput: extractSection(content, TASK_INPUT_ALIASES),
|
|
40
|
+
taskInputHeading: findSectionHeading(content, TASK_INPUT_ALIASES)
|
|
36
41
|
};
|
|
37
42
|
}
|
|
38
43
|
function issueBody(args = []) {
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
const MANAGED_AGENTS = {
|
|
3
|
+
'agent-infra-lifecycle-executor': 'executor',
|
|
4
|
+
'agent-infra-lifecycle-reviewer': 'reviewer'
|
|
5
|
+
};
|
|
6
|
+
function managedDelegationRole(nativeAgent) {
|
|
7
|
+
return MANAGED_AGENTS[nativeAgent] ?? null;
|
|
8
|
+
}
|
|
9
|
+
function fail(code, message) {
|
|
10
|
+
return { ok: false, code, message };
|
|
11
|
+
}
|
|
12
|
+
function prepareDelegation(input, options = {}) {
|
|
13
|
+
return Object.freeze({
|
|
14
|
+
...input,
|
|
15
|
+
id: (options.id ?? randomUUID)(),
|
|
16
|
+
actualModel: null,
|
|
17
|
+
modelFallbackReason: null,
|
|
18
|
+
parentId: null,
|
|
19
|
+
childId: null,
|
|
20
|
+
spawnMode: null,
|
|
21
|
+
agent: null,
|
|
22
|
+
status: 'prepared',
|
|
23
|
+
afterFingerprint: null,
|
|
24
|
+
changedPaths: Object.freeze([]),
|
|
25
|
+
createdAt: (options.now ?? (() => new Date().toISOString()))(),
|
|
26
|
+
activatedAt: null,
|
|
27
|
+
sealedAt: null,
|
|
28
|
+
consumedAt: null
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function activateDelegation(receipt, event, options = {}) {
|
|
32
|
+
const managedRole = managedDelegationRole(event.nativeAgent);
|
|
33
|
+
if (!managedRole)
|
|
34
|
+
return fail('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
|
|
35
|
+
if (receipt.status !== 'prepared')
|
|
36
|
+
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected prepared`);
|
|
37
|
+
if (managedRole !== receipt.role)
|
|
38
|
+
return fail('DELEGATION_ROLE_MISMATCH', `managed role ${managedRole} does not match ${receipt.role}`);
|
|
39
|
+
if (!event.parentId || (receipt.parentId !== null && event.parentId !== receipt.parentId) || !event.childId || event.childId === event.parentId) {
|
|
40
|
+
return fail('DELEGATION_IDENTITY_INVALID', 'native parent/child identity does not match the prepared delegation');
|
|
41
|
+
}
|
|
42
|
+
if (event.spawnMode !== 'fresh')
|
|
43
|
+
return fail('DELEGATION_FORK_FORBIDDEN', `spawn mode '${event.spawnMode}' is not fresh`);
|
|
44
|
+
const actualModel = event.actualModel ?? null;
|
|
45
|
+
if (actualModel && receipt.requestedModel && actualModel !== receipt.requestedModel && !event.modelFallbackReason) {
|
|
46
|
+
return fail('DELEGATION_MODEL_FALLBACK_UNRECORDED', 'actual model differs from requested model without a fallback reason');
|
|
47
|
+
}
|
|
48
|
+
return { ok: true, receipt: Object.freeze({
|
|
49
|
+
...receipt,
|
|
50
|
+
status: 'activated',
|
|
51
|
+
parentId: event.parentId,
|
|
52
|
+
childId: event.childId,
|
|
53
|
+
spawnMode: event.spawnMode,
|
|
54
|
+
actualModel,
|
|
55
|
+
modelFallbackReason: event.modelFallbackReason ?? null,
|
|
56
|
+
activatedAt: (options.now ?? (() => new Date().toISOString()))()
|
|
57
|
+
}) };
|
|
58
|
+
}
|
|
59
|
+
function completeDelegationStage(receipt, event) {
|
|
60
|
+
if (receipt.status !== 'activated')
|
|
61
|
+
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected activated`);
|
|
62
|
+
if (event.stage !== receipt.stage || event.round !== receipt.round || event.artifact !== receipt.artifact) {
|
|
63
|
+
return fail('DELEGATION_STAGE_MISMATCH', 'stage completion identity does not match the active delegation');
|
|
64
|
+
}
|
|
65
|
+
const acceptedAgents = receipt.client === 'claude-code' ? ['claude', 'claude-code'] : [receipt.client];
|
|
66
|
+
if (!acceptedAgents.includes(event.agent)) {
|
|
67
|
+
return fail('DELEGATION_AGENT_MISMATCH', `stage agent '${event.agent}' does not match client '${receipt.client}'`);
|
|
68
|
+
}
|
|
69
|
+
return { ok: true, receipt: Object.freeze({ ...receipt, status: 'stage-completed', agent: event.agent }) };
|
|
70
|
+
}
|
|
71
|
+
function sealDelegation(receipt, event, options = {}) {
|
|
72
|
+
if (receipt.status !== 'stage-completed')
|
|
73
|
+
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected stage-completed`);
|
|
74
|
+
if (event.childId !== receipt.childId || event.exitCode !== 0)
|
|
75
|
+
return fail('DELEGATION_STOP_INVALID', 'native stop identity or exit status is invalid');
|
|
76
|
+
if (receipt.role === 'reviewer') {
|
|
77
|
+
const taskRoot = `.agents/workspace/active/${receipt.taskId}/`;
|
|
78
|
+
const allowed = new Set([
|
|
79
|
+
`${taskRoot}${receipt.artifact}`,
|
|
80
|
+
`${taskRoot}task.md`,
|
|
81
|
+
`${taskRoot}orchestration.json`
|
|
82
|
+
]);
|
|
83
|
+
const disallowed = event.changedPaths.find((entry) => !allowed.has(entry));
|
|
84
|
+
if (disallowed)
|
|
85
|
+
return fail('DELEGATION_REVIEWER_WRITE_FORBIDDEN', `reviewer changed forbidden path '${disallowed}'`);
|
|
86
|
+
}
|
|
87
|
+
return { ok: true, receipt: Object.freeze({
|
|
88
|
+
...receipt,
|
|
89
|
+
status: 'sealed',
|
|
90
|
+
afterFingerprint: event.afterFingerprint,
|
|
91
|
+
changedPaths: Object.freeze([...event.changedPaths]),
|
|
92
|
+
sealedAt: (options.now ?? (() => new Date().toISOString()))()
|
|
93
|
+
}) };
|
|
94
|
+
}
|
|
95
|
+
function consumeDelegation(receipt, options = {}) {
|
|
96
|
+
if (receipt.status === 'consumed')
|
|
97
|
+
return fail('DELEGATION_REPLAY', `delegation ${receipt.id} was already consumed`);
|
|
98
|
+
if (receipt.status !== 'sealed')
|
|
99
|
+
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected sealed`);
|
|
100
|
+
return { ok: true, receipt: Object.freeze({
|
|
101
|
+
...receipt,
|
|
102
|
+
status: 'consumed',
|
|
103
|
+
consumedAt: (options.now ?? (() => new Date().toISOString()))()
|
|
104
|
+
}) };
|
|
105
|
+
}
|
|
106
|
+
export { activateDelegation, completeDelegationStage, consumeDelegation, managedDelegationRole, prepareDelegation, sealDelegation };
|
|
107
|
+
//# sourceMappingURL=delegation-receipts.js.map
|
package/dist/lib/task/events.js
CHANGED
|
@@ -3,8 +3,11 @@ import { appendActivityEntry, locateActivityLog, pairEntries } from './activity-
|
|
|
3
3
|
import { buildArtifactLinkSection, artifactName, parseArtifactName, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
4
4
|
import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
5
5
|
import { consumeImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
|
|
6
|
+
import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
7
|
+
import { parseReviewSummary } from './review-artifacts.js';
|
|
6
8
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
7
9
|
import { findSectionHeading } from './sections.js';
|
|
10
|
+
import { completeOrchestrationStage, validateOrchestrationStage } from './orchestration.js';
|
|
8
11
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
9
12
|
const eventCatalog = [
|
|
10
13
|
'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
|
|
@@ -75,12 +78,75 @@ function validateTaskEventRequest(request) {
|
|
|
75
78
|
const FAMILY = {
|
|
76
79
|
analyze: { artifact: 'analysis', started: ['requirement-analysis', 'requirement-analysis-review', 'code'], completed: ['requirement-analysis', 'requirement-analysis-review', 'code'], target: 'requirement-analysis', label: 'Analyze Task' },
|
|
77
80
|
'review-analysis': { artifact: 'review-analysis', started: ['requirement-analysis', 'requirement-analysis-review'], completed: ['requirement-analysis', 'requirement-analysis-review'], target: 'requirement-analysis-review', label: 'Review Analysis' },
|
|
78
|
-
plan: { artifact: 'plan', started: ['requirement-analysis-review', 'technical-design-review'], completed: ['requirement-analysis-review', 'technical-design-review'], target: 'technical-design', label: 'Plan Task' },
|
|
81
|
+
plan: { artifact: 'plan', started: ['requirement-analysis-review', 'technical-design-review', 'commit'], completed: ['requirement-analysis-review', 'technical-design-review', 'commit'], target: 'technical-design', label: 'Plan Task' },
|
|
79
82
|
'review-plan': { artifact: 'review-plan', started: ['technical-design', 'technical-design-review'], completed: ['technical-design', 'technical-design-review'], target: 'technical-design-review', label: 'Review Plan' },
|
|
80
83
|
code: { artifact: 'code', started: ['technical-design-review', 'code-review'], completed: ['technical-design-review', 'code-review'], target: 'code', label: 'Code Task' },
|
|
81
84
|
'review-code': { artifact: 'review-code', started: ['code', 'code-review', 'commit'], completed: ['code', 'code-review', 'commit'], target: 'code-review', label: 'Review Code' },
|
|
82
|
-
'manual-validation': { artifact: 'manual-validation', started: ['code-review'], completed: ['code-review'], target: null, label: 'Complete Manual Validation' }
|
|
85
|
+
'manual-validation': { artifact: 'manual-validation', started: ['code-review', 'commit'], completed: ['code-review', 'commit'], target: null, label: 'Complete Manual Validation' }
|
|
83
86
|
};
|
|
87
|
+
const REVIEW_LEDGER_STAGES = {
|
|
88
|
+
'review-analysis': 'analysis',
|
|
89
|
+
'review-plan': 'plan',
|
|
90
|
+
'review-code': 'code'
|
|
91
|
+
};
|
|
92
|
+
function validateReviewFindingCounts(request, content, family, artifactPath) {
|
|
93
|
+
const stage = REVIEW_LEDGER_STAGES[family];
|
|
94
|
+
if (!stage || !artifactPath)
|
|
95
|
+
return null;
|
|
96
|
+
const rows = parseLedger(content);
|
|
97
|
+
const invalid = validateLedgerRows(rows);
|
|
98
|
+
if (invalid)
|
|
99
|
+
return { code: 'TASK_DOCUMENT_INVALID', message: `${invalid.code}: ${invalid.message}` };
|
|
100
|
+
const expected = summarizeLedgerStage(rows, stage).unresolvedFindingCounts;
|
|
101
|
+
const payload = {
|
|
102
|
+
blocker: request.blockers,
|
|
103
|
+
major: request.major,
|
|
104
|
+
minor: request.minor
|
|
105
|
+
};
|
|
106
|
+
let reportContent;
|
|
107
|
+
try {
|
|
108
|
+
reportContent = fs.readFileSync(artifactPath, 'utf8');
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return { code: 'EVENT_FINDING_COUNT_MISMATCH', message: String(error) };
|
|
112
|
+
}
|
|
113
|
+
const parsed = parseReviewSummary(reportContent);
|
|
114
|
+
if (!parsed.ok || !parsed.summary.counts) {
|
|
115
|
+
return {
|
|
116
|
+
code: 'EVENT_FINDING_COUNT_MISMATCH',
|
|
117
|
+
message: parsed.ok ? 'review summary finding counts are not finalized' : parsed.message
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const report = parsed.summary.counts;
|
|
121
|
+
const reportVerdict = parsed.summary.verdict === 'Approved'
|
|
122
|
+
? 'approved'
|
|
123
|
+
: parsed.summary.verdict === 'Changes Requested' ? 'changes-requested' : 'rejected';
|
|
124
|
+
const fields = [
|
|
125
|
+
['blocker', 'blockers'],
|
|
126
|
+
['major', 'major'],
|
|
127
|
+
['minor', 'minor']
|
|
128
|
+
];
|
|
129
|
+
const differences = fields.flatMap(([severity, cliField]) => {
|
|
130
|
+
const values = [
|
|
131
|
+
expected[severity] === payload[severity]
|
|
132
|
+
? null
|
|
133
|
+
: `${cliField} ledger ${expected[severity]}, payload ${payload[severity]}`,
|
|
134
|
+
expected[severity] === report[severity]
|
|
135
|
+
? null
|
|
136
|
+
: `${cliField} ledger ${expected[severity]}, report ${report[severity]}`
|
|
137
|
+
];
|
|
138
|
+
return values.filter((value) => value !== null);
|
|
139
|
+
});
|
|
140
|
+
if (request.verdict !== reportVerdict) {
|
|
141
|
+
differences.push(`verdict report ${reportVerdict}, payload ${request.verdict}`);
|
|
142
|
+
}
|
|
143
|
+
if (differences.length === 0)
|
|
144
|
+
return null;
|
|
145
|
+
return {
|
|
146
|
+
code: 'EVENT_FINDING_COUNT_MISMATCH',
|
|
147
|
+
message: `review summary, payload, and ${stage} ledger do not match: ${differences.join('; ')}`
|
|
148
|
+
};
|
|
149
|
+
}
|
|
84
150
|
function eventParts(event) {
|
|
85
151
|
const [family, suffix] = event.split('.');
|
|
86
152
|
return { family, phase: suffix === 'started' ? 'started' : suffix === 'awaiting-input' ? 'waiting' : 'completed' };
|
|
@@ -257,6 +323,24 @@ function applyTaskEvent(request, options = {}) {
|
|
|
257
323
|
const allowed = FAMILY[eventIdentity.family][eventIdentity.phase === 'started' ? 'started' : 'completed'];
|
|
258
324
|
if (!allowed.includes(currentStep))
|
|
259
325
|
return failed(normalized, { code: 'EVENT_TRANSITION_INVALID', message: `${normalized.event} is not allowed from '${currentStep}'` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
326
|
+
const findingCountError = validateReviewFindingCounts(normalized, content, eventIdentity.family, completedArtifact?.path ?? null);
|
|
327
|
+
if (findingCountError)
|
|
328
|
+
return failed(normalized, findingCountError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
329
|
+
if (eventIdentity.phase === 'completed' && !normalized.dryRun && eventIdentity.family !== 'manual-validation') {
|
|
330
|
+
const orchestrationStage = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
|
|
331
|
+
const provenance = validateOrchestrationStage(normalized.taskRef, {
|
|
332
|
+
stage: orchestrationStage,
|
|
333
|
+
round: normalized.round,
|
|
334
|
+
artifact: normalized.artifact,
|
|
335
|
+
role: eventIdentity.family.startsWith('review-') ? 'reviewer' : 'executor'
|
|
336
|
+
}, { repoRoot: options.repoRoot });
|
|
337
|
+
if (provenance.status === 'failed' || provenance.status === 'paused') {
|
|
338
|
+
return failed(normalized, {
|
|
339
|
+
code: 'EVENT_TRANSITION_INVALID',
|
|
340
|
+
message: provenance.error?.message ?? provenance.run?.pause?.message ?? 'orchestration provenance validation failed'
|
|
341
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
342
|
+
}
|
|
343
|
+
}
|
|
260
344
|
let metadata;
|
|
261
345
|
try {
|
|
262
346
|
metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
|
|
@@ -295,6 +379,21 @@ function applyTaskEvent(request, options = {}) {
|
|
|
295
379
|
const result = writeTask({ taskRef: normalized.taskRef, expectedState: 'active', dryRun: normalized.dryRun, mutations }, { ...options, metadataProvider: () => metadata });
|
|
296
380
|
if (result.status === 'failed')
|
|
297
381
|
return failed(normalized, result.error, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
|
|
382
|
+
if (eventIdentity.phase === 'completed' && !normalized.dryRun && eventIdentity.family !== 'manual-validation') {
|
|
383
|
+
const orchestrationStage = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
|
|
384
|
+
const orchestration = completeOrchestrationStage(normalized.taskRef, {
|
|
385
|
+
stage: orchestrationStage,
|
|
386
|
+
round: normalized.round,
|
|
387
|
+
artifact: normalized.artifact,
|
|
388
|
+
agent: normalized.agent
|
|
389
|
+
}, { repoRoot: options.repoRoot });
|
|
390
|
+
if (orchestration.status === 'failed' || orchestration.status === 'paused') {
|
|
391
|
+
return failed(normalized, {
|
|
392
|
+
code: 'EVENT_TRANSITION_INVALID',
|
|
393
|
+
message: orchestration.error?.message ?? orchestration.run?.pause?.message ?? 'orchestration provenance validation failed'
|
|
394
|
+
}, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
298
397
|
return {
|
|
299
398
|
status: result.status, changed: result.changed, event: normalized.event,
|
|
300
399
|
requestRef: normalized.taskRef, taskId: result.taskId, taskMdPath: result.taskMdPath,
|
|
@@ -73,6 +73,8 @@ function renderTemplateBody(formText, fields) {
|
|
|
73
73
|
const value = mapped.trim() === '' ? PLACEHOLDER : mapped;
|
|
74
74
|
sections.push(`### ${label}\n\n${value}`);
|
|
75
75
|
}
|
|
76
|
+
const taskInput = fields.taskInput.trim() === '' ? PLACEHOLDER : fields.taskInput;
|
|
77
|
+
sections.push(`### ${fields.taskInputHeading}\n\n${taskInput}`);
|
|
76
78
|
return `${sections.join('\n\n')}\n`;
|
|
77
79
|
}
|
|
78
80
|
export { requirementFieldLabels, renderTemplateBody, mapFieldValue, PLACEHOLDER };
|