@moretea-labs/forge 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +58 -62
- package/CHANGELOG.md +26 -0
- package/CLAUDE.md +42 -62
- package/README.md +2 -2
- package/SKILL.md +0 -1
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/adapters/browser/index.ts +2 -0
- package/adapters/chatgpt/browser-delivery-host.ts +71 -0
- package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
- package/adapters/chatgpt/controller-binding-store.ts +55 -0
- package/adapters/chatgpt/controller-host.ts +119 -0
- package/adapters/chatgpt/controller-round-host.ts +78 -0
- package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
- package/adapters/chatgpt/provider-delivery.ts +113 -0
- package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
- package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
- package/adapters/computer/desktop-operator-contract.ts +7 -0
- package/adapters/computer/desktop-operator-discovery.ts +201 -0
- package/adapters/computer/desktop-operator-negotiation.ts +174 -0
- package/adapters/computer/desktop-operator-provider.ts +312 -0
- package/adapters/computer/index.ts +1 -0
- package/adapters/computer/interaction-target-authority.ts +944 -0
- package/adapters/controller-process/binding-store.ts +34 -0
- package/adapters/controller-process/controller-host.ts +26 -0
- package/adapters/mcp/access-mode.ts +132 -0
- package/adapters/mcp/audit.ts +32 -0
- package/adapters/mcp/auth.ts +407 -0
- package/adapters/mcp/controller-round-compatibility.ts +184 -0
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
- package/adapters/mcp/instructions.ts +36 -0
- package/adapters/mcp/legacy-context.ts +18 -0
- package/adapters/mcp/multi-repository.ts +437 -0
- package/adapters/mcp/oauth.ts +391 -0
- package/adapters/mcp/paths.ts +140 -0
- package/adapters/mcp/policy.ts +232 -0
- package/adapters/mcp/proxy-env.ts +62 -0
- package/adapters/mcp/readiness-projection.ts +24 -0
- package/adapters/mcp/redaction.ts +21 -0
- package/adapters/mcp/repo.ts +24 -0
- package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
- package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
- package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
- package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
- package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
- package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
- package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
- package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
- package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
- package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
- package/adapters/mcp/runtime-gateway/router.ts +728 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
- package/adapters/mcp/server.ts +1048 -0
- package/adapters/mcp/setup.ts +791 -0
- package/adapters/mcp/tool-capability-cache.ts +87 -0
- package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
- package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
- package/adapters/mcp/tool-mapping/tools.ts +9 -0
- package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
- package/adapters/mcp/toolset-names.ts +74 -0
- package/adapters/mcp/toolset-selection.ts +41 -0
- package/adapters/mcp/toolset.ts +336 -0
- package/adapters/mcp/transports/http.ts +1456 -0
- package/adapters/mcp/transports/session-registry.ts +513 -0
- package/adapters/mcp/transports/stdio.ts +8 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
- package/adapters/mcp/types.ts +41 -0
- package/assets/AGENTS.md +2 -16
- package/assets/CLAUDE.md +2 -16
- package/assets/hooks/AGENTS.md +3 -14
- package/assets/hooks/CLAUDE.md +3 -14
- package/assets/hooks/hook-input.sh +9 -2
- package/assets/hooks/lib/workflow-state.sh +28 -10
- package/assets/hooks/prompt-guard.sh +63 -37
- package/assets/hooks/security-sentinel.sh +2 -1
- package/assets/hooks/session-start-context.sh +7 -5
- package/assets/hooks/stop-orchestrator.sh +13 -3
- package/assets/initializer-question-pack.v4.json +11 -7
- package/assets/partials/05-workflow.partial.md +18 -73
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -2
- package/assets/partials-agents/04-task-protocol.partial.md +14 -59
- package/assets/partials-agents/06-quality-safety.partial.md +11 -16
- package/assets/partials-agents/08-deep-docs.partial.md +1 -2
- package/assets/plan-map.json +11 -11
- package/assets/plugin-registry.v1.json +9 -3
- package/assets/plugin-trust-policy.v1.json +6 -0
- package/assets/recovery/ForgeRecovery.ps1 +100 -0
- package/assets/recovery/forge-wsl-rescue +271 -0
- package/assets/reference-configs/development-protocol.md +11 -15
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/global-working-rules.md +9 -1
- package/assets/reference-configs/harness-overview.md +22 -112
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +2 -2
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/architecture-event.ts +3 -34
- package/assets/templates/helpers/architecture-queue.sh +61 -10
- package/assets/templates/helpers/archive-workflow.sh +90 -180
- package/assets/templates/helpers/capability-config.ts +8 -11
- package/assets/templates/helpers/capability-resolver.ts +37 -19
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +1 -9
- package/assets/templates/helpers/check-task-sync.sh +82 -11
- package/assets/templates/helpers/check-task-workflow.sh +1 -3
- package/assets/templates/helpers/context-contract-sync.sh +1 -12
- package/assets/templates/helpers/contract-worktree.sh +26 -24
- package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
- package/assets/templates/helpers/inspect-project-state.ts +1 -1
- package/assets/templates/helpers/migrate-project-template.sh +1 -1
- package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
- package/assets/templates/helpers/new-plan.sh +2 -2
- package/assets/templates/helpers/plan-to-todo.sh +3 -31
- package/assets/templates/helpers/sprint-backlog.sh +1 -1
- package/assets/templates/helpers/switch-plan.sh +1 -1
- package/assets/templates/helpers/workflow-contract.ts +1 -0
- package/assets/workflow-contract.v1.json +63 -171
- package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
- package/docs/architecture/CURRENT.md +148 -13
- package/docs/architecture/EVOLUTION.md +13 -0
- package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
- package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
- package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
- package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
- package/docs/architecture/domains/verification.md +7 -0
- package/docs/architecture/domains/workflow-engine.md +1 -1
- package/docs/architecture/index.md +3 -3
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
- package/docs/forge-chatgpt-mcp-setup.md +5 -3
- package/docs/forge-plugin-management.md +20 -4
- package/docs/forge-runtime-self-healing-loop.md +2 -0
- package/docs/operations/benchmark-cross-version-freeze.md +38 -0
- package/docs/operations/controller-browser-plugin.md +3 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/platform-support.md +16 -8
- package/docs/operations/platform-support.zh-CN.md +2 -2
- package/docs/operations/standalone-disaster-recovery.md +26 -1
- package/docs/operations/uu-remote-rescue.md +41 -0
- package/docs/operations/windows-wsl-host-rescue.md +133 -0
- package/docs/public-usage-guide.md +1 -1
- package/docs/public-usage-guide.zh-CN.md +1 -1
- package/docs/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/02-connect-chatgpt.md +4 -4
- package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +23 -14
- package/packages/kernel/controller/api/index.ts +2 -0
- package/packages/kernel/controller/application/controller-round-service.ts +29 -0
- package/packages/kernel/controller/application/controller-service.ts +30 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
- package/packages/kernel/controller/domain/controller-round.ts +107 -0
- package/packages/kernel/controller/domain/execution-quality.ts +133 -0
- package/packages/kernel/controller/domain/types.ts +56 -0
- package/packages/kernel/controller/index.ts +1 -0
- package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
- package/packages/kernel/controller/ports/controller-host.ts +16 -0
- package/packages/kernel/goal/api/index.ts +1 -0
- package/packages/kernel/goal/domain/types.ts +40 -0
- package/packages/kernel/goal/index.ts +1 -0
- package/packages/kernel/identity/api/index.ts +5 -0
- package/packages/kernel/identity/application/identity-service.ts +67 -0
- package/packages/kernel/identity/domain/scope.ts +63 -0
- package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
- package/packages/kernel/identity/domain/types.ts +44 -0
- package/packages/kernel/identity/domain/workspace.ts +56 -0
- package/packages/kernel/identity/index.ts +1 -0
- package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
- package/packages/kernel/memory/api/index.ts +5 -0
- package/packages/kernel/memory/application/experience-service.ts +118 -0
- package/packages/kernel/memory/application/outcome-service.ts +77 -0
- package/packages/kernel/memory/domain/experience.ts +103 -0
- package/packages/kernel/memory/domain/operational-prior.ts +92 -0
- package/packages/kernel/memory/index.ts +1 -0
- package/packages/kernel/progression/api/index.ts +2 -0
- package/packages/kernel/progression/application/projector.ts +211 -0
- package/packages/kernel/progression/domain/types.ts +116 -0
- package/packages/kernel/progression/index.ts +1 -0
- package/packages/kernel/scheduler/api/index.ts +6 -0
- package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
- package/packages/kernel/scheduler/application/eligibility.ts +252 -0
- package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
- package/packages/kernel/scheduler/application/settlement.ts +197 -0
- package/packages/kernel/scheduler/domain/continuation.ts +38 -0
- package/packages/kernel/scheduler/domain/schedule.ts +149 -0
- package/packages/kernel/scheduler/index.ts +1 -0
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
- package/packages/kernel/work/api/index.ts +15 -0
- package/packages/kernel/work/application/work-service.ts +148 -0
- package/packages/kernel/work/domain/admission-policy.ts +34 -0
- package/packages/kernel/work/domain/check-receipt.ts +43 -0
- package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
- package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
- package/packages/kernel/work/domain/engineering-design.ts +281 -0
- package/packages/kernel/work/domain/engineering-profile.ts +242 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
- package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
- package/packages/kernel/work/domain/implementation-review.ts +510 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
- package/packages/kernel/work/domain/state-machine.ts +259 -0
- package/packages/kernel/work/domain/types.ts +528 -0
- package/packages/kernel/work/index.ts +1 -0
- package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
- package/packages/kernel/work/ports/work-contract-store.ts +18 -0
- package/packages/plugin-runtime/browser/index.ts +3 -0
- package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
- package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
- package/packages/plugin-runtime/browser/session-authority.ts +61 -0
- package/packages/plugin-runtime/computer/index.ts +6 -0
- package/packages/plugin-runtime/computer/provider-error.ts +26 -0
- package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
- package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
- package/packages/plugin-runtime/computer/provider.ts +19 -0
- package/packages/plugin-runtime/computer/target-authority.ts +190 -0
- package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
- package/packages/plugin-runtime/external/index.ts +1 -0
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
- package/packages/plugin-runtime/index.ts +3 -0
- package/packages/protocols/browser/index.ts +1 -0
- package/packages/protocols/browser/session.ts +98 -0
- package/packages/protocols/computer/contract.ts +199 -0
- package/packages/protocols/computer/index.ts +1 -0
- package/packages/protocols/failure/contract.ts +158 -0
- package/packages/protocols/failure/index.ts +1 -0
- package/packages/protocols/handoff/index.ts +1 -0
- package/packages/protocols/handoff/status.ts +22 -0
- package/packages/protocols/mcp/execution-context.ts +11 -0
- package/packages/protocols/mcp/index.ts +2 -0
- package/packages/protocols/mcp/tool-contract.ts +42 -0
- package/packages/workflow-runtime/api/index.ts +2 -0
- package/packages/workflow-runtime/application/interpreter.ts +342 -0
- package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
- package/packages/workflow-runtime/index.ts +1 -0
- package/scripts/assemble-template.ts +1 -1
- package/scripts/benchmark-computer-hotpath.ts +255 -0
- package/scripts/benchmark-controller-round2.ts +54 -2
- package/scripts/benchmark-ios-agent-device.ts +3 -6
- package/scripts/benchmark-mcp-transport.ts +3 -4
- package/scripts/benchmark-operational-memory-activation.ts +235 -0
- package/scripts/benchmark-operational-shadow.ts +28 -0
- package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
- package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
- package/scripts/canary-controller-semantic-wait.ts +195 -0
- package/scripts/canary-global-hook.sh +2 -2
- package/scripts/check-mcp-compatibility.ts +173 -7
- package/scripts/check-platform-support.mjs +28 -2
- package/scripts/check-public-docs.sh +1 -1
- package/scripts/check-release-readiness.sh +1 -1
- package/scripts/check-repository-hygiene.mjs +92 -0
- package/scripts/check-runtime-architecture.mjs +1206 -95
- package/scripts/check-source-duplication.mjs +9 -1
- package/scripts/check-tarball-install-smoke.sh +98 -15
- package/scripts/controller-ngrok-rotation.sh +5 -4
- package/scripts/create-project-dirs.sh +10 -74
- package/scripts/forge-local-recovery-helper.mjs +204 -0
- package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
- package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
- package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
- package/scripts/hook-shim.sh +2 -2
- package/scripts/init-project.sh +12 -70
- package/scripts/install-desktop-operator-registration.ts +4 -2
- package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
- package/scripts/install-standalone-recovery.ts +122 -10
- package/scripts/install-uu-remote-rescue-registration.ts +74 -0
- package/scripts/install-windows-host-recovery-registration.ts +22 -0
- package/scripts/lib/controller-home-env.sh +6 -3
- package/scripts/lib/project-init-lib.sh +39 -582
- package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
- package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
- package/scripts/open-source-audit-allowlist.txt +12 -1
- package/scripts/portability-bindings.mjs +70 -0
- package/scripts/run-bounded-command.mjs +55 -0
- package/scripts/run-bun-test-file.ts +22 -6
- package/scripts/run-governed-gate.ts +4 -1
- package/scripts/smoke-mcp-http-runtime.ts +1 -1
- package/scripts/smoke-runtime-recovery.ts +34 -1
- package/scripts/smoke-schedule-engine.ts +1 -1
- package/scripts/stage-runtime-release.ts +6 -1
- package/scripts/test-governance.ts +51 -2
- package/src/cli/agent-jobs/job-manager.ts +10 -1
- package/src/cli/chatgpt-browser/bind-server.ts +2 -1
- package/src/cli/chatgpt-browser/binding.ts +20 -10
- package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
- package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
- package/src/cli/chatgpt-browser/engine.ts +10 -11
- package/src/cli/chatgpt-browser/native-provider.ts +24 -83
- package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
- package/src/cli/chatgpt-browser/types.ts +1 -0
- package/src/cli/commands/bootstrap-control.ts +334 -0
- package/src/cli/commands/brain-assistant.ts +38 -0
- package/src/cli/commands/brain.ts +193 -40
- package/src/cli/commands/chatgpt.ts +127 -0
- package/src/cli/commands/computer.ts +437 -0
- package/src/cli/commands/doctor.ts +18 -1
- package/src/cli/commands/init-hook.ts +71 -61
- package/src/cli/commands/init.ts +3 -1
- package/src/cli/commands/mcp.ts +16 -10
- package/src/cli/commands/plugin.ts +38 -12
- package/src/cli/commands/recovery.ts +493 -80
- package/src/cli/commands/runtime.ts +9 -4
- package/src/cli/commands/setup-profile.ts +89 -39
- package/src/cli/commands/status.ts +2 -1
- package/src/cli/controller/check-runner.ts +487 -98
- package/src/cli/controller/context/types.ts +7 -1
- package/src/cli/controller/context-pack.ts +23 -3
- package/src/cli/controller/issue-store.ts +1 -1
- package/src/cli/controller/systemd-user.ts +110 -0
- package/src/cli/controller/types.ts +10 -70
- package/src/cli/controller/worklog.ts +67 -13
- package/src/cli/editing/edit-operation-contract.ts +92 -0
- package/src/cli/editing/edit-session.ts +289 -13
- package/src/cli/github/plugin.ts +17 -12
- package/src/cli/hook/route-registry.ts +3 -5
- package/src/cli/hook/runtime.ts +49 -3
- package/src/cli/index.ts +3 -0
- package/src/cli/local-bridge/connector-freshness.ts +2 -2
- package/src/cli/local-bridge/console-automations.ts +2 -2
- package/src/cli/local-bridge/facade-api.ts +41 -14
- package/src/cli/local-bridge/job-store.ts +1 -1
- package/src/cli/local-bridge/server.ts +24 -27
- package/src/cli/mcp/access-mode.ts +2 -132
- package/src/cli/mcp/access-tools.ts +2 -387
- package/src/cli/mcp/audit.ts +2 -32
- package/src/cli/mcp/auth.ts +2 -404
- package/src/cli/mcp/instructions.ts +2 -34
- package/src/cli/mcp/legacy-context.ts +2 -16
- package/src/cli/mcp/legacy-tool-service.ts +2 -5308
- package/src/cli/mcp/multi-repository.ts +2 -436
- package/src/cli/mcp/oauth.ts +2 -375
- package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
- package/src/cli/mcp/paths.ts +2 -140
- package/src/cli/mcp/policy.ts +2 -232
- package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
- package/src/cli/mcp/proxy-env.ts +2 -62
- package/src/cli/mcp/readiness-projection.ts +2 -24
- package/src/cli/mcp/redaction.ts +2 -21
- package/src/cli/mcp/repo.ts +2 -24
- package/src/cli/mcp/repository-tools.ts +2 -1613
- package/src/cli/mcp/server.ts +2 -951
- package/src/cli/mcp/setup.ts +2 -737
- package/src/cli/mcp/tool-capability-cache.ts +2 -87
- package/src/cli/mcp/tool-contract.ts +2 -14
- package/src/cli/mcp/tools.ts +2 -9
- package/src/cli/mcp/toolset-names.ts +2 -70
- package/src/cli/mcp/toolset-selection.ts +2 -41
- package/src/cli/mcp/toolset.ts +2 -336
- package/src/cli/mcp/transports/AGENTS.md +1 -10
- package/src/cli/mcp/transports/CLAUDE.md +1 -10
- package/src/cli/mcp/transports/http.ts +2 -1414
- package/src/cli/mcp/transports/session-registry.ts +2 -490
- package/src/cli/mcp/transports/stdio.ts +2 -8
- package/src/cli/mcp/types.ts +2 -41
- package/src/cli/mcp/umbrella-tools.ts +2 -117
- package/src/cli/repositories/command-executor.ts +64 -18
- package/src/cli/repositories/command-scope.ts +117 -1
- package/src/cli/repositories/controller-home.ts +119 -1
- package/src/cli/repositories/identity.ts +13 -0
- package/src/cli/repositories/registry.ts +68 -49
- package/src/cli/repositories/repository-snapshot.ts +53 -7
- package/src/cli/repositories/runtime-storage.ts +27 -2
- package/src/cli/repositories/safe-patch.ts +34 -38
- package/src/cli/repositories/selected-path-actions.ts +22 -2
- package/src/cli/repositories/structured-git.ts +105 -25
- package/src/cli/runtime/helper-runner.ts +7 -0
- package/src/cli/runtime-invocation.ts +8 -1
- package/src/cli/tools/codegraph.ts +44 -25
- package/src/core/adoption/manifest-templates.ts +1 -1
- package/src/core/adoption/plan.ts +3 -26
- package/src/core/adoption/workflow-contract-plan.ts +17 -9
- package/src/effects/process-runner.ts +8 -14
- package/src/runtime/context/assistant-context.ts +154 -0
- package/src/runtime/context/assistant-work-context.ts +147 -0
- package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
- package/src/runtime/context/codegraph-read-provider.ts +55 -22
- package/src/runtime/context/codegraph-sidecar.cjs +8 -1
- package/src/runtime/context/context-closure.ts +268 -0
- package/src/runtime/context/context-plane.ts +211 -0
- package/src/runtime/context/project-engineering-contract.ts +60 -0
- package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
- package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
- package/src/runtime/control-plane/bootstrap/index.ts +5 -0
- package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
- package/src/runtime/control-plane/bootstrap/store.ts +21 -0
- package/src/runtime/control-plane/bootstrap/types.ts +87 -0
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
- package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +369 -99
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
- package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
- package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
- package/src/runtime/control-plane/execution/validation.ts +28 -3
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
- package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
- package/src/runtime/control-plane/execution/work-handle-authority.ts +219 -22
- package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
- package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
- package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
- package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
- package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
- package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
- package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
- package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
- package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
- package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
- package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
- package/src/runtime/control-plane/facade/index.ts +4 -2
- package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
- package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
- package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
- package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
- package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
- package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
- package/src/runtime/control-plane/facade/types.ts +189 -532
- package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
- package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
- package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
- package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
- package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
- package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
- package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
- package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
- package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
- package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
- package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
- package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
- package/src/runtime/control-plane/release-retention.ts +56 -16
- package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
- package/src/runtime/control-plane/routing/route-policy.ts +19 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
- package/src/runtime/control-plane/runtime-generation.ts +6 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
- package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
- package/src/runtime/diagnostics/incident-repair.ts +406 -0
- package/src/runtime/diagnostics/mcp-timing.ts +125 -6
- package/src/runtime/diagnostics/performance.ts +6 -14
- package/src/runtime/diagnostics/read-only-tool.ts +1 -1
- package/src/runtime/evidence/artifact-store.ts +65 -2
- package/src/runtime/evidence/operational-shadow.ts +38 -0
- package/src/runtime/evidence/process-check-receipt.ts +5 -43
- package/src/runtime/evidence/sensitive-output.ts +4 -4
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +121 -2
- package/src/runtime/execution/managed-workspace.ts +55 -5
- package/src/runtime/execution/process-runtime/canary.ts +19 -5
- package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
- package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
- package/src/runtime/execution/process-runtime/check-result.ts +73 -2
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
- package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
- package/src/runtime/execution/process-runtime/index.ts +1 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
- package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
- package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
- package/src/runtime/execution/process-runtime/runtime.ts +132 -15
- package/src/runtime/execution/process-runtime/types.ts +22 -0
- package/src/runtime/execution/workers/executor.ts +16 -13
- package/src/runtime/execution/workers/worker-entry.ts +1 -1
- package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
- package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
- package/src/runtime/gateway/mcp/process-tools.ts +2 -289
- package/src/runtime/gateway/mcp/router.ts +2 -1125
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
- package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
- package/src/runtime/maintenance/cleanup.ts +38 -24
- package/src/runtime/platform/browser-product-discovery.ts +174 -0
- package/src/runtime/platform/computer-platform.ts +14 -0
- package/src/runtime/platform/executable-discovery.ts +54 -0
- package/src/runtime/platform/fetch-http-server.ts +90 -0
- package/src/runtime/platform/service-manager.ts +138 -0
- package/src/runtime/platform/windows-host-discovery.ts +118 -0
- package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
- package/src/runtime/plugins/action-application.ts +135 -0
- package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
- package/src/runtime/plugins/browser-adapter.ts +140 -100
- package/src/runtime/plugins/browser-automation-service.ts +15 -36
- package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
- package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
- package/src/runtime/plugins/browser-native-session-service.ts +1 -1
- package/src/runtime/plugins/browser-provider-registry.ts +2 -109
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
- package/src/runtime/plugins/browser-runtime.ts +41 -9
- package/src/runtime/plugins/browser-session-authority.ts +101 -291
- package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
- package/src/runtime/plugins/browser-session-store.ts +229 -36
- package/src/runtime/plugins/browser-session-types.ts +2 -98
- package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
- package/src/runtime/plugins/catalog-trust.ts +82 -0
- package/src/runtime/plugins/computer-registration.ts +839 -0
- package/src/runtime/plugins/config-store.ts +119 -0
- package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
- package/src/runtime/plugins/errors.ts +8 -1
- package/src/runtime/plugins/external-adapter.ts +45 -322
- package/src/runtime/plugins/external-provider-policy.ts +48 -0
- package/src/runtime/plugins/external-registration.ts +9 -1
- package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
- package/src/runtime/plugins/external-unix-socket.ts +64 -193
- package/src/runtime/plugins/first-party-external-registration.ts +163 -0
- package/src/runtime/plugins/first-party-registry.ts +3 -1
- package/src/runtime/plugins/github-adapter.ts +1 -2
- package/src/runtime/plugins/gmail-adapter.ts +6 -6
- package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
- package/src/runtime/plugins/google-shared.ts +31 -46
- package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
- package/src/runtime/plugins/ios-adapter.ts +1 -2
- package/src/runtime/plugins/local-system-adapter.ts +180 -0
- package/src/runtime/plugins/macos-capability-broker.ts +15 -105
- package/src/runtime/plugins/resend-adapter.ts +15 -24
- package/src/runtime/plugins/store.ts +493 -51
- package/src/runtime/plugins/types.ts +8 -1
- package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
- package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
- package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
- package/src/runtime/projections/controller-context.ts +12 -6
- package/src/runtime/projections/git-status-sampler.ts +73 -22
- package/src/runtime/projections/materialized-view.ts +14 -8
- package/src/runtime/recovery/capability-action-application.ts +228 -0
- package/src/runtime/recovery/index.ts +3 -0
- package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
- package/src/runtime/recovery/local-jobs-repair.ts +14 -14
- package/src/runtime/recovery/maintenance-executor.ts +358 -76
- package/src/runtime/recovery/quarantine-retention.ts +256 -0
- package/src/runtime/release/identity.ts +6 -0
- package/src/runtime/resources/leases/store.ts +1 -1
- package/src/runtime/root/active-execution-power-assertion.ts +139 -0
- package/src/runtime/root/assistant-learning-loop.ts +45 -0
- package/src/runtime/root/browser-session-composition.ts +59 -0
- package/src/runtime/root/computer-composition.ts +191 -0
- package/src/runtime/root/computer-target-composition.ts +16 -0
- package/src/runtime/root/computer-target-persistence.ts +39 -0
- package/src/runtime/root/controller-round-composition.ts +107 -0
- package/src/runtime/root/controller-services.ts +2 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/gateway-adapter.ts +3 -3
- package/src/runtime/root/local-bridge.ts +5 -2
- package/src/runtime/root/mcp-transport.ts +5 -5
- package/src/runtime/root/package-connector-service.ts +108 -56
- package/src/runtime/root/package-runtime-release.ts +85 -18
- package/src/runtime/root/package-runtime-service.ts +60 -97
- package/src/runtime/root/release-execution-canary.ts +71 -0
- package/src/runtime/root/release-loader.ts +68 -0
- package/src/runtime/root/release-manifest.ts +106 -2
- package/src/runtime/root/release-materialize.ts +103 -4
- package/src/runtime/root/release-store.ts +31 -2
- package/src/runtime/root/runtime.ts +240 -25
- package/src/runtime/root/scheduled-controller-composition.ts +51 -0
- package/src/runtime/root/scheduler.ts +12 -4
- package/src/runtime/root/service-runner.ts +1 -1
- package/src/runtime/root/service.ts +41 -16
- package/src/runtime/root/status.ts +1 -0
- package/src/runtime/root/types.ts +24 -1
- package/src/runtime/shared/local-bridge-surface.ts +1 -1
- package/src/runtime/shared/process-environment.ts +19 -4
- package/src/runtime/shared/storage-capacity.ts +68 -0
- package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
- package/src/runtime/standalone-recovery/core.ts +984 -230
- package/src/runtime/standalone-recovery/entry.ts +182 -51
- package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
- package/src/runtime/standalone-recovery/installer.ts +212 -104
- package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
- package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
- package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
- package/src/runtime/standalone-recovery/performance.ts +135 -0
- package/src/runtime/standalone-recovery/release.ts +5 -0
- package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
- package/src/runtime/workflow/schedules/engine.ts +191 -343
- package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
- package/src/runtime/workflow/schedules/settlement.ts +2 -191
- package/src/runtime/workflow/schedules/store.ts +2 -607
- package/src/runtime/workflow/schedules/types.ts +2 -127
- package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
- package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
- package/src/runtime/workflows/runtime.ts +486 -0
- package/src/testing/test-governance.ts +90 -29
- package/assets/templates/helpers/archive-architecture-request.sh +0 -244
- package/docs/architecture/history/README.md +0 -7
- package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
- package/docs/architecture/history/global-hook-runtime.md +0 -166
- package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
- package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
- package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
- package/docs/architecture/requests/archive/2026/root.md +0 -68
- package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
- package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
- package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
- package/scripts/archive-architecture-request.sh +0 -4
- package/scripts/run-skill-evals.ts +0 -1022
|
@@ -1,23 +1,39 @@
|
|
|
1
|
-
import { randomUUID } from 'crypto';
|
|
1
|
+
import { createHash, randomUUID } from 'crypto';
|
|
2
|
+
import { globMatches } from '../../../cli/mcp/paths';
|
|
2
3
|
import {
|
|
3
4
|
createHandoffItem,
|
|
4
5
|
listHandoffItems,
|
|
5
6
|
type HandoffInboxStoreOptions,
|
|
6
7
|
} from './handoff-inbox-store';
|
|
7
|
-
import { getControllerSession } from '
|
|
8
|
+
import { getControllerSession } from '../../../../packages/kernel/controller/api/index';
|
|
9
|
+
import { executionPlacement, readForgeInstanceIdentity } from '../../../../packages/kernel/identity/api/index';
|
|
10
|
+
import { projectAutonomousGoalProgression, type ProgressionWorkSnapshot } from '../../../../packages/kernel/progression/api/index';
|
|
8
11
|
import {
|
|
12
|
+
applyEngineeringBlockerDisposition,
|
|
13
|
+
buildEngineeringBlockerDispositionReceipt,
|
|
14
|
+
buildEngineeringContextReceipt,
|
|
15
|
+
engineeringWorkProfileForRisk,
|
|
16
|
+
evaluateEngineeringAdmission,
|
|
9
17
|
appendVerificationRecord,
|
|
10
18
|
appendWorkEvidence,
|
|
11
19
|
appendWorkHandoffRef,
|
|
20
|
+
cancelWorkContract,
|
|
12
21
|
createWorkContract,
|
|
22
|
+
failWorkContract,
|
|
13
23
|
getWorkContract,
|
|
24
|
+
isTerminalWorkContractStatus,
|
|
14
25
|
listWorkContracts,
|
|
26
|
+
readActiveWorkCandidates,
|
|
27
|
+
recordWorkEvidenceState,
|
|
15
28
|
recordWorkScopeEvidence,
|
|
29
|
+
recordWorkImplementationReview,
|
|
30
|
+
requestWorkImplementationReview,
|
|
16
31
|
summarizeWorkContract,
|
|
17
32
|
transitionWorkContractPhase,
|
|
18
33
|
updateWorkContract,
|
|
34
|
+
type EngineeringAdmissionEvidence,
|
|
19
35
|
type WorkContractStoreOptions,
|
|
20
|
-
} from '
|
|
36
|
+
} from '../../../../packages/kernel/work/api/index';
|
|
21
37
|
import {
|
|
22
38
|
claimPlanStepForWork,
|
|
23
39
|
completePlanStepForWork,
|
|
@@ -26,7 +42,7 @@ import {
|
|
|
26
42
|
} from './plan-contract-store';
|
|
27
43
|
import { withPrimaryWorkAdmissionLock } from './semantic-admission';
|
|
28
44
|
import { completeWorkWithReceipt } from '../execution/work-completion-authority';
|
|
29
|
-
import { evaluateReadOnlyReviewSourceIdentity, evaluateWorkCompletionEvidence, evaluateWorkImplementationEvidence
|
|
45
|
+
import { effectiveCurrentWorkVerificationRecords, evaluateReadOnlyReviewSourceIdentity, evaluateWorkCompletionEvidence, evaluateWorkImplementationEvidence } from '../execution/work-evidence-policy';
|
|
30
46
|
import { readRequirement } from '../persistence/requirement-store';
|
|
31
47
|
import {
|
|
32
48
|
classifyVerificationOutcome,
|
|
@@ -35,6 +51,18 @@ import {
|
|
|
35
51
|
type CheckDefinitionLike,
|
|
36
52
|
} from './check-normalization';
|
|
37
53
|
import { evaluatePolicyGate } from './policy-gate';
|
|
54
|
+
import {
|
|
55
|
+
assertImplementationReviewPreDeliveryBoundary,
|
|
56
|
+
authoritativeImplementationReviewVerificationEvidence,
|
|
57
|
+
implementationReviewChangedPathDigest,
|
|
58
|
+
latestImplementationReview,
|
|
59
|
+
normalizeImplementationReviewChangedPaths,
|
|
60
|
+
workRequiresImplementationReview,
|
|
61
|
+
type ImplementationReviewDecision,
|
|
62
|
+
type ImplementationReviewCandidateIdentity,
|
|
63
|
+
type WorkImplementationReviewFinding,
|
|
64
|
+
type WorkImplementationReviewRecord,
|
|
65
|
+
} from '../../../../packages/kernel/work/api/index';
|
|
38
66
|
import { buildFacadeResult } from './facade-result';
|
|
39
67
|
import { validateSuggestedNextActions } from './suggested-actions';
|
|
40
68
|
import { buildWorkContinuationSnapshot } from './work-continuation';
|
|
@@ -53,8 +81,9 @@ import type {
|
|
|
53
81
|
} from './types';
|
|
54
82
|
import { selectExecutionMode } from './types';
|
|
55
83
|
import { resolveWorkspaceAdmissionConstraint } from '../routing/workspace-admission';
|
|
84
|
+
import { resolveContextPlane, type ContextScope } from '../../context/context-plane';
|
|
56
85
|
|
|
57
|
-
export type GoalWorkloopOperation = 'start' | 'continue' | 'verify' | 'finalize' | 'stop';
|
|
86
|
+
export type GoalWorkloopOperation = 'start' | 'continue' | 'verify' | 'review' | 'finalize' | 'stop';
|
|
58
87
|
|
|
59
88
|
export interface GoalWorkloopContext {
|
|
60
89
|
workStore: WorkContractStoreOptions;
|
|
@@ -63,10 +92,16 @@ export interface GoalWorkloopContext {
|
|
|
63
92
|
availableChecks?: readonly CheckDefinitionLike[];
|
|
64
93
|
planStore?: PlanContractStoreOptions;
|
|
65
94
|
sourceRevision?: string;
|
|
95
|
+
/** Explicit only when repository observation proves the Work started from a revision or an unborn HEAD. */
|
|
96
|
+
sourceBaseState?: 'revision' | 'unborn';
|
|
66
97
|
checkoutId?: string;
|
|
67
98
|
principalId?: string;
|
|
68
99
|
controllerInstanceId?: string;
|
|
69
100
|
workspaceFingerprint?: string;
|
|
101
|
+
/** Trusted repository observation for placement admission. Never accept this as caller-supplied semantic authority. */
|
|
102
|
+
workspaceDirty?: boolean;
|
|
103
|
+
/** Stage-insensitive Git-canonical identity of the exact current review candidate. Derived by the trusted repository adapter. */
|
|
104
|
+
implementationReviewWorkspaceFingerprint?: string;
|
|
70
105
|
/** Current net repository paths changed from the Work base plus dirty checkout paths. */
|
|
71
106
|
workspaceChangedPaths?: readonly string[];
|
|
72
107
|
/** Durable terminal repository Process ids explicitly bound to this Work + checkout. */
|
|
@@ -81,6 +116,8 @@ export type WorkAdmissionRelation = 'continue' | 'extend' | 'parallel' | 'new_go
|
|
|
81
116
|
|
|
82
117
|
export interface GoalWorkloopStartInput {
|
|
83
118
|
objective: string;
|
|
119
|
+
/** Optional exact durable identity reserved by the admission authority before creation. */
|
|
120
|
+
workId?: string;
|
|
84
121
|
acceptanceCriteria?: string[];
|
|
85
122
|
allowedPaths?: string[];
|
|
86
123
|
/** Non-authoritative first-pass discovery candidates. */
|
|
@@ -90,6 +127,8 @@ export interface GoalWorkloopStartInput {
|
|
|
90
127
|
constraints?: WorkContract['constraints'];
|
|
91
128
|
modeInput: ExecutionModeSelectionInput;
|
|
92
129
|
requestedBy?: WorkContract['requestedBy'];
|
|
130
|
+
/** Stable durable admission identity for system-originated Work such as recurrent incident repair. */
|
|
131
|
+
requestId?: string;
|
|
93
132
|
relatedWorkId?: string;
|
|
94
133
|
workRelation?: WorkAdmissionRelation;
|
|
95
134
|
requirementId?: string;
|
|
@@ -102,6 +141,8 @@ export interface GoalWorkloopStartInput {
|
|
|
102
141
|
planStepId?: string;
|
|
103
142
|
/** Explicit technical evidence shape chosen by the semantic Controller. Never inferred from objective/check text. */
|
|
104
143
|
workKind?: Extract<WorkKind, 'repository_change' | 'completed_no_change' | 'read_only_review' | 'investigation' | 'local_effect' | 'remote_effect' | 'reconciliation'>;
|
|
144
|
+
/** Trusted source-bound evidence resolved by Forge evidence authorities before admission. Never populate this field from raw caller-supplied receipt ids. */
|
|
145
|
+
verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
|
|
105
146
|
}
|
|
106
147
|
|
|
107
148
|
export interface GoalWorkloopAcceptanceEvidenceBinding {
|
|
@@ -124,6 +165,15 @@ export interface GoalWorkloopContinueInput {
|
|
|
124
165
|
acceptanceEvidence?: GoalWorkloopAcceptanceEvidenceBinding[];
|
|
125
166
|
/** Explicit Controller decision after deterministic acceptance failure; never inferred from note text. */
|
|
126
167
|
acceptanceFailureDecision?: 'repair' | 'rescope';
|
|
168
|
+
/** Trusted refreshed engineering evidence minted by Runtime composition, never copied from raw caller receipt ids. */
|
|
169
|
+
verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
|
|
170
|
+
/** Semantic Controller blocker classification; Forge derives the permitted action and persists the receipt. */
|
|
171
|
+
engineeringBlocker?: {
|
|
172
|
+
blockerId: string;
|
|
173
|
+
classification: 'same_root_cause' | 'unrelated';
|
|
174
|
+
rationale: string;
|
|
175
|
+
semanticScopeKeys?: string[];
|
|
176
|
+
};
|
|
127
177
|
}
|
|
128
178
|
|
|
129
179
|
export interface GoalWorkloopVerifyInput {
|
|
@@ -147,6 +197,13 @@ export interface GoalWorkloopVerifyInput {
|
|
|
147
197
|
skipped?: boolean;
|
|
148
198
|
}
|
|
149
199
|
|
|
200
|
+
export interface GoalWorkloopReviewInput {
|
|
201
|
+
workId: string;
|
|
202
|
+
decision: ImplementationReviewDecision;
|
|
203
|
+
rationale: string;
|
|
204
|
+
findings?: WorkImplementationReviewFinding[];
|
|
205
|
+
}
|
|
206
|
+
|
|
150
207
|
export interface GoalWorkloopFinalizeInput {
|
|
151
208
|
workId: string;
|
|
152
209
|
forceFailed?: boolean;
|
|
@@ -163,6 +220,57 @@ function nowIso(ctx: GoalWorkloopContext): string {
|
|
|
163
220
|
return ctx.now?.() ?? new Date().toISOString();
|
|
164
221
|
}
|
|
165
222
|
|
|
223
|
+
function currentImplementationReviewCandidate(
|
|
224
|
+
ctx: GoalWorkloopContext,
|
|
225
|
+
work: WorkContract,
|
|
226
|
+
): ImplementationReviewCandidateIdentity {
|
|
227
|
+
const sourceRevision = ctx.sourceRevision?.trim() ?? '';
|
|
228
|
+
const verificationWorkspaceFingerprint = ctx.workspaceFingerprint?.trim() ?? '';
|
|
229
|
+
const workspaceFingerprint = ctx.implementationReviewWorkspaceFingerprint?.trim() ?? '';
|
|
230
|
+
const changedPaths = normalizeImplementationReviewChangedPaths(
|
|
231
|
+
ctx.workspaceChangedPaths ?? work.scopeEvidence?.actualChangedPaths ?? [],
|
|
232
|
+
);
|
|
233
|
+
if (!sourceRevision || !verificationWorkspaceFingerprint || !workspaceFingerprint) {
|
|
234
|
+
throw new Error('WORK_IMPLEMENTATION_REVIEW_SOURCE_IDENTITY_REQUIRED');
|
|
235
|
+
}
|
|
236
|
+
const verification = authoritativeImplementationReviewVerificationEvidence({
|
|
237
|
+
repoId: work.repoId,
|
|
238
|
+
workId: work.workId,
|
|
239
|
+
requiredCheckIds: work.checks,
|
|
240
|
+
records: work.checkRefs,
|
|
241
|
+
sourceRevision,
|
|
242
|
+
workspaceFingerprint: verificationWorkspaceFingerprint,
|
|
243
|
+
});
|
|
244
|
+
if (verification.missingCheckIds.length > 0) {
|
|
245
|
+
throw new Error(`WORK_IMPLEMENTATION_REVIEW_VERIFICATION_REQUIRED: ${verification.missingCheckIds.join(', ')}`);
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
sourceRevision,
|
|
249
|
+
workspaceFingerprint,
|
|
250
|
+
verificationWorkspaceFingerprint,
|
|
251
|
+
changedPaths,
|
|
252
|
+
verificationEvidence: verification.evidence,
|
|
253
|
+
architectureEvidence: [],
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function implementationReviewAcceptanceSummary(work: WorkContract): string {
|
|
258
|
+
const summary = work.acceptanceCriteria.map((criterion) => criterion.trim()).filter(Boolean).join(' | ');
|
|
259
|
+
return (summary || 'No explicit acceptance criteria; reviewer assessed the Work objective and exact current implementation evidence.').slice(0, 2_000);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function implementationReviewSuggestedAction(workId: string): SuggestedNextAction {
|
|
263
|
+
return {
|
|
264
|
+
label: 'Review implementation',
|
|
265
|
+
tool: 'rh_work',
|
|
266
|
+
operation: 'review',
|
|
267
|
+
payload: { work_id: workId },
|
|
268
|
+
risk: 'workspace_write',
|
|
269
|
+
confidence: 'high',
|
|
270
|
+
reason: 'Verification is complete; an explicit Controller implementation decision is required before delivery.',
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
166
274
|
function workIdFor(objective: string): string {
|
|
167
275
|
const slug = objective
|
|
168
276
|
.toLowerCase()
|
|
@@ -172,6 +280,11 @@ function workIdFor(objective: string): string {
|
|
|
172
280
|
return `work-${slug}-${randomUUID().slice(0, 8)}`;
|
|
173
281
|
}
|
|
174
282
|
|
|
283
|
+
function linkedEngineeringBlockerWorkId(parentWorkId: string, blockerId: string): string {
|
|
284
|
+
const digest = createHash('sha256').update(`${parentWorkId}\0${blockerId.trim()}`).digest('hex').slice(0, 24);
|
|
285
|
+
return `work-linked-engineering-blocker-${digest}`;
|
|
286
|
+
}
|
|
287
|
+
|
|
175
288
|
function handoffIdFor(prefix: string): string {
|
|
176
289
|
return `hnd-${prefix}-${randomUUID().slice(0, 8)}`;
|
|
177
290
|
}
|
|
@@ -188,6 +301,22 @@ function workRiskFor(input: GoalWorkloopStartInput): WorkRisk {
|
|
|
188
301
|
return 'medium';
|
|
189
302
|
}
|
|
190
303
|
|
|
304
|
+
function resolvedWorkKindFor(input: GoalWorkloopStartInput): WorkKind {
|
|
305
|
+
if (input.workKind) return input.workKind;
|
|
306
|
+
// allowed_paths and discovery hints do not prove repository mutation. External
|
|
307
|
+
// effects become repository_change only when source-change intent is explicit.
|
|
308
|
+
const repositoryChangeIntent = (input.modeInput.expectedFiles ?? 0) > 0
|
|
309
|
+
|| (input.modeInput.expectedChangedLines ?? 0) > 0;
|
|
310
|
+
const typedRecoverableReadOnlyReview = input.modeInput.mutation === false
|
|
311
|
+
&& input.modeInput.requiresInvestigation === true
|
|
312
|
+
&& input.modeInput.requiresRecovery === true;
|
|
313
|
+
if (typedRecoverableReadOnlyReview) return 'read_only_review';
|
|
314
|
+
if (input.modeInput.requiresExternalEffect === true && !repositoryChangeIntent) {
|
|
315
|
+
return input.modeInput.remoteWrite === true ? 'remote_effect' : 'local_effect';
|
|
316
|
+
}
|
|
317
|
+
return 'repository_change';
|
|
318
|
+
}
|
|
319
|
+
|
|
191
320
|
function suggestedForWorkIdentity(workId: string, checks: string[], extras: SuggestedNextAction[] = []): SuggestedNextAction[] {
|
|
192
321
|
const base: SuggestedNextAction[] = [
|
|
193
322
|
{
|
|
@@ -259,7 +388,23 @@ export function routeWorkStart(
|
|
|
259
388
|
const strategyConflictRequiresApproval = input.constraints?.architectureStrategyChange === true
|
|
260
389
|
|| input.constraints?.conflictsWithThinHarnessPolicy === true
|
|
261
390
|
|| input.constraints?.changesDefaultExecutionStrategy === true;
|
|
262
|
-
|
|
391
|
+
let relatedLifecycleSource: WorkContract | undefined;
|
|
392
|
+
if (input.relatedWorkId) {
|
|
393
|
+
try {
|
|
394
|
+
relatedLifecycleSource = getWorkContract(ctx.workStore, input.relatedWorkId);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
return buildFacadeResult({
|
|
397
|
+
status: 'blocked',
|
|
398
|
+
summary: `WORK_RELATED_CONTRACT_INVALID: ${input.relatedWorkId} is malformed and cannot authorize a Work relationship.`,
|
|
399
|
+
data: {
|
|
400
|
+
executionStarted: false,
|
|
401
|
+
workContractCreated: false,
|
|
402
|
+
invalidWorkId: input.relatedWorkId,
|
|
403
|
+
invalidWorkError: error instanceof Error ? error.message : String(error),
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
}
|
|
263
408
|
const inheritedReadOnlyReview = Boolean(
|
|
264
409
|
input.workKind === undefined
|
|
265
410
|
&& input.workRelation === 'new_goal'
|
|
@@ -279,10 +424,21 @@ export function routeWorkStart(
|
|
|
279
424
|
});
|
|
280
425
|
}
|
|
281
426
|
const requestedWorkKind = input.workKind ?? (inheritedReadOnlyReview ? 'read_only_review' : undefined);
|
|
427
|
+
const explicitSourceMutationFence = input.allowedPaths !== undefined
|
|
428
|
+
&& input.allowedPaths.length === 0
|
|
429
|
+
&& (input.forbiddenPaths ?? []).some((value) => {
|
|
430
|
+
const normalized = value.trim().replace(/\\/g, '/');
|
|
431
|
+
return normalized === '*' || normalized === '**' || normalized === '**/*';
|
|
432
|
+
});
|
|
282
433
|
const typedReadOnlyReviewRequested = requestedWorkKind === undefined
|
|
283
|
-
&& (
|
|
284
|
-
|
|
285
|
-
|
|
434
|
+
&& (
|
|
435
|
+
explicitSourceMutationFence
|
|
436
|
+
|| (
|
|
437
|
+
(input.modeInput.mutation === false || (input.modeInput.mutation === undefined && input.modeInput.risk === 'readonly'))
|
|
438
|
+
&& input.modeInput.requiresInvestigation === true
|
|
439
|
+
&& input.modeInput.requiresRecovery === true
|
|
440
|
+
)
|
|
441
|
+
);
|
|
286
442
|
const effectiveWorkKind = requestedWorkKind ?? (typedReadOnlyReviewRequested ? 'read_only_review' : undefined);
|
|
287
443
|
const readOnlyReviewRequested = effectiveWorkKind === 'read_only_review';
|
|
288
444
|
const readOnlyMutationConflict = readOnlyReviewRequested && (
|
|
@@ -300,12 +456,35 @@ export function routeWorkStart(
|
|
|
300
456
|
data: { executionStarted: false, workContractCreated: false, workKind: effectiveWorkKind },
|
|
301
457
|
});
|
|
302
458
|
}
|
|
459
|
+
const contextScopes: ContextScope[] = [];
|
|
460
|
+
if (input.requirementId?.trim()) contextScopes.push({ schemaVersion: 1, kind: 'requirement', id: input.requirementId.trim() });
|
|
461
|
+
if (input.planId?.trim()) contextScopes.push({ schemaVersion: 1, kind: 'plan', id: input.planId.trim() });
|
|
462
|
+
if (input.relatedWorkId?.trim()) contextScopes.push({ schemaVersion: 1, kind: 'work', id: input.relatedWorkId.trim() });
|
|
463
|
+
const resolvedContext = ctx.workStore.controllerHome
|
|
464
|
+
? resolveContextPlane({
|
|
465
|
+
controllerHome: ctx.workStore.controllerHome,
|
|
466
|
+
scopes: contextScopes,
|
|
467
|
+
intent: input.modeInput.routePolicyInput?.intent.taskIntent ?? 'implementation',
|
|
468
|
+
now: nowIso(ctx),
|
|
469
|
+
})
|
|
470
|
+
: undefined;
|
|
471
|
+
// A terminal continuation is not a new semantic request. Its durable predecessor
|
|
472
|
+
// and Plan lineage already define the goal scope, while the successor Plan step
|
|
473
|
+
// becomes the Work objective later in startGoalWorkloop. Do not force the external
|
|
474
|
+
// Controller to duplicate semantic text merely to satisfy generic route selection.
|
|
475
|
+
// This fallback is routing context only and never becomes successor Work authority.
|
|
476
|
+
const routeObjective = input.objective.trim()
|
|
477
|
+
|| (input.workRelation === 'continue' && relatedLifecycleSource?.status === 'completed'
|
|
478
|
+
? relatedLifecycleSource.objective.trim()
|
|
479
|
+
: '');
|
|
303
480
|
const effectiveModeInput: ExecutionModeSelectionInput = {
|
|
304
481
|
...input.modeInput,
|
|
305
|
-
|
|
482
|
+
contextRouteHints: input.modeInput.contextRouteHints ?? resolvedContext?.routeHints,
|
|
483
|
+
objective: routeObjective,
|
|
306
484
|
knownPaths: input.allowedPaths,
|
|
307
485
|
workspacePlacement: placementConstraint.workspaceMode,
|
|
308
486
|
directMainProhibited: placementConstraint.directMainProhibited,
|
|
487
|
+
workspaceDirty: ctx.workspaceDirty ?? input.modeInput.workspaceDirty,
|
|
309
488
|
mutation: readOnlyReviewRequested ? false : input.modeInput.mutation ?? input.modeInput.risk !== 'readonly',
|
|
310
489
|
risk: readOnlyReviewRequested ? 'readonly' : input.modeInput.risk,
|
|
311
490
|
approvalConfirmed: input.approvalConfirmed === true,
|
|
@@ -383,10 +562,21 @@ export function routeWorkStart(
|
|
|
383
562
|
// the RouteDecision. The actual repository mutation can pass this Work id
|
|
384
563
|
// to repository_safe_patch_apply / repository_command_execute for evidence
|
|
385
564
|
// attribution while staying on the Direct/Process path.
|
|
386
|
-
const
|
|
387
|
-
?
|
|
388
|
-
|
|
389
|
-
|
|
565
|
+
const directAdmissionSnapshot = input.relatedWorkId || (input.planId && input.planStepId)
|
|
566
|
+
? readActiveWorkCandidates({ ...ctx.workStore, limit: 100 })
|
|
567
|
+
: { contracts: [], invalid: [] };
|
|
568
|
+
const invalidDirectOwner = directAdmissionSnapshot.invalid.find((candidate) =>
|
|
569
|
+
candidate.workId === input.relatedWorkId
|
|
570
|
+
|| (input.planId && input.planStepId && candidate.planId === input.planId && candidate.planStepId === input.planStepId));
|
|
571
|
+
if (invalidDirectOwner) {
|
|
572
|
+
return buildFacadeResult({
|
|
573
|
+
status: 'blocked',
|
|
574
|
+
summary: `WORK_ADMISSION_INVALID_OWNER: ${invalidDirectOwner.workId} is malformed and cannot be used as Direct Control ownership authority.`,
|
|
575
|
+
data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidDirectOwner.workId, invalidWorkError: invalidDirectOwner.error },
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
const directOwnershipCandidates = directAdmissionSnapshot.contracts
|
|
579
|
+
.filter((candidate) => (candidate.lifecycleRole ?? 'primary') === 'primary');
|
|
390
580
|
const directPlan = input.planId && ctx.planStore ? getPlanContract(ctx.planStore, input.planId) : undefined;
|
|
391
581
|
const directPlanStep = directPlan && input.planStepId ? directPlan.steps.find((step) => step.id === input.planStepId) : undefined;
|
|
392
582
|
const directOwner = (input.relatedWorkId
|
|
@@ -587,21 +777,215 @@ export function startGoalWorkloop(
|
|
|
587
777
|
));
|
|
588
778
|
}
|
|
589
779
|
const at = nowIso(ctx);
|
|
780
|
+
const resolvedWorkKind = resolvedWorkKindFor(input);
|
|
781
|
+
const workRisk = workRiskFor(input);
|
|
782
|
+
// External-effect risk is governed by authorization/confirmation/receipt
|
|
783
|
+
// semantics. Engineering admission is specifically for repository mutation.
|
|
784
|
+
const engineeringRisk: WorkRisk = resolvedWorkKind === 'repository_change' ? workRisk : 'readonly';
|
|
785
|
+
const engineeringProfile = engineeringWorkProfileForRisk(engineeringRisk);
|
|
786
|
+
const engineeringSourceIdentity = ctx.sourceRevision?.trim()
|
|
787
|
+
? { kind: 'revision' as const, revision: ctx.sourceRevision.trim() }
|
|
788
|
+
: ctx.sourceBaseState === 'unborn'
|
|
789
|
+
? { kind: 'unborn' as const }
|
|
790
|
+
: { kind: 'unknown' as const };
|
|
791
|
+
let engineeringContext;
|
|
792
|
+
try {
|
|
793
|
+
engineeringContext = buildEngineeringContextReceipt({
|
|
794
|
+
risk: engineeringRisk,
|
|
795
|
+
sourceIdentity: engineeringSourceIdentity,
|
|
796
|
+
evidence: input.verifiedEngineeringEvidence,
|
|
797
|
+
recordedAt: at,
|
|
798
|
+
});
|
|
799
|
+
} catch (error) {
|
|
800
|
+
return buildFacadeResult({
|
|
801
|
+
status: 'blocked',
|
|
802
|
+
summary: error instanceof Error ? error.message : 'ENGINEERING_CONTEXT_INVALID',
|
|
803
|
+
data: { executionStarted: false, workContractCreated: false, engineeringProfile },
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
const engineeringMutation = resolvedWorkKind === 'repository_change'
|
|
807
|
+
&& (input.modeInput.mutation ?? input.modeInput.risk !== 'readonly');
|
|
808
|
+
const engineeringAdmission = evaluateEngineeringAdmission({
|
|
809
|
+
profile: engineeringProfile,
|
|
810
|
+
receipt: engineeringContext,
|
|
811
|
+
mutation: engineeringMutation,
|
|
812
|
+
});
|
|
813
|
+
if (!engineeringAdmission.allowed) {
|
|
814
|
+
return buildFacadeResult({
|
|
815
|
+
status: 'blocked',
|
|
816
|
+
summary: `${engineeringAdmission.code}: ${engineeringProfile.riskClass} mutation is missing required pre-mutation engineering evidence.`,
|
|
817
|
+
data: {
|
|
818
|
+
executionStarted: false,
|
|
819
|
+
workContractCreated: false,
|
|
820
|
+
engineeringProfile,
|
|
821
|
+
engineeringContext,
|
|
822
|
+
missingEngineeringEvidence: engineeringAdmission.missing,
|
|
823
|
+
},
|
|
824
|
+
});
|
|
825
|
+
}
|
|
590
826
|
const available = ctx.availableChecks ?? [];
|
|
591
827
|
const workspaceMode = placementConstraint.workspaceMode;
|
|
592
|
-
const
|
|
828
|
+
const activeAdmissionSnapshot = readActiveWorkCandidates({ ...ctx.workStore, limit: 100 });
|
|
829
|
+
const activeWorks = activeAdmissionSnapshot.contracts
|
|
593
830
|
.filter((candidate) => (candidate.lifecycleRole ?? 'primary') === 'primary');
|
|
831
|
+
const invalidRelatedWork = input.relatedWorkId
|
|
832
|
+
? activeAdmissionSnapshot.invalid.find((candidate) => candidate.workId === input.relatedWorkId)
|
|
833
|
+
: undefined;
|
|
834
|
+
if (invalidRelatedWork) {
|
|
835
|
+
return buildFacadeResult({
|
|
836
|
+
status: 'blocked',
|
|
837
|
+
summary: `WORK_RELATED_CONTRACT_INVALID: ${invalidRelatedWork.workId} is malformed and cannot authorize a Work relationship.`,
|
|
838
|
+
data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidRelatedWork.workId, invalidWorkError: invalidRelatedWork.error },
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
const relatedLifecycleSource = input.relatedWorkId
|
|
842
|
+
? getWorkContract(ctx.workStore, input.relatedWorkId)
|
|
843
|
+
: undefined;
|
|
594
844
|
const workspaceOwner = ctx.checkoutId
|
|
595
845
|
? activeWorks.find((candidate) => candidate.checkoutId === ctx.checkoutId
|
|
596
846
|
&& candidate.worktreePolicy.required !== true
|
|
597
847
|
&& !candidate.worktreeRef)
|
|
598
848
|
: undefined;
|
|
849
|
+
const terminalContinuationSource = input.workRelation === 'continue'
|
|
850
|
+
&& relatedLifecycleSource
|
|
851
|
+
&& isTerminalWorkContractStatus(relatedLifecycleSource.status)
|
|
852
|
+
? relatedLifecycleSource
|
|
853
|
+
: undefined;
|
|
854
|
+
if (terminalContinuationSource && !terminalContinuationSource.requirementId && !terminalContinuationSource.planId) {
|
|
855
|
+
return buildFacadeResult({
|
|
856
|
+
status: 'blocked',
|
|
857
|
+
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.`,
|
|
858
|
+
data: { executionStarted: false, workContractCreated: false, relatedWorkId: terminalContinuationSource.workId },
|
|
859
|
+
});
|
|
860
|
+
}
|
|
599
861
|
const explicitRelatedWork = input.relatedWorkId
|
|
600
862
|
? activeWorks.find((candidate) => candidate.workId === input.relatedWorkId)
|
|
601
863
|
: undefined;
|
|
602
|
-
|
|
603
|
-
|
|
864
|
+
|
|
865
|
+
let resolvedPlanId = input.planId?.trim() || undefined;
|
|
866
|
+
let resolvedPlanStepId = input.planStepId?.trim() || undefined;
|
|
867
|
+
if (terminalContinuationSource?.planId) {
|
|
868
|
+
if (resolvedPlanId && resolvedPlanId !== terminalContinuationSource.planId) {
|
|
869
|
+
return buildFacadeResult({
|
|
870
|
+
status: 'blocked',
|
|
871
|
+
summary: `WORK_SUCCESSOR_PLAN_MISMATCH: predecessor ${terminalContinuationSource.workId} belongs to ${terminalContinuationSource.planId}, not ${resolvedPlanId}. Supersede/replan explicitly instead of silently moving continuation authority.`,
|
|
872
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, predecessorPlanId: terminalContinuationSource.planId, requestedPlanId: resolvedPlanId },
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
resolvedPlanId = terminalContinuationSource.planId;
|
|
876
|
+
}
|
|
877
|
+
const plan = resolvedPlanId && ctx.planStore ? getPlanContract(ctx.planStore, resolvedPlanId) : undefined;
|
|
878
|
+
if (terminalContinuationSource?.planId && !plan) {
|
|
879
|
+
return buildFacadeResult({
|
|
880
|
+
status: 'blocked',
|
|
881
|
+
summary: `WORK_SUCCESSOR_PLAN_NOT_FOUND: ${terminalContinuationSource.planId}. Recover the durable Plan authority before creating a successor Work.`,
|
|
882
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: terminalContinuationSource.planId },
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
if (terminalContinuationSource?.planStepId && plan && !resolvedPlanStepId) {
|
|
886
|
+
const predecessorStep = plan.steps.find((step) => step.id === terminalContinuationSource.planStepId);
|
|
887
|
+
if (!predecessorStep) {
|
|
888
|
+
return buildFacadeResult({
|
|
889
|
+
status: 'blocked',
|
|
890
|
+
summary: `WORK_SUCCESSOR_PLAN_STEP_NOT_FOUND: predecessor ${terminalContinuationSource.workId} references ${terminalContinuationSource.planStepId}.`,
|
|
891
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, predecessorPlanStepId: terminalContinuationSource.planStepId },
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
const requirementRecord = plan.requirementId && ctx.workStore.controllerHome
|
|
895
|
+
? readRequirement({ controllerHome: ctx.workStore.controllerHome }, plan.requirementId)
|
|
896
|
+
: undefined;
|
|
897
|
+
const progressionWorks: ProgressionWorkSnapshot[] = plan.steps
|
|
898
|
+
.flatMap((candidate) => candidate.workId ? [getWorkContract(ctx.workStore, candidate.workId)] : [])
|
|
899
|
+
.filter((candidate): candidate is NonNullable<typeof candidate> => Boolean(candidate))
|
|
900
|
+
.map((candidate) => ({
|
|
901
|
+
workId: candidate.workId,
|
|
902
|
+
requirementId: candidate.requirementId,
|
|
903
|
+
planId: candidate.planId,
|
|
904
|
+
planStepId: candidate.planStepId,
|
|
905
|
+
status: candidate.status,
|
|
906
|
+
baseRevision: candidate.baseRevision,
|
|
907
|
+
completionTargetRevision: candidate.completionReceipt && 'targetRevision' in candidate.completionReceipt
|
|
908
|
+
? candidate.completionReceipt.targetRevision
|
|
909
|
+
: undefined,
|
|
910
|
+
}));
|
|
911
|
+
const progression = projectAutonomousGoalProgression({
|
|
912
|
+
requirement: {
|
|
913
|
+
requirementId: plan.requirementId ?? terminalContinuationSource.requirementId ?? `plan:${plan.planId}`,
|
|
914
|
+
state: requirementRecord?.value.state ?? 'active',
|
|
915
|
+
revision: requirementRecord?.revision ?? 0,
|
|
916
|
+
},
|
|
917
|
+
plan: {
|
|
918
|
+
planId: plan.planId,
|
|
919
|
+
requirementId: plan.requirementId,
|
|
920
|
+
sourceRevision: plan.sourceRevision,
|
|
921
|
+
status: plan.status,
|
|
922
|
+
steps: plan.steps.map((candidate) => ({ id: candidate.id, dependencies: candidate.dependencies, status: candidate.status, workId: candidate.workId })),
|
|
923
|
+
},
|
|
924
|
+
currentSourceRevision: ctx.sourceRevision ?? plan.sourceRevision,
|
|
925
|
+
works: progressionWorks,
|
|
926
|
+
controllerRounds: [],
|
|
927
|
+
});
|
|
928
|
+
if (progression.kind === 'request_controller_acceptance') {
|
|
929
|
+
return buildFacadeResult({
|
|
930
|
+
status: 'blocked',
|
|
931
|
+
summary: `PLAN_STEP_SEMANTIC_ACCEPTANCE_REQUIRED: ${plan.planId}/${progression.planStepId ?? predecessorStep.id} has machine-complete delivery. Accept the delivered step before advancing the same semantic goal.`,
|
|
932
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, planStepId: progression.planStepId ?? predecessorStep.id, semanticAcceptanceRequired: true, progression },
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
if (progression.reasonCode === 'PLAN_STEP_TERMINAL_WORK_RECONCILIATION_REQUIRED') {
|
|
936
|
+
return buildFacadeResult({
|
|
937
|
+
status: 'blocked',
|
|
938
|
+
summary: `PLAN_STEP_TERMINAL_WORK_RECONCILIATION_REQUIRED: ${plan.planId}/${progression.planStepId ?? predecessorStep.id} must project terminal Work ${terminalContinuationSource.workId} before semantic progression.`,
|
|
939
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, planStepId: progression.planStepId ?? predecessorStep.id, repairRequired: true, progression },
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
if (progression.kind === 'start_next_plan_step' && progression.planStepId) {
|
|
943
|
+
resolvedPlanStepId = progression.planStepId;
|
|
944
|
+
} else if (progression.kind === 'request_requirement_acceptance') {
|
|
945
|
+
return buildFacadeResult({
|
|
946
|
+
status: 'ok',
|
|
947
|
+
summary: `CONTINUATION_REQUIREMENT_ACCEPTANCE_REQUIRED: ${plan.planId} is finalized; the semantic Controller must record goal_complete before the Requirement becomes done.`,
|
|
948
|
+
data: { executionStarted: false, workContractCreated: false, admissionDecision: 'requirement_acceptance_required', requirementAcceptanceRequired: true, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, requirementId: plan.requirementId, progression },
|
|
949
|
+
});
|
|
950
|
+
} else if (progression.kind === 'goal_complete') {
|
|
951
|
+
return buildFacadeResult({
|
|
952
|
+
status: 'ok',
|
|
953
|
+
summary: `CONTINUATION_GOAL_COMPLETE: ${plan.planId} Requirement is already semantically complete; no successor Work was created.`,
|
|
954
|
+
data: { executionStarted: false, workContractCreated: false, admissionDecision: 'goal_complete', goalComplete: true, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, requirementId: plan.requirementId, progression },
|
|
955
|
+
});
|
|
956
|
+
} else if (progression.reasonCode === 'MULTIPLE_READY_PLAN_STEPS') {
|
|
957
|
+
const candidates = (progression.dependencyStepIds ?? [])
|
|
958
|
+
.map((stepId) => plan.steps.find((candidate) => candidate.id === stepId))
|
|
959
|
+
.filter((candidate): candidate is NonNullable<typeof candidate> => Boolean(candidate));
|
|
960
|
+
return buildFacadeResult({
|
|
961
|
+
status: 'ok',
|
|
962
|
+
summary: `PLAN_SUCCESSOR_STEP_RESOLUTION_REQUIRED: ${plan.planId} has ${candidates.length} executable successor steps. The semantic Controller must select one; Forge will not invent ordering.`,
|
|
963
|
+
data: { executionStarted: false, workContractCreated: false, admissionDecision: 'resolution_required', resolutionRequired: true, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, candidatePlanSteps: candidates.map((candidate) => ({ id: candidate.id, objective: candidate.objective })), progression },
|
|
964
|
+
});
|
|
965
|
+
} else if (progression.kind === 'request_replan') {
|
|
966
|
+
return buildFacadeResult({
|
|
967
|
+
status: 'blocked',
|
|
968
|
+
summary: `PLAN_SUCCESSOR_REPLAN_REQUIRED: ${plan.planId} ${progression.reasonCode}.`,
|
|
969
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, progression },
|
|
970
|
+
});
|
|
971
|
+
} else if (progression.kind !== 'start_next_plan_step') {
|
|
972
|
+
return buildFacadeResult({
|
|
973
|
+
status: 'blocked',
|
|
974
|
+
summary: `PLAN_SUCCESSOR_PROGRESSION_BLOCKED: ${plan.planId} ${progression.reasonCode}.`,
|
|
975
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, planId: plan.planId, progression },
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
const planStep = plan && resolvedPlanStepId ? plan.steps.find((step) => step.id === resolvedPlanStepId) : undefined;
|
|
604
980
|
const requestedRequirementId = input.requirementId?.trim() || undefined;
|
|
981
|
+
const predecessorRequirementId = terminalContinuationSource?.requirementId;
|
|
982
|
+
if (predecessorRequirementId && requestedRequirementId && predecessorRequirementId !== requestedRequirementId) {
|
|
983
|
+
return buildFacadeResult({
|
|
984
|
+
status: 'blocked',
|
|
985
|
+
summary: `WORK_SUCCESSOR_REQUIREMENT_MISMATCH: predecessor ${terminalContinuationSource.workId} belongs to ${predecessorRequirementId}, not ${requestedRequirementId}.`,
|
|
986
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource.workId, predecessorRequirementId, requestedRequirementId },
|
|
987
|
+
});
|
|
988
|
+
}
|
|
605
989
|
if (plan && requestedRequirementId && requestedRequirementId !== plan.requirementId) {
|
|
606
990
|
return buildFacadeResult({
|
|
607
991
|
status: 'blocked',
|
|
@@ -609,7 +993,14 @@ export function startGoalWorkloop(
|
|
|
609
993
|
data: { executionStarted: false, workContractCreated: false, planId: plan.planId, planRequirementId: plan.requirementId, requestedRequirementId },
|
|
610
994
|
});
|
|
611
995
|
}
|
|
612
|
-
|
|
996
|
+
if (plan?.requirementId && predecessorRequirementId && plan.requirementId !== predecessorRequirementId) {
|
|
997
|
+
return buildFacadeResult({
|
|
998
|
+
status: 'blocked',
|
|
999
|
+
summary: `WORK_SUCCESSOR_PLAN_REQUIREMENT_MISMATCH: predecessor ${terminalContinuationSource!.workId} belongs to ${predecessorRequirementId}, while ${plan.planId} belongs to ${plan.requirementId}.`,
|
|
1000
|
+
data: { executionStarted: false, workContractCreated: false, predecessorWorkId: terminalContinuationSource!.workId, predecessorRequirementId, planId: plan.planId, planRequirementId: plan.requirementId },
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
const effectiveRequirementId = requestedRequirementId || plan?.requirementId || predecessorRequirementId;
|
|
613
1004
|
if (
|
|
614
1005
|
effectiveRequirementId
|
|
615
1006
|
&& ctx.workStore.controllerHome
|
|
@@ -637,33 +1028,68 @@ export function startGoalWorkloop(
|
|
|
637
1028
|
if (mismatches.length > 0) {
|
|
638
1029
|
return buildFacadeResult({
|
|
639
1030
|
status: 'blocked',
|
|
640
|
-
summary: `PLAN_STEP_WORK_CONTRACT_MISMATCH: ${
|
|
641
|
-
data: { executionStarted: false, workContractCreated: false, planId:
|
|
1031
|
+
summary: `PLAN_STEP_WORK_CONTRACT_MISMATCH: ${resolvedPlanId}/${resolvedPlanStepId} conflicts with frozen Plan step field(s): ${mismatches.join(', ')}. Replan instead of widening or narrowing Work admission.`,
|
|
1032
|
+
data: { executionStarted: false, workContractCreated: false, planId: resolvedPlanId, planStepId: resolvedPlanStepId, mismatches },
|
|
642
1033
|
});
|
|
643
1034
|
}
|
|
644
1035
|
}
|
|
1036
|
+
const effectiveObjective = planStep?.objective ?? input.objective;
|
|
645
1037
|
const effectiveAcceptanceCriteria = planStep?.acceptanceCriteria ?? input.acceptanceCriteria ?? [];
|
|
646
1038
|
const effectiveAllowedPaths = planStep?.allowedPaths ?? input.allowedPaths ?? [];
|
|
647
1039
|
const effectiveForbiddenPaths = planStep?.forbiddenPaths ?? input.forbiddenPaths ?? [];
|
|
648
1040
|
const effectiveChecks = planStep?.checks ?? input.checks ?? [];
|
|
649
1041
|
const normalized = normalizeCheckIds(effectiveChecks, available);
|
|
650
|
-
const
|
|
651
|
-
|
|
1042
|
+
const invalidLineageWork = activeAdmissionSnapshot.invalid.find((candidate) =>
|
|
1043
|
+
(input.relatedWorkId && candidate.workId === input.relatedWorkId)
|
|
1044
|
+
|| (resolvedPlanId && resolvedPlanStepId && candidate.planId === resolvedPlanId && candidate.planStepId === resolvedPlanStepId));
|
|
1045
|
+
if (invalidLineageWork) {
|
|
1046
|
+
return buildFacadeResult({
|
|
1047
|
+
status: 'blocked',
|
|
1048
|
+
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.`,
|
|
1049
|
+
data: {
|
|
1050
|
+
executionStarted: false,
|
|
1051
|
+
workContractCreated: false,
|
|
1052
|
+
invalidWorkId: invalidLineageWork.workId,
|
|
1053
|
+
invalidWorkError: invalidLineageWork.error,
|
|
1054
|
+
planId: resolvedPlanId,
|
|
1055
|
+
planStepId: resolvedPlanStepId,
|
|
1056
|
+
requirementId: effectiveRequirementId,
|
|
1057
|
+
},
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
const newWorkWillBeIsolated = placementConstraint.requireWorktree
|
|
1061
|
+
|| placementConstraint.workspaceMode === 'isolated'
|
|
1062
|
+
|| input.modeInput.requiresParallelism === true
|
|
1063
|
+
|| routeDecision.requiresIsolation === true;
|
|
1064
|
+
const invalidSharedWorkspaceOwner = !newWorkWillBeIsolated
|
|
1065
|
+
? activeAdmissionSnapshot.invalid.find((candidate) => candidate.isolation === 'shared'
|
|
1066
|
+
&& (!candidate.checkoutId || candidate.checkoutId === ctx.checkoutId))
|
|
1067
|
+
: undefined;
|
|
1068
|
+
if (invalidSharedWorkspaceOwner) {
|
|
1069
|
+
return buildFacadeResult({
|
|
1070
|
+
status: 'blocked',
|
|
1071
|
+
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.`,
|
|
1072
|
+
data: { executionStarted: false, workContractCreated: false, invalidWorkId: invalidSharedWorkspaceOwner.workId, invalidWorkError: invalidSharedWorkspaceOwner.error },
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
const boundPlanStepWorks = resolvedPlanId && resolvedPlanStepId
|
|
1076
|
+
? activeWorks.filter((candidate) => candidate.planId === resolvedPlanId && candidate.planStepId === resolvedPlanStepId)
|
|
652
1077
|
: [];
|
|
653
1078
|
const explicitPlanStepWork = planStep?.workId ? activeWorks.find((candidate) => candidate.workId === planStep.workId) : undefined;
|
|
654
1079
|
const planStepWork = explicitPlanStepWork ?? (boundPlanStepWorks.length === 1 ? boundPlanStepWorks[0] : undefined);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
1080
|
+
// Requirement membership is portfolio ownership, not semantic Work identity.
|
|
1081
|
+
// Only an explicit related Work or the exact bound Plan step may select an
|
|
1082
|
+
// existing Work authority. Siblings under one Requirement remain unrelated
|
|
1083
|
+
// for semantic admission and meet only in placement/resource arbitration.
|
|
658
1084
|
const deterministicTarget = input.relatedWorkId
|
|
659
1085
|
? explicitRelatedWork
|
|
660
|
-
: planStepWork
|
|
1086
|
+
: planStepWork;
|
|
661
1087
|
const requestedRelation = input.workRelation ?? (input.modeInput.requiresParallelism === true ? 'parallel' : undefined);
|
|
662
1088
|
// Only strong semantic bindings participate in ownership resolution. An
|
|
663
1089
|
// unrelated active Work or a checkout writer is a placement fact, not a
|
|
664
1090
|
// semantic candidate for continue/extend/parallel/new_goal.
|
|
665
1091
|
const candidateWorks = [...new Map(
|
|
666
|
-
[explicitRelatedWork, planStepWork, ...boundPlanStepWorks
|
|
1092
|
+
[explicitRelatedWork, planStepWork, ...boundPlanStepWorks]
|
|
667
1093
|
.filter((candidate): candidate is WorkContract => Boolean(candidate))
|
|
668
1094
|
.map((candidate) => [candidate.workId, candidate]),
|
|
669
1095
|
).values()].slice(0, 8);
|
|
@@ -697,18 +1123,42 @@ export function startGoalWorkloop(
|
|
|
697
1123
|
});
|
|
698
1124
|
|
|
699
1125
|
if (boundPlanStepWorks.length > 1) {
|
|
700
|
-
return resolutionRequired(`PLAN_STEP_MULTIPLE_PRIMARY_WORKS: ${
|
|
1126
|
+
return resolutionRequired(`PLAN_STEP_MULTIPLE_PRIMARY_WORKS: ${resolvedPlanId}/${resolvedPlanStepId} has ${boundPlanStepWorks.length} active primary Work records and requires repair before execution.`);
|
|
701
1127
|
}
|
|
1128
|
+
if (terminalContinuationSource && requestedRelation === 'continue' && planStepWork) {
|
|
1129
|
+
if (planStepWork.predecessorWorkId !== terminalContinuationSource.workId) {
|
|
1130
|
+
return resolutionRequired(
|
|
1131
|
+
`PLAN_STEP_SUCCESSOR_LINEAGE_CONFLICT: ${resolvedPlanId}/${resolvedPlanStepId} is already owned by ${planStepWork.workId}, but its predecessor is ${planStepWork.predecessorWorkId ?? 'none'} rather than ${terminalContinuationSource.workId}.`,
|
|
1132
|
+
planStepWork,
|
|
1133
|
+
);
|
|
1134
|
+
}
|
|
1135
|
+
return buildFacadeResult({
|
|
1136
|
+
status: 'ok',
|
|
1137
|
+
summary: `PLAN_STEP_REUSES_SUCCESSOR_WORK: ${resolvedPlanId}/${resolvedPlanStepId} already continues terminal ${terminalContinuationSource.workId} as ${planStepWork.workId}.`,
|
|
1138
|
+
data: {
|
|
1139
|
+
executionStarted: false,
|
|
1140
|
+
workContractCreated: false,
|
|
1141
|
+
admissionDecision: 'reuse_existing',
|
|
1142
|
+
resolutionRequired: false,
|
|
1143
|
+
predecessorWorkId: terminalContinuationSource.workId,
|
|
1144
|
+
work: summarizeWorkContract(planStepWork),
|
|
1145
|
+
},
|
|
1146
|
+
evidenceRefs: planStepWork.evidenceRefs,
|
|
1147
|
+
suggestedNextActions: [{ label: 'Continue successor Work', tool: 'rh_work', operation: 'continue', payload: { work_id: planStepWork.workId }, risk: 'readonly', confidence: 'high' }],
|
|
1148
|
+
rawAvailable: false,
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
|
|
702
1152
|
if (planStep?.workId && !explicitPlanStepWork) {
|
|
703
1153
|
return buildFacadeResult({
|
|
704
1154
|
status: 'blocked',
|
|
705
|
-
summary: `PLAN_STEP_BOUND_WORK_MISSING: ${
|
|
706
|
-
data: { executionStarted: false, workContractCreated: false, planId:
|
|
1155
|
+
summary: `PLAN_STEP_BOUND_WORK_MISSING: ${resolvedPlanId}/${resolvedPlanStepId} is bound to ${planStep.workId}, but that Work is not active. Repair the exact binding; do not create a replacement Work.`,
|
|
1156
|
+
data: { executionStarted: false, workContractCreated: false, planId: resolvedPlanId, planStepId: resolvedPlanStepId, boundWorkId: planStep.workId, repairRequired: true },
|
|
707
1157
|
suggestedNextActions: [{
|
|
708
1158
|
label: 'Diagnose exact Plan step binding',
|
|
709
1159
|
tool: 'rh_work',
|
|
710
1160
|
operation: 'repair',
|
|
711
|
-
payload: { plan_id:
|
|
1161
|
+
payload: { plan_id: resolvedPlanId, plan_step_id: resolvedPlanStepId, repair_operation: 'diagnose', dry_run: true },
|
|
712
1162
|
risk: 'readonly',
|
|
713
1163
|
confidence: 'high',
|
|
714
1164
|
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.',
|
|
@@ -728,7 +1178,7 @@ export function startGoalWorkloop(
|
|
|
728
1178
|
if (planStepWork && !requestedRelation) {
|
|
729
1179
|
return buildFacadeResult({
|
|
730
1180
|
status: 'ok',
|
|
731
|
-
summary: `PLAN_STEP_REUSES_ACTIVE_WORK: ${
|
|
1181
|
+
summary: `PLAN_STEP_REUSES_ACTIVE_WORK: ${resolvedPlanId}/${resolvedPlanStepId} already executes as ${planStepWork.workId}; reuse that Work instead of creating another.`,
|
|
732
1182
|
data: {
|
|
733
1183
|
executionStarted: false,
|
|
734
1184
|
workContractCreated: false,
|
|
@@ -742,9 +1192,9 @@ export function startGoalWorkloop(
|
|
|
742
1192
|
});
|
|
743
1193
|
}
|
|
744
1194
|
|
|
745
|
-
if (requestedRelation === 'continue' || requestedRelation === 'extend') {
|
|
1195
|
+
if ((requestedRelation === 'continue' || requestedRelation === 'extend') && !terminalContinuationSource) {
|
|
746
1196
|
if (!deterministicTarget) {
|
|
747
|
-
return resolutionRequired(`${requestedRelation.toUpperCase()}_TARGET_REQUIRED: select related_work_id or bind the request to an active Plan
|
|
1197
|
+
return resolutionRequired(`${requestedRelation.toUpperCase()}_TARGET_REQUIRED: select related_work_id or bind the request to an exact active Plan step before execution.`);
|
|
748
1198
|
}
|
|
749
1199
|
if (requestedRelation === 'extend' && deterministicTarget.planId) {
|
|
750
1200
|
return resolutionRequired(
|
|
@@ -793,12 +1243,38 @@ export function startGoalWorkloop(
|
|
|
793
1243
|
);
|
|
794
1244
|
}
|
|
795
1245
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
1246
|
+
// Pure remote effects do not participate in repository workspace ownership.
|
|
1247
|
+
// An unrelated canonical writer, dirty checkout, or repository parallelism must
|
|
1248
|
+
// not manufacture a Git worktree for browser/API work that has no source authority.
|
|
1249
|
+
// Explicit typed isolation remains authoritative for the rare remote effect that
|
|
1250
|
+
// genuinely declares a repository workspace requirement.
|
|
1251
|
+
const repositoryWorkspaceParticipant = resolvedWorkKind !== 'remote_effect';
|
|
1252
|
+
const placementConflict = repositoryWorkspaceParticipant && Boolean(workspaceOwner);
|
|
1253
|
+
const trustedDirtyPaths = ctx.workspaceChangedPaths
|
|
1254
|
+
? [...new Set(ctx.workspaceChangedPaths.map((path) => path.trim()).filter(Boolean))].sort()
|
|
1255
|
+
: undefined;
|
|
1256
|
+
const dirtyWorkspaceOwnershipConflict = repositoryWorkspaceParticipant
|
|
1257
|
+
&& input.modeInput.workspaceDirty === true
|
|
1258
|
+
&& (
|
|
1259
|
+
!trustedDirtyPaths
|
|
1260
|
+
|| trustedDirtyPaths.length === 0
|
|
1261
|
+
|| effectiveAllowedPaths.length === 0
|
|
1262
|
+
|| trustedDirtyPaths.some((path) => (
|
|
1263
|
+
effectiveForbiddenPaths.some((pattern) => globMatches(pattern, path))
|
|
1264
|
+
|| !effectiveAllowedPaths.some((pattern) => globMatches(pattern, path))
|
|
1265
|
+
))
|
|
1266
|
+
);
|
|
1267
|
+
const automaticRepositoryIsolation = repositoryWorkspaceParticipant && (
|
|
1268
|
+
placementConflict
|
|
1269
|
+
|| dirtyWorkspaceOwnershipConflict
|
|
799
1270
|
|| input.modeInput.requiresParallelism === true
|
|
800
|
-
|
|
801
|
-
|
|
1271
|
+
);
|
|
1272
|
+
const needsWorktree = executionMode === 'goal_workloop' && (
|
|
1273
|
+
placementConstraint.requireWorktree
|
|
1274
|
+
|| automaticRepositoryIsolation
|
|
1275
|
+
|| routeDecision.requiresIsolation === true
|
|
1276
|
+
);
|
|
1277
|
+
if (!needsWorktree && workspaceOwner && repositoryWorkspaceParticipant) {
|
|
802
1278
|
return buildFacadeResult({
|
|
803
1279
|
status: 'blocked',
|
|
804
1280
|
summary: `WORKSPACE_OWNERSHIP_INVARIANT: unresolved admission reached checkout ${ctx.checkoutId} while active Work ${workspaceOwner.workId} owns it. This is an internal routing invariant, not a normal fallback path.`,
|
|
@@ -812,9 +1288,17 @@ export function startGoalWorkloop(
|
|
|
812
1288
|
rawAvailable: false,
|
|
813
1289
|
});
|
|
814
1290
|
}
|
|
815
|
-
const
|
|
816
|
-
if (
|
|
817
|
-
|
|
1291
|
+
const requestedWorkId = input.workId?.trim();
|
|
1292
|
+
if (requestedWorkId && !/^work-[a-z0-9][a-z0-9-]{0,199}$/i.test(requestedWorkId)) {
|
|
1293
|
+
return buildFacadeResult({
|
|
1294
|
+
status: 'blocked',
|
|
1295
|
+
summary: 'WORK_ID_INVALID: explicit Work ids must use the canonical work-<slug> form.',
|
|
1296
|
+
data: { executionStarted: false, workContractCreated: false },
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
const generatedWorkId = requestedWorkId ?? workIdFor(effectiveObjective);
|
|
1300
|
+
if (resolvedPlanId || resolvedPlanStepId) {
|
|
1301
|
+
if (!resolvedPlanId || !resolvedPlanStepId || !ctx.planStore || !ctx.sourceRevision || !plan || !planStep) {
|
|
818
1302
|
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
1303
|
}
|
|
820
1304
|
if (plan.status !== 'approved' && plan.status !== 'executing') {
|
|
@@ -822,8 +1306,8 @@ export function startGoalWorkloop(
|
|
|
822
1306
|
}
|
|
823
1307
|
if (plan.sourceRevision !== ctx.sourceRevision) {
|
|
824
1308
|
const invalidated = claimPlanStepForWork(ctx.planStore, {
|
|
825
|
-
planId:
|
|
826
|
-
stepId:
|
|
1309
|
+
planId: resolvedPlanId,
|
|
1310
|
+
stepId: resolvedPlanStepId,
|
|
827
1311
|
workId: generatedWorkId,
|
|
828
1312
|
sourceRevision: ctx.sourceRevision,
|
|
829
1313
|
});
|
|
@@ -846,46 +1330,48 @@ export function startGoalWorkloop(
|
|
|
846
1330
|
normalized.validCheckIds,
|
|
847
1331
|
normalized.suggestedNextActions,
|
|
848
1332
|
);
|
|
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
1333
|
const remoteDeliveryRequired = resolvedWorkKind === 'repository_change'
|
|
869
1334
|
&& input.modeInput.requiresExternalEffect === true
|
|
870
1335
|
&& input.modeInput.remoteWrite === true;
|
|
1336
|
+
const effectiveConstraints: WorkContract['constraints'] = {
|
|
1337
|
+
...canonicalConstraints,
|
|
1338
|
+
...(needsWorktree ? { workspaceMode: 'isolated' as const, requireWorktree: true } : {}),
|
|
1339
|
+
...(remoteDeliveryRequired ? { remoteDeliveryRequired: true } : {}),
|
|
1340
|
+
};
|
|
1341
|
+
const worktreeReason = placementConstraint.requireWorktree || routeDecision.requiresIsolation === true
|
|
1342
|
+
? 'Typed placement or Route Policy requires isolated execution.'
|
|
1343
|
+
: dirtyWorkspaceOwnershipConflict
|
|
1344
|
+
? 'Trusted repository observation found dirty paths outside or ambiguous to the Work path fence; isolated placement prevents unrelated changes from entering Work ownership or verification.'
|
|
1345
|
+
: placementConflict
|
|
1346
|
+
? 'The selected checkout is already owned by another active Work; this Work requires isolated placement.'
|
|
1347
|
+
: repositoryWorkspaceParticipant && input.modeInput.requiresParallelism === true
|
|
1348
|
+
? 'Parallel Work requires isolated placement.'
|
|
1349
|
+
: 'Current workspace is the stability-first default; isolation remains opt-in.';
|
|
1350
|
+
const forgeInstanceId = ctx.workStore.controllerHome
|
|
1351
|
+
? readForgeInstanceIdentity(ctx.workStore.controllerHome)?.instanceId
|
|
1352
|
+
: undefined;
|
|
871
1353
|
const work = createWorkContract(ctx.workStore, {
|
|
872
1354
|
workId: generatedWorkId,
|
|
873
1355
|
repoId: ctx.repoId,
|
|
874
1356
|
checkoutId: needsWorktree ? undefined : ctx.checkoutId,
|
|
1357
|
+
executionPlacement: executionPlacement({
|
|
1358
|
+
...(forgeInstanceId ? { forgeInstanceId } : {}),
|
|
1359
|
+
repositoryId: ctx.repoId,
|
|
1360
|
+
...(!needsWorktree && ctx.checkoutId ? { checkoutId: ctx.checkoutId } : {}),
|
|
1361
|
+
}),
|
|
875
1362
|
principalId: ctx.principalId,
|
|
876
1363
|
controllerInstanceId: ctx.controllerInstanceId,
|
|
877
1364
|
baseRevision: ctx.sourceRevision,
|
|
1365
|
+
repositoryBaseState: ctx.sourceBaseState ?? (ctx.sourceRevision ? 'revision' : undefined),
|
|
878
1366
|
workspaceFingerprint: needsWorktree ? undefined : ctx.workspaceFingerprint,
|
|
879
1367
|
routeDecisionFingerprint: routeDecision.inputFingerprint,
|
|
880
1368
|
routeDecision,
|
|
881
1369
|
mode: executionMode,
|
|
882
|
-
objective:
|
|
1370
|
+
objective: effectiveObjective,
|
|
883
1371
|
acceptanceCriteria: effectiveAcceptanceCriteria,
|
|
884
|
-
constraints:
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
},
|
|
888
|
-
risk: workRiskFor(input),
|
|
1372
|
+
constraints: effectiveConstraints,
|
|
1373
|
+
risk: workRisk,
|
|
1374
|
+
engineeringContext,
|
|
889
1375
|
// Remote delivery is a risk/effect dimension, not a reason to erase a
|
|
890
1376
|
// repository-change Work's semantic identity. Pure external actions with no
|
|
891
1377
|
// repository-change signal remain remote_effect and keep plugin receipt semantics.
|
|
@@ -895,9 +1381,10 @@ export function startGoalWorkloop(
|
|
|
895
1381
|
issueId: input.issueId,
|
|
896
1382
|
taskId: input.taskId,
|
|
897
1383
|
requirementId: effectiveRequirementId,
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1384
|
+
predecessorWorkId: terminalContinuationSource?.workId,
|
|
1385
|
+
planId: resolvedPlanId,
|
|
1386
|
+
planStepId: resolvedPlanStepId,
|
|
1387
|
+
planSourceRevision: resolvedPlanId ? ctx.sourceRevision : undefined,
|
|
901
1388
|
scopeSummary: input.modeInput.scopeClear ? 'scope declared at start' : 'scope incomplete',
|
|
902
1389
|
scopeEvidence: {
|
|
903
1390
|
initialLikelyPaths: [...new Set(input.initialLikelyPaths ?? effectiveAllowedPaths)].slice(0, 100),
|
|
@@ -917,9 +1404,7 @@ export function startGoalWorkloop(
|
|
|
917
1404
|
},
|
|
918
1405
|
worktreePolicy: {
|
|
919
1406
|
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.',
|
|
1407
|
+
reason: worktreeReason,
|
|
923
1408
|
},
|
|
924
1409
|
worktreeRef: undefined,
|
|
925
1410
|
evidencePolicy: {
|
|
@@ -938,17 +1423,18 @@ export function startGoalWorkloop(
|
|
|
938
1423
|
handoffOnAmbiguity: true,
|
|
939
1424
|
},
|
|
940
1425
|
requestedBy: input.requestedBy ?? 'chatgpt',
|
|
941
|
-
|
|
1426
|
+
requestId: input.requestId?.trim() || undefined,
|
|
1427
|
+
evidenceRefs: [initialEvidence(effectiveObjective)],
|
|
942
1428
|
policyDecisions: policy ? [policy] : [],
|
|
943
1429
|
suggestedNextActions: initialSuggestedNextActions,
|
|
944
|
-
continuationPrompt: `Continue work ${ctx.repoId}: ${
|
|
1430
|
+
continuationPrompt: `Continue work ${ctx.repoId}: ${effectiveObjective.slice(0, 200)}`,
|
|
945
1431
|
});
|
|
946
1432
|
|
|
947
|
-
if (
|
|
1433
|
+
if (resolvedPlanId && resolvedPlanStepId && ctx.planStore && ctx.sourceRevision) {
|
|
948
1434
|
try {
|
|
949
1435
|
const claimed = claimPlanStepForWork(ctx.planStore, {
|
|
950
|
-
planId:
|
|
951
|
-
stepId:
|
|
1436
|
+
planId: resolvedPlanId,
|
|
1437
|
+
stepId: resolvedPlanStepId,
|
|
952
1438
|
workId: work.workId,
|
|
953
1439
|
sourceRevision: ctx.sourceRevision,
|
|
954
1440
|
});
|
|
@@ -963,16 +1449,18 @@ export function startGoalWorkloop(
|
|
|
963
1449
|
return buildFacadeResult({
|
|
964
1450
|
status: 'blocked',
|
|
965
1451
|
summary: error instanceof Error ? error.message : 'PLAN_STEP_CLAIM_FAILED',
|
|
966
|
-
data: { planId:
|
|
1452
|
+
data: { planId: resolvedPlanId, planStepId: resolvedPlanStepId, executionStarted: false, workContractCreated: true, work: summarizeWorkContract(work), canonicalWorkRetained: true },
|
|
967
1453
|
});
|
|
968
1454
|
}
|
|
969
1455
|
}
|
|
970
1456
|
|
|
971
1457
|
return buildFacadeResult({
|
|
972
1458
|
status: 'ok',
|
|
973
|
-
summary:
|
|
974
|
-
? `
|
|
975
|
-
:
|
|
1459
|
+
summary: terminalContinuationSource
|
|
1460
|
+
? `Successor Work ${work.workId} continues semantic lineage from terminal ${terminalContinuationSource.workId}${resolvedPlanId && resolvedPlanStepId ? ` at ${resolvedPlanId}/${resolvedPlanStepId}` : ''}.`
|
|
1461
|
+
: executionMode === 'direct_control'
|
|
1462
|
+
? `Direct-control Work lineage started as ${work.workId}.`
|
|
1463
|
+
: `Goal workloop started as ${work.workId}.`,
|
|
976
1464
|
data: {
|
|
977
1465
|
mode: {
|
|
978
1466
|
mode: executionMode,
|
|
@@ -1026,6 +1514,150 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1026
1514
|
});
|
|
1027
1515
|
}
|
|
1028
1516
|
|
|
1517
|
+
if (input.verifiedEngineeringEvidence) {
|
|
1518
|
+
const sourceIdentity = ctx.sourceRevision?.trim()
|
|
1519
|
+
? { kind: 'revision' as const, revision: ctx.sourceRevision.trim() }
|
|
1520
|
+
: ctx.sourceBaseState === 'unborn'
|
|
1521
|
+
? { kind: 'unborn' as const }
|
|
1522
|
+
: { kind: 'unknown' as const };
|
|
1523
|
+
let refreshedEngineeringContext;
|
|
1524
|
+
try {
|
|
1525
|
+
refreshedEngineeringContext = buildEngineeringContextReceipt({
|
|
1526
|
+
risk: work.risk,
|
|
1527
|
+
sourceIdentity,
|
|
1528
|
+
evidence: input.verifiedEngineeringEvidence,
|
|
1529
|
+
recordedAt: nowIso(ctx),
|
|
1530
|
+
});
|
|
1531
|
+
} catch (error) {
|
|
1532
|
+
return buildFacadeResult({
|
|
1533
|
+
status: 'blocked',
|
|
1534
|
+
summary: error instanceof Error ? error.message : 'ENGINEERING_CONTEXT_INVALID',
|
|
1535
|
+
data: { work: summarizeWorkContract(work), engineeringEvidenceUpdated: false },
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
const priorDesign = work.engineeringContext?.evidence.designDecisionReceipt;
|
|
1539
|
+
if (work.engineeringContext?.designState === 'revisit_required' && priorDesign) {
|
|
1540
|
+
const nextDesign = refreshedEngineeringContext.evidence.designDecisionReceipt;
|
|
1541
|
+
if (!nextDesign || nextDesign.supersedesReceiptId !== priorDesign.receiptId) {
|
|
1542
|
+
return buildFacadeResult({
|
|
1543
|
+
status: 'blocked',
|
|
1544
|
+
summary: 'ENGINEERING_DESIGN_SUPERSESSION_REQUIRED: same-root-cause re-entry requires a new design receipt that explicitly supersedes the prior design.',
|
|
1545
|
+
data: { work: summarizeWorkContract(work), priorDesignReceiptId: priorDesign.receiptId },
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
if (refreshedEngineeringContext.evidence.independentCritiqueReceipt?.decision !== 'approved') {
|
|
1549
|
+
return buildFacadeResult({
|
|
1550
|
+
status: 'blocked',
|
|
1551
|
+
summary: 'ENGINEERING_DESIGN_CRITIQUE_APPROVAL_REQUIRED: same-root-cause re-entry requires an independently approved critique of the superseding design.',
|
|
1552
|
+
data: { work: summarizeWorkContract(work), priorDesignReceiptId: priorDesign.receiptId, nextDesignReceiptId: nextDesign.receiptId },
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
work = updateWorkContract(ctx.workStore, work.workId, { engineeringContext: refreshedEngineeringContext });
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
if (work.engineeringContext?.designState === 'revisit_required') {
|
|
1560
|
+
return buildFacadeResult({
|
|
1561
|
+
status: 'blocked',
|
|
1562
|
+
summary: 'ENGINEERING_DESIGN_REVISIT_REQUIRED: same-root-cause design authority must be explicitly superseded and independently approved before further Work mutation.',
|
|
1563
|
+
data: { work: summarizeWorkContract(work) },
|
|
1564
|
+
suggestedNextActions: [{
|
|
1565
|
+
label: 'Refresh design evidence',
|
|
1566
|
+
tool: 'rh_context',
|
|
1567
|
+
operation: 'search',
|
|
1568
|
+
payload: { work_id: work.workId, query: 'Refresh current source and design evidence for this Work before engineering re-entry.' },
|
|
1569
|
+
risk: 'readonly',
|
|
1570
|
+
}],
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
if (input.engineeringBlocker) {
|
|
1575
|
+
if (!work.engineeringContext || work.engineeringContext.sourceIdentity.kind !== 'revision') {
|
|
1576
|
+
return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_SOURCE_IDENTITY_REQUIRED', data: { work: summarizeWorkContract(work) } });
|
|
1577
|
+
}
|
|
1578
|
+
let blocker;
|
|
1579
|
+
let linkedWork: WorkContract | undefined;
|
|
1580
|
+
try {
|
|
1581
|
+
let linkedWorkId: string | undefined;
|
|
1582
|
+
if (input.engineeringBlocker.classification === 'unrelated') {
|
|
1583
|
+
linkedWorkId = linkedEngineeringBlockerWorkId(work.workId, input.engineeringBlocker.blockerId);
|
|
1584
|
+
linkedWork = getWorkContract(ctx.workStore, linkedWorkId);
|
|
1585
|
+
if (!linkedWork) {
|
|
1586
|
+
const linked = routeWorkStart(ctx, {
|
|
1587
|
+
workId: linkedWorkId,
|
|
1588
|
+
objective: `Resolve unrelated blocker ${input.engineeringBlocker.blockerId}: ${input.engineeringBlocker.rationale}`,
|
|
1589
|
+
acceptanceCriteria: [
|
|
1590
|
+
`Resolve blocker ${input.engineeringBlocker.blockerId} without widening Work ${work.workId}.`,
|
|
1591
|
+
'Return bounded evidence or a precise wake/blocker condition to the owning Controller.',
|
|
1592
|
+
],
|
|
1593
|
+
allowedPaths: [],
|
|
1594
|
+
initialLikelyPaths: [],
|
|
1595
|
+
forbiddenPaths: [],
|
|
1596
|
+
checks: [],
|
|
1597
|
+
modeInput: {
|
|
1598
|
+
scopeClear: false,
|
|
1599
|
+
mutation: false,
|
|
1600
|
+
requiresInvestigation: true,
|
|
1601
|
+
requiresRecovery: true,
|
|
1602
|
+
requiresParallelism: true,
|
|
1603
|
+
},
|
|
1604
|
+
requestedBy: 'chatgpt',
|
|
1605
|
+
relatedWorkId: work.workId,
|
|
1606
|
+
workRelation: 'parallel',
|
|
1607
|
+
requirementId: work.requirementId,
|
|
1608
|
+
workKind: 'investigation',
|
|
1609
|
+
constraints: { accessMode: work.constraints.accessMode },
|
|
1610
|
+
});
|
|
1611
|
+
linkedWork = getWorkContract(ctx.workStore, linkedWorkId);
|
|
1612
|
+
if (linked.status !== 'ok' || !linkedWork) {
|
|
1613
|
+
return buildFacadeResult({
|
|
1614
|
+
status: 'blocked',
|
|
1615
|
+
summary: `ENGINEERING_LINKED_WORK_ADMISSION_FAILED: ${linked.summary}`,
|
|
1616
|
+
data: { work: summarizeWorkContract(work), linkedWorkId, linkedWorkCreated: false },
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
blocker = buildEngineeringBlockerDispositionReceipt({
|
|
1622
|
+
sourceRevision: work.engineeringContext.sourceIdentity.revision,
|
|
1623
|
+
blockerId: input.engineeringBlocker.blockerId,
|
|
1624
|
+
classification: input.engineeringBlocker.classification,
|
|
1625
|
+
semanticScopeKeys: input.engineeringBlocker.semanticScopeKeys ?? work.engineeringContext.semanticScope?.keys ?? [],
|
|
1626
|
+
...(linkedWork ? { linkedWorkId: linkedWork.workId } : {}),
|
|
1627
|
+
rationale: input.engineeringBlocker.rationale,
|
|
1628
|
+
recordedAt: nowIso(ctx),
|
|
1629
|
+
});
|
|
1630
|
+
const nextEngineering = applyEngineeringBlockerDisposition(work.engineeringContext, blocker);
|
|
1631
|
+
work = updateWorkContract(ctx.workStore, work.workId, { engineeringContext: nextEngineering });
|
|
1632
|
+
} catch (error) {
|
|
1633
|
+
return buildFacadeResult({ status: 'blocked', summary: error instanceof Error ? error.message : 'ENGINEERING_BLOCKER_INVALID', data: { work: summarizeWorkContract(work) } });
|
|
1634
|
+
}
|
|
1635
|
+
const returnToDesign = blocker.action === 'return_to_design';
|
|
1636
|
+
return buildFacadeResult({
|
|
1637
|
+
status: 'blocked',
|
|
1638
|
+
summary: returnToDesign
|
|
1639
|
+
? `Same-root-cause blocker ${blocker.blockerId} requires Product/Design re-entry before further mutation.`
|
|
1640
|
+
: `Unrelated blocker ${blocker.blockerId} is linked to ${blocker.linkedWorkId}; current Work semantic scope is unchanged.`,
|
|
1641
|
+
data: {
|
|
1642
|
+
work: summarizeWorkContract(work),
|
|
1643
|
+
engineeringBlocker: blocker,
|
|
1644
|
+
...(linkedWork ? { linkedWork: summarizeWorkContract(linkedWork), linkedWorkCreated: true } : {}),
|
|
1645
|
+
nextStep: blocker.action,
|
|
1646
|
+
},
|
|
1647
|
+
suggestedNextActions: returnToDesign
|
|
1648
|
+
? [{
|
|
1649
|
+
label: 'Refresh design evidence',
|
|
1650
|
+
tool: 'rh_context',
|
|
1651
|
+
operation: 'search',
|
|
1652
|
+
payload: { work_id: work.workId, query: 'Refresh current source and design evidence for this Work before engineering re-entry.' },
|
|
1653
|
+
risk: 'readonly',
|
|
1654
|
+
}]
|
|
1655
|
+
: linkedWork
|
|
1656
|
+
? [{ label: 'Continue linked Work', tool: 'rh_work', operation: 'continue', payload: { work_id: linkedWork.workId }, risk: 'workspace_write' }]
|
|
1657
|
+
: [],
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1029
1661
|
if ((input.reviewFindings?.length ?? 0) > 0 && work.workKind !== 'read_only_review') {
|
|
1030
1662
|
return buildFacadeResult({
|
|
1031
1663
|
status: 'blocked',
|
|
@@ -1185,8 +1817,11 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1185
1817
|
}
|
|
1186
1818
|
}
|
|
1187
1819
|
|
|
1188
|
-
const currentCheckRefs =
|
|
1189
|
-
|
|
1820
|
+
const currentCheckRefs = effectiveCurrentWorkVerificationRecords(
|
|
1821
|
+
work,
|
|
1822
|
+
ctx.sourceRevision,
|
|
1823
|
+
ctx.workspaceFingerprint,
|
|
1824
|
+
);
|
|
1190
1825
|
const history = reconcileVerificationHistory(
|
|
1191
1826
|
currentCheckRefs.map((record) => ({ checkId: record.checkId, outcome: record.outcome, recordedAt: record.recordedAt })),
|
|
1192
1827
|
);
|
|
@@ -1530,38 +2165,35 @@ export function continueGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1530
2165
|
});
|
|
1531
2166
|
}
|
|
1532
2167
|
|
|
1533
|
-
const
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
2168
|
+
const changedPaths = normalizeImplementationReviewChangedPaths(ctx.workspaceChangedPaths ?? work.scopeEvidence?.actualChangedPaths ?? []);
|
|
2169
|
+
if (workRequiresImplementationReview(work.workKind, changedPaths)) {
|
|
2170
|
+
recordWorkScopeEvidence(ctx.workStore, work.workId, { actualChangedPaths: changedPaths });
|
|
2171
|
+
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
2172
|
+
status: 'running',
|
|
2173
|
+
phase: 'verification',
|
|
2174
|
+
state: 'satisfied',
|
|
2175
|
+
summary: 'Exact Work completion evidence proves the current candidate is verified and eligible for implementation review.',
|
|
2176
|
+
evidenceRefs: work.evidenceRefs,
|
|
2177
|
+
});
|
|
2178
|
+
requestWorkImplementationReview(ctx.workStore, work.workId, 'Implementation and verification evidence are complete; explicit Controller implementation review is required before delivery.');
|
|
2179
|
+
const suggested = validateSuggestedNextActions([implementationReviewSuggestedAction(work.workId)]).actions;
|
|
2180
|
+
const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
|
|
2181
|
+
return buildFacadeResult({
|
|
2182
|
+
status: 'ok',
|
|
2183
|
+
summary: 'Continue: implementation and verification evidence are complete; explicit implementation review is next.',
|
|
2184
|
+
data: { work: summarizeWorkContract(updated), backgroundCompleted: false, nextStep: 'review' },
|
|
2185
|
+
suggestedNextActions: suggested,
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
const suggested = validateSuggestedNextActions([{
|
|
2189
|
+
label: 'Finalize work', tool: 'rh_work', operation: 'finalize', payload: { work_id: work.workId }, risk: 'readonly', confidence: 'high',
|
|
2190
|
+
}]).actions;
|
|
1543
2191
|
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
1544
|
-
status: 'running',
|
|
1545
|
-
|
|
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,
|
|
2192
|
+
status: 'running', phase: 'delivery', state: 'active',
|
|
2193
|
+
summary: 'Source-free Work evidence is complete; implementation review is not required and semantic finalization is next.', evidenceRefs: work.evidenceRefs,
|
|
1564
2194
|
});
|
|
2195
|
+
const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
|
|
2196
|
+
return buildFacadeResult({ status: 'ok', summary: 'Continue: evidence is complete; ready to finalize.', data: { work: summarizeWorkContract(updated), backgroundCompleted: false, nextStep: 'finalize' }, suggestedNextActions: suggested });
|
|
1565
2197
|
}
|
|
1566
2198
|
|
|
1567
2199
|
export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopVerifyInput): FacadeResult {
|
|
@@ -1668,17 +2300,49 @@ export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloop
|
|
|
1668
2300
|
const completionEvidence = classified.outcome === 'valid_pass'
|
|
1669
2301
|
? evaluateWorkCompletionEvidence(updated, sourceRevision, input.workspaceFingerprint)
|
|
1670
2302
|
: undefined;
|
|
1671
|
-
const
|
|
2303
|
+
const validPassReadyForNextBoundary = completionEvidence?.status === 'complete';
|
|
2304
|
+
const currentChangedPaths = normalizeImplementationReviewChangedPaths(ctx.workspaceChangedPaths ?? updated.scopeEvidence?.actualChangedPaths ?? []);
|
|
2305
|
+
const latestReview = latestImplementationReview(updated.implementationReviews);
|
|
2306
|
+
const approvedReviewRemainsAuthoritative = Boolean(
|
|
2307
|
+
validPassReadyForNextBoundary
|
|
2308
|
+
&& updated.phase === 'delivery'
|
|
2309
|
+
&& updated.phaseEvidence.review.state === 'satisfied'
|
|
2310
|
+
&& latestReview?.decision === 'approved'
|
|
2311
|
+
&& sourceRevision
|
|
2312
|
+
&& input.workspaceFingerprint
|
|
2313
|
+
&& latestReview.sourceRevision === sourceRevision
|
|
2314
|
+
&& latestReview.verificationWorkspaceFingerprint === input.workspaceFingerprint
|
|
2315
|
+
&& latestReview.changedPathDigest === implementationReviewChangedPathDigest(currentChangedPaths)
|
|
2316
|
+
);
|
|
2317
|
+
const reviewRequiredAfterPass = validPassReadyForNextBoundary
|
|
2318
|
+
&& !approvedReviewRemainsAuthoritative
|
|
2319
|
+
&& workRequiresImplementationReview(updated.workKind, currentChangedPaths);
|
|
2320
|
+
if (approvedReviewRemainsAuthoritative && updated.evidenceState !== 'valid') {
|
|
2321
|
+
recordWorkEvidenceState(ctx.workStore, updated.workId, 'valid');
|
|
2322
|
+
}
|
|
2323
|
+
if (reviewRequiredAfterPass) {
|
|
2324
|
+
recordWorkScopeEvidence(ctx.workStore, updated.workId, { actualChangedPaths: [...(ctx.workspaceChangedPaths ?? [])] });
|
|
2325
|
+
transitionWorkContractPhase(ctx.workStore, updated.workId, {
|
|
2326
|
+
status: 'running',
|
|
2327
|
+
phase: 'verification',
|
|
2328
|
+
state: 'satisfied',
|
|
2329
|
+
summary: 'All required exact Work verification receipts are current; implementation review admission is now allowed.',
|
|
2330
|
+
evidenceRefs: updated.evidenceRefs,
|
|
2331
|
+
});
|
|
2332
|
+
requestWorkImplementationReview(ctx.workStore, updated.workId, 'Verification is complete; explicit Controller implementation review is required before delivery.');
|
|
2333
|
+
}
|
|
1672
2334
|
const suggested = validateSuggestedNextActions(
|
|
1673
2335
|
classified.outcome === 'valid_pass'
|
|
1674
|
-
? [
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
2336
|
+
? [reviewRequiredAfterPass
|
|
2337
|
+
? implementationReviewSuggestedAction(work.workId)
|
|
2338
|
+
: {
|
|
2339
|
+
label: validPassReadyForNextBoundary ? 'Finalize work' : 'Continue workloop',
|
|
2340
|
+
tool: 'rh_work',
|
|
2341
|
+
operation: validPassReadyForNextBoundary ? 'finalize' : 'continue',
|
|
2342
|
+
payload: { work_id: work.workId },
|
|
2343
|
+
risk: 'readonly',
|
|
2344
|
+
confidence: 'high',
|
|
2345
|
+
}]
|
|
1682
2346
|
: classified.outcome === 'valid_fail'
|
|
1683
2347
|
? [{
|
|
1684
2348
|
label: 'Continue for review handoff',
|
|
@@ -1714,7 +2378,7 @@ export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloop
|
|
|
1714
2378
|
},
|
|
1715
2379
|
backgroundCompleted: false,
|
|
1716
2380
|
...(classified.outcome === 'valid_pass'
|
|
1717
|
-
? { nextStep:
|
|
2381
|
+
? { nextStep: reviewRequiredAfterPass ? 'review' : validPassReadyForNextBoundary ? 'finalize' : 'continue' }
|
|
1718
2382
|
: {}),
|
|
1719
2383
|
},
|
|
1720
2384
|
warnings: classified.warnings,
|
|
@@ -1723,6 +2387,70 @@ export function verifyGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloop
|
|
|
1723
2387
|
});
|
|
1724
2388
|
}
|
|
1725
2389
|
|
|
2390
|
+
export function reviewGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopReviewInput): FacadeResult {
|
|
2391
|
+
let work = getWorkContract(ctx.workStore, input.workId);
|
|
2392
|
+
if (!work) return buildFacadeResult({ status: 'not_found', summary: `WorkContract ${input.workId} not found.`, data: { workId: input.workId } });
|
|
2393
|
+
if (work.status === 'completed' || work.status === 'cancelled' || work.status === 'failed') {
|
|
2394
|
+
return buildFacadeResult({ status: 'blocked', summary: `WorkContract ${work.workId} is terminal (${work.status}); implementation review was not recorded.`, data: { work: summarizeWorkContract(work) } });
|
|
2395
|
+
}
|
|
2396
|
+
const reviewerPrincipalId = ctx.principalId?.trim() ?? '';
|
|
2397
|
+
if (!reviewerPrincipalId) {
|
|
2398
|
+
return buildFacadeResult({ status: 'blocked', summary: 'WORK_IMPLEMENTATION_REVIEW_REVIEWER_REQUIRED: authenticated reviewer principal is required.', data: { work: summarizeWorkContract(work) } });
|
|
2399
|
+
}
|
|
2400
|
+
if (!input.rationale?.trim()) {
|
|
2401
|
+
return buildFacadeResult({ status: 'blocked', summary: 'WORK_IMPLEMENTATION_REVIEW_RATIONALE_REQUIRED: explicit review rationale is required.', data: { work: summarizeWorkContract(work) } });
|
|
2402
|
+
}
|
|
2403
|
+
try {
|
|
2404
|
+
const candidate = currentImplementationReviewCandidate(ctx, work);
|
|
2405
|
+
recordWorkScopeEvidence(ctx.workStore, work.workId, { actualChangedPaths: [...candidate.changedPaths] });
|
|
2406
|
+
work = getWorkContract(ctx.workStore, work.workId) ?? work;
|
|
2407
|
+
if (work.phase !== 'review') {
|
|
2408
|
+
throw new Error('WORK_IMPLEMENTATION_REVIEW_PHASE_REQUIRED: verify or continue the exact candidate into review before recording a decision.');
|
|
2409
|
+
}
|
|
2410
|
+
const at = nowIso(ctx);
|
|
2411
|
+
const review: WorkImplementationReviewRecord = {
|
|
2412
|
+
schemaVersion: 1,
|
|
2413
|
+
reviewId: `impl-review-${randomUUID().slice(0, 12)}`,
|
|
2414
|
+
workId: work.workId,
|
|
2415
|
+
reviewerPrincipalId,
|
|
2416
|
+
decision: input.decision,
|
|
2417
|
+
rationale: input.rationale.trim().slice(0, 4_000),
|
|
2418
|
+
findings: (input.findings ?? []).slice(0, 100).map((finding) => ({
|
|
2419
|
+
severity: finding.severity,
|
|
2420
|
+
category: finding.category.trim().slice(0, 160),
|
|
2421
|
+
summary: finding.summary.trim().slice(0, 1_000),
|
|
2422
|
+
...(finding.path?.trim() ? { path: finding.path.trim() } : {}),
|
|
2423
|
+
...(finding.symbol?.trim() ? { symbol: finding.symbol.trim().slice(0, 240) } : {}),
|
|
2424
|
+
})),
|
|
2425
|
+
sourceRevision: candidate.sourceRevision,
|
|
2426
|
+
workspaceFingerprint: candidate.workspaceFingerprint,
|
|
2427
|
+
verificationWorkspaceFingerprint: candidate.verificationWorkspaceFingerprint,
|
|
2428
|
+
changedPaths: [...candidate.changedPaths],
|
|
2429
|
+
changedPathDigest: implementationReviewChangedPathDigest(candidate.changedPaths),
|
|
2430
|
+
acceptanceCriteriaSummary: implementationReviewAcceptanceSummary(work),
|
|
2431
|
+
verificationEvidence: [...candidate.verificationEvidence],
|
|
2432
|
+
architectureEvidence: [...(candidate.architectureEvidence ?? [])],
|
|
2433
|
+
recordedAt: at,
|
|
2434
|
+
};
|
|
2435
|
+
const updated = recordWorkImplementationReview(ctx.workStore, work.workId, review);
|
|
2436
|
+
const suggested = validateSuggestedNextActions(input.decision === 'approved'
|
|
2437
|
+
? [{ label: 'Finalize reviewed work', tool: 'rh_work', operation: 'finalize', payload: { work_id: work.workId }, risk: 'workspace_write', confidence: 'high' }]
|
|
2438
|
+
: input.decision === 'changes_required'
|
|
2439
|
+
? [{ label: 'Continue implementation repairs', tool: 'rh_work', operation: 'continue', payload: { work_id: work.workId }, risk: 'workspace_write', confidence: 'high' }]
|
|
2440
|
+
: [{ label: 'Inspect blocked review evidence', tool: 'rh_context', operation: 'get', payload: { work_id: work.workId }, risk: 'readonly', confidence: 'high' }]).actions;
|
|
2441
|
+
const persisted = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
|
|
2442
|
+
return buildFacadeResult({
|
|
2443
|
+
status: input.decision === 'blocked' ? 'blocked' : 'ok',
|
|
2444
|
+
summary: `Implementation review ${review.reviewId}: ${input.decision}.`,
|
|
2445
|
+
data: { work: summarizeWorkContract(persisted), review, nextStep: input.decision === 'approved' ? 'finalize' : input.decision === 'changes_required' ? 'continue' : 'blocked' },
|
|
2446
|
+
suggestedNextActions: suggested,
|
|
2447
|
+
});
|
|
2448
|
+
} catch (error) {
|
|
2449
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2450
|
+
return buildFacadeResult({ status: 'blocked', summary: message, data: { work: summarizeWorkContract(work) }, suggestedNextActions: suggestedForWork(work) });
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
|
|
1726
2454
|
export function finalizeGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopFinalizeInput): FacadeResult {
|
|
1727
2455
|
const work = getWorkContract(ctx.workStore, input.workId);
|
|
1728
2456
|
if (!work) {
|
|
@@ -1764,6 +2492,65 @@ export function finalizeGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1764
2492
|
});
|
|
1765
2493
|
}
|
|
1766
2494
|
|
|
2495
|
+
const currentChangedPaths = normalizeImplementationReviewChangedPaths(ctx.workspaceChangedPaths ?? work.scopeEvidence?.actualChangedPaths ?? []);
|
|
2496
|
+
if (workRequiresImplementationReview(work.workKind, currentChangedPaths)) {
|
|
2497
|
+
try {
|
|
2498
|
+
const candidate = currentImplementationReviewCandidate(ctx, work);
|
|
2499
|
+
assertImplementationReviewPreDeliveryBoundary({
|
|
2500
|
+
repoId: work.repoId,
|
|
2501
|
+
workId: work.workId,
|
|
2502
|
+
workKind: work.workKind,
|
|
2503
|
+
reviews: work.implementationReviews,
|
|
2504
|
+
candidate,
|
|
2505
|
+
requiredCheckIds: work.checks,
|
|
2506
|
+
verificationRecords: work.checkRefs,
|
|
2507
|
+
});
|
|
2508
|
+
} catch (error) {
|
|
2509
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2510
|
+
const reviewDecisionBlocked = /WORK_IMPLEMENTATION_REVIEW_(REQUIRED|STALE|CHANGES_REQUIRED|BLOCKED)/.test(message);
|
|
2511
|
+
if (reviewDecisionBlocked) {
|
|
2512
|
+
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
2513
|
+
status: 'running',
|
|
2514
|
+
phase: 'verification',
|
|
2515
|
+
state: 'satisfied',
|
|
2516
|
+
summary: `Current exact verification remains authoritative, but implementation review must be renewed before delivery: ${message}`,
|
|
2517
|
+
evidenceRefs: work.evidenceRefs,
|
|
2518
|
+
});
|
|
2519
|
+
requestWorkImplementationReview(ctx.workStore, work.workId, `Pre-delivery implementation review gate requires a renewed Controller decision: ${message}`);
|
|
2520
|
+
const suggested = validateSuggestedNextActions([implementationReviewSuggestedAction(work.workId)]).actions;
|
|
2521
|
+
const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
|
|
2522
|
+
return buildFacadeResult({
|
|
2523
|
+
status: 'blocked',
|
|
2524
|
+
summary: message,
|
|
2525
|
+
data: { work: summarizeWorkContract(updated), nextStep: 'review' },
|
|
2526
|
+
suggestedNextActions: suggested,
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
transitionWorkContractPhase(ctx.workStore, work.workId, {
|
|
2530
|
+
status: 'running',
|
|
2531
|
+
phase: 'verification',
|
|
2532
|
+
state: 'active',
|
|
2533
|
+
summary: `Pre-delivery review could not prove an exact verified candidate: ${message}`,
|
|
2534
|
+
evidenceRefs: work.evidenceRefs,
|
|
2535
|
+
});
|
|
2536
|
+
const suggested = validateSuggestedNextActions([{
|
|
2537
|
+
label: 'Verify exact candidate',
|
|
2538
|
+
tool: 'rh_work',
|
|
2539
|
+
operation: 'verify',
|
|
2540
|
+
payload: { work_id: work.workId, check_id: work.checks[0] },
|
|
2541
|
+
risk: 'workspace_write',
|
|
2542
|
+
confidence: work.checks[0] ? 'high' : 'medium',
|
|
2543
|
+
}], { validCheckIds: work.checks }).actions;
|
|
2544
|
+
const updated = updateWorkContract(ctx.workStore, work.workId, { suggestedNextActions: suggested });
|
|
2545
|
+
return buildFacadeResult({
|
|
2546
|
+
status: 'blocked',
|
|
2547
|
+
summary: message,
|
|
2548
|
+
data: { work: summarizeWorkContract(updated), nextStep: 'verify' },
|
|
2549
|
+
suggestedNextActions: suggested,
|
|
2550
|
+
});
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
|
|
1767
2554
|
const completionEvidence = evaluateWorkCompletionEvidence(
|
|
1768
2555
|
work,
|
|
1769
2556
|
ctx.sourceRevision,
|
|
@@ -1774,11 +2561,9 @@ export function finalizeGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorklo
|
|
|
1774
2561
|
const history = completionEvidence.history;
|
|
1775
2562
|
|
|
1776
2563
|
if (input.forceFailed || completionEvidence.status === 'failed') {
|
|
1777
|
-
const updated =
|
|
1778
|
-
|
|
1779
|
-
phase: '
|
|
1780
|
-
state: 'failed',
|
|
1781
|
-
summary: `Work failed acceptance/finalization: ${history.acceptanceFailures.join(', ') || 'forced failure'}.`,
|
|
2564
|
+
const updated = failWorkContract(ctx.workStore, work.workId, {
|
|
2565
|
+
phase: work.phase,
|
|
2566
|
+
summary: `Work failed acceptance/finalization while in ${work.phase}: ${history.acceptanceFailures.join(', ') || 'forced failure'}.`,
|
|
1782
2567
|
evidenceRefs: work.evidenceRefs,
|
|
1783
2568
|
});
|
|
1784
2569
|
if (updated.planId && updated.planStepId && ctx.planStore) {
|
|
@@ -2017,10 +2802,7 @@ export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopSt
|
|
|
2017
2802
|
}
|
|
2018
2803
|
|
|
2019
2804
|
const destructiveCleanup = input.authorizeDestructiveCleanup === true;
|
|
2020
|
-
|
|
2021
|
-
status: 'cancelled',
|
|
2022
|
-
phase: 'cleanup',
|
|
2023
|
-
state: 'skipped',
|
|
2805
|
+
cancelWorkContract(ctx.workStore, work.workId, {
|
|
2024
2806
|
summary: input.reason ? `Stopped: ${input.reason}` : 'Work stopped without destructive cleanup.',
|
|
2025
2807
|
evidenceRefs: work.evidenceRefs,
|
|
2026
2808
|
});
|
|
@@ -2068,15 +2850,22 @@ export function stopGoalWorkloop(ctx: GoalWorkloopContext, input: GoalWorkloopSt
|
|
|
2068
2850
|
});
|
|
2069
2851
|
}
|
|
2070
2852
|
|
|
2853
|
+
export interface GoalWorkloopTrustedInput {
|
|
2854
|
+
/** Runtime-composed source-bound evidence. This channel is intentionally separate from raw MCP/tool arguments. */
|
|
2855
|
+
verifiedEngineeringEvidence?: EngineeringAdmissionEvidence;
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2071
2858
|
export function runGoalWorkloop(
|
|
2072
2859
|
ctx: GoalWorkloopContext,
|
|
2073
2860
|
operation: GoalWorkloopOperation,
|
|
2074
2861
|
args: Record<string, unknown>,
|
|
2862
|
+
trusted: GoalWorkloopTrustedInput = {},
|
|
2075
2863
|
): FacadeResult {
|
|
2076
2864
|
switch (operation) {
|
|
2077
2865
|
case 'start':
|
|
2078
2866
|
return routeWorkStart(ctx, {
|
|
2079
2867
|
objective: String(args.objective ?? ''),
|
|
2868
|
+
workId: typeof args.work_id === 'string' ? args.work_id : undefined,
|
|
2080
2869
|
acceptanceCriteria: Array.isArray(args.acceptance_criteria) ? args.acceptance_criteria.map(String) : undefined,
|
|
2081
2870
|
allowedPaths: Array.isArray(args.allowed_paths) ? args.allowed_paths.map(String) : undefined,
|
|
2082
2871
|
initialLikelyPaths: Array.isArray(args.initial_likely_paths) ? args.initial_likely_paths.map(String) : undefined,
|
|
@@ -2090,7 +2879,7 @@ export function runGoalWorkloop(
|
|
|
2090
2879
|
requiresInvestigation: args.requires_investigation === true,
|
|
2091
2880
|
requiresLongRunningChecks: args.requires_long_running_checks === true,
|
|
2092
2881
|
requiresParallelism: args.requires_parallelism === true,
|
|
2093
|
-
explicitMode: args.mode === '
|
|
2882
|
+
explicitMode: args.mode === 'plan' || args.mode === 'scale' ? args.mode : undefined,
|
|
2094
2883
|
needsDependencies: args.needs_dependencies === true,
|
|
2095
2884
|
requiresRecovery: args.requires_recovery === true,
|
|
2096
2885
|
requiresWorker: args.requires_worker === true,
|
|
@@ -2117,6 +2906,7 @@ export function runGoalWorkloop(
|
|
|
2117
2906
|
requirementId: typeof args.requirement_id === 'string' ? args.requirement_id : undefined,
|
|
2118
2907
|
planId: typeof args.plan_id === 'string' ? args.plan_id : undefined,
|
|
2119
2908
|
planStepId: typeof args.plan_step_id === 'string' ? args.plan_step_id : undefined,
|
|
2909
|
+
verifiedEngineeringEvidence: trusted.verifiedEngineeringEvidence,
|
|
2120
2910
|
workKind: args.work_kind === 'repository_change'
|
|
2121
2911
|
|| args.work_kind === 'completed_no_change'
|
|
2122
2912
|
|| args.work_kind === 'read_only_review'
|
|
@@ -2127,7 +2917,24 @@ export function runGoalWorkloop(
|
|
|
2127
2917
|
? args.work_kind
|
|
2128
2918
|
: undefined,
|
|
2129
2919
|
});
|
|
2130
|
-
case 'continue':
|
|
2920
|
+
case 'continue': {
|
|
2921
|
+
let engineeringBlocker: GoalWorkloopContinueInput['engineeringBlocker'];
|
|
2922
|
+
if (args.engineering_blocker !== undefined) {
|
|
2923
|
+
if (!args.engineering_blocker || typeof args.engineering_blocker !== 'object' || Array.isArray(args.engineering_blocker)) {
|
|
2924
|
+
return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_INVALID', data: { workId: String(args.work_id ?? '') } });
|
|
2925
|
+
}
|
|
2926
|
+
const rawBlocker = args.engineering_blocker as Record<string, unknown>;
|
|
2927
|
+
const classification = rawBlocker.classification;
|
|
2928
|
+
if (classification !== 'same_root_cause' && classification !== 'unrelated') {
|
|
2929
|
+
return buildFacadeResult({ status: 'blocked', summary: 'ENGINEERING_BLOCKER_CLASSIFICATION_INVALID', data: { workId: String(args.work_id ?? '') } });
|
|
2930
|
+
}
|
|
2931
|
+
engineeringBlocker = {
|
|
2932
|
+
blockerId: String(rawBlocker.blocker_id ?? ''),
|
|
2933
|
+
classification,
|
|
2934
|
+
rationale: String(rawBlocker.rationale ?? ''),
|
|
2935
|
+
semanticScopeKeys: Array.isArray(rawBlocker.semantic_scope_keys) ? rawBlocker.semantic_scope_keys.map(String) : undefined,
|
|
2936
|
+
};
|
|
2937
|
+
}
|
|
2131
2938
|
return continueGoalWorkloop(ctx, {
|
|
2132
2939
|
workId: String(args.work_id ?? ''),
|
|
2133
2940
|
note: typeof args.note === 'string' ? args.note : undefined,
|
|
@@ -2147,7 +2954,10 @@ export function runGoalWorkloop(
|
|
|
2147
2954
|
acceptanceFailureDecision: args.acceptance_failure_decision === 'repair' || args.acceptance_failure_decision === 'rescope'
|
|
2148
2955
|
? args.acceptance_failure_decision
|
|
2149
2956
|
: undefined,
|
|
2957
|
+
verifiedEngineeringEvidence: trusted.verifiedEngineeringEvidence,
|
|
2958
|
+
engineeringBlocker,
|
|
2150
2959
|
});
|
|
2960
|
+
}
|
|
2151
2961
|
case 'verify':
|
|
2152
2962
|
return verifyGoalWorkloop(ctx, {
|
|
2153
2963
|
workId: String(args.work_id ?? ''),
|
|
@@ -2156,6 +2966,31 @@ export function runGoalWorkloop(
|
|
|
2156
2966
|
checkFailed: args.check_failed === true,
|
|
2157
2967
|
skipped: args.skipped === true,
|
|
2158
2968
|
});
|
|
2969
|
+
case 'review': {
|
|
2970
|
+
if (args.review_decision !== 'approved' && args.review_decision !== 'changes_required' && args.review_decision !== 'blocked') {
|
|
2971
|
+
return buildFacadeResult({
|
|
2972
|
+
status: 'blocked',
|
|
2973
|
+
summary: 'WORK_IMPLEMENTATION_REVIEW_DECISION_REQUIRED: operation=review requires an explicit approved, changes_required, or blocked decision.',
|
|
2974
|
+
data: { workId: String(args.work_id ?? '') },
|
|
2975
|
+
});
|
|
2976
|
+
}
|
|
2977
|
+
return reviewGoalWorkloop(ctx, {
|
|
2978
|
+
workId: String(args.work_id ?? ''),
|
|
2979
|
+
decision: args.review_decision,
|
|
2980
|
+
rationale: typeof args.review_rationale === 'string' ? args.review_rationale : '',
|
|
2981
|
+
findings: Array.isArray(args.implementation_review_findings)
|
|
2982
|
+
? args.implementation_review_findings
|
|
2983
|
+
.filter((value): value is Record<string, unknown> => Boolean(value) && typeof value === 'object')
|
|
2984
|
+
.map((value) => ({
|
|
2985
|
+
severity: value.severity === 'critical' || value.severity === 'high' || value.severity === 'medium' || value.severity === 'low' || value.severity === 'info' ? value.severity : 'info',
|
|
2986
|
+
category: typeof value.category === 'string' ? value.category : '',
|
|
2987
|
+
summary: typeof value.summary === 'string' ? value.summary : '',
|
|
2988
|
+
...(typeof value.path === 'string' ? { path: value.path } : {}),
|
|
2989
|
+
...(typeof value.symbol === 'string' ? { symbol: value.symbol } : {}),
|
|
2990
|
+
}))
|
|
2991
|
+
: undefined,
|
|
2992
|
+
});
|
|
2993
|
+
}
|
|
2159
2994
|
case 'finalize':
|
|
2160
2995
|
return finalizeGoalWorkloop(ctx, {
|
|
2161
2996
|
workId: String(args.work_id ?? ''),
|