@moretea-labs/forge 1.7.2 → 1.8.1
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 +121 -62
- package/CHANGELOG.md +40 -0
- package/CLAUDE.md +57 -62
- package/CONTRIBUTING.md +3 -2
- 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 +77 -0
- package/adapters/chatgpt/browser-delivery-runtime.ts +1256 -0
- package/adapters/chatgpt/controller-binding-store.ts +134 -0
- package/adapters/chatgpt/controller-round-host.ts +68 -0
- package/adapters/chatgpt/controller-round-settlement-store.ts +99 -0
- package/adapters/chatgpt/provider-delivery.ts +340 -0
- package/adapters/chatgpt/work-conversation-binding-store.ts +218 -0
- package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
- package/adapters/computer/chatgpt-extension-target.ts +463 -0
- package/adapters/computer/chatgpt-target-router.ts +103 -0
- package/adapters/computer/chrome-extension/background.js +385 -0
- package/adapters/computer/chrome-extension/content.js +378 -0
- package/adapters/computer/chrome-extension/core.js +24 -0
- package/adapters/computer/chrome-extension/manifest.json +30 -0
- package/adapters/computer/desktop-operator-contract.ts +7 -0
- package/adapters/computer/desktop-operator-discovery.ts +181 -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 +1068 -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 +406 -0
- package/adapters/mcp/instructions.ts +37 -0
- package/adapters/mcp/legacy-context.ts +18 -0
- package/adapters/mcp/multi-repository.ts +421 -0
- package/adapters/mcp/oauth.ts +464 -0
- package/adapters/mcp/paths.ts +143 -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/automation-receipt-adapter.ts +87 -0
- package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
- package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +1167 -0
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +206 -0
- package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +130 -0
- package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
- package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +173 -0
- package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +412 -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 +380 -0
- package/adapters/mcp/runtime-gateway/process-tools.ts +457 -0
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +88 -0
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +285 -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 +79 -0
- package/adapters/mcp/runtime-gateway/result-adapter.ts +114 -0
- package/adapters/mcp/runtime-gateway/router.ts +242 -0
- package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1200 -0
- package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +574 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +78 -0
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +187 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +45 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +962 -0
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +325 -0
- package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
- package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
- package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
- package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
- package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
- package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
- package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
- package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
- package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
- package/adapters/mcp/server.ts +1092 -0
- package/adapters/mcp/setup.ts +789 -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 +5139 -0
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +145 -0
- package/adapters/mcp/tool-mapping/repository-tools.ts +1682 -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 +79 -0
- package/adapters/mcp/toolset-selection.ts +41 -0
- package/adapters/mcp/toolset.ts +337 -0
- package/adapters/mcp/transports/http-observation.ts +365 -0
- package/adapters/mcp/transports/http.ts +933 -0
- package/adapters/mcp/transports/oauth-http.ts +322 -0
- package/adapters/mcp/transports/session-registry.ts +519 -0
- package/adapters/mcp/transports/stdio.ts +8 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +223 -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 +11 -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/agentic-development-flow.md +25 -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 +28 -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 +233 -28
- package/docs/architecture/EVOLUTION.md +13 -0
- package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
- 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 +37 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +311 -0
- package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
- package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
- package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
- package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
- package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
- package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
- package/docs/architecture/domains/verification.md +8 -0
- package/docs/architecture/domains/workflow-engine.md +7 -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/controller-performance-and-502.md +17 -0
- package/docs/operations/controller-reliability-runbook.md +2 -2
- package/docs/operations/features.md +1 -1
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +2 -0
- package/docs/operations/platform-support.md +16 -8
- package/docs/operations/platform-support.zh-CN.md +2 -2
- package/docs/operations/releasing.md +2 -0
- package/docs/operations/standalone-disaster-recovery.md +38 -5
- 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 +46 -26
- package/packages/kernel/cognition/api/index.ts +6 -0
- package/packages/kernel/cognition/application/activation-service.ts +451 -0
- package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
- package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
- package/packages/kernel/cognition/application/memory-service.ts +64 -0
- package/packages/kernel/cognition/domain/learning.ts +106 -0
- package/packages/kernel/cognition/domain/memory.ts +254 -0
- package/packages/kernel/controller/api/index.ts +3 -0
- package/packages/kernel/controller/application/continuation-service.ts +281 -0
- package/packages/kernel/controller/application/controller-round-service.ts +41 -0
- package/packages/kernel/controller/application/controller-service.ts +30 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +529 -0
- package/packages/kernel/controller/domain/controller-round.ts +110 -0
- package/packages/kernel/controller/domain/execution-quality.ts +135 -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 +1970 -0
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +804 -0
- package/packages/kernel/controller/ports/controller-host.ts +18 -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 +15 -0
- package/packages/kernel/identity/application/identity-service.ts +67 -0
- package/packages/kernel/identity/domain/activity-projection.ts +43 -0
- package/packages/kernel/identity/domain/capability-broker.ts +60 -0
- package/packages/kernel/identity/domain/grant.ts +72 -0
- package/packages/kernel/identity/domain/owned-resource.ts +116 -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/user-request.ts +93 -0
- package/packages/kernel/identity/domain/workspace.ts +56 -0
- package/packages/kernel/identity/index.ts +1 -0
- package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
- package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
- package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
- package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
- package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
- package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -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/scheduler/api/index.ts +4 -0
- package/packages/kernel/scheduler/application/eligibility.ts +252 -0
- package/packages/kernel/scheduler/application/schedule-service.ts +27 -0
- package/packages/kernel/scheduler/application/settlement.ts +182 -0
- package/packages/kernel/scheduler/domain/schedule.ts +236 -0
- package/packages/kernel/scheduler/index.ts +1 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +805 -0
- package/packages/kernel/work/api/index.ts +16 -0
- package/packages/kernel/work/application/work-service.ts +123 -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/current-task-lineage.ts +104 -0
- package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
- package/packages/kernel/work/domain/engineering-design.ts +286 -0
- package/packages/kernel/work/domain/engineering-profile.ts +238 -0
- package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +247 -0
- package/packages/kernel/work/domain/execution-snapshot.ts +2 -0
- package/packages/kernel/work/domain/implementation-review.ts +193 -0
- package/packages/kernel/work/domain/objective-graph.ts +119 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +224 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
- package/{src/runtime/control-plane/facade/work-state-machine.ts → packages/kernel/work/domain/state-machine.ts} +37 -66
- package/packages/kernel/work/domain/types.ts +534 -0
- package/packages/kernel/work/index.ts +1 -0
- package/packages/kernel/work/infrastructure/work-contract-store.ts +1967 -0
- package/packages/kernel/work/ports/work-contract-store.ts +21 -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 +62 -0
- package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
- package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
- package/packages/plugin-runtime/computer/index.ts +8 -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 +215 -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 +581 -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 +225 -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/activate-source-baseline.ts +7 -9
- package/scripts/assemble-template.ts +1 -1
- 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 +183 -0
- package/scripts/canary-global-hook.sh +2 -2
- package/scripts/check-mcp-compatibility.ts +85 -7
- package/scripts/check-platform-support.mjs +28 -2
- package/scripts/check-public-docs.sh +1 -1
- package/scripts/check-repository-hygiene.mjs +92 -0
- package/scripts/check-runtime-architecture.mjs +1643 -127
- package/scripts/check-source-duplication.mjs +9 -1
- package/scripts/controller-ngrok-rotation.sh +5 -4
- package/scripts/create-project-dirs.sh +10 -74
- package/scripts/forge-local-recovery-helper.mjs +282 -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 +42 -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 +11 -1
- package/scripts/portability-bindings.mjs +70 -0
- package/scripts/run-bounded-command.mjs +60 -0
- package/scripts/run-bun-test-file.ts +22 -6
- package/scripts/run-governed-gate.ts +4 -2
- package/scripts/run-mobile-monitor-bridge.ts +0 -1
- package/scripts/smoke-control-plane-failure-domain.ts +94 -0
- package/scripts/smoke-mcp-http-runtime.ts +6 -1
- package/scripts/smoke-runtime-recovery.ts +13 -11
- package/scripts/smoke-schedule-engine.ts +24 -16
- package/scripts/smoke-unattended-transport-recovery.ts +223 -0
- package/scripts/smoke-user-request-continuation-restart.ts +232 -0
- package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
- package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
- package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
- package/scripts/smoke-workflow-supervisor.ts +103 -0
- package/scripts/test-governance.ts +51 -2
- package/scripts/verify-forge-runtime.sh +2 -1
- package/src/cli/agent-jobs/executor-health.ts +11 -34
- package/src/cli/agent-jobs/integration.ts +18 -49
- package/src/cli/agent-jobs/job-manager.ts +17 -4
- 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 +52 -0
- package/src/cli/commands/brain.ts +191 -40
- package/src/cli/commands/chatgpt.ts +32 -67
- 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 +507 -80
- package/src/cli/commands/runtime.ts +12 -7
- package/src/cli/commands/setup-profile.ts +97 -39
- package/src/cli/commands/status.ts +2 -1
- package/src/cli/controller/check-runner.ts +500 -100
- package/src/cli/controller/completion-backlog.ts +3 -3
- package/src/cli/controller/completion-orchestrator.ts +4 -43
- package/src/cli/controller/completion-target.ts +3 -72
- package/src/cli/controller/context/source-materializer.ts +81 -100
- package/src/cli/controller/context/types.ts +7 -1
- package/src/cli/controller/context-pack.ts +28 -6
- package/src/cli/controller/execution-completion.ts +4 -7
- package/src/cli/controller/execution-policy.ts +36 -153
- package/src/cli/controller/governance.ts +2 -3
- package/src/cli/controller/issue-store.ts +4 -99
- package/src/cli/controller/launch-agents.ts +59 -18
- package/src/cli/controller/operational-plan.ts +24 -51
- package/src/cli/controller/runtime-config.ts +5 -1
- package/src/cli/controller/stuck-state-migration.ts +2 -3
- package/src/cli/controller/systemd-user.ts +148 -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 +290 -20
- 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 +20 -15
- package/src/cli/local-bridge/console-view-models.ts +3 -12
- package/src/cli/local-bridge/facade-api.ts +105 -205
- package/src/cli/local-bridge/job-store.ts +1 -1
- package/src/cli/local-bridge/server.ts +48 -174
- package/src/cli/local-bridge/types.ts +0 -1
- 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-client-retirement.ts +74 -0
- 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 +91 -39
- package/src/cli/repositories/command-scope.ts +139 -4
- package/src/cli/repositories/controller-home.ts +167 -1
- package/src/cli/repositories/ephemeral-workspace.ts +20 -30
- package/src/cli/repositories/git-command-executor.ts +2 -2
- package/src/cli/repositories/identity.ts +13 -0
- package/src/cli/repositories/registry.ts +145 -60
- 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 +167 -27
- package/src/cli/repository/session-cache.ts +92 -6
- 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 +167 -0
- package/src/runtime/context/assistant-work-context.ts +209 -0
- package/src/runtime/context/automatic-learning.ts +565 -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/cognitive-attention.ts +173 -0
- package/src/runtime/context/cognitive-skill-canary.ts +158 -0
- package/src/runtime/context/context-closure.ts +305 -0
- package/src/runtime/context/context-pack-process.ts +103 -0
- package/src/runtime/context/context-plane.ts +203 -0
- package/src/runtime/context/direct-controller-learning.ts +298 -0
- package/src/runtime/context/project-engineering-contract.ts +60 -0
- package/src/runtime/context/semantic-navigation-contract.ts +9 -0
- package/src/runtime/context/semantic-navigation.ts +6 -34
- package/src/runtime/context/typescript-navigation-process.ts +145 -0
- package/src/runtime/context/typescript-navigation.ts +89 -3
- 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 +531 -0
- package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +282 -0
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +380 -0
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +113 -16
- package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
- package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
- package/src/runtime/control-plane/execution/retained-work-resume.ts +439 -102
- package/src/runtime/control-plane/execution/session-store.ts +31 -1
- package/src/runtime/control-plane/execution/validation.ts +34 -11
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +87 -0
- package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +31 -44
- package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +46 -53
- package/src/runtime/control-plane/execution/work-execution-support.ts +85 -164
- package/src/runtime/control-plane/execution/work-handle-authority.ts +349 -73
- package/src/runtime/control-plane/execution/work-handle-store.ts +55 -2
- package/src/runtime/control-plane/execution/work-head-settlement.ts +108 -0
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +173 -28
- package/src/runtime/control-plane/execution/work-operation-service.ts +92 -40
- package/src/runtime/control-plane/execution/work-preparation-service.ts +145 -269
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
- package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +543 -225
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +59 -45
- package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
- package/src/runtime/control-plane/execution/work-verification-service.ts +662 -52
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +205 -16
- package/src/runtime/control-plane/facade/capability-registry.ts +380 -44
- package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
- package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
- package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -22
- package/src/runtime/control-plane/facade/goal-workloop.ts +565 -1202
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +145 -0
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +266 -52
- package/src/runtime/control-plane/facade/index.ts +4 -3
- package/src/runtime/control-plane/facade/operation-digest.ts +34 -73
- package/src/runtime/control-plane/facade/plan-contract-store.ts +671 -245
- package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
- package/src/runtime/control-plane/facade/repository-work-admission.ts +31 -23
- package/src/runtime/control-plane/facade/requirement-authority.ts +121 -0
- package/src/runtime/control-plane/facade/requirement-board.ts +9 -4
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +85 -0
- package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
- package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
- package/src/runtime/control-plane/facade/suggested-actions.ts +15 -15
- package/src/runtime/control-plane/facade/types.ts +218 -620
- package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
- package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
- package/src/runtime/control-plane/generated-cache-retention.ts +336 -19
- package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +13 -2
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +274 -84
- 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 +3 -2
- package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +227 -109
- package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
- package/src/runtime/control-plane/governance/access-policy.ts +19 -2
- package/src/runtime/control-plane/governance/authorization.ts +20 -3
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +160 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +139 -670
- package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +25 -6
- package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -194
- package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
- package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +302 -0
- package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +230 -80
- package/src/runtime/control-plane/persistence/sqlite-store.ts +654 -78
- 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 +154 -26
- package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +844 -38
- package/src/runtime/control-plane/runtime-generation.ts +6 -0
- package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +479 -0
- package/src/runtime/control-plane/workspace/workspace-store.ts +249 -0
- package/src/runtime/diagnostics/incident-repair.ts +573 -0
- package/src/runtime/diagnostics/mcp-timing.ts +239 -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 +216 -35
- package/src/runtime/evidence/evidence-store.ts +112 -8
- package/src/runtime/evidence/operational-shadow.ts +38 -0
- package/src/runtime/evidence/output-handle-index.ts +225 -0
- package/src/runtime/evidence/process-check-receipt.ts +5 -43
- package/src/runtime/evidence/result-store.ts +158 -44
- 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 +124 -6
- package/src/runtime/execution/managed-workspace.ts +112 -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 +24 -10
- 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 +177 -2
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +58 -9
- package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +117 -114
- package/src/runtime/execution/process-runtime/gc.ts +15 -2
- package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
- package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
- package/src/runtime/execution/process-runtime/index.ts +4 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +283 -73
- package/src/runtime/execution/process-runtime/persisted-check.ts +130 -23
- package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
- package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
- package/src/runtime/execution/process-runtime/resource-claims.ts +125 -64
- package/src/runtime/execution/process-runtime/runtime.ts +167 -17
- package/src/runtime/execution/process-runtime/store.ts +30 -2
- package/src/runtime/execution/process-runtime/types.ts +27 -0
- package/src/runtime/execution/workers/executor.ts +18 -19
- 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-inventory.ts +130 -0
- package/src/runtime/platform/service-manager.ts +146 -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 +192 -0
- package/src/runtime/plugins/app-store-connect-adapter.ts +47 -35
- package/src/runtime/plugins/browser-adapter.ts +898 -135
- package/src/runtime/plugins/browser-automation-service.ts +33 -34
- package/src/runtime/plugins/browser-macos-bridge.ts +386 -46
- package/src/runtime/plugins/browser-manifest-surface.ts +75 -17
- package/src/runtime/plugins/browser-native-session-service.ts +64 -14
- package/src/runtime/plugins/browser-provider-registry.ts +2 -109
- package/src/runtime/plugins/browser-registration.ts +32 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
- package/src/runtime/plugins/browser-runtime.ts +55 -10
- 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 +243 -80
- package/src/runtime/plugins/browser-session-types.ts +2 -98
- package/src/runtime/plugins/capability-authorization-grants.ts +272 -59
- package/src/runtime/plugins/catalog-trust.ts +82 -0
- package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
- package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
- package/src/runtime/plugins/computer-registration.ts +1050 -0
- package/src/runtime/plugins/config-store.ts +119 -0
- package/src/runtime/plugins/desktop-operator-external-policy.ts +964 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +123 -12
- package/src/runtime/plugins/errors.ts +8 -1
- package/src/runtime/plugins/external-adapter.ts +82 -323
- package/src/runtime/plugins/external-provider-policy.ts +48 -0
- package/src/runtime/plugins/external-registration.ts +30 -2
- 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 +29 -8
- package/src/runtime/plugins/google-calendar-adapter.ts +8 -8
- package/src/runtime/plugins/google-shared.ts +45 -53
- package/src/runtime/plugins/google-tasks-adapter.ts +8 -8
- package/src/runtime/plugins/ios-adapter.ts +2 -3
- package/src/runtime/plugins/lightweight-action.ts +208 -2
- package/src/runtime/plugins/local-system-adapter.ts +192 -2
- package/src/runtime/plugins/macos-capability-broker.ts +15 -105
- package/src/runtime/plugins/managed-process-adapter.ts +12 -5
- package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
- package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
- package/src/runtime/plugins/resend-adapter.ts +15 -24
- package/src/runtime/plugins/store.ts +1003 -387
- package/src/runtime/plugins/types.ts +28 -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 +102 -430
- package/src/runtime/projections/controller-context.ts +12 -6
- package/src/runtime/projections/git-status-sampler.ts +73 -22
- package/src/runtime/projections/invalidation.ts +7 -3
- package/src/runtime/projections/materialized-view.ts +93 -43
- package/src/runtime/recovery/actions.ts +3 -3
- package/src/runtime/recovery/capability-action-application.ts +229 -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 +701 -168
- package/src/runtime/recovery/quarantine-retention.ts +256 -0
- package/src/runtime/recovery/store.ts +29 -11
- package/src/runtime/release/identity.ts +6 -0
- package/src/runtime/release/release-coordinator.ts +296 -0
- package/src/runtime/release/release-session.ts +716 -0
- package/src/runtime/release/release-state-migration.ts +25 -0
- package/src/runtime/resources/leases/store.ts +56 -25
- 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 +376 -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-progression-composition.ts +192 -0
- package/src/runtime/root/controller-round-composition.ts +126 -0
- package/src/runtime/root/controller-services.ts +2 -0
- package/src/runtime/root/deployment-topology.ts +106 -0
- package/src/runtime/root/entry.ts +8 -6
- package/src/runtime/root/gateway-adapter.ts +3 -3
- package/src/runtime/root/known-good-recovery.ts +184 -0
- package/src/runtime/root/local-bridge.ts +5 -3
- package/src/runtime/root/mcp-transport.ts +116 -23
- package/src/runtime/root/ownership.ts +261 -9
- package/src/runtime/root/package-connector-service.ts +212 -74
- package/src/runtime/root/package-runtime-release.ts +199 -33
- package/src/runtime/root/package-runtime-service.ts +77 -108
- package/src/runtime/root/release-execution-canary.ts +103 -0
- package/src/runtime/root/release-loader.ts +150 -0
- package/src/runtime/root/release-manifest.ts +185 -7
- package/src/runtime/root/release-materialize.ts +474 -31
- package/src/runtime/root/release-store.ts +425 -37
- package/src/runtime/root/runtime-lane.ts +191 -0
- package/src/runtime/root/runtime.ts +379 -32
- package/src/runtime/root/scheduled-controller-composition.ts +76 -0
- package/src/runtime/root/scheduler.ts +12 -4
- package/src/runtime/root/service-runner.ts +3 -3
- package/src/runtime/root/service.ts +73 -45
- package/src/runtime/root/status.ts +49 -0
- package/src/runtime/root/types.ts +55 -3
- package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
- package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
- package/src/runtime/root/write-fence.ts +50 -2
- package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
- package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
- package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
- package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
- package/src/runtime/shared/local-bridge-surface.ts +1 -1
- package/src/runtime/shared/process-environment.ts +36 -10
- package/src/runtime/shared/response-budget.ts +1 -3
- package/src/runtime/shared/storage-capacity.ts +68 -0
- package/src/runtime/standalone-recovery/controller-home-migration.ts +1108 -0
- package/src/runtime/standalone-recovery/core.ts +4609 -495
- package/src/runtime/standalone-recovery/entry.ts +642 -81
- package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
- package/src/runtime/standalone-recovery/installer.ts +399 -177
- package/src/runtime/standalone-recovery/mcp-server.ts +79 -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 +173 -0
- package/src/runtime/standalone-recovery/release.ts +58 -5
- package/src/runtime/standalone-recovery/service-labels.ts +5 -2
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
- package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
- package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +11 -4
- package/src/runtime/workflow/schedules/engine.ts +298 -484
- 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 +220 -16
- package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
- package/src/runtime/workflows/runtime.ts +482 -0
- package/src/testing/test-governance.ts +95 -30
- package/supervisor/chatgpt-conversation.ts +23 -0
- package/supervisor/client.ts +201 -0
- package/supervisor/control-plane.ts +868 -0
- package/supervisor/entry.ts +15 -0
- package/supervisor/forge-validators.ts +80 -0
- package/supervisor/index.ts +5 -0
- package/supervisor/native-browser-adapter.ts +786 -0
- package/supervisor/native-messaging/host.ts +39 -0
- package/supervisor/native-messaging/manifest.ts +26 -0
- package/supervisor/native-messaging/protocol.ts +27 -0
- package/supervisor/paths.ts +18 -0
- package/supervisor/protocol.ts +202 -0
- package/supervisor/server.ts +404 -0
- package/supervisor/store.ts +1451 -0
- package/supervisor/tsconfig.json +8 -0
- package/supervisor/types.ts +255 -0
- 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/benchmark-background-check-overlap.ts +0 -228
- package/scripts/benchmark-controller-hotpath.ts +0 -212
- package/scripts/benchmark-controller-round2.ts +0 -139
- package/scripts/benchmark-ios-agent-device.ts +0 -514
- package/scripts/benchmark-mcp-host.sh +0 -117
- package/scripts/benchmark-mcp-transport.ts +0 -170
- package/scripts/benchmark-quality-harness.ts +0 -146
- package/scripts/benchmark-route-session-concurrency.ts +0 -915
- package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
- package/scripts/benchmark-thin-harness.ts +0 -251
- package/scripts/benchmark-typescript-navigation.ts +0 -186
- package/scripts/benchmark-work-efficiency.ts +0 -334
- package/scripts/build-controller-ui.ts +0 -28
- package/scripts/check-npm-release.sh +0 -32
- package/scripts/check-public-release-surface.sh +0 -19
- package/scripts/check-release-published.sh +0 -57
- package/scripts/check-release-readiness.sh +0 -37
- package/scripts/check-release-version.mjs +0 -74
- package/scripts/check-tarball-install-smoke.sh +0 -67
- package/scripts/package-source-archive.sh +0 -147
- package/scripts/public-release-files.txt +0 -39
- package/scripts/public-release.ts +0 -174
- package/scripts/publish-release-tarball.sh +0 -27
- package/scripts/route-nl-vs-ts-eval.ts +0 -679
- package/scripts/run-skill-evals.ts +0 -1022
- package/scripts/stage-runtime-release.ts +0 -39
- package/src/cli/controller/progress.ts +0 -656
- package/src/cli/controller/work-mode.ts +0 -181
- package/src/cli/local-bridge/console-assets.ts +0 -17
- package/src/cli/local-bridge/dashboard.ts +0 -17
- package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
- package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
- package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
- package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
- package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/styles.css +0 -28
- package/src/cli/local-bridge/frontend/src/types.ts +0 -15
- package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
- package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
- package/src/cli/local-bridge/ui-dist/app.css +0 -1
- package/src/cli/local-bridge/ui-dist/app.js +0 -11228
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -385
- package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -1913
- package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
- package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -449
- package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -1118
- package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -93
- package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
- package/src/runtime/control-plane/routing/index.ts +0 -1
- package/src/runtime/control-plane/routing/route-policy.ts +0 -382
- package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
|
@@ -1,35 +1,42 @@
|
|
|
1
|
-
import { randomUUID } from 'crypto';
|
|
1
|
+
import { createHash, randomUUID } from 'crypto';
|
|
2
2
|
import {
|
|
3
3
|
createHandoffItem,
|
|
4
|
-
listHandoffItems,
|
|
5
4
|
type HandoffInboxStoreOptions,
|
|
6
5
|
} from './handoff-inbox-store';
|
|
7
|
-
import {
|
|
6
|
+
import { executionPlacement, readForgeInstanceIdentity } from '../../../../packages/kernel/identity/api/index';
|
|
8
7
|
import {
|
|
9
|
-
|
|
8
|
+
applyEngineeringBlockerDisposition,
|
|
9
|
+
buildEngineeringBlockerDispositionReceipt,
|
|
10
|
+
buildEngineeringContextReceipt,
|
|
11
|
+
engineeringWorkProfileForRisk,
|
|
10
12
|
appendWorkEvidence,
|
|
11
13
|
appendWorkHandoffRef,
|
|
12
14
|
createWorkContract,
|
|
15
|
+
failWorkContract,
|
|
13
16
|
getWorkContract,
|
|
17
|
+
semanticWorkState,
|
|
14
18
|
listWorkContracts,
|
|
19
|
+
readActiveWorkCandidates,
|
|
20
|
+
recordWorkEvidenceState,
|
|
15
21
|
recordWorkScopeEvidence,
|
|
22
|
+
reviseWorkSemanticContext,
|
|
16
23
|
summarizeWorkContract,
|
|
17
24
|
transitionWorkContractPhase,
|
|
18
25
|
updateWorkContract,
|
|
26
|
+
type EngineeringAdmissionEvidence,
|
|
27
|
+
type EvaluationPromotionReceipt,
|
|
19
28
|
type WorkContractStoreOptions,
|
|
20
|
-
} from '
|
|
29
|
+
} from '../../../../packages/kernel/work/api/index';
|
|
21
30
|
import {
|
|
22
|
-
claimPlanStepForWork,
|
|
23
|
-
completePlanStepForWork,
|
|
24
31
|
getPlanContract,
|
|
32
|
+
currentPlanSemanticRevision,
|
|
25
33
|
type PlanContractStoreOptions,
|
|
26
34
|
} from './plan-contract-store';
|
|
27
35
|
import { withPrimaryWorkAdmissionLock } from './semantic-admission';
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import { readRequirement } from '../persistence/requirement-store';
|
|
36
|
+
import { effectiveCurrentWorkVerificationRecords, evaluateWorkCompletionEvidence, evaluateWorkImplementationEvidence } from '../execution/work-evidence-policy';
|
|
37
|
+
import { hasSettledWorkDeliveryReceipt } from '../execution/work-completion-authority';
|
|
38
|
+
import { currentRequirementSemanticRevision, readRequirement } from '../persistence/requirement-store';
|
|
31
39
|
import {
|
|
32
|
-
classifyVerificationOutcome,
|
|
33
40
|
normalizeCheckIds,
|
|
34
41
|
reconcileVerificationHistory,
|
|
35
42
|
type CheckDefinitionLike,
|
|
@@ -41,20 +48,17 @@ import { buildWorkContinuationSnapshot } from './work-continuation';
|
|
|
41
48
|
import type {
|
|
42
49
|
CapabilityRisk,
|
|
43
50
|
EvidenceRef,
|
|
44
|
-
ExecutionModeSelectionInput,
|
|
45
51
|
FacadeResult,
|
|
46
52
|
PlanContract,
|
|
47
53
|
PolicyDecision,
|
|
48
54
|
SuggestedNextAction,
|
|
49
|
-
VerificationRecord,
|
|
50
55
|
WorkContract,
|
|
56
|
+
WorkStartFacts,
|
|
51
57
|
WorkKind,
|
|
52
58
|
WorkRisk,
|
|
53
59
|
} from './types';
|
|
54
|
-
import { selectExecutionMode } from './types';
|
|
55
|
-
import { resolveWorkspaceAdmissionConstraint } from '../routing/workspace-admission';
|
|
56
60
|
|
|
57
|
-
export type GoalWorkloopOperation = 'start' | 'continue' | '
|
|
61
|
+
export type GoalWorkloopOperation = 'start' | 'continue' | 'stop';
|
|
58
62
|
|
|
59
63
|
export interface GoalWorkloopContext {
|
|
60
64
|
workStore: WorkContractStoreOptions;
|
|
@@ -63,10 +67,16 @@ export interface GoalWorkloopContext {
|
|
|
63
67
|
availableChecks?: readonly CheckDefinitionLike[];
|
|
64
68
|
planStore?: PlanContractStoreOptions;
|
|
65
69
|
sourceRevision?: string;
|
|
70
|
+
/** Explicit only when repository observation proves the Work started from a revision or an unborn HEAD. */
|
|
71
|
+
sourceBaseState?: 'revision' | 'unborn';
|
|
66
72
|
checkoutId?: string;
|
|
67
73
|
principalId?: string;
|
|
68
74
|
controllerInstanceId?: string;
|
|
69
75
|
workspaceFingerprint?: string;
|
|
76
|
+
/** Trusted repository observation for placement admission. Never accept this as caller-supplied semantic authority. */
|
|
77
|
+
workspaceDirty?: boolean;
|
|
78
|
+
/** Stage-insensitive Git-canonical identity of the exact current review candidate. Derived by the trusted repository adapter. */
|
|
79
|
+
implementationReviewWorkspaceFingerprint?: string;
|
|
70
80
|
/** Current net repository paths changed from the Work base plus dirty checkout paths. */
|
|
71
81
|
workspaceChangedPaths?: readonly string[];
|
|
72
82
|
/** Durable terminal repository Process ids explicitly bound to this Work + checkout. */
|
|
@@ -81,6 +91,8 @@ export type WorkAdmissionRelation = 'continue' | 'extend' | 'parallel' | 'new_go
|
|
|
81
91
|
|
|
82
92
|
export interface GoalWorkloopStartInput {
|
|
83
93
|
objective: string;
|
|
94
|
+
/** Optional exact durable identity reserved by the admission authority before creation. */
|
|
95
|
+
workId?: string;
|
|
84
96
|
acceptanceCriteria?: string[];
|
|
85
97
|
allowedPaths?: string[];
|
|
86
98
|
/** Non-authoritative first-pass discovery candidates. */
|
|
@@ -88,8 +100,11 @@ export interface GoalWorkloopStartInput {
|
|
|
88
100
|
forbiddenPaths?: string[];
|
|
89
101
|
checks?: string[];
|
|
90
102
|
constraints?: WorkContract['constraints'];
|
|
91
|
-
|
|
103
|
+
/** Concrete mechanical request facts. This capability is itself the explicit durable-Work choice. */
|
|
104
|
+
request: WorkStartFacts;
|
|
92
105
|
requestedBy?: WorkContract['requestedBy'];
|
|
106
|
+
/** Stable durable admission identity for system-originated Work such as recurrent incident repair. */
|
|
107
|
+
requestId?: string;
|
|
93
108
|
relatedWorkId?: string;
|
|
94
109
|
workRelation?: WorkAdmissionRelation;
|
|
95
110
|
requirementId?: string;
|
|
@@ -97,11 +112,12 @@ export interface GoalWorkloopStartInput {
|
|
|
97
112
|
issueId?: string;
|
|
98
113
|
approvalConfirmed?: boolean;
|
|
99
114
|
dryRun?: boolean;
|
|
100
|
-
forceMode?: WorkContract['mode'];
|
|
101
115
|
planId?: string;
|
|
102
116
|
planStepId?: string;
|
|
103
117
|
/** Explicit technical evidence shape chosen by the semantic Controller. Never inferred from objective/check text. */
|
|
104
118
|
workKind?: Extract<WorkKind, 'repository_change' | 'completed_no_change' | 'read_only_review' | 'investigation' | 'local_effect' | 'remote_effect' | 'reconciliation'>;
|
|
119
|
+
/** Trusted source-bound evidence resolved by Forge evidence authorities before admission. Never populate this field from raw caller-supplied receipt ids. */
|
|
120
|
+
verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
|
|
105
121
|
}
|
|
106
122
|
|
|
107
123
|
export interface GoalWorkloopAcceptanceEvidenceBinding {
|
|
@@ -124,32 +140,17 @@ export interface GoalWorkloopContinueInput {
|
|
|
124
140
|
acceptanceEvidence?: GoalWorkloopAcceptanceEvidenceBinding[];
|
|
125
141
|
/** Explicit Controller decision after deterministic acceptance failure; never inferred from note text. */
|
|
126
142
|
acceptanceFailureDecision?: 'repair' | 'rescope';
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
/** Bounded command/invocation audit identity; not part of reusable semantic inputs. */
|
|
139
|
-
commandFingerprint?: string;
|
|
140
|
-
/** Persistence-safe Process receipt. Accepted only when its Work/repo/check identity matches this verification. */
|
|
141
|
-
receipt?: VerificationRecord['receipt'];
|
|
142
|
-
/** When true, simulate infrastructure failure rather than acceptance fail. */
|
|
143
|
-
infrastructureFailed?: boolean;
|
|
144
|
-
/** When true and check is valid, record acceptance failure. */
|
|
145
|
-
checkFailed?: boolean;
|
|
146
|
-
/** When true, skip without acceptance implication. */
|
|
147
|
-
skipped?: boolean;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export interface GoalWorkloopFinalizeInput {
|
|
151
|
-
workId: string;
|
|
152
|
-
forceFailed?: boolean;
|
|
143
|
+
/** Trusted refreshed engineering evidence minted by Runtime composition, never copied from raw caller receipt ids. */
|
|
144
|
+
verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
|
|
145
|
+
/** Semantic Controller blocker classification; Forge derives the permitted action and persists the receipt. */
|
|
146
|
+
engineeringBlocker?: {
|
|
147
|
+
blockerId: string;
|
|
148
|
+
classification: 'same_root_cause' | 'same_root_cause_scope_extension' | 'unrelated';
|
|
149
|
+
rationale: string;
|
|
150
|
+
semanticScopeKeys?: string[];
|
|
151
|
+
/** Required for classification=unrelated: the exact Work that already owns the blocker. */
|
|
152
|
+
linkedWorkId?: string;
|
|
153
|
+
};
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
export interface GoalWorkloopStopInput {
|
|
@@ -178,44 +179,41 @@ function handoffIdFor(prefix: string): string {
|
|
|
178
179
|
|
|
179
180
|
function workRiskFor(input: GoalWorkloopStartInput): WorkRisk {
|
|
180
181
|
if (input.workKind === 'read_only_review') return 'readonly';
|
|
181
|
-
const risk = input.
|
|
182
|
-
if (input.
|
|
183
|
-
if (input.
|
|
182
|
+
const risk = input.request.risk;
|
|
183
|
+
if (input.request.secretAccess === true || input.request.destructive === true) return 'destructive';
|
|
184
|
+
if (input.request.remoteWrite === true) return 'high';
|
|
184
185
|
if (risk === 'readonly') return 'readonly';
|
|
185
186
|
if (risk === 'destructive' || risk === 'destructive_remote' || risk === 'raw_secret_config') return 'destructive';
|
|
186
187
|
if (risk === 'remote_write') return 'high';
|
|
187
188
|
if (risk === 'local_repo_write') return 'low';
|
|
189
|
+
if (risk === undefined) return 'low';
|
|
188
190
|
return 'medium';
|
|
189
191
|
}
|
|
190
192
|
|
|
193
|
+
function resolvedWorkKindFor(input: GoalWorkloopStartInput): WorkKind {
|
|
194
|
+
if (input.workKind) return input.workKind;
|
|
195
|
+
if (input.request.requiresExternalEffect === true) {
|
|
196
|
+
// Mechanical effect classification only: a remote effect differs from a local
|
|
197
|
+
// effect in receipt identity. Predicted scope size is model strategy and is
|
|
198
|
+
// never consulted; callers declare `work_kind` when a repository change is
|
|
199
|
+
// also involved.
|
|
200
|
+
return input.request.remoteWrite === true ? 'remote_effect' : 'local_effect';
|
|
201
|
+
}
|
|
202
|
+
return 'repository_change';
|
|
203
|
+
}
|
|
204
|
+
|
|
191
205
|
function suggestedForWorkIdentity(workId: string, checks: string[], extras: SuggestedNextAction[] = []): SuggestedNextAction[] {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
tool: 'rh_work',
|
|
204
|
-
operation: 'verify',
|
|
205
|
-
payload: { work_id: workId, check_id: checks[0] },
|
|
206
|
-
risk: 'workspace_write',
|
|
207
|
-
confidence: checks[0] ? 'high' : 'low',
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
label: 'Finalize when ready',
|
|
211
|
-
tool: 'rh_work',
|
|
212
|
-
operation: 'finalize',
|
|
213
|
-
payload: { work_id: workId },
|
|
214
|
-
risk: 'readonly',
|
|
215
|
-
confidence: 'medium',
|
|
216
|
-
},
|
|
217
|
-
];
|
|
218
|
-
return validateSuggestedNextActions([...extras, ...base], {
|
|
206
|
+
const semanticRead: SuggestedNextAction = {
|
|
207
|
+
label: 'Read current Work semantic context',
|
|
208
|
+
tool: 'rh_work',
|
|
209
|
+
operation: 'get',
|
|
210
|
+
payload: { work_id: workId },
|
|
211
|
+
risk: 'readonly',
|
|
212
|
+
confidence: 'high',
|
|
213
|
+
};
|
|
214
|
+
// Suggested actions expose facts/capabilities only. The model decides whether
|
|
215
|
+
// implementation, checks, review, delivery, or semantic completion is next.
|
|
216
|
+
return validateSuggestedNextActions([...extras, semanticRead], {
|
|
219
217
|
validCheckIds: checks,
|
|
220
218
|
}).actions;
|
|
221
219
|
}
|
|
@@ -240,31 +238,36 @@ export function routeWorkStart(
|
|
|
240
238
|
ctx: GoalWorkloopContext,
|
|
241
239
|
input: GoalWorkloopStartInput,
|
|
242
240
|
): FacadeResult {
|
|
243
|
-
const
|
|
244
|
-
if (placementResolution.ok === false) {
|
|
245
|
-
return buildFacadeResult({
|
|
246
|
-
status: 'blocked',
|
|
247
|
-
summary: `${placementResolution.code}: ${placementResolution.message}`,
|
|
248
|
-
data: { executionStarted: false, workContractCreated: false, placementConstraintConflict: true },
|
|
249
|
-
rawAvailable: false,
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
const placementConstraint = placementResolution.constraint;
|
|
241
|
+
const workspaceMode: 'current' | 'isolated' = input.constraints?.workspaceMode === 'isolated' || input.constraints?.workspaceMode === 'auto' ? 'isolated' : 'current';
|
|
253
242
|
const canonicalConstraints: WorkContract['constraints'] = {
|
|
254
243
|
...(input.constraints ?? {}),
|
|
255
|
-
workspaceMode
|
|
256
|
-
requireWorktree: placementConstraint.requireWorktree,
|
|
257
|
-
directMainProhibited: placementConstraint.directMainProhibited,
|
|
244
|
+
workspaceMode,
|
|
258
245
|
};
|
|
259
246
|
const strategyConflictRequiresApproval = input.constraints?.architectureStrategyChange === true
|
|
260
247
|
|| input.constraints?.conflictsWithThinHarnessPolicy === true
|
|
261
248
|
|| input.constraints?.changesDefaultExecutionStrategy === true;
|
|
262
|
-
|
|
249
|
+
let relatedLifecycleSource: WorkContract | undefined;
|
|
250
|
+
if (input.relatedWorkId) {
|
|
251
|
+
try {
|
|
252
|
+
relatedLifecycleSource = getWorkContract(ctx.workStore, input.relatedWorkId);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
return buildFacadeResult({
|
|
255
|
+
status: 'blocked',
|
|
256
|
+
summary: `WORK_RELATED_CONTRACT_INVALID: ${input.relatedWorkId} is malformed and cannot authorize a Work relationship.`,
|
|
257
|
+
data: {
|
|
258
|
+
executionStarted: false,
|
|
259
|
+
workContractCreated: false,
|
|
260
|
+
invalidWorkId: input.relatedWorkId,
|
|
261
|
+
invalidWorkError: error instanceof Error ? error.message : String(error),
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
263
266
|
const inheritedReadOnlyReview = Boolean(
|
|
264
267
|
input.workKind === undefined
|
|
265
268
|
&& input.workRelation === 'new_goal'
|
|
266
269
|
&& relatedLifecycleSource?.workKind === 'read_only_review'
|
|
267
|
-
&& (relatedLifecycleSource
|
|
270
|
+
&& semanticWorkState(relatedLifecycleSource) !== 'open',
|
|
268
271
|
);
|
|
269
272
|
if (
|
|
270
273
|
inheritedReadOnlyReview
|
|
@@ -279,19 +282,23 @@ export function routeWorkStart(
|
|
|
279
282
|
});
|
|
280
283
|
}
|
|
281
284
|
const requestedWorkKind = input.workKind ?? (inheritedReadOnlyReview ? 'read_only_review' : undefined);
|
|
282
|
-
const
|
|
283
|
-
&&
|
|
284
|
-
&& input.
|
|
285
|
-
|
|
285
|
+
const explicitSourceMutationFence = input.allowedPaths !== undefined
|
|
286
|
+
&& input.allowedPaths.length === 0
|
|
287
|
+
&& (input.forbiddenPaths ?? []).some((value) => {
|
|
288
|
+
const normalized = value.trim().replace(/\\/g, '/');
|
|
289
|
+
return normalized === '*' || normalized === '**' || normalized === '**/*';
|
|
290
|
+
});
|
|
291
|
+
// A read-only review is an explicit `work_kind`, or an explicit source-mutation
|
|
292
|
+
// fence that proves no repository path may change. Forge never infers it from
|
|
293
|
+
// caller hints about investigation depth or recovery needs.
|
|
294
|
+
const typedReadOnlyReviewRequested = requestedWorkKind === undefined && explicitSourceMutationFence;
|
|
286
295
|
const effectiveWorkKind = requestedWorkKind ?? (typedReadOnlyReviewRequested ? 'read_only_review' : undefined);
|
|
287
296
|
const readOnlyReviewRequested = effectiveWorkKind === 'read_only_review';
|
|
288
297
|
const readOnlyMutationConflict = readOnlyReviewRequested && (
|
|
289
|
-
input.
|
|
290
|
-
||
|
|
291
|
-
||
|
|
292
|
-
|| input.
|
|
293
|
-
|| input.modeInput.remoteWrite === true
|
|
294
|
-
|| input.modeInput.destructive === true
|
|
298
|
+
input.request.mutation === true
|
|
299
|
+
|| input.request.requiresExternalEffect === true
|
|
300
|
+
|| input.request.remoteWrite === true
|
|
301
|
+
|| input.request.destructive === true
|
|
295
302
|
);
|
|
296
303
|
if (readOnlyMutationConflict) {
|
|
297
304
|
return buildFacadeResult({
|
|
@@ -300,151 +307,49 @@ export function routeWorkStart(
|
|
|
300
307
|
data: { executionStarted: false, workContractCreated: false, workKind: effectiveWorkKind },
|
|
301
308
|
});
|
|
302
309
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
// A terminal continuation is not a new semantic request. Its durable predecessor
|
|
311
|
+
// and Plan lineage already define the goal scope, while the successor Plan step
|
|
312
|
+
// becomes the Work objective later in startGoalWorkloop.
|
|
313
|
+
const routeObjective = input.objective.trim()
|
|
314
|
+
|| (input.workRelation === 'continue' && relatedLifecycleSource && semanticWorkState(relatedLifecycleSource) === 'completed'
|
|
315
|
+
? relatedLifecycleSource.objective.trim()
|
|
316
|
+
: '');
|
|
317
|
+
const effectiveRequest: WorkStartFacts = {
|
|
318
|
+
...input.request,
|
|
319
|
+
objective: routeObjective,
|
|
320
|
+
workspaceDirty: ctx.workspaceDirty ?? input.request.workspaceDirty,
|
|
321
|
+
mutation: readOnlyReviewRequested ? false : input.request.mutation ?? input.request.risk !== 'readonly',
|
|
322
|
+
risk: readOnlyReviewRequested ? 'readonly' : input.request.risk,
|
|
311
323
|
approvalConfirmed: input.approvalConfirmed === true,
|
|
312
324
|
requiresUserApproval: input.approvalConfirmed === true
|
|
313
325
|
? false
|
|
314
|
-
: input.
|
|
326
|
+
: input.request.requiresUserApproval === true || strategyConflictRequiresApproval,
|
|
315
327
|
};
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
? {
|
|
325
|
-
...selected,
|
|
326
|
-
mode: input.forceMode,
|
|
327
|
-
reason: `Forced mode: ${input.forceMode}. ${selected.reason}`,
|
|
328
|
-
createWorkContract: input.forceMode === 'handoff_only' ? false : selected.requiresWork,
|
|
329
|
-
createHandoff: input.forceMode === 'handoff_only',
|
|
330
|
-
}
|
|
331
|
-
: selected;
|
|
332
|
-
const evaluateAccessPolicy = (selected: ReturnType<typeof selectExecutionMode>) => evaluatePolicyGate({
|
|
333
|
-
capabilityId: selected.mode === 'direct_control' ? 'repository.direct_edit' : selected.mode === 'goal_workloop' ? 'controller.goal_workloop' : 'controller.handoff_inbox',
|
|
334
|
-
risk: effectiveModeInput.risk
|
|
335
|
-
?? (input.modeInput.secretAccess === true ? 'raw_secret_config'
|
|
336
|
-
: input.modeInput.destructive === true ? 'destructive'
|
|
337
|
-
: input.modeInput.remoteWrite === true ? 'remote_write'
|
|
338
|
-
: input.modeInput.requiresApproval === true || input.modeInput.requiresUserApproval === true ? 'workspace_write'
|
|
339
|
-
: selected.mode === 'direct_control' ? 'local_repo_write'
|
|
340
|
-
: selected.mode === 'goal_workloop' ? 'workspace_write'
|
|
341
|
-
: 'readonly'),
|
|
328
|
+
const evaluateAccessPolicy = () => evaluatePolicyGate({
|
|
329
|
+
capabilityId: 'controller.work_contract',
|
|
330
|
+
risk: effectiveRequest.risk
|
|
331
|
+
?? (input.request.secretAccess === true ? 'raw_secret_config'
|
|
332
|
+
: input.request.destructive === true ? 'destructive'
|
|
333
|
+
: input.request.remoteWrite === true ? 'remote_write'
|
|
334
|
+
: input.request.requiresApproval === true || input.request.requiresUserApproval === true ? 'workspace_write'
|
|
335
|
+
: 'workspace_write'),
|
|
342
336
|
accessMode: input.constraints?.accessMode,
|
|
343
337
|
approvalConfirmed: input.approvalConfirmed === true,
|
|
344
338
|
dryRun: input.dryRun === true,
|
|
345
|
-
directEditBoundary: {
|
|
346
|
-
scopeClear: effectiveModeInput.scopeClear,
|
|
347
|
-
maxChangedFiles: effectiveModeInput.expectedFiles,
|
|
348
|
-
maxChangedLines: effectiveModeInput.expectedChangedLines,
|
|
349
|
-
pathsExplicit: effectiveModeInput.scopeClear,
|
|
350
|
-
},
|
|
351
339
|
});
|
|
340
|
+
// Missing mechanical admission fields are reported to the caller verbatim.
|
|
341
|
+
// Forge does not translate them into an engineering-mode recommendation.
|
|
342
|
+
const missingContractFields: string[] = [];
|
|
343
|
+
if (!input.request.scopeClear) missingContractFields.push('scopeSummary', 'acceptanceCriteria', 'allowedPaths');
|
|
344
|
+
if (input.objective.trim().length === 0) missingContractFields.push('objective');
|
|
352
345
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return buildFacadeResult({
|
|
356
|
-
status: 'blocked',
|
|
357
|
-
summary: 'WORKSPACE_PLACEMENT_DIRECT_CONTROL_FORBIDDEN: typed placement requires isolation, so force_mode=direct_control cannot override admission policy.',
|
|
358
|
-
data: { executionStarted: false, workContractCreated: false, routeDecision: selectedMode.routeDecision },
|
|
359
|
-
rawAvailable: false,
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
let policy = evaluateAccessPolicy(selectedMode);
|
|
363
|
-
if (policy.decision === 'allowed' && effectiveModeInput.requiresUserApproval !== true) {
|
|
364
|
-
selectedMode = selectExecutionMode({ ...effectiveModeInput, approvalConfirmed: true });
|
|
365
|
-
policy = evaluateAccessPolicy(selectedMode);
|
|
366
|
-
}
|
|
367
|
-
let mode = applyForcedMode(selectedMode);
|
|
368
|
-
const routeApprovalRequired = mode.routeDecision.requiresApproval
|
|
369
|
-
&& mode.routeDecision.waitForUser
|
|
370
|
-
&& mode.routeDecision.approvalState !== 'blocked_by_policy';
|
|
371
|
-
const approvalRequired = policy.decision === 'approval_required' || routeApprovalRequired;
|
|
372
|
-
|
|
373
|
-
// Route Policy is the sole execution-depth authority. Existing Work is
|
|
374
|
-
// ownership context, not a reason to upgrade an otherwise Direct operation
|
|
375
|
-
// into Durable Work. Explicit Work/Plan ownership is resolved only after a
|
|
376
|
-
// durable route has already been selected; checkout writer conflicts affect
|
|
377
|
-
// placement, not semantic task identity.
|
|
378
|
-
|
|
379
|
-
if (mode.mode === 'direct_control') {
|
|
380
|
-
const available = ctx.availableChecks ?? [];
|
|
381
|
-
const normalized = normalizeCheckIds(input.checks ?? [], available);
|
|
382
|
-
// Explicit ownership may annotate a Direct operation, but it never changes
|
|
383
|
-
// the RouteDecision. The actual repository mutation can pass this Work id
|
|
384
|
-
// to repository_safe_patch_apply / repository_command_execute for evidence
|
|
385
|
-
// attribution while staying on the Direct/Process path.
|
|
386
|
-
const directOwnershipCandidates = input.relatedWorkId || (input.planId && input.planStepId)
|
|
387
|
-
? listWorkContracts({ ...ctx.workStore, status: 'active', limit: 100 })
|
|
388
|
-
.filter((candidate) => (candidate.lifecycleRole ?? 'primary') === 'primary')
|
|
389
|
-
: [];
|
|
390
|
-
const directPlan = input.planId && ctx.planStore ? getPlanContract(ctx.planStore, input.planId) : undefined;
|
|
391
|
-
const directPlanStep = directPlan && input.planStepId ? directPlan.steps.find((step) => step.id === input.planStepId) : undefined;
|
|
392
|
-
const directOwner = (input.relatedWorkId
|
|
393
|
-
? directOwnershipCandidates.find((candidate) => candidate.workId === input.relatedWorkId)
|
|
394
|
-
: undefined)
|
|
395
|
-
?? (directPlanStep?.workId
|
|
396
|
-
? directOwnershipCandidates.find((candidate) => candidate.workId === directPlanStep.workId)
|
|
397
|
-
: undefined);
|
|
398
|
-
const suggested = validateSuggestedNextActions([
|
|
399
|
-
{
|
|
400
|
-
label: 'Apply bounded direct edit',
|
|
401
|
-
tool: 'rh_work',
|
|
402
|
-
operation: 'start',
|
|
403
|
-
payload: { mode: 'direct_control', objective: input.objective.slice(0, 200) },
|
|
404
|
-
risk: 'local_repo_write',
|
|
405
|
-
confidence: 'high',
|
|
406
|
-
reason: 'Small supervised task stays on Direct Control; no WorkContract created.',
|
|
407
|
-
},
|
|
408
|
-
...normalized.suggestedNextActions,
|
|
409
|
-
{
|
|
410
|
-
label: 'Read repository context',
|
|
411
|
-
tool: 'rh_context',
|
|
412
|
-
operation: 'get',
|
|
413
|
-
risk: 'readonly',
|
|
414
|
-
confidence: 'medium',
|
|
415
|
-
},
|
|
416
|
-
], { validCheckIds: normalized.validCheckIds }).actions;
|
|
417
|
-
|
|
418
|
-
return buildFacadeResult({
|
|
419
|
-
status: policy.decision === 'denied' ? 'blocked' : 'ok',
|
|
420
|
-
summary: `Direct control recommended. No WorkContract created. ${mode.reason}`,
|
|
421
|
-
data: {
|
|
422
|
-
mode,
|
|
423
|
-
policy,
|
|
424
|
-
workContractCreated: false,
|
|
425
|
-
directControlPreserved: true,
|
|
426
|
-
objective: input.objective.slice(0, 1_000),
|
|
427
|
-
normalizedChecks: normalized,
|
|
428
|
-
...(directOwner ? {
|
|
429
|
-
ownership: {
|
|
430
|
-
workId: directOwner.workId,
|
|
431
|
-
relation: input.workRelation ?? 'continue',
|
|
432
|
-
executionDepthPreserved: true,
|
|
433
|
-
},
|
|
434
|
-
} : {}),
|
|
435
|
-
},
|
|
436
|
-
warnings: [...policy.warnings, ...normalized.warnings],
|
|
437
|
-
suggestedNextActions: suggested,
|
|
438
|
-
rawAvailable: false,
|
|
439
|
-
});
|
|
440
|
-
}
|
|
346
|
+
const policy = evaluateAccessPolicy();
|
|
347
|
+
const approvalRequired = policy.decision === 'approval_required';
|
|
441
348
|
|
|
442
|
-
// Policy approval decisions stop before Work creation. Ordinary host-managed
|
|
443
|
-
// reaches this point only after the
|
|
444
|
-
// Policy's explicit allowed decision as authorization evidence.
|
|
349
|
+
// Policy approval decisions stop before Work creation. Ordinary host-managed
|
|
350
|
+
// local work reaches this point only after the Access Policy authorized it.
|
|
445
351
|
const blockForHandoff =
|
|
446
|
-
|
|
447
|
-
|| policy.decision === 'denied'
|
|
352
|
+
policy.decision === 'denied'
|
|
448
353
|
|| policy.decision === 'approval_required';
|
|
449
354
|
|
|
450
355
|
if (blockForHandoff) {
|
|
@@ -453,20 +358,19 @@ export function routeWorkStart(
|
|
|
453
358
|
repoId: ctx.repoId,
|
|
454
359
|
title: 'Work blocked pending decision',
|
|
455
360
|
severity: policy.decision === 'denied' ? 'blocked' : 'needs_review',
|
|
456
|
-
creationReason: !input.
|
|
361
|
+
creationReason: !input.request.scopeClear
|
|
457
362
|
? 'invalid_objective'
|
|
458
363
|
: approvalRequired
|
|
459
|
-
? (input.
|
|
364
|
+
? (input.request.destructive ? 'destructive_action_requires_confirmation' : 'policy_approval_required')
|
|
460
365
|
: 'missing_authorization',
|
|
461
|
-
reason:
|
|
462
|
-
summary: `
|
|
366
|
+
reason: policy.reason,
|
|
367
|
+
summary: `Execution blocked by access policy: ${policy.reason}`,
|
|
463
368
|
currentState: {
|
|
464
369
|
repoId: ctx.repoId,
|
|
465
|
-
mode: 'handoff_only',
|
|
466
370
|
statusSummary: 'waiting for ChatGPT or user decision; no execution started',
|
|
467
|
-
blockedBy:
|
|
371
|
+
blockedBy: missingContractFields,
|
|
468
372
|
},
|
|
469
|
-
attemptedActions: ['
|
|
373
|
+
attemptedActions: ['work_start'],
|
|
470
374
|
evidenceRefs: [],
|
|
471
375
|
blockingDecision: approvalRequired
|
|
472
376
|
? 'Approve side effects or restate a safer objective.'
|
|
@@ -479,27 +383,19 @@ export function routeWorkStart(
|
|
|
479
383
|
operation: 'start',
|
|
480
384
|
label: 'Approve and start work',
|
|
481
385
|
summary: 'Create the work contract with the original scope and explicit approval.',
|
|
482
|
-
risk: input.
|
|
386
|
+
risk: input.request.destructive ? 'destructive' : 'workspace_write',
|
|
483
387
|
payload: {
|
|
484
388
|
objective: input.objective,
|
|
485
389
|
acceptanceCriteria: input.acceptanceCriteria,
|
|
486
390
|
allowedPaths: input.allowedPaths,
|
|
487
391
|
forbiddenPaths: input.forbiddenPaths,
|
|
488
392
|
checkIds: input.checks,
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
requiresInvestigation: input.modeInput.requiresInvestigation,
|
|
493
|
-
requiresLongRunningChecks: input.modeInput.requiresLongRunningChecks,
|
|
494
|
-
requiresWorker: input.modeInput.requiresWorker,
|
|
495
|
-
requiresApproval: input.modeInput.requiresApproval === true || input.modeInput.requiresUserApproval === true,
|
|
496
|
-
destructive: input.modeInput.destructive === true,
|
|
393
|
+
scopeClear: input.request.scopeClear,
|
|
394
|
+
requiresApproval: input.request.requiresApproval === true || input.request.requiresUserApproval === true,
|
|
395
|
+
destructive: input.request.destructive === true,
|
|
497
396
|
accessMode: input.constraints?.accessMode,
|
|
498
|
-
workspaceMode
|
|
499
|
-
requireWorktree: placementConstraint.requireWorktree,
|
|
500
|
-
directMainProhibited: placementConstraint.directMainProhibited,
|
|
397
|
+
workspaceMode,
|
|
501
398
|
approvalConfirmed: true,
|
|
502
|
-
forceMode: 'goal_workloop',
|
|
503
399
|
},
|
|
504
400
|
}
|
|
505
401
|
: undefined,
|
|
@@ -516,9 +412,8 @@ export function routeWorkStart(
|
|
|
516
412
|
|
|
517
413
|
return buildFacadeResult({
|
|
518
414
|
status: policy.decision === 'denied' ? 'blocked' : approvalRequired ? 'approval_required' : 'blocked',
|
|
519
|
-
summary:
|
|
415
|
+
summary: 'Blocked: no WorkContract created and no execution started until the policy decision is resolved.',
|
|
520
416
|
data: {
|
|
521
|
-
mode,
|
|
522
417
|
policy,
|
|
523
418
|
workContractCreated: false,
|
|
524
419
|
handoffId: handoff.id,
|
|
@@ -548,33 +443,20 @@ export function routeWorkStart(
|
|
|
548
443
|
return startGoalWorkloop(ctx, {
|
|
549
444
|
...input,
|
|
550
445
|
constraints: canonicalConstraints,
|
|
551
|
-
|
|
446
|
+
request: effectiveRequest,
|
|
552
447
|
workKind: effectiveWorkKind,
|
|
553
|
-
}, policy
|
|
448
|
+
}, policy);
|
|
554
449
|
}
|
|
555
450
|
|
|
556
451
|
export function startGoalWorkloop(
|
|
557
452
|
ctx: GoalWorkloopContext,
|
|
558
453
|
input: GoalWorkloopStartInput,
|
|
559
454
|
policy?: PolicyDecision,
|
|
560
|
-
executionMode: 'direct_control' | 'goal_workloop' = 'goal_workloop',
|
|
561
|
-
routeDecision = selectExecutionMode({ ...input.modeInput, objective: input.objective, knownPaths: input.allowedPaths }).routeDecision,
|
|
562
455
|
): FacadeResult {
|
|
563
|
-
const
|
|
564
|
-
if (placementResolution.ok === false) {
|
|
565
|
-
return buildFacadeResult({
|
|
566
|
-
status: 'blocked',
|
|
567
|
-
summary: `${placementResolution.code}: ${placementResolution.message}`,
|
|
568
|
-
data: { executionStarted: false, workContractCreated: false, placementConstraintConflict: true },
|
|
569
|
-
rawAvailable: false,
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
const placementConstraint = placementResolution.constraint;
|
|
456
|
+
const workspaceMode: 'current' | 'isolated' = input.constraints?.workspaceMode === 'isolated' || input.constraints?.workspaceMode === 'auto' ? 'isolated' : 'current';
|
|
573
457
|
const canonicalConstraints: WorkContract['constraints'] = {
|
|
574
458
|
...(input.constraints ?? {}),
|
|
575
|
-
workspaceMode
|
|
576
|
-
requireWorktree: placementConstraint.requireWorktree,
|
|
577
|
-
directMainProhibited: placementConstraint.directMainProhibited,
|
|
459
|
+
workspaceMode,
|
|
578
460
|
};
|
|
579
461
|
const hasStrongAdmissionBinding = Boolean(input.planId || input.planStepId || input.requirementId || input.relatedWorkId || input.workRelation);
|
|
580
462
|
if (hasStrongAdmissionBinding && !ctx.semanticAdmissionLocked) {
|
|
@@ -582,38 +464,134 @@ export function startGoalWorkloop(
|
|
|
582
464
|
{ ...ctx, semanticAdmissionLocked: true },
|
|
583
465
|
input,
|
|
584
466
|
policy,
|
|
585
|
-
executionMode,
|
|
586
|
-
routeDecision,
|
|
587
467
|
));
|
|
588
468
|
}
|
|
589
469
|
const at = nowIso(ctx);
|
|
470
|
+
let resolvedWorkKind: WorkKind;
|
|
471
|
+
try {
|
|
472
|
+
resolvedWorkKind = resolvedWorkKindFor(input);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
return buildFacadeResult({
|
|
475
|
+
status: 'blocked',
|
|
476
|
+
summary: error instanceof Error ? error.message : 'WORK_KIND_RESOLUTION_INVALID',
|
|
477
|
+
data: { executionStarted: false, workContractCreated: false },
|
|
478
|
+
rawAvailable: false,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
const workRisk = workRiskFor(input);
|
|
482
|
+
// External-effect risk is governed by authorization/confirmation/receipt
|
|
483
|
+
// semantics. Engineering admission is specifically for repository mutation.
|
|
484
|
+
const engineeringRisk: WorkRisk = resolvedWorkKind === 'repository_change' ? workRisk : 'readonly';
|
|
485
|
+
const engineeringProfile = engineeringWorkProfileForRisk(engineeringRisk);
|
|
486
|
+
const engineeringSourceIdentity = ctx.sourceRevision?.trim()
|
|
487
|
+
? { kind: 'revision' as const, revision: ctx.sourceRevision.trim() }
|
|
488
|
+
: ctx.sourceBaseState === 'unborn'
|
|
489
|
+
? { kind: 'unborn' as const }
|
|
490
|
+
: { kind: 'unknown' as const };
|
|
491
|
+
let engineeringContext;
|
|
492
|
+
try {
|
|
493
|
+
engineeringContext = buildEngineeringContextReceipt({
|
|
494
|
+
risk: engineeringRisk,
|
|
495
|
+
sourceIdentity: engineeringSourceIdentity,
|
|
496
|
+
evidence: input.verifiedEngineeringEvidence,
|
|
497
|
+
recordedAt: at,
|
|
498
|
+
});
|
|
499
|
+
} catch (error) {
|
|
500
|
+
return buildFacadeResult({
|
|
501
|
+
status: 'blocked',
|
|
502
|
+
summary: error instanceof Error ? error.message : 'ENGINEERING_CONTEXT_INVALID',
|
|
503
|
+
data: { executionStarted: false, workContractCreated: false, engineeringProfile },
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
const engineeringMutation = resolvedWorkKind === 'repository_change'
|
|
507
|
+
&& (input.request.mutation ?? input.request.risk !== 'readonly');
|
|
508
|
+
// Engineering context is durable model/project guidance only. Safety and
|
|
509
|
+
// capability admission is enforced by the concrete executor, never here.
|
|
510
|
+
void engineeringMutation;
|
|
590
511
|
const available = ctx.availableChecks ?? [];
|
|
591
|
-
const
|
|
592
|
-
const activeWorks =
|
|
512
|
+
const activeAdmissionSnapshot = readActiveWorkCandidates({ ...ctx.workStore, limit: 100 });
|
|
513
|
+
const activeWorks = activeAdmissionSnapshot.contracts
|
|
593
514
|
.filter((candidate) => (candidate.lifecycleRole ?? 'primary') === 'primary');
|
|
594
|
-
const
|
|
595
|
-
?
|
|
596
|
-
&& candidate.worktreePolicy.required !== true
|
|
597
|
-
&& !candidate.worktreeRef)
|
|
515
|
+
const invalidRelatedWork = input.relatedWorkId
|
|
516
|
+
? activeAdmissionSnapshot.invalid.find((candidate) => candidate.workId === input.relatedWorkId)
|
|
598
517
|
: undefined;
|
|
518
|
+
if (invalidRelatedWork) {
|
|
519
|
+
return buildFacadeResult({
|
|
520
|
+
status: 'blocked',
|
|
521
|
+
summary: `WORK_RELATED_CONTRACT_INVALID: ${invalidRelatedWork.workId} is malformed and cannot authorize a Work relationship.`,
|
|
522
|
+
data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidRelatedWork.workId, invalidWorkError: invalidRelatedWork.error },
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
const relatedLifecycleSource = input.relatedWorkId
|
|
526
|
+
? getWorkContract(ctx.workStore, input.relatedWorkId)
|
|
527
|
+
: undefined;
|
|
528
|
+
const terminalContinuationSource = input.workRelation === 'continue'
|
|
529
|
+
&& relatedLifecycleSource
|
|
530
|
+
&& semanticWorkState(relatedLifecycleSource) !== 'open'
|
|
531
|
+
? relatedLifecycleSource
|
|
532
|
+
: undefined;
|
|
533
|
+
if (terminalContinuationSource && !terminalContinuationSource.requirementId && !terminalContinuationSource.planId) {
|
|
534
|
+
return buildFacadeResult({
|
|
535
|
+
status: 'blocked',
|
|
536
|
+
summary: `WORK_SUCCESSOR_SEMANTIC_SCOPE_REQUIRED: ${terminalContinuationSource.workId} is terminal but has no Requirement/Plan lineage. Use new_goal or bind an explicit durable semantic authority instead of guessing continuity.`,
|
|
537
|
+
data: { executionStarted: false, workContractCreated: false, relatedWorkId: terminalContinuationSource.workId },
|
|
538
|
+
});
|
|
539
|
+
}
|
|
599
540
|
const explicitRelatedWork = input.relatedWorkId
|
|
600
541
|
? activeWorks.find((candidate) => candidate.workId === input.relatedWorkId)
|
|
601
542
|
: undefined;
|
|
602
|
-
|
|
603
|
-
|
|
543
|
+
|
|
544
|
+
let resolvedPlanId = input.planId?.trim() || undefined;
|
|
545
|
+
const resolvedPlanStepId = input.planStepId?.trim() || undefined;
|
|
546
|
+
if (terminalContinuationSource?.planId) {
|
|
547
|
+
if (resolvedPlanId && resolvedPlanId !== terminalContinuationSource.planId) {
|
|
548
|
+
return buildFacadeResult({
|
|
549
|
+
status: 'blocked',
|
|
550
|
+
summary: `WORK_SUCCESSOR_PLAN_MISMATCH: predecessor ${terminalContinuationSource.workId} belongs to ${terminalContinuationSource.planId}, not ${resolvedPlanId}. Supersede/replan explicitly instead of silently moving continuation authority.`,
|
|
551
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, predecessorPlanId: terminalContinuationSource.planId, requestedPlanId: resolvedPlanId },
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
resolvedPlanId = terminalContinuationSource.planId;
|
|
555
|
+
}
|
|
556
|
+
// Plan and Plan item references are recorded provenance for this Work. They
|
|
557
|
+
// never select the successor, own the scope, gate admission or require
|
|
558
|
+
// acceptance; only the Plan's existence is validated so a Work cannot record a
|
|
559
|
+
// reference to a Plan authority that does not exist.
|
|
560
|
+
const plan = resolvedPlanId && ctx.planStore ? getPlanContract(ctx.planStore, resolvedPlanId) : undefined;
|
|
561
|
+
if (resolvedPlanId && !plan) {
|
|
562
|
+
return buildFacadeResult({
|
|
563
|
+
status: 'blocked',
|
|
564
|
+
summary: terminalContinuationSource?.planId
|
|
565
|
+
? `WORK_SUCCESSOR_PLAN_NOT_FOUND: ${terminalContinuationSource.planId}. Recover the durable Plan authority before creating a successor Work.`
|
|
566
|
+
: `PLAN_NOT_FOUND: ${resolvedPlanId}. Recover or create the semantic Plan before using it as Work provenance.`,
|
|
567
|
+
data: {
|
|
568
|
+
executionStarted: false,
|
|
569
|
+
workContractCreated: false,
|
|
570
|
+
planId: resolvedPlanId,
|
|
571
|
+
...(terminalContinuationSource?.planId ? { predecessorWorkId: terminalContinuationSource.workId } : {}),
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
}
|
|
604
575
|
const requestedRequirementId = input.requirementId?.trim() || undefined;
|
|
605
|
-
|
|
576
|
+
const predecessorRequirementId = terminalContinuationSource?.requirementId;
|
|
577
|
+
if (predecessorRequirementId && requestedRequirementId && predecessorRequirementId !== requestedRequirementId) {
|
|
606
578
|
return buildFacadeResult({
|
|
607
579
|
status: 'blocked',
|
|
608
|
-
summary: `
|
|
609
|
-
data: { executionStarted: false, workContractCreated: false,
|
|
580
|
+
summary: `WORK_SUCCESSOR_REQUIREMENT_MISMATCH: predecessor ${terminalContinuationSource.workId} belongs to ${predecessorRequirementId}, not ${requestedRequirementId}.`,
|
|
581
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, predecessorRequirementId, requestedRequirementId },
|
|
610
582
|
});
|
|
611
583
|
}
|
|
612
|
-
|
|
584
|
+
// A selected Plan supplies its Requirement when no explicit/predecessor link exists.
|
|
585
|
+
// Requirement membership alone never selects sibling Plans or successor Works.
|
|
586
|
+
const effectiveRequirementId = requestedRequirementId || predecessorRequirementId || plan?.requirementId;
|
|
587
|
+
if (plan?.requirementId && effectiveRequirementId && plan.requirementId !== effectiveRequirementId) {
|
|
588
|
+
return buildFacadeResult({ status: 'blocked', summary: `WORK_PLAN_REQUIREMENT_MISMATCH: ${resolvedPlanId} belongs to ${plan.requirementId}, not ${effectiveRequirementId}.`, data: { executionStarted: false, workContractCreated: false } });
|
|
589
|
+
}
|
|
590
|
+
const effectiveRequirementRecord = effectiveRequirementId && ctx.workStore.controllerHome ? readRequirement({ controllerHome: ctx.workStore.controllerHome }, effectiveRequirementId) : undefined;
|
|
613
591
|
if (
|
|
614
592
|
effectiveRequirementId
|
|
615
593
|
&& ctx.workStore.controllerHome
|
|
616
|
-
&& !
|
|
594
|
+
&& !effectiveRequirementRecord
|
|
617
595
|
) {
|
|
618
596
|
return buildFacadeResult({
|
|
619
597
|
status: 'blocked',
|
|
@@ -621,49 +599,55 @@ export function startGoalWorkloop(
|
|
|
621
599
|
data: { executionStarted: false, workContractCreated: false, requirementId: effectiveRequirementId, planId: plan?.planId },
|
|
622
600
|
});
|
|
623
601
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
const mismatches = [
|
|
632
|
-
!sameStringSet(input.acceptanceCriteria, planStep.acceptanceCriteria) && 'acceptance_criteria',
|
|
633
|
-
!sameStringSet(input.allowedPaths, planStep.allowedPaths) && 'allowed_paths',
|
|
634
|
-
!sameStringSet(input.forbiddenPaths, planStep.forbiddenPaths) && 'forbidden_paths',
|
|
635
|
-
!sameStringSet(input.checks, planStep.checks) && 'check_ids',
|
|
636
|
-
].filter((value): value is string => Boolean(value));
|
|
637
|
-
if (mismatches.length > 0) {
|
|
638
|
-
return buildFacadeResult({
|
|
639
|
-
status: 'blocked',
|
|
640
|
-
summary: `PLAN_STEP_WORK_CONTRACT_MISMATCH: ${input.planId}/${input.planStepId} conflicts with frozen Plan step field(s): ${mismatches.join(', ')}. Replan instead of widening or narrowing Work admission.`,
|
|
641
|
-
data: { executionStarted: false, workContractCreated: false, planId: input.planId, planStepId: input.planStepId, mismatches },
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
const effectiveAcceptanceCriteria = planStep?.acceptanceCriteria ?? input.acceptanceCriteria ?? [];
|
|
646
|
-
const effectiveAllowedPaths = planStep?.allowedPaths ?? input.allowedPaths ?? [];
|
|
647
|
-
const effectiveForbiddenPaths = planStep?.forbiddenPaths ?? input.forbiddenPaths ?? [];
|
|
648
|
-
const effectiveChecks = planStep?.checks ?? input.checks ?? [];
|
|
602
|
+
// Work scope is authored by the caller. A referenced Plan item contributes no
|
|
603
|
+
// objective, paths, checks or acceptance criteria.
|
|
604
|
+
const effectiveObjective = input.objective;
|
|
605
|
+
const effectiveAcceptanceCriteria = input.acceptanceCriteria ?? [];
|
|
606
|
+
const effectiveAllowedPaths = input.allowedPaths ?? [];
|
|
607
|
+
const effectiveForbiddenPaths = input.forbiddenPaths ?? [];
|
|
608
|
+
const effectiveChecks = input.checks ?? [];
|
|
649
609
|
const normalized = normalizeCheckIds(effectiveChecks, available);
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
610
|
+
const invalidLineageWork = activeAdmissionSnapshot.invalid.find((candidate) =>
|
|
611
|
+
(input.relatedWorkId && candidate.workId === input.relatedWorkId));
|
|
612
|
+
if (invalidLineageWork) {
|
|
613
|
+
return buildFacadeResult({
|
|
614
|
+
status: 'blocked',
|
|
615
|
+
summary: `WORK_ADMISSION_INVALID_SEMANTIC_AUTHORITY: ${invalidLineageWork.workId} is malformed but still claims the requested durable Plan/Requirement lineage. Repair that exact authority before creating a sibling Work.`,
|
|
616
|
+
data: {
|
|
617
|
+
executionStarted: false,
|
|
618
|
+
workContractCreated: false,
|
|
619
|
+
invalidWorkId: invalidLineageWork.workId,
|
|
620
|
+
invalidWorkError: invalidLineageWork.error,
|
|
621
|
+
planId: resolvedPlanId,
|
|
622
|
+
planStepId: resolvedPlanStepId,
|
|
623
|
+
requirementId: effectiveRequirementId,
|
|
624
|
+
},
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
const newWorkWillBeIsolated = workspaceMode === 'isolated';
|
|
628
|
+
const invalidSharedWorkspaceOwner = !newWorkWillBeIsolated
|
|
629
|
+
? activeAdmissionSnapshot.invalid.find((candidate) => candidate.isolation === 'shared'
|
|
630
|
+
&& (!candidate.checkoutId || candidate.checkoutId === ctx.checkoutId))
|
|
631
|
+
: undefined;
|
|
632
|
+
if (invalidSharedWorkspaceOwner) {
|
|
633
|
+
return buildFacadeResult({
|
|
634
|
+
status: 'blocked',
|
|
635
|
+
summary: `WORK_ADMISSION_INVALID_WORKSPACE_AUTHORITY: ${invalidSharedWorkspaceOwner.workId} is malformed and may still own the shared checkout; isolated placement or exact authority repair is required.`,
|
|
636
|
+
data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidSharedWorkspaceOwner.workId, invalidWorkError: invalidSharedWorkspaceOwner.error },
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
// Requirement membership is portfolio ownership, not semantic Work identity.
|
|
640
|
+
// Only an explicit related Work may select an existing Work authority. A Plan
|
|
641
|
+
// item never selects, owns or reuses Work. Siblings under one Requirement
|
|
642
|
+
// remain unrelated for semantic admission and meet only in
|
|
643
|
+
// placement/resource arbitration.
|
|
644
|
+
const deterministicTarget = input.relatedWorkId ? explicitRelatedWork : undefined;
|
|
645
|
+
const requestedRelation = input.workRelation;
|
|
662
646
|
// Only strong semantic bindings participate in ownership resolution. An
|
|
663
647
|
// unrelated active Work or a checkout writer is a placement fact, not a
|
|
664
648
|
// semantic candidate for continue/extend/parallel/new_goal.
|
|
665
649
|
const candidateWorks = [...new Map(
|
|
666
|
-
[explicitRelatedWork
|
|
650
|
+
[explicitRelatedWork]
|
|
667
651
|
.filter((candidate): candidate is WorkContract => Boolean(candidate))
|
|
668
652
|
.map((candidate) => [candidate.workId, candidate]),
|
|
669
653
|
).values()].slice(0, 8);
|
|
@@ -684,39 +668,18 @@ export function startGoalWorkloop(
|
|
|
684
668
|
evidenceRefs: target?.evidenceRefs ?? [],
|
|
685
669
|
suggestedNextActions: target
|
|
686
670
|
? [{
|
|
687
|
-
label: '
|
|
671
|
+
label: 'Inspect existing Work',
|
|
688
672
|
tool: 'rh_work',
|
|
689
|
-
operation: '
|
|
673
|
+
operation: 'get',
|
|
690
674
|
payload: { work_id: target.workId },
|
|
691
675
|
risk: 'readonly',
|
|
692
|
-
confidence:
|
|
693
|
-
reason: 'Resolve intent before execution;
|
|
676
|
+
confidence: explicitRelatedWork ? 'high' : 'medium',
|
|
677
|
+
reason: 'Resolve intent before execution; semantic Work inspection does not progress lifecycle state.',
|
|
694
678
|
}]
|
|
695
679
|
: [],
|
|
696
680
|
rawAvailable: false,
|
|
697
681
|
});
|
|
698
682
|
|
|
699
|
-
if (boundPlanStepWorks.length > 1) {
|
|
700
|
-
return resolutionRequired(`PLAN_STEP_MULTIPLE_PRIMARY_WORKS: ${input.planId}/${input.planStepId} has ${boundPlanStepWorks.length} active primary Work records and requires repair before execution.`);
|
|
701
|
-
}
|
|
702
|
-
if (planStep?.workId && !explicitPlanStepWork) {
|
|
703
|
-
return buildFacadeResult({
|
|
704
|
-
status: 'blocked',
|
|
705
|
-
summary: `PLAN_STEP_BOUND_WORK_MISSING: ${input.planId}/${input.planStepId} is bound to ${planStep.workId}, but that Work is not active. Repair the exact binding; do not create a replacement Work.`,
|
|
706
|
-
data: { executionStarted: false, workContractCreated: false, planId: input.planId, planStepId: input.planStepId, boundWorkId: planStep.workId, repairRequired: true },
|
|
707
|
-
suggestedNextActions: [{
|
|
708
|
-
label: 'Diagnose exact Plan step binding',
|
|
709
|
-
tool: 'rh_work',
|
|
710
|
-
operation: 'repair',
|
|
711
|
-
payload: { plan_id: input.planId, plan_step_id: input.planStepId, repair_operation: 'diagnose', dry_run: true },
|
|
712
|
-
risk: 'readonly',
|
|
713
|
-
confidence: 'high',
|
|
714
|
-
reason: 'The Plan step has an exact Work identity but the active Work view cannot resolve it. Diagnose that binding before any replacement Work can be admitted.',
|
|
715
|
-
}],
|
|
716
|
-
rawAvailable: false,
|
|
717
|
-
});
|
|
718
|
-
}
|
|
719
|
-
|
|
720
683
|
if ((input.requestedBy ?? 'chatgpt') === 'scheduler') {
|
|
721
684
|
return resolutionRequired(
|
|
722
685
|
deterministicTarget
|
|
@@ -725,42 +688,17 @@ export function startGoalWorkloop(
|
|
|
725
688
|
);
|
|
726
689
|
}
|
|
727
690
|
|
|
728
|
-
if (
|
|
729
|
-
return buildFacadeResult({
|
|
730
|
-
status: 'ok',
|
|
731
|
-
summary: `PLAN_STEP_REUSES_ACTIVE_WORK: ${input.planId}/${input.planStepId} already executes as ${planStepWork.workId}; reuse that Work instead of creating another.`,
|
|
732
|
-
data: {
|
|
733
|
-
executionStarted: false,
|
|
734
|
-
workContractCreated: false,
|
|
735
|
-
admissionDecision: 'reuse_existing',
|
|
736
|
-
resolutionRequired: false,
|
|
737
|
-
work: summarizeWorkContract(planStepWork),
|
|
738
|
-
},
|
|
739
|
-
evidenceRefs: planStepWork.evidenceRefs,
|
|
740
|
-
suggestedNextActions: [{ label: 'Continue existing Work', tool: 'rh_work', operation: 'continue', payload: { work_id: planStepWork.workId }, risk: 'readonly', confidence: 'high' }],
|
|
741
|
-
rawAvailable: false,
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
if (requestedRelation === 'continue' || requestedRelation === 'extend') {
|
|
691
|
+
if ((requestedRelation === 'continue' || requestedRelation === 'extend') && !terminalContinuationSource) {
|
|
746
692
|
if (!deterministicTarget) {
|
|
747
|
-
return resolutionRequired(`${requestedRelation.toUpperCase()}_TARGET_REQUIRED: select related_work_id
|
|
748
|
-
}
|
|
749
|
-
if (requestedRelation === 'extend' && deterministicTarget.planId) {
|
|
750
|
-
return resolutionRequired(
|
|
751
|
-
`PLAN_EXTENSION_REQUIRES_REPLAN: Work ${deterministicTarget.workId} is governed by Plan ${deterministicTarget.planId}; record additional discovery in scopeEvidence, but update the authoritative Plan before widening policy or acceptance scope.`,
|
|
752
|
-
deterministicTarget,
|
|
753
|
-
);
|
|
693
|
+
return resolutionRequired(`${requestedRelation.toUpperCase()}_TARGET_REQUIRED: select related_work_id before execution.`);
|
|
754
694
|
}
|
|
755
695
|
const selected = requestedRelation === 'extend'
|
|
756
696
|
? updateWorkContract(ctx.workStore, deterministicTarget.workId, {
|
|
757
697
|
acceptanceCriteria: [...new Set([...deterministicTarget.acceptanceCriteria, ...(input.acceptanceCriteria ?? [])])].slice(0, 30),
|
|
758
|
-
allowedPaths: [...new Set([...deterministicTarget.allowedPaths, ...(input.allowedPaths ?? [])])].slice(0, 50),
|
|
759
|
-
forbiddenPaths: [...new Set([...deterministicTarget.forbiddenPaths, ...(input.forbiddenPaths ?? [])])].slice(0, 50),
|
|
760
698
|
checks: [...new Set([...deterministicTarget.checks, ...normalized.validCheckIds])].slice(0, 30),
|
|
761
699
|
scopeEvidence: {
|
|
762
700
|
initialLikelyPaths: [...new Set([
|
|
763
|
-
...(deterministicTarget.scopeEvidence?.initialLikelyPaths ??
|
|
701
|
+
...(deterministicTarget.scopeEvidence?.initialLikelyPaths ?? []),
|
|
764
702
|
...(input.initialLikelyPaths ?? input.allowedPaths ?? []),
|
|
765
703
|
])].slice(0, 100),
|
|
766
704
|
inspectedPaths: deterministicTarget.scopeEvidence?.inspectedPaths ?? [],
|
|
@@ -782,7 +720,7 @@ export function startGoalWorkloop(
|
|
|
782
720
|
work: summarizeWorkContract(selected),
|
|
783
721
|
},
|
|
784
722
|
evidenceRefs: selected.evidenceRefs,
|
|
785
|
-
suggestedNextActions: [{ label: '
|
|
723
|
+
suggestedNextActions: [{ label: 'Read existing Work context', tool: 'rh_work', operation: 'get', payload: { work_id: selected.workId }, risk: 'readonly', confidence: 'high' }],
|
|
786
724
|
rawAvailable: false,
|
|
787
725
|
});
|
|
788
726
|
}
|
|
@@ -793,112 +731,72 @@ export function startGoalWorkloop(
|
|
|
793
731
|
);
|
|
794
732
|
}
|
|
795
733
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
if (
|
|
734
|
+
// Repository topology is caller-owned. Forge records the selected placement
|
|
735
|
+
// and lets concrete WorkHandle/Lease/Git authorities report real conflicts at
|
|
736
|
+
// the mutation boundary instead of inferring isolation from semantic context.
|
|
737
|
+
const needsWorktree = workspaceMode === 'isolated';
|
|
738
|
+
const requestedWorkId = input.workId?.trim();
|
|
739
|
+
if (requestedWorkId && !/^work-[a-z0-9][a-z0-9-]{0,199}$/i.test(requestedWorkId)) {
|
|
802
740
|
return buildFacadeResult({
|
|
803
741
|
status: 'blocked',
|
|
804
|
-
summary:
|
|
805
|
-
data: {
|
|
806
|
-
executionStarted: false,
|
|
807
|
-
workContractCreated: false,
|
|
808
|
-
conflictType: 'workspace_single_writer_invariant',
|
|
809
|
-
existingWork: summarizeWorkContract(workspaceOwner),
|
|
810
|
-
},
|
|
811
|
-
evidenceRefs: workspaceOwner.evidenceRefs,
|
|
812
|
-
rawAvailable: false,
|
|
742
|
+
summary: 'WORK_ID_INVALID: explicit Work ids must use the canonical work-<slug> form.',
|
|
743
|
+
data: { executionStarted: false, workContractCreated: false },
|
|
813
744
|
});
|
|
814
745
|
}
|
|
815
|
-
const generatedWorkId = workIdFor(
|
|
816
|
-
if (input.planId || input.planStepId) {
|
|
817
|
-
if (!input.planId || !input.planStepId || !ctx.planStore || !ctx.sourceRevision || !plan || !planStep) {
|
|
818
|
-
return buildFacadeResult({ status: 'blocked', summary: 'PLAN_CONTEXT_REQUIRED: plan_id, plan_step_id, an executable Plan step and a current source revision are required.', data: { executionStarted: false } });
|
|
819
|
-
}
|
|
820
|
-
if (plan.status !== 'approved' && plan.status !== 'executing') {
|
|
821
|
-
return buildFacadeResult({ status: 'blocked', summary: `PLAN_NOT_EXECUTABLE: ${plan.planId} is ${plan.status}`, data: { executionStarted: false, planId: plan.planId } });
|
|
822
|
-
}
|
|
823
|
-
if (plan.sourceRevision !== ctx.sourceRevision) {
|
|
824
|
-
const invalidated = claimPlanStepForWork(ctx.planStore, {
|
|
825
|
-
planId: input.planId,
|
|
826
|
-
stepId: input.planStepId,
|
|
827
|
-
workId: generatedWorkId,
|
|
828
|
-
sourceRevision: ctx.sourceRevision,
|
|
829
|
-
});
|
|
830
|
-
return buildFacadeResult({
|
|
831
|
-
status: 'blocked',
|
|
832
|
-
summary: `PLAN_SOURCE_DRIFT: ${invalidated.planId} was invalidated because its source revision no longer matches. Replan before execution.`,
|
|
833
|
-
data: { planId: invalidated.planId, executionStarted: false, workContractCreated: false, replanRequired: true },
|
|
834
|
-
});
|
|
835
|
-
}
|
|
836
|
-
const unresolved = planStep.dependencies.filter((dependency) => plan.steps.find((candidate) => candidate.id === dependency)?.status !== 'completed');
|
|
837
|
-
if (unresolved.length > 0) {
|
|
838
|
-
return buildFacadeResult({ status: 'blocked', summary: `PLAN_STEP_DEPENDENCIES_PENDING: ${unresolved.join(', ')}`, data: { executionStarted: false, workContractCreated: false, planId: plan.planId, planStepId: planStep.id } });
|
|
839
|
-
}
|
|
840
|
-
if (planStep.status === 'completed') {
|
|
841
|
-
return buildFacadeResult({ status: 'blocked', summary: `PLAN_STEP_ALREADY_COMPLETED: ${planStep.id}`, data: { executionStarted: false, workContractCreated: false, planId: plan.planId, planStepId: planStep.id } });
|
|
842
|
-
}
|
|
843
|
-
}
|
|
746
|
+
const generatedWorkId = requestedWorkId ?? workIdFor(effectiveObjective);
|
|
844
747
|
const initialSuggestedNextActions = suggestedForWorkIdentity(
|
|
845
748
|
generatedWorkId,
|
|
846
749
|
normalized.validCheckIds,
|
|
847
750
|
normalized.suggestedNextActions,
|
|
848
751
|
);
|
|
849
|
-
// allowed_paths and initial_likely_paths are policy/discovery fences, not proof
|
|
850
|
-
// that this Work owns a repository mutation. External-effect Work therefore
|
|
851
|
-
// needs either an explicit repository_change semantic choice or concrete
|
|
852
|
-
// expected source-change evidence before Forge treats it as repository_change.
|
|
853
|
-
const repositoryChangeIntent = input.workKind === 'repository_change'
|
|
854
|
-
|| (input.modeInput.expectedFiles ?? 0) > 0
|
|
855
|
-
|| (input.modeInput.expectedChangedLines ?? 0) > 0;
|
|
856
|
-
const typedRecoverableReadOnlyReview = input.workKind === undefined
|
|
857
|
-
&& input.modeInput.mutation === false
|
|
858
|
-
&& input.modeInput.requiresInvestigation === true
|
|
859
|
-
&& input.modeInput.requiresRecovery === true;
|
|
860
|
-
const resolvedWorkKind = input.workKind ?? (
|
|
861
|
-
typedRecoverableReadOnlyReview
|
|
862
|
-
? 'read_only_review'
|
|
863
|
-
: input.modeInput.requiresExternalEffect === true
|
|
864
|
-
&& !repositoryChangeIntent
|
|
865
|
-
? (input.modeInput.remoteWrite === true ? 'remote_effect' : 'local_effect')
|
|
866
|
-
: 'repository_change'
|
|
867
|
-
);
|
|
868
752
|
const remoteDeliveryRequired = resolvedWorkKind === 'repository_change'
|
|
869
|
-
&& input.
|
|
870
|
-
&& input.
|
|
753
|
+
&& input.request.requiresExternalEffect === true
|
|
754
|
+
&& input.request.remoteWrite === true;
|
|
755
|
+
const effectiveConstraints: WorkContract['constraints'] = {
|
|
756
|
+
...canonicalConstraints,
|
|
757
|
+
...(remoteDeliveryRequired ? { remoteDeliveryRequired: true } : {}),
|
|
758
|
+
};
|
|
759
|
+
const worktreeReason = needsWorktree
|
|
760
|
+
? 'Caller explicitly selected isolated repository placement.'
|
|
761
|
+
: 'Caller selected the current repository checkout.';
|
|
762
|
+
const forgeInstanceId = ctx.workStore.controllerHome
|
|
763
|
+
? readForgeInstanceIdentity(ctx.workStore.controllerHome)?.instanceId
|
|
764
|
+
: undefined;
|
|
871
765
|
const work = createWorkContract(ctx.workStore, {
|
|
872
766
|
workId: generatedWorkId,
|
|
873
767
|
repoId: ctx.repoId,
|
|
874
768
|
checkoutId: needsWorktree ? undefined : ctx.checkoutId,
|
|
769
|
+
executionPlacement: executionPlacement({
|
|
770
|
+
...(forgeInstanceId ? { forgeInstanceId } : {}),
|
|
771
|
+
repositoryId: ctx.repoId,
|
|
772
|
+
...(!needsWorktree && ctx.checkoutId ? { checkoutId: ctx.checkoutId } : {}),
|
|
773
|
+
}),
|
|
875
774
|
principalId: ctx.principalId,
|
|
876
775
|
controllerInstanceId: ctx.controllerInstanceId,
|
|
877
776
|
baseRevision: ctx.sourceRevision,
|
|
777
|
+
repositoryBaseState: ctx.sourceBaseState ?? (ctx.sourceRevision ? 'revision' : undefined),
|
|
878
778
|
workspaceFingerprint: needsWorktree ? undefined : ctx.workspaceFingerprint,
|
|
879
|
-
|
|
880
|
-
routeDecision,
|
|
881
|
-
mode: executionMode,
|
|
882
|
-
objective: input.objective,
|
|
779
|
+
objective: effectiveObjective,
|
|
883
780
|
acceptanceCriteria: effectiveAcceptanceCriteria,
|
|
884
|
-
constraints:
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
},
|
|
888
|
-
risk: workRiskFor(input),
|
|
781
|
+
constraints: effectiveConstraints,
|
|
782
|
+
risk: workRisk,
|
|
783
|
+
engineeringContext,
|
|
889
784
|
// Remote delivery is a risk/effect dimension, not a reason to erase a
|
|
890
785
|
// repository-change Work's semantic identity. Pure external actions with no
|
|
891
786
|
// repository-change signal remain remote_effect and keep plugin receipt semantics.
|
|
892
787
|
workKind: resolvedWorkKind,
|
|
893
|
-
|
|
788
|
+
dispatchState: 'running',
|
|
894
789
|
phase: 'implementation',
|
|
895
790
|
issueId: input.issueId,
|
|
896
791
|
taskId: input.taskId,
|
|
897
792
|
requirementId: effectiveRequirementId,
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
793
|
+
requirementRevision: effectiveRequirementRecord ? currentRequirementSemanticRevision(effectiveRequirementRecord.value) : undefined,
|
|
794
|
+
predecessorWorkId: terminalContinuationSource?.workId,
|
|
795
|
+
planId: resolvedPlanId,
|
|
796
|
+
planRevision: plan ? currentPlanSemanticRevision(plan) : undefined,
|
|
797
|
+
planStepId: resolvedPlanStepId,
|
|
798
|
+
planSourceRevision: resolvedPlanStepId ? plan?.sourceRevision : undefined,
|
|
799
|
+
scopeSummary: input.request.scopeClear ? 'scope declared at start' : 'scope incomplete',
|
|
902
800
|
scopeEvidence: {
|
|
903
801
|
initialLikelyPaths: [...new Set(input.initialLikelyPaths ?? effectiveAllowedPaths)].slice(0, 100),
|
|
904
802
|
inspectedPaths: [],
|
|
@@ -908,18 +806,9 @@ export function startGoalWorkloop(
|
|
|
908
806
|
allowedPaths: effectiveAllowedPaths,
|
|
909
807
|
forbiddenPaths: effectiveForbiddenPaths,
|
|
910
808
|
checks: normalized.validCheckIds,
|
|
911
|
-
driver: {
|
|
912
|
-
preferred: executionMode === 'direct_control'
|
|
913
|
-
? 'direct_edit'
|
|
914
|
-
: input.modeInput.requiresWorker === true ? 'external_controller' : needsWorktree ? 'isolated_worktree' : 'direct_edit',
|
|
915
|
-
allowWorker: false,
|
|
916
|
-
allowDirectEdit: executionMode === 'direct_control' || (input.modeInput.requiresWorker !== true && !needsWorktree),
|
|
917
|
-
},
|
|
918
809
|
worktreePolicy: {
|
|
919
810
|
required: needsWorktree,
|
|
920
|
-
reason:
|
|
921
|
-
? 'Isolation was explicitly requested or required for parallel execution.'
|
|
922
|
-
: 'Current workspace is the stability-first default; isolation remains opt-in.',
|
|
811
|
+
reason: worktreeReason,
|
|
923
812
|
},
|
|
924
813
|
worktreeRef: undefined,
|
|
925
814
|
evidencePolicy: {
|
|
@@ -928,8 +817,8 @@ export function startGoalWorkloop(
|
|
|
928
817
|
maxEvidenceRefs: 20,
|
|
929
818
|
},
|
|
930
819
|
approvalPolicy: {
|
|
931
|
-
required: input.
|
|
932
|
-
reasons: input.
|
|
820
|
+
required: input.request.requiresApproval === true || input.request.requiresUserApproval === true,
|
|
821
|
+
reasons: input.request.requiresApproval || input.request.requiresUserApproval ? ['approval requested at start'] : [],
|
|
933
822
|
confirmed: input.approvalConfirmed === true,
|
|
934
823
|
},
|
|
935
824
|
recoveryPolicy: {
|
|
@@ -938,52 +827,26 @@ export function startGoalWorkloop(
|
|
|
938
827
|
handoffOnAmbiguity: true,
|
|
939
828
|
},
|
|
940
829
|
requestedBy: input.requestedBy ?? 'chatgpt',
|
|
941
|
-
|
|
830
|
+
requestId: input.requestId?.trim() || undefined,
|
|
831
|
+
evidenceRefs: [initialEvidence(effectiveObjective)],
|
|
942
832
|
policyDecisions: policy ? [policy] : [],
|
|
943
833
|
suggestedNextActions: initialSuggestedNextActions,
|
|
944
|
-
continuationPrompt: `Continue work ${ctx.repoId}: ${
|
|
834
|
+
continuationPrompt: `Continue work ${ctx.repoId}: ${effectiveObjective.slice(0, 200)}`,
|
|
945
835
|
});
|
|
946
836
|
|
|
947
|
-
if (input.planId && input.planStepId && ctx.planStore && ctx.sourceRevision) {
|
|
948
|
-
try {
|
|
949
|
-
const claimed = claimPlanStepForWork(ctx.planStore, {
|
|
950
|
-
planId: input.planId,
|
|
951
|
-
stepId: input.planStepId,
|
|
952
|
-
workId: work.workId,
|
|
953
|
-
sourceRevision: ctx.sourceRevision,
|
|
954
|
-
});
|
|
955
|
-
if (claimed.status === 'invalidated_by_drift') {
|
|
956
|
-
return buildFacadeResult({
|
|
957
|
-
status: 'blocked',
|
|
958
|
-
summary: `PLAN_SOURCE_DRIFT: ${claimed.planId} was invalidated because its source revision no longer matches. Repair or replan before execution.`,
|
|
959
|
-
data: { executionStarted: false, workContractCreated: true, work: summarizeWorkContract(work), planId: claimed.planId, canonicalWorkRetained: true, replanRequired: true },
|
|
960
|
-
});
|
|
961
|
-
}
|
|
962
|
-
} catch (error) {
|
|
963
|
-
return buildFacadeResult({
|
|
964
|
-
status: 'blocked',
|
|
965
|
-
summary: error instanceof Error ? error.message : 'PLAN_STEP_CLAIM_FAILED',
|
|
966
|
-
data: { planId: input.planId, planStepId: input.planStepId, executionStarted: false, workContractCreated: true, work: summarizeWorkContract(work), canonicalWorkRetained: true },
|
|
967
|
-
});
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
|
|
971
837
|
return buildFacadeResult({
|
|
972
838
|
status: 'ok',
|
|
973
|
-
summary:
|
|
974
|
-
? `
|
|
975
|
-
: `
|
|
839
|
+
summary: terminalContinuationSource
|
|
840
|
+
? `Successor Work ${work.workId} continues semantic lineage from terminal ${terminalContinuationSource.workId}${resolvedPlanId && resolvedPlanStepId ? ` at ${resolvedPlanId}/${resolvedPlanStepId}` : ''}.`
|
|
841
|
+
: `Work started as ${work.workId}.`,
|
|
976
842
|
data: {
|
|
977
|
-
mode
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
missingContractFields: [],
|
|
985
|
-
requiresWork: true,
|
|
986
|
-
routeDecision,
|
|
843
|
+
// Caller-visible placement facts only. Execution mode tokens, route reasons
|
|
844
|
+
// and RouteDecision are legacy orchestration metadata, not Work semantics.
|
|
845
|
+
placement: {
|
|
846
|
+
workContractCreated: true,
|
|
847
|
+
worktreeRequired: work.worktreePolicy.required,
|
|
848
|
+
isolated: work.worktreePolicy.required,
|
|
849
|
+
requiresRecovery: work.recoveryPolicy.allowSelfHealing === true || false,
|
|
987
850
|
},
|
|
988
851
|
workContractCreated: true,
|
|
989
852
|
work: summarizeWorkContract(work),
|
|
@@ -1009,10 +872,10 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1009
872
|
});
|
|
1010
873
|
}
|
|
1011
874
|
|
|
1012
|
-
if (work
|
|
875
|
+
if (semanticWorkState(work) !== 'open') {
|
|
1013
876
|
return buildFacadeResult({
|
|
1014
877
|
status: 'blocked',
|
|
1015
|
-
summary: `WorkContract ${work.workId} is terminal (${work
|
|
878
|
+
summary: `WorkContract ${work.workId} is terminal (${semanticWorkState(work)}); continue is not allowed.`,
|
|
1016
879
|
data: { work: summarizeWorkContract(work) },
|
|
1017
880
|
suggestedNextActions: [
|
|
1018
881
|
{
|
|
@@ -1026,35 +889,123 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1026
889
|
});
|
|
1027
890
|
}
|
|
1028
891
|
|
|
1029
|
-
if ((
|
|
892
|
+
if (semanticWorkState(work) === 'open' && hasSettledWorkDeliveryReceipt(work)) {
|
|
1030
893
|
return buildFacadeResult({
|
|
1031
|
-
status: '
|
|
1032
|
-
summary: `
|
|
1033
|
-
data: {
|
|
894
|
+
status: 'ok',
|
|
895
|
+
summary: `Continue: physical delivery is settled for ${work.workId}; semantic Work remains open for explicit Controller judgment.`,
|
|
896
|
+
data: {
|
|
897
|
+
work: summarizeWorkContract(work),
|
|
898
|
+
backgroundCompleted: false,
|
|
899
|
+
nextStep: 'semantic_decision',
|
|
900
|
+
deliverySettled: true,
|
|
901
|
+
semanticWorkState: 'open',
|
|
902
|
+
},
|
|
903
|
+
evidenceRefs: work.evidenceRefs.slice(0, 5),
|
|
1034
904
|
});
|
|
1035
905
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
906
|
+
|
|
907
|
+
if (input.verifiedEngineeringEvidence) {
|
|
908
|
+
const sourceIdentity = ctx.sourceRevision?.trim()
|
|
909
|
+
? { kind: 'revision' as const, revision: ctx.sourceRevision.trim() }
|
|
910
|
+
: ctx.sourceBaseState === 'unborn'
|
|
911
|
+
? { kind: 'unborn' as const }
|
|
912
|
+
: { kind: 'unknown' as const };
|
|
913
|
+
let refreshedEngineeringContext;
|
|
914
|
+
try {
|
|
915
|
+
refreshedEngineeringContext = buildEngineeringContextReceipt({
|
|
916
|
+
risk: work.risk,
|
|
917
|
+
sourceIdentity,
|
|
918
|
+
evidence: input.verifiedEngineeringEvidence,
|
|
919
|
+
recordedAt: nowIso(ctx),
|
|
920
|
+
});
|
|
921
|
+
} catch (error) {
|
|
1039
922
|
return buildFacadeResult({
|
|
1040
923
|
status: 'blocked',
|
|
1041
|
-
summary:
|
|
1042
|
-
data: { work: summarizeWorkContract(work),
|
|
924
|
+
summary: error instanceof Error ? error.message : 'ENGINEERING_CONTEXT_INVALID',
|
|
925
|
+
data: { work: summarizeWorkContract(work), engineeringEvidenceUpdated: false },
|
|
1043
926
|
});
|
|
1044
927
|
}
|
|
928
|
+
work = updateWorkContract(ctx.workStore, work.workId, { engineeringContext: refreshedEngineeringContext });
|
|
1045
929
|
}
|
|
1046
930
|
|
|
931
|
+
if (input.engineeringBlocker) {
|
|
932
|
+
if (!work.engineeringContext || work.engineeringContext.sourceIdentity.kind !== 'revision') {
|
|
933
|
+
return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_SOURCE_IDENTITY_REQUIRED', data: { work: summarizeWorkContract(work) } });
|
|
934
|
+
}
|
|
935
|
+
let blocker;
|
|
936
|
+
let linkedWork: WorkContract | undefined;
|
|
937
|
+
try {
|
|
938
|
+
let linkedWorkId: string | undefined;
|
|
939
|
+
if (input.engineeringBlocker.classification === 'unrelated') {
|
|
940
|
+
// Forge does not decompose work on the model's behalf. An unrelated blocker
|
|
941
|
+
// must name the Work that already owns it; the caller creates that Work
|
|
942
|
+
// explicitly (rh_work start) and passes its exact id here.
|
|
943
|
+
const declaredLinkedWorkId = input.engineeringBlocker.linkedWorkId?.trim();
|
|
944
|
+
if (!declaredLinkedWorkId) {
|
|
945
|
+
return buildFacadeResult({
|
|
946
|
+
status: 'blocked',
|
|
947
|
+
summary: 'ENGINEERING_BLOCKER_LINKED_WORK_REQUIRED: an unrelated blocker must name the Work that owns it via linked_work_id; Forge never creates a child Work automatically.',
|
|
948
|
+
data: { work: summarizeWorkContract(work), linkedWorkCreated: false },
|
|
949
|
+
suggestedNextActions: [{
|
|
950
|
+
label: 'Start the owning Work explicitly',
|
|
951
|
+
tool: 'rh_work',
|
|
952
|
+
operation: 'start',
|
|
953
|
+
payload: { objective: `Resolve unrelated blocker ${input.engineeringBlocker.blockerId}: ${input.engineeringBlocker.rationale}` },
|
|
954
|
+
risk: 'readonly',
|
|
955
|
+
confidence: 'medium',
|
|
956
|
+
}],
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
linkedWork = getWorkContract(ctx.workStore, declaredLinkedWorkId);
|
|
960
|
+
if (!linkedWork) {
|
|
961
|
+
return buildFacadeResult({
|
|
962
|
+
status: 'blocked',
|
|
963
|
+
summary: `ENGINEERING_BLOCKER_LINKED_WORK_UNKNOWN: ${declaredLinkedWorkId} is not a readable Work.`,
|
|
964
|
+
data: { work: summarizeWorkContract(work), linkedWorkId: declaredLinkedWorkId, linkedWorkCreated: false },
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
linkedWorkId = linkedWork.workId;
|
|
968
|
+
}
|
|
969
|
+
blocker = buildEngineeringBlockerDispositionReceipt({
|
|
970
|
+
sourceRevision: work.engineeringContext.sourceIdentity.revision,
|
|
971
|
+
blockerId: input.engineeringBlocker.blockerId,
|
|
972
|
+
classification: input.engineeringBlocker.classification,
|
|
973
|
+
semanticScopeKeys: input.engineeringBlocker.semanticScopeKeys ?? work.engineeringContext.semanticScope?.keys ?? [],
|
|
974
|
+
...(linkedWork ? { linkedWorkId: linkedWork.workId } : {}),
|
|
975
|
+
rationale: input.engineeringBlocker.rationale,
|
|
976
|
+
recordedAt: nowIso(ctx),
|
|
977
|
+
});
|
|
978
|
+
const nextEngineering = applyEngineeringBlockerDisposition(work.engineeringContext, blocker);
|
|
979
|
+
work = updateWorkContract(ctx.workStore, work.workId, { engineeringContext: nextEngineering });
|
|
980
|
+
} catch (error) {
|
|
981
|
+
return buildFacadeResult({ status: 'blocked', summary: error instanceof Error ? error.message : 'ENGINEERING_BLOCKER_INVALID', data: { work: summarizeWorkContract(work) } });
|
|
982
|
+
}
|
|
983
|
+
if (blocker.action === 'extend_candidate') {
|
|
984
|
+
work = appendWorkEvidence(ctx.workStore, work.workId, {
|
|
985
|
+
title: 'same-root candidate scope extension',
|
|
986
|
+
summary: `Blocker ${blocker.blockerId} remains inside the current architecture authority; continue the exact candidate without Design re-entry or sibling Work.`,
|
|
987
|
+
detailLevel: 'summary',
|
|
988
|
+
});
|
|
989
|
+
} else {
|
|
990
|
+
work = appendWorkEvidence(ctx.workStore, work.workId, {
|
|
991
|
+
title: 'engineering blocker observation',
|
|
992
|
+
summary: `Recorded ${blocker.classification} blocker ${blocker.blockerId}; it is model guidance, not a Work continuation gate.`,
|
|
993
|
+
detailLevel: 'summary',
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
if ((input.reviewFindings?.length ?? 0) > 0 && work.workKind !== 'read_only_review') {
|
|
999
|
+
return buildFacadeResult({
|
|
1000
|
+
status: 'blocked',
|
|
1001
|
+
summary: `READ_ONLY_REVIEW_FINDINGS_KIND_REQUIRED: ${work.workId} is ${work.workKind}, not read_only_review.`,
|
|
1002
|
+
data: { work: summarizeWorkContract(work), reviewFindingsRecorded: false },
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1047
1005
|
const explicitPolicyScope = input.allowedPaths !== undefined
|
|
1048
1006
|
|| input.forbiddenPaths !== undefined
|
|
1049
1007
|
|| input.checks !== undefined;
|
|
1050
1008
|
if (explicitPolicyScope) {
|
|
1051
|
-
if (work.planId) {
|
|
1052
|
-
return buildFacadeResult({
|
|
1053
|
-
status: 'blocked',
|
|
1054
|
-
summary: `PLAN_EXTENSION_REQUIRES_REPLAN: Work ${work.workId} is governed by Plan ${work.planId}; continue cannot widen frozen policy scope. Replan the authoritative Plan instead.`,
|
|
1055
|
-
data: { work: summarizeWorkContract(work), planId: work.planId, policyScopeUpdated: false },
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
1009
|
const normalizedChecks = normalizeCheckIds(input.checks ?? [], ctx.availableChecks ?? []);
|
|
1059
1010
|
if (normalizedChecks.invalidCheckIds.length > 0) {
|
|
1060
1011
|
return buildFacadeResult({
|
|
@@ -1064,12 +1015,10 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1064
1015
|
});
|
|
1065
1016
|
}
|
|
1066
1017
|
work = updateWorkContract(ctx.workStore, work.workId, {
|
|
1067
|
-
allowedPaths: [...new Set([...work.allowedPaths, ...(input.allowedPaths ?? [])])].slice(0, 50),
|
|
1068
|
-
forbiddenPaths: [...new Set([...work.forbiddenPaths, ...(input.forbiddenPaths ?? [])])].slice(0, 50),
|
|
1069
1018
|
checks: [...new Set([...work.checks, ...normalizedChecks.validCheckIds])].slice(0, 30),
|
|
1070
1019
|
scopeEvidence: {
|
|
1071
1020
|
initialLikelyPaths: [...new Set([
|
|
1072
|
-
...(work.scopeEvidence?.initialLikelyPaths ??
|
|
1021
|
+
...(work.scopeEvidence?.initialLikelyPaths ?? []),
|
|
1073
1022
|
...(input.allowedPaths ?? []),
|
|
1074
1023
|
])].slice(0, 100),
|
|
1075
1024
|
inspectedPaths: work.scopeEvidence?.inspectedPaths ?? [],
|
|
@@ -1185,8 +1134,11 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1185
1134
|
}
|
|
1186
1135
|
}
|
|
1187
1136
|
|
|
1188
|
-
const currentCheckRefs =
|
|
1189
|
-
|
|
1137
|
+
const currentCheckRefs = effectiveCurrentWorkVerificationRecords(
|
|
1138
|
+
work,
|
|
1139
|
+
ctx.sourceRevision,
|
|
1140
|
+
ctx.workspaceFingerprint,
|
|
1141
|
+
);
|
|
1190
1142
|
const history = reconcileVerificationHistory(
|
|
1191
1143
|
currentCheckRefs.map((record) => ({ checkId: record.checkId, outcome: record.outcome, recordedAt: record.recordedAt })),
|
|
1192
1144
|
);
|
|
@@ -1201,7 +1153,6 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1201
1153
|
detailLevel: 'summary',
|
|
1202
1154
|
});
|
|
1203
1155
|
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1204
|
-
status: 'running',
|
|
1205
1156
|
phase: 'implementation',
|
|
1206
1157
|
state: 'active',
|
|
1207
1158
|
summary: `Controller chose bounded ${decision} after acceptance failure; implementation may resume before re-verification.`,
|
|
@@ -1224,148 +1175,36 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1224
1175
|
});
|
|
1225
1176
|
}
|
|
1226
1177
|
|
|
1227
|
-
//
|
|
1228
|
-
//
|
|
1229
|
-
|
|
1230
|
-
// authority until newer valid-fail evidence; an unresolved matching handoff is reused.
|
|
1231
|
-
if (history.acceptanceFailures.length > 0 && !explicitAcceptanceRepair && work.recoveryPolicy.handoffOnAmbiguity) {
|
|
1178
|
+
// Acceptance failure is a model/controller decision point, not a human blocker.
|
|
1179
|
+
// Do not manufacture Handoff/UserRequest state for repair vs re-scope.
|
|
1180
|
+
if (history.acceptanceFailures.length > 0 && !explicitAcceptanceRepair) {
|
|
1232
1181
|
const failureReason = `Acceptance checks failed: ${history.acceptanceFailures.join(', ')}`;
|
|
1233
|
-
const latestFailureAt = work.checkRefs
|
|
1234
|
-
.filter((record) => record.outcome === 'valid_fail' && history.acceptanceFailures.includes(record.checkId))
|
|
1235
|
-
.map((record) => record.recordedAt)
|
|
1236
|
-
.sort((left, right) => right.localeCompare(left))[0];
|
|
1237
|
-
const matchingHandoffs = listHandoffItems({ ...ctx.handoffStore, status: 'all', limit: 100 })
|
|
1238
|
-
.filter((item) => (
|
|
1239
|
-
item.workId === input.workId
|
|
1240
|
-
&& item.creationReason === 'ambiguous_outcome'
|
|
1241
|
-
&& item.title === 'Acceptance failure needs review'
|
|
1242
|
-
&& item.reason === failureReason
|
|
1243
|
-
&& (!latestFailureAt || item.createdAt >= latestFailureAt)
|
|
1244
|
-
));
|
|
1245
|
-
const activeHandoff = matchingHandoffs.find((item) => item.status === 'pending' || item.status === 'acknowledged');
|
|
1246
|
-
if (activeHandoff) {
|
|
1247
|
-
return buildFacadeResult({
|
|
1248
|
-
status: 'blocked',
|
|
1249
|
-
summary: `Continue remains paused for ChatGPT review through existing handoff ${activeHandoff.id}.`,
|
|
1250
|
-
data: {
|
|
1251
|
-
work: summarizeWorkContract(work),
|
|
1252
|
-
handoffId: activeHandoff.id,
|
|
1253
|
-
acceptanceFailures: history.acceptanceFailures,
|
|
1254
|
-
infrastructureIssues: history.infrastructureIssues,
|
|
1255
|
-
backgroundCompleted: false,
|
|
1256
|
-
},
|
|
1257
|
-
evidenceRefs: work.evidenceRefs.slice(0, 5),
|
|
1258
|
-
suggestedNextActions: [{
|
|
1259
|
-
label: 'Get handoff',
|
|
1260
|
-
tool: 'rh_inbox',
|
|
1261
|
-
operation: 'get',
|
|
1262
|
-
payload: { handoff_id: activeHandoff.id },
|
|
1263
|
-
risk: 'readonly',
|
|
1264
|
-
confidence: 'high',
|
|
1265
|
-
}],
|
|
1266
|
-
});
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
const resolvedHandoff = matchingHandoffs.find((item) => item.status === 'resolved');
|
|
1270
|
-
if (!resolvedHandoff) {
|
|
1271
|
-
const continuation = buildWorkContinuationSnapshot(work);
|
|
1272
|
-
const handoff = createHandoffItem(ctx.handoffStore, {
|
|
1273
|
-
id: handoffIdFor('continue'),
|
|
1274
|
-
repoId: ctx.repoId,
|
|
1275
|
-
workId: work.workId,
|
|
1276
|
-
title: 'Acceptance failure needs review',
|
|
1277
|
-
severity: 'needs_review',
|
|
1278
|
-
creationReason: 'ambiguous_outcome',
|
|
1279
|
-
reason: failureReason,
|
|
1280
|
-
summary: 'Continue paused; ChatGPT must decide repair vs re-scope.',
|
|
1281
|
-
currentState: {
|
|
1282
|
-
repoId: ctx.repoId,
|
|
1283
|
-
workId: work.workId,
|
|
1284
|
-
mode: work.mode,
|
|
1285
|
-
statusSummary: 'waiting_for_review after acceptance failure',
|
|
1286
|
-
checks: history.acceptanceFailures.map((checkId) => ({ checkId, ok: false, outcome: 'valid_fail' as const })),
|
|
1287
|
-
workSemantics: continuation.semantics,
|
|
1288
|
-
reconciliationRequired: continuation.reconciliationRequired,
|
|
1289
|
-
nextSafeAction: continuation.nextSafeAction,
|
|
1290
|
-
},
|
|
1291
|
-
attemptedActions: ['continue'],
|
|
1292
|
-
evidenceRefs: work.evidenceRefs.slice(0, 5),
|
|
1293
|
-
blockingDecision: 'Decide whether to repair code, adjust acceptance criteria, or stop.',
|
|
1294
|
-
recommendedDecision: 'Inspect evidence and either repair or stop the workloop.',
|
|
1295
|
-
recommendedPrompt: work.continuationPrompt ?? `Continue from work ${work.workId}.`,
|
|
1296
|
-
recommendedContinuationPrompt: continuation.continuationPrompt,
|
|
1297
|
-
suggestedNextActions: [
|
|
1298
|
-
{
|
|
1299
|
-
label: 'Read work context',
|
|
1300
|
-
tool: 'rh_context',
|
|
1301
|
-
operation: 'get',
|
|
1302
|
-
payload: { work_id: work.workId },
|
|
1303
|
-
risk: 'readonly',
|
|
1304
|
-
},
|
|
1305
|
-
],
|
|
1306
|
-
});
|
|
1307
|
-
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1308
|
-
status: 'ready',
|
|
1309
|
-
phase: 'verification',
|
|
1310
|
-
state: 'blocked',
|
|
1311
|
-
summary: `Acceptance failure requires review through handoff ${handoff.id}.`,
|
|
1312
|
-
evidenceRefs: work.evidenceRefs,
|
|
1313
|
-
});
|
|
1314
|
-
const updated = appendWorkHandoffRef(ctx.workStore, work.workId, handoff.id);
|
|
1315
|
-
|
|
1316
|
-
return buildFacadeResult({
|
|
1317
|
-
status: 'blocked',
|
|
1318
|
-
summary: `Continue paused for ChatGPT review; handoff ${handoff.id} created. No background execution pretended.`,
|
|
1319
|
-
data: {
|
|
1320
|
-
work: summarizeWorkContract(updated),
|
|
1321
|
-
handoffId: handoff.id,
|
|
1322
|
-
acceptanceFailures: history.acceptanceFailures,
|
|
1323
|
-
infrastructureIssues: history.infrastructureIssues,
|
|
1324
|
-
backgroundCompleted: false,
|
|
1325
|
-
},
|
|
1326
|
-
evidenceRefs: work.evidenceRefs.slice(0, 5),
|
|
1327
|
-
suggestedNextActions: [
|
|
1328
|
-
{
|
|
1329
|
-
label: 'Get handoff',
|
|
1330
|
-
tool: 'rh_inbox',
|
|
1331
|
-
operation: 'get',
|
|
1332
|
-
payload: { handoff_id: handoff.id },
|
|
1333
|
-
risk: 'readonly',
|
|
1334
|
-
confidence: 'high',
|
|
1335
|
-
},
|
|
1336
|
-
],
|
|
1337
|
-
});
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1341
|
-
status: 'running',
|
|
1342
|
-
phase: 'verification',
|
|
1343
|
-
state: 'active',
|
|
1344
|
-
summary: `Resolved acceptance-failure handoff ${resolvedHandoff.id} authorizes bounded continuation to re-verification.`,
|
|
1345
|
-
evidenceRefs: work.evidenceRefs,
|
|
1346
|
-
});
|
|
1347
|
-
work = getWorkContract(ctx.workStore, work.workId) ?? work;
|
|
1348
1182
|
return buildFacadeResult({
|
|
1349
|
-
status: '
|
|
1350
|
-
summary:
|
|
1183
|
+
status: 'blocked',
|
|
1184
|
+
summary: `${failureReason}. The model must explicitly choose bounded repair or re-scope before continuing; no human Handoff was created.`,
|
|
1351
1185
|
data: {
|
|
1352
1186
|
work: summarizeWorkContract(work),
|
|
1353
|
-
handoffId: resolvedHandoff.id,
|
|
1354
1187
|
acceptanceFailures: history.acceptanceFailures,
|
|
1355
1188
|
infrastructureIssues: history.infrastructureIssues,
|
|
1356
1189
|
backgroundCompleted: false,
|
|
1357
|
-
|
|
1358
|
-
remainingChecks: history.acceptanceFailures,
|
|
1190
|
+
acceptanceFailureDecisionRequired: true,
|
|
1359
1191
|
},
|
|
1360
1192
|
evidenceRefs: work.evidenceRefs.slice(0, 5),
|
|
1361
|
-
suggestedNextActions:
|
|
1193
|
+
suggestedNextActions: [{
|
|
1194
|
+
label: 'Inspect Work before repair or re-scope',
|
|
1195
|
+
tool: 'rh_context',
|
|
1196
|
+
operation: 'get',
|
|
1197
|
+
payload: { work_id: work.workId },
|
|
1198
|
+
risk: 'readonly',
|
|
1199
|
+
confidence: 'high',
|
|
1200
|
+
reason: 'Acceptance evidence is factual input; the model chooses the next capability without a Work lifecycle transition.',
|
|
1201
|
+
}],
|
|
1362
1202
|
});
|
|
1363
1203
|
}
|
|
1364
1204
|
|
|
1365
1205
|
// Infrastructure issues: suggest self-healing, not acceptance failure.
|
|
1366
1206
|
if (history.infrastructureIssues.length > 0) {
|
|
1367
1207
|
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1368
|
-
status: 'running',
|
|
1369
1208
|
phase: 'implementation',
|
|
1370
1209
|
state: 'active',
|
|
1371
1210
|
summary: `Infrastructure issues require repair: ${history.infrastructureIssues.join(', ')}.`,
|
|
@@ -1414,7 +1253,6 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1414
1253
|
},
|
|
1415
1254
|
]).actions;
|
|
1416
1255
|
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1417
|
-
status: 'running',
|
|
1418
1256
|
phase: 'implementation',
|
|
1419
1257
|
state: 'active',
|
|
1420
1258
|
summary: implementationEvidence.reasons.join(' '),
|
|
@@ -1443,24 +1281,15 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1443
1281
|
|
|
1444
1282
|
if (work.checks.length > 0 && history.validPasses.length < work.checks.length) {
|
|
1445
1283
|
const remaining = work.checks.filter((checkId) => !history.validPasses.includes(checkId));
|
|
1446
|
-
const suggested = validateSuggestedNextActions(
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
{ validCheckIds: work.checks },
|
|
1456
|
-
).actions;
|
|
1457
|
-
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1458
|
-
status: 'running',
|
|
1459
|
-
phase: 'verification',
|
|
1460
|
-
state: 'active',
|
|
1461
|
-
summary: `Verification remains for: ${remaining.join(', ')}.`,
|
|
1462
|
-
evidenceRefs: work.evidenceRefs,
|
|
1463
|
-
});
|
|
1284
|
+
const suggested = validateSuggestedNextActions([{
|
|
1285
|
+
label: 'Inspect Work and check evidence',
|
|
1286
|
+
tool: 'rh_context',
|
|
1287
|
+
operation: 'get',
|
|
1288
|
+
payload: { work_id: work.workId },
|
|
1289
|
+
risk: 'readonly',
|
|
1290
|
+
confidence: 'high',
|
|
1291
|
+
reason: 'Registered checks are independent execution evidence; they do not progress Work lifecycle state.',
|
|
1292
|
+
}]).actions;
|
|
1464
1293
|
const updated = updateWorkContract(ctx.workStore, work.workId, {
|
|
1465
1294
|
suggestedNextActions: suggested,
|
|
1466
1295
|
continuationPrompt: input.note
|
|
@@ -1469,12 +1298,12 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1469
1298
|
});
|
|
1470
1299
|
return buildFacadeResult({
|
|
1471
1300
|
status: 'ok',
|
|
1472
|
-
summary: `
|
|
1301
|
+
summary: `Registered check evidence is still missing for: ${remaining.join(', ')}. Run checks through the check capability; Work semantic state is unchanged.`,
|
|
1473
1302
|
data: {
|
|
1474
1303
|
work: summarizeWorkContract(updated),
|
|
1475
1304
|
remainingChecks: remaining,
|
|
1476
1305
|
backgroundCompleted: false,
|
|
1477
|
-
nextStep: '
|
|
1306
|
+
nextStep: 'run_check',
|
|
1478
1307
|
},
|
|
1479
1308
|
suggestedNextActions: suggested,
|
|
1480
1309
|
});
|
|
@@ -1500,7 +1329,6 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1500
1329
|
},
|
|
1501
1330
|
]).actions;
|
|
1502
1331
|
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1503
|
-
status: 'running',
|
|
1504
1332
|
phase: work.workKind === 'read_only_review' ? 'verification' : 'implementation',
|
|
1505
1333
|
state: 'active',
|
|
1506
1334
|
summary: work.workKind === 'read_only_review'
|
|
@@ -1530,200 +1358,13 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1530
1358
|
});
|
|
1531
1359
|
}
|
|
1532
1360
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
operation: 'finalize',
|
|
1538
|
-
payload: { work_id: work.workId },
|
|
1539
|
-
risk: 'readonly',
|
|
1540
|
-
confidence: 'high',
|
|
1541
|
-
},
|
|
1542
|
-
]).actions;
|
|
1543
|
-
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1544
|
-
status: 'running',
|
|
1545
|
-
phase: 'delivery',
|
|
1546
|
-
state: 'active',
|
|
1547
|
-
summary: work.workKind === 'read_only_review'
|
|
1548
|
-
? 'Read-only review evidence is source-bound and clean; semantic no-change finalization is next.'
|
|
1549
|
-
: 'Implementation and verification evidence are sufficient; exact delivery and cleanup receipt is next.',
|
|
1550
|
-
evidenceRefs: work.evidenceRefs,
|
|
1551
|
-
});
|
|
1552
|
-
const updated = updateWorkContract(ctx.workStore, work.workId, {
|
|
1553
|
-
suggestedNextActions: suggested,
|
|
1554
|
-
});
|
|
1555
|
-
return buildFacadeResult({
|
|
1556
|
-
status: 'ok',
|
|
1557
|
-
summary: `Continue: checks satisfied or none required; ready to finalize after ChatGPT review.`,
|
|
1558
|
-
data: {
|
|
1559
|
-
work: summarizeWorkContract(updated),
|
|
1560
|
-
backgroundCompleted: false,
|
|
1561
|
-
nextStep: 'finalize',
|
|
1562
|
-
},
|
|
1563
|
-
suggestedNextActions: suggested,
|
|
1564
|
-
});
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopVerifyInput): FacadeResult {
|
|
1568
|
-
const work = getWorkContract(ctx.workStore, input.workId);
|
|
1569
|
-
if (!work) {
|
|
1570
|
-
return buildFacadeResult({
|
|
1571
|
-
status: 'not_found',
|
|
1572
|
-
summary: `WorkContract ${input.workId} not found.`,
|
|
1573
|
-
data: { workId: input.workId },
|
|
1574
|
-
});
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
const available = ctx.availableChecks ?? work.checks.map((id) => ({ id }));
|
|
1578
|
-
const classified = classifyVerificationOutcome({
|
|
1579
|
-
checkId: input.checkId,
|
|
1580
|
-
available,
|
|
1581
|
-
infrastructureFailed: input.infrastructureFailed,
|
|
1582
|
-
checkFailed: input.checkFailed,
|
|
1583
|
-
skipped: input.skipped,
|
|
1584
|
-
});
|
|
1585
|
-
if (work.status === 'completed' || work.status === 'cancelled' || work.status === 'failed') {
|
|
1586
|
-
const resolvedCheckId = classified.normalizedCheckId ?? classified.checkId;
|
|
1587
|
-
const existing = [...work.checkRefs].reverse().find((record) => record.checkId === resolvedCheckId);
|
|
1588
|
-
const completed = work.status === 'completed';
|
|
1589
|
-
return buildFacadeResult({
|
|
1590
|
-
status: completed ? 'ok' : 'blocked',
|
|
1591
|
-
summary: `WorkContract ${work.workId} is terminal (${work.status}); verification was not re-executed.`,
|
|
1592
|
-
data: {
|
|
1593
|
-
work: summarizeWorkContract(work),
|
|
1594
|
-
verification: {
|
|
1595
|
-
checkId: resolvedCheckId,
|
|
1596
|
-
...(existing ? { outcome: existing.outcome } : {}),
|
|
1597
|
-
terminal: true,
|
|
1598
|
-
idempotent: true,
|
|
1599
|
-
reexecuted: false,
|
|
1600
|
-
isAcceptanceFailure: existing?.outcome === 'valid_fail',
|
|
1601
|
-
isInfrastructureIssue: existing?.outcome === 'invalid_check_id' || existing?.outcome === 'infrastructure_failure',
|
|
1602
|
-
doesNotRequestTaskChanges: existing?.outcome !== 'valid_fail',
|
|
1603
|
-
},
|
|
1604
|
-
backgroundCompleted: false,
|
|
1605
|
-
},
|
|
1606
|
-
evidenceRefs: existing?.evidenceRef ? [existing.evidenceRef] : [],
|
|
1607
|
-
warnings: classified.warnings,
|
|
1608
|
-
suggestedNextActions: [{ label: 'Inspect work via context', tool: 'rh_context', operation: 'get', payload: { work_id: work.workId }, risk: 'readonly', confidence: 'high' }],
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
const at = nowIso(ctx);
|
|
1613
|
-
const resolvedCheckId = classified.normalizedCheckId ?? classified.checkId;
|
|
1614
|
-
const receipt = input.receipt
|
|
1615
|
-
&& input.receipt.repoId === work.repoId
|
|
1616
|
-
&& input.receipt.workId === work.workId
|
|
1617
|
-
&& input.receipt.checkId === resolvedCheckId
|
|
1618
|
-
? input.receipt
|
|
1619
|
-
: undefined;
|
|
1620
|
-
const sourceRevision = receipt && input.sourceRevision?.trim() ? input.sourceRevision.trim() : undefined;
|
|
1621
|
-
const verificationSummary = receipt
|
|
1622
|
-
? `${classified.summary} Durable Process receipt ${receipt.receiptId}.`
|
|
1623
|
-
: classified.summary;
|
|
1624
|
-
const record: VerificationRecord = {
|
|
1625
|
-
checkId: resolvedCheckId,
|
|
1626
|
-
outcome: classified.outcome,
|
|
1627
|
-
summary: verificationSummary,
|
|
1628
|
-
recordedAt: at,
|
|
1629
|
-
sourceRevision,
|
|
1630
|
-
workspaceFingerprint: receipt && sourceRevision ? input.workspaceFingerprint : undefined,
|
|
1631
|
-
verificationInputFingerprint: receipt && sourceRevision ? input.verificationInputFingerprint : undefined,
|
|
1632
|
-
commandFingerprint: receipt && sourceRevision ? input.commandFingerprint : undefined,
|
|
1633
|
-
startedAt: receipt?.startedAt,
|
|
1634
|
-
completedAt: receipt?.finishedAt,
|
|
1635
|
-
receipt,
|
|
1636
|
-
evidenceRef: {
|
|
1637
|
-
title: `verification:${classified.outcome}`,
|
|
1638
|
-
summary: verificationSummary,
|
|
1639
|
-
detailLevel: 'summary',
|
|
1640
|
-
},
|
|
1641
|
-
};
|
|
1642
|
-
|
|
1643
|
-
// Supersede prior invalid/infrastructure noise when a valid outcome arrives for the same check.
|
|
1644
|
-
let checkRefs = work.checkRefs;
|
|
1645
|
-
if (classified.outcome === 'valid_pass' || classified.outcome === 'valid_fail') {
|
|
1646
|
-
checkRefs = work.checkRefs.map((existing) => {
|
|
1647
|
-
if (
|
|
1648
|
-
existing.checkId === record.checkId
|
|
1649
|
-
&& (existing.outcome === 'invalid_check_id' || existing.outcome === 'infrastructure_failure')
|
|
1650
|
-
) {
|
|
1651
|
-
return { ...existing, outcome: 'superseded' as const, summary: `Superseded by ${classified.outcome} at ${at}` };
|
|
1652
|
-
}
|
|
1653
|
-
return existing;
|
|
1654
|
-
});
|
|
1655
|
-
updateWorkContract(ctx.workStore, work.workId, { checkRefs });
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
const updated = appendVerificationRecord(ctx.workStore, work.workId, record);
|
|
1659
|
-
if (record.evidenceRef) appendWorkEvidence(ctx.workStore, work.workId, record.evidenceRef);
|
|
1660
|
-
|
|
1661
|
-
const status =
|
|
1662
|
-
classified.outcome === 'invalid_check_id' || classified.outcome === 'infrastructure_failure'
|
|
1663
|
-
? 'ok'
|
|
1664
|
-
: classified.outcome === 'valid_fail'
|
|
1665
|
-
? 'failed'
|
|
1666
|
-
: 'ok';
|
|
1667
|
-
|
|
1668
|
-
const completionEvidence = classified.outcome === 'valid_pass'
|
|
1669
|
-
? evaluateWorkCompletionEvidence(updated, sourceRevision, input.workspaceFingerprint)
|
|
1670
|
-
: undefined;
|
|
1671
|
-
const validPassReadyToFinalize = completionEvidence?.status === 'complete';
|
|
1672
|
-
const suggested = validateSuggestedNextActions(
|
|
1673
|
-
classified.outcome === 'valid_pass'
|
|
1674
|
-
? [{
|
|
1675
|
-
label: validPassReadyToFinalize ? 'Finalize work' : 'Continue workloop',
|
|
1676
|
-
tool: 'rh_work',
|
|
1677
|
-
operation: validPassReadyToFinalize ? 'finalize' : 'continue',
|
|
1678
|
-
payload: { work_id: work.workId },
|
|
1679
|
-
risk: 'readonly',
|
|
1680
|
-
confidence: 'high',
|
|
1681
|
-
}]
|
|
1682
|
-
: classified.outcome === 'valid_fail'
|
|
1683
|
-
? [{
|
|
1684
|
-
label: 'Continue for review handoff',
|
|
1685
|
-
tool: 'rh_work',
|
|
1686
|
-
operation: 'continue',
|
|
1687
|
-
payload: { work_id: work.workId },
|
|
1688
|
-
risk: 'readonly',
|
|
1689
|
-
confidence: 'high',
|
|
1690
|
-
}]
|
|
1691
|
-
: [{
|
|
1692
|
-
label: 'Diagnose infrastructure (dry-run)',
|
|
1693
|
-
tool: 'rh_work',
|
|
1694
|
-
operation: 'repair',
|
|
1695
|
-
payload: { work_id: work.workId, repair_operation: 'diagnose', dry_run: true },
|
|
1696
|
-
risk: 'readonly',
|
|
1697
|
-
confidence: 'high',
|
|
1698
|
-
}],
|
|
1699
|
-
{ validCheckIds: work.checks },
|
|
1700
|
-
).actions;
|
|
1701
|
-
|
|
1702
|
-
return buildFacadeResult({
|
|
1703
|
-
status: status === 'failed' ? 'failed' : 'ok',
|
|
1704
|
-
summary: classified.summary,
|
|
1705
|
-
data: {
|
|
1706
|
-
work: summarizeWorkContract(updated),
|
|
1707
|
-
verification: {
|
|
1708
|
-
checkId: record.checkId,
|
|
1709
|
-
outcome: classified.outcome,
|
|
1710
|
-
isAcceptanceFailure: classified.isAcceptanceFailure,
|
|
1711
|
-
isInfrastructureIssue: classified.isInfrastructureIssue,
|
|
1712
|
-
// Explicitly separate pollution classes for ChatGPT.
|
|
1713
|
-
doesNotRequestTaskChanges: !classified.isAcceptanceFailure,
|
|
1714
|
-
},
|
|
1715
|
-
backgroundCompleted: false,
|
|
1716
|
-
...(classified.outcome === 'valid_pass'
|
|
1717
|
-
? { nextStep: validPassReadyToFinalize ? 'finalize' : 'continue' }
|
|
1718
|
-
: {}),
|
|
1719
|
-
},
|
|
1720
|
-
warnings: classified.warnings,
|
|
1721
|
-
evidenceRefs: record.evidenceRef ? [record.evidenceRef] : [],
|
|
1722
|
-
suggestedNextActions: suggested,
|
|
1723
|
-
});
|
|
1361
|
+
// Completion evidence is observational. It cannot choose a review/finalize
|
|
1362
|
+
// transition or tell the model which capability to invoke next.
|
|
1363
|
+
const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: [] });
|
|
1364
|
+
return buildFacadeResult({ status: 'ok', summary: 'Continue: current Work evidence recorded.', data: { work: summarizeWorkContract(updated), backgroundCompleted: false }, suggestedNextActions: [] });
|
|
1724
1365
|
}
|
|
1725
1366
|
|
|
1726
|
-
export function
|
|
1367
|
+
export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopStopInput): FacadeResult {
|
|
1727
1368
|
const work = getWorkContract(ctx.workStore, input.workId);
|
|
1728
1369
|
if (!work) {
|
|
1729
1370
|
return buildFacadeResult({
|
|
@@ -1733,326 +1374,38 @@ export function finalizeGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1733
1374
|
});
|
|
1734
1375
|
}
|
|
1735
1376
|
|
|
1736
|
-
|
|
1377
|
+
const destructiveCleanup = input.authorizeDestructiveCleanup === true;
|
|
1378
|
+
if (semanticWorkState(work) === 'completed') {
|
|
1737
1379
|
return buildFacadeResult({
|
|
1738
1380
|
status: 'blocked',
|
|
1739
|
-
summary: `
|
|
1381
|
+
summary: `WORK_CANCEL_COMPLETED: ${work.workId}`,
|
|
1740
1382
|
data: { work: summarizeWorkContract(work) },
|
|
1741
1383
|
});
|
|
1742
1384
|
}
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
status: 'ok',
|
|
1754
|
-
summary: `Finalize result: succeeded for ${work.workId}.`,
|
|
1755
|
-
data: {
|
|
1756
|
-
work: summarizeWorkContract(work),
|
|
1757
|
-
finalStatus: 'completed',
|
|
1758
|
-
completionReceipt: work.completionReceipt,
|
|
1759
|
-
idempotent: true,
|
|
1760
|
-
hiddenFailure: false,
|
|
1761
|
-
},
|
|
1762
|
-
evidenceRefs: work.evidenceRefs.slice(0, 5),
|
|
1763
|
-
suggestedNextActions: [{ label: 'Read controller status', tool: 'rh_status', operation: 'get', risk: 'readonly' }],
|
|
1764
|
-
});
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
const completionEvidence = evaluateWorkCompletionEvidence(
|
|
1768
|
-
work,
|
|
1769
|
-
ctx.sourceRevision,
|
|
1770
|
-
ctx.workspaceFingerprint,
|
|
1771
|
-
ctx.workBoundProcessEvidenceIds,
|
|
1772
|
-
ctx.workspaceChangedPaths,
|
|
1773
|
-
);
|
|
1774
|
-
const history = completionEvidence.history;
|
|
1775
|
-
|
|
1776
|
-
if (input.forceFailed || completionEvidence.status === 'failed') {
|
|
1777
|
-
const updated = transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1778
|
-
status: 'failed',
|
|
1779
|
-
phase: 'cleanup',
|
|
1780
|
-
state: 'failed',
|
|
1781
|
-
summary: `Work failed acceptance/finalization: ${history.acceptanceFailures.join(', ') || 'forced failure'}.`,
|
|
1782
|
-
evidenceRefs: work.evidenceRefs,
|
|
1783
|
-
});
|
|
1784
|
-
if (updated.planId && updated.planStepId && ctx.planStore) {
|
|
1785
|
-
completePlanStepForWork(ctx.planStore, { planId: updated.planId, stepId: updated.planStepId, work: updated });
|
|
1786
|
-
}
|
|
1787
|
-
return buildFacadeResult({
|
|
1788
|
-
status: 'failed',
|
|
1789
|
-
summary: `Finalize result: failed. Acceptance failures: ${history.acceptanceFailures.join(', ') || 'forced'}.`,
|
|
1790
|
-
data: {
|
|
1791
|
-
work: summarizeWorkContract(updated),
|
|
1792
|
-
finalStatus: 'failed',
|
|
1793
|
-
acceptanceFailures: history.acceptanceFailures,
|
|
1794
|
-
infrastructureIssues: history.infrastructureIssues,
|
|
1795
|
-
invalidCheckIds: history.invalidCheckIds,
|
|
1796
|
-
// Failures are not hidden.
|
|
1797
|
-
hiddenFailure: false,
|
|
1798
|
-
},
|
|
1799
|
-
suggestedNextActions: [
|
|
1800
|
-
{
|
|
1801
|
-
label: 'List handoffs',
|
|
1802
|
-
tool: 'rh_inbox',
|
|
1803
|
-
operation: 'list',
|
|
1804
|
-
risk: 'readonly',
|
|
1805
|
-
},
|
|
1806
|
-
],
|
|
1807
|
-
});
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
// Weak refs, partial checks, invalid ids, and infrastructure issues never imply successful completion.
|
|
1811
|
-
if (completionEvidence.status === 'incomplete') {
|
|
1812
|
-
const updated = transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1813
|
-
status: 'ready',
|
|
1814
|
-
phase: 'verification',
|
|
1815
|
-
state: 'blocked',
|
|
1816
|
-
summary: `Completion evidence remains incomplete: ${completionEvidence.reasons.join(' ')}`,
|
|
1817
|
-
evidenceRefs: work.evidenceRefs,
|
|
1818
|
-
});
|
|
1819
|
-
return buildFacadeResult({
|
|
1820
|
-
status: 'blocked',
|
|
1821
|
-
summary: `Finalize result: waiting_for_review. ${completionEvidence.reasons.join(' ')}`,
|
|
1822
|
-
data: {
|
|
1823
|
-
work: summarizeWorkContract(updated),
|
|
1824
|
-
finalStatus: 'ready',
|
|
1825
|
-
infrastructureIssues: history.infrastructureIssues,
|
|
1826
|
-
invalidCheckIds: history.invalidCheckIds,
|
|
1827
|
-
validPasses: history.validPasses,
|
|
1828
|
-
missingChecks: completionEvidence.missingChecks,
|
|
1829
|
-
durableResultEvidence: completionEvidence.durableResultEvidence,
|
|
1830
|
-
ignoredWeakReferences: {
|
|
1831
|
-
workerRef: Boolean(work.workerRef),
|
|
1832
|
-
worktreeRef: Boolean(work.worktreeRef),
|
|
1833
|
-
},
|
|
1834
|
-
hiddenFailure: false,
|
|
1835
|
-
},
|
|
1836
|
-
suggestedNextActions: [
|
|
1837
|
-
{
|
|
1838
|
-
label: 'Continue workloop',
|
|
1839
|
-
tool: 'rh_work',
|
|
1840
|
-
operation: 'continue',
|
|
1841
|
-
payload: { work_id: work.workId },
|
|
1842
|
-
risk: 'readonly',
|
|
1843
|
-
},
|
|
1844
|
-
],
|
|
1845
|
-
});
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
// Read-only review completion is semantic no-change authority, not Git delivery.
|
|
1849
|
-
// The exact source/workspace fence is re-evaluated above at finalize time; only
|
|
1850
|
-
// a clean review with persisted inspected paths and zero findings can close.
|
|
1851
|
-
if (work.workKind === 'read_only_review' && !work.completionReceipt) {
|
|
1852
|
-
const reviewEvidence = work.readOnlyReviewEvidence!;
|
|
1853
|
-
const recordedAt = nowIso(ctx);
|
|
1854
|
-
const completed = completeWorkWithReceipt(
|
|
1855
|
-
ctx.workStore,
|
|
1856
|
-
work.workId,
|
|
1857
|
-
{
|
|
1858
|
-
schemaVersion: 1,
|
|
1859
|
-
receiptId: `ROR-WORK-${randomUUID()}`,
|
|
1860
|
-
source: 'read_only_review',
|
|
1861
|
-
workId: work.workId,
|
|
1862
|
-
baseRevision: work.baseRevision!,
|
|
1863
|
-
sourceRevision: ctx.sourceRevision!,
|
|
1864
|
-
...(ctx.workspaceFingerprint ? { workspaceFingerprint: ctx.workspaceFingerprint } : {}),
|
|
1865
|
-
workspaceChangedPaths: [],
|
|
1866
|
-
inspectedPaths: reviewEvidence.inspectedPaths,
|
|
1867
|
-
findingCount: 0,
|
|
1868
|
-
recordedAt,
|
|
1869
|
-
},
|
|
1870
|
-
'completed_no_change',
|
|
1871
|
-
'read_only_review',
|
|
1872
|
-
);
|
|
1873
|
-
if (completed.planId && completed.planStepId && ctx.planStore) {
|
|
1874
|
-
completePlanStepForWork(ctx.planStore, { planId: completed.planId, stepId: completed.planStepId, work: completed });
|
|
1875
|
-
}
|
|
1876
|
-
return buildFacadeResult({
|
|
1877
|
-
status: 'ok',
|
|
1878
|
-
summary: `Finalize result: clean read-only review completed with no source change for ${work.workId}.`,
|
|
1879
|
-
data: {
|
|
1880
|
-
work: summarizeWorkContract(completed),
|
|
1881
|
-
finalStatus: 'completed',
|
|
1882
|
-
completionOutcome: 'completed_no_change',
|
|
1883
|
-
completionReceipt: completed.completionReceipt,
|
|
1884
|
-
inspectedPathCount: reviewEvidence.inspectedPaths.length,
|
|
1885
|
-
hiddenFailure: false,
|
|
1886
|
-
},
|
|
1887
|
-
evidenceRefs: completed.evidenceRefs.slice(0, 5),
|
|
1888
|
-
suggestedNextActions: [{ label: 'Read controller status', tool: 'rh_status', operation: 'get', risk: 'readonly' }],
|
|
1889
|
-
});
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
// For a controller-local effect Work, the semantic Controller's explicit
|
|
1893
|
-
// finalize call is the terminalization decision, but it is admissible only
|
|
1894
|
-
// after durable result evidence exists. Record one canonical Work receipt here
|
|
1895
|
-
// instead of routing an effect-only Work through Git delivery. Remote effects
|
|
1896
|
-
// remain bound to their durable plugin action receipt in the plugin layer.
|
|
1897
|
-
if (work.workKind === 'local_effect' && !work.completionReceipt && completionEvidence.durableResultEvidence) {
|
|
1898
|
-
const recordedAt = nowIso(ctx);
|
|
1899
|
-
const completed = completeWorkWithReceipt(
|
|
1900
|
-
ctx.workStore,
|
|
1901
|
-
work.workId,
|
|
1902
|
-
{
|
|
1903
|
-
schemaVersion: 1,
|
|
1904
|
-
receiptId: `LFX-WORK-${randomUUID()}`,
|
|
1905
|
-
source: 'local_effect',
|
|
1906
|
-
workId: work.workId,
|
|
1907
|
-
operation: 'controller_work/local_effect',
|
|
1908
|
-
target: { kind: 'controller_local', id: work.workId },
|
|
1909
|
-
changed: true,
|
|
1910
|
-
recordedAt,
|
|
1911
|
-
},
|
|
1912
|
-
'completed_local',
|
|
1913
|
-
'local_effect',
|
|
1914
|
-
);
|
|
1915
|
-
if (completed.planId && completed.planStepId && ctx.planStore) {
|
|
1916
|
-
completePlanStepForWork(ctx.planStore, { planId: completed.planId, stepId: completed.planStepId, work: completed });
|
|
1917
|
-
}
|
|
1918
|
-
return buildFacadeResult({
|
|
1919
|
-
status: 'ok',
|
|
1920
|
-
summary: `Finalize result: succeeded for ${work.workId}.`,
|
|
1921
|
-
data: {
|
|
1922
|
-
work: summarizeWorkContract(completed),
|
|
1923
|
-
finalStatus: 'completed',
|
|
1924
|
-
completionReceipt: completed.completionReceipt,
|
|
1925
|
-
validPasses: history.validPasses,
|
|
1926
|
-
hiddenFailure: false,
|
|
1927
|
-
},
|
|
1928
|
-
evidenceRefs: completed.evidenceRefs.slice(0, 5),
|
|
1929
|
-
suggestedNextActions: [{ label: 'Read controller status', tool: 'rh_status', operation: 'get', risk: 'readonly' }],
|
|
1930
|
-
});
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
// Checks and result evidence are necessary but not sufficient. Delivery and
|
|
1934
|
-
// cleanup must produce the exact Work-owned receipt before Work can become
|
|
1935
|
-
// terminal; a Run exit or a missing receipt is never completion authority.
|
|
1936
|
-
if (!work.completionReceipt) {
|
|
1937
|
-
const updated = transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1938
|
-
status: 'ready',
|
|
1939
|
-
phase: 'delivery',
|
|
1940
|
-
state: 'blocked',
|
|
1941
|
-
summary: 'Exact delivery and cleanup completion receipt is required.',
|
|
1942
|
-
evidenceRefs: work.evidenceRefs,
|
|
1943
|
-
});
|
|
1944
|
-
return buildFacadeResult({
|
|
1945
|
-
status: 'blocked',
|
|
1946
|
-
summary: `Finalize blocked for ${work.workId}: an exact delivery and cleanup completion receipt is required.`,
|
|
1947
|
-
data: { work: summarizeWorkContract(updated), finalStatus: 'ready', completionReceiptRequired: true },
|
|
1948
|
-
suggestedNextActions: [{ label: 'Complete Work delivery and cleanup', tool: 'rh_work', operation: 'finalize', payload: { work_id: work.workId }, risk: 'local_repo_write' }],
|
|
1949
|
-
});
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
const updated = getWorkContract(ctx.workStore, work.workId)!;
|
|
1953
|
-
if (updated.planId && updated.planStepId && ctx.planStore) {
|
|
1954
|
-
completePlanStepForWork(ctx.planStore, { planId: updated.planId, stepId: updated.planStepId, work: updated });
|
|
1955
|
-
}
|
|
1956
|
-
return buildFacadeResult({
|
|
1957
|
-
status: 'ok',
|
|
1958
|
-
summary: `Finalize result: succeeded for ${work.workId}.`,
|
|
1959
|
-
data: {
|
|
1960
|
-
work: summarizeWorkContract(updated),
|
|
1961
|
-
finalStatus: 'completed',
|
|
1962
|
-
validPasses: history.validPasses,
|
|
1963
|
-
hiddenFailure: false,
|
|
1964
|
-
},
|
|
1965
|
-
evidenceRefs: work.evidenceRefs.slice(0, 5),
|
|
1966
|
-
suggestedNextActions: [
|
|
1967
|
-
{
|
|
1968
|
-
label: 'Read controller status',
|
|
1969
|
-
tool: 'rh_status',
|
|
1970
|
-
operation: 'get',
|
|
1971
|
-
risk: 'readonly',
|
|
1972
|
-
},
|
|
1973
|
-
],
|
|
1974
|
-
});
|
|
1975
|
-
}
|
|
1976
|
-
|
|
1977
|
-
export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopStopInput): FacadeResult {
|
|
1978
|
-
const work = getWorkContract(ctx.workStore, input.workId);
|
|
1979
|
-
if (!work) {
|
|
1980
|
-
return buildFacadeResult({
|
|
1981
|
-
status: 'not_found',
|
|
1982
|
-
summary: `WorkContract ${input.workId} not found.`,
|
|
1983
|
-
data: { workId: input.workId },
|
|
1984
|
-
});
|
|
1985
|
-
}
|
|
1986
|
-
|
|
1987
|
-
const owner = ctx.workStore.controllerHome
|
|
1988
|
-
? getControllerSession({ controllerHome: ctx.workStore.controllerHome, repoId: ctx.repoId }, work.workId)
|
|
1989
|
-
: undefined;
|
|
1990
|
-
if (owner) {
|
|
1991
|
-
const ownerPrincipal = owner.principalId?.trim() || owner.controllerId;
|
|
1992
|
-
const ownerInstanceId = owner.controllerInstanceId?.trim();
|
|
1993
|
-
const callerPrincipal = ctx.principalId?.trim();
|
|
1994
|
-
const callerInstanceId = ctx.controllerInstanceId?.trim();
|
|
1995
|
-
const sameAuthority = Boolean(
|
|
1996
|
-
callerPrincipal
|
|
1997
|
-
&& ownerPrincipal === callerPrincipal
|
|
1998
|
-
&& (!ownerInstanceId || ownerInstanceId === callerInstanceId),
|
|
1999
|
-
);
|
|
2000
|
-
if (!sameAuthority) {
|
|
2001
|
-
return buildFacadeResult({
|
|
2002
|
-
status: 'blocked',
|
|
2003
|
-
summary: `WORK_TERMINALIZATION_ACTIVE_CONTROLLER_FENCE: ${work.workId} has a newer active Controller claim generation ${owner.claimGeneration ?? 1}.`,
|
|
2004
|
-
data: {
|
|
2005
|
-
work: summarizeWorkContract(work),
|
|
2006
|
-
activeController: {
|
|
2007
|
-
controllerId: owner.controllerId,
|
|
2008
|
-
principalId: ownerPrincipal,
|
|
2009
|
-
controllerInstanceId: owner.controllerInstanceId,
|
|
2010
|
-
sessionId: owner.sessionId,
|
|
2011
|
-
claimGeneration: owner.claimGeneration ?? 1,
|
|
2012
|
-
},
|
|
2013
|
-
finalStatus: work.status,
|
|
2014
|
-
},
|
|
2015
|
-
});
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
|
-
const destructiveCleanup = input.authorizeDestructiveCleanup === true;
|
|
2020
|
-
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
2021
|
-
status: 'cancelled',
|
|
2022
|
-
phase: 'cleanup',
|
|
2023
|
-
state: 'skipped',
|
|
2024
|
-
summary: input.reason ? `Stopped: ${input.reason}` : 'Work stopped without destructive cleanup.',
|
|
2025
|
-
evidenceRefs: work.evidenceRefs,
|
|
2026
|
-
});
|
|
2027
|
-
const updated = updateWorkContract(ctx.workStore, work.workId, {
|
|
2028
|
-
continuationPrompt: input.reason
|
|
2029
|
-
? `Stopped: ${input.reason}`.slice(0, 2_000)
|
|
2030
|
-
: work.continuationPrompt,
|
|
2031
|
-
// Authorization is not proof of cleanup. Preserve the reference until a real
|
|
2032
|
-
// cleanup handler verifies ownership, cleanliness and successful removal.
|
|
2033
|
-
worktreeRef: work.worktreeRef,
|
|
1385
|
+
const semanticRevision = Number.isInteger(work.semanticRevision) && Number(work.semanticRevision) > 0
|
|
1386
|
+
? Number(work.semanticRevision)
|
|
1387
|
+
: 1;
|
|
1388
|
+
const updated = reviseWorkSemanticContext(ctx.workStore, work.workId, {
|
|
1389
|
+
expectedRevision: semanticRevision,
|
|
1390
|
+
state: 'cancelled',
|
|
1391
|
+
resultRefs: work.evidenceRefs
|
|
1392
|
+
.flatMap((evidence) => [evidence.evidenceId, evidence.artifactId])
|
|
1393
|
+
.map((value) => value?.trim())
|
|
1394
|
+
.filter((value): value is string => Boolean(value)),
|
|
2034
1395
|
});
|
|
2035
1396
|
const planId = updated.planId;
|
|
2036
1397
|
const planStepId = updated.planStepId;
|
|
2037
1398
|
let plan = planId && ctx.planStore
|
|
2038
1399
|
? getPlanContract(ctx.planStore, planId)
|
|
2039
1400
|
: undefined;
|
|
2040
|
-
if (plan && planId && planStepId && ctx.planStore) {
|
|
2041
|
-
const step = plan.steps.find((entry) => entry.id === planStepId);
|
|
2042
|
-
if (step?.workId === updated.workId) {
|
|
2043
|
-
plan = completePlanStepForWork(ctx.planStore, { planId, stepId: planStepId, work: updated });
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
1401
|
|
|
2047
1402
|
return buildFacadeResult({
|
|
2048
1403
|
status: 'ok',
|
|
2049
|
-
summary: `WorkContract ${work.workId} cancelled/stopped. Evidence retained.
|
|
1404
|
+
summary: `WorkContract ${work.workId} cancelled/stopped. Evidence retained. Managed resource cleanup/retention is settled by the canonical Work finalization service.`,
|
|
2050
1405
|
data: {
|
|
2051
1406
|
work: summarizeWorkContract(updated),
|
|
2052
1407
|
finalStatus: 'cancelled',
|
|
2053
1408
|
evidenceRetained: true,
|
|
2054
|
-
worktreeDeleted: false,
|
|
2055
|
-
cleanupPending: destructiveCleanup && Boolean(work.worktreeRef),
|
|
2056
1409
|
destructiveCleanupAuthorized: destructiveCleanup,
|
|
2057
1410
|
...(plan ? { plan } : {}),
|
|
2058
1411
|
},
|
|
@@ -2068,32 +1421,36 @@ export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopSt
|
|
|
2068
1421
|
});
|
|
2069
1422
|
}
|
|
2070
1423
|
|
|
1424
|
+
export interface GoalWorkloopTrustedInput {
|
|
1425
|
+
/** Runtime-composed source-bound evidence. This channel is intentionally separate from raw MCP/tool arguments. */
|
|
1426
|
+
verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
|
|
1427
|
+
/**
|
|
1428
|
+
* Evaluator-minted evidence for one exact candidate. Kept off the raw MCP
|
|
1429
|
+
* argument surface so callers cannot manufacture architecture evidence.
|
|
1430
|
+
*/
|
|
1431
|
+
evaluationPromotionReceipt?: EvaluationPromotionReceipt;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
2071
1434
|
export function runGoalWorkloop(
|
|
2072
1435
|
ctx: GoalWorkloopContext,
|
|
2073
1436
|
operation: GoalWorkloopOperation,
|
|
2074
1437
|
args: Record<string, unknown>,
|
|
1438
|
+
trusted: GoalWorkloopTrustedInput = {},
|
|
2075
1439
|
): FacadeResult {
|
|
2076
1440
|
switch (operation) {
|
|
2077
1441
|
case 'start':
|
|
2078
1442
|
return routeWorkStart(ctx, {
|
|
2079
1443
|
objective: String(args.objective ?? ''),
|
|
1444
|
+
workId: typeof args.work_id === 'string' ? args.work_id : undefined,
|
|
2080
1445
|
acceptanceCriteria: Array.isArray(args.acceptance_criteria) ? args.acceptance_criteria.map(String) : undefined,
|
|
2081
1446
|
allowedPaths: Array.isArray(args.allowed_paths) ? args.allowed_paths.map(String) : undefined,
|
|
2082
1447
|
initialLikelyPaths: Array.isArray(args.initial_likely_paths) ? args.initial_likely_paths.map(String) : undefined,
|
|
2083
1448
|
forbiddenPaths: Array.isArray(args.forbidden_paths) ? args.forbidden_paths.map(String) : undefined,
|
|
2084
1449
|
checks: Array.isArray(args.check_ids) ? args.check_ids.map(String) : undefined,
|
|
2085
|
-
|
|
1450
|
+
request: {
|
|
2086
1451
|
objective: typeof args.objective === 'string' ? args.objective : undefined,
|
|
2087
|
-
expectedFiles: typeof args.expected_files === 'number' ? args.expected_files : undefined,
|
|
2088
|
-
expectedChangedLines: typeof args.expected_changed_lines === 'number' ? args.expected_changed_lines : undefined,
|
|
2089
1452
|
scopeClear: args.scope_clear === undefined ? true : args.scope_clear === true,
|
|
2090
|
-
requiresInvestigation: args.requires_investigation === true,
|
|
2091
|
-
requiresLongRunningChecks: args.requires_long_running_checks === true,
|
|
2092
|
-
requiresParallelism: args.requires_parallelism === true,
|
|
2093
|
-
explicitMode: args.mode === 'scale' ? 'scale' : undefined,
|
|
2094
|
-
needsDependencies: args.needs_dependencies === true,
|
|
2095
1453
|
requiresRecovery: args.requires_recovery === true,
|
|
2096
|
-
requiresWorker: args.requires_worker === true,
|
|
2097
1454
|
requiresExternalEffect: args.requires_external_effect === true,
|
|
2098
1455
|
requiresApproval: args.requires_approval === true,
|
|
2099
1456
|
requiresUserApproval: args.requires_user_approval === true,
|
|
@@ -2107,9 +1464,6 @@ export function runGoalWorkloop(
|
|
|
2107
1464
|
issueId: typeof args.issue_id === 'string' ? args.issue_id : undefined,
|
|
2108
1465
|
approvalConfirmed: args.approval_confirmed === true,
|
|
2109
1466
|
dryRun: args.dry_run === true,
|
|
2110
|
-
forceMode: args.force_mode === 'direct_control' || args.force_mode === 'goal_workloop' || args.force_mode === 'handoff_only'
|
|
2111
|
-
? args.force_mode
|
|
2112
|
-
: undefined,
|
|
2113
1467
|
relatedWorkId: typeof args.related_work_id === 'string' ? args.related_work_id : undefined,
|
|
2114
1468
|
workRelation: args.work_relation === 'continue' || args.work_relation === 'extend' || args.work_relation === 'parallel' || args.work_relation === 'new_goal'
|
|
2115
1469
|
? args.work_relation
|
|
@@ -2117,6 +1471,7 @@ export function runGoalWorkloop(
|
|
|
2117
1471
|
requirementId: typeof args.requirement_id === 'string' ? args.requirement_id : undefined,
|
|
2118
1472
|
planId: typeof args.plan_id === 'string' ? args.plan_id : undefined,
|
|
2119
1473
|
planStepId: typeof args.plan_step_id === 'string' ? args.plan_step_id : undefined,
|
|
1474
|
+
verifiedEngineeringEvidence: trusted.verifiedEngineeringEvidence,
|
|
2120
1475
|
workKind: args.work_kind === 'repository_change'
|
|
2121
1476
|
|| args.work_kind === 'completed_no_change'
|
|
2122
1477
|
|| args.work_kind === 'read_only_review'
|
|
@@ -2127,7 +1482,25 @@ export function runGoalWorkloop(
|
|
|
2127
1482
|
? args.work_kind
|
|
2128
1483
|
: undefined,
|
|
2129
1484
|
});
|
|
2130
|
-
case 'continue':
|
|
1485
|
+
case 'continue': {
|
|
1486
|
+
let engineeringBlocker: GoalWorkloopContinueInput['engineeringBlocker'];
|
|
1487
|
+
if (args.engineering_blocker !== undefined) {
|
|
1488
|
+
if (!args.engineering_blocker || typeof args.engineering_blocker !== 'object' || Array.isArray(args.engineering_blocker)) {
|
|
1489
|
+
return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_INVALID', data: { workId: String(args.work_id ?? '') } });
|
|
1490
|
+
}
|
|
1491
|
+
const rawBlocker = args.engineering_blocker as Record<string, unknown>;
|
|
1492
|
+
const classification = rawBlocker.classification;
|
|
1493
|
+
if (classification !== 'same_root_cause' && classification !== 'same_root_cause_scope_extension' && classification !== 'unrelated') {
|
|
1494
|
+
return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_CLASSIFICATION_INVALID', data: { workId: String(args.work_id ?? '') } });
|
|
1495
|
+
}
|
|
1496
|
+
engineeringBlocker = {
|
|
1497
|
+
blockerId: String(rawBlocker.blocker_id ?? ''),
|
|
1498
|
+
classification,
|
|
1499
|
+
rationale: String(rawBlocker.rationale ?? ''),
|
|
1500
|
+
semanticScopeKeys: Array.isArray(rawBlocker.semantic_scope_keys) ? rawBlocker.semantic_scope_keys.map(String) : undefined,
|
|
1501
|
+
linkedWorkId: typeof rawBlocker.linked_work_id === 'string' ? rawBlocker.linked_work_id : undefined,
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
2131
1504
|
return continueGoalWorkloop(ctx, {
|
|
2132
1505
|
workId: String(args.work_id ?? ''),
|
|
2133
1506
|
note: typeof args.note === 'string' ? args.note : undefined,
|
|
@@ -2147,20 +1520,10 @@ export function runGoalWorkloop(
|
|
|
2147
1520
|
acceptanceFailureDecision: args.acceptance_failure_decision === 'repair' || args.acceptance_failure_decision === 'rescope'
|
|
2148
1521
|
? args.acceptance_failure_decision
|
|
2149
1522
|
: undefined,
|
|
1523
|
+
verifiedEngineeringEvidence: trusted.verifiedEngineeringEvidence,
|
|
1524
|
+
engineeringBlocker,
|
|
2150
1525
|
});
|
|
2151
|
-
|
|
2152
|
-
return verifyGoalWorkloop(ctx, {
|
|
2153
|
-
workId: String(args.work_id ?? ''),
|
|
2154
|
-
checkId: String(args.check_id ?? args.checkId ?? ''),
|
|
2155
|
-
infrastructureFailed: args.infrastructure_failed === true,
|
|
2156
|
-
checkFailed: args.check_failed === true,
|
|
2157
|
-
skipped: args.skipped === true,
|
|
2158
|
-
});
|
|
2159
|
-
case 'finalize':
|
|
2160
|
-
return finalizeGoalWorkloop(ctx, {
|
|
2161
|
-
workId: String(args.work_id ?? ''),
|
|
2162
|
-
forceFailed: args.force_failed === true,
|
|
2163
|
-
});
|
|
1526
|
+
}
|
|
2164
1527
|
case 'stop':
|
|
2165
1528
|
return stopGoalWorkloop(ctx, {
|
|
2166
1529
|
workId: String(args.work_id ?? ''),
|