@moretea-labs/forge 1.5.1 → 1.7.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 +2 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +2 -0
- package/DESIGN.md +7 -2
- package/README.md +4 -4
- package/README.zh-CN.md +2 -2
- 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/pre-edit-guard.sh +8 -39
- package/assets/hooks/prompt-guard.sh +7 -83
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +54 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/hook-operations.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-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- 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/sprint-backlog.sh +6 -6
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- 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 +170 -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-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +38 -22
- package/docs/operations/controller-performance-and-502.md +124 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +3 -3
- 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/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Installation.md +1 -1
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Quick-Start.md +1 -1
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +29 -10
- package/scripts/activate-source-baseline.ts +17 -13
- 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/capture-plan.sh +3 -3
- package/scripts/check-agent-tooling.sh +8 -35
- package/scripts/check-architecture-sync.sh +17 -86
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-public-docs.sh +14 -1
- package/scripts/check-runtime-architecture.mjs +107 -64
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-workflow.sh +4 -4
- package/scripts/contract-worktree.sh +1 -21
- package/scripts/ensure-task-workflow.sh +22 -55
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +15 -44
- package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
- package/scripts/migrate-project-template.sh +23 -1179
- package/scripts/new-plan.sh +1 -1
- package/scripts/plan-to-todo.sh +11 -36
- package/scripts/run-governed-gate.ts +2 -1
- package/scripts/ship-worktrees.sh +2 -29
- 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 -1
- package/scripts/verify-contract.sh +1 -111
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +3 -180
- package/scripts/workflow-contract.ts +1 -5
- package/src/cli/commands/chatgpt.ts +31 -2
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +37 -10
- 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 +106 -3
- 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 +898 -430
- 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/github.ts +133 -0
- package/src/cli/hook/prompt-guard-decision.ts +17 -18
- package/src/cli/hook/prompt-intents.ts +1 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +225 -67
- package/src/cli/local-bridge/console-view-models.ts +3 -35
- package/src/cli/local-bridge/facade-api.ts +2 -217
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/styles.css +27 -11
- package/src/cli/local-bridge/frontend/src/types.ts +8 -7
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +29 -532
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -189
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/paths.ts +8 -2
- package/src/cli/mcp/repository-tools.ts +356 -101
- package/src/cli/mcp/server.ts +625 -54
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +42 -5
- package/src/cli/mcp/transports/http.ts +125 -15
- package/src/cli/mcp/transports/session-registry.ts +67 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +209 -14
- package/src/cli/repositories/command-executor.ts +55 -521
- 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 +121 -0
- package/src/cli/repositories/safe-patch.ts +6 -4
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +110 -10
- package/src/cli/repository/inspector.ts +565 -62
- 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.ts +275 -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 +196 -2
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/validation.ts +15 -2
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- 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-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
- package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
- package/src/runtime/control-plane/facade/index.ts +1 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
- package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
- 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 +55 -11
- package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
- 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 +145 -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 +273 -542
- 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 +50 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
- 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/routing/route-policy.ts +7 -16
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +19 -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/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 +52 -14
- package/src/runtime/execution/process-runtime/check-result.ts +63 -2
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
- package/src/runtime/execution/process-runtime/gc.ts +98 -12
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
- package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
- package/src/runtime/execution/process-runtime/runtime.ts +59 -4
- package/src/runtime/execution/process-runtime/store.ts +7 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -0
- 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/workers/executor.ts +0 -48
- package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +185 -83
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
- package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
- package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
- package/src/runtime/plugins/browser-adapter.ts +2925 -318
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +6 -31
- package/src/runtime/plugins/browser-handoff.ts +92 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
- 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 +152 -0
- package/src/runtime/plugins/browser-runtime.ts +183 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
- package/src/runtime/plugins/external-adapter.ts +324 -3
- package/src/runtime/plugins/external-registration.ts +84 -5
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +4 -2
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/gmail-adapter.ts +28 -2
- 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 +125 -8
- package/src/runtime/plugins/macos-capability-broker.ts +55 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +366 -24
- package/src/runtime/plugins/types.ts +39 -2
- 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/actions.ts +0 -10
- package/src/runtime/recovery/classifier.ts +0 -9
- package/src/runtime/recovery/maintenance-executor.ts +415 -18
- package/src/runtime/recovery/probe.ts +0 -1
- package/src/runtime/recovery/types.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +186 -8
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +430 -22
- package/src/runtime/root/release-manifest.ts +7 -2
- package/src/runtime/root/release-materialize.ts +162 -19
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -27
- package/src/runtime/root/types.ts +2 -0
- package/src/runtime/safe-tooling/index.ts +0 -1
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/safe-tooling/types.ts +0 -30
- 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 +702 -94
- package/src/runtime/standalone-recovery/entry.ts +99 -23
- package/src/runtime/standalone-recovery/installer.ts +19 -3
- 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 +258 -0
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
- package/src/runtime/workflow/schedules/engine.ts +509 -80
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +48 -4
- package/src/runtime/workflow/schedules/store.ts +131 -10
- package/src/runtime/workflow/schedules/types.ts +6 -3
- package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
- 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/bin/forge-desktop-helper.mjs +0 -204
- 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 -453
- package/docs/architecture/current/codegraph-context-provider.md +0 -226
- 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 -354
- package/docs/architecture/current/entity-model.md +0 -592
- 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 -144
- 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 -311
- 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 -83
- package/docs/operations/controller-desktop-plugin.md +0 -112
- 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/plugins/desktop-adapter.ts +0 -374
- package/src/runtime/safe-tooling/browser-safe.ts +0 -133
- 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,487 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'crypto';
|
|
2
|
+
import { chmodSync, createReadStream, existsSync, mkdirSync, renameSync, statSync, writeFileSync } from 'fs';
|
|
3
|
+
import { dirname, join, resolve } from 'path';
|
|
4
|
+
import {
|
|
5
|
+
backupControlPlaneDatabase,
|
|
6
|
+
CONTROL_PLANE_SCHEMA_VERSION,
|
|
7
|
+
inspectControlPlaneDatabaseFile,
|
|
8
|
+
listControlPlaneRecords,
|
|
9
|
+
readControlPlaneRecord,
|
|
10
|
+
readControlPlaneRecordWithinTransaction,
|
|
11
|
+
withControlPlaneTransaction,
|
|
12
|
+
writeControlPlaneRecord,
|
|
13
|
+
writeControlPlaneRecordWithinTransaction,
|
|
14
|
+
type ControlPlaneDatabaseInspection,
|
|
15
|
+
type ControlPlaneRecord,
|
|
16
|
+
} from './sqlite-store';
|
|
17
|
+
import { listRepositories } from '../../../cli/repositories/registry';
|
|
18
|
+
|
|
19
|
+
const MIGRATION_NAMESPACE = 'controller_home_migration';
|
|
20
|
+
const MIGRATION_SCOPE = 'controller';
|
|
21
|
+
const IMPORTABLE_REFERENCED_NAMESPACES = [
|
|
22
|
+
'execution_work_validation_index',
|
|
23
|
+
'execution_edit_validation_run',
|
|
24
|
+
'execution_edit_validation_index',
|
|
25
|
+
'chatgpt_work_conversation_binding',
|
|
26
|
+
'controller_session_claim_store',
|
|
27
|
+
'controller_round_relay',
|
|
28
|
+
'external_controller_launch_reservation',
|
|
29
|
+
] as const;
|
|
30
|
+
const TERMINAL_WORK_STATUSES = new Set(['completed', 'failed', 'cancelled']);
|
|
31
|
+
|
|
32
|
+
export interface ControllerHomeMigrationRecordRef {
|
|
33
|
+
namespace: string;
|
|
34
|
+
scope: string;
|
|
35
|
+
key: string;
|
|
36
|
+
schemaVersion: number;
|
|
37
|
+
sourceRevision: number;
|
|
38
|
+
sourceUpdatedAt: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ControllerHomeMigrationConflict extends ControllerHomeMigrationRecordRef {
|
|
42
|
+
kind: 'payload_conflict';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface ControllerHomeMigrationPreview {
|
|
46
|
+
sourceHome: string;
|
|
47
|
+
destinationHome: string;
|
|
48
|
+
sourceDatabase: {
|
|
49
|
+
path: string;
|
|
50
|
+
sha256: string;
|
|
51
|
+
walPath?: string;
|
|
52
|
+
walSha256?: string;
|
|
53
|
+
inspection: ControlPlaneDatabaseInspection;
|
|
54
|
+
};
|
|
55
|
+
destinationDatabase: {
|
|
56
|
+
path: string;
|
|
57
|
+
exists: boolean;
|
|
58
|
+
inspection?: ControlPlaneDatabaseInspection;
|
|
59
|
+
};
|
|
60
|
+
selected: ControllerHomeMigrationRecordRef[];
|
|
61
|
+
selectedByNamespace: Record<string, number>;
|
|
62
|
+
identical: ControllerHomeMigrationRecordRef[];
|
|
63
|
+
conflicts: ControllerHomeMigrationConflict[];
|
|
64
|
+
archivedOnly: {
|
|
65
|
+
terminalWorkContracts: number;
|
|
66
|
+
excludedNamespaces: readonly string[];
|
|
67
|
+
auditChain: 'retained_in_source_sqlite';
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface ImportedRecord extends ControllerHomeMigrationRecordRef {
|
|
72
|
+
destinationRevision: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface ControllerHomeMigrationRecord {
|
|
76
|
+
schemaVersion: 1;
|
|
77
|
+
migrationId: string;
|
|
78
|
+
status: 'applied' | 'rolled_back';
|
|
79
|
+
sourceHome: string;
|
|
80
|
+
destinationHome: string;
|
|
81
|
+
sourceDatabase: ControllerHomeMigrationPreview['sourceDatabase'];
|
|
82
|
+
destinationBackupPath: string;
|
|
83
|
+
selected: ControllerHomeMigrationRecordRef[];
|
|
84
|
+
imported: ImportedRecord[];
|
|
85
|
+
identical: ControllerHomeMigrationRecordRef[];
|
|
86
|
+
archivedOnly: ControllerHomeMigrationPreview['archivedOnly'];
|
|
87
|
+
sourceArchive: {
|
|
88
|
+
databaseModeBefore: number;
|
|
89
|
+
databaseModeAfter: number;
|
|
90
|
+
walModeBefore?: number;
|
|
91
|
+
walModeAfter?: number;
|
|
92
|
+
state: 'read_only' | 'restored';
|
|
93
|
+
};
|
|
94
|
+
appliedAt: string;
|
|
95
|
+
rolledBackAt?: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface AppliedControllerHomeMigration {
|
|
99
|
+
preview: ControllerHomeMigrationPreview;
|
|
100
|
+
migration: ControllerHomeMigrationRecord;
|
|
101
|
+
reportPath: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Archives imported Work that cannot be routed because its repository was not
|
|
106
|
+
* migrated. This is a migration operation, not a Work execution transition:
|
|
107
|
+
* original payloads remain in the frozen source SQLite and each target record
|
|
108
|
+
* receives a new auditable terminal revision.
|
|
109
|
+
*/
|
|
110
|
+
export function archiveUnroutableMigratedWork(input: {
|
|
111
|
+
destinationHome: string;
|
|
112
|
+
migrationId: string;
|
|
113
|
+
}): { migrationId: string; archivedWorkIds: string[]; archivedHandleIds: string[]; skipped: string[] } {
|
|
114
|
+
const destinationHome = resolve(input.destinationHome);
|
|
115
|
+
const migration = readControlPlaneRecord<ControllerHomeMigrationRecord>(destinationHome, MIGRATION_NAMESPACE, MIGRATION_SCOPE, input.migrationId);
|
|
116
|
+
if (!migration?.value || migration.value.status !== 'applied') throw new Error(`CONTROLLER_HOME_MIGRATION_NOT_APPLIED: ${input.migrationId}`);
|
|
117
|
+
const knownRepositories = new Set(listRepositories(destinationHome, { includeRemoved: false }).map((repository) => repository.repoId));
|
|
118
|
+
const imported = new Set(migration.value.imported.map((record) => recordIdentity({ namespace: record.namespace, scope: record.scope, key: record.key })));
|
|
119
|
+
const archivedWorkIds: string[] = [];
|
|
120
|
+
const archivedHandleIds: string[] = [];
|
|
121
|
+
const skipped: string[] = [];
|
|
122
|
+
withControlPlaneTransaction(destinationHome, (database) => {
|
|
123
|
+
for (const record of listControlPlaneRecords<unknown>(destinationHome, { namespace: 'work_contract', limit: 5_000 })) {
|
|
124
|
+
if (!imported.has(recordIdentity(record)) || knownRepositories.has(record.scope)) continue;
|
|
125
|
+
const value = record.value as Record<string, unknown>;
|
|
126
|
+
if (TERMINAL_WORK_STATUSES.has(String(value.status ?? ''))) { skipped.push(record.key); continue; }
|
|
127
|
+
writeControlPlaneRecordWithinTransaction(database, {
|
|
128
|
+
namespace: record.namespace, scope: record.scope, key: record.key, schemaVersion: record.schemaVersion,
|
|
129
|
+
value: { ...value, status: 'cancelled', updatedAt: new Date().toISOString(), terminalReason: 'CONTROLLER_HOME_MIGRATION_UNROUTABLE_REPOSITORY_ARCHIVED' },
|
|
130
|
+
action: 'controller_home_migration_archive_unroutable_work', expectedRevision: record.revision,
|
|
131
|
+
});
|
|
132
|
+
archivedWorkIds.push(record.key);
|
|
133
|
+
}
|
|
134
|
+
for (const record of listControlPlaneRecords<unknown>(destinationHome, { namespace: 'execution_work_handle', limit: 5_000 })) {
|
|
135
|
+
if (!imported.has(recordIdentity(record)) || knownRepositories.has(record.scope)) continue;
|
|
136
|
+
const value = record.value as Record<string, unknown>;
|
|
137
|
+
if (String(value.state ?? '') === 'cleaned') { skipped.push(record.key); continue; }
|
|
138
|
+
const finalization = typeof value.finalization === 'object' && value.finalization ? value.finalization : {};
|
|
139
|
+
writeControlPlaneRecordWithinTransaction(database, {
|
|
140
|
+
namespace: record.namespace, scope: record.scope, key: record.key, schemaVersion: record.schemaVersion,
|
|
141
|
+
value: { ...value, state: 'cleaned', updatedAt: new Date().toISOString(), failureReason: 'CONTROLLER_HOME_MIGRATION_UNROUTABLE_REPOSITORY_ARCHIVED', finalization: { ...finalization, validation: 'skipped', commit: 'skipped', merge: 'skipped', branchCleanup: 'skipped', worktreeCleanup: 'skipped' } },
|
|
142
|
+
action: 'controller_home_migration_archive_unroutable_handle', expectedRevision: record.revision,
|
|
143
|
+
});
|
|
144
|
+
archivedHandleIds.push(record.key);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return { migrationId: input.migrationId, archivedWorkIds, archivedHandleIds, skipped };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function databasePath(home: string): string {
|
|
151
|
+
return join(resolve(home), 'control-plane.sqlite');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function recordRef(record: ControlPlaneRecord<unknown>): ControllerHomeMigrationRecordRef {
|
|
155
|
+
return {
|
|
156
|
+
namespace: record.namespace,
|
|
157
|
+
scope: record.scope,
|
|
158
|
+
key: record.key,
|
|
159
|
+
schemaVersion: record.schemaVersion,
|
|
160
|
+
sourceRevision: record.revision,
|
|
161
|
+
sourceUpdatedAt: record.updatedAt,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function recordIdentity(record: Pick<ControlPlaneRecord<unknown>, 'namespace' | 'scope' | 'key'>): string {
|
|
166
|
+
return `${record.namespace}\u0000${record.scope}\u0000${record.key}`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function sameRecord(left: ControlPlaneRecord<unknown>, right: ControlPlaneRecord<unknown>): boolean {
|
|
170
|
+
return left.schemaVersion === right.schemaVersion && JSON.stringify(left.value) === JSON.stringify(right.value);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function referencesOneOf(value: unknown, ids: ReadonlySet<string>): boolean {
|
|
174
|
+
if (typeof value === 'string') return ids.has(value);
|
|
175
|
+
if (!value || typeof value !== 'object') return false;
|
|
176
|
+
if (Array.isArray(value)) return value.some((entry) => referencesOneOf(entry, ids));
|
|
177
|
+
return Object.values(value as Record<string, unknown>).some((entry) => referencesOneOf(entry, ids));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function addRecord(
|
|
181
|
+
selected: Map<string, ControlPlaneRecord<unknown>>,
|
|
182
|
+
record: ControlPlaneRecord<unknown>,
|
|
183
|
+
): void {
|
|
184
|
+
selected.set(recordIdentity(record), record);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function allRecords(home: string, namespace: string): ControlPlaneRecord<unknown>[] {
|
|
188
|
+
return listControlPlaneRecords<unknown>(home, { namespace, limit: 5_000 });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function sourceSelection(sourceHome: string): {
|
|
192
|
+
records: ControlPlaneRecord<unknown>[];
|
|
193
|
+
terminalWorkContracts: number;
|
|
194
|
+
} {
|
|
195
|
+
const selected = new Map<string, ControlPlaneRecord<unknown>>();
|
|
196
|
+
const workContracts = allRecords(sourceHome, 'work_contract');
|
|
197
|
+
const activeWork = workContracts.filter((record) => {
|
|
198
|
+
const status = (record.value as { status?: unknown }).status;
|
|
199
|
+
return typeof status === 'string' && !TERMINAL_WORK_STATUSES.has(status);
|
|
200
|
+
});
|
|
201
|
+
for (const record of activeWork) addRecord(selected, record);
|
|
202
|
+
|
|
203
|
+
const planIds = new Set(
|
|
204
|
+
activeWork
|
|
205
|
+
.map((record) => (record.value as { planId?: unknown }).planId)
|
|
206
|
+
.filter((value): value is string => typeof value === 'string' && value.length > 0),
|
|
207
|
+
);
|
|
208
|
+
for (const record of allRecords(sourceHome, 'plan_contract')) {
|
|
209
|
+
if (planIds.has(record.key)) addRecord(selected, record);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const workIds = new Set(activeWork.map((record) => record.key));
|
|
213
|
+
const workHandles = allRecords(sourceHome, 'execution_work_handle').filter((record) => {
|
|
214
|
+
const value = record.value as { workId?: unknown; workContractId?: unknown };
|
|
215
|
+
return (typeof value.workId === 'string' && workIds.has(value.workId))
|
|
216
|
+
|| (typeof value.workContractId === 'string' && workIds.has(value.workContractId));
|
|
217
|
+
});
|
|
218
|
+
for (const record of workHandles) addRecord(selected, record);
|
|
219
|
+
|
|
220
|
+
const sessionIds = new Set(
|
|
221
|
+
workHandles
|
|
222
|
+
.map((record) => (record.value as { sessionId?: unknown }).sessionId)
|
|
223
|
+
.filter((value): value is string => typeof value === 'string' && value.length > 0),
|
|
224
|
+
);
|
|
225
|
+
for (const record of allRecords(sourceHome, 'execution_session')) {
|
|
226
|
+
const sessionId = (record.value as { sessionId?: unknown }).sessionId;
|
|
227
|
+
if (sessionIds.has(record.key) || (typeof sessionId === 'string' && sessionIds.has(sessionId))) addRecord(selected, record);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const referenceIds = new Set<string>([...workIds, ...planIds, ...sessionIds]);
|
|
231
|
+
for (const namespace of IMPORTABLE_REFERENCED_NAMESPACES) {
|
|
232
|
+
for (const record of allRecords(sourceHome, namespace)) {
|
|
233
|
+
if (referencesOneOf(record.value, referenceIds)) addRecord(selected, record);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
records: [...selected.values()].sort((left, right) => recordIdentity(left).localeCompare(recordIdentity(right))),
|
|
239
|
+
terminalWorkContracts: workContracts.length - activeWork.length,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function sha256(path: string): Promise<string> {
|
|
244
|
+
return await new Promise<string>((resolveHash, reject) => {
|
|
245
|
+
const hash = createHash('sha256');
|
|
246
|
+
const input = createReadStream(path);
|
|
247
|
+
input.on('error', reject);
|
|
248
|
+
input.on('data', (chunk: Buffer) => hash.update(chunk));
|
|
249
|
+
input.on('end', () => resolveHash(hash.digest('hex')));
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function selectedByNamespace(records: readonly ControllerHomeMigrationRecordRef[]): Record<string, number> {
|
|
254
|
+
return records.reduce<Record<string, number>>((counts, record) => {
|
|
255
|
+
counts[record.namespace] = (counts[record.namespace] ?? 0) + 1;
|
|
256
|
+
return counts;
|
|
257
|
+
}, {});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function destinationRecordIfPresent(
|
|
261
|
+
destinationHome: string,
|
|
262
|
+
destinationExists: boolean,
|
|
263
|
+
record: ControlPlaneRecord<unknown>,
|
|
264
|
+
): ControlPlaneRecord<unknown> | undefined {
|
|
265
|
+
if (!destinationExists) return undefined;
|
|
266
|
+
return readControlPlaneRecord<unknown>(destinationHome, record.namespace, record.scope, record.key);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export async function previewControllerHomeMigration(input: {
|
|
270
|
+
sourceHome: string;
|
|
271
|
+
destinationHome: string;
|
|
272
|
+
}): Promise<ControllerHomeMigrationPreview> {
|
|
273
|
+
const sourceHome = resolve(input.sourceHome);
|
|
274
|
+
const destinationHome = resolve(input.destinationHome);
|
|
275
|
+
if (sourceHome === destinationHome) throw new Error('CONTROLLER_HOME_MIGRATION_SAME_HOME');
|
|
276
|
+
const sourceDatabase = databasePath(sourceHome);
|
|
277
|
+
if (!existsSync(sourceDatabase)) throw new Error(`CONTROLLER_HOME_MIGRATION_SOURCE_DATABASE_MISSING: ${sourceDatabase}`);
|
|
278
|
+
const sourceWal = `${sourceDatabase}-wal`;
|
|
279
|
+
const sourceWalExists = existsSync(sourceWal);
|
|
280
|
+
|
|
281
|
+
const selection = sourceSelection(sourceHome);
|
|
282
|
+
const selected = selection.records.map(recordRef);
|
|
283
|
+
const destinationDatabase = databasePath(destinationHome);
|
|
284
|
+
const destinationExists = existsSync(destinationDatabase);
|
|
285
|
+
const identical: ControllerHomeMigrationRecordRef[] = [];
|
|
286
|
+
const conflicts: ControllerHomeMigrationConflict[] = [];
|
|
287
|
+
for (const record of selection.records) {
|
|
288
|
+
const destination = destinationRecordIfPresent(destinationHome, destinationExists, record);
|
|
289
|
+
if (!destination) continue;
|
|
290
|
+
if (sameRecord(record, destination)) identical.push(recordRef(record));
|
|
291
|
+
else conflicts.push({ ...recordRef(record), kind: 'payload_conflict' });
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
sourceHome,
|
|
296
|
+
destinationHome,
|
|
297
|
+
sourceDatabase: {
|
|
298
|
+
path: sourceDatabase,
|
|
299
|
+
sha256: await sha256(sourceDatabase),
|
|
300
|
+
...(sourceWalExists ? { walPath: sourceWal, walSha256: await sha256(sourceWal) } : {}),
|
|
301
|
+
inspection: inspectControlPlaneDatabaseFile(sourceDatabase),
|
|
302
|
+
},
|
|
303
|
+
destinationDatabase: {
|
|
304
|
+
path: destinationDatabase,
|
|
305
|
+
exists: destinationExists,
|
|
306
|
+
...(destinationExists ? { inspection: inspectControlPlaneDatabaseFile(destinationDatabase) } : {}),
|
|
307
|
+
},
|
|
308
|
+
selected,
|
|
309
|
+
selectedByNamespace: selectedByNamespace(selected),
|
|
310
|
+
identical,
|
|
311
|
+
conflicts,
|
|
312
|
+
archivedOnly: {
|
|
313
|
+
terminalWorkContracts: selection.terminalWorkContracts,
|
|
314
|
+
excludedNamespaces: ['mcp credentials', 'runtime release authority', 'runtime owner', 'Recovery state', 'plugin configuration', 'process bindings and logs'],
|
|
315
|
+
auditChain: 'retained_in_source_sqlite',
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function reportPath(destinationHome: string, migrationId: string): string {
|
|
321
|
+
return join(destinationHome, 'archives', 'controller-home-migrations', `${migrationId}.json`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function writeReport(path: string, value: unknown): void {
|
|
325
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
326
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
327
|
+
writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
|
|
328
|
+
renameSync(temporary, path);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export async function applyControllerHomeMigration(input: {
|
|
332
|
+
sourceHome: string;
|
|
333
|
+
destinationHome: string;
|
|
334
|
+
}): Promise<AppliedControllerHomeMigration> {
|
|
335
|
+
const preview = await previewControllerHomeMigration(input);
|
|
336
|
+
if (preview.conflicts.length > 0) {
|
|
337
|
+
throw new Error(`CONTROLLER_HOME_MIGRATION_CONFLICTS: ${preview.conflicts.length}`);
|
|
338
|
+
}
|
|
339
|
+
const migrationId = `controller-home-${randomUUID()}`;
|
|
340
|
+
const destinationBackupPath = join(
|
|
341
|
+
preview.destinationHome,
|
|
342
|
+
'backups',
|
|
343
|
+
'controller-home-migrations',
|
|
344
|
+
`${migrationId}.before.sqlite`,
|
|
345
|
+
);
|
|
346
|
+
backupControlPlaneDatabase(preview.destinationHome, destinationBackupPath);
|
|
347
|
+
const sourceMode = statSync(preview.sourceDatabase.path).mode & 0o777;
|
|
348
|
+
const sourceWalMode = preview.sourceDatabase.walPath && existsSync(preview.sourceDatabase.walPath)
|
|
349
|
+
? statSync(preview.sourceDatabase.walPath).mode & 0o777
|
|
350
|
+
: undefined;
|
|
351
|
+
const imported: ImportedRecord[] = [];
|
|
352
|
+
const selectedRecords = sourceSelection(preview.sourceHome).records;
|
|
353
|
+
const migration = withControlPlaneTransaction(preview.destinationHome, (database) => {
|
|
354
|
+
for (const record of selectedRecords) {
|
|
355
|
+
const current = readControlPlaneRecordWithinTransaction<unknown>(database, record.namespace, record.scope, record.key);
|
|
356
|
+
if (current) {
|
|
357
|
+
if (sameRecord(record, current)) continue;
|
|
358
|
+
throw new Error(`CONTROLLER_HOME_MIGRATION_CONFLICT: ${record.namespace}/${record.scope}/${record.key}`);
|
|
359
|
+
}
|
|
360
|
+
const written = writeControlPlaneRecordWithinTransaction(database, {
|
|
361
|
+
namespace: record.namespace,
|
|
362
|
+
scope: record.scope,
|
|
363
|
+
key: record.key,
|
|
364
|
+
schemaVersion: record.schemaVersion,
|
|
365
|
+
value: record.value,
|
|
366
|
+
action: 'controller_home_migration_import',
|
|
367
|
+
expectedRevision: null,
|
|
368
|
+
});
|
|
369
|
+
imported.push({ ...recordRef(record), destinationRevision: written.revision });
|
|
370
|
+
}
|
|
371
|
+
const value: ControllerHomeMigrationRecord = {
|
|
372
|
+
schemaVersion: 1,
|
|
373
|
+
migrationId,
|
|
374
|
+
status: 'applied',
|
|
375
|
+
sourceHome: preview.sourceHome,
|
|
376
|
+
destinationHome: preview.destinationHome,
|
|
377
|
+
sourceDatabase: preview.sourceDatabase,
|
|
378
|
+
destinationBackupPath,
|
|
379
|
+
selected: preview.selected,
|
|
380
|
+
imported,
|
|
381
|
+
identical: preview.identical,
|
|
382
|
+
archivedOnly: preview.archivedOnly,
|
|
383
|
+
sourceArchive: {
|
|
384
|
+
databaseModeBefore: sourceMode,
|
|
385
|
+
databaseModeAfter: sourceMode,
|
|
386
|
+
...(sourceWalMode === undefined ? {} : { walModeBefore: sourceWalMode, walModeAfter: sourceWalMode }),
|
|
387
|
+
state: 'restored',
|
|
388
|
+
},
|
|
389
|
+
appliedAt: new Date().toISOString(),
|
|
390
|
+
};
|
|
391
|
+
writeControlPlaneRecordWithinTransaction(database, {
|
|
392
|
+
namespace: MIGRATION_NAMESPACE,
|
|
393
|
+
scope: MIGRATION_SCOPE,
|
|
394
|
+
key: migrationId,
|
|
395
|
+
schemaVersion: 1,
|
|
396
|
+
value,
|
|
397
|
+
action: 'controller_home_migration_applied',
|
|
398
|
+
expectedRevision: null,
|
|
399
|
+
});
|
|
400
|
+
return value;
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Only the legacy SQLite authority is frozen. Its full audit history stays
|
|
404
|
+
// queryable in place; no OAuth, Runtime, Recovery, or plugin files move.
|
|
405
|
+
const readOnlyMode = sourceMode & ~0o222;
|
|
406
|
+
chmodSync(preview.sourceDatabase.path, readOnlyMode);
|
|
407
|
+
const readOnlyWalMode = sourceWalMode === undefined ? undefined : sourceWalMode & ~0o222;
|
|
408
|
+
if (preview.sourceDatabase.walPath && readOnlyWalMode !== undefined) chmodSync(preview.sourceDatabase.walPath, readOnlyWalMode);
|
|
409
|
+
const archived = {
|
|
410
|
+
...migration,
|
|
411
|
+
sourceArchive: {
|
|
412
|
+
databaseModeBefore: sourceMode,
|
|
413
|
+
databaseModeAfter: readOnlyMode,
|
|
414
|
+
...(sourceWalMode === undefined ? {} : { walModeBefore: sourceWalMode, walModeAfter: readOnlyWalMode }),
|
|
415
|
+
state: 'read_only' as const,
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
writeControlPlaneRecord(preview.destinationHome, {
|
|
419
|
+
namespace: MIGRATION_NAMESPACE,
|
|
420
|
+
scope: MIGRATION_SCOPE,
|
|
421
|
+
key: migrationId,
|
|
422
|
+
schemaVersion: 1,
|
|
423
|
+
value: archived,
|
|
424
|
+
action: 'controller_home_migration_archive_source',
|
|
425
|
+
expectedRevision: 1,
|
|
426
|
+
});
|
|
427
|
+
const path = reportPath(preview.destinationHome, migrationId);
|
|
428
|
+
writeReport(path, { preview, migration: archived });
|
|
429
|
+
return { preview, migration: archived, reportPath: path };
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export function rollbackControllerHomeMigration(input: {
|
|
433
|
+
destinationHome: string;
|
|
434
|
+
migrationId: string;
|
|
435
|
+
}): ControllerHomeMigrationRecord {
|
|
436
|
+
const destinationHome = resolve(input.destinationHome);
|
|
437
|
+
const current = readControlPlaneRecord<ControllerHomeMigrationRecord>(
|
|
438
|
+
destinationHome,
|
|
439
|
+
MIGRATION_NAMESPACE,
|
|
440
|
+
MIGRATION_SCOPE,
|
|
441
|
+
input.migrationId,
|
|
442
|
+
);
|
|
443
|
+
if (!current) throw new Error(`CONTROLLER_HOME_MIGRATION_NOT_FOUND: ${input.migrationId}`);
|
|
444
|
+
if (current.value.status === 'rolled_back') return current.value;
|
|
445
|
+
|
|
446
|
+
const rolledBack = withControlPlaneTransaction(destinationHome, (database) => {
|
|
447
|
+
for (const imported of current.value.imported) {
|
|
448
|
+
const record = readControlPlaneRecordWithinTransaction<unknown>(database, imported.namespace, imported.scope, imported.key);
|
|
449
|
+
if (!record || record.revision !== imported.destinationRevision) {
|
|
450
|
+
throw new Error(`CONTROLLER_HOME_MIGRATION_ROLLBACK_CONFLICT: ${imported.namespace}/${imported.scope}/${imported.key}`);
|
|
451
|
+
}
|
|
452
|
+
database.prepare(`
|
|
453
|
+
DELETE FROM control_plane_audit
|
|
454
|
+
WHERE namespace = ? AND scope = ? AND record_key = ?
|
|
455
|
+
`).run(imported.namespace, imported.scope, imported.key);
|
|
456
|
+
database.prepare(`
|
|
457
|
+
DELETE FROM control_plane_records
|
|
458
|
+
WHERE namespace = ? AND scope = ? AND record_key = ?
|
|
459
|
+
`).run(imported.namespace, imported.scope, imported.key);
|
|
460
|
+
}
|
|
461
|
+
const value: ControllerHomeMigrationRecord = {
|
|
462
|
+
...current.value,
|
|
463
|
+
status: 'rolled_back',
|
|
464
|
+
sourceArchive: {
|
|
465
|
+
...current.value.sourceArchive,
|
|
466
|
+
state: 'restored',
|
|
467
|
+
},
|
|
468
|
+
rolledBackAt: new Date().toISOString(),
|
|
469
|
+
};
|
|
470
|
+
writeControlPlaneRecordWithinTransaction(database, {
|
|
471
|
+
namespace: MIGRATION_NAMESPACE,
|
|
472
|
+
scope: MIGRATION_SCOPE,
|
|
473
|
+
key: current.value.migrationId,
|
|
474
|
+
schemaVersion: CONTROL_PLANE_SCHEMA_VERSION,
|
|
475
|
+
value,
|
|
476
|
+
action: 'controller_home_migration_rolled_back',
|
|
477
|
+
expectedRevision: current.revision,
|
|
478
|
+
});
|
|
479
|
+
return value;
|
|
480
|
+
});
|
|
481
|
+
chmodSync(rolledBack.sourceDatabase.path, rolledBack.sourceArchive.databaseModeBefore);
|
|
482
|
+
if (rolledBack.sourceDatabase.walPath && rolledBack.sourceArchive.walModeBefore !== undefined) {
|
|
483
|
+
chmodSync(rolledBack.sourceDatabase.walPath, rolledBack.sourceArchive.walModeBefore);
|
|
484
|
+
}
|
|
485
|
+
writeReport(reportPath(destinationHome, rolledBack.migrationId), { migration: rolledBack });
|
|
486
|
+
return rolledBack;
|
|
487
|
+
}
|
|
@@ -20,6 +20,7 @@ export interface Requirement {
|
|
|
20
20
|
outcomeStatement: string;
|
|
21
21
|
acceptanceCriteria: string[];
|
|
22
22
|
requiredDeliveryReferences: string[];
|
|
23
|
+
/** @deprecated Migration compatibility pointer only. Current Plan.requirementId is the relationship authority. */
|
|
23
24
|
activePlanId?: string;
|
|
24
25
|
state: RequirementState;
|
|
25
26
|
needsAttention: boolean;
|
|
@@ -124,7 +125,7 @@ export function createRequirement(
|
|
|
124
125
|
const ALLOWED_TRANSITIONS: Readonly<Record<RequirementState, readonly RequirementState[]>> = {
|
|
125
126
|
planned: ['planned', 'active', 'waiting_for_user', 'cancelled'],
|
|
126
127
|
active: ['active', 'waiting_for_user', 'done', 'cancelled'],
|
|
127
|
-
waiting_for_user: ['waiting_for_user', 'planned', 'active', 'cancelled'],
|
|
128
|
+
waiting_for_user: ['waiting_for_user', 'planned', 'active', 'done', 'cancelled'],
|
|
128
129
|
done: ['done', 'cancelled'],
|
|
129
130
|
cancelled: ['cancelled'],
|
|
130
131
|
};
|
|
@@ -164,6 +165,7 @@ export function updateRequirement(
|
|
|
164
165
|
});
|
|
165
166
|
}
|
|
166
167
|
|
|
168
|
+
/** @deprecated Migration/legacy compatibility only. New Plan creation must bind Plan.requirementId and derive active Plan slices by query. */
|
|
167
169
|
export function setRequirementPlan(
|
|
168
170
|
options: RequirementStoreOptions,
|
|
169
171
|
input: { requirementId: string; planId: string; action?: string },
|
|
@@ -189,12 +191,13 @@ export interface RequirementCompletionInput {
|
|
|
189
191
|
}
|
|
190
192
|
|
|
191
193
|
/**
|
|
192
|
-
*
|
|
194
|
+
* Project machine-complete Work delivery into a Requirement without asserting
|
|
195
|
+
* semantic acceptance. The Requirement waits for an explicit ChatGPT/user
|
|
196
|
+
* decision; a previously audited `done` Requirement is never reopened by
|
|
197
|
+
* historical execution evidence.
|
|
193
198
|
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
* legacy Task receipt, or stale Work projection therefore cannot reopen or
|
|
197
|
-
* rewrite a completed user outcome.
|
|
199
|
+
* The legacy function name is retained for compatibility with completion
|
|
200
|
+
* callers. Its authority is evidence projection only, not semantic completion.
|
|
198
201
|
*/
|
|
199
202
|
export function completeRequirementFromWork(
|
|
200
203
|
options: RequirementStoreOptions,
|
|
@@ -221,19 +224,20 @@ export function completeRequirementFromWork(
|
|
|
221
224
|
if (receipt.delivery.kind === 'superseded' || receipt.delivery.status !== 'integrated' || !receipt.delivery.reachable) throw new Error('REQUIREMENT_WORK_DELIVERY_NOT_PROVEN');
|
|
222
225
|
if (!['complete', 'maintenance_warning'].includes(receipt.cleanup.status) || receipt.cleanup.blockers.length > 0) throw new Error('REQUIREMENT_WORK_CLEANUP_NOT_PROVEN');
|
|
223
226
|
|
|
227
|
+
if (current.value.state === 'waiting_for_user' && current.value.auditRefs.includes(receipt.receiptId)) return current.value;
|
|
224
228
|
return updateRequirement(options, {
|
|
225
229
|
requirementId,
|
|
226
|
-
action: '
|
|
230
|
+
action: 'requirement_delivery_evidence_recorded',
|
|
227
231
|
mutate: (latest) => {
|
|
228
|
-
// Preserve a concurrent
|
|
229
|
-
// never allowed to move a terminal Requirement backwards.
|
|
232
|
+
// Preserve a concurrent explicit semantic completion; stale execution
|
|
233
|
+
// evidence is never allowed to move a terminal Requirement backwards.
|
|
230
234
|
if (latest.state === 'done') return latest;
|
|
231
235
|
if (latest.state === 'cancelled') throw new Error('REQUIREMENT_CANCELLED');
|
|
232
236
|
return {
|
|
233
237
|
...latest,
|
|
234
|
-
state: '
|
|
238
|
+
state: 'waiting_for_user',
|
|
235
239
|
needsAttention: false,
|
|
236
|
-
attentionSummary:
|
|
240
|
+
attentionSummary: `Work ${work.workId} delivered machine-valid evidence (${receipt.receiptId}); ChatGPT/user semantic acceptance is still required.`,
|
|
237
241
|
auditRefs: Array.from(new Set([...latest.auditRefs, receipt.receiptId])).slice(-50),
|
|
238
242
|
};
|
|
239
243
|
},
|
|
@@ -207,6 +207,37 @@ function openDatabase(controllerHome: string): SqliteDatabase {
|
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Open an already initialized authority for SELECT-only callers without
|
|
212
|
+
* re-running WAL/schema setup. In WAL mode a reader can coexist with a writer
|
|
213
|
+
* holding BEGIN IMMEDIATE; forcing PRAGMA journal_mode/DDL on every read turns
|
|
214
|
+
* that harmless writer reservation into a synchronous busy_timeout on the
|
|
215
|
+
* Canonical Runtime event loop.
|
|
216
|
+
*/
|
|
217
|
+
function openDatabaseForRead(controllerHome: string): SqliteDatabase {
|
|
218
|
+
const path = controlPlaneDatabasePath(controllerHome);
|
|
219
|
+
if (!existsSync(path)) return openDatabase(controllerHome);
|
|
220
|
+
|
|
221
|
+
const database = openRawDatabase(path);
|
|
222
|
+
try {
|
|
223
|
+
database.exec('PRAGMA busy_timeout = 5000; PRAGMA foreign_keys = ON;');
|
|
224
|
+
const schemaVersion = assertSupportedSchema(database, path);
|
|
225
|
+
if (schemaVersion !== undefined) return database;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
database.close();
|
|
228
|
+
const busy = sqliteBusyError(path, error);
|
|
229
|
+
if (busy) throw busy;
|
|
230
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
231
|
+
if (message.startsWith('CONTROL_PLANE_')) throw error;
|
|
232
|
+
throw new Error(`CONTROL_PLANE_SQLITE_CORRUPT: ${path}: ${message}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Preserve first-use/partially initialized compatibility: only a database
|
|
236
|
+
// with no schema authority falls back to the initializer/writer path.
|
|
237
|
+
database.close();
|
|
238
|
+
return openDatabase(controllerHome);
|
|
239
|
+
}
|
|
240
|
+
|
|
210
241
|
function rowToRecord<T>(row: StoredRecordRow): ControlPlaneRecord<T> {
|
|
211
242
|
let value: T;
|
|
212
243
|
try {
|
|
@@ -321,7 +352,7 @@ export function withControlPlaneTransaction<T>(controllerHome: string, operation
|
|
|
321
352
|
}
|
|
322
353
|
|
|
323
354
|
export function readControlPlaneRecord<T>(controllerHome: string, namespace: string, scope: string, key: string): ControlPlaneRecord<T> | undefined {
|
|
324
|
-
const database =
|
|
355
|
+
const database = openDatabaseForRead(controllerHome);
|
|
325
356
|
try {
|
|
326
357
|
return selectRecord<T>(database, namespace, scope, key);
|
|
327
358
|
} finally {
|
|
@@ -361,7 +392,7 @@ export function listControlPlaneRecords<T>(
|
|
|
361
392
|
controllerHome: string,
|
|
362
393
|
input: { namespace: string; scope?: string; limit?: number },
|
|
363
394
|
): ControlPlaneRecord<T>[] {
|
|
364
|
-
const database =
|
|
395
|
+
const database = openDatabaseForRead(controllerHome);
|
|
365
396
|
try {
|
|
366
397
|
const limit = Math.max(1, Math.min(Math.trunc(input.limit ?? 1000), 5000));
|
|
367
398
|
const rows = input.scope === undefined
|