@moretea-labs/forge 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +58 -62
- package/CHANGELOG.md +26 -0
- package/CLAUDE.md +42 -62
- package/README.md +2 -2
- package/SKILL.md +0 -1
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/adapters/browser/index.ts +2 -0
- package/adapters/chatgpt/browser-delivery-host.ts +71 -0
- package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
- package/adapters/chatgpt/controller-binding-store.ts +55 -0
- package/adapters/chatgpt/controller-host.ts +119 -0
- package/adapters/chatgpt/controller-round-host.ts +78 -0
- package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
- package/adapters/chatgpt/provider-delivery.ts +113 -0
- package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
- package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
- package/adapters/computer/desktop-operator-contract.ts +7 -0
- package/adapters/computer/desktop-operator-discovery.ts +201 -0
- package/adapters/computer/desktop-operator-negotiation.ts +174 -0
- package/adapters/computer/desktop-operator-provider.ts +312 -0
- package/adapters/computer/index.ts +1 -0
- package/adapters/computer/interaction-target-authority.ts +944 -0
- package/adapters/controller-process/binding-store.ts +34 -0
- package/adapters/controller-process/controller-host.ts +26 -0
- package/adapters/mcp/access-mode.ts +132 -0
- package/adapters/mcp/audit.ts +32 -0
- package/adapters/mcp/auth.ts +407 -0
- package/adapters/mcp/controller-round-compatibility.ts +184 -0
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
- package/adapters/mcp/instructions.ts +36 -0
- package/adapters/mcp/legacy-context.ts +18 -0
- package/adapters/mcp/multi-repository.ts +437 -0
- package/adapters/mcp/oauth.ts +391 -0
- package/adapters/mcp/paths.ts +140 -0
- package/adapters/mcp/policy.ts +232 -0
- package/adapters/mcp/proxy-env.ts +62 -0
- package/adapters/mcp/readiness-projection.ts +24 -0
- package/adapters/mcp/redaction.ts +21 -0
- package/adapters/mcp/repo.ts +24 -0
- package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
- package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
- package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
- package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
- package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
- package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
- package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
- package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
- package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
- package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
- package/adapters/mcp/runtime-gateway/router.ts +728 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
- package/adapters/mcp/server.ts +1048 -0
- package/adapters/mcp/setup.ts +791 -0
- package/adapters/mcp/tool-capability-cache.ts +87 -0
- package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
- package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
- package/adapters/mcp/tool-mapping/tools.ts +9 -0
- package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
- package/adapters/mcp/toolset-names.ts +74 -0
- package/adapters/mcp/toolset-selection.ts +41 -0
- package/adapters/mcp/toolset.ts +336 -0
- package/adapters/mcp/transports/http.ts +1456 -0
- package/adapters/mcp/transports/session-registry.ts +513 -0
- package/adapters/mcp/transports/stdio.ts +8 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
- package/adapters/mcp/types.ts +41 -0
- package/assets/AGENTS.md +2 -16
- package/assets/CLAUDE.md +2 -16
- package/assets/hooks/AGENTS.md +3 -14
- package/assets/hooks/CLAUDE.md +3 -14
- package/assets/hooks/hook-input.sh +9 -2
- package/assets/hooks/lib/workflow-state.sh +28 -10
- package/assets/hooks/prompt-guard.sh +63 -37
- package/assets/hooks/security-sentinel.sh +2 -1
- package/assets/hooks/session-start-context.sh +7 -5
- package/assets/hooks/stop-orchestrator.sh +13 -3
- package/assets/initializer-question-pack.v4.json +11 -7
- package/assets/partials/05-workflow.partial.md +18 -73
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -2
- package/assets/partials-agents/04-task-protocol.partial.md +14 -59
- package/assets/partials-agents/06-quality-safety.partial.md +11 -16
- package/assets/partials-agents/08-deep-docs.partial.md +1 -2
- package/assets/plan-map.json +11 -11
- package/assets/plugin-registry.v1.json +9 -3
- package/assets/plugin-trust-policy.v1.json +6 -0
- package/assets/recovery/ForgeRecovery.ps1 +100 -0
- package/assets/recovery/forge-wsl-rescue +271 -0
- package/assets/reference-configs/development-protocol.md +11 -15
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/global-working-rules.md +9 -1
- package/assets/reference-configs/harness-overview.md +22 -112
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +2 -2
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/architecture-event.ts +3 -34
- package/assets/templates/helpers/architecture-queue.sh +61 -10
- package/assets/templates/helpers/archive-workflow.sh +90 -180
- package/assets/templates/helpers/capability-config.ts +8 -11
- package/assets/templates/helpers/capability-resolver.ts +37 -19
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +1 -9
- package/assets/templates/helpers/check-task-sync.sh +82 -11
- package/assets/templates/helpers/check-task-workflow.sh +1 -3
- package/assets/templates/helpers/context-contract-sync.sh +1 -12
- package/assets/templates/helpers/contract-worktree.sh +26 -24
- package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
- package/assets/templates/helpers/inspect-project-state.ts +1 -1
- package/assets/templates/helpers/migrate-project-template.sh +1 -1
- package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
- package/assets/templates/helpers/new-plan.sh +2 -2
- package/assets/templates/helpers/plan-to-todo.sh +3 -31
- package/assets/templates/helpers/sprint-backlog.sh +1 -1
- package/assets/templates/helpers/switch-plan.sh +1 -1
- package/assets/templates/helpers/workflow-contract.ts +1 -0
- package/assets/workflow-contract.v1.json +63 -171
- package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
- package/docs/architecture/CURRENT.md +148 -13
- package/docs/architecture/EVOLUTION.md +13 -0
- package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
- package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
- package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
- package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
- package/docs/architecture/domains/verification.md +7 -0
- package/docs/architecture/domains/workflow-engine.md +1 -1
- package/docs/architecture/index.md +3 -3
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
- package/docs/forge-chatgpt-mcp-setup.md +5 -3
- package/docs/forge-plugin-management.md +20 -4
- package/docs/forge-runtime-self-healing-loop.md +2 -0
- package/docs/operations/benchmark-cross-version-freeze.md +38 -0
- package/docs/operations/controller-browser-plugin.md +3 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/platform-support.md +16 -8
- package/docs/operations/platform-support.zh-CN.md +2 -2
- package/docs/operations/standalone-disaster-recovery.md +26 -1
- package/docs/operations/uu-remote-rescue.md +41 -0
- package/docs/operations/windows-wsl-host-rescue.md +133 -0
- package/docs/public-usage-guide.md +1 -1
- package/docs/public-usage-guide.zh-CN.md +1 -1
- package/docs/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/02-connect-chatgpt.md +4 -4
- package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +23 -14
- package/packages/kernel/controller/api/index.ts +2 -0
- package/packages/kernel/controller/application/controller-round-service.ts +29 -0
- package/packages/kernel/controller/application/controller-service.ts +30 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
- package/packages/kernel/controller/domain/controller-round.ts +107 -0
- package/packages/kernel/controller/domain/execution-quality.ts +133 -0
- package/packages/kernel/controller/domain/types.ts +56 -0
- package/packages/kernel/controller/index.ts +1 -0
- package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
- package/packages/kernel/controller/ports/controller-host.ts +16 -0
- package/packages/kernel/goal/api/index.ts +1 -0
- package/packages/kernel/goal/domain/types.ts +40 -0
- package/packages/kernel/goal/index.ts +1 -0
- package/packages/kernel/identity/api/index.ts +5 -0
- package/packages/kernel/identity/application/identity-service.ts +67 -0
- package/packages/kernel/identity/domain/scope.ts +63 -0
- package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
- package/packages/kernel/identity/domain/types.ts +44 -0
- package/packages/kernel/identity/domain/workspace.ts +56 -0
- package/packages/kernel/identity/index.ts +1 -0
- package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
- package/packages/kernel/memory/api/index.ts +5 -0
- package/packages/kernel/memory/application/experience-service.ts +118 -0
- package/packages/kernel/memory/application/outcome-service.ts +77 -0
- package/packages/kernel/memory/domain/experience.ts +103 -0
- package/packages/kernel/memory/domain/operational-prior.ts +92 -0
- package/packages/kernel/memory/index.ts +1 -0
- package/packages/kernel/progression/api/index.ts +2 -0
- package/packages/kernel/progression/application/projector.ts +211 -0
- package/packages/kernel/progression/domain/types.ts +116 -0
- package/packages/kernel/progression/index.ts +1 -0
- package/packages/kernel/scheduler/api/index.ts +6 -0
- package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
- package/packages/kernel/scheduler/application/eligibility.ts +252 -0
- package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
- package/packages/kernel/scheduler/application/settlement.ts +197 -0
- package/packages/kernel/scheduler/domain/continuation.ts +38 -0
- package/packages/kernel/scheduler/domain/schedule.ts +149 -0
- package/packages/kernel/scheduler/index.ts +1 -0
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
- package/packages/kernel/work/api/index.ts +15 -0
- package/packages/kernel/work/application/work-service.ts +148 -0
- package/packages/kernel/work/domain/admission-policy.ts +34 -0
- package/packages/kernel/work/domain/check-receipt.ts +43 -0
- package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
- package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
- package/packages/kernel/work/domain/engineering-design.ts +281 -0
- package/packages/kernel/work/domain/engineering-profile.ts +242 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
- package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
- package/packages/kernel/work/domain/implementation-review.ts +510 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
- package/packages/kernel/work/domain/state-machine.ts +259 -0
- package/packages/kernel/work/domain/types.ts +528 -0
- package/packages/kernel/work/index.ts +1 -0
- package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
- package/packages/kernel/work/ports/work-contract-store.ts +18 -0
- package/packages/plugin-runtime/browser/index.ts +3 -0
- package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
- package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
- package/packages/plugin-runtime/browser/session-authority.ts +61 -0
- package/packages/plugin-runtime/computer/index.ts +6 -0
- package/packages/plugin-runtime/computer/provider-error.ts +26 -0
- package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
- package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
- package/packages/plugin-runtime/computer/provider.ts +19 -0
- package/packages/plugin-runtime/computer/target-authority.ts +190 -0
- package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
- package/packages/plugin-runtime/external/index.ts +1 -0
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
- package/packages/plugin-runtime/index.ts +3 -0
- package/packages/protocols/browser/index.ts +1 -0
- package/packages/protocols/browser/session.ts +98 -0
- package/packages/protocols/computer/contract.ts +199 -0
- package/packages/protocols/computer/index.ts +1 -0
- package/packages/protocols/failure/contract.ts +158 -0
- package/packages/protocols/failure/index.ts +1 -0
- package/packages/protocols/handoff/index.ts +1 -0
- package/packages/protocols/handoff/status.ts +22 -0
- package/packages/protocols/mcp/execution-context.ts +11 -0
- package/packages/protocols/mcp/index.ts +2 -0
- package/packages/protocols/mcp/tool-contract.ts +42 -0
- package/packages/workflow-runtime/api/index.ts +2 -0
- package/packages/workflow-runtime/application/interpreter.ts +342 -0
- package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
- package/packages/workflow-runtime/index.ts +1 -0
- package/scripts/assemble-template.ts +1 -1
- package/scripts/benchmark-computer-hotpath.ts +255 -0
- package/scripts/benchmark-controller-round2.ts +54 -2
- package/scripts/benchmark-ios-agent-device.ts +3 -6
- package/scripts/benchmark-mcp-transport.ts +3 -4
- package/scripts/benchmark-operational-memory-activation.ts +235 -0
- package/scripts/benchmark-operational-shadow.ts +28 -0
- package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
- package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
- package/scripts/canary-controller-semantic-wait.ts +195 -0
- package/scripts/canary-global-hook.sh +2 -2
- package/scripts/check-mcp-compatibility.ts +173 -7
- package/scripts/check-platform-support.mjs +28 -2
- package/scripts/check-public-docs.sh +1 -1
- package/scripts/check-release-readiness.sh +1 -1
- package/scripts/check-repository-hygiene.mjs +92 -0
- package/scripts/check-runtime-architecture.mjs +1206 -95
- package/scripts/check-source-duplication.mjs +9 -1
- package/scripts/check-tarball-install-smoke.sh +98 -15
- package/scripts/controller-ngrok-rotation.sh +5 -4
- package/scripts/create-project-dirs.sh +10 -74
- package/scripts/forge-local-recovery-helper.mjs +204 -0
- package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
- package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
- package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
- package/scripts/hook-shim.sh +2 -2
- package/scripts/init-project.sh +12 -70
- package/scripts/install-desktop-operator-registration.ts +4 -2
- package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
- package/scripts/install-standalone-recovery.ts +122 -10
- package/scripts/install-uu-remote-rescue-registration.ts +74 -0
- package/scripts/install-windows-host-recovery-registration.ts +22 -0
- package/scripts/lib/controller-home-env.sh +6 -3
- package/scripts/lib/project-init-lib.sh +39 -582
- package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
- package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
- package/scripts/open-source-audit-allowlist.txt +12 -1
- package/scripts/portability-bindings.mjs +70 -0
- package/scripts/run-bounded-command.mjs +55 -0
- package/scripts/run-bun-test-file.ts +22 -6
- package/scripts/run-governed-gate.ts +4 -1
- package/scripts/smoke-mcp-http-runtime.ts +1 -1
- package/scripts/smoke-runtime-recovery.ts +34 -1
- package/scripts/smoke-schedule-engine.ts +1 -1
- package/scripts/stage-runtime-release.ts +6 -1
- package/scripts/test-governance.ts +51 -2
- package/src/cli/agent-jobs/job-manager.ts +10 -1
- package/src/cli/chatgpt-browser/bind-server.ts +2 -1
- package/src/cli/chatgpt-browser/binding.ts +20 -10
- package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
- package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
- package/src/cli/chatgpt-browser/engine.ts +10 -11
- package/src/cli/chatgpt-browser/native-provider.ts +24 -83
- package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
- package/src/cli/chatgpt-browser/types.ts +1 -0
- package/src/cli/commands/bootstrap-control.ts +334 -0
- package/src/cli/commands/brain-assistant.ts +38 -0
- package/src/cli/commands/brain.ts +193 -40
- package/src/cli/commands/chatgpt.ts +127 -0
- package/src/cli/commands/computer.ts +437 -0
- package/src/cli/commands/doctor.ts +18 -1
- package/src/cli/commands/init-hook.ts +71 -61
- package/src/cli/commands/init.ts +3 -1
- package/src/cli/commands/mcp.ts +16 -10
- package/src/cli/commands/plugin.ts +38 -12
- package/src/cli/commands/recovery.ts +493 -80
- package/src/cli/commands/runtime.ts +9 -4
- package/src/cli/commands/setup-profile.ts +89 -39
- package/src/cli/commands/status.ts +2 -1
- package/src/cli/controller/check-runner.ts +487 -98
- package/src/cli/controller/context/types.ts +7 -1
- package/src/cli/controller/context-pack.ts +23 -3
- package/src/cli/controller/issue-store.ts +1 -1
- package/src/cli/controller/systemd-user.ts +110 -0
- package/src/cli/controller/types.ts +10 -70
- package/src/cli/controller/worklog.ts +67 -13
- package/src/cli/editing/edit-operation-contract.ts +92 -0
- package/src/cli/editing/edit-session.ts +289 -13
- package/src/cli/github/plugin.ts +17 -12
- package/src/cli/hook/route-registry.ts +3 -5
- package/src/cli/hook/runtime.ts +49 -3
- package/src/cli/index.ts +3 -0
- package/src/cli/local-bridge/connector-freshness.ts +2 -2
- package/src/cli/local-bridge/console-automations.ts +2 -2
- package/src/cli/local-bridge/facade-api.ts +41 -14
- package/src/cli/local-bridge/job-store.ts +1 -1
- package/src/cli/local-bridge/server.ts +24 -27
- package/src/cli/mcp/access-mode.ts +2 -132
- package/src/cli/mcp/access-tools.ts +2 -387
- package/src/cli/mcp/audit.ts +2 -32
- package/src/cli/mcp/auth.ts +2 -404
- package/src/cli/mcp/instructions.ts +2 -34
- package/src/cli/mcp/legacy-context.ts +2 -16
- package/src/cli/mcp/legacy-tool-service.ts +2 -5308
- package/src/cli/mcp/multi-repository.ts +2 -436
- package/src/cli/mcp/oauth.ts +2 -375
- package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
- package/src/cli/mcp/paths.ts +2 -140
- package/src/cli/mcp/policy.ts +2 -232
- package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
- package/src/cli/mcp/proxy-env.ts +2 -62
- package/src/cli/mcp/readiness-projection.ts +2 -24
- package/src/cli/mcp/redaction.ts +2 -21
- package/src/cli/mcp/repo.ts +2 -24
- package/src/cli/mcp/repository-tools.ts +2 -1613
- package/src/cli/mcp/server.ts +2 -951
- package/src/cli/mcp/setup.ts +2 -737
- package/src/cli/mcp/tool-capability-cache.ts +2 -87
- package/src/cli/mcp/tool-contract.ts +2 -14
- package/src/cli/mcp/tools.ts +2 -9
- package/src/cli/mcp/toolset-names.ts +2 -70
- package/src/cli/mcp/toolset-selection.ts +2 -41
- package/src/cli/mcp/toolset.ts +2 -336
- package/src/cli/mcp/transports/AGENTS.md +1 -10
- package/src/cli/mcp/transports/CLAUDE.md +1 -10
- package/src/cli/mcp/transports/http.ts +2 -1414
- package/src/cli/mcp/transports/session-registry.ts +2 -490
- package/src/cli/mcp/transports/stdio.ts +2 -8
- package/src/cli/mcp/types.ts +2 -41
- package/src/cli/mcp/umbrella-tools.ts +2 -117
- package/src/cli/repositories/command-executor.ts +64 -18
- package/src/cli/repositories/command-scope.ts +117 -1
- package/src/cli/repositories/controller-home.ts +119 -1
- package/src/cli/repositories/identity.ts +13 -0
- package/src/cli/repositories/registry.ts +68 -49
- package/src/cli/repositories/repository-snapshot.ts +53 -7
- package/src/cli/repositories/runtime-storage.ts +27 -2
- package/src/cli/repositories/safe-patch.ts +34 -38
- package/src/cli/repositories/selected-path-actions.ts +22 -2
- package/src/cli/repositories/structured-git.ts +105 -25
- package/src/cli/runtime/helper-runner.ts +7 -0
- package/src/cli/runtime-invocation.ts +8 -1
- package/src/cli/tools/codegraph.ts +44 -25
- package/src/core/adoption/manifest-templates.ts +1 -1
- package/src/core/adoption/plan.ts +3 -26
- package/src/core/adoption/workflow-contract-plan.ts +17 -9
- package/src/effects/process-runner.ts +8 -14
- package/src/runtime/context/assistant-context.ts +154 -0
- package/src/runtime/context/assistant-work-context.ts +147 -0
- package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
- package/src/runtime/context/codegraph-read-provider.ts +55 -22
- package/src/runtime/context/codegraph-sidecar.cjs +8 -1
- package/src/runtime/context/context-closure.ts +268 -0
- package/src/runtime/context/context-plane.ts +211 -0
- package/src/runtime/context/project-engineering-contract.ts +60 -0
- package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
- package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
- package/src/runtime/control-plane/bootstrap/index.ts +5 -0
- package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
- package/src/runtime/control-plane/bootstrap/store.ts +21 -0
- package/src/runtime/control-plane/bootstrap/types.ts +87 -0
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
- package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +369 -99
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
- package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
- package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
- package/src/runtime/control-plane/execution/validation.ts +28 -3
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
- package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
- package/src/runtime/control-plane/execution/work-handle-authority.ts +219 -22
- package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
- package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
- package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
- package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
- package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
- package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
- package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
- package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
- package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
- package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
- package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
- package/src/runtime/control-plane/facade/index.ts +4 -2
- package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
- package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
- package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
- package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
- package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
- package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
- package/src/runtime/control-plane/facade/types.ts +189 -532
- package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
- package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
- package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
- package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
- package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
- package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
- package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
- package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
- package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
- package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
- package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
- package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
- package/src/runtime/control-plane/release-retention.ts +56 -16
- package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
- package/src/runtime/control-plane/routing/route-policy.ts +19 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
- package/src/runtime/control-plane/runtime-generation.ts +6 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
- package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
- package/src/runtime/diagnostics/incident-repair.ts +406 -0
- package/src/runtime/diagnostics/mcp-timing.ts +125 -6
- package/src/runtime/diagnostics/performance.ts +6 -14
- package/src/runtime/diagnostics/read-only-tool.ts +1 -1
- package/src/runtime/evidence/artifact-store.ts +65 -2
- package/src/runtime/evidence/operational-shadow.ts +38 -0
- package/src/runtime/evidence/process-check-receipt.ts +5 -43
- package/src/runtime/evidence/sensitive-output.ts +4 -4
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +121 -2
- package/src/runtime/execution/managed-workspace.ts +55 -5
- package/src/runtime/execution/process-runtime/canary.ts +19 -5
- package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
- package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
- package/src/runtime/execution/process-runtime/check-result.ts +73 -2
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
- package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
- package/src/runtime/execution/process-runtime/index.ts +1 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
- package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
- package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
- package/src/runtime/execution/process-runtime/runtime.ts +132 -15
- package/src/runtime/execution/process-runtime/types.ts +22 -0
- package/src/runtime/execution/workers/executor.ts +16 -13
- package/src/runtime/execution/workers/worker-entry.ts +1 -1
- package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
- package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
- package/src/runtime/gateway/mcp/process-tools.ts +2 -289
- package/src/runtime/gateway/mcp/router.ts +2 -1125
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
- package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
- package/src/runtime/maintenance/cleanup.ts +38 -24
- package/src/runtime/platform/browser-product-discovery.ts +174 -0
- package/src/runtime/platform/computer-platform.ts +14 -0
- package/src/runtime/platform/executable-discovery.ts +54 -0
- package/src/runtime/platform/fetch-http-server.ts +90 -0
- package/src/runtime/platform/service-manager.ts +138 -0
- package/src/runtime/platform/windows-host-discovery.ts +118 -0
- package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
- package/src/runtime/plugins/action-application.ts +135 -0
- package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
- package/src/runtime/plugins/browser-adapter.ts +140 -100
- package/src/runtime/plugins/browser-automation-service.ts +15 -36
- package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
- package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
- package/src/runtime/plugins/browser-native-session-service.ts +1 -1
- package/src/runtime/plugins/browser-provider-registry.ts +2 -109
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
- package/src/runtime/plugins/browser-runtime.ts +41 -9
- package/src/runtime/plugins/browser-session-authority.ts +101 -291
- package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
- package/src/runtime/plugins/browser-session-store.ts +229 -36
- package/src/runtime/plugins/browser-session-types.ts +2 -98
- package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
- package/src/runtime/plugins/catalog-trust.ts +82 -0
- package/src/runtime/plugins/computer-registration.ts +839 -0
- package/src/runtime/plugins/config-store.ts +119 -0
- package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
- package/src/runtime/plugins/errors.ts +8 -1
- package/src/runtime/plugins/external-adapter.ts +45 -322
- package/src/runtime/plugins/external-provider-policy.ts +48 -0
- package/src/runtime/plugins/external-registration.ts +9 -1
- package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
- package/src/runtime/plugins/external-unix-socket.ts +64 -193
- package/src/runtime/plugins/first-party-external-registration.ts +163 -0
- package/src/runtime/plugins/first-party-registry.ts +3 -1
- package/src/runtime/plugins/github-adapter.ts +1 -2
- package/src/runtime/plugins/gmail-adapter.ts +6 -6
- package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
- package/src/runtime/plugins/google-shared.ts +31 -46
- package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
- package/src/runtime/plugins/ios-adapter.ts +1 -2
- package/src/runtime/plugins/local-system-adapter.ts +180 -0
- package/src/runtime/plugins/macos-capability-broker.ts +15 -105
- package/src/runtime/plugins/resend-adapter.ts +15 -24
- package/src/runtime/plugins/store.ts +493 -51
- package/src/runtime/plugins/types.ts +8 -1
- package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
- package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
- package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
- package/src/runtime/projections/controller-context.ts +12 -6
- package/src/runtime/projections/git-status-sampler.ts +73 -22
- package/src/runtime/projections/materialized-view.ts +14 -8
- package/src/runtime/recovery/capability-action-application.ts +228 -0
- package/src/runtime/recovery/index.ts +3 -0
- package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
- package/src/runtime/recovery/local-jobs-repair.ts +14 -14
- package/src/runtime/recovery/maintenance-executor.ts +358 -76
- package/src/runtime/recovery/quarantine-retention.ts +256 -0
- package/src/runtime/release/identity.ts +6 -0
- package/src/runtime/resources/leases/store.ts +1 -1
- package/src/runtime/root/active-execution-power-assertion.ts +139 -0
- package/src/runtime/root/assistant-learning-loop.ts +45 -0
- package/src/runtime/root/browser-session-composition.ts +59 -0
- package/src/runtime/root/computer-composition.ts +191 -0
- package/src/runtime/root/computer-target-composition.ts +16 -0
- package/src/runtime/root/computer-target-persistence.ts +39 -0
- package/src/runtime/root/controller-round-composition.ts +107 -0
- package/src/runtime/root/controller-services.ts +2 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/gateway-adapter.ts +3 -3
- package/src/runtime/root/local-bridge.ts +5 -2
- package/src/runtime/root/mcp-transport.ts +5 -5
- package/src/runtime/root/package-connector-service.ts +108 -56
- package/src/runtime/root/package-runtime-release.ts +85 -18
- package/src/runtime/root/package-runtime-service.ts +60 -97
- package/src/runtime/root/release-execution-canary.ts +71 -0
- package/src/runtime/root/release-loader.ts +68 -0
- package/src/runtime/root/release-manifest.ts +106 -2
- package/src/runtime/root/release-materialize.ts +103 -4
- package/src/runtime/root/release-store.ts +31 -2
- package/src/runtime/root/runtime.ts +240 -25
- package/src/runtime/root/scheduled-controller-composition.ts +51 -0
- package/src/runtime/root/scheduler.ts +12 -4
- package/src/runtime/root/service-runner.ts +1 -1
- package/src/runtime/root/service.ts +41 -16
- package/src/runtime/root/status.ts +1 -0
- package/src/runtime/root/types.ts +24 -1
- package/src/runtime/shared/local-bridge-surface.ts +1 -1
- package/src/runtime/shared/process-environment.ts +19 -4
- package/src/runtime/shared/storage-capacity.ts +68 -0
- package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
- package/src/runtime/standalone-recovery/core.ts +984 -230
- package/src/runtime/standalone-recovery/entry.ts +182 -51
- package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
- package/src/runtime/standalone-recovery/installer.ts +212 -104
- package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
- package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
- package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
- package/src/runtime/standalone-recovery/performance.ts +135 -0
- package/src/runtime/standalone-recovery/release.ts +5 -0
- package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
- package/src/runtime/workflow/schedules/engine.ts +191 -343
- package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
- package/src/runtime/workflow/schedules/settlement.ts +2 -191
- package/src/runtime/workflow/schedules/store.ts +2 -607
- package/src/runtime/workflow/schedules/types.ts +2 -127
- package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
- package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
- package/src/runtime/workflows/runtime.ts +486 -0
- package/src/testing/test-governance.ts +90 -29
- package/assets/templates/helpers/archive-architecture-request.sh +0 -244
- package/docs/architecture/history/README.md +0 -7
- package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
- package/docs/architecture/history/global-hook-runtime.md +0 -166
- package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
- package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
- package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
- package/docs/architecture/requests/archive/2026/root.md +0 -68
- package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
- package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
- package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
- package/scripts/archive-architecture-request.sh +0 -4
- package/scripts/run-skill-evals.ts +0 -1022
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { listControllerChecks } from '../../../cli/controller/check-runner';
|
|
2
2
|
import type { ControllerCheck } from '../../../cli/controller/check-runner';
|
|
3
3
|
import type { CompletionReceipt } from '../../../cli/controller/types';
|
|
4
|
-
import type {
|
|
4
|
+
import type { McpExecutionContext } from '../../../../packages/protocols/mcp/execution-context';
|
|
5
5
|
import { globMatches } from '../../../cli/mcp/paths';
|
|
6
6
|
import { withControllerLock } from '../../../cli/repositories/locks';
|
|
7
7
|
import { getRepository, selectRepositoryCheckout, setRepositoryCheckoutLifecycle } from '../../../cli/repositories/registry';
|
|
@@ -9,17 +9,28 @@ import { repositoryGitCommit, repositoryGitDeleteBranch, repositoryGitFinishWork
|
|
|
9
9
|
import type { RepositoryRecord } from '../../../cli/repositories/types';
|
|
10
10
|
import { hasCurrentWorkValidationAuthority, markWorkValidationPending, projectWorkValidationOutcome } from './work-validation-reconciler';
|
|
11
11
|
import { markRepositoryProjectionDirty } from '../../projections/invalidation';
|
|
12
|
-
import { withControllerSessionTerminalizationFence } from '
|
|
12
|
+
import { withControllerSessionTerminalizationFence } from '../../../../packages/kernel/controller/api/index';
|
|
13
13
|
import type { VerificationRecord } from '../facade/types';
|
|
14
|
-
import { appendVerificationRecord, appendWorkEvidence, listWorkContracts, transitionWorkContractPhase, updateWorkContract } from '
|
|
14
|
+
import { appendVerificationRecord, appendWorkEvidence, listWorkContracts, reconcileApprovedWorkImplementationReviewProjection, transitionWorkContractPhase, updateWorkContract } from '../../../../packages/kernel/work/api/index';
|
|
15
15
|
import { readRepositoryAccessPolicy } from '../governance/access-policy';
|
|
16
16
|
import { assertResolvedAuthorization, decideAuthorization } from '../governance/authorization';
|
|
17
17
|
import { updateExecutionSession } from './session-store';
|
|
18
|
-
import { validateWorkHandle } from './validation';
|
|
18
|
+
import { validateWorkHandle, WorkHandleValidationError } from './validation';
|
|
19
|
+
import { implementationReviewContentFingerprint } from './implementation-review-content';
|
|
20
|
+
import { transferReviewedWorkAuthorityAcrossContentEquivalentCommit } from './content-equivalent-commit-authority';
|
|
21
|
+
import {
|
|
22
|
+
assertImplementationReviewPreDeliveryBoundary,
|
|
23
|
+
authoritativeImplementationReviewVerificationEvidence,
|
|
24
|
+
latestImplementationReview,
|
|
25
|
+
implementationReviewChangedPathDigest,
|
|
26
|
+
normalizeImplementationReviewChangedPaths,
|
|
27
|
+
workRequiresImplementationReview,
|
|
28
|
+
type ImplementationReviewCandidateIdentity,
|
|
29
|
+
} from '../../../../packages/kernel/work/api/index';
|
|
19
30
|
import { effectiveVerificationEvidence, verificationInputFingerprint, workspaceValidationFingerprint, workValidationInputFingerprint } from './verification-evidence';
|
|
20
31
|
import { completeWorkWithReceipt } from './work-completion-authority';
|
|
21
|
-
import {
|
|
22
|
-
import type { WorkFinalizationStages, WorkHandleState } from './work-handle-store';
|
|
32
|
+
import { adoptWorkHandleSuccessorCandidate, readWorkHandle, resolveWorkDeliveryTargetBranch, transitionWorkHandle, workDeliveryBaseRevision, writeWorkHandle } from './work-handle-store';
|
|
33
|
+
import type { WorkFinalizationFailureCode, WorkFinalizationStages, WorkHandleState } from './work-handle-store';
|
|
23
34
|
import { findWorkPathScopeViolation } from './work-path-scope';
|
|
24
35
|
import type { WorkRemoteDeliveryReceipt } from './work-remote-delivery';
|
|
25
36
|
import { pushExactWorkRemoteDelivery } from './work-remote-delivery';
|
|
@@ -28,16 +39,7 @@ import { createHash } from 'crypto';
|
|
|
28
39
|
import { existsSync, realpathSync } from 'fs';
|
|
29
40
|
import { basename, resolve } from 'path';
|
|
30
41
|
import { assertWorkControllerOwnership, compactHandle, contractFor, gitChangedPaths, gitCommit, gitHead, gitMergeBase, gitRevision, identityFor, reconcileTerminalCleanup, releasePreparedWorkOwnership, requireSession, selectWorkFinalizationTarget, terminalCleanupOutcome, workForSession, workReturnCheckoutId } from './work-execution-support';
|
|
31
|
-
|
|
32
|
-
function gitIsAncestor(root: string, ancestor: string, descendant: string): boolean {
|
|
33
|
-
const result = spawnSync('git', ['-C', root, 'merge-base', '--is-ancestor', ancestor, descendant], {
|
|
34
|
-
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
35
|
-
});
|
|
36
|
-
if (result.error || (result.status !== 0 && result.status !== 1)) {
|
|
37
|
-
throw new Error(`WORK_TARGET_ADVANCE_ANCESTRY_UNAVAILABLE: ${ancestor} -> ${descendant}`);
|
|
38
|
-
}
|
|
39
|
-
return result.status === 0;
|
|
40
|
-
}
|
|
42
|
+
import { gitIsAncestor } from './direct-canonical-work-reconciliation';
|
|
41
43
|
|
|
42
44
|
export interface WorkTargetAdvanceInspection {
|
|
43
45
|
relation: 'candidate_contains_target' | 'target_contains_candidate' | 'diverged_clean' | 'diverged_conflict';
|
|
@@ -97,6 +99,69 @@ export function targetAdvanceLinearMergeCommits(root: string, targetRevision: st
|
|
|
97
99
|
return output.stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
export interface FailedNonLinearTargetAdvanceRepairInspection {
|
|
103
|
+
eligible: boolean;
|
|
104
|
+
reason:
|
|
105
|
+
| 'equivalent_linear_repair'
|
|
106
|
+
| 'same_revision'
|
|
107
|
+
| 'target_not_ancestor_of_failed'
|
|
108
|
+
| 'failed_candidate_is_linear'
|
|
109
|
+
| 'target_not_ancestor_of_repair'
|
|
110
|
+
| 'repair_contains_merge'
|
|
111
|
+
| 'tree_mismatch';
|
|
112
|
+
failedHead: string;
|
|
113
|
+
repairedHead: string;
|
|
114
|
+
targetHead: string;
|
|
115
|
+
failedTree: string;
|
|
116
|
+
repairedTree: string;
|
|
117
|
+
failedMergeCommits: string[];
|
|
118
|
+
repairedMergeCommits: string[];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function gitTree(root: string, revision: string, label: string): string {
|
|
122
|
+
const result = spawnSync('git', ['-C', root, 'rev-parse', '--verify', `${revision}^{tree}`], {
|
|
123
|
+
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
124
|
+
});
|
|
125
|
+
if (result.status !== 0 || result.error || typeof result.stdout !== 'string' || !result.stdout.trim()) {
|
|
126
|
+
throw new Error(`WORK_TARGET_ADVANCE_${label}_TREE_UNAVAILABLE: ${revision}`);
|
|
127
|
+
}
|
|
128
|
+
return result.stdout.trim();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Inspect the only safe history-repair case after target-advance finalization
|
|
133
|
+
* rejected a controller-created merge commit. The repaired candidate may move
|
|
134
|
+
* backwards in commit ancestry only when it is a clean linear representation
|
|
135
|
+
* of the exact same Git tree above the same reconciled target. This proves a
|
|
136
|
+
* history-shape repair, not a source-content rewrite.
|
|
137
|
+
*/
|
|
138
|
+
export function inspectFailedNonLinearTargetAdvanceRepair(
|
|
139
|
+
root: string,
|
|
140
|
+
failedRevision: string,
|
|
141
|
+
repairedRevision: string,
|
|
142
|
+
targetRevision: string,
|
|
143
|
+
): FailedNonLinearTargetAdvanceRepairInspection {
|
|
144
|
+
const failedHead = gitCommit(root, failedRevision, 'FAILED_NON_LINEAR_CANDIDATE');
|
|
145
|
+
const repairedHead = gitCommit(root, repairedRevision, 'REPAIRED_LINEAR_CANDIDATE');
|
|
146
|
+
const targetHead = gitCommit(root, targetRevision, 'REPAIRED_LINEAR_TARGET');
|
|
147
|
+
const failedTree = gitTree(root, failedHead, 'FAILED_NON_LINEAR');
|
|
148
|
+
const repairedTree = gitTree(root, repairedHead, 'REPAIRED_LINEAR');
|
|
149
|
+
const failedMergeCommits = gitIsAncestor(root, targetHead, failedHead)
|
|
150
|
+
? targetAdvanceLinearMergeCommits(root, targetHead, failedHead)
|
|
151
|
+
: [];
|
|
152
|
+
const repairedMergeCommits = gitIsAncestor(root, targetHead, repairedHead)
|
|
153
|
+
? targetAdvanceLinearMergeCommits(root, targetHead, repairedHead)
|
|
154
|
+
: [];
|
|
155
|
+
const base = { failedHead, repairedHead, targetHead, failedTree, repairedTree, failedMergeCommits, repairedMergeCommits };
|
|
156
|
+
if (failedHead === repairedHead) return { ...base, eligible: false, reason: 'same_revision' };
|
|
157
|
+
if (!gitIsAncestor(root, targetHead, failedHead)) return { ...base, eligible: false, reason: 'target_not_ancestor_of_failed' };
|
|
158
|
+
if (failedMergeCommits.length === 0) return { ...base, eligible: false, reason: 'failed_candidate_is_linear' };
|
|
159
|
+
if (!gitIsAncestor(root, targetHead, repairedHead)) return { ...base, eligible: false, reason: 'target_not_ancestor_of_repair' };
|
|
160
|
+
if (repairedMergeCommits.length > 0) return { ...base, eligible: false, reason: 'repair_contains_merge' };
|
|
161
|
+
if (failedTree !== repairedTree) return { ...base, eligible: false, reason: 'tree_mismatch' };
|
|
162
|
+
return { ...base, eligible: true, reason: 'equivalent_linear_repair' };
|
|
163
|
+
}
|
|
164
|
+
|
|
100
165
|
function normalizedReadScope(scope: string): string {
|
|
101
166
|
return scope.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/+$/, '') || '.';
|
|
102
167
|
}
|
|
@@ -195,7 +260,7 @@ export function targetAdvanceWorkScopeViolation(
|
|
|
195
260
|
}
|
|
196
261
|
|
|
197
262
|
function replaceTargetAdvanceScopeEvidence(
|
|
198
|
-
ctx:
|
|
263
|
+
ctx: McpExecutionContext,
|
|
199
264
|
contract: NonNullable<ReturnType<typeof contractFor>>,
|
|
200
265
|
actualChangedPaths: string[],
|
|
201
266
|
): void {
|
|
@@ -283,6 +348,292 @@ function workOwnedDirtyPaths(
|
|
|
283
348
|
return { dirtyPaths, ownedPaths };
|
|
284
349
|
}
|
|
285
350
|
|
|
351
|
+
|
|
352
|
+
export function implementationReviewCommittedBaseRevision(
|
|
353
|
+
repository: Pick<RepositoryRecord, 'canonicalRoot' | 'defaultBranch'>,
|
|
354
|
+
handle: Pick<WorkHandleState, 'workId' | 'managedWorktree' | 'deliveryTargetBranch' | 'baseCommit' | 'deliveryBaseCommit'> & Partial<Pick<WorkHandleState, 'state'>>,
|
|
355
|
+
fallbackBaseRevision: string | undefined,
|
|
356
|
+
head: string | undefined,
|
|
357
|
+
explicitTargetBranch?: string,
|
|
358
|
+
): string | undefined {
|
|
359
|
+
const base = workDeliveryBaseRevision(handle) ?? fallbackBaseRevision;
|
|
360
|
+
if (!base || !head || !handle.managedWorktree) return base;
|
|
361
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(handle, repository.defaultBranch, explicitTargetBranch);
|
|
362
|
+
const targetHead = gitRevision(repository.canonicalRoot, targetBranch);
|
|
363
|
+
if (!targetHead || targetHead === base) return base;
|
|
364
|
+
// Once the delivery target has caught up to the exact candidate HEAD, using
|
|
365
|
+
// that target as the review baseline would erase the Work delta and make an
|
|
366
|
+
// unchanged approved review appear stale. Preserve only the already-durable
|
|
367
|
+
// delivery base in this exact catch-up state; without that durable fence, keep
|
|
368
|
+
// the previous fail-closed behavior below.
|
|
369
|
+
if (
|
|
370
|
+
targetHead === head
|
|
371
|
+
&& (
|
|
372
|
+
handle.state === 'merged'
|
|
373
|
+
|| (
|
|
374
|
+
handle.deliveryBaseCommit?.trim()
|
|
375
|
+
&& handle.deliveryBaseCommit.trim() !== (handle.baseCommit?.trim() || fallbackBaseRevision?.trim())
|
|
376
|
+
)
|
|
377
|
+
)
|
|
378
|
+
) return base;
|
|
379
|
+
// Review may exclude canonical target-only history only after proving that the
|
|
380
|
+
// recorded delivery base advances linearly to the exact target and that this
|
|
381
|
+
// target is already contained by the managed candidate. This is a read-only
|
|
382
|
+
// review baseline; deliveryBaseCommit remains persisted later by the existing
|
|
383
|
+
// target-advance delivery transition.
|
|
384
|
+
if (!gitIsAncestor(repository.canonicalRoot, base, targetHead)) return base;
|
|
385
|
+
if (!gitIsAncestor(repository.canonicalRoot, targetHead, head)) return base;
|
|
386
|
+
return targetHead;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function implementationReviewCommittedChangedPaths(input: {
|
|
390
|
+
repository: Pick<RepositoryRecord, 'canonicalRoot' | 'defaultBranch'>;
|
|
391
|
+
handle: WorkHandleState;
|
|
392
|
+
contract: NonNullable<ReturnType<typeof contractFor>>;
|
|
393
|
+
head: string | undefined;
|
|
394
|
+
explicitTargetBranch?: string;
|
|
395
|
+
review?: NonNullable<ReturnType<typeof latestImplementationReview>>;
|
|
396
|
+
}): string[] {
|
|
397
|
+
const committedBase = implementationReviewCommittedBaseRevision(
|
|
398
|
+
input.repository,
|
|
399
|
+
input.handle,
|
|
400
|
+
input.contract.baseRevision,
|
|
401
|
+
input.head,
|
|
402
|
+
input.explicitTargetBranch,
|
|
403
|
+
);
|
|
404
|
+
if (!input.head || !committedBase) return [];
|
|
405
|
+
const pathsFrom = (base: string) => normalizeImplementationReviewChangedPaths(
|
|
406
|
+
input.head === base ? [] : gitChangedPaths(input.repository.canonicalRoot, base, input.head!),
|
|
407
|
+
);
|
|
408
|
+
const committedPaths = pathsFrom(committedBase);
|
|
409
|
+
if (!input.review) return committedPaths;
|
|
410
|
+
|
|
411
|
+
// Review preparation, pre-delivery finalization, branch-cleanup retry, and
|
|
412
|
+
// cleaned-completion catch-up must share one committed review-baseline
|
|
413
|
+
// authority. The canonical target may legitimately move after review because
|
|
414
|
+
// this candidate was merged (making the current helper base equal HEAD), or
|
|
415
|
+
// it may already have contained the candidate before review. Only select
|
|
416
|
+
// between the current proven helper base and the durable delivery base when
|
|
417
|
+
// one exactly reproduces the immutable reviewed changed-path identity. A real
|
|
418
|
+
// source/path drift therefore still fails closed in the caller.
|
|
419
|
+
const reviewedDigest = implementationReviewChangedPathDigest(
|
|
420
|
+
normalizeImplementationReviewChangedPaths(input.review.changedPaths),
|
|
421
|
+
);
|
|
422
|
+
if (implementationReviewChangedPathDigest(committedPaths) === reviewedDigest) return committedPaths;
|
|
423
|
+
|
|
424
|
+
const recordedBase = workDeliveryBaseRevision(input.handle) ?? input.contract.baseRevision;
|
|
425
|
+
if (recordedBase && recordedBase !== committedBase) {
|
|
426
|
+
const recordedPaths = pathsFrom(recordedBase);
|
|
427
|
+
if (implementationReviewChangedPathDigest(recordedPaths) === reviewedDigest) return recordedPaths;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// New reviews intentionally preserve the durable delivery base once target has
|
|
431
|
+
// caught the exact candidate (implementationReviewCommittedBaseRevision). A
|
|
432
|
+
// review already persisted by an older Runtime may, however, legitimately
|
|
433
|
+
// carry the former exact-target-relative empty identity. Recovery may honor
|
|
434
|
+
// that historical identity only when the current canonical target is exactly
|
|
435
|
+
// the immutable reviewed source HEAD and its path digest matches. This branch
|
|
436
|
+
// is review-only compatibility; it can never create a new empty review.
|
|
437
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(
|
|
438
|
+
input.handle,
|
|
439
|
+
input.repository.defaultBranch,
|
|
440
|
+
input.explicitTargetBranch,
|
|
441
|
+
);
|
|
442
|
+
const targetHead = gitRevision(input.repository.canonicalRoot, targetBranch);
|
|
443
|
+
if (targetHead === input.head && input.review.sourceRevision === input.head) {
|
|
444
|
+
const exactTargetPaths = pathsFrom(targetHead);
|
|
445
|
+
if (implementationReviewChangedPathDigest(exactTargetPaths) === reviewedDigest) return exactTargetPaths;
|
|
446
|
+
}
|
|
447
|
+
return committedPaths;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function currentWorkReviewChangedPaths(
|
|
451
|
+
repository: RepositoryRecord,
|
|
452
|
+
handle: WorkHandleState,
|
|
453
|
+
contract: NonNullable<ReturnType<typeof contractFor>>,
|
|
454
|
+
explicitTargetBranch?: string,
|
|
455
|
+
): string[] {
|
|
456
|
+
const status = repositoryGitStatus(repository);
|
|
457
|
+
const { dirtyPaths, ownedPaths } = workOwnedDirtyPaths(contract, status);
|
|
458
|
+
if (dirtyPaths.length !== ownedPaths.length) {
|
|
459
|
+
const unowned = dirtyPaths.filter((path) => !ownedPaths.includes(path));
|
|
460
|
+
throw new Error(`WORK_IMPLEMENTATION_REVIEW_UNOWNED_DIRTY_PATH: ${unowned.join(', ')}`);
|
|
461
|
+
}
|
|
462
|
+
const head = status.head?.trim();
|
|
463
|
+
const committedPaths = implementationReviewCommittedChangedPaths({
|
|
464
|
+
repository, handle, contract, head, explicitTargetBranch,
|
|
465
|
+
});
|
|
466
|
+
const observed = normalizeImplementationReviewChangedPaths([...committedPaths, ...ownedPaths]);
|
|
467
|
+
const scopeViolation = findWorkPathScopeViolation(contract, observed);
|
|
468
|
+
if (scopeViolation) {
|
|
469
|
+
throw new Error(`WORK_IMPLEMENTATION_REVIEW_SCOPE_VIOLATION: ${scopeViolation.kind}:${scopeViolation.path}`);
|
|
470
|
+
}
|
|
471
|
+
return observed;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function assertPhysicalImplementationReviewGate(input: {
|
|
475
|
+
ctx: McpExecutionContext;
|
|
476
|
+
repository: RepositoryRecord;
|
|
477
|
+
handle: WorkHandleState;
|
|
478
|
+
contract: NonNullable<ReturnType<typeof contractFor>>;
|
|
479
|
+
/** Use the exact strict verification identity observed before repository staging. */
|
|
480
|
+
verificationWorkspaceFingerprint?: string;
|
|
481
|
+
targetBranch?: string;
|
|
482
|
+
}): ImplementationReviewCandidateIdentity {
|
|
483
|
+
const status = repositoryGitStatus(input.repository);
|
|
484
|
+
const sourceRevision = status.head?.trim();
|
|
485
|
+
if (!sourceRevision) throw new Error('WORK_IMPLEMENTATION_REVIEW_SOURCE_IDENTITY_REQUIRED');
|
|
486
|
+
const changedPaths = currentWorkReviewChangedPaths(input.repository, input.handle, input.contract, input.targetBranch);
|
|
487
|
+
const verificationWorkspaceFingerprint = input.verificationWorkspaceFingerprint?.trim()
|
|
488
|
+
|| workspaceValidationFingerprint(input.repository.canonicalRoot, status);
|
|
489
|
+
const workspaceFingerprint = implementationReviewContentFingerprint(input.repository.canonicalRoot, changedPaths);
|
|
490
|
+
const verification = authoritativeImplementationReviewVerificationEvidence({
|
|
491
|
+
repoId: input.contract.repoId,
|
|
492
|
+
workId: input.contract.workId,
|
|
493
|
+
requiredCheckIds: input.contract.checks,
|
|
494
|
+
records: input.contract.checkRefs,
|
|
495
|
+
sourceRevision,
|
|
496
|
+
workspaceFingerprint: verificationWorkspaceFingerprint,
|
|
497
|
+
});
|
|
498
|
+
if (verification.missingCheckIds.length > 0) {
|
|
499
|
+
throw new Error(`WORK_IMPLEMENTATION_REVIEW_VERIFICATION_REQUIRED: ${verification.missingCheckIds.join(', ')}`);
|
|
500
|
+
}
|
|
501
|
+
const candidate: ImplementationReviewCandidateIdentity = {
|
|
502
|
+
sourceRevision,
|
|
503
|
+
workspaceFingerprint,
|
|
504
|
+
verificationWorkspaceFingerprint,
|
|
505
|
+
changedPaths,
|
|
506
|
+
verificationEvidence: verification.evidence,
|
|
507
|
+
architectureEvidence: [],
|
|
508
|
+
};
|
|
509
|
+
assertImplementationReviewPreDeliveryBoundary({
|
|
510
|
+
repoId: input.contract.repoId,
|
|
511
|
+
workId: input.contract.workId,
|
|
512
|
+
workKind: input.contract.workKind,
|
|
513
|
+
reviews: input.contract.implementationReviews,
|
|
514
|
+
candidate,
|
|
515
|
+
requiredCheckIds: input.contract.checks,
|
|
516
|
+
verificationRecords: input.contract.checkRefs,
|
|
517
|
+
});
|
|
518
|
+
return candidate;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* A successful branch-cleanup retry can run after the reviewed worktree has
|
|
523
|
+
* already been removed. In that state filesystem content cannot be re-hashed,
|
|
524
|
+
* but the Work branch commit is immutable. Re-bind the shared review boundary
|
|
525
|
+
* to that exact branch HEAD and recompute current Work-bound verification
|
|
526
|
+
* evidence before deleting the ref. Cancelled/failed cleanup uses separate
|
|
527
|
+
* recovery authority and never calls this successful-delivery gate.
|
|
528
|
+
*/
|
|
529
|
+
function assertPhysicalBranchCleanupImplementationReviewGate(input: {
|
|
530
|
+
target: RepositoryRecord;
|
|
531
|
+
handle: WorkHandleState;
|
|
532
|
+
contract: NonNullable<ReturnType<typeof contractFor>>;
|
|
533
|
+
targetBranch?: string;
|
|
534
|
+
}): void {
|
|
535
|
+
const branchHead = gitRevision(input.target.canonicalRoot, input.handle.branch);
|
|
536
|
+
if (!branchHead) throw new Error('WORK_IMPLEMENTATION_REVIEW_BRANCH_SOURCE_REQUIRED');
|
|
537
|
+
if (input.handle.expectedHead && branchHead !== input.handle.expectedHead) {
|
|
538
|
+
throw new Error('WORK_IMPLEMENTATION_REVIEW_BRANCH_SOURCE_CHANGED');
|
|
539
|
+
}
|
|
540
|
+
const review = latestImplementationReview(input.contract.implementationReviews);
|
|
541
|
+
const changedPaths = implementationReviewCommittedChangedPaths({
|
|
542
|
+
repository: input.target,
|
|
543
|
+
handle: input.handle,
|
|
544
|
+
contract: input.contract,
|
|
545
|
+
head: branchHead,
|
|
546
|
+
explicitTargetBranch: input.targetBranch,
|
|
547
|
+
review,
|
|
548
|
+
});
|
|
549
|
+
if (!workRequiresImplementationReview(input.contract.workKind, changedPaths)) return;
|
|
550
|
+
if (!review) throw new Error('WORK_IMPLEMENTATION_REVIEW_REQUIRED');
|
|
551
|
+
if (review.sourceRevision !== branchHead) throw new Error('WORK_IMPLEMENTATION_REVIEW_STALE: branch source revision changed');
|
|
552
|
+
const verification = authoritativeImplementationReviewVerificationEvidence({
|
|
553
|
+
repoId: input.contract.repoId,
|
|
554
|
+
workId: input.contract.workId,
|
|
555
|
+
requiredCheckIds: input.contract.checks,
|
|
556
|
+
records: input.contract.checkRefs,
|
|
557
|
+
sourceRevision: branchHead,
|
|
558
|
+
workspaceFingerprint: review.verificationWorkspaceFingerprint,
|
|
559
|
+
});
|
|
560
|
+
if (verification.missingCheckIds.length > 0) {
|
|
561
|
+
throw new Error(`WORK_IMPLEMENTATION_REVIEW_VERIFICATION_REQUIRED: ${verification.missingCheckIds.join(', ')}`);
|
|
562
|
+
}
|
|
563
|
+
assertImplementationReviewPreDeliveryBoundary({
|
|
564
|
+
repoId: input.contract.repoId,
|
|
565
|
+
workId: input.contract.workId,
|
|
566
|
+
workKind: input.contract.workKind,
|
|
567
|
+
reviews: input.contract.implementationReviews,
|
|
568
|
+
candidate: {
|
|
569
|
+
sourceRevision: branchHead,
|
|
570
|
+
workspaceFingerprint: review.workspaceFingerprint,
|
|
571
|
+
verificationWorkspaceFingerprint: review.verificationWorkspaceFingerprint,
|
|
572
|
+
changedPaths,
|
|
573
|
+
verificationEvidence: verification.evidence,
|
|
574
|
+
architectureEvidence: review.architectureEvidence,
|
|
575
|
+
},
|
|
576
|
+
requiredCheckIds: input.contract.checks,
|
|
577
|
+
verificationRecords: input.contract.checkRefs,
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Re-prove review authority from immutable Git + durable review/check identity
|
|
583
|
+
* after successful managed-worktree cleanup. This path never recreates a
|
|
584
|
+
* checkout and never authorizes commit/merge; it only allows a missing semantic
|
|
585
|
+
* completion receipt to catch up with already-durable physical finalization.
|
|
586
|
+
*/
|
|
587
|
+
function assertCleanedCompletionImplementationReviewGate(input: {
|
|
588
|
+
target: RepositoryRecord;
|
|
589
|
+
handle: WorkHandleState;
|
|
590
|
+
contract: NonNullable<ReturnType<typeof contractFor>>;
|
|
591
|
+
}): NonNullable<ReturnType<typeof latestImplementationReview>> {
|
|
592
|
+
const deliveryRevision = input.handle.expectedHead ?? input.handle.baseCommit;
|
|
593
|
+
if (!deliveryRevision) throw new Error('WORK_IMPLEMENTATION_REVIEW_SOURCE_IDENTITY_REQUIRED');
|
|
594
|
+
const review = latestImplementationReview(input.contract.implementationReviews);
|
|
595
|
+
if (!review) throw new Error('WORK_IMPLEMENTATION_REVIEW_REQUIRED');
|
|
596
|
+
if (review.sourceRevision !== deliveryRevision) {
|
|
597
|
+
throw new Error('WORK_IMPLEMENTATION_REVIEW_STALE: cleaned delivery source revision changed');
|
|
598
|
+
}
|
|
599
|
+
const changedPaths = implementationReviewCommittedChangedPaths({
|
|
600
|
+
repository: input.target, handle: input.handle, contract: input.contract, head: deliveryRevision, review,
|
|
601
|
+
});
|
|
602
|
+
const reviewedPaths = normalizeImplementationReviewChangedPaths(review.changedPaths);
|
|
603
|
+
if (implementationReviewChangedPathDigest(reviewedPaths) !== implementationReviewChangedPathDigest(changedPaths)) {
|
|
604
|
+
throw new Error('WORK_IMPLEMENTATION_REVIEW_STALE: cleaned delivery changed-path identity changed');
|
|
605
|
+
}
|
|
606
|
+
const verification = authoritativeImplementationReviewVerificationEvidence({
|
|
607
|
+
repoId: input.contract.repoId,
|
|
608
|
+
workId: input.contract.workId,
|
|
609
|
+
requiredCheckIds: input.contract.checks,
|
|
610
|
+
records: input.contract.checkRefs,
|
|
611
|
+
sourceRevision: deliveryRevision,
|
|
612
|
+
workspaceFingerprint: review.verificationWorkspaceFingerprint,
|
|
613
|
+
});
|
|
614
|
+
if (verification.missingCheckIds.length > 0) {
|
|
615
|
+
throw new Error(`WORK_IMPLEMENTATION_REVIEW_VERIFICATION_REQUIRED: ${verification.missingCheckIds.join(', ')}`);
|
|
616
|
+
}
|
|
617
|
+
assertImplementationReviewPreDeliveryBoundary({
|
|
618
|
+
repoId: input.contract.repoId,
|
|
619
|
+
workId: input.contract.workId,
|
|
620
|
+
workKind: input.contract.workKind,
|
|
621
|
+
reviews: input.contract.implementationReviews,
|
|
622
|
+
candidate: {
|
|
623
|
+
sourceRevision: deliveryRevision,
|
|
624
|
+
workspaceFingerprint: review.workspaceFingerprint,
|
|
625
|
+
verificationWorkspaceFingerprint: review.verificationWorkspaceFingerprint,
|
|
626
|
+
changedPaths,
|
|
627
|
+
verificationEvidence: verification.evidence,
|
|
628
|
+
architectureEvidence: review.architectureEvidence,
|
|
629
|
+
},
|
|
630
|
+
requiredCheckIds: input.contract.checks,
|
|
631
|
+
verificationRecords: input.contract.checkRefs,
|
|
632
|
+
});
|
|
633
|
+
return review;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
|
|
286
637
|
export interface TargetDirtyWorkOwnershipInspection {
|
|
287
638
|
owned: boolean;
|
|
288
639
|
dirtyPaths: string[];
|
|
@@ -338,6 +689,26 @@ export function inspectTargetDirtyWorkOwnership(input: {
|
|
|
338
689
|
};
|
|
339
690
|
}
|
|
340
691
|
|
|
692
|
+
export interface TargetDirtyPreservationPlan {
|
|
693
|
+
preserveDirtyTargetPaths: string[];
|
|
694
|
+
ownership: TargetDirtyWorkOwnershipInspection;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Dirty target content is a Git preservation concern first. Semantic Work
|
|
699
|
+
* ownership remains useful provenance, but it is not required when the
|
|
700
|
+
* integration candidate is disjoint. repositoryGitFinishWorkflow owns the
|
|
701
|
+
* exact overlap fence and fails before target mutation when a preserved path
|
|
702
|
+
* conflicts with the candidate.
|
|
703
|
+
*/
|
|
704
|
+
export function planTargetDirtyPreservation(input: Parameters<typeof inspectTargetDirtyWorkOwnership>[0]): TargetDirtyPreservationPlan {
|
|
705
|
+
const ownership = inspectTargetDirtyWorkOwnership(input);
|
|
706
|
+
return {
|
|
707
|
+
preserveDirtyTargetPaths: ownership.dirtyPaths,
|
|
708
|
+
ownership,
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
341
712
|
export interface DirectCanonicalTargetAdvanceInspection {
|
|
342
713
|
reconcilable: boolean;
|
|
343
714
|
reason:
|
|
@@ -474,6 +845,169 @@ export function inspectDirectCanonicalTargetAdvanceReconciliation(input: {
|
|
|
474
845
|
};
|
|
475
846
|
}
|
|
476
847
|
|
|
848
|
+
export interface DirectCanonicalTargetAdvanceReconciliationCommandResult {
|
|
849
|
+
reconciled: boolean;
|
|
850
|
+
handle: WorkHandleState;
|
|
851
|
+
inspection: DirectCanonicalTargetAdvanceInspection;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Application command for advancing Direct canonical Work delivery identity.
|
|
856
|
+
* The pure inspector remains the admission authority; this command is the only
|
|
857
|
+
* place shared review/finalization callers persist the approved identity move
|
|
858
|
+
* and its evidence. Gateway callers consume the returned handle and never write
|
|
859
|
+
* lifecycle/evidence records themselves.
|
|
860
|
+
*/
|
|
861
|
+
export function reconcileDirectCanonicalTargetAdvanceCommand(input: {
|
|
862
|
+
controllerHome: string;
|
|
863
|
+
repoId: string;
|
|
864
|
+
workId: string;
|
|
865
|
+
handle: WorkHandleState;
|
|
866
|
+
root: string;
|
|
867
|
+
targetBranch: string;
|
|
868
|
+
status: ReturnType<typeof repositoryGitStatus>;
|
|
869
|
+
scope?: { allowedPaths: string[]; forbiddenPaths: string[] };
|
|
870
|
+
checkIds: string[];
|
|
871
|
+
checkRefs: VerificationRecord[];
|
|
872
|
+
persistHandle?: (patch: { deliveryBaseCommit: string; expectedHead: string; failureReason: undefined }) => WorkHandleState;
|
|
873
|
+
evidenceTitle?: string;
|
|
874
|
+
}): DirectCanonicalTargetAdvanceReconciliationCommandResult {
|
|
875
|
+
if (input.handle.repositoryId !== input.repoId) {
|
|
876
|
+
throw new Error(`WORK_DIRECT_TARGET_ADVANCE_REPOSITORY_MISMATCH: expected ${input.handle.repositoryId}, found ${input.repoId}`);
|
|
877
|
+
}
|
|
878
|
+
const boundWorkId = input.handle.workContractId ?? input.handle.workId;
|
|
879
|
+
if (boundWorkId !== input.workId) {
|
|
880
|
+
throw new Error(`WORK_DIRECT_TARGET_ADVANCE_IDENTITY_MISMATCH: expected ${boundWorkId}, found ${input.workId}`);
|
|
881
|
+
}
|
|
882
|
+
const inspection = inspectDirectCanonicalTargetAdvanceReconciliation({
|
|
883
|
+
root: input.root,
|
|
884
|
+
worktreePath: input.handle.worktreePath,
|
|
885
|
+
managedWorktree: input.handle.managedWorktree,
|
|
886
|
+
workBranch: input.handle.branch,
|
|
887
|
+
targetBranch: input.targetBranch,
|
|
888
|
+
expectedRevision: input.handle.expectedHead,
|
|
889
|
+
status: input.status,
|
|
890
|
+
scope: input.scope,
|
|
891
|
+
checkIds: input.checkIds,
|
|
892
|
+
checkRefs: input.checkRefs,
|
|
893
|
+
});
|
|
894
|
+
if (!inspection.reconcilable || !inspection.targetHead) {
|
|
895
|
+
return { reconciled: false, handle: input.handle, inspection };
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
const previousHead = input.handle.expectedHead;
|
|
899
|
+
const patch = {
|
|
900
|
+
deliveryBaseCommit: inspection.targetHead,
|
|
901
|
+
expectedHead: inspection.targetHead,
|
|
902
|
+
failureReason: undefined,
|
|
903
|
+
} as const;
|
|
904
|
+
const handle = input.persistHandle
|
|
905
|
+
? input.persistHandle(patch)
|
|
906
|
+
: transitionWorkHandle(input.controllerHome, input.handle, input.handle.state, patch);
|
|
907
|
+
appendWorkEvidence({ controllerHome: input.controllerHome, repoId: input.repoId }, input.workId, {
|
|
908
|
+
title: input.evidenceTitle ?? 'direct canonical target advancement reconciled',
|
|
909
|
+
summary: `Canonical ${input.targetBranch} advanced linearly ${previousHead} -> ${inspection.targetHead}. Delivery identity adopted only the target revision after proving all ${inspection.dirtyPaths.length} dirty path(s) are Work-owned, ${inspection.targetChangedPaths.length} target-only path(s) are disjoint, and fresh exact verification bound [${inspection.freshCheckIds.join(', ')}] to source ${inspection.targetHead} plus workspace ${inspection.workspaceFingerprint?.slice(0, 16) ?? 'unknown'}.`,
|
|
910
|
+
detailLevel: 'summary',
|
|
911
|
+
});
|
|
912
|
+
return { reconciled: true, handle, inspection };
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
export interface ManagedWorkSuccessorAdoptionInspection {
|
|
916
|
+
adoptable: boolean;
|
|
917
|
+
reason:
|
|
918
|
+
| 'not_managed_worktree'
|
|
919
|
+
| 'path_mismatch'
|
|
920
|
+
| 'branch_mismatch'
|
|
921
|
+
| 'workspace_dirty'
|
|
922
|
+
| 'revision_unavailable'
|
|
923
|
+
| 'no_head_change'
|
|
924
|
+
| 'descendant_progress'
|
|
925
|
+
| 'target_not_advanced'
|
|
926
|
+
| 'target_history_rewritten'
|
|
927
|
+
| 'target_not_candidate_ancestor'
|
|
928
|
+
| 'non_linear_candidate'
|
|
929
|
+
| 'scope_violation'
|
|
930
|
+
| 'adoptable';
|
|
931
|
+
previousHead?: string;
|
|
932
|
+
previousDeliveryBase?: string;
|
|
933
|
+
candidateHead?: string;
|
|
934
|
+
targetHead?: string;
|
|
935
|
+
candidateChangedPaths: string[];
|
|
936
|
+
detail?: string;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Inspect a manually conflict-repaired managed Work candidate before changing
|
|
941
|
+
* WorkHandle authority. This is intentionally narrower than ordinary
|
|
942
|
+
* descendant progress: canonical target must have advanced linearly from the
|
|
943
|
+
* recorded delivery base, and the rewritten clean candidate must contain that
|
|
944
|
+
* exact target with only Work-scoped target-relative changes.
|
|
945
|
+
*/
|
|
946
|
+
export function inspectManagedWorkSuccessorAdoption(input: {
|
|
947
|
+
root: string;
|
|
948
|
+
worktreePath: string;
|
|
949
|
+
managedWorktree: boolean;
|
|
950
|
+
workBranch: string;
|
|
951
|
+
targetBranch: string;
|
|
952
|
+
expectedRevision?: string;
|
|
953
|
+
deliveryBaseRevision?: string;
|
|
954
|
+
status: ReturnType<typeof repositoryGitStatus>;
|
|
955
|
+
scope?: { allowedPaths: string[]; forbiddenPaths: string[] };
|
|
956
|
+
}): ManagedWorkSuccessorAdoptionInspection {
|
|
957
|
+
const empty = (reason: ManagedWorkSuccessorAdoptionInspection['reason'], detail?: string): ManagedWorkSuccessorAdoptionInspection => ({
|
|
958
|
+
adoptable: false,
|
|
959
|
+
reason,
|
|
960
|
+
candidateChangedPaths: [],
|
|
961
|
+
...(detail ? { detail } : {}),
|
|
962
|
+
});
|
|
963
|
+
if (!input.managedWorktree) return empty('not_managed_worktree');
|
|
964
|
+
try {
|
|
965
|
+
if (realpathSync(input.root) !== realpathSync(input.worktreePath)) return empty('path_mismatch');
|
|
966
|
+
} catch {
|
|
967
|
+
return empty('path_mismatch');
|
|
968
|
+
}
|
|
969
|
+
const branch = spawnSync('git', ['-C', input.root, 'branch', '--show-current'], {
|
|
970
|
+
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
971
|
+
});
|
|
972
|
+
if (branch.status !== 0 || String(branch.stdout ?? '').trim() !== input.workBranch) return empty('branch_mismatch');
|
|
973
|
+
if (!input.status.clean) return empty('workspace_dirty');
|
|
974
|
+
|
|
975
|
+
const previousHead = input.expectedRevision ? gitRevision(input.root, input.expectedRevision) : undefined;
|
|
976
|
+
const previousDeliveryBase = input.deliveryBaseRevision ? gitRevision(input.root, input.deliveryBaseRevision) : undefined;
|
|
977
|
+
const candidateHead = input.status.head ? gitRevision(input.root, input.status.head) : undefined;
|
|
978
|
+
const targetHead = gitRevision(input.root, input.targetBranch);
|
|
979
|
+
if (!previousHead || !previousDeliveryBase || !candidateHead || !targetHead) return empty('revision_unavailable');
|
|
980
|
+
const base = { previousHead, previousDeliveryBase, candidateHead, targetHead, candidateChangedPaths: [] as string[] };
|
|
981
|
+
if (candidateHead === previousHead) return { ...base, adoptable: false, reason: 'no_head_change' };
|
|
982
|
+
if (gitIsAncestor(input.root, previousHead, candidateHead)) return { ...base, adoptable: false, reason: 'descendant_progress' };
|
|
983
|
+
if (targetHead === previousDeliveryBase) return { ...base, adoptable: false, reason: 'target_not_advanced' };
|
|
984
|
+
if (!gitIsAncestor(input.root, previousDeliveryBase, targetHead)) {
|
|
985
|
+
return { ...base, adoptable: false, reason: 'target_history_rewritten' };
|
|
986
|
+
}
|
|
987
|
+
if (!gitIsAncestor(input.root, targetHead, candidateHead)) {
|
|
988
|
+
return { ...base, adoptable: false, reason: 'target_not_candidate_ancestor' };
|
|
989
|
+
}
|
|
990
|
+
const mergeCommits = targetAdvanceLinearMergeCommits(input.root, targetHead, candidateHead);
|
|
991
|
+
if (mergeCommits.length > 0) {
|
|
992
|
+
return { ...base, adoptable: false, reason: 'non_linear_candidate', detail: mergeCommits.slice(0, 8).join(', ') };
|
|
993
|
+
}
|
|
994
|
+
const candidateChangedPaths = gitChangedPaths(input.root, targetHead, candidateHead);
|
|
995
|
+
if (!input.scope) {
|
|
996
|
+
return { ...base, candidateChangedPaths, adoptable: false, reason: 'scope_violation', detail: 'WorkContract scope unavailable' };
|
|
997
|
+
}
|
|
998
|
+
const scopeViolation = findWorkPathScopeViolation(input.scope, candidateChangedPaths);
|
|
999
|
+
if (scopeViolation) {
|
|
1000
|
+
return {
|
|
1001
|
+
...base,
|
|
1002
|
+
candidateChangedPaths,
|
|
1003
|
+
adoptable: false,
|
|
1004
|
+
reason: 'scope_violation',
|
|
1005
|
+
detail: `${scopeViolation.kind}:${scopeViolation.path}`,
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
return { ...base, candidateChangedPaths, adoptable: true, reason: 'adoptable' };
|
|
1009
|
+
}
|
|
1010
|
+
|
|
477
1011
|
export function completionReceiptChangedPaths(repoRoot: string, baseRevision: string, deliveryRevision: string): string[] {
|
|
478
1012
|
const result = spawnSync('git', ['-C', repoRoot, 'diff', '--name-only', `${baseRevision}..${deliveryRevision}`], {
|
|
479
1013
|
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
@@ -486,16 +1020,18 @@ export function completionReceiptChangedPaths(repoRoot: string, baseRevision: st
|
|
|
486
1020
|
}
|
|
487
1021
|
|
|
488
1022
|
function completionReceiptForFinalizedWork(
|
|
489
|
-
ctx:
|
|
1023
|
+
ctx: McpExecutionContext,
|
|
490
1024
|
handle: WorkHandleState,
|
|
491
1025
|
contract: ReturnType<typeof contractFor>,
|
|
492
1026
|
args: Record<string, unknown>,
|
|
493
1027
|
): CompletionReceipt {
|
|
494
1028
|
const repository = getRepository(handle.repositoryId, ctx.controllerHome, { includeRemoved: true });
|
|
495
1029
|
const target = selectWorkFinalizationTarget(repository, handle);
|
|
496
|
-
const targetBranch =
|
|
497
|
-
|
|
498
|
-
|
|
1030
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(
|
|
1031
|
+
handle,
|
|
1032
|
+
repository.defaultBranch,
|
|
1033
|
+
typeof args.target_branch === 'string' ? args.target_branch : undefined,
|
|
1034
|
+
);
|
|
499
1035
|
const targetRevision = gitRevision(target.canonicalRoot, targetBranch);
|
|
500
1036
|
if (!targetRevision) throw new Error(`WORK_COMPLETION_RECEIPT_TARGET_REQUIRED: target branch ${targetBranch} is unavailable`);
|
|
501
1037
|
const noChange = args.completion_outcome === 'completed_no_change';
|
|
@@ -577,7 +1113,7 @@ function runCleanup(targetRoot: string, worktreePath: string): { ok: boolean; me
|
|
|
577
1113
|
* crash without turning a missing checkout into proof of safety.
|
|
578
1114
|
*/
|
|
579
1115
|
export function inspectCleanupOnlyMergedHead(
|
|
580
|
-
ctx:
|
|
1116
|
+
ctx: McpExecutionContext,
|
|
581
1117
|
current: WorkHandleState,
|
|
582
1118
|
args: Record<string, unknown>,
|
|
583
1119
|
): { currentHead: string; cancelledContract: boolean; worktreeMissing: boolean } | undefined {
|
|
@@ -587,9 +1123,11 @@ export function inspectCleanupOnlyMergedHead(
|
|
|
587
1123
|
|
|
588
1124
|
const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
|
|
589
1125
|
const target = selectWorkFinalizationTarget(repository, current);
|
|
590
|
-
const targetBranch =
|
|
591
|
-
|
|
592
|
-
|
|
1126
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(
|
|
1127
|
+
current,
|
|
1128
|
+
repository.defaultBranch,
|
|
1129
|
+
typeof args.target_branch === 'string' ? args.target_branch : undefined,
|
|
1130
|
+
);
|
|
593
1131
|
const branchHeadResult = spawnSync('git', ['-C', target.canonicalRoot, 'rev-parse', '--verify', `refs/heads/${current.branch}`], {
|
|
594
1132
|
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
595
1133
|
});
|
|
@@ -624,7 +1162,11 @@ export function inspectCleanupOnlyMergedHead(
|
|
|
624
1162
|
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
625
1163
|
});
|
|
626
1164
|
if (currentBranch.status !== 0 || String(currentBranch.stdout ?? '').trim() !== current.branch) return undefined;
|
|
627
|
-
|
|
1165
|
+
// Cleanup reconciliation proves the exact Work-owned revision, regardless of
|
|
1166
|
+
// terminal contract status. A same-named branch may be advanced later by a
|
|
1167
|
+
// different controller or manual Git operation; reachability from the target
|
|
1168
|
+
// is not authority to adopt that newer HEAD as this Work's delivery.
|
|
1169
|
+
if (current.expectedHead && currentHead !== current.expectedHead) return undefined;
|
|
628
1170
|
return { currentHead, cancelledContract, worktreeMissing: false };
|
|
629
1171
|
}
|
|
630
1172
|
|
|
@@ -640,7 +1182,7 @@ interface FailedCleanupProof {
|
|
|
640
1182
|
* cleanup only; it never proves successful verification or delivery.
|
|
641
1183
|
*/
|
|
642
1184
|
function failedCleanupOnlyHead(
|
|
643
|
-
ctx:
|
|
1185
|
+
ctx: McpExecutionContext,
|
|
644
1186
|
current: WorkHandleState,
|
|
645
1187
|
args: Record<string, unknown>,
|
|
646
1188
|
): FailedCleanupProof | undefined {
|
|
@@ -658,9 +1200,11 @@ function failedCleanupOnlyHead(
|
|
|
658
1200
|
if (!expectedHead) return undefined;
|
|
659
1201
|
const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
|
|
660
1202
|
const target = selectWorkFinalizationTarget(repository, current);
|
|
661
|
-
const targetBranch =
|
|
662
|
-
|
|
663
|
-
|
|
1203
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(
|
|
1204
|
+
current,
|
|
1205
|
+
repository.defaultBranch,
|
|
1206
|
+
typeof args.target_branch === 'string' ? args.target_branch : undefined,
|
|
1207
|
+
);
|
|
664
1208
|
const merged = spawnSync('git', ['-C', target.canonicalRoot, 'merge-base', '--is-ancestor', expectedHead, targetBranch], {
|
|
665
1209
|
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
666
1210
|
});
|
|
@@ -708,6 +1252,98 @@ function retryHandleStateForFinalization(stages: WorkFinalizationStages): WorkHa
|
|
|
708
1252
|
return 'validating';
|
|
709
1253
|
}
|
|
710
1254
|
|
|
1255
|
+
function reconcileFailedNonLinearTargetAdvanceRepair(
|
|
1256
|
+
ctx: McpExecutionContext,
|
|
1257
|
+
current: WorkHandleState,
|
|
1258
|
+
args: Record<string, unknown>,
|
|
1259
|
+
): WorkHandleState {
|
|
1260
|
+
if (
|
|
1261
|
+
current.state !== 'failed'
|
|
1262
|
+
|| current.finalization.merge !== 'failed'
|
|
1263
|
+
|| current.finalization.failureCode !== 'WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION'
|
|
1264
|
+
|| args.merge !== true
|
|
1265
|
+
|| !current.managedWorktree
|
|
1266
|
+
|| !current.expectedHead
|
|
1267
|
+
) return current;
|
|
1268
|
+
|
|
1269
|
+
const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
|
|
1270
|
+
const worktree = selectRepositoryCheckout(repository, current.checkoutId);
|
|
1271
|
+
const status = repositoryGitStatus(worktree);
|
|
1272
|
+
const repairedHead = status.head;
|
|
1273
|
+
if (!status.clean || !repairedHead || repairedHead === current.expectedHead) return current;
|
|
1274
|
+
const branch = spawnSync('git', ['-C', worktree.canonicalRoot, 'branch', '--show-current'], {
|
|
1275
|
+
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
1276
|
+
});
|
|
1277
|
+
if (branch.status !== 0 || String(branch.stdout ?? '').trim() !== current.branch) {
|
|
1278
|
+
throw new Error('WORK_TARGET_ADVANCE_REPAIR_BRANCH_MISMATCH');
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
const targetRevision = workDeliveryBaseRevision(current);
|
|
1282
|
+
if (!targetRevision) throw new Error('WORK_TARGET_ADVANCE_REPAIR_TARGET_MISSING');
|
|
1283
|
+
const inspection = inspectFailedNonLinearTargetAdvanceRepair(
|
|
1284
|
+
worktree.canonicalRoot,
|
|
1285
|
+
current.expectedHead,
|
|
1286
|
+
repairedHead,
|
|
1287
|
+
targetRevision,
|
|
1288
|
+
);
|
|
1289
|
+
if (!inspection.eligible) {
|
|
1290
|
+
throw new Error(`WORK_TARGET_ADVANCE_REPAIR_UNSAFE: ${inspection.reason}`);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
const contract = contractFor(ctx, current);
|
|
1294
|
+
if (!contract) throw new Error(`WORK_TARGET_ADVANCE_REPAIR_CONTRACT_MISSING: ${current.workContractId ?? current.workId}`);
|
|
1295
|
+
const changedPaths = gitChangedPaths(worktree.canonicalRoot, inspection.targetHead, inspection.repairedHead);
|
|
1296
|
+
const scopeViolation = targetAdvanceWorkScopeViolation(contract, changedPaths);
|
|
1297
|
+
if (scopeViolation) {
|
|
1298
|
+
throw new Error(`WORK_TARGET_ADVANCE_REPAIR_SCOPE_VIOLATION: Work-owned ${scopeViolation.kind} path ${scopeViolation.path}`);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
const repaired = withControllerLock(
|
|
1302
|
+
ctx.controllerHome,
|
|
1303
|
+
{ scope: 'worktree', repoId: current.repositoryId, worktreeId: current.checkoutId },
|
|
1304
|
+
`work-finalize:${current.workId}:repair-non-linear-target-advance`,
|
|
1305
|
+
() => {
|
|
1306
|
+
const fresh = readWorkHandle(ctx.controllerHome, current.repositoryId, current.workId) ?? current;
|
|
1307
|
+
if (fresh.state !== 'failed' || fresh.expectedHead !== inspection.failedHead || fresh.finalization.merge !== 'failed') {
|
|
1308
|
+
throw new Error('WORK_TARGET_ADVANCE_REPAIR_STALE_HANDLE');
|
|
1309
|
+
}
|
|
1310
|
+
return transitionWorkHandle(ctx.controllerHome, fresh, 'validating', {
|
|
1311
|
+
expectedHead: inspection.repairedHead,
|
|
1312
|
+
deliveryBaseCommit: inspection.targetHead,
|
|
1313
|
+
failureReason: undefined,
|
|
1314
|
+
validationRun: undefined,
|
|
1315
|
+
validatedInputFingerprint: undefined,
|
|
1316
|
+
finalization: {
|
|
1317
|
+
...fresh.finalization,
|
|
1318
|
+
validation: 'pending',
|
|
1319
|
+
commit: fresh.finalization.commit === 'failed' ? 'pending' : fresh.finalization.commit,
|
|
1320
|
+
merge: 'pending',
|
|
1321
|
+
failureCode: undefined, lastError: undefined,
|
|
1322
|
+
},
|
|
1323
|
+
});
|
|
1324
|
+
},
|
|
1325
|
+
10_000,
|
|
1326
|
+
);
|
|
1327
|
+
replaceTargetAdvanceScopeEvidence(ctx, contract, changedPaths);
|
|
1328
|
+
transitionWorkContractPhase(
|
|
1329
|
+
{ controllerHome: ctx.controllerHome, repoId: current.repositoryId },
|
|
1330
|
+
contract.workId,
|
|
1331
|
+
{
|
|
1332
|
+
phase: 'delivery',
|
|
1333
|
+
status: 'running',
|
|
1334
|
+
state: 'active',
|
|
1335
|
+
summary: `Repaired non-linear target-advance history ${inspection.failedHead} -> ${inspection.repairedHead} with identical Git tree; exact validation is required again before delivery.`,
|
|
1336
|
+
},
|
|
1337
|
+
);
|
|
1338
|
+
appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, contract.workId, {
|
|
1339
|
+
title: 'non-linear target-advance candidate repaired',
|
|
1340
|
+
summary: `Replaced failed merge candidate ${inspection.failedHead} with linear candidate ${inspection.repairedHead} above target ${inspection.targetHead} only after exact Git-tree equality, zero repaired merge commits, clean exact Work checkout/branch ownership, and Work path-scope verification. Prior validation identity was invalidated and must be re-established.`,
|
|
1341
|
+
detailLevel: 'summary',
|
|
1342
|
+
});
|
|
1343
|
+
markRepositoryProjectionDirty(ctx.controllerHome, current.repositoryId, `finalize:${current.workId}:non-linear-target-repair`);
|
|
1344
|
+
return repaired;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
711
1347
|
export function resetFinalizationStagesForRequest(
|
|
712
1348
|
stages: WorkFinalizationStages,
|
|
713
1349
|
wants: { commit: boolean; merge: boolean; cleanup: boolean },
|
|
@@ -761,6 +1397,48 @@ export function resetFinalizationStagesForRequest(
|
|
|
761
1397
|
return next;
|
|
762
1398
|
}
|
|
763
1399
|
|
|
1400
|
+
function completeFinalizedWorkContract(input: {
|
|
1401
|
+
ctx: McpExecutionContext;
|
|
1402
|
+
handle: WorkHandleState;
|
|
1403
|
+
contract: NonNullable<ReturnType<typeof contractFor>>;
|
|
1404
|
+
args: Record<string, unknown>;
|
|
1405
|
+
terminalizationOwner: ReturnType<typeof assertWorkControllerOwnership>;
|
|
1406
|
+
outcome: 'completed_changed' | 'completed_no_change';
|
|
1407
|
+
}): CompletionReceipt {
|
|
1408
|
+
const receipt = completionReceiptForFinalizedWork(input.ctx, input.handle, input.contract, input.args);
|
|
1409
|
+
const ownerPrincipal = input.terminalizationOwner.principalId?.trim() || input.terminalizationOwner.controllerId;
|
|
1410
|
+
const ownerInstanceId = input.terminalizationOwner.controllerInstanceId?.trim() || '';
|
|
1411
|
+
const ownerClaimGeneration = input.terminalizationOwner.claimGeneration;
|
|
1412
|
+
if (!ownerInstanceId || typeof ownerClaimGeneration !== 'number' || ownerClaimGeneration < 1) {
|
|
1413
|
+
throw new Error(`WORK_CONTROLLER_TERMINALIZATION_AUTHORITY_INVALID: ${input.contract.workId}`);
|
|
1414
|
+
}
|
|
1415
|
+
const fencedCompletion = withControllerSessionTerminalizationFence(
|
|
1416
|
+
{ controllerHome: input.ctx.controllerHome, repoId: input.handle.repositoryId },
|
|
1417
|
+
{
|
|
1418
|
+
workId: input.contract.workId,
|
|
1419
|
+
actor: `work-finalize-completion:${input.terminalizationOwner.controllerId}:${ownerInstanceId}`,
|
|
1420
|
+
authority: {
|
|
1421
|
+
controllerId: input.terminalizationOwner.controllerId,
|
|
1422
|
+
controllerType: input.terminalizationOwner.controllerType,
|
|
1423
|
+
principalId: ownerPrincipal,
|
|
1424
|
+
controllerInstanceId: ownerInstanceId,
|
|
1425
|
+
claimGeneration: ownerClaimGeneration,
|
|
1426
|
+
},
|
|
1427
|
+
},
|
|
1428
|
+
() => completeWorkWithReceipt(
|
|
1429
|
+
{ controllerHome: input.ctx.controllerHome, repoId: input.handle.repositoryId },
|
|
1430
|
+
input.contract.workId,
|
|
1431
|
+
receipt,
|
|
1432
|
+
input.outcome,
|
|
1433
|
+
input.outcome === 'completed_changed' ? 'repository_change' : input.contract.workKind,
|
|
1434
|
+
),
|
|
1435
|
+
);
|
|
1436
|
+
if (!fencedCompletion.allowed) {
|
|
1437
|
+
throw new Error(`WORK_TERMINALIZATION_AUTHORITY_FENCED: ${input.contract.workId}:${fencedCompletion.reason}`);
|
|
1438
|
+
}
|
|
1439
|
+
return receipt;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
764
1442
|
function finalizationComplete(stages: WorkFinalizationStages): boolean {
|
|
765
1443
|
return stages.validation === 'done'
|
|
766
1444
|
&& stages.commit !== 'pending'
|
|
@@ -797,17 +1475,15 @@ function currentWorkValidationInput(
|
|
|
797
1475
|
};
|
|
798
1476
|
}
|
|
799
1477
|
|
|
800
|
-
export async function finalizeWork(ctx:
|
|
1478
|
+
export async function finalizeWork(ctx: McpExecutionContext, args: Record<string, unknown>): Promise<Record<string, unknown>> {
|
|
801
1479
|
const session = requireSession(ctx, args);
|
|
802
1480
|
let current = workForSession(ctx, session, args, { allowClaimedTerminalCleanup: args.cleanup !== false });
|
|
803
1481
|
const requestedWants = { commit: args.commit === true, merge: args.merge === true, cleanup: args.cleanup === true };
|
|
804
|
-
|
|
805
|
-
? requestedFailedFinalizationRetry(current.finalization, requestedWants)
|
|
806
|
-
: undefined;
|
|
1482
|
+
let retryStage = requestedFailedFinalizationRetry(current.finalization, requestedWants);
|
|
807
1483
|
const retryContract = retryStage ? contractFor(ctx, current) : undefined;
|
|
808
1484
|
if (
|
|
809
1485
|
retryStage
|
|
810
|
-
&& retryContract?.status === 'failed'
|
|
1486
|
+
&& (retryContract?.status === 'failed' || retryContract?.status === 'blocked')
|
|
811
1487
|
&& !retryContract.completionOutcome
|
|
812
1488
|
&& !retryContract.completionReceipt
|
|
813
1489
|
) {
|
|
@@ -827,6 +1503,8 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
827
1503
|
return await reconcileTerminalCleanup(ctx, session, current, args, terminalOutcome);
|
|
828
1504
|
}
|
|
829
1505
|
const terminalizationOwner = assertWorkControllerOwnership(ctx, session, current, args);
|
|
1506
|
+
current = reconcileFailedNonLinearTargetAdvanceRepair(ctx, current, args);
|
|
1507
|
+
if (retryStage && current.finalization[retryStage] !== 'failed') retryStage = undefined;
|
|
830
1508
|
if (terminalOutcome && args.cleanup === false) {
|
|
831
1509
|
const recordedAt = new Date().toISOString();
|
|
832
1510
|
current = withControllerLock(
|
|
@@ -844,6 +1522,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
844
1522
|
merge: fresh.finalization.merge === 'pending' ? 'skipped' : fresh.finalization.merge,
|
|
845
1523
|
branchCleanup: 'skipped',
|
|
846
1524
|
worktreeCleanup: 'skipped',
|
|
1525
|
+
failureCode: fresh.finalization.validation === 'failed' ? fresh.finalization.failureCode : undefined,
|
|
847
1526
|
lastError: fresh.finalization.validation === 'failed' ? fresh.finalization.lastError : undefined,
|
|
848
1527
|
},
|
|
849
1528
|
terminalResourceDisposition: {
|
|
@@ -901,6 +1580,53 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
901
1580
|
failurePreserved: true,
|
|
902
1581
|
};
|
|
903
1582
|
}
|
|
1583
|
+
if (terminalContract?.status === 'completed' && terminalContract.completionReceipt) {
|
|
1584
|
+
updateExecutionSession(ctx.controllerHome, identityFor(ctx, args), {
|
|
1585
|
+
activeWorkId: undefined,
|
|
1586
|
+
activeCheckoutId: workReturnCheckoutId(ctx, current, session.activeCheckoutId),
|
|
1587
|
+
});
|
|
1588
|
+
return { idempotent: true, work: compactHandle(current), stages: current.finalization, completed: true };
|
|
1589
|
+
}
|
|
1590
|
+
const cleanedCompletionRecovery = finalizationComplete(current.finalization)
|
|
1591
|
+
&& args.cleanup === true
|
|
1592
|
+
&& args.commit !== true
|
|
1593
|
+
&& args.merge !== true
|
|
1594
|
+
&& args.completion_outcome === 'completed_changed'
|
|
1595
|
+
&& terminalContract
|
|
1596
|
+
&& terminalContract.status !== 'cancelled'
|
|
1597
|
+
&& terminalContract.status !== 'failed'
|
|
1598
|
+
&& !terminalContract.completionReceipt;
|
|
1599
|
+
if (cleanedCompletionRecovery && terminalContract) {
|
|
1600
|
+
const terminalizationOwner = assertWorkControllerOwnership(ctx, session, current, args);
|
|
1601
|
+
const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
|
|
1602
|
+
const target = selectWorkFinalizationTarget(repository, current);
|
|
1603
|
+
const durableReview = assertCleanedCompletionImplementationReviewGate({ target, handle: current, contract: terminalContract });
|
|
1604
|
+
const reconciledContract = reconcileApprovedWorkImplementationReviewProjection(
|
|
1605
|
+
{ controllerHome: ctx.controllerHome, repoId: current.repositoryId },
|
|
1606
|
+
terminalContract.workId,
|
|
1607
|
+
{
|
|
1608
|
+
reviewId: durableReview.reviewId,
|
|
1609
|
+
sourceRevision: durableReview.sourceRevision,
|
|
1610
|
+
changedPathDigest: durableReview.changedPathDigest,
|
|
1611
|
+
},
|
|
1612
|
+
);
|
|
1613
|
+
completeFinalizedWorkContract({
|
|
1614
|
+
ctx, handle: current, contract: reconciledContract, args, terminalizationOwner, outcome: 'completed_changed',
|
|
1615
|
+
});
|
|
1616
|
+
releasePreparedWorkOwnership(ctx, current);
|
|
1617
|
+
updateExecutionSession(ctx.controllerHome, identityFor(ctx, args), {
|
|
1618
|
+
activeWorkId: undefined,
|
|
1619
|
+
activeCheckoutId: workReturnCheckoutId(ctx, current, session.activeCheckoutId),
|
|
1620
|
+
});
|
|
1621
|
+
return {
|
|
1622
|
+
idempotent: true,
|
|
1623
|
+
recoveredCompletionReceipt: true,
|
|
1624
|
+
work: compactHandle(current),
|
|
1625
|
+
stages: current.finalization,
|
|
1626
|
+
completed: true,
|
|
1627
|
+
cleanupCompleted: true,
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
904
1630
|
validateWorkHandle(ctx.controllerHome, current, identityFor(ctx, args), 'none', 'finalize');
|
|
905
1631
|
return { idempotent: true, work: compactHandle(current) };
|
|
906
1632
|
}
|
|
@@ -931,14 +1657,36 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
931
1657
|
return update(fresh);
|
|
932
1658
|
}, 10_000);
|
|
933
1659
|
|
|
934
|
-
const failStage = (
|
|
1660
|
+
const failStage = (
|
|
1661
|
+
stage: keyof WorkFinalizationStages,
|
|
1662
|
+
reason: string,
|
|
1663
|
+
failureCode?: WorkFinalizationFailureCode,
|
|
1664
|
+
): Record<string, unknown> => {
|
|
935
1665
|
current = transact(`fail:${String(stage)}`, (fresh) => {
|
|
936
|
-
const finalization = { ...fresh.finalization, [stage]: 'failed', lastError: reason } as WorkFinalizationStages;
|
|
937
|
-
|
|
1666
|
+
const finalization = { ...fresh.finalization, [stage]: 'failed', failureCode, lastError: reason } as WorkFinalizationStages;
|
|
1667
|
+
const retryableState = stage === 'validation'
|
|
1668
|
+
? 'validating'
|
|
1669
|
+
: retryHandleStateForFinalization(finalization);
|
|
1670
|
+
return transitionWorkHandle(ctx.controllerHome, fresh, retryableState, {
|
|
1671
|
+
failureReason: reason,
|
|
1672
|
+
finalization,
|
|
1673
|
+
...(stage === 'validation' ? { validationRun: undefined, validatedInputFingerprint: undefined } : {}),
|
|
1674
|
+
});
|
|
938
1675
|
});
|
|
939
1676
|
if (current.workContractId) {
|
|
940
1677
|
if (stage === 'validation') {
|
|
941
|
-
|
|
1678
|
+
transitionWorkContractPhase(
|
|
1679
|
+
{ controllerHome: ctx.controllerHome, repoId: current.repositoryId },
|
|
1680
|
+
current.workContractId,
|
|
1681
|
+
{
|
|
1682
|
+
phase: 'verification',
|
|
1683
|
+
status: 'blocked',
|
|
1684
|
+
state: 'blocked',
|
|
1685
|
+
dispatchState: 'blocked',
|
|
1686
|
+
summary: `Retryable Work finalization validation failed; exact verification/review authority must be re-established: ${reason}`,
|
|
1687
|
+
},
|
|
1688
|
+
);
|
|
1689
|
+
markWorkValidationPending(ctx.controllerHome, current);
|
|
942
1690
|
} else if (stage === 'commit' || stage === 'merge' || stage === 'branchCleanup' || stage === 'worktreeCleanup') {
|
|
943
1691
|
transitionWorkContractPhase(
|
|
944
1692
|
{ controllerHome: ctx.controllerHome, repoId: current.repositoryId },
|
|
@@ -947,6 +1695,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
947
1695
|
phase: retryPhaseForFinalizationStage(stage),
|
|
948
1696
|
status: 'blocked',
|
|
949
1697
|
state: 'blocked',
|
|
1698
|
+
dispatchState: 'blocked',
|
|
950
1699
|
summary: `Retryable Work finalization stage ${stage} failed: ${reason}`,
|
|
951
1700
|
},
|
|
952
1701
|
);
|
|
@@ -1006,44 +1755,92 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1006
1755
|
});
|
|
1007
1756
|
if (gitAuthorization.decision !== 'allow') return { authorization: gitAuthorization, work: compactHandle(current), stages: current.finalization };
|
|
1008
1757
|
|
|
1009
|
-
// A
|
|
1010
|
-
//
|
|
1011
|
-
//
|
|
1012
|
-
//
|
|
1013
|
-
|
|
1758
|
+
// A conflict-repaired managed candidate rewrites commit identity, so the
|
|
1759
|
+
// normal descendant-HEAD fence cannot authorize it. Re-adopt only an exact,
|
|
1760
|
+
// clean, scope-contained candidate that has reconciled the linearly advanced
|
|
1761
|
+
// target. The transition returns before delivery, so the successor must gain
|
|
1762
|
+
// current-source verification/review authority before merge can resume.
|
|
1763
|
+
if (wants.merge && current.managedWorktree && current.expectedHead) {
|
|
1014
1764
|
const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
|
|
1015
1765
|
const worktree = selectRepositoryCheckout(repository, current.checkoutId, { allowArchived: true });
|
|
1016
1766
|
const target = selectWorkFinalizationTarget(repository, current);
|
|
1017
|
-
const targetBranch =
|
|
1767
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(current, target.defaultBranch, explicitTargetBranch);
|
|
1018
1768
|
const contract = contractFor(ctx, current);
|
|
1019
|
-
const
|
|
1020
|
-
root:
|
|
1769
|
+
const adoption = inspectManagedWorkSuccessorAdoption({
|
|
1770
|
+
root: worktree.canonicalRoot,
|
|
1021
1771
|
worktreePath: current.worktreePath,
|
|
1022
1772
|
managedWorktree: current.managedWorktree,
|
|
1023
1773
|
workBranch: current.branch,
|
|
1024
1774
|
targetBranch,
|
|
1025
1775
|
expectedRevision: current.expectedHead,
|
|
1776
|
+
deliveryBaseRevision: workDeliveryBaseRevision(current),
|
|
1026
1777
|
status: repositoryGitStatus(worktree),
|
|
1027
1778
|
scope: contract ? { allowedPaths: contract.allowedPaths, forbiddenPaths: contract.forbiddenPaths } : undefined,
|
|
1028
|
-
checkIds: contract?.checks ?? [],
|
|
1029
|
-
checkRefs: contract?.checkRefs ?? [],
|
|
1030
1779
|
});
|
|
1031
|
-
if (
|
|
1780
|
+
if (adoption.adoptable && adoption.candidateHead && adoption.targetHead) {
|
|
1032
1781
|
const previousHead = current.expectedHead;
|
|
1033
|
-
current = transact('
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1782
|
+
current = transact('managed-successor-adopted', (fresh) => adoptWorkHandleSuccessorCandidate(
|
|
1783
|
+
ctx.controllerHome,
|
|
1784
|
+
fresh,
|
|
1785
|
+
{ candidateHead: adoption.candidateHead!, targetHead: adoption.targetHead! },
|
|
1786
|
+
));
|
|
1787
|
+
if (contract) {
|
|
1788
|
+
transitionWorkContractPhase(
|
|
1789
|
+
{ controllerHome: ctx.controllerHome, repoId: current.repositoryId },
|
|
1790
|
+
contract.workId,
|
|
1791
|
+
{
|
|
1792
|
+
phase: 'verification',
|
|
1793
|
+
status: 'running',
|
|
1794
|
+
state: 'active',
|
|
1795
|
+
summary: `Conflict-repaired successor ${adoption.candidateHead} adopted after canonical ${targetBranch} advanced to ${adoption.targetHead}; current-source verification is required before delivery resumes.`,
|
|
1796
|
+
},
|
|
1797
|
+
);
|
|
1798
|
+
}
|
|
1039
1799
|
appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
|
|
1040
|
-
title: '
|
|
1041
|
-
summary: `
|
|
1800
|
+
title: 'managed Work successor candidate adopted',
|
|
1801
|
+
summary: `WorkHandle authority moved ${previousHead} -> ${adoption.candidateHead} only after proving clean exact checkout ownership, canonical target ancestry at ${adoption.targetHead}, linear candidate history, and ${adoption.candidateChangedPaths.length} scope-contained target-relative path(s). Validation/delivery authority was re-armed; no canonical target mutation occurred.`,
|
|
1042
1802
|
detailLevel: 'summary',
|
|
1043
1803
|
});
|
|
1804
|
+
markWorkValidationPending(ctx.controllerHome, current);
|
|
1805
|
+
return {
|
|
1806
|
+
work: compactHandle(current),
|
|
1807
|
+
stages: current.finalization,
|
|
1808
|
+
completed: false,
|
|
1809
|
+
successorAdopted: true,
|
|
1810
|
+
continuation: `WORK_SUCCESSOR_REVALIDATION_REQUIRED: successor ${adoption.candidateHead} is now the exact Work candidate after target ${adoption.targetHead}; verify/review this source revision before retrying delivery`,
|
|
1811
|
+
};
|
|
1044
1812
|
}
|
|
1045
1813
|
}
|
|
1046
1814
|
|
|
1815
|
+
// A direct Work can share canonical main with another Work's preserved dirty
|
|
1816
|
+
// delta. If main advanced after this Work was prepared, keep the global
|
|
1817
|
+
// execution-identity fence strict and reconcile only here, where delivery
|
|
1818
|
+
// ownership plus exact current verification can be proven together.
|
|
1819
|
+
if (wants.commit && !current.managedWorktree && current.expectedHead) {
|
|
1820
|
+
const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
|
|
1821
|
+
const worktree = selectRepositoryCheckout(repository, current.checkoutId, { allowArchived: true });
|
|
1822
|
+
const target = selectWorkFinalizationTarget(repository, current);
|
|
1823
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(current, repository.defaultBranch, explicitTargetBranch);
|
|
1824
|
+
const contract = contractFor(ctx, current);
|
|
1825
|
+
const reconciliation = reconcileDirectCanonicalTargetAdvanceCommand({
|
|
1826
|
+
controllerHome: ctx.controllerHome,
|
|
1827
|
+
repoId: current.repositoryId,
|
|
1828
|
+
workId: current.workContractId ?? current.workId,
|
|
1829
|
+
handle: current,
|
|
1830
|
+
root: target.canonicalRoot,
|
|
1831
|
+
targetBranch,
|
|
1832
|
+
status: repositoryGitStatus(worktree),
|
|
1833
|
+
scope: contract ? { allowedPaths: contract.allowedPaths, forbiddenPaths: contract.forbiddenPaths } : undefined,
|
|
1834
|
+
checkIds: contract?.checks ?? [],
|
|
1835
|
+
checkRefs: contract?.checkRefs ?? [],
|
|
1836
|
+
persistHandle: (patch) => transact('direct-canonical-target-advance-reconciled', (fresh) => writeWorkHandle(ctx.controllerHome, {
|
|
1837
|
+
...fresh,
|
|
1838
|
+
...patch,
|
|
1839
|
+
})),
|
|
1840
|
+
});
|
|
1841
|
+
if (reconciliation.reconciled) current = reconciliation.handle;
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1047
1844
|
if (failedCleanupProof) {
|
|
1048
1845
|
const preservedFailure = current.failureReason ?? current.finalization.lastError ?? 'Work validation failed.';
|
|
1049
1846
|
current = transact('failed-cleanup-begin', (fresh) => writeWorkHandle(ctx.controllerHome, {
|
|
@@ -1171,6 +1968,16 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1171
1968
|
&& wants.cleanup
|
|
1172
1969
|
&& !wants.commit
|
|
1173
1970
|
&& !wants.merge;
|
|
1971
|
+
const noChangeCleanupReconciliation = requestedOutcome === 'completed_no_change'
|
|
1972
|
+
? inspectCleanupOnlyMergedHead(ctx, current, args)
|
|
1973
|
+
: undefined;
|
|
1974
|
+
const noChangeRemovedWorktreeRecovery = Boolean(
|
|
1975
|
+
noChangeCleanupReconciliation?.worktreeMissing
|
|
1976
|
+
&& current.finalization.worktreeCleanup === 'done'
|
|
1977
|
+
&& current.finalization.validation === 'done'
|
|
1978
|
+
&& current.validatedInputFingerprint
|
|
1979
|
+
&& (current.state === 'merged' || current.state === 'cleaned'),
|
|
1980
|
+
);
|
|
1174
1981
|
const cleanupReconciliation = requestedOutcome === 'completed_no_change'
|
|
1175
1982
|
? undefined
|
|
1176
1983
|
: inspectCleanupOnlyMergedHead(ctx, current, args);
|
|
@@ -1194,7 +2001,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1194
2001
|
branchCleanup: deleteBranchRequested
|
|
1195
2002
|
? (fresh.finalization.branchCleanup === 'done' ? 'done' : 'pending')
|
|
1196
2003
|
: 'skipped',
|
|
1197
|
-
lastError: undefined,
|
|
2004
|
+
failureCode: undefined, lastError: undefined,
|
|
1198
2005
|
},
|
|
1199
2006
|
}));
|
|
1200
2007
|
}
|
|
@@ -1214,14 +2021,22 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1214
2021
|
if (terminalCleanupOnly) {
|
|
1215
2022
|
current = transact('terminal-cleanup-validation-skipped', (fresh) => writeWorkHandle(ctx.controllerHome, {
|
|
1216
2023
|
...fresh,
|
|
1217
|
-
finalization: { ...fresh.finalization, validation: 'done', lastError: undefined },
|
|
2024
|
+
finalization: { ...fresh.finalization, validation: 'done', failureCode: undefined, lastError: undefined },
|
|
1218
2025
|
}));
|
|
2026
|
+
} else if (noChangeRemovedWorktreeRecovery) {
|
|
2027
|
+
// A prior no-change finalize may have durably recorded validation and physical
|
|
2028
|
+
// worktree cleanup before terminal Work completion. Target containment above
|
|
2029
|
+
// re-proves the exact expected HEAD; do not recreate mutable workspace state
|
|
2030
|
+
// merely to repeat a validation whose fingerprint is already durable.
|
|
1219
2031
|
} else {
|
|
1220
2032
|
let validatedRepository: RepositoryRecord;
|
|
1221
2033
|
try {
|
|
1222
2034
|
validatedRepository = validateWorkHandle(ctx.controllerHome, current, identity, 'full', 'finalize').worktreeRepository;
|
|
1223
2035
|
} catch (error) {
|
|
1224
|
-
|
|
2036
|
+
const failureCode = error instanceof WorkHandleValidationError && error.code === 'WORK_HANDLE_HEAD_CHANGED'
|
|
2037
|
+
? error.code
|
|
2038
|
+
: undefined;
|
|
2039
|
+
return failStage('validation', error instanceof Error ? error.message : String(error), failureCode);
|
|
1225
2040
|
}
|
|
1226
2041
|
const validationContract = contractFor(ctx, current);
|
|
1227
2042
|
if (!validationContract) throw new Error(`WORK_VALIDATION_CONTRACT_MISSING: ${current.workContractId ?? current.workId}`);
|
|
@@ -1232,7 +2047,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1232
2047
|
current = transact('validation-no-checks', (fresh) => writeWorkHandle(ctx.controllerHome, {
|
|
1233
2048
|
...fresh,
|
|
1234
2049
|
validatedInputFingerprint: validationInput.fingerprint,
|
|
1235
|
-
finalization: { ...fresh.finalization, validation: 'done', lastError: undefined },
|
|
2050
|
+
finalization: { ...fresh.finalization, validation: 'done', failureCode: undefined, lastError: undefined },
|
|
1236
2051
|
}));
|
|
1237
2052
|
}
|
|
1238
2053
|
projectWorkValidationOutcome(ctx.controllerHome, current, 'passed', 'No validation checks were required.');
|
|
@@ -1245,7 +2060,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1245
2060
|
current = transact('validation-required', (fresh) => writeWorkHandle(ctx.controllerHome, {
|
|
1246
2061
|
...fresh,
|
|
1247
2062
|
validatedInputFingerprint: undefined,
|
|
1248
|
-
finalization: { ...fresh.finalization, validation: 'pending', lastError: undefined },
|
|
2063
|
+
finalization: { ...fresh.finalization, validation: 'pending', failureCode: undefined, lastError: undefined },
|
|
1249
2064
|
}));
|
|
1250
2065
|
markWorkValidationPending(ctx.controllerHome, current);
|
|
1251
2066
|
throw new Error('WORK_VALIDATION_REQUIRED: run work_validate against the exact current workspace before finalization');
|
|
@@ -1269,16 +2084,20 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1269
2084
|
}
|
|
1270
2085
|
|
|
1271
2086
|
if (noChangeFastPath) {
|
|
1272
|
-
if (!exactValidationInput?.clean) {
|
|
2087
|
+
if (!exactValidationInput?.clean && !noChangeRemovedWorktreeRecovery) {
|
|
1273
2088
|
throw new Error('WORK_NO_CHANGE_DIRTY: completed_no_change cannot retain an owned dirty worktree');
|
|
1274
2089
|
}
|
|
2090
|
+
const noChangeValidationFingerprint = exactValidationInput?.fingerprint ?? current.validatedInputFingerprint;
|
|
2091
|
+
if (!noChangeValidationFingerprint) {
|
|
2092
|
+
throw new Error('WORK_NO_CHANGE_VALIDATION_AUTHORITY_REQUIRED: completed_no_change requires retained exact validation authority');
|
|
2093
|
+
}
|
|
1275
2094
|
const noChangeFinalization: WorkFinalizationStages = {
|
|
1276
2095
|
...current.finalization,
|
|
1277
2096
|
validation: 'done',
|
|
1278
2097
|
commit: 'skipped',
|
|
1279
2098
|
merge: 'skipped',
|
|
1280
2099
|
branchCleanup: wants.cleanup && deleteBranchRequested ? 'pending' : 'skipped',
|
|
1281
|
-
lastError: undefined,
|
|
2100
|
+
failureCode: undefined, lastError: undefined,
|
|
1282
2101
|
};
|
|
1283
2102
|
// Collapse the no-op Git stages into the same durable lifecycle write used
|
|
1284
2103
|
// to enter/establish the delivery boundary. Physical cleanup remains
|
|
@@ -1286,13 +2105,13 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1286
2105
|
if (current.state === 'prepared') {
|
|
1287
2106
|
current = transact('no-change-validated', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'validating', {
|
|
1288
2107
|
finalization: noChangeFinalization,
|
|
1289
|
-
validatedInputFingerprint:
|
|
2108
|
+
validatedInputFingerprint: noChangeValidationFingerprint,
|
|
1290
2109
|
failureReason: undefined,
|
|
1291
2110
|
}));
|
|
1292
2111
|
} else if (current.state === 'editing' || current.state === 'validating' || current.state === 'committed') {
|
|
1293
2112
|
current = transact('no-change-delivery-integrated', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'merged', {
|
|
1294
2113
|
finalization: noChangeFinalization,
|
|
1295
|
-
validatedInputFingerprint:
|
|
2114
|
+
validatedInputFingerprint: noChangeValidationFingerprint,
|
|
1296
2115
|
failureReason: undefined,
|
|
1297
2116
|
}));
|
|
1298
2117
|
}
|
|
@@ -1307,28 +2126,53 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1307
2126
|
if (dirtyPaths.length > 0 && commitPaths.length === 0) {
|
|
1308
2127
|
throw new Error(`WORK_COMMIT_NO_OWNED_DIRTY_PATHS: ${current.workId} has ${dirtyPaths.length} dirty path(s), but none are owned by its WorkContract path scope`);
|
|
1309
2128
|
}
|
|
2129
|
+
if (!contract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
|
|
2130
|
+
const preCommitReviewCandidate = assertPhysicalImplementationReviewGate({
|
|
2131
|
+
ctx,
|
|
2132
|
+
repository: validated.worktreeRepository,
|
|
2133
|
+
handle: current,
|
|
2134
|
+
contract,
|
|
2135
|
+
verificationWorkspaceFingerprint: exactValidationInput?.workspaceFingerprint,
|
|
2136
|
+
targetBranch: resolveWorkDeliveryTargetBranch(current, validated.worktreeRepository.defaultBranch, explicitTargetBranch),
|
|
2137
|
+
});
|
|
2138
|
+
const reviewedCommitPaths = normalizeImplementationReviewChangedPaths(preCommitReviewCandidate.changedPaths);
|
|
2139
|
+
const exactCommitPaths = normalizeImplementationReviewChangedPaths(commitPaths);
|
|
2140
|
+
if (JSON.stringify(reviewedCommitPaths) !== JSON.stringify(exactCommitPaths)) {
|
|
2141
|
+
throw new Error('WORK_IMPLEMENTATION_REVIEW_TRANSFER_COMMIT_SCOPE_MISMATCH: commit must materialize the complete reviewed Work path set');
|
|
2142
|
+
}
|
|
1310
2143
|
const committed = repositoryGitCommit(ctx.controllerHome, validated.worktreeRepository, { message: String(args.message ?? `Complete ${current.workId}`), paths: commitPaths, allowEmpty: false, authorizationDecision: gitAuthorization, sessionId: session.sessionId, principalId: session.principalId, workId: current.workId, goalId: current.goalId });
|
|
1311
2144
|
const pendingAuthorization = [committed.stage, committed.commit].find((execution) => execution?.authorizationDecision?.decision === 'user_confirmation_required')?.authorizationDecision;
|
|
1312
2145
|
if (pendingAuthorization) return { authorization: pendingAuthorization, work: compactHandle(current), stages: current.finalization };
|
|
1313
2146
|
if (!committed.committed) return { ...failStage('commit', committed.error?.message ?? 'commit failed'), commit: committed };
|
|
1314
2147
|
const checks = contract?.checks ?? [];
|
|
1315
|
-
const
|
|
1316
|
-
|
|
1317
|
-
|
|
2148
|
+
const committedHead = gitHead(validated.worktreeRepository.canonicalRoot);
|
|
2149
|
+
if (!committedHead) throw new Error('WORK_COMMIT_HEAD_REQUIRED: committed revision is unavailable');
|
|
2150
|
+
const postCommitInput = currentWorkValidationInput(validated.worktreeRepository, { ...current, expectedHead: committedHead }, checks);
|
|
2151
|
+
const postCommitContentFingerprint = implementationReviewContentFingerprint(
|
|
2152
|
+
validated.worktreeRepository.canonicalRoot,
|
|
2153
|
+
preCommitReviewCandidate.changedPaths,
|
|
2154
|
+
);
|
|
2155
|
+
const committedPaths = exactCommitChangedPaths(validated.worktreeRepository.canonicalRoot, committedHead);
|
|
2156
|
+
const reviewedCommitPathsAfterCommit = normalizeImplementationReviewChangedPaths(preCommitReviewCandidate.changedPaths);
|
|
2157
|
+
const exactCommittedPaths = normalizeImplementationReviewChangedPaths(committedPaths);
|
|
2158
|
+
const reviewedContentPreservedAcrossCommit = JSON.stringify(reviewedCommitPathsAfterCommit) === JSON.stringify(exactCommittedPaths)
|
|
2159
|
+
&& postCommitContentFingerprint === preCommitReviewCandidate.workspaceFingerprint;
|
|
2160
|
+
// Git staging/commit changes workspace-status shape even when the reviewed bytes
|
|
2161
|
+
// are identical. Preserve validation from exact reviewed content equivalence;
|
|
2162
|
+
// transferWorkVerificationAcrossContentEquivalentCommit remains responsible
|
|
2163
|
+
// for invalidating checks whose semantics are genuinely Git-sensitive.
|
|
1318
2164
|
const validationPreservedAcrossCommit = Boolean(
|
|
1319
2165
|
checks.length > 0
|
|
1320
2166
|
&& exactValidationInput
|
|
1321
|
-
&&
|
|
2167
|
+
&& reviewedContentPreservedAcrossCommit
|
|
1322
2168
|
);
|
|
1323
|
-
const committedHead = gitHead(validated.worktreeRepository.canonicalRoot);
|
|
1324
|
-
const postCommitInput = currentWorkValidationInput(validated.worktreeRepository, { ...current, expectedHead: committedHead }, checks);
|
|
1325
2169
|
current = transact('commit-done', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'committed', {
|
|
1326
2170
|
expectedHead: committedHead,
|
|
1327
2171
|
finalization: {
|
|
1328
2172
|
...fresh.finalization,
|
|
1329
2173
|
validation: checks.length ? (validationPreservedAcrossCommit ? 'done' : 'pending') : 'done',
|
|
1330
2174
|
commit: 'done',
|
|
1331
|
-
lastError: undefined,
|
|
2175
|
+
failureCode: undefined, lastError: undefined,
|
|
1332
2176
|
},
|
|
1333
2177
|
validatedInputFingerprint: checks.length
|
|
1334
2178
|
? (validationPreservedAcrossCommit ? postCommitInput.fingerprint : undefined)
|
|
@@ -1344,55 +2188,64 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1344
2188
|
continuation: 'WORK_COMMITTED_REVALIDATION_REQUIRED: committed content changed from the exact validated workspace; run work_validate on the committed HEAD before merge or completion',
|
|
1345
2189
|
};
|
|
1346
2190
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
detailLevel: 'summary',
|
|
1377
|
-
},
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
|
|
1381
|
-
title: 'validation authority preserved across content-equivalent commit',
|
|
1382
|
-
summary: `Exact validated workspace ${exactValidationInput.workspaceFingerprint.slice(0, 16)} was committed without content/status drift; non-Git-sensitive per-check authority transferred to committed HEAD ${postCommitInput.head}.`,
|
|
1383
|
-
detailLevel: 'summary',
|
|
1384
|
-
});
|
|
1385
|
-
} else if (checks.length === 0) {
|
|
1386
|
-
projectWorkValidationOutcome(ctx.controllerHome, current, 'passed', 'No validation checks were required after commit.');
|
|
2191
|
+
const authorityTransfer = transferReviewedWorkAuthorityAcrossContentEquivalentCommit({
|
|
2192
|
+
controllerHome: ctx.controllerHome,
|
|
2193
|
+
repository: validated.worktreeRepository,
|
|
2194
|
+
workId: current.workContractId ?? current.workId,
|
|
2195
|
+
preCommitCandidate: preCommitReviewCandidate,
|
|
2196
|
+
preCommitDirtyPaths: commitPaths,
|
|
2197
|
+
committedPaths,
|
|
2198
|
+
preCommitContentDigest: preCommitReviewCandidate.workspaceFingerprint,
|
|
2199
|
+
postCommitSourceRevision: postCommitInput.head,
|
|
2200
|
+
postCommitContentDigest: postCommitContentFingerprint,
|
|
2201
|
+
postCommitVerificationWorkspaceFingerprint: postCommitInput.workspaceFingerprint,
|
|
2202
|
+
postCommitChangedPaths: preCommitReviewCandidate.changedPaths,
|
|
2203
|
+
});
|
|
2204
|
+
if (authorityTransfer.invalidatedCheckIds.length > 0) {
|
|
2205
|
+
current = transact('commit-review-revalidation-required', (fresh) => writeWorkHandle(ctx.controllerHome, {
|
|
2206
|
+
...fresh,
|
|
2207
|
+
finalization: { ...fresh.finalization, validation: 'pending', failureCode: undefined, lastError: undefined },
|
|
2208
|
+
validatedInputFingerprint: undefined,
|
|
2209
|
+
}));
|
|
2210
|
+
markWorkValidationPending(ctx.controllerHome, current);
|
|
2211
|
+
return {
|
|
2212
|
+
work: compactHandle(current),
|
|
2213
|
+
stages: current.finalization,
|
|
2214
|
+
completed: false,
|
|
2215
|
+
continuation: `WORK_COMMITTED_REVALIDATION_REQUIRED: content-equivalent verification transfer invalidated [${authorityTransfer.invalidatedCheckIds.join(', ')}]`,
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
if (!authorityTransfer.transferred || !authorityTransfer.derivedReview) {
|
|
2219
|
+
throw new Error('WORK_IMPLEMENTATION_REVIEW_TRANSFER_REQUIRED');
|
|
1387
2220
|
}
|
|
2221
|
+
appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
|
|
2222
|
+
title: 'verification and implementation-review authority preserved across content-equivalent commit',
|
|
2223
|
+
summary: `The exact reviewed Work content was committed as ${postCommitInput.head}; ${authorityTransfer.reusableCheckIds.length}/${checks.length} non-Git-sensitive Process receipt(s) and review ${authorityTransfer.derivedReview.reviewId} were transferred atomically.`,
|
|
2224
|
+
detailLevel: 'summary',
|
|
2225
|
+
});
|
|
1388
2226
|
} else if (!wants.commit && current.finalization.commit === 'pending') {
|
|
1389
|
-
|
|
2227
|
+
const committedRepository = validateWorkHandle(ctx.controllerHome, current, identity, 'full', 'finalize').worktreeRepository;
|
|
2228
|
+
const committedStatus = repositoryGitStatus(committedRepository);
|
|
2229
|
+
const alreadyMaterializedCommit = Boolean(
|
|
2230
|
+
committedStatus.clean
|
|
2231
|
+
&& committedStatus.head
|
|
2232
|
+
&& current.expectedHead
|
|
2233
|
+
&& current.baseCommit
|
|
2234
|
+
&& committedStatus.head === current.expectedHead
|
|
2235
|
+
&& committedStatus.head !== current.baseCommit,
|
|
2236
|
+
);
|
|
2237
|
+
current = alreadyMaterializedCommit
|
|
2238
|
+
? transact('commit-already-materialized', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'committed', {
|
|
2239
|
+
finalization: { ...fresh.finalization, commit: 'done', failureCode: undefined, lastError: undefined },
|
|
2240
|
+
failureReason: undefined,
|
|
2241
|
+
}))
|
|
2242
|
+
: transact('commit-skipped', (fresh) => writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, commit: 'skipped' } }));
|
|
1390
2243
|
}
|
|
1391
2244
|
|
|
1392
2245
|
if (wants.merge && current.finalization.merge === 'pending' && current.finalization.commit === 'done') {
|
|
1393
2246
|
const repository = getRepository(current.repositoryId, ctx.controllerHome);
|
|
1394
2247
|
const target = selectWorkFinalizationTarget(repository, current);
|
|
1395
|
-
const targetBranch =
|
|
2248
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(current, repository.defaultBranch, explicitTargetBranch);
|
|
1396
2249
|
const directTarget = inspectDirectTargetDelivery(
|
|
1397
2250
|
target.canonicalRoot,
|
|
1398
2251
|
current.worktreePath,
|
|
@@ -1403,7 +2256,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1403
2256
|
);
|
|
1404
2257
|
if (directTarget.integrated) {
|
|
1405
2258
|
current = transact('direct-target-delivery-integrated', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'merged', {
|
|
1406
|
-
finalization: { ...fresh.finalization, merge: 'done', branchCleanup: 'skipped', lastError: undefined },
|
|
2259
|
+
finalization: { ...fresh.finalization, merge: 'done', branchCleanup: 'skipped', failureCode: undefined, lastError: undefined },
|
|
1407
2260
|
failureReason: undefined,
|
|
1408
2261
|
}));
|
|
1409
2262
|
appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
|
|
@@ -1425,9 +2278,16 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1425
2278
|
}
|
|
1426
2279
|
const contract = contractFor(ctx, current);
|
|
1427
2280
|
if (contract?.constraints.allowMerge === false) throw new Error('WORK_MERGE_NOT_ALLOWED: WorkContract disallows merge');
|
|
2281
|
+
if (!contract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
|
|
1428
2282
|
const target = selectWorkFinalizationTarget(getRepository(current.repositoryId, ctx.controllerHome), current);
|
|
1429
2283
|
const deleteAfterWorktreeCleanup = current.managedWorktree && deleteBranchRequested;
|
|
1430
|
-
const targetBranch =
|
|
2284
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(current, target.defaultBranch, explicitTargetBranch);
|
|
2285
|
+
// Re-read the exact Work candidate immediately before any merge-path mutation
|
|
2286
|
+
// (including target-advance rebase). The finalizer is an independent physical
|
|
2287
|
+
// gate; semantic finalize authority is not trusted as a cached boolean.
|
|
2288
|
+
assertPhysicalImplementationReviewGate({
|
|
2289
|
+
ctx, repository: mergeValidated.worktreeRepository, handle: current, contract, targetBranch,
|
|
2290
|
+
});
|
|
1431
2291
|
const activeMergeHead = retryStage === 'merge' ? gitRevision(target.canonicalRoot, 'MERGE_HEAD') : undefined;
|
|
1432
2292
|
if (!activeMergeHead && current.managedWorktree && current.expectedHead) {
|
|
1433
2293
|
const targetHead = gitRevision(target.canonicalRoot, targetBranch);
|
|
@@ -1442,6 +2302,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1442
2302
|
phase: 'delivery',
|
|
1443
2303
|
status: 'blocked',
|
|
1444
2304
|
state: 'blocked',
|
|
2305
|
+
dispatchState: 'blocked',
|
|
1445
2306
|
summary: `Target branch ${targetBranch} advanced to ${advance.targetHead} and conflicts with candidate ${advance.candidateHead}; canonical target was not mutated.`,
|
|
1446
2307
|
},
|
|
1447
2308
|
);
|
|
@@ -1466,7 +2327,11 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1466
2327
|
advance.candidateHead,
|
|
1467
2328
|
);
|
|
1468
2329
|
if (nonLinearCommits.length > 0) {
|
|
1469
|
-
return failStage(
|
|
2330
|
+
return failStage(
|
|
2331
|
+
'merge',
|
|
2332
|
+
`WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION: candidate already contains merge commit(s) above reconciled target ${advance.targetHead}: ${nonLinearCommits.slice(0, 8).join(', ')}`,
|
|
2333
|
+
'WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION',
|
|
2334
|
+
);
|
|
1470
2335
|
}
|
|
1471
2336
|
const scopeViolation = targetAdvanceWorkScopeViolation(contract, advance.candidateChangedPaths);
|
|
1472
2337
|
if (scopeViolation) {
|
|
@@ -1505,6 +2370,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1505
2370
|
phase: 'delivery',
|
|
1506
2371
|
status: 'blocked',
|
|
1507
2372
|
state: 'blocked',
|
|
2373
|
+
dispatchState: 'blocked',
|
|
1508
2374
|
summary: `Linear target-advance integration failed inside the isolated Work checkout; canonical ${targetBranch} was not mutated.`,
|
|
1509
2375
|
},
|
|
1510
2376
|
);
|
|
@@ -1537,7 +2403,11 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1537
2403
|
encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
1538
2404
|
});
|
|
1539
2405
|
if (mergeCommits.length > 0) {
|
|
1540
|
-
return failStage(
|
|
2406
|
+
return failStage(
|
|
2407
|
+
'merge',
|
|
2408
|
+
`WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION: integrated candidate contains merge commit(s): ${mergeCommits.slice(0, 8).join(', ')}`,
|
|
2409
|
+
'WORK_TARGET_ADVANCE_LINEAR_HISTORY_VIOLATION',
|
|
2410
|
+
);
|
|
1541
2411
|
}
|
|
1542
2412
|
if (
|
|
1543
2413
|
!advance.mergedTree
|
|
@@ -1585,7 +2455,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1585
2455
|
deliveryBaseCommit: advance.targetHead,
|
|
1586
2456
|
expectedHead: integratedHead,
|
|
1587
2457
|
failureReason: undefined,
|
|
1588
|
-
finalization: { ...fresh.finalization, validation: validationPreserved ? 'done' : 'pending', merge: 'pending', lastError: undefined },
|
|
2458
|
+
finalization: { ...fresh.finalization, validation: validationPreserved ? 'done' : 'pending', merge: 'pending', failureCode: undefined, lastError: undefined },
|
|
1589
2459
|
validationRun: undefined,
|
|
1590
2460
|
validatedInputFingerprint: validationPreserved ? integratedInput.fingerprint : undefined,
|
|
1591
2461
|
},
|
|
@@ -1610,6 +2480,31 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1610
2480
|
}
|
|
1611
2481
|
}
|
|
1612
2482
|
}
|
|
2483
|
+
const deliveryContract = contractFor(ctx, current);
|
|
2484
|
+
if (!deliveryContract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
|
|
2485
|
+
try {
|
|
2486
|
+
const deliveryRepository = selectRepositoryCheckout(
|
|
2487
|
+
getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true }),
|
|
2488
|
+
current.checkoutId,
|
|
2489
|
+
{ allowArchived: true },
|
|
2490
|
+
);
|
|
2491
|
+
assertPhysicalImplementationReviewGate({
|
|
2492
|
+
ctx, repository: deliveryRepository, handle: current, contract: deliveryContract, targetBranch,
|
|
2493
|
+
});
|
|
2494
|
+
} catch (error) {
|
|
2495
|
+
return {
|
|
2496
|
+
work: compactHandle(current),
|
|
2497
|
+
stages: current.finalization,
|
|
2498
|
+
completed: false,
|
|
2499
|
+
blocked: true,
|
|
2500
|
+
recoverable: true,
|
|
2501
|
+
error: {
|
|
2502
|
+
code: 'WORK_IMPLEMENTATION_REVIEW_REQUIRED',
|
|
2503
|
+
message: error instanceof Error ? error.message : String(error),
|
|
2504
|
+
},
|
|
2505
|
+
continuation: 'WORK_IMPLEMENTATION_REVIEW_REQUIRED: target integration changed the Work delivery candidate; verify/review the exact current candidate before mutating the canonical target.',
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
1613
2508
|
let merged: ReturnType<typeof repositoryGitFinishWorkflow>;
|
|
1614
2509
|
if (activeMergeHead) {
|
|
1615
2510
|
const expectedMergeHead = current.expectedHead ? gitRevision(target.canonicalRoot, current.expectedHead) : undefined;
|
|
@@ -1691,29 +2586,26 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1691
2586
|
])].sort();
|
|
1692
2587
|
let preserveDirtyTargetPaths: string[] | undefined;
|
|
1693
2588
|
if (lockedDirtyPaths.length > 0) {
|
|
1694
|
-
const
|
|
2589
|
+
const preservation = planTargetDirtyPreservation({
|
|
1695
2590
|
dirtyPaths: lockedDirtyPaths,
|
|
1696
2591
|
targetCheckoutId: target.activeCheckoutId,
|
|
1697
2592
|
currentWorkId: current.workId,
|
|
1698
2593
|
activeWorks: listWorkContracts({ controllerHome: ctx.controllerHome, repoId: current.repositoryId, status: 'active', limit: 200 }),
|
|
1699
2594
|
});
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
throw new Error(`WORK_TARGET_DIRTY_OWNERSHIP_REQUIRED: target ${targetBranch} has dirty path(s) that cannot be attributed uniquely to another active Work. ${detail}`.trim());
|
|
1706
|
-
}
|
|
1707
|
-
preserveDirtyTargetPaths = ownership.dirtyPaths;
|
|
2595
|
+
const ownership = preservation.ownership;
|
|
2596
|
+
preserveDirtyTargetPaths = preservation.preserveDirtyTargetPaths;
|
|
2597
|
+
const attribution = ownership.owned
|
|
2598
|
+
? 'all preserved paths are attributed uniquely to other active Work'
|
|
2599
|
+
: `preserved paths include unattributed or ambiguous content (unowned=${ownership.unownedPaths.length}, ambiguous=${ownership.ambiguousPaths.length})`;
|
|
1708
2600
|
appendWorkEvidence({ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, current.workContractId ?? current.workId, {
|
|
1709
|
-
title: 'concurrent target dirty
|
|
1710
|
-
summary: `Target ${targetBranch} retained ${
|
|
2601
|
+
title: 'concurrent target dirty content preserved',
|
|
2602
|
+
summary: `Target ${targetBranch} retained ${preserveDirtyTargetPaths.length} dirty path(s) in place; ${attribution}. Git integration remains fail-closed before target mutation if the candidate overlaps any preserved path.`,
|
|
1711
2603
|
detailLevel: 'summary',
|
|
1712
2604
|
});
|
|
1713
2605
|
}
|
|
1714
2606
|
return repositoryGitFinishWorkflow(ctx.controllerHome, target, {
|
|
1715
2607
|
featureBranch: current.branch,
|
|
1716
|
-
targetBranch
|
|
2608
|
+
targetBranch,
|
|
1717
2609
|
deleteBranch: !deleteAfterWorktreeCleanup && deleteBranchRequested,
|
|
1718
2610
|
noFf: args.no_ff === true,
|
|
1719
2611
|
preserveDirtyTargetPaths,
|
|
@@ -1735,7 +2627,7 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1735
2627
|
if (pendingAuthorization) return { authorization: pendingAuthorization, work: compactHandle(current), stages: current.finalization };
|
|
1736
2628
|
if (!merged.completed) return { ...failStage('merge', merged.error?.message ?? 'merge failed'), merge: merged };
|
|
1737
2629
|
current = transact('merge-done', (fresh) => transitionWorkHandle(ctx.controllerHome, fresh, 'merged', {
|
|
1738
|
-
finalization: { ...fresh.finalization, merge: 'done', branchCleanup: !deleteBranchRequested ? 'skipped' : deleteAfterWorktreeCleanup ? 'pending' : 'done', lastError: undefined },
|
|
2630
|
+
finalization: { ...fresh.finalization, merge: 'done', branchCleanup: !deleteBranchRequested ? 'skipped' : deleteAfterWorktreeCleanup ? 'pending' : 'done', failureCode: undefined, lastError: undefined },
|
|
1739
2631
|
failureReason: undefined,
|
|
1740
2632
|
}));
|
|
1741
2633
|
} else if (!wants.merge && current.finalization.merge === 'pending') {
|
|
@@ -1755,10 +2647,15 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1755
2647
|
if (remoteDeliveryRequired && requestedOutcome !== 'completed_no_change') {
|
|
1756
2648
|
const repository = getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true });
|
|
1757
2649
|
const target = selectWorkFinalizationTarget(repository, current);
|
|
1758
|
-
const targetBranch =
|
|
2650
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(current, repository.defaultBranch, explicitTargetBranch);
|
|
1759
2651
|
const targetRevision = gitRevision(target.canonicalRoot, targetBranch);
|
|
1760
2652
|
if (!targetRevision) throw new Error(`WORK_REMOTE_DELIVERY_TARGET_REQUIRED: target branch ${targetBranch} is unavailable`);
|
|
1761
2653
|
const authorityRepository = selectRepositoryCheckout(repository, current.checkoutId, { allowArchived: true });
|
|
2654
|
+
const remoteDeliveryContract = contractFor(ctx, current);
|
|
2655
|
+
if (!remoteDeliveryContract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
|
|
2656
|
+
assertPhysicalImplementationReviewGate({
|
|
2657
|
+
ctx, repository: authorityRepository, handle: current, contract: remoteDeliveryContract, targetBranch,
|
|
2658
|
+
});
|
|
1762
2659
|
remoteDelivery = await pushExactWorkRemoteDelivery({
|
|
1763
2660
|
controllerHome: ctx.controllerHome,
|
|
1764
2661
|
repository: authorityRepository,
|
|
@@ -1806,19 +2703,50 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1806
2703
|
}
|
|
1807
2704
|
}
|
|
1808
2705
|
|
|
2706
|
+
let prevalidatedNoChangeReceipt: CompletionReceipt | undefined;
|
|
2707
|
+
if (requestedOutcome === 'completed_no_change'
|
|
2708
|
+
&& current.finalization.validation === 'done'
|
|
2709
|
+
&& current.finalization.commit === 'skipped'
|
|
2710
|
+
&& current.finalization.merge === 'skipped'
|
|
2711
|
+
&& (current.state === 'merged' || current.state === 'cleaned')) {
|
|
2712
|
+
const completionContract = contractFor(ctx, current);
|
|
2713
|
+
if (!completionContract) throw new Error(`WORK_COMPLETION_CONTRACT_MISSING: ${current.workContractId ?? current.workId}`);
|
|
2714
|
+
// Delivery/target identity is semantic completion evidence. Prove it before
|
|
2715
|
+
// deleting any managed worktree or branch so a wrong target remains safely
|
|
2716
|
+
// retryable with the exact workspace intact.
|
|
2717
|
+
prevalidatedNoChangeReceipt = completionReceiptForFinalizedWork(ctx, current, completionContract, args);
|
|
2718
|
+
}
|
|
2719
|
+
|
|
1809
2720
|
if (wants.cleanup && current.finalization.worktreeCleanup === 'pending') {
|
|
1810
2721
|
const contract = contractFor(ctx, current);
|
|
1811
2722
|
if (contract?.constraints.allowCleanup === false) throw new Error('WORK_CLEANUP_NOT_ALLOWED: WorkContract disallows cleanup');
|
|
1812
2723
|
if (!current.managedWorktree) {
|
|
1813
2724
|
current = transact('worktree-cleanup-skipped', (fresh) => writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, worktreeCleanup: 'skipped' } }));
|
|
1814
2725
|
} else {
|
|
2726
|
+
const cleanupContract = contractFor(ctx, current);
|
|
2727
|
+
if (!cleanupContract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
|
|
2728
|
+
if (cleanupContract.status !== 'cancelled' && cleanupContract.status !== 'failed') {
|
|
2729
|
+
const cleanupRepository = selectRepositoryCheckout(
|
|
2730
|
+
getRepository(current.repositoryId, ctx.controllerHome, { includeRemoved: true }),
|
|
2731
|
+
current.checkoutId,
|
|
2732
|
+
{ allowArchived: true },
|
|
2733
|
+
);
|
|
2734
|
+
const cleanupTargetBranch = resolveWorkDeliveryTargetBranch(
|
|
2735
|
+
current,
|
|
2736
|
+
cleanupRepository.defaultBranch,
|
|
2737
|
+
explicitTargetBranch,
|
|
2738
|
+
);
|
|
2739
|
+
assertPhysicalImplementationReviewGate({
|
|
2740
|
+
ctx, repository: cleanupRepository, handle: current, contract: cleanupContract, targetBranch: cleanupTargetBranch,
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
1815
2743
|
const target = selectWorkFinalizationTarget(getRepository(current.repositoryId, ctx.controllerHome), current);
|
|
1816
2744
|
const cleanup = runCleanup(target.canonicalRoot, current.worktreePath);
|
|
1817
2745
|
if (!cleanup.ok) return failStage('worktreeCleanup', cleanup.message ?? 'worktree cleanup failed');
|
|
1818
2746
|
current = transact('worktree-cleanup-done', (fresh) => {
|
|
1819
2747
|
setRepositoryCheckoutLifecycle({ controllerHome: ctx.controllerHome, repoId: fresh.repositoryId, checkoutId: fresh.checkoutId, lifecycle: 'removed', reason: `Work ${fresh.workId} cleanup completed.` });
|
|
1820
2748
|
markRepositoryProjectionDirty(ctx.controllerHome, fresh.repositoryId, `cleanup:${fresh.workId}:worktree`);
|
|
1821
|
-
return writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, worktreeCleanup: 'done', lastError: undefined } });
|
|
2749
|
+
return writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, worktreeCleanup: 'done', failureCode: undefined, lastError: undefined } });
|
|
1822
2750
|
});
|
|
1823
2751
|
}
|
|
1824
2752
|
} else if (!wants.cleanup && current.finalization.worktreeCleanup === 'pending') {
|
|
@@ -1831,12 +2759,23 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1831
2759
|
&& (current.finalization.merge === 'done' || requestedOutcome === 'completed_no_change')
|
|
1832
2760
|
) {
|
|
1833
2761
|
const target = selectWorkFinalizationTarget(getRepository(current.repositoryId, ctx.controllerHome), current);
|
|
2762
|
+
const branchCleanupContract = contractFor(ctx, current);
|
|
2763
|
+
if (!branchCleanupContract) throw new Error(`WORK_IMPLEMENTATION_REVIEW_CONTRACT_REQUIRED: ${current.workId}`);
|
|
2764
|
+
if (branchCleanupContract.status !== 'cancelled' && branchCleanupContract.status !== 'failed') {
|
|
2765
|
+
const targetBranch = resolveWorkDeliveryTargetBranch(current, target.defaultBranch, explicitTargetBranch);
|
|
2766
|
+
assertPhysicalBranchCleanupImplementationReviewGate({
|
|
2767
|
+
target,
|
|
2768
|
+
handle: current,
|
|
2769
|
+
contract: branchCleanupContract,
|
|
2770
|
+
targetBranch,
|
|
2771
|
+
});
|
|
2772
|
+
}
|
|
1834
2773
|
const deleted = repositoryGitDeleteBranch(ctx.controllerHome, target, { branch: current.branch, force: false, authorizationDecision: gitAuthorization, sessionId: session.sessionId, principalId: session.principalId, workId: current.workId, goalId: current.goalId });
|
|
1835
2774
|
if (deleted.execution.authorizationDecision?.decision === 'user_confirmation_required') return { authorization: deleted.execution.authorizationDecision, work: compactHandle(current), stages: current.finalization };
|
|
1836
2775
|
if (deleted.execution.status !== 'executed' || deleted.execution.ok !== true) return failStage('branchCleanup', deleted.execution.stderr || 'feature branch cleanup failed');
|
|
1837
2776
|
current = transact('branch-cleanup-done', (fresh) => {
|
|
1838
2777
|
markRepositoryProjectionDirty(ctx.controllerHome, fresh.repositoryId, `cleanup:${fresh.workId}:branch`);
|
|
1839
|
-
return writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, branchCleanup: 'done', lastError: undefined } });
|
|
2778
|
+
return writeWorkHandle(ctx.controllerHome, { ...fresh, finalization: { ...fresh.finalization, branchCleanup: 'done', failureCode: undefined, lastError: undefined } });
|
|
1840
2779
|
});
|
|
1841
2780
|
}
|
|
1842
2781
|
|
|
@@ -1866,36 +2805,34 @@ export async function finalizeWork(ctx: MultiRepositoryMcpToolContext, args: Rec
|
|
|
1866
2805
|
detailLevel: 'summary',
|
|
1867
2806
|
});
|
|
1868
2807
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
{ controllerHome: ctx.controllerHome, repoId: current.repositoryId },
|
|
1878
|
-
{
|
|
1879
|
-
workId,
|
|
1880
|
-
actor: `work-finalize-completion:${terminalizationOwner.controllerId}:${ownerInstanceId}`,
|
|
1881
|
-
authority: {
|
|
1882
|
-
controllerId: terminalizationOwner.controllerId,
|
|
1883
|
-
controllerType: terminalizationOwner.controllerType,
|
|
1884
|
-
principalId: ownerPrincipal,
|
|
1885
|
-
controllerInstanceId: ownerInstanceId,
|
|
1886
|
-
claimGeneration: ownerClaimGeneration,
|
|
1887
|
-
},
|
|
1888
|
-
},
|
|
1889
|
-
() => completeWorkWithReceipt(
|
|
2808
|
+
if (prevalidatedNoChangeReceipt) {
|
|
2809
|
+
const ownerPrincipal = terminalizationOwner.principalId?.trim() || terminalizationOwner.controllerId;
|
|
2810
|
+
const ownerInstanceId = terminalizationOwner.controllerInstanceId?.trim() || '';
|
|
2811
|
+
const ownerClaimGeneration = terminalizationOwner.claimGeneration;
|
|
2812
|
+
if (!ownerInstanceId || typeof ownerClaimGeneration !== 'number' || ownerClaimGeneration < 1) {
|
|
2813
|
+
throw new Error(`WORK_CONTROLLER_TERMINALIZATION_AUTHORITY_INVALID: ${workId}`);
|
|
2814
|
+
}
|
|
2815
|
+
const fencedCompletion = withControllerSessionTerminalizationFence(
|
|
1890
2816
|
{ controllerHome: ctx.controllerHome, repoId: current.repositoryId },
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
2817
|
+
{
|
|
2818
|
+
workId,
|
|
2819
|
+
actor: `work-finalize-completion:${terminalizationOwner.controllerId}:${ownerInstanceId}`,
|
|
2820
|
+
authority: {
|
|
2821
|
+
controllerId: terminalizationOwner.controllerId, controllerType: terminalizationOwner.controllerType,
|
|
2822
|
+
principalId: ownerPrincipal, controllerInstanceId: ownerInstanceId, claimGeneration: ownerClaimGeneration,
|
|
2823
|
+
},
|
|
2824
|
+
},
|
|
2825
|
+
() => completeWorkWithReceipt(
|
|
2826
|
+
{ controllerHome: ctx.controllerHome, repoId: current.repositoryId }, workId, prevalidatedNoChangeReceipt,
|
|
2827
|
+
'completed_no_change', 'completed_no_change',
|
|
2828
|
+
),
|
|
2829
|
+
);
|
|
2830
|
+
if (!fencedCompletion.allowed) throw new Error(`WORK_TERMINALIZATION_AUTHORITY_FENCED: ${workId}:${fencedCompletion.reason}`);
|
|
2831
|
+
} else {
|
|
2832
|
+
completeFinalizedWorkContract({
|
|
2833
|
+
ctx, handle: current, contract: completionContract, args, terminalizationOwner,
|
|
2834
|
+
outcome: requestedOutcome === 'completed_no_change' ? 'completed_no_change' : 'completed_changed',
|
|
2835
|
+
});
|
|
1899
2836
|
}
|
|
1900
2837
|
}
|
|
1901
2838
|
// Successful WorkContract completion always ends controller ownership.
|