@moretea-labs/forge 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +4 -0
- package/README.md +2 -2
- package/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/prompt-guard.sh +5 -81
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +178 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-plugin-management.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +26 -6
- package/docs/operations/controller-performance-and-502.md +143 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +2 -2
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +33 -11
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1558
- package/scripts/check-architecture-sync.sh +2 -371
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +349 -79
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -722
- package/scripts/ensure-task-workflow.sh +1 -1225
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +22 -46
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -1189
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -796
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +5 -1
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -558
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -703
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +1 -677
- package/scripts/workflow-contract.ts +4 -205
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +113 -5
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +828 -487
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +135 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +54 -78
- package/src/cli/local-bridge/console-view-models.ts +1 -35
- package/src/cli/local-bridge/facade-api.ts +37 -264
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
- package/src/cli/local-bridge/frontend/src/styles.css +1 -1
- package/src/cli/local-bridge/frontend/src/types.ts +5 -8
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +2 -515
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -222
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +282 -146
- package/src/cli/mcp/server.ts +586 -116
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +44 -7
- package/src/cli/mcp/transports/http.ts +124 -14
- package/src/cli/mcp/transports/session-registry.ts +43 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +103 -21
- package/src/cli/repositories/command-executor.ts +78 -526
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +124 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +118 -6
- package/src/cli/repository/inspector.ts +563 -65
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +223 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
- package/src/runtime/control-plane/facade/index.ts +3 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +90 -10
- package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +24 -21
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +13 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
- package/src/runtime/execution/process-runtime/check-result.ts +88 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +53 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +4 -12
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -61
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +186 -84
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
- package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
- package/src/runtime/plugins/browser-adapter.ts +2662 -829
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +110 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
- package/src/runtime/plugins/browser-runtime.ts +193 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
- package/src/runtime/plugins/external-adapter.ts +358 -3
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +2 -0
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +123 -6
- package/src/runtime/plugins/macos-capability-broker.ts +47 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +330 -33
- package/src/runtime/plugins/types.ts +38 -3
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/maintenance-executor.ts +426 -25
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +230 -10
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +461 -22
- package/src/runtime/root/release-manifest.ts +144 -18
- package/src/runtime/root/release-materialize.ts +125 -32
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -29
- package/src/runtime/root/types.ts +12 -0
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +1079 -240
- package/src/runtime/standalone-recovery/entry.ts +100 -24
- package/src/runtime/standalone-recovery/installer.ts +20 -4
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
- package/src/runtime/workflow/schedules/engine.ts +364 -202
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +64 -17
- package/src/runtime/workflow/schedules/store.ts +209 -19
- package/src/runtime/workflow/schedules/types.ts +2 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
- package/docs/architecture/current/codegraph-context-provider.md +0 -238
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
- package/docs/architecture/current/entity-model.md +0 -612
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -169
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -321
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { spawnSync } from 'child_process';
|
|
2
|
+
import type { RepositoryRecord } from '../../../cli/repositories/types';
|
|
3
|
+
import { getWorkContract } from '../facade/work-contract-store';
|
|
4
|
+
import { isTerminalWorkContractStatus } from '../facade/types';
|
|
5
|
+
import { executeRepositoryCommandViaProcessRuntime, waitRepositoryCommandProcess } from '../../execution/process-runtime/command-facade';
|
|
6
|
+
import { executionIdentityForRepository } from './execution-identity';
|
|
7
|
+
|
|
8
|
+
export interface WorkRemoteDeliveryReceipt {
|
|
9
|
+
targetBranch: string;
|
|
10
|
+
targetRevision: string;
|
|
11
|
+
remoteRevisionBefore: string;
|
|
12
|
+
remoteRevisionAfter: string;
|
|
13
|
+
pushed: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function gitText(root: string, args: string[], timeoutMs = 30_000): string {
|
|
17
|
+
const result = spawnSync('git', ['-C', root, ...args], {
|
|
18
|
+
encoding: 'utf8',
|
|
19
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
20
|
+
timeout: timeoutMs,
|
|
21
|
+
});
|
|
22
|
+
if (result.status !== 0 || result.error || typeof result.stdout !== 'string') {
|
|
23
|
+
throw new Error(`WORK_REMOTE_DELIVERY_GIT_FAILED: git ${args[0] ?? 'command'} failed`);
|
|
24
|
+
}
|
|
25
|
+
return result.stdout.trim();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function gitSucceeds(root: string, args: string[]): boolean {
|
|
29
|
+
const result = spawnSync('git', ['-C', root, ...args], {
|
|
30
|
+
encoding: 'utf8',
|
|
31
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
32
|
+
timeout: 15_000,
|
|
33
|
+
});
|
|
34
|
+
return result.status === 0 && !result.error;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function exactRevision(root: string, revision: string, label: string): string {
|
|
38
|
+
const value = gitText(root, ['rev-parse', '--verify', `${revision}^{commit}`]);
|
|
39
|
+
if (!/^[a-f0-9]{40}$/i.test(value)) throw new Error(`WORK_REMOTE_DELIVERY_${label}_INVALID`);
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function assertTargetBranch(root: string, targetBranch: string): void {
|
|
44
|
+
if (!targetBranch.trim() || !gitSucceeds(root, ['check-ref-format', '--branch', targetBranch])) {
|
|
45
|
+
throw new Error(`WORK_REMOTE_DELIVERY_TARGET_BRANCH_INVALID: ${targetBranch || 'missing'}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function assertActiveWork(input: {
|
|
50
|
+
controllerHome: string;
|
|
51
|
+
repository: RepositoryRecord;
|
|
52
|
+
workId: string;
|
|
53
|
+
}): void {
|
|
54
|
+
const contract = getWorkContract(
|
|
55
|
+
{ controllerHome: input.controllerHome, repoId: input.repository.repoId },
|
|
56
|
+
input.workId,
|
|
57
|
+
);
|
|
58
|
+
if (
|
|
59
|
+
!contract
|
|
60
|
+
|| contract.repoId !== input.repository.repoId
|
|
61
|
+
|| (contract.checkoutId && contract.checkoutId !== input.repository.activeCheckoutId)
|
|
62
|
+
|| isTerminalWorkContractStatus(contract.status)
|
|
63
|
+
|| Boolean(contract.completionReceipt)
|
|
64
|
+
) {
|
|
65
|
+
throw new Error(`WORK_REMOTE_DELIVERY_ACTIVE_AUTHORITY_REQUIRED: ${input.workId}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function remoteHead(root: string, targetBranch: string): string {
|
|
70
|
+
const line = gitText(root, ['ls-remote', '--heads', 'origin', `refs/heads/${targetBranch}`])
|
|
71
|
+
.split(/\r?\n/)
|
|
72
|
+
.find(Boolean);
|
|
73
|
+
const revision = line?.split(/\s+/)[0] ?? '';
|
|
74
|
+
if (!/^[a-f0-9]{40}$/i.test(revision)) {
|
|
75
|
+
throw new Error(`WORK_REMOTE_DELIVERY_REMOTE_BRANCH_MISSING: origin/${targetBranch}`);
|
|
76
|
+
}
|
|
77
|
+
return revision;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function executeGit(input: {
|
|
81
|
+
controllerHome: string;
|
|
82
|
+
repository: RepositoryRecord;
|
|
83
|
+
workId: string;
|
|
84
|
+
command: string[];
|
|
85
|
+
timeoutMs: number;
|
|
86
|
+
commandId: string;
|
|
87
|
+
}): Promise<{ ok: boolean; detail: string }> {
|
|
88
|
+
const result = await executeRepositoryCommandViaProcessRuntime({
|
|
89
|
+
controllerHome: input.controllerHome,
|
|
90
|
+
repository: input.repository,
|
|
91
|
+
command: input.command,
|
|
92
|
+
timeoutMs: input.timeoutMs,
|
|
93
|
+
interactiveWaitMs: Math.min(5_000, input.timeoutMs),
|
|
94
|
+
maxOutputBytes: 256 * 1024,
|
|
95
|
+
workId: input.workId,
|
|
96
|
+
commandId: input.commandId,
|
|
97
|
+
executionIdentity: executionIdentityForRepository(input.repository, { workId: input.workId }),
|
|
98
|
+
});
|
|
99
|
+
if (result.route === 'reject' || result.route === 'durable') {
|
|
100
|
+
return { ok: false, detail: result.reason ?? result.route };
|
|
101
|
+
}
|
|
102
|
+
if (result.process && !result.process.completed) {
|
|
103
|
+
const waited = await waitRepositoryCommandProcess(
|
|
104
|
+
input.controllerHome,
|
|
105
|
+
input.repository.repoId,
|
|
106
|
+
result.process.processId,
|
|
107
|
+
{ timeoutMs: input.timeoutMs },
|
|
108
|
+
);
|
|
109
|
+
return { ok: waited.ok === true, detail: waited.stderr ?? waited.stdout ?? '' };
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
ok: result.ok === true || result.process?.ok === true,
|
|
113
|
+
detail: result.stderr ?? result.stdout ?? result.process?.stderr ?? result.process?.stdout ?? '',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function fetchRemoteTipObject(input: {
|
|
118
|
+
controllerHome: string;
|
|
119
|
+
repository: RepositoryRecord;
|
|
120
|
+
workId: string;
|
|
121
|
+
targetBranch: string;
|
|
122
|
+
timeoutMs: number;
|
|
123
|
+
}): Promise<void> {
|
|
124
|
+
const fetched = await executeGit({
|
|
125
|
+
controllerHome: input.controllerHome,
|
|
126
|
+
repository: input.repository,
|
|
127
|
+
workId: input.workId,
|
|
128
|
+
command: ['git', 'fetch', '--no-tags', '--no-write-fetch-head', 'origin', `refs/heads/${input.targetBranch}`],
|
|
129
|
+
timeoutMs: input.timeoutMs,
|
|
130
|
+
commandId: `work-remote-delivery-fetch:${input.workId}:${input.targetBranch}`,
|
|
131
|
+
});
|
|
132
|
+
if (!fetched.ok) {
|
|
133
|
+
throw new Error(`WORK_REMOTE_DELIVERY_FETCH_FAILED: ${fetched.detail || `origin/${input.targetBranch}`}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function observedRemoteContainsTarget(input: {
|
|
138
|
+
controllerHome: string;
|
|
139
|
+
repository: RepositoryRecord;
|
|
140
|
+
workId: string;
|
|
141
|
+
targetBranch: string;
|
|
142
|
+
targetRevision: string;
|
|
143
|
+
remoteRevision: string;
|
|
144
|
+
timeoutMs: number;
|
|
145
|
+
}): Promise<boolean> {
|
|
146
|
+
await fetchRemoteTipObject(input);
|
|
147
|
+
if (!gitSucceeds(input.repository.canonicalRoot, ['cat-file', '-e', `${input.remoteRevision}^{commit}`])) {
|
|
148
|
+
throw new Error(`WORK_REMOTE_DELIVERY_REMOTE_OBJECT_UNAVAILABLE: ${input.remoteRevision}`);
|
|
149
|
+
}
|
|
150
|
+
return gitSucceeds(
|
|
151
|
+
input.repository.canonicalRoot,
|
|
152
|
+
['merge-base', '--is-ancestor', input.targetRevision, input.remoteRevision],
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function pushExactWorkRemoteDelivery(input: {
|
|
157
|
+
controllerHome: string;
|
|
158
|
+
repository: RepositoryRecord;
|
|
159
|
+
workId: string;
|
|
160
|
+
targetBranch: string;
|
|
161
|
+
targetRevision: string;
|
|
162
|
+
timeoutMs?: number;
|
|
163
|
+
}): Promise<WorkRemoteDeliveryReceipt> {
|
|
164
|
+
assertTargetBranch(input.repository.canonicalRoot, input.targetBranch);
|
|
165
|
+
assertActiveWork(input);
|
|
166
|
+
const targetRevision = exactRevision(input.repository.canonicalRoot, input.targetRevision, 'TARGET_REVISION');
|
|
167
|
+
const timeoutMs = Math.min(Math.max(input.timeoutMs ?? 120_000, 5_000), 120_000);
|
|
168
|
+
const remoteRevisionBefore = remoteHead(input.repository.canonicalRoot, input.targetBranch);
|
|
169
|
+
|
|
170
|
+
if (await observedRemoteContainsTarget({ ...input, targetRevision, remoteRevision: remoteRevisionBefore, timeoutMs })) {
|
|
171
|
+
return {
|
|
172
|
+
targetBranch: input.targetBranch,
|
|
173
|
+
targetRevision,
|
|
174
|
+
remoteRevisionBefore,
|
|
175
|
+
remoteRevisionAfter: remoteRevisionBefore,
|
|
176
|
+
pushed: false,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
if (!gitSucceeds(input.repository.canonicalRoot, ['merge-base', '--is-ancestor', remoteRevisionBefore, targetRevision])) {
|
|
180
|
+
throw new Error(`WORK_REMOTE_DELIVERY_REMOTE_DIVERGED: ${remoteRevisionBefore} is not an ancestor of ${targetRevision}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Re-read durable authority immediately before the only remote mutation. A
|
|
184
|
+
// terminal Work may be inspected/reconciled, but it can never be reused as
|
|
185
|
+
// authority for an arbitrary post-finalize push.
|
|
186
|
+
assertActiveWork(input);
|
|
187
|
+
const pushed = await executeGit({
|
|
188
|
+
controllerHome: input.controllerHome,
|
|
189
|
+
repository: input.repository,
|
|
190
|
+
workId: input.workId,
|
|
191
|
+
command: ['git', 'push', '--porcelain', 'origin', `${targetRevision}:refs/heads/${input.targetBranch}`],
|
|
192
|
+
timeoutMs,
|
|
193
|
+
commandId: `work-remote-delivery-push:${input.workId}:${targetRevision}`,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const remoteRevisionAfter = remoteHead(input.repository.canonicalRoot, input.targetBranch);
|
|
197
|
+
const delivered = await observedRemoteContainsTarget({
|
|
198
|
+
...input,
|
|
199
|
+
targetRevision,
|
|
200
|
+
remoteRevision: remoteRevisionAfter,
|
|
201
|
+
timeoutMs,
|
|
202
|
+
});
|
|
203
|
+
if (!delivered) {
|
|
204
|
+
throw new Error(
|
|
205
|
+
`WORK_REMOTE_DELIVERY_PUSH_FAILED: ${pushed.detail || `${targetRevision} is not reachable from origin/${input.targetBranch}`}`,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
targetBranch: input.targetBranch,
|
|
210
|
+
targetRevision,
|
|
211
|
+
remoteRevisionBefore,
|
|
212
|
+
remoteRevisionAfter,
|
|
213
|
+
pushed: pushed.ok,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
@@ -3,8 +3,8 @@ import { spawnSync } from 'child_process';
|
|
|
3
3
|
import { getIssue, listIssues, recordTaskVerification, acceptVerifiedTask, projectTaskFromWork } from '../../../cli/controller/issue-store';
|
|
4
4
|
import { resolveCompletionTargetBranch } from '../../../cli/controller/completion-target';
|
|
5
5
|
import type { CompletionReceipt, ControllerIssue } from '../../../cli/controller/types';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { getWorkContract, updateWorkContract } from '../facade/work-contract-store';
|
|
7
|
+
import { completeWorkWithReceipt } from './work-completion-authority';
|
|
8
8
|
import { isRepositoryCompletionReceipt, WORK_RECONCILIATION_METHODS, WORK_RECONCILIATION_OUTCOMES } from '../facade/types';
|
|
9
9
|
import type {
|
|
10
10
|
WorkReconciliationMethod,
|
|
@@ -27,7 +27,7 @@ function commitRevision(repoRoot: string, revision: string | undefined, label: s
|
|
|
27
27
|
return gitText(repoRoot, ['rev-parse', `${revision.trim()}^{commit}`]);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function changedPaths(repoRoot: string, baseRevision: string, targetRevision: string): string[] {
|
|
30
|
+
export function changedPaths(repoRoot: string, baseRevision: string, targetRevision: string): string[] {
|
|
31
31
|
const result = spawnSync('git', ['diff', '--name-only', '-z', baseRevision, targetRevision], {
|
|
32
32
|
cwd: repoRoot,
|
|
33
33
|
encoding: 'utf-8',
|
|
@@ -39,6 +39,22 @@ function changedPaths(repoRoot: string, baseRevision: string, targetRevision: st
|
|
|
39
39
|
return [...new Set(result.stdout.split('\0').filter(Boolean))].sort();
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
|
|
43
|
+
function completionReceiptChangedPaths(
|
|
44
|
+
contract: NonNullable<ReturnType<typeof getWorkContract>>,
|
|
45
|
+
repoRoot: string,
|
|
46
|
+
baseRevision: string,
|
|
47
|
+
targetRevision: string,
|
|
48
|
+
): string[] {
|
|
49
|
+
const reconciled = contract.reconciliations.find((entry) =>
|
|
50
|
+
entry.outcome === 'accepted_equivalence'
|
|
51
|
+
&& entry.reachable
|
|
52
|
+
&& entry.baseRevision === baseRevision
|
|
53
|
+
&& entry.observedTargetRevision === targetRevision);
|
|
54
|
+
if (!reconciled) return changedPaths(repoRoot, baseRevision, targetRevision);
|
|
55
|
+
return [...new Set(reconciled.comparedPaths.map((path) => path.trim()).filter(Boolean))].sort();
|
|
56
|
+
}
|
|
57
|
+
|
|
42
58
|
function assertWorkNotBoundElsewhere(repoRoot: string, workId: string, issueId: string, taskId: string): void {
|
|
43
59
|
for (const issue of listIssues(repoRoot)) {
|
|
44
60
|
for (const task of issue.tasks) {
|
|
@@ -273,19 +289,13 @@ export function acceptVerifiedTaskFromReviewedWorkReconciliation(input: Controll
|
|
|
273
289
|
recordedAt,
|
|
274
290
|
};
|
|
275
291
|
const completionOutcome = noChange ? 'completed_no_change' : contract.workKind === 'remote_effect' ? 'completed_remote' : 'completed_changed';
|
|
276
|
-
const recorded =
|
|
292
|
+
const recorded = completeWorkWithReceipt(
|
|
277
293
|
{ controllerHome: input.controllerHome, repoId: input.repoId },
|
|
278
294
|
input.workId,
|
|
279
295
|
receipt,
|
|
280
296
|
completionOutcome,
|
|
281
297
|
contract.workKind,
|
|
282
298
|
);
|
|
283
|
-
if (recorded.requirementId) {
|
|
284
|
-
completeRequirementFromWork(
|
|
285
|
-
{ controllerHome: input.controllerHome },
|
|
286
|
-
{ requirementId: recorded.requirementId, work: recorded },
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
299
|
const recordedReceipt = recorded.completionReceipt;
|
|
290
300
|
const projectedReceipt = recordedReceipt && isRepositoryCompletionReceipt(recordedReceipt) ? recordedReceipt : receipt;
|
|
291
301
|
const projectedVerification = { ...task.verification!, completionReceipt: projectedReceipt };
|
|
@@ -361,7 +371,7 @@ export function acceptVerifiedTaskFromControllerWork(input: ControllerWorkTaskRe
|
|
|
361
371
|
}
|
|
362
372
|
assertCurrentRequiredChecks(contract, targetRevision);
|
|
363
373
|
const baseRevision = commitRevision(input.repoRoot, handle.baseCommit, 'BASE_REVISION');
|
|
364
|
-
const receiptChangedPaths =
|
|
374
|
+
const receiptChangedPaths = completionReceiptChangedPaths(contract, input.repoRoot, baseRevision, targetRevision);
|
|
365
375
|
if (noChange && receiptChangedPaths.length > 0) {
|
|
366
376
|
throw new Error(`CONTROLLER_WORK_RECEIPT_NO_CHANGE_DIRTY: ${input.workId}`);
|
|
367
377
|
}
|
|
@@ -409,18 +419,12 @@ export function acceptVerifiedTaskFromControllerWork(input: ControllerWorkTaskRe
|
|
|
409
419
|
// historical Task write is interrupted, the Work authority is still
|
|
410
420
|
// durable and a retry can safely rebuild the projection.
|
|
411
421
|
const completionOutcome = noChange ? 'completed_no_change' : contract.workKind === 'remote_effect' ? 'completed_remote' : 'completed_changed';
|
|
412
|
-
const recorded =
|
|
422
|
+
const recorded = completeWorkWithReceipt(
|
|
413
423
|
{ controllerHome: input.controllerHome, repoId: input.repoId },
|
|
414
424
|
input.workId,
|
|
415
425
|
receipt,
|
|
416
426
|
completionOutcome,
|
|
417
427
|
);
|
|
418
|
-
if (recorded.requirementId) {
|
|
419
|
-
completeRequirementFromWork(
|
|
420
|
-
{ controllerHome: input.controllerHome },
|
|
421
|
-
{ requirementId: recorded.requirementId, work: recorded },
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
428
|
const recordedReceipt = recorded.completionReceipt;
|
|
425
429
|
const projectedReceipt = recordedReceipt && isRepositoryCompletionReceipt(recordedReceipt) ? recordedReceipt : receipt;
|
|
426
430
|
const projectedVerification = {
|
|
@@ -8,16 +8,18 @@ import {
|
|
|
8
8
|
writeFileSync,
|
|
9
9
|
} from 'fs';
|
|
10
10
|
import { dirname, join, resolve } from 'path';
|
|
11
|
-
import { repositoryControllerRoot } from '../../../cli/repositories/controller-home';
|
|
11
|
+
import { repoLocalNoIndexControllerHome, repositoryControllerRoot } from '../../../cli/repositories/controller-home';
|
|
12
12
|
import {
|
|
13
13
|
getRepository,
|
|
14
14
|
listRepositories,
|
|
15
15
|
selectRepositoryCheckout,
|
|
16
16
|
setRepositoryCheckoutLifecycle,
|
|
17
17
|
} from '../../../cli/repositories/registry';
|
|
18
|
+
import { managedPathInside, managedWorktreeStorageRoot } from '../../../cli/repositories/worktree-storage';
|
|
18
19
|
import { markRepositoryProjectionDirty } from '../../projections/invalidation';
|
|
20
|
+
import { listControlPlaneRecords } from '../persistence/sqlite-store';
|
|
19
21
|
import { getWorkContract } from '../facade/work-contract-store';
|
|
20
|
-
import { getControllerSession,
|
|
22
|
+
import { getControllerSession, releaseObservedControllerSession } from '../facade/controller-session-store';
|
|
21
23
|
import { isRepositoryCompletionReceipt, isTerminalWorkContractStatus, type WorkContract } from '../facade/types';
|
|
22
24
|
import {
|
|
23
25
|
cancelProcess,
|
|
@@ -30,6 +32,7 @@ import {
|
|
|
30
32
|
import { getProcessRecord, listProcessRecords } from '../../execution/process-runtime/store';
|
|
31
33
|
import {
|
|
32
34
|
listWorkHandles,
|
|
35
|
+
readWorkHandle,
|
|
33
36
|
transitionWorkHandle,
|
|
34
37
|
writeWorkHandle,
|
|
35
38
|
type WorkCleanupReceipt,
|
|
@@ -391,6 +394,42 @@ function selectTerminalCleanupTarget(repository: ReturnType<typeof getRepository
|
|
|
391
394
|
return repository;
|
|
392
395
|
}
|
|
393
396
|
|
|
397
|
+
function isCurrentControllerManagedWorktree(
|
|
398
|
+
controllerHome: string,
|
|
399
|
+
worktreePath: string,
|
|
400
|
+
): boolean {
|
|
401
|
+
try {
|
|
402
|
+
const storageRoot = managedWorktreeStorageRoot(
|
|
403
|
+
controllerHome,
|
|
404
|
+
listRepositories(controllerHome, { includeRemoved: true }),
|
|
405
|
+
);
|
|
406
|
+
if (managedPathInside(storageRoot, worktreePath)) return true;
|
|
407
|
+
// Older Controller releases used a namespaced sibling of controller/.
|
|
408
|
+
// Accept only namespaces whose source Home appears in this target's
|
|
409
|
+
// controlled migration receipt; then let the caller's Git common-dir,
|
|
410
|
+
// branch, cleanliness, and preservation checks prove the exact Work before
|
|
411
|
+
// any removal.
|
|
412
|
+
const sourceHomes = new Set<string>([resolve(controllerHome)]);
|
|
413
|
+
for (const record of listControlPlaneRecords<{ sourceHome?: unknown; status?: unknown }>(controllerHome, {
|
|
414
|
+
namespace: 'controller_home_migration',
|
|
415
|
+
limit: 1_000,
|
|
416
|
+
})) {
|
|
417
|
+
if (record.value.status !== 'applied' || typeof record.value.sourceHome !== 'string') continue;
|
|
418
|
+
const sourceHome = resolve(record.value.sourceHome);
|
|
419
|
+
sourceHomes.add(sourceHome);
|
|
420
|
+
const physical = repoLocalNoIndexControllerHome(sourceHome);
|
|
421
|
+
if (physical) sourceHomes.add(physical);
|
|
422
|
+
}
|
|
423
|
+
return [...sourceHomes].some((sourceHome) => {
|
|
424
|
+
const namespace = createHash('sha256').update(sourceHome).digest('hex').slice(0, 16);
|
|
425
|
+
const legacyRoot = join(dirname(resolve(controllerHome)), 'managed-worktrees', namespace);
|
|
426
|
+
return managedPathInside(legacyRoot, worktreePath);
|
|
427
|
+
});
|
|
428
|
+
} catch {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
394
433
|
export interface TerminalWorkCleanupReconcileOptions {
|
|
395
434
|
nowMs?: number;
|
|
396
435
|
minAgeMs?: number;
|
|
@@ -417,11 +456,81 @@ function terminalOutcomeForContract(contract: WorkContract): WorkTerminalOutcome
|
|
|
417
456
|
return 'failed';
|
|
418
457
|
}
|
|
419
458
|
|
|
420
|
-
function
|
|
459
|
+
export function recoverTerminalWorkHandle(
|
|
460
|
+
controllerHome: string,
|
|
461
|
+
repositoryId: string,
|
|
462
|
+
workId: string,
|
|
463
|
+
): WorkHandleState | undefined {
|
|
464
|
+
const existing = readWorkHandle(controllerHome, repositoryId, workId);
|
|
465
|
+
if (existing) return existing;
|
|
466
|
+
const contract = getWorkContract({ controllerHome, repoId: repositoryId }, workId);
|
|
467
|
+
if (!contract || !isTerminalWorkContractStatus(contract.status)) return undefined;
|
|
468
|
+
const worktreePath = contract.worktreeRef?.trim();
|
|
469
|
+
const checkoutId = contract.checkoutId?.trim();
|
|
470
|
+
if (!worktreePath || !checkoutId || !existsSync(worktreePath)) return undefined;
|
|
471
|
+
if (!isCurrentControllerManagedWorktree(controllerHome, worktreePath)) return undefined;
|
|
472
|
+
|
|
473
|
+
const repository = getRepository(repositoryId, controllerHome, { includeRemoved: true });
|
|
474
|
+
// Reconstruct physical cleanup authority from the recorded checkout entry itself.
|
|
475
|
+
// selectRepositoryCheckout returns a RepositoryRecord view rooted at a checkout,
|
|
476
|
+
// not the checkout entry (and terminal cleanup must also tolerate an already
|
|
477
|
+
// archived/removed registry lifecycle while the physical worktree still exists).
|
|
478
|
+
const checkout = repository.checkouts.find((candidate) => candidate.checkoutId === checkoutId);
|
|
479
|
+
if (!checkout || checkout.worktree !== true || resolve(checkout.canonicalRoot) !== resolve(worktreePath)) return undefined;
|
|
480
|
+
const branch = checkout.branch?.trim();
|
|
481
|
+
if (!branch) return undefined;
|
|
482
|
+
|
|
483
|
+
const head = git(worktreePath, ['rev-parse', 'HEAD']);
|
|
484
|
+
if (!head.ok || !head.stdout) return undefined;
|
|
485
|
+
const targetBranch = repository.defaultBranch || 'main';
|
|
486
|
+
const targetExists = branchExists(repository.canonicalRoot, targetBranch);
|
|
487
|
+
const contained = targetExists
|
|
488
|
+
? git(repository.canonicalRoot, ['merge-base', '--is-ancestor', head.stdout, `refs/heads/${targetBranch}`]).ok
|
|
489
|
+
: false;
|
|
490
|
+
const delivered = contract.status === 'completed' && contained;
|
|
491
|
+
const session = getControllerSession({ controllerHome, repoId: repositoryId }, workId);
|
|
492
|
+
const recordedAt = nowIso();
|
|
493
|
+
return writeWorkHandle(controllerHome, {
|
|
494
|
+
schemaVersion: 1,
|
|
495
|
+
workId,
|
|
496
|
+
workContractId: workId,
|
|
497
|
+
sessionId: session?.sessionId ?? `terminal-recovery:${workId}`,
|
|
498
|
+
principalId: contract.principalId?.trim() || session?.principalId || 'terminal-recovery',
|
|
499
|
+
repositoryId,
|
|
500
|
+
checkoutId,
|
|
501
|
+
sourceCheckoutId: repository.activeCheckoutId,
|
|
502
|
+
worktreePath,
|
|
503
|
+
branch,
|
|
504
|
+
managedWorktree: true,
|
|
505
|
+
baseCommit: contract.baseRevision,
|
|
506
|
+
expectedHead: head.stdout,
|
|
507
|
+
permissionSnapshotVersion: 1,
|
|
508
|
+
state: delivered ? 'merged' : 'failed_terminal_cleanup',
|
|
509
|
+
failureReason: delivered ? undefined : 'Recovered terminal Work ownership for cleanup; target-branch containment was not proven.',
|
|
510
|
+
createdAt: contract.createdAt || recordedAt,
|
|
511
|
+
updatedAt: recordedAt,
|
|
512
|
+
cleanupResponsibility: { owner: 'work_finalizer', registeredAt: recordedAt },
|
|
513
|
+
finalization: {
|
|
514
|
+
validation: contract.status === 'failed' ? 'failed' : 'done',
|
|
515
|
+
commit: 'skipped',
|
|
516
|
+
merge: delivered ? 'done' : 'skipped',
|
|
517
|
+
branchCleanup: 'pending',
|
|
518
|
+
worktreeCleanup: 'pending',
|
|
519
|
+
...(contract.status === 'failed' ? { lastError: 'Recovered failed terminal Work for cleanup.' } : {}),
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function cleanupRetainedByRequest(contract: WorkContract, handle: WorkHandleState): boolean {
|
|
421
525
|
// `skipped` in old WorkHandle finalization records was also used for legacy
|
|
422
526
|
// no-op/error paths, so it is not sufficient proof of an explicit retention
|
|
423
|
-
// request.
|
|
424
|
-
//
|
|
527
|
+
// request. New cancelled/terminal Work records persist an explicit physical
|
|
528
|
+
// resource disposition on the WorkHandle. Completed Work keeps the existing
|
|
529
|
+
// completion-receipt warning as the delivery authority.
|
|
530
|
+
if (
|
|
531
|
+
handle.terminalResourceDisposition?.mode === 'retained_by_request'
|
|
532
|
+
&& handle.terminalResourceDisposition.retainWorktree === true
|
|
533
|
+
) return true;
|
|
425
534
|
const receipt = contract.completionReceipt;
|
|
426
535
|
if (!receipt || !isRepositoryCompletionReceipt(receipt)) return false;
|
|
427
536
|
return receipt.cleanup.warnings.some((warning) => warning.code === 'cleanup_retained_by_request');
|
|
@@ -588,7 +697,10 @@ export async function cleanupTerminalWork(input: TerminalWorkCleanupInput): Prom
|
|
|
588
697
|
current = persist(input.controllerHome, current, receipt);
|
|
589
698
|
return { handle: current, receipt };
|
|
590
699
|
}
|
|
591
|
-
|
|
700
|
+
const migratedManagedWorktree = !registeredPath
|
|
701
|
+
&& existsSync(current.worktreePath)
|
|
702
|
+
&& isCurrentControllerManagedWorktree(input.controllerHome, current.worktreePath);
|
|
703
|
+
if (!registeredPath && existsSync(current.worktreePath) && !migratedManagedWorktree) {
|
|
592
704
|
addBlocker(receipt, 'CHECKOUT_NOT_CONTROLLER_REGISTERED');
|
|
593
705
|
current = persist(input.controllerHome, current, receipt);
|
|
594
706
|
return { handle: current, receipt };
|
|
@@ -686,6 +798,9 @@ export async function cleanupTerminalWork(input: TerminalWorkCleanupInput): Prom
|
|
|
686
798
|
addBlocker(receipt, `CHECKOUT_REGISTRY_REMOVE_FAILED: ${receipt.checkoutRegistry.reason}`);
|
|
687
799
|
}
|
|
688
800
|
}
|
|
801
|
+
if (!registeredPath && migratedManagedWorktree) {
|
|
802
|
+
receipt.checkoutRegistry.reason = 'Checkout metadata was absent after Controller Home migration; canonical managed-worktree and Git identity checks passed before cleanup.';
|
|
803
|
+
}
|
|
689
804
|
|
|
690
805
|
if (!deleteBranch) {
|
|
691
806
|
receipt.branchCleanup.status = 'retained';
|
|
@@ -789,8 +904,24 @@ export async function reconcileTerminalWorkCleanups(
|
|
|
789
904
|
const repositories = listRepositories(controllerHome).filter((repository) => repository.enabled && !repository.removedAt);
|
|
790
905
|
outer: for (const repository of repositories) {
|
|
791
906
|
const handles = listWorkHandles(controllerHome, repository.repoId, 10_000)
|
|
792
|
-
.filter((handle) => handle.managedWorktree)
|
|
793
|
-
|
|
907
|
+
.filter((handle) => handle.managedWorktree);
|
|
908
|
+
const knownWorkIds = new Set(handles.map((handle) => handle.workId));
|
|
909
|
+
const recoveredWorkIds = new Set<string>();
|
|
910
|
+
for (const record of listControlPlaneRecords<WorkContract>(controllerHome, {
|
|
911
|
+
namespace: 'work_contract',
|
|
912
|
+
scope: repository.repoId,
|
|
913
|
+
limit: 10_000,
|
|
914
|
+
})) {
|
|
915
|
+
const contract = record.value;
|
|
916
|
+
if (knownWorkIds.has(contract.workId) || !isTerminalWorkContractStatus(contract.status)) continue;
|
|
917
|
+
if (!contract.worktreeRef?.trim() || !existsSync(contract.worktreeRef)) continue;
|
|
918
|
+
const recovered = recoverTerminalWorkHandle(controllerHome, repository.repoId, contract.workId);
|
|
919
|
+
if (!recovered) continue;
|
|
920
|
+
handles.push(recovered);
|
|
921
|
+
knownWorkIds.add(recovered.workId);
|
|
922
|
+
recoveredWorkIds.add(recovered.workId);
|
|
923
|
+
}
|
|
924
|
+
handles.sort((left, right) => left.updatedAt.localeCompare(right.updatedAt));
|
|
794
925
|
for (const originalHandle of handles) {
|
|
795
926
|
report.scanned += 1;
|
|
796
927
|
const contract = getWorkContract({ controllerHome, repoId: repository.repoId }, originalHandle.workContractId ?? originalHandle.workId);
|
|
@@ -803,7 +934,11 @@ export async function reconcileTerminalWorkCleanups(
|
|
|
803
934
|
report.skippedRetained.push(originalHandle.workId);
|
|
804
935
|
continue;
|
|
805
936
|
}
|
|
806
|
-
|
|
937
|
+
// Reconstructing a missing handle is metadata recovery, not new Work activity.
|
|
938
|
+
// Do not let that write reset the terminal-age grace period indefinitely.
|
|
939
|
+
const terminalAt = recoveredWorkIds.has(originalHandle.workId)
|
|
940
|
+
? (Date.parse(contract.updatedAt) || 0)
|
|
941
|
+
: Math.max(Date.parse(contract.updatedAt) || 0, Date.parse(originalHandle.updatedAt) || 0);
|
|
807
942
|
if (terminalAt > 0 && nowMs - terminalAt < minAgeMs) {
|
|
808
943
|
report.skippedRecent.push(originalHandle.workId);
|
|
809
944
|
continue;
|
|
@@ -833,8 +968,18 @@ export async function reconcileTerminalWorkCleanups(
|
|
|
833
968
|
const workId = cleaned.handle.workContractId ?? cleaned.handle.workId;
|
|
834
969
|
const controllerSession = getControllerSession({ controllerHome, repoId: repository.repoId }, workId);
|
|
835
970
|
if (controllerSession) {
|
|
836
|
-
|
|
837
|
-
|
|
971
|
+
const released = releaseObservedControllerSession(
|
|
972
|
+
{ controllerHome, repoId: repository.repoId },
|
|
973
|
+
{ workId, actor: `terminal-cleanup:${originalHandle.workId}`, owner: controllerSession },
|
|
974
|
+
);
|
|
975
|
+
if (released.allowed) {
|
|
976
|
+
cleaned.receipt.ownership.controllerLease = 'released';
|
|
977
|
+
} else {
|
|
978
|
+
cleaned.receipt.complete = false;
|
|
979
|
+
cleaned.receipt.partial = true;
|
|
980
|
+
cleaned.receipt.completedAt = undefined;
|
|
981
|
+
cleaned.receipt.blockers = appendUnique(cleaned.receipt.blockers, `controller lease release fenced: ${released.reason}`);
|
|
982
|
+
}
|
|
838
983
|
} else {
|
|
839
984
|
cleaned.receipt.ownership.controllerLease = 'already_released';
|
|
840
985
|
}
|
|
@@ -166,27 +166,35 @@ export function reconcileWorkValidation(
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
try {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
if (binding.checkExecution && binding.checkExecution.checkId !== checkId) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`PROCESS_CHECK_RECEIPT_IDENTITY_MISMATCH: bound check ${binding.checkExecution.checkId} != ${checkId}`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const expectedCheckExecution = binding.checkExecution
|
|
175
|
+
?? (record.checkExecution
|
|
176
|
+
? controllerCheckExecutionIdentity(handle.worktreePath, checkId, record.checkExecution.timeoutMs)
|
|
177
|
+
: undefined);
|
|
172
178
|
const receipt = processCheckCompletionReceipt(record, {
|
|
173
179
|
repoId: handle.repositoryId,
|
|
174
180
|
checkoutId: handle.checkoutId,
|
|
175
181
|
workId: handle.workId,
|
|
176
182
|
checkId,
|
|
177
183
|
processId: binding.processId,
|
|
178
|
-
...(
|
|
184
|
+
...(expectedCheckExecution ? {
|
|
179
185
|
checkExecution: {
|
|
180
|
-
cacheKey:
|
|
181
|
-
revision:
|
|
182
|
-
definitionDigest:
|
|
183
|
-
environmentFingerprint:
|
|
184
|
-
timeoutMs:
|
|
185
|
-
scopeKey:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
cacheKey: expectedCheckExecution.cacheKey,
|
|
187
|
+
revision: expectedCheckExecution.revision,
|
|
188
|
+
definitionDigest: expectedCheckExecution.definitionDigest,
|
|
189
|
+
environmentFingerprint: expectedCheckExecution.environmentFingerprint,
|
|
190
|
+
timeoutMs: expectedCheckExecution.timeoutMs,
|
|
191
|
+
scopeKey: binding.checkExecution
|
|
192
|
+
? binding.checkExecution.scopeKey
|
|
193
|
+
: processCheckSemanticScopeKey({
|
|
194
|
+
checkoutId: handle.checkoutId,
|
|
195
|
+
workId: handle.workId,
|
|
196
|
+
verificationBinding: { executionSessionId: handle.sessionId },
|
|
197
|
+
}, expectedCheckExecution.reuseScope),
|
|
190
198
|
},
|
|
191
199
|
} : {}),
|
|
192
200
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { listControllerChecks, type ControllerCheck } from '../../../cli/controller/check-runner';
|
|
2
|
+
import { selectRepositoryCheckout } from '../../../cli/repositories/registry';
|
|
3
|
+
import type { RepositoryRecord } from '../../../cli/repositories/types';
|
|
4
|
+
import { getWorkContract } from '../facade/work-contract-store';
|
|
5
|
+
import type { WorkContract } from '../facade/types';
|
|
6
|
+
|
|
7
|
+
export interface WorkVerificationContext {
|
|
8
|
+
store: { controllerHome: string; repoId: string };
|
|
9
|
+
repository: RepositoryRecord;
|
|
10
|
+
workContract?: WorkContract;
|
|
11
|
+
checks: ControllerCheck[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type WorkVerificationContextResolution =
|
|
15
|
+
| { ok: true; context: WorkVerificationContext }
|
|
16
|
+
| { ok: false; code: 'WORK_VERIFICATION_CHECKOUT_UNAVAILABLE'; detail: string; workContract?: WorkContract };
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the only repository/check-registry context that may be used to verify
|
|
20
|
+
* a Work. A Work-bound checkout is semantic verification authority: if it can no
|
|
21
|
+
* longer be resolved, verification must fail closed instead of silently reading
|
|
22
|
+
* the canonical/main registry.
|
|
23
|
+
*/
|
|
24
|
+
export function resolveWorkVerificationContext(input: {
|
|
25
|
+
controllerHome: string;
|
|
26
|
+
repository: RepositoryRecord;
|
|
27
|
+
workId?: string;
|
|
28
|
+
}): WorkVerificationContextResolution {
|
|
29
|
+
const store = { controllerHome: input.controllerHome, repoId: input.repository.repoId };
|
|
30
|
+
const workId = input.workId?.trim();
|
|
31
|
+
const workContract = workId ? getWorkContract(store, workId) : undefined;
|
|
32
|
+
let repository = input.repository;
|
|
33
|
+
|
|
34
|
+
if (workContract?.checkoutId) {
|
|
35
|
+
try {
|
|
36
|
+
repository = selectRepositoryCheckout(input.repository, workContract.checkoutId, { allowArchived: true });
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
code: 'WORK_VERIFICATION_CHECKOUT_UNAVAILABLE',
|
|
41
|
+
detail: error instanceof Error ? error.message : 'Work checkout could not be resolved',
|
|
42
|
+
workContract,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
ok: true,
|
|
49
|
+
context: {
|
|
50
|
+
store,
|
|
51
|
+
repository,
|
|
52
|
+
workContract,
|
|
53
|
+
checks: listControllerChecks(repository.canonicalRoot),
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|