@fitlab-ai/agent-infra 0.9.2 → 0.9.4
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 +12 -9
- package/README.zh-CN.md +12 -9
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +134 -108
- package/dist/lib/agent-clients/adapter.js +137 -4
- package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
- package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
- package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
- package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +27 -4
- package/dist/lib/agent-clients/adapters/codex.js +9 -0
- package/dist/lib/agent-clients/adapters/index.js +2 -2
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
- package/dist/lib/agent-clients/adapters/opencode.js +24 -3
- package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/dist/lib/agent-clients/config.js +42 -4
- package/dist/lib/agent-clients/next-steps.js +11 -2
- package/dist/lib/agent-clients/reconcile.js +2 -1
- package/dist/lib/agent-clients/registry.js +16 -2
- package/dist/lib/agent-clients/schema.js +18 -0
- package/dist/lib/agent-clients/tokens.js +49 -0
- package/dist/lib/agent-clients/types.js +1 -1
- package/dist/lib/defaults.json +1 -1
- package/dist/lib/git/workflow.js +54 -1
- package/dist/lib/internal/agent-client.js +61 -4
- package/dist/lib/internal/git-workflow.js +6 -1
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +29 -2
- package/dist/lib/internal/platform-issue.js +7 -0
- package/dist/lib/internal/platform-pr-review.js +132 -0
- package/dist/lib/internal/platform-pr.js +20 -2
- package/dist/lib/internal/platform-release-notes.js +52 -16
- package/dist/lib/internal/pr-review-grade.js +140 -0
- package/dist/lib/internal/release-workflow.js +13 -6
- package/dist/lib/internal/sandbox-control.js +24 -0
- package/dist/lib/internal/task-activity.js +105 -0
- package/dist/lib/internal/task-event.js +17 -1
- package/dist/lib/internal/task-ledger.js +12 -3
- package/dist/lib/internal/task-lifecycle.js +26 -1
- package/dist/lib/internal/task-orchestration.js +167 -18
- package/dist/lib/internal/task-review.js +8 -3
- package/dist/lib/platform/adapters.js +8 -1
- package/dist/lib/platform/completion-backfill.js +122 -0
- package/dist/lib/platform/github-client.js +19 -2
- package/dist/lib/platform/issue-comments.js +3 -2
- package/dist/lib/platform/merged-pr-equivalence.js +115 -5
- package/dist/lib/platform/pr-checks.js +65 -42
- package/dist/lib/platform/pr-review.js +166 -0
- package/dist/lib/platform/pull-requests.js +371 -4
- package/dist/lib/platform/release-notes.js +85 -3
- package/dist/lib/platform/releases.js +13 -5
- package/dist/lib/pr-review/evidence-grading.js +229 -0
- package/dist/lib/process-data/index.js +239 -0
- package/dist/lib/process-data/normalize.js +198 -0
- package/dist/lib/process-data/privacy.js +33 -0
- package/dist/lib/process-data/reconcile.js +95 -0
- package/dist/lib/process-data/repair.js +58 -0
- package/dist/lib/process-data/sources.js +345 -0
- package/dist/lib/process-data/store.js +282 -0
- package/dist/lib/process-data/types.js +2 -0
- package/dist/lib/run/index.js +4 -3
- package/dist/lib/run/tui.js +10 -13
- package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
- package/dist/lib/sandbox/capture.js +7 -2
- package/dist/lib/sandbox/commands/create.js +109 -248
- package/dist/lib/sandbox/commands/enter.js +15 -47
- package/dist/lib/sandbox/commands/list-running.js +16 -6
- package/dist/lib/sandbox/commands/ls.js +10 -6
- package/dist/lib/sandbox/commands/rebuild.js +4 -3
- package/dist/lib/sandbox/commands/refresh.js +4 -84
- package/dist/lib/sandbox/commands/rm.js +24 -3
- package/dist/lib/sandbox/commands/show.js +13 -4
- package/dist/lib/sandbox/commands/start.js +7 -4
- package/dist/lib/sandbox/config.js +2 -0
- package/dist/lib/sandbox/constants.js +6 -0
- package/dist/lib/sandbox/control/client.js +44 -0
- package/dist/lib/sandbox/control/protocol.js +35 -0
- package/dist/lib/sandbox/control/server.js +109 -0
- package/dist/lib/sandbox/dockerfile.js +36 -13
- package/dist/lib/sandbox/mounts.js +17 -4
- package/dist/lib/sandbox/recovery.js +128 -5
- package/dist/lib/sandbox/redaction.js +12 -0
- package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/dist/lib/sandbox/workspace-identity.js +95 -0
- package/dist/lib/sandbox/workspace-view.js +103 -0
- package/dist/lib/task/activity-intent.js +307 -0
- package/dist/lib/task/artifact-lifecycle.js +2 -1
- package/dist/lib/task/commands/log.js +15 -14
- package/dist/lib/task/commit-finalization.js +267 -0
- package/dist/lib/task/commit-intent.js +165 -0
- package/dist/lib/task/decision-intents.js +31 -17
- package/dist/lib/task/delegation-receipts.js +25 -4
- package/dist/lib/task/events.js +67 -28
- package/dist/lib/task/finalization-artifacts.js +35 -0
- package/dist/lib/task/implementation-inputs.js +54 -7
- package/dist/lib/task/ledger-intents.js +35 -1
- package/dist/lib/task/lifecycle-execution.js +46 -0
- package/dist/lib/task/orchestration.js +753 -54
- package/dist/lib/task/resolve-ref.js +41 -1
- package/dist/lib/task/review-finalization.js +29 -9
- package/dist/lib/task/task-execution-lock.js +231 -0
- package/dist/lib/task/verification-checks.js +1 -1
- package/dist/lib/task/verification-engine.js +218 -2
- package/dist/lib/task/verification.js +4 -3
- package/dist/lib/task/workspace-snapshot.js +17 -5
- package/lib/agent-clients/adapter.ts +201 -4
- package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
- package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
- package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
- package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
- package/lib/agent-clients/adapters/claude-code.ts +31 -4
- package/lib/agent-clients/adapters/codex.ts +9 -0
- package/lib/agent-clients/adapters/index.ts +2 -2
- package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
- package/lib/agent-clients/adapters/opencode.ts +27 -3
- package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/lib/agent-clients/config.ts +49 -4
- package/lib/agent-clients/next-steps.ts +15 -2
- package/lib/agent-clients/reconcile.ts +2 -1
- package/lib/agent-clients/registry.ts +19 -1
- package/lib/agent-clients/schema.ts +18 -0
- package/lib/agent-clients/tokens.ts +53 -0
- package/lib/agent-clients/types.ts +15 -1
- package/lib/defaults.json +1 -1
- package/lib/git/workflow.ts +66 -2
- package/lib/internal/agent-client.ts +59 -4
- package/lib/internal/git-workflow.ts +5 -1
- package/lib/internal/platform-checks.ts +4 -4
- package/lib/internal/platform-comment.ts +17 -2
- package/lib/internal/platform-issue.ts +4 -0
- package/lib/internal/platform-pr-review.ts +104 -0
- package/lib/internal/platform-pr.ts +14 -1
- package/lib/internal/platform-release-notes.ts +59 -16
- package/lib/internal/pr-review-grade.ts +130 -0
- package/lib/internal/release-workflow.ts +13 -6
- package/lib/internal/sandbox-control.ts +24 -0
- package/lib/internal/task-activity.ts +91 -0
- package/lib/internal/task-event.ts +9 -1
- package/lib/internal/task-ledger.ts +12 -3
- package/lib/internal/task-lifecycle.ts +26 -1
- package/lib/internal/task-orchestration.ts +123 -14
- package/lib/internal/task-review.ts +8 -3
- package/lib/platform/adapters.ts +29 -2
- package/lib/platform/completion-backfill.ts +134 -0
- package/lib/platform/github-client.ts +20 -2
- package/lib/platform/issue-comments.ts +3 -2
- package/lib/platform/merged-pr-equivalence.ts +127 -5
- package/lib/platform/pr-checks.ts +73 -46
- package/lib/platform/pr-review.ts +201 -0
- package/lib/platform/pull-requests.ts +444 -3
- package/lib/platform/release-notes.ts +100 -4
- package/lib/platform/releases.ts +16 -4
- package/lib/pr-review/evidence-grading.ts +318 -0
- package/lib/process-data/index.ts +224 -0
- package/lib/process-data/normalize.ts +202 -0
- package/lib/process-data/privacy.ts +41 -0
- package/lib/process-data/reconcile.ts +110 -0
- package/lib/process-data/repair.ts +64 -0
- package/lib/process-data/sources.ts +354 -0
- package/lib/process-data/store.ts +315 -0
- package/lib/process-data/types.ts +135 -0
- package/lib/run/index.ts +5 -3
- package/lib/run/tui.ts +14 -12
- package/lib/sandbox/agent-client-reconciler.ts +44 -2
- package/lib/sandbox/capture.ts +9 -2
- package/lib/sandbox/commands/create.ts +126 -280
- package/lib/sandbox/commands/enter.ts +13 -59
- package/lib/sandbox/commands/list-running.ts +23 -6
- package/lib/sandbox/commands/ls.ts +22 -6
- package/lib/sandbox/commands/rebuild.ts +4 -3
- package/lib/sandbox/commands/refresh.ts +4 -126
- package/lib/sandbox/commands/rm.ts +24 -3
- package/lib/sandbox/commands/show.ts +30 -4
- package/lib/sandbox/commands/start.ts +6 -4
- package/lib/sandbox/config.ts +4 -0
- package/lib/sandbox/constants.ts +8 -0
- package/lib/sandbox/control/client.ts +55 -0
- package/lib/sandbox/control/protocol.ts +75 -0
- package/lib/sandbox/control/server.ts +117 -0
- package/lib/sandbox/dockerfile.ts +66 -13
- package/lib/sandbox/mounts.ts +24 -4
- package/lib/sandbox/recovery.ts +163 -6
- package/lib/sandbox/redaction.ts +14 -0
- package/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/lib/sandbox/tool-types.ts +16 -0
- package/lib/sandbox/workspace-identity.ts +123 -0
- package/lib/sandbox/workspace-view.ts +143 -0
- package/lib/server/server.schema.json +2 -2
- package/lib/task/activity-intent.ts +417 -0
- package/lib/task/artifact-lifecycle.ts +2 -1
- package/lib/task/commands/log.ts +15 -14
- package/lib/task/commit-finalization.ts +356 -0
- package/lib/task/commit-intent.ts +230 -0
- package/lib/task/decision-intents.ts +32 -12
- package/lib/task/delegation-receipts.ts +34 -5
- package/lib/task/events.ts +75 -30
- package/lib/task/finalization-artifacts.ts +58 -0
- package/lib/task/implementation-inputs.ts +62 -8
- package/lib/task/ledger-intents.ts +43 -3
- package/lib/task/lifecycle-execution.ts +106 -0
- package/lib/task/orchestration.ts +1019 -72
- package/lib/task/resolve-ref.ts +46 -1
- package/lib/task/review-finalization.ts +40 -9
- package/lib/task/task-execution-lock.ts +296 -0
- package/lib/task/verification-checks.ts +1 -1
- package/lib/task/verification-engine.ts +249 -2
- package/lib/task/verification.ts +5 -3
- package/lib/task/workspace-snapshot.ts +26 -5
- package/package.json +3 -3
- package/templates/.agents/QUICKSTART.en.md +25 -8
- package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
- package/templates/.agents/README.en.md +10 -7
- package/templates/.agents/README.zh-CN.md +10 -7
- package/templates/.agents/hooks/lifecycle-delegation.js +15 -4
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/create-issue.github.en.md +4 -2
- package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
- package/templates/.agents/rules/human-decision-context.en.md +11 -2
- package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
- package/templates/.agents/rules/issue-fields.github.en.md +3 -1
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-sync.github.en.md +7 -3
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
- package/templates/.agents/rules/lifecycle-orchestration.en.md +5 -2
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +5 -2
- package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
- package/templates/.agents/rules/next-step-output.en.md +3 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +17 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +17 -0
- package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-sync.en.md +3 -1
- package/templates/.agents/rules/pr-sync.github.en.md +11 -1
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
- package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
- package/templates/.agents/rules/release-commands.en.md +1 -1
- package/templates/.agents/rules/release-commands.github.en.md +12 -2
- package/templates/.agents/rules/release-commands.github.zh-CN.md +12 -2
- package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/task-management.en.md +13 -0
- package/templates/.agents/rules/task-management.zh-CN.md +13 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -7
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -7
- package/templates/.agents/skills/block-task/SKILL.en.md +7 -5
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +7 -5
- package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +10 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +12 -9
- package/templates/.agents/skills/commit/SKILL.en.md +23 -11
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +29 -11
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +49 -0
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +55 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +4 -4
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +22 -7
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +22 -7
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -5
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -5
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
- package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -3
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
- package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -3
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/plan-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/release/SKILL.en.md +16 -10
- package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
- package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-pr/SKILL.en.md +186 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +186 -0
- package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +8 -8
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/run-task/config/verify.en.json +2 -1
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +2 -1
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +98 -107
- package/templates/.agents/skills/watch-pr/SKILL.en.md +13 -13
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
- package/templates/.agents/workflows/code-review.en.yaml +1 -1
- package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
- package/templates/.agents/workflows/feature-development.en.yaml +4 -4
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
- package/templates/.agents/workflows/refactoring.en.yaml +3 -3
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
- package/templates/.claude/commands/post-release.en.md +1 -0
- package/templates/.claude/commands/post-release.zh-CN.md +1 -0
- package/templates/.claude/commands/review-pr.en.md +8 -0
- package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.claude/rules/tool-preferences.en.md +20 -0
- package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
- package/templates/.opencode/commands/post-release.en.md +2 -0
- package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
- package/templates/.opencode/commands/review-pr.en.md +11 -0
- package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
- package/templates/.opencode/commands/watch-pr.en.md +1 -1
- package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
- package/dist/lib/agent-clients/adapters/gemini-cli.js +0 -64
- package/lib/agent-clients/adapters/gemini-cli.ts +0 -65
- package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
- package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
- package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
- package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/release.en.toml +0 -8
- package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/run-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/test.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
- package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
- package/templates/.gemini/settings.json +0 -3
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { createHash, randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
class CommitIntentError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
constructor(code, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'CommitIntentError';
|
|
9
|
+
this.code = code;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function commitIntentPath(taskDir) {
|
|
13
|
+
return path.join(taskDir, 'commit-intent.json');
|
|
14
|
+
}
|
|
15
|
+
function digest(value) {
|
|
16
|
+
return createHash('sha256').update(value).digest('hex');
|
|
17
|
+
}
|
|
18
|
+
function serialize(value) {
|
|
19
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
20
|
+
}
|
|
21
|
+
function atomicWrite(file, value) {
|
|
22
|
+
const temp = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
23
|
+
try {
|
|
24
|
+
fs.writeFileSync(temp, serialize(value), { encoding: 'utf8', flag: 'wx', mode: 0o600 });
|
|
25
|
+
const handle = fs.openSync(temp, 'r+');
|
|
26
|
+
try {
|
|
27
|
+
fs.fsyncSync(handle);
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
fs.closeSync(handle);
|
|
31
|
+
}
|
|
32
|
+
fs.renameSync(temp, file);
|
|
33
|
+
fs.chmodSync(file, 0o600);
|
|
34
|
+
try {
|
|
35
|
+
const directory = fs.openSync(path.dirname(file), 'r');
|
|
36
|
+
try {
|
|
37
|
+
fs.fsyncSync(directory);
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
fs.closeSync(directory);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const code = error.code;
|
|
45
|
+
if (!['EACCES', 'EISDIR', 'EINVAL', 'EPERM'].includes(code ?? ''))
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
if (fs.existsSync(temp))
|
|
51
|
+
fs.unlinkSync(temp);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function isRecord(value) {
|
|
55
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
56
|
+
}
|
|
57
|
+
function keysEqual(value, expected) {
|
|
58
|
+
return Object.keys(value).sort().join('\0') === [...expected].sort().join('\0');
|
|
59
|
+
}
|
|
60
|
+
function validPushEvidence(value) {
|
|
61
|
+
return value === null || (isRecord(value)
|
|
62
|
+
&& keysEqual(value, ['remote', 'ref', 'head'])
|
|
63
|
+
&& ['remote', 'ref', 'head'].every((key) => typeof value[key] === 'string' && value[key] !== ''));
|
|
64
|
+
}
|
|
65
|
+
function validOrchestration(value) {
|
|
66
|
+
if (value === null)
|
|
67
|
+
return true;
|
|
68
|
+
if (!isRecord(value) || !keysEqual(value, [
|
|
69
|
+
'runId', 'receiptId', 'authorizationIssuedAt', 'sourceRunDigest', 'plannedRunDigest',
|
|
70
|
+
'completionUpdatedAt', 'plannedReceipt'
|
|
71
|
+
]))
|
|
72
|
+
return false;
|
|
73
|
+
return [
|
|
74
|
+
'runId', 'receiptId', 'authorizationIssuedAt', 'sourceRunDigest', 'plannedRunDigest', 'completionUpdatedAt'
|
|
75
|
+
].every((key) => typeof value[key] === 'string' && value[key] !== '') && isRecord(value.plannedReceipt);
|
|
76
|
+
}
|
|
77
|
+
function parseIntent(raw, taskId) {
|
|
78
|
+
let value;
|
|
79
|
+
try {
|
|
80
|
+
value = JSON.parse(raw);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
throw new CommitIntentError('ORCHESTRATION_COMMIT_INTENT_INVALID', 'commit intent is not valid JSON');
|
|
84
|
+
}
|
|
85
|
+
if (!isRecord(value) || !keysEqual(value, [
|
|
86
|
+
'schemaVersion', 'taskId', 'mode', 'phase', 'tokenDigest', 'baselineHead', 'committedHead',
|
|
87
|
+
'pushEvidence', 'orchestration', 'createdAt', 'updatedAt'
|
|
88
|
+
])) {
|
|
89
|
+
throw new CommitIntentError('ORCHESTRATION_COMMIT_INTENT_INVALID', 'commit intent has an invalid schema');
|
|
90
|
+
}
|
|
91
|
+
if (value.schemaVersion !== 1
|
|
92
|
+
|| value.taskId !== taskId
|
|
93
|
+
|| !['standalone', 'orchestrated'].includes(String(value.mode))
|
|
94
|
+
|| !['prepared', 'committed', 'pushed'].includes(String(value.phase))
|
|
95
|
+
|| typeof value.tokenDigest !== 'string'
|
|
96
|
+
|| !/^[a-f0-9]{64}$/.test(value.tokenDigest)
|
|
97
|
+
|| typeof value.baselineHead !== 'string'
|
|
98
|
+
|| (value.committedHead !== null && typeof value.committedHead !== 'string')
|
|
99
|
+
|| !validPushEvidence(value.pushEvidence)
|
|
100
|
+
|| !validOrchestration(value.orchestration)
|
|
101
|
+
|| typeof value.createdAt !== 'string'
|
|
102
|
+
|| typeof value.updatedAt !== 'string'
|
|
103
|
+
|| (value.mode === 'standalone') !== (value.orchestration === null)) {
|
|
104
|
+
throw new CommitIntentError('ORCHESTRATION_COMMIT_INTENT_INVALID', 'commit intent has invalid field values');
|
|
105
|
+
}
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
function tokenMatches(actualDigest, token) {
|
|
109
|
+
const actual = Buffer.from(actualDigest, 'hex');
|
|
110
|
+
const supplied = Buffer.from(digest(token), 'hex');
|
|
111
|
+
return actual.length === supplied.length && timingSafeEqual(actual, supplied);
|
|
112
|
+
}
|
|
113
|
+
function readCommitIntent(taskDir, taskId, token) {
|
|
114
|
+
const file = commitIntentPath(taskDir);
|
|
115
|
+
const intent = parseIntent(fs.readFileSync(file, 'utf8'), taskId);
|
|
116
|
+
if (token !== undefined && !tokenMatches(intent.tokenDigest, token)) {
|
|
117
|
+
throw new CommitIntentError('ORCHESTRATION_COMMIT_INTENT_TOKEN_MISMATCH', 'commit intent token does not match');
|
|
118
|
+
}
|
|
119
|
+
return intent;
|
|
120
|
+
}
|
|
121
|
+
function createCommitIntent(taskDir, input, options = {}) {
|
|
122
|
+
const file = commitIntentPath(taskDir);
|
|
123
|
+
if (fs.existsSync(file)) {
|
|
124
|
+
throw new CommitIntentError('ORCHESTRATION_COMMIT_INTENT_BUSY', 'an active commit intent already exists');
|
|
125
|
+
}
|
|
126
|
+
const token = (options.token ?? (() => randomBytes(32).toString('base64url')))();
|
|
127
|
+
const intent = Object.freeze({ schemaVersion: 1, ...input, tokenDigest: digest(token) });
|
|
128
|
+
atomicWrite(file, intent);
|
|
129
|
+
return { intent, token };
|
|
130
|
+
}
|
|
131
|
+
function updateCommitIntent(taskDir, taskId, token, updates) {
|
|
132
|
+
const current = readCommitIntent(taskDir, taskId, token);
|
|
133
|
+
const nextPhase = updates.phase ?? current.phase;
|
|
134
|
+
const transitions = {
|
|
135
|
+
prepared: ['prepared', 'committed', 'pushed'],
|
|
136
|
+
committed: ['committed', 'pushed'],
|
|
137
|
+
pushed: ['pushed']
|
|
138
|
+
};
|
|
139
|
+
if (!transitions[current.phase].includes(nextPhase)) {
|
|
140
|
+
throw new CommitIntentError('ORCHESTRATION_COMMIT_INTENT_STATE_INVALID', `cannot move commit intent from ${current.phase} to ${nextPhase}`);
|
|
141
|
+
}
|
|
142
|
+
const updated = Object.freeze({ ...current, ...updates, phase: nextPhase });
|
|
143
|
+
atomicWrite(commitIntentPath(taskDir), updated);
|
|
144
|
+
return updated;
|
|
145
|
+
}
|
|
146
|
+
function removeCommitIntent(taskDir, taskId, token) {
|
|
147
|
+
readCommitIntent(taskDir, taskId, token);
|
|
148
|
+
fs.unlinkSync(commitIntentPath(taskDir));
|
|
149
|
+
}
|
|
150
|
+
function removeCommitIntentByDigest(taskDir, taskId, expectedDigest) {
|
|
151
|
+
const file = commitIntentPath(taskDir);
|
|
152
|
+
const raw = fs.readFileSync(file);
|
|
153
|
+
parseIntent(raw.toString('utf8'), taskId);
|
|
154
|
+
const actual = digest(raw);
|
|
155
|
+
const expected = Buffer.from(expectedDigest, 'hex');
|
|
156
|
+
const observed = Buffer.from(actual, 'hex');
|
|
157
|
+
if (!/^[a-f0-9]{64}$/.test(expectedDigest)
|
|
158
|
+
|| expected.length !== observed.length
|
|
159
|
+
|| !timingSafeEqual(expected, observed)) {
|
|
160
|
+
throw new CommitIntentError('ORCHESTRATION_COMMIT_RECOVERY_REQUIRED', 'commit intent changed after recovery inspection');
|
|
161
|
+
}
|
|
162
|
+
fs.unlinkSync(file);
|
|
163
|
+
}
|
|
164
|
+
export { CommitIntentError, commitIntentPath, createCommitIntent, digest, readCommitIntent, removeCommitIntent, removeCommitIntentByDigest, serialize, updateCommitIntent };
|
|
165
|
+
//# sourceMappingURL=commit-intent.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { appendActivityEntry, locateActivityLog } from './activity-log.js';
|
|
3
3
|
import { isDecisionItem, listDecisionItems, selectDecisionItem } from './decision-items.js';
|
|
4
|
-
import { createImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
|
|
4
|
+
import { createImplementationInput, finalizeImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs, selectDeclaredImplementationInput } from './implementation-inputs.js';
|
|
5
5
|
import { LEDGER_COLUMNS, LEDGER_HEADINGS, parseLedger } from './ledger.js';
|
|
6
6
|
import { resolveTaskContext } from './resolve-ref.js';
|
|
7
7
|
import { extractSection, extractSubSection, findSectionHeading } from './sections.js';
|
|
@@ -45,7 +45,7 @@ function existingDecision(content, row, decision, needsImplementation) {
|
|
|
45
45
|
? { recordId, implementationInputId: null }
|
|
46
46
|
: { conflict: '--needs-implementation is only valid for code-stage decisions' };
|
|
47
47
|
const inputs = parseImplementationInputs(content).rows.filter((input) => input.ledgerId === row.id && input.decisionEvidence === row.evidence);
|
|
48
|
-
if (inputs.length !== 1 || inputs[0].needsImplementation !== needsImplementation) {
|
|
48
|
+
if (inputs.length !== 1 || (needsImplementation !== undefined && inputs[0].needsImplementation !== needsImplementation)) {
|
|
49
49
|
return { conflict: `implementation intent for '${row.id}' conflicts with the existing decision` };
|
|
50
50
|
}
|
|
51
51
|
return { recordId, implementationInputId: inputs[0].id };
|
|
@@ -87,10 +87,29 @@ function applyHumanDecision(request, options = {}) {
|
|
|
87
87
|
return failed('DECISION_TARGET_INVALID', message, resolved.taskId);
|
|
88
88
|
}
|
|
89
89
|
const row = selected.row;
|
|
90
|
-
if (row.stage === 'code' && request.needsImplementation === undefined)
|
|
91
|
-
return failed('DECISION_PAYLOAD_INVALID', 'code-stage decisions require --needs-implementation true|false', resolved.taskId, row.id);
|
|
92
90
|
if (row.stage !== 'code' && request.needsImplementation !== undefined)
|
|
93
91
|
return failed('DECISION_PAYLOAD_INVALID', '--needs-implementation is only valid for code-stage decisions', resolved.taskId, row.id);
|
|
92
|
+
let parsedInputs = null;
|
|
93
|
+
let declaredInput = null;
|
|
94
|
+
let effectiveNeedsImplementation = request.needsImplementation;
|
|
95
|
+
if (row.stage === 'code') {
|
|
96
|
+
try {
|
|
97
|
+
parsedInputs = parseImplementationInputs(content);
|
|
98
|
+
declaredInput = selectDeclaredImplementationInput(parsedInputs.rows, row.id, row.evidence);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
return failed('DECISION_DOCUMENT_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId, row.id);
|
|
102
|
+
}
|
|
103
|
+
if (declaredInput) {
|
|
104
|
+
if (request.needsImplementation !== undefined && request.needsImplementation !== declaredInput.needsImplementation) {
|
|
105
|
+
return failed('DECISION_CONFLICT', `implementation intent for '${row.id}' conflicts with its declaration`, resolved.taskId, row.id);
|
|
106
|
+
}
|
|
107
|
+
effectiveNeedsImplementation = declaredInput.needsImplementation;
|
|
108
|
+
}
|
|
109
|
+
else if (effectiveNeedsImplementation === undefined) {
|
|
110
|
+
return failed('DECISION_PAYLOAD_INVALID', 'code-stage decisions require --needs-implementation true|false when no implementation intent was declared', resolved.taskId, row.id);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
94
113
|
let metadata;
|
|
95
114
|
try {
|
|
96
115
|
metadata = (options.metadataProvider ?? captureTaskWriteMetadata)();
|
|
@@ -119,22 +138,17 @@ function applyHumanDecision(request, options = {}) {
|
|
|
119
138
|
];
|
|
120
139
|
let implementationInputId = null;
|
|
121
140
|
if (row.stage === 'code') {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const input = createImplementationInput(parsed.rows, {
|
|
130
|
-
ledgerId: row.id, decisionEvidence: evidence,
|
|
131
|
-
needsImplementation: request.needsImplementation, decidedAt: metadata.timestamp
|
|
132
|
-
});
|
|
133
|
-
implementationInputId = input.id;
|
|
141
|
+
const nextInputs = declaredInput
|
|
142
|
+
? finalizeImplementationInput(parsedInputs.rows, declaredInput.id, evidence, metadata.timestamp)
|
|
143
|
+
: [...parsedInputs.rows, createImplementationInput(parsedInputs.rows, {
|
|
144
|
+
ledgerId: row.id, decisionEvidence: evidence,
|
|
145
|
+
needsImplementation: effectiveNeedsImplementation, decidedAt: metadata.timestamp
|
|
146
|
+
})];
|
|
147
|
+
implementationInputId = declaredInput?.id ?? nextInputs[nextInputs.length - 1].id;
|
|
134
148
|
mutations.push({
|
|
135
149
|
kind: 'section', aliases: IMPLEMENTATION_INPUT_ALIASES,
|
|
136
150
|
heading: findSectionHeading(content, [...IMPLEMENTATION_INPUT_ALIASES]),
|
|
137
|
-
body: renderImplementationInputs(
|
|
151
|
+
body: renderImplementationInputs(nextInputs)
|
|
138
152
|
});
|
|
139
153
|
}
|
|
140
154
|
mutations.push({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
2
3
|
const MANAGED_AGENTS = {
|
|
3
4
|
'agent-infra-lifecycle-executor': 'executor',
|
|
4
5
|
'agent-infra-lifecycle-reviewer': 'reviewer'
|
|
@@ -14,7 +15,9 @@ function prepareDelegation(input, options = {}) {
|
|
|
14
15
|
...input,
|
|
15
16
|
id: (options.id ?? randomUUID)(),
|
|
16
17
|
actualModel: null,
|
|
18
|
+
actualReasoningEffort: null,
|
|
17
19
|
modelFallbackReason: null,
|
|
20
|
+
reasoningEffortFallbackReason: null,
|
|
18
21
|
parentId: null,
|
|
19
22
|
childId: null,
|
|
20
23
|
spawnMode: null,
|
|
@@ -41,10 +44,27 @@ function activateDelegation(receipt, event, options = {}) {
|
|
|
41
44
|
}
|
|
42
45
|
if (event.spawnMode !== 'fresh')
|
|
43
46
|
return fail('DELEGATION_FORK_FORBIDDEN', `spawn mode '${event.spawnMode}' is not fresh`);
|
|
44
|
-
const actualModel = event.actualModel
|
|
45
|
-
if (actualModel
|
|
47
|
+
const actualModel = event.actualModel;
|
|
48
|
+
if (!actualModel || actualModel.trim() !== actualModel) {
|
|
49
|
+
return fail('DELEGATION_MODEL_IDENTITY_MISSING', 'native start event must provide a non-empty actual model identity');
|
|
50
|
+
}
|
|
51
|
+
if (actualModel !== receipt.requestedModel && (!event.modelFallbackReason || event.modelFallbackReason.trim() === '')) {
|
|
46
52
|
return fail('DELEGATION_MODEL_FALLBACK_UNRECORDED', 'actual model differs from requested model without a fallback reason');
|
|
47
53
|
}
|
|
54
|
+
const actualReasoningEffort = event.actualReasoningEffort;
|
|
55
|
+
if (!actualReasoningEffort || actualReasoningEffort.trim() !== actualReasoningEffort) {
|
|
56
|
+
return fail('DELEGATION_REASONING_EFFORT_MISSING', 'native start event must provide a non-empty actual reasoning effort');
|
|
57
|
+
}
|
|
58
|
+
if (actualReasoningEffort !== receipt.requestedReasoningEffort
|
|
59
|
+
&& (!event.reasoningEffortFallbackReason || event.reasoningEffortFallbackReason.trim() === '')) {
|
|
60
|
+
return fail('DELEGATION_REASONING_EFFORT_FALLBACK_UNRECORDED', 'actual reasoning effort differs from requested effort without a fallback reason');
|
|
61
|
+
}
|
|
62
|
+
if (actualModel === receipt.requestedModel && event.modelFallbackReason) {
|
|
63
|
+
return fail('DELEGATION_MODEL_FALLBACK_INVALID', 'model fallback reason is only valid when actual model differs');
|
|
64
|
+
}
|
|
65
|
+
if (actualReasoningEffort === receipt.requestedReasoningEffort && event.reasoningEffortFallbackReason) {
|
|
66
|
+
return fail('DELEGATION_REASONING_EFFORT_FALLBACK_INVALID', 'reasoning-effort fallback reason is only valid when actual effort differs');
|
|
67
|
+
}
|
|
48
68
|
return { ok: true, receipt: Object.freeze({
|
|
49
69
|
...receipt,
|
|
50
70
|
status: 'activated',
|
|
@@ -52,7 +72,9 @@ function activateDelegation(receipt, event, options = {}) {
|
|
|
52
72
|
childId: event.childId,
|
|
53
73
|
spawnMode: event.spawnMode,
|
|
54
74
|
actualModel,
|
|
75
|
+
actualReasoningEffort,
|
|
55
76
|
modelFallbackReason: event.modelFallbackReason ?? null,
|
|
77
|
+
reasoningEffortFallbackReason: event.reasoningEffortFallbackReason ?? null,
|
|
56
78
|
activatedAt: (options.now ?? (() => new Date().toISOString()))()
|
|
57
79
|
}) };
|
|
58
80
|
}
|
|
@@ -62,8 +84,7 @@ function completeDelegationStage(receipt, event) {
|
|
|
62
84
|
if (event.stage !== receipt.stage || event.round !== receipt.round || event.artifact !== receipt.artifact) {
|
|
63
85
|
return fail('DELEGATION_STAGE_MISMATCH', 'stage completion identity does not match the active delegation');
|
|
64
86
|
}
|
|
65
|
-
|
|
66
|
-
if (!acceptedAgents.includes(event.agent)) {
|
|
87
|
+
if (normalizeAgentToken(event.agent) !== normalizeAgentToken(receipt.client)) {
|
|
67
88
|
return fail('DELEGATION_AGENT_MISMATCH', `stage agent '${event.agent}' does not match client '${receipt.client}'`);
|
|
68
89
|
}
|
|
69
90
|
return { ok: true, receipt: Object.freeze({ ...receipt, status: 'stage-completed', agent: event.agent }) };
|
package/dist/lib/task/events.js
CHANGED
|
@@ -7,7 +7,9 @@ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.
|
|
|
7
7
|
import { parseReviewSummary } from './review-artifacts.js';
|
|
8
8
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
9
9
|
import { findSectionHeading } from './sections.js';
|
|
10
|
-
import {
|
|
10
|
+
import { validateLifecycleExecution } from './lifecycle-execution.js';
|
|
11
|
+
import { commitOrchestrationStageCompletion } from './orchestration.js';
|
|
12
|
+
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
11
13
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
12
14
|
const eventCatalog = [
|
|
13
15
|
'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
|
|
@@ -22,17 +24,17 @@ const BASE_FIELDS = new Set(['taskRef', 'event', 'agent', 'dryRun']);
|
|
|
22
24
|
const SCHEMAS = {
|
|
23
25
|
'analyze.started': { optional: ['round'] },
|
|
24
26
|
'analyze.awaiting-input': { required: ['question'] },
|
|
25
|
-
'analyze.completed': { required: ['artifact'], optional: ['round'] },
|
|
27
|
+
'analyze.completed': { required: ['artifact'], optional: ['round', 'orchestrated'] },
|
|
26
28
|
'review-analysis.started': { optional: ['round'] },
|
|
27
|
-
'review-analysis.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round'] },
|
|
29
|
+
'review-analysis.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round', 'orchestrated'] },
|
|
28
30
|
'plan.started': { optional: ['round'] },
|
|
29
|
-
'plan.completed': { required: ['artifact'], optional: ['round'] },
|
|
31
|
+
'plan.completed': { required: ['artifact'], optional: ['round', 'orchestrated'] },
|
|
30
32
|
'review-plan.started': { optional: ['round'] },
|
|
31
|
-
'review-plan.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round'] },
|
|
33
|
+
'review-plan.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round', 'orchestrated'] },
|
|
32
34
|
'code.started': { optional: ['round', 'fixFor', 'implementationInput'] },
|
|
33
|
-
'code.completed': { required: ['artifact'], optional: ['round', 'fixFor', 'implementationInput', 'filesModified', 'testsPassed', 'blockers', 'major', 'minor', 'manualValidation'] },
|
|
35
|
+
'code.completed': { required: ['artifact'], optional: ['round', 'fixFor', 'implementationInput', 'filesModified', 'testsPassed', 'blockers', 'major', 'minor', 'manualValidation', 'orchestrated'] },
|
|
34
36
|
'review-code.started': { optional: ['round'] },
|
|
35
|
-
'review-code.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round'] },
|
|
37
|
+
'review-code.completed': { required: ['artifact', 'verdict', 'blockers', 'major', 'minor', 'manualValidation'], optional: ['round', 'orchestrated'] },
|
|
36
38
|
'manual-validation.started': { optional: ['round'] },
|
|
37
39
|
'manual-validation.completed': { required: ['artifact', 'summaryResult'], optional: ['round'] }
|
|
38
40
|
};
|
|
@@ -229,7 +231,7 @@ function openStartedIdentity(rows, family) {
|
|
|
229
231
|
});
|
|
230
232
|
return matches.length === 1 ? matches[0] : matches.length > 1 ? { conflict: true } : null;
|
|
231
233
|
}
|
|
232
|
-
function
|
|
234
|
+
function applyTaskEventUnlocked(request, options = {}) {
|
|
233
235
|
const invalid = validateTaskEventRequest(request);
|
|
234
236
|
if (invalid)
|
|
235
237
|
return failed(request, invalid);
|
|
@@ -326,20 +328,27 @@ function applyTaskEvent(request, options = {}) {
|
|
|
326
328
|
const findingCountError = validateReviewFindingCounts(normalized, content, eventIdentity.family, completedArtifact?.path ?? null);
|
|
327
329
|
if (findingCountError)
|
|
328
330
|
return failed(normalized, findingCountError, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
329
|
-
|
|
331
|
+
let orchestrationCompletion = null;
|
|
332
|
+
if (eventIdentity.phase === 'completed' && eventIdentity.family !== 'manual-validation') {
|
|
330
333
|
const orchestrationStage = eventIdentity.family === 'analyze' ? 'analysis' : eventIdentity.family;
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
334
|
+
const execution = validateLifecycleExecution(normalized.taskRef, {
|
|
335
|
+
mode: normalized.orchestrated ? 'orchestrated' : 'standalone',
|
|
336
|
+
identity: {
|
|
337
|
+
stage: orchestrationStage,
|
|
338
|
+
round: normalized.round,
|
|
339
|
+
artifact: normalized.artifact,
|
|
340
|
+
role: eventIdentity.family.startsWith('review-') ? 'reviewer' : 'executor'
|
|
341
|
+
},
|
|
342
|
+
agent: normalized.agent,
|
|
343
|
+
dryRun: normalized.dryRun
|
|
336
344
|
}, { repoRoot: options.repoRoot });
|
|
337
|
-
if (
|
|
345
|
+
if (!execution.ok) {
|
|
338
346
|
return failed(normalized, {
|
|
339
347
|
code: 'EVENT_TRANSITION_INVALID',
|
|
340
|
-
message:
|
|
348
|
+
message: `${execution.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH'}: ${execution.error?.message ?? 'orchestration provenance validation failed'}`
|
|
341
349
|
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
342
350
|
}
|
|
351
|
+
orchestrationCompletion = execution.completionPlan;
|
|
343
352
|
}
|
|
344
353
|
let metadata;
|
|
345
354
|
try {
|
|
@@ -379,19 +388,26 @@ function applyTaskEvent(request, options = {}) {
|
|
|
379
388
|
const result = writeTask({ taskRef: normalized.taskRef, expectedState: 'active', dryRun: normalized.dryRun, mutations }, { ...options, metadataProvider: () => metadata });
|
|
380
389
|
if (result.status === 'failed')
|
|
381
390
|
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 (
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
artifact: normalized.artifact,
|
|
388
|
-
agent: normalized.agent
|
|
389
|
-
}, { repoRoot: options.repoRoot });
|
|
390
|
-
if (orchestration.status === 'failed' || orchestration.status === 'paused') {
|
|
391
|
+
if (!normalized.dryRun && orchestrationCompletion) {
|
|
392
|
+
try {
|
|
393
|
+
(options.commitOrchestrationCompletion ?? commitOrchestrationStageCompletion)(orchestrationCompletion);
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
391
396
|
return failed(normalized, {
|
|
392
|
-
code: '
|
|
393
|
-
message:
|
|
394
|
-
}, {
|
|
397
|
+
code: 'EVENT_ORCHESTRATION_COMMIT_FAILED',
|
|
398
|
+
message: `task.md was written but orchestration completion could not be persisted; manual recovery is required: ${error instanceof Error ? error.message : String(error)}`
|
|
399
|
+
}, {
|
|
400
|
+
taskId: result.taskId,
|
|
401
|
+
taskMdPath: result.taskMdPath,
|
|
402
|
+
fromStep: currentStep,
|
|
403
|
+
toStep: step,
|
|
404
|
+
action: eventIdentity.action,
|
|
405
|
+
phase: eventIdentity.phase,
|
|
406
|
+
timestamp: result.timestamp,
|
|
407
|
+
agentInfraVersion: result.agentInfraVersion,
|
|
408
|
+
operations: result.operations,
|
|
409
|
+
artifactContext
|
|
410
|
+
});
|
|
395
411
|
}
|
|
396
412
|
}
|
|
397
413
|
return {
|
|
@@ -404,6 +420,29 @@ function applyTaskEvent(request, options = {}) {
|
|
|
404
420
|
agentInfraVersion: result.agentInfraVersion, operations: result.operations, error: null
|
|
405
421
|
};
|
|
406
422
|
}
|
|
423
|
+
function applyTaskEvent(request, options = {}) {
|
|
424
|
+
const invalid = validateTaskEventRequest(request);
|
|
425
|
+
if (invalid)
|
|
426
|
+
return failed(request, invalid);
|
|
427
|
+
const parts = eventParts(request.event);
|
|
428
|
+
if (request.dryRun || parts.phase !== 'completed' || parts.family === 'manual-validation') {
|
|
429
|
+
return applyTaskEventUnlocked(request, options);
|
|
430
|
+
}
|
|
431
|
+
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
432
|
+
if (!resolved.ok)
|
|
433
|
+
return applyTaskEventUnlocked(request, options);
|
|
434
|
+
try {
|
|
435
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `task-event.${request.event}`, () => applyTaskEventUnlocked(request, options));
|
|
436
|
+
}
|
|
437
|
+
catch (error) {
|
|
438
|
+
if (!(error instanceof TaskExecutionLockError))
|
|
439
|
+
throw error;
|
|
440
|
+
return failed(request, {
|
|
441
|
+
code: 'EVENT_TRANSITION_INVALID',
|
|
442
|
+
message: `${error.code}: ${error.message}`
|
|
443
|
+
}, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
407
446
|
function successNoOp(request, taskId, taskMdPath, currentStep, eventIdentity, timestamp, frontmatter, artifactContext) {
|
|
408
447
|
return {
|
|
409
448
|
...failed(request, { code: 'EVENT_LOG_CONFLICT', message: '' }), status: 'no-op', error: null,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { assertWritableInventory, inspectTaskArtifacts } from './artifact-lifecycle.js';
|
|
2
|
+
const COMPLETION_BACKFILL_FAMILIES = [
|
|
3
|
+
'analysis',
|
|
4
|
+
'review-analysis',
|
|
5
|
+
'plan',
|
|
6
|
+
'review-plan',
|
|
7
|
+
'code',
|
|
8
|
+
'review-code',
|
|
9
|
+
'manual-validation'
|
|
10
|
+
];
|
|
11
|
+
function inspectCompletionArtifacts(taskRef, options = {}) {
|
|
12
|
+
const artifacts = [];
|
|
13
|
+
let taskId = null;
|
|
14
|
+
let taskDir = null;
|
|
15
|
+
for (const family of COMPLETION_BACKFILL_FAMILIES) {
|
|
16
|
+
const inventory = inspectTaskArtifacts(taskRef, family, options);
|
|
17
|
+
taskId = inventory.taskId ?? taskId;
|
|
18
|
+
taskDir = inventory.taskDir ?? taskDir;
|
|
19
|
+
if (inventory.status === 'failed') {
|
|
20
|
+
return { status: 'failed', changed: false, taskId, taskDir, artifacts: [], error: inventory.error };
|
|
21
|
+
}
|
|
22
|
+
const writableError = assertWritableInventory(inventory);
|
|
23
|
+
const brokenReference = inventory.diagnostics.find((item) => item.code === 'BROKEN_REFERENCE');
|
|
24
|
+
const error = writableError ?? (brokenReference ? {
|
|
25
|
+
code: 'ARTIFACT_REFERENCE_INVALID',
|
|
26
|
+
message: brokenReference.message
|
|
27
|
+
} : null);
|
|
28
|
+
if (error)
|
|
29
|
+
return { status: 'failed', changed: false, taskId, taskDir, artifacts: [], error };
|
|
30
|
+
artifacts.push(...inventory.artifacts);
|
|
31
|
+
}
|
|
32
|
+
return { status: 'ready', changed: false, taskId, taskDir, artifacts, error: null };
|
|
33
|
+
}
|
|
34
|
+
export { COMPLETION_BACKFILL_FAMILIES, inspectCompletionArtifacts };
|
|
35
|
+
//# sourceMappingURL=finalization-artifacts.js.map
|
|
@@ -13,12 +13,11 @@ function validateInput(row) {
|
|
|
13
13
|
throw new Error(`implementation input ${row.id} has incomplete decision identity`);
|
|
14
14
|
if (row.stage !== 'code')
|
|
15
15
|
throw new Error(`implementation input ${row.id} has invalid stage`);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
(
|
|
21
|
-
(row.needsImplementation && row.status === 'consumed' && ARTIFACT_RE.test(row.consumedBy));
|
|
16
|
+
const hasValidTimestamp = TIMESTAMP_RE.test(row.decidedAt) && Number.isFinite(Date.parse(row.decidedAt.replace(' ', 'T')));
|
|
17
|
+
const valid = (row.status === 'declared' && row.decidedAt === '' && row.consumedBy === '') ||
|
|
18
|
+
(hasValidTimestamp && row.needsImplementation && row.status === 'pending' && row.consumedBy === '') ||
|
|
19
|
+
(hasValidTimestamp && !row.needsImplementation && row.status === 'not-required' && row.consumedBy === '') ||
|
|
20
|
+
(hasValidTimestamp && row.needsImplementation && row.status === 'consumed' && ARTIFACT_RE.test(row.consumedBy));
|
|
22
21
|
if (!valid)
|
|
23
22
|
throw new Error(`implementation input ${row.id} has an invalid state combination`);
|
|
24
23
|
}
|
|
@@ -67,6 +66,54 @@ function createImplementationInput(rows, draft) {
|
|
|
67
66
|
validateInput(row);
|
|
68
67
|
return row;
|
|
69
68
|
}
|
|
69
|
+
function declareImplementationInput(rows, draft) {
|
|
70
|
+
const existing = rows.filter((row) => row.ledgerId === draft.ledgerId && row.status === 'declared');
|
|
71
|
+
if (existing.length > 1)
|
|
72
|
+
throw new Error(`implementation input declaration for '${draft.ledgerId}' conflicts with existing rows`);
|
|
73
|
+
if (existing.length === 1) {
|
|
74
|
+
const row = existing[0];
|
|
75
|
+
if (row.decisionEvidence !== draft.decisionEvidence || row.needsImplementation !== draft.needsImplementation) {
|
|
76
|
+
throw new Error(`implementation input declaration for '${draft.ledgerId}' conflicts with the existing row`);
|
|
77
|
+
}
|
|
78
|
+
return row;
|
|
79
|
+
}
|
|
80
|
+
const row = {
|
|
81
|
+
id: nextId(rows), ledgerId: draft.ledgerId, decisionEvidence: draft.decisionEvidence,
|
|
82
|
+
stage: 'code', needsImplementation: draft.needsImplementation, decidedAt: '',
|
|
83
|
+
status: 'declared', consumedBy: ''
|
|
84
|
+
};
|
|
85
|
+
validateInput(row);
|
|
86
|
+
return row;
|
|
87
|
+
}
|
|
88
|
+
function selectDeclaredImplementationInput(rows, ledgerId, decisionEvidence) {
|
|
89
|
+
const forLedger = rows.filter((row) => row.ledgerId === ledgerId && row.status === 'declared');
|
|
90
|
+
if (forLedger.length > 1)
|
|
91
|
+
throw new Error(`implementation input declaration for '${ledgerId}' is ambiguous`);
|
|
92
|
+
if (forLedger.length === 0)
|
|
93
|
+
return null;
|
|
94
|
+
if (forLedger[0].decisionEvidence !== decisionEvidence) {
|
|
95
|
+
throw new Error(`implementation input declaration for '${ledgerId}' has conflicting evidence`);
|
|
96
|
+
}
|
|
97
|
+
return forLedger[0];
|
|
98
|
+
}
|
|
99
|
+
function finalizeImplementationInput(rows, id, decisionEvidence, decidedAt) {
|
|
100
|
+
let found = false;
|
|
101
|
+
const next = rows.map((row) => {
|
|
102
|
+
if (row.id !== id)
|
|
103
|
+
return row;
|
|
104
|
+
found = true;
|
|
105
|
+
if (row.status !== 'declared')
|
|
106
|
+
throw new Error(`implementation input ${id} is not declared`);
|
|
107
|
+
return {
|
|
108
|
+
...row, decisionEvidence, decidedAt,
|
|
109
|
+
status: row.needsImplementation ? 'pending' : 'not-required'
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
if (!found)
|
|
113
|
+
throw new Error(`implementation input ${id} was not found`);
|
|
114
|
+
next.forEach(validateInput);
|
|
115
|
+
return next;
|
|
116
|
+
}
|
|
70
117
|
function selectPendingImplementationInput(rows, reviewCompletedAt) {
|
|
71
118
|
const reviewEpoch = Date.parse(reviewCompletedAt.replace(' ', 'T'));
|
|
72
119
|
if (!Number.isFinite(reviewEpoch))
|
|
@@ -113,5 +160,5 @@ function renderImplementationInputs(rows) {
|
|
|
113
160
|
})
|
|
114
161
|
].join('\n');
|
|
115
162
|
}
|
|
116
|
-
export { SECTION_ALIASES as IMPLEMENTATION_INPUT_ALIASES, createImplementationInput, consumeImplementationInput, parseImplementationInputs, renderImplementationInputs, selectPendingImplementationInput };
|
|
163
|
+
export { SECTION_ALIASES as IMPLEMENTATION_INPUT_ALIASES, createImplementationInput, consumeImplementationInput, declareImplementationInput, finalizeImplementationInput, parseImplementationInputs, renderImplementationInputs, selectDeclaredImplementationInput, selectPendingImplementationInput };
|
|
117
164
|
//# sourceMappingURL=implementation-inputs.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { declareImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
|
|
3
4
|
import { LEDGER_COLUMNS, LEDGER_HEADINGS, nextHdId, parseLedger, validateLedgerRows } from './ledger.js';
|
|
4
5
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
5
6
|
import { writeTask } from './write.js';
|
|
@@ -66,6 +67,14 @@ function ledgerSectionMutation(content) {
|
|
|
66
67
|
body: `| ${LEDGER_COLUMNS.join(' | ')} |\n|----|-------|-------|----------|--------|----------|`
|
|
67
68
|
}];
|
|
68
69
|
}
|
|
70
|
+
function implementationInputMutation(content, rows) {
|
|
71
|
+
const english = /^##\s+Activity Log\s*$/m.test(content);
|
|
72
|
+
return {
|
|
73
|
+
kind: 'section', aliases: IMPLEMENTATION_INPUT_ALIASES,
|
|
74
|
+
heading: english ? 'Implementation Inputs' : '实现输入',
|
|
75
|
+
body: renderImplementationInputs(rows)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
69
78
|
function mapWrite(intent, entityId, before, after, result) {
|
|
70
79
|
if (result.status === 'failed')
|
|
71
80
|
return failed(intent, result.error.code, result.error.message, result.taskId, entityId);
|
|
@@ -174,9 +183,34 @@ function applyLedgerIntent(intent, options = {}) {
|
|
|
174
183
|
}
|
|
175
184
|
}
|
|
176
185
|
}
|
|
186
|
+
let implementationMutation = null;
|
|
187
|
+
if (intent.kind === 'decision-upsert' || intent.kind === 'finding-review') {
|
|
188
|
+
const stage = intent.kind === 'decision-upsert' ? intent.stage : after.stage;
|
|
189
|
+
const escalates = intent.kind === 'decision-upsert' || intent.status === 'needs-human-decision';
|
|
190
|
+
if (stage === 'code' && escalates && intent.needsImplementation === undefined) {
|
|
191
|
+
return failed(intent, 'LEDGER_PAYLOAD_INVALID', 'code-stage decisions require --needs-implementation true|false', resolved.taskId, after.id);
|
|
192
|
+
}
|
|
193
|
+
if ((stage !== 'code' || !escalates) && intent.needsImplementation !== undefined) {
|
|
194
|
+
return failed(intent, 'LEDGER_PAYLOAD_INVALID', '--needs-implementation is only valid for code-stage escalation', resolved.taskId, after.id);
|
|
195
|
+
}
|
|
196
|
+
if (stage === 'code' && escalates) {
|
|
197
|
+
try {
|
|
198
|
+
const parsed = parseImplementationInputs(content);
|
|
199
|
+
const declaration = declareImplementationInput(parsed.rows, {
|
|
200
|
+
ledgerId: after.id, decisionEvidence: after.evidence,
|
|
201
|
+
needsImplementation: intent.needsImplementation
|
|
202
|
+
});
|
|
203
|
+
const nextRows = parsed.rows.includes(declaration) ? parsed.rows : [...parsed.rows, declaration];
|
|
204
|
+
implementationMutation = implementationInputMutation(content, nextRows);
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
return failed(intent, 'LEDGER_DOCUMENT_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId, after.id);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
177
211
|
const result = writeTask({
|
|
178
212
|
taskRef: intent.taskRef, expectedState: 'active',
|
|
179
|
-
mutations: [...ledgerSectionMutation(content), rowMutation(after)],
|
|
213
|
+
mutations: [...ledgerSectionMutation(content), rowMutation(after), ...(implementationMutation ? [implementationMutation] : [])],
|
|
180
214
|
dryRun: 'dryRun' in intent ? intent.dryRun : false
|
|
181
215
|
}, { ...options, taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state } });
|
|
182
216
|
return mapWrite(intent, after.id, before, after, result);
|