@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
|
@@ -52,13 +52,6 @@ const LOCAL_JOBS_LEGACY_ACTIVE_PATTERNS = [
|
|
|
52
52
|
/local jobs?.*finish before runtime storage/i,
|
|
53
53
|
];
|
|
54
54
|
|
|
55
|
-
const BROWSER_GRANT_PATTERNS = [
|
|
56
|
-
/web_target_not_allowed/i,
|
|
57
|
-
/browser.*domain.*not allowed/i,
|
|
58
|
-
/allowed_domains/i,
|
|
59
|
-
/domain access.*required/i,
|
|
60
|
-
/browser domain grant/i,
|
|
61
|
-
];
|
|
62
55
|
|
|
63
56
|
const EXTERNAL_FILESYSTEM_GRANT_PATTERNS = [
|
|
64
57
|
/external filesystem/i,
|
|
@@ -125,7 +118,6 @@ export function classifyFailure(message: string | undefined): RecoveryClass {
|
|
|
125
118
|
if (LOCAL_JOBS_UNREADABLE_PATTERNS.some((pattern) => pattern.test(text))) return 'local_jobs_unreadable';
|
|
126
119
|
if (RUNTIME_STORAGE_PATTERNS.some((pattern) => pattern.test(text))) return 'runtime_storage_not_ready';
|
|
127
120
|
if (AUTH_PATTERNS.some((pattern) => pattern.test(text))) return 'auth_required';
|
|
128
|
-
if (BROWSER_GRANT_PATTERNS.some((pattern) => pattern.test(text))) return 'browser_domain_grant_required';
|
|
129
121
|
if (EXTERNAL_FILESYSTEM_GRANT_PATTERNS.some((pattern) => pattern.test(text))) return 'external_filesystem_grant_required';
|
|
130
122
|
if (DIRTY_WORKTREE_CONFLICT_PATTERNS.some((pattern) => pattern.test(text))) return 'dirty_worktree_conflict';
|
|
131
123
|
if (USER_ACTION_PATTERNS.some((pattern) => pattern.test(text))) return 'user_action_required';
|
|
@@ -144,7 +136,6 @@ export function dominantRecoveryClass(classes: readonly RecoveryClass[]): Recove
|
|
|
144
136
|
'local_jobs_legacy_active',
|
|
145
137
|
'local_jobs_unreadable',
|
|
146
138
|
'runtime_storage_not_ready',
|
|
147
|
-
'browser_domain_grant_required',
|
|
148
139
|
'external_filesystem_grant_required',
|
|
149
140
|
'local_jobs_reconciliation_required',
|
|
150
141
|
'maintenance_executor_required',
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'fs';
|
|
2
2
|
import { basename, dirname, join, relative, resolve } from 'path';
|
|
3
3
|
import { tmpdir } from 'os';
|
|
4
|
+
import { runProcess } from '../../effects/process-runner';
|
|
4
5
|
import { cleanupEditSession, getEditSession, listEditSessions, reconcileEditSession } from '../../cli/editing/edit-session';
|
|
5
6
|
import { ensureRepositoryRuntimeStorage, type RepositoryRuntimeStorageReport } from '../../cli/repositories/runtime-storage';
|
|
6
7
|
import type { RepositoryRecord } from '../../cli/repositories/types';
|
|
7
8
|
import { rebuildRepositoryProjection } from '../projections/materialized-view';
|
|
8
9
|
import { getWorkContract, readWorkContractStore, transitionWorkContractPhase } from '../control-plane/facade/work-contract-store';
|
|
9
|
-
import {
|
|
10
|
+
import { getControllerSession, withControllerSessionTerminalizationFence } from '../control-plane/facade/controller-session-store';
|
|
11
|
+
import { listPlanContracts } from '../control-plane/facade/plan-contract-store';
|
|
12
|
+
import { readRequirement } from '../control-plane/persistence/requirement-store';
|
|
13
|
+
import { listControlPlaneRecords, type ControlPlaneRecord } from '../control-plane/persistence/sqlite-store';
|
|
14
|
+
import { isTerminalWorkContractStatus, type WorkContract } from '../control-plane/facade/types';
|
|
15
|
+
import { listSchedules } from '../workflow/schedules/store';
|
|
10
16
|
import {
|
|
11
17
|
collectRuntimeProcesses,
|
|
12
18
|
removeRuntimeTempEntry,
|
|
@@ -36,11 +42,14 @@ export type RuntimeMaintenanceCandidateKind =
|
|
|
36
42
|
| 'runtime_storage_warning'
|
|
37
43
|
| 'stale_runtime_temp_entry'
|
|
38
44
|
| 'stale_work_contract'
|
|
39
|
-
| 'stale_edit_session'
|
|
45
|
+
| 'stale_edit_session'
|
|
46
|
+
| 'retained_migrated_work'
|
|
47
|
+
| 'unowned_managed_worktree';
|
|
40
48
|
|
|
41
49
|
export interface RuntimeMaintenanceRepository {
|
|
42
50
|
repoId: string;
|
|
43
51
|
canonicalRoot: string;
|
|
52
|
+
defaultBranch?: string;
|
|
44
53
|
/** Test-only or embedded-runtime override. Production defaults to system temp roots. */
|
|
45
54
|
runtimeTempRoots?: string[];
|
|
46
55
|
}
|
|
@@ -79,6 +88,11 @@ export interface RuntimeMaintenanceCandidate {
|
|
|
79
88
|
deadlineAt?: string;
|
|
80
89
|
suggestedAction: RuntimeMaintenanceActionId;
|
|
81
90
|
ownershipStatus?: 'explicit' | 'unknown';
|
|
91
|
+
sourceControllerHome?: string;
|
|
92
|
+
objective?: string;
|
|
93
|
+
continuationPrompt?: string;
|
|
94
|
+
disposition?: 'resume_or_supersede_review' | 'completion_reconciliation' | 'ownership_reconciliation' | 'source_preservation_required';
|
|
95
|
+
sourceState?: 'no_managed_source' | 'clean_integrated' | 'dirty_worktree' | 'unintegrated_commits' | 'source_state_unknown';
|
|
82
96
|
}
|
|
83
97
|
|
|
84
98
|
export interface RuntimeMaintenanceSummary {
|
|
@@ -93,6 +107,8 @@ export interface RuntimeMaintenanceSummary {
|
|
|
93
107
|
staleRuntimeTempEntries: number;
|
|
94
108
|
staleWorkContracts: number;
|
|
95
109
|
staleEditSessions: number;
|
|
110
|
+
retainedMigratedWork: number;
|
|
111
|
+
unownedManagedWorktrees: number;
|
|
96
112
|
}
|
|
97
113
|
|
|
98
114
|
export interface RuntimeMaintenanceStatus {
|
|
@@ -223,6 +239,8 @@ function summarize(candidates: RuntimeMaintenanceCandidate[]): RuntimeMaintenanc
|
|
|
223
239
|
staleRuntimeTempEntries: candidates.filter((candidate) => candidate.kind === 'stale_runtime_temp_entry').length,
|
|
224
240
|
staleWorkContracts: candidates.filter((candidate) => candidate.kind === 'stale_work_contract').length,
|
|
225
241
|
staleEditSessions: candidates.filter((candidate) => candidate.kind === 'stale_edit_session').length,
|
|
242
|
+
retainedMigratedWork: candidates.filter((candidate) => candidate.kind === 'retained_migrated_work').length,
|
|
243
|
+
unownedManagedWorktrees: candidates.filter((candidate) => candidate.kind === 'unowned_managed_worktree').length,
|
|
226
244
|
};
|
|
227
245
|
}
|
|
228
246
|
|
|
@@ -389,6 +407,282 @@ function scanRuntimeTempCandidates(
|
|
|
389
407
|
}));
|
|
390
408
|
}
|
|
391
409
|
|
|
410
|
+
interface RetainedMigratedWork {
|
|
411
|
+
migrationId: string;
|
|
412
|
+
sourceHome: string;
|
|
413
|
+
work: WorkContract;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
interface AppliedControllerHomeMigrationRecord {
|
|
417
|
+
migrationId: string;
|
|
418
|
+
status: 'applied' | 'rolled_back';
|
|
419
|
+
sourceHome: string;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function listRetainedMigratedWork(destinationHomeInput: string, repoId: string): RetainedMigratedWork[] {
|
|
423
|
+
const destinationHome = resolve(destinationHomeInput);
|
|
424
|
+
const destinationWorkIds = new Set(
|
|
425
|
+
listControlPlaneRecords<WorkContract>(destinationHome, { namespace: 'work_contract', scope: repoId, limit: 5_000 })
|
|
426
|
+
.map((record) => record.key),
|
|
427
|
+
);
|
|
428
|
+
const migrations = listControlPlaneRecords<AppliedControllerHomeMigrationRecord>(destinationHome, {
|
|
429
|
+
namespace: 'controller_home_migration',
|
|
430
|
+
scope: 'controller',
|
|
431
|
+
limit: 100,
|
|
432
|
+
});
|
|
433
|
+
const retained: RetainedMigratedWork[] = [];
|
|
434
|
+
for (const record of migrations) {
|
|
435
|
+
const migration = record.value;
|
|
436
|
+
if (migration.status !== 'applied') continue;
|
|
437
|
+
const sourceHome = resolve(migration.sourceHome);
|
|
438
|
+
if (!existsSync(join(sourceHome, 'control-plane.sqlite'))) continue;
|
|
439
|
+
let sourceWork: ControlPlaneRecord<WorkContract>[];
|
|
440
|
+
try {
|
|
441
|
+
sourceWork = listControlPlaneRecords<WorkContract>(sourceHome, { namespace: 'work_contract', limit: 5_000 });
|
|
442
|
+
} catch {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
for (const source of sourceWork) {
|
|
446
|
+
const work = source.value;
|
|
447
|
+
if (work.repoId !== repoId || !isTerminalWorkContractStatus(work.status)) continue;
|
|
448
|
+
if (!work.worktreeRef?.trim() || !existsSync(work.worktreeRef)) continue;
|
|
449
|
+
if (destinationWorkIds.has(work.workId)) continue;
|
|
450
|
+
retained.push({ migrationId: migration.migrationId, sourceHome, work });
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return retained.sort((left, right) => left.work.updatedAt.localeCompare(right.work.updatedAt));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function gitWorktreePaths(repoRoot: string): string[] {
|
|
457
|
+
const result = runProcess('git', ['worktree', 'list', '--porcelain'], {
|
|
458
|
+
cwd: repoRoot,
|
|
459
|
+
timeoutMs: 10_000,
|
|
460
|
+
maxOutputBytes: 500_000,
|
|
461
|
+
});
|
|
462
|
+
if (!result.ok) return [];
|
|
463
|
+
return result.stdout
|
|
464
|
+
.split(/\r?\n/)
|
|
465
|
+
.filter((line) => line.startsWith('worktree '))
|
|
466
|
+
.map((line) => resolve(line.slice('worktree '.length).trim()))
|
|
467
|
+
.filter(Boolean);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function looksLikeForgeManagedWorktree(path: string): boolean {
|
|
471
|
+
const normalized = resolve(path).replace(/\\/g, '/');
|
|
472
|
+
return normalized.includes('/.forge/managed-worktrees/')
|
|
473
|
+
|| normalized.includes('/.forge/controller/managed-worktrees/')
|
|
474
|
+
|| normalized.includes('/.repo-harness/managed-worktrees/')
|
|
475
|
+
|| normalized.includes('/managed-worktrees/repo_');
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
type StaleWorkSourceState = NonNullable<RuntimeMaintenanceCandidate['sourceState']>;
|
|
479
|
+
|
|
480
|
+
interface StaleWorkSourceInspection {
|
|
481
|
+
safeToCancel: boolean;
|
|
482
|
+
state: StaleWorkSourceState;
|
|
483
|
+
path?: string;
|
|
484
|
+
detail: string;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function inspectStaleWorkRepositorySource(
|
|
488
|
+
repository: RuntimeMaintenanceRepository,
|
|
489
|
+
contract: WorkContract,
|
|
490
|
+
): StaleWorkSourceInspection {
|
|
491
|
+
const recordedPath = contract.worktreeRef?.trim();
|
|
492
|
+
if (!recordedPath) {
|
|
493
|
+
return {
|
|
494
|
+
safeToCancel: true,
|
|
495
|
+
state: 'no_managed_source',
|
|
496
|
+
detail: 'Work has no recorded managed worktree source.',
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
const path = resolve(recordedPath);
|
|
500
|
+
if (!looksLikeForgeManagedWorktree(path)) {
|
|
501
|
+
return {
|
|
502
|
+
safeToCancel: true,
|
|
503
|
+
state: 'no_managed_source',
|
|
504
|
+
path,
|
|
505
|
+
detail: 'Recorded Work path is not a Forge-managed worktree and is not disposable maintenance source.',
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
if (!existsSync(path)) {
|
|
509
|
+
return {
|
|
510
|
+
safeToCancel: false,
|
|
511
|
+
state: 'source_state_unknown',
|
|
512
|
+
path,
|
|
513
|
+
detail: 'Recorded managed worktree path is absent, so maintenance cannot prove that no unique branch/source remains recoverable through Git or checkout metadata; destructive stale-Work cancellation is fenced.',
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const status = runProcess('git', ['status', '--porcelain', '--untracked-files=all'], {
|
|
518
|
+
cwd: path,
|
|
519
|
+
timeoutMs: 10_000,
|
|
520
|
+
maxOutputBytes: 500_000,
|
|
521
|
+
});
|
|
522
|
+
if (!status.ok) {
|
|
523
|
+
return {
|
|
524
|
+
safeToCancel: false,
|
|
525
|
+
state: 'source_state_unknown',
|
|
526
|
+
path,
|
|
527
|
+
detail: `Managed worktree source state could not be read (${status.stderr || 'git status failed'}); destructive stale-Work cancellation is fenced.`,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
if (status.stdout.trim()) {
|
|
531
|
+
return {
|
|
532
|
+
safeToCancel: false,
|
|
533
|
+
state: 'dirty_worktree',
|
|
534
|
+
path,
|
|
535
|
+
detail: 'Managed worktree contains uncommitted or untracked repository source; durable metadata/process evidence is not a substitute for preserving the live source checkout.',
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const head = runProcess('git', ['rev-parse', 'HEAD'], {
|
|
540
|
+
cwd: path,
|
|
541
|
+
timeoutMs: 10_000,
|
|
542
|
+
maxOutputBytes: 100_000,
|
|
543
|
+
});
|
|
544
|
+
const targetBranch = repository.defaultBranch?.trim() || 'main';
|
|
545
|
+
const target = runProcess('git', ['rev-parse', '--verify', `refs/heads/${targetBranch}`], {
|
|
546
|
+
cwd: repository.canonicalRoot,
|
|
547
|
+
timeoutMs: 10_000,
|
|
548
|
+
maxOutputBytes: 100_000,
|
|
549
|
+
});
|
|
550
|
+
if (!head.ok || !head.stdout.trim() || !target.ok || !target.stdout.trim()) {
|
|
551
|
+
return {
|
|
552
|
+
safeToCancel: false,
|
|
553
|
+
state: 'source_state_unknown',
|
|
554
|
+
path,
|
|
555
|
+
detail: `Managed worktree HEAD or target branch ${targetBranch} could not be resolved; destructive stale-Work cancellation is fenced.`,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
const unique = runProcess('git', ['rev-list', '--count', `${target.stdout.trim()}..${head.stdout.trim()}`], {
|
|
559
|
+
cwd: repository.canonicalRoot,
|
|
560
|
+
timeoutMs: 10_000,
|
|
561
|
+
maxOutputBytes: 100_000,
|
|
562
|
+
});
|
|
563
|
+
if (!unique.ok || !/^\d+$/.test(unique.stdout.trim())) {
|
|
564
|
+
return {
|
|
565
|
+
safeToCancel: false,
|
|
566
|
+
state: 'source_state_unknown',
|
|
567
|
+
path,
|
|
568
|
+
detail: 'Managed worktree unique-commit state could not be proven; destructive stale-Work cancellation is fenced.',
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
if (Number(unique.stdout.trim()) > 0) {
|
|
572
|
+
return {
|
|
573
|
+
safeToCancel: false,
|
|
574
|
+
state: 'unintegrated_commits',
|
|
575
|
+
path,
|
|
576
|
+
detail: `Managed worktree HEAD has ${unique.stdout.trim()} commit(s) not reachable from ${targetBranch}; explicit semantic adoption or cleanup authority is required.`,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
safeToCancel: true,
|
|
581
|
+
state: 'clean_integrated',
|
|
582
|
+
path,
|
|
583
|
+
detail: `Managed worktree is clean and its HEAD is already reachable from ${targetBranch}.`,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function staleWorkCandidateReason(source: StaleWorkSourceInspection): string {
|
|
588
|
+
return source.safeToCancel
|
|
589
|
+
? `Nonterminal WorkContract exceeded the explicit maintenance age threshold and has no active Plan, Requirement, Schedule, or Controller authority. ${source.detail} Explicit full maintenance may cancel Work metadata while retaining durable evidence.`
|
|
590
|
+
: `Protected stale Work: no active lifecycle authority remains, but repository source preservation is required. ${source.detail} The Work must remain nonterminal until source is explicitly adopted, delivered, or cleanup is semantically authorized.`;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function scanRetainedWorktreeCandidates(
|
|
594
|
+
repository: RuntimeMaintenanceRepository,
|
|
595
|
+
controllerHome: string,
|
|
596
|
+
maxCandidates: number,
|
|
597
|
+
): RuntimeMaintenanceCandidate[] {
|
|
598
|
+
const worktreePaths = gitWorktreePaths(repository.canonicalRoot);
|
|
599
|
+
if (worktreePaths.length === 0) return [];
|
|
600
|
+
const normalizedInventory = new Set(worktreePaths.map((path) => resolve(path)));
|
|
601
|
+
const currentContracts = readWorkContractStore({ controllerHome, repoId: repository.repoId }).contracts;
|
|
602
|
+
const currentOwnedPaths = new Set(
|
|
603
|
+
currentContracts
|
|
604
|
+
.map((contract) => contract.worktreeRef?.trim())
|
|
605
|
+
.filter((path): path is string => Boolean(path))
|
|
606
|
+
.map((path) => resolve(path)),
|
|
607
|
+
);
|
|
608
|
+
const candidates: RuntimeMaintenanceCandidate[] = [];
|
|
609
|
+
const retained = listRetainedMigratedWork(controllerHome, repository.repoId);
|
|
610
|
+
const retainedByPath = new Map(retained.map((entry) => [resolve(entry.work.worktreeRef!), entry]));
|
|
611
|
+
|
|
612
|
+
for (const path of worktreePaths) {
|
|
613
|
+
const normalized = resolve(path);
|
|
614
|
+
if (normalized === resolve(repository.canonicalRoot) || currentOwnedPaths.has(normalized)) continue;
|
|
615
|
+
const migrated = retainedByPath.get(normalized);
|
|
616
|
+
if (migrated) {
|
|
617
|
+
const completed = migrated.work.status === 'completed';
|
|
618
|
+
candidates.push({
|
|
619
|
+
kind: 'retained_migrated_work',
|
|
620
|
+
id: migrated.work.workId,
|
|
621
|
+
path: normalized,
|
|
622
|
+
status: migrated.work.status,
|
|
623
|
+
safe: false,
|
|
624
|
+
reason: completed
|
|
625
|
+
? 'A terminal Work from a retired Controller Home still owns a Git worktree and requires completion/cleanup reconciliation under the current Controller before lifecycle health can be reported.'
|
|
626
|
+
: 'A terminal Work from a retired Controller Home still owns preserved implementation state in a Git worktree. Review and explicitly resume or supersede it under current authority; automatic maintenance must not delete it.',
|
|
627
|
+
suggestedAction: 'full_maintenance_pass',
|
|
628
|
+
ownershipStatus: 'explicit',
|
|
629
|
+
sourceControllerHome: migrated.sourceHome,
|
|
630
|
+
objective: migrated.work.objective,
|
|
631
|
+
continuationPrompt: migrated.work.continuationPrompt,
|
|
632
|
+
disposition: completed ? 'completion_reconciliation' : 'resume_or_supersede_review',
|
|
633
|
+
});
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (!looksLikeForgeManagedWorktree(normalized)) continue;
|
|
637
|
+
candidates.push({
|
|
638
|
+
kind: 'unowned_managed_worktree',
|
|
639
|
+
id: `managed-worktree:${basename(normalized)}`,
|
|
640
|
+
path: normalized,
|
|
641
|
+
safe: false,
|
|
642
|
+
reason: 'Git reports a Forge-managed worktree for this repository, but the current Controller has no WorkContract ownership and no retained migration identity for it. Ownership must be reconciled before lifecycle health can be reported.',
|
|
643
|
+
suggestedAction: 'full_maintenance_pass',
|
|
644
|
+
ownershipStatus: 'unknown',
|
|
645
|
+
disposition: 'ownership_reconciliation',
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// A retained migrated Work whose referenced path still exists but is no longer
|
|
650
|
+
// in Git inventory remains preservation debt rather than disappearing silently.
|
|
651
|
+
for (const migrated of retained) {
|
|
652
|
+
const normalized = resolve(migrated.work.worktreeRef!);
|
|
653
|
+
if (normalizedInventory.has(normalized) || currentOwnedPaths.has(normalized)) continue;
|
|
654
|
+
// Legacy WorkContract records sometimes stored the repository checkout/root
|
|
655
|
+
// in worktreeRef even though it was not a Controller-managed worktree. Such
|
|
656
|
+
// historical references remain audit evidence, but must not turn every old
|
|
657
|
+
// repository root into current lifecycle debt. Only known Forge/RepoHarness
|
|
658
|
+
// managed-worktree storage can represent preserved implementation here.
|
|
659
|
+
if (!looksLikeForgeManagedWorktree(normalized)) continue;
|
|
660
|
+
// A removed legacy campaign may leave an empty managed-storage directory
|
|
661
|
+
// behind after its Git metadata and contents are already gone. That is not
|
|
662
|
+
// preserved implementation and must not block lifecycle health. A retained
|
|
663
|
+
// path outside current Git inventory only remains lifecycle debt when its
|
|
664
|
+
// worktree .git marker still exists for manual reconciliation.
|
|
665
|
+
if (!existsSync(join(normalized, '.git'))) continue;
|
|
666
|
+
const completed = migrated.work.status === 'completed';
|
|
667
|
+
candidates.push({
|
|
668
|
+
kind: 'retained_migrated_work',
|
|
669
|
+
id: migrated.work.workId,
|
|
670
|
+
path: normalized,
|
|
671
|
+
status: migrated.work.status,
|
|
672
|
+
safe: false,
|
|
673
|
+
reason: 'A terminal Work retained by a migrated Controller Home still has preserved filesystem state, but Git no longer reports it as a registered worktree. Manual lifecycle reconciliation is required; automatic cleanup is forbidden.',
|
|
674
|
+
suggestedAction: 'full_maintenance_pass',
|
|
675
|
+
ownershipStatus: 'explicit',
|
|
676
|
+
sourceControllerHome: migrated.sourceHome,
|
|
677
|
+
objective: migrated.work.objective,
|
|
678
|
+
continuationPrompt: migrated.work.continuationPrompt,
|
|
679
|
+
disposition: completed ? 'completion_reconciliation' : 'resume_or_supersede_review',
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return candidates.slice(0, maxCandidates);
|
|
684
|
+
}
|
|
685
|
+
|
|
392
686
|
function normalizedOptions(options: RuntimeMaintenanceOptions = {}): Required<RuntimeMaintenanceOptions> {
|
|
393
687
|
return {
|
|
394
688
|
minAgeMinutes: clampNumber(options.minAgeMinutes, DEFAULT_MIN_AGE_MINUTES, 0, 7 * 24 * 60),
|
|
@@ -397,6 +691,29 @@ function normalizedOptions(options: RuntimeMaintenanceOptions = {}): Required<Ru
|
|
|
397
691
|
};
|
|
398
692
|
}
|
|
399
693
|
|
|
694
|
+
function activeWorkAuthorityRefs(
|
|
695
|
+
repository: RuntimeMaintenanceRepository,
|
|
696
|
+
controllerHome: string,
|
|
697
|
+
contract: WorkContract,
|
|
698
|
+
): string[] {
|
|
699
|
+
const refs: string[] = [];
|
|
700
|
+
if (getControllerSession({ controllerHome, repoId: repository.repoId }, contract.workId)) refs.push('controller_session');
|
|
701
|
+
const activePlans = listPlanContracts({ controllerHome, repoId: repository.repoId, status: 'active', limit: 100 });
|
|
702
|
+
const plan = activePlans.find((candidate) => candidate.planId === contract.planId || candidate.steps.some((step) => step.workId === contract.workId));
|
|
703
|
+
if (plan) refs.push(`plan:${plan.planId}`);
|
|
704
|
+
if (contract.requirementId) {
|
|
705
|
+
const requirement = readRequirement({ controllerHome }, contract.requirementId)?.value;
|
|
706
|
+
if (requirement && !['done', 'cancelled'].includes(requirement.state)) refs.push(`requirement:${requirement.requirementId}:${requirement.state}`);
|
|
707
|
+
}
|
|
708
|
+
const boundSchedule = listSchedules(controllerHome, repository.repoId).find((schedule) => {
|
|
709
|
+
if (!schedule.enabled) return false;
|
|
710
|
+
const args = schedule.action.arguments as Record<string, unknown> | undefined;
|
|
711
|
+
return args?.work_id === contract.workId;
|
|
712
|
+
});
|
|
713
|
+
if (boundSchedule) refs.push(`schedule:${boundSchedule.scheduleId}`);
|
|
714
|
+
return refs;
|
|
715
|
+
}
|
|
716
|
+
|
|
400
717
|
function scanStaleWorkContractCandidates(
|
|
401
718
|
repository: RuntimeMaintenanceRepository,
|
|
402
719
|
controllerHome: string,
|
|
@@ -411,20 +728,109 @@ function scanStaleWorkContractCandidates(
|
|
|
411
728
|
return { contract, ageMinutes };
|
|
412
729
|
})
|
|
413
730
|
.filter(({ ageMinutes }) => ageMinutes >= options.minAgeMinutes)
|
|
731
|
+
.flatMap(({ contract, ageMinutes }) => {
|
|
732
|
+
const authorityRefs = activeWorkAuthorityRefs(repository, controllerHome, contract);
|
|
733
|
+
// A live Plan/Requirement/Schedule/Controller reference is lifecycle authority,
|
|
734
|
+
// not maintenance debt. Keep the Work fenced until that authority disappears;
|
|
735
|
+
// if it later becomes unowned, the next scan will surface it as stale.
|
|
736
|
+
if (authorityRefs.length > 0) return [];
|
|
737
|
+
return [{ contract, ageMinutes, source: inspectStaleWorkRepositorySource(repository, contract) }];
|
|
738
|
+
})
|
|
414
739
|
.sort((left, right) => right.ageMinutes - left.ageMinutes)
|
|
415
740
|
.slice(0, options.maxCandidates)
|
|
416
|
-
.map(({ contract, ageMinutes }) => ({
|
|
741
|
+
.map(({ contract, ageMinutes, source }) => ({
|
|
417
742
|
kind: 'stale_work_contract' as const,
|
|
418
743
|
id: contract.workId,
|
|
744
|
+
path: source.path,
|
|
419
745
|
status: contract.status,
|
|
420
|
-
safe:
|
|
421
|
-
reason:
|
|
746
|
+
safe: source.safeToCancel,
|
|
747
|
+
reason: staleWorkCandidateReason(source),
|
|
422
748
|
ageMinutes,
|
|
423
749
|
suggestedAction: 'full_maintenance_pass' as const,
|
|
424
750
|
ownershipStatus: 'explicit' as const,
|
|
751
|
+
sourceState: source.state,
|
|
752
|
+
disposition: source.safeToCancel ? undefined : ('source_preservation_required' as const),
|
|
425
753
|
}));
|
|
426
754
|
}
|
|
427
755
|
|
|
756
|
+
export function applyStaleWorkContractMaintenanceCandidate(
|
|
757
|
+
repository: RuntimeMaintenanceRepository,
|
|
758
|
+
controllerHome: string,
|
|
759
|
+
candidate: RuntimeMaintenanceCandidate,
|
|
760
|
+
): RuntimeMaintenanceCandidate & { applied: boolean; result: string } {
|
|
761
|
+
const work = getWorkContract({ controllerHome, repoId: repository.repoId }, candidate.id);
|
|
762
|
+
if (!work || isTerminalWorkContractStatus(work.status)) {
|
|
763
|
+
return { ...candidate, applied: false, result: 'already_terminal' };
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// The stale scan is discovery only. Re-evaluate every non-Controller authority
|
|
767
|
+
// while holding the canonical ControllerSession lock, then perform the Work
|
|
768
|
+
// transition before releasing it. A newer controller_claim therefore either
|
|
769
|
+
// wins the lock first and fences this mutation, or starts only after an already
|
|
770
|
+
// completed maintenance terminalization; there is no check-then-cancel gap.
|
|
771
|
+
const fenced = withControllerSessionTerminalizationFence(
|
|
772
|
+
{ controllerHome, repoId: repository.repoId },
|
|
773
|
+
{ workId: work.workId, actor: `runtime-maintenance-terminalize:${work.workId}` },
|
|
774
|
+
() => {
|
|
775
|
+
const current = getWorkContract({ controllerHome, repoId: repository.repoId }, work.workId);
|
|
776
|
+
if (!current || isTerminalWorkContractStatus(current.status)) {
|
|
777
|
+
return { ...candidate, applied: false, result: 'already_terminal' };
|
|
778
|
+
}
|
|
779
|
+
const authorityRefs = activeWorkAuthorityRefs(repository, controllerHome, current);
|
|
780
|
+
if (authorityRefs.length > 0) {
|
|
781
|
+
return {
|
|
782
|
+
...candidate,
|
|
783
|
+
applied: false,
|
|
784
|
+
result: `work_authority_became_active:${authorityRefs.join(',')}`,
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
// Discovery and mutation are separated by an arbitrary Controller/MCP delay.
|
|
788
|
+
// Re-probe the live worktree while the canonical ControllerSession fence is
|
|
789
|
+
// held so neither a newer Controller claim nor a late source write can be
|
|
790
|
+
// raced by stale maintenance terminalization.
|
|
791
|
+
const source = inspectStaleWorkRepositorySource(repository, current);
|
|
792
|
+
if (!source.safeToCancel) {
|
|
793
|
+
return {
|
|
794
|
+
...candidate,
|
|
795
|
+
path: source.path ?? candidate.path,
|
|
796
|
+
safe: false,
|
|
797
|
+
reason: staleWorkCandidateReason(source),
|
|
798
|
+
sourceState: source.state,
|
|
799
|
+
disposition: 'source_preservation_required' as const,
|
|
800
|
+
applied: false,
|
|
801
|
+
result: `work_source_preserved:${source.state}`,
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
transitionWorkContractPhase({ controllerHome, repoId: repository.repoId }, current.workId, {
|
|
805
|
+
phase: 'cleanup',
|
|
806
|
+
status: 'cancelled',
|
|
807
|
+
state: 'skipped',
|
|
808
|
+
summary: 'Cancelled by explicit full maintenance stale Work cleanup after proving no unique live repository source; durable evidence retained.',
|
|
809
|
+
evidenceRefs: current.evidenceRefs,
|
|
810
|
+
});
|
|
811
|
+
return {
|
|
812
|
+
...candidate,
|
|
813
|
+
path: source.path ?? candidate.path,
|
|
814
|
+
safe: true,
|
|
815
|
+
reason: staleWorkCandidateReason(source),
|
|
816
|
+
sourceState: source.state,
|
|
817
|
+
disposition: undefined,
|
|
818
|
+
applied: true,
|
|
819
|
+
result: 'work_contract_cancelled_evidence_retained',
|
|
820
|
+
};
|
|
821
|
+
},
|
|
822
|
+
);
|
|
823
|
+
if (!fenced.allowed) {
|
|
824
|
+
const generation = fenced.owner?.claimGeneration;
|
|
825
|
+
return {
|
|
826
|
+
...candidate,
|
|
827
|
+
applied: false,
|
|
828
|
+
result: `work_terminalization_fenced:${fenced.reason}${typeof generation === 'number' ? `:claim_generation=${generation}` : ''}`,
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
return fenced.value;
|
|
832
|
+
}
|
|
833
|
+
|
|
428
834
|
function scanStaleEditSessionCandidates(
|
|
429
835
|
repository: RuntimeMaintenanceRepository,
|
|
430
836
|
controllerHome: string,
|
|
@@ -520,7 +926,8 @@ export function buildRuntimeMaintenanceStatus(
|
|
|
520
926
|
const tempCandidates = scanRuntimeTempCandidates(repository, normalized.maxCandidates);
|
|
521
927
|
const staleWorkCandidates = scanStaleWorkContractCandidates(repository, controllerHome, normalized);
|
|
522
928
|
const staleEditCandidates = scanStaleEditSessionCandidates(repository, controllerHome, normalized);
|
|
523
|
-
const
|
|
929
|
+
const retainedWorktreeCandidates = scanRetainedWorktreeCandidates(repository, controllerHome, normalized.maxCandidates);
|
|
930
|
+
const candidates = [...localJobCandidates, ...storageCandidates, ...staleWorkCandidates, ...staleEditCandidates, ...retainedWorktreeCandidates, ...tempCandidates].slice(0, normalized.maxCandidates);
|
|
524
931
|
const summary = summarize(candidates);
|
|
525
932
|
const blockingCandidates = candidates.filter((candidate) => candidate.kind !== 'stale_runtime_temp_entry');
|
|
526
933
|
const readyForExecution = storage.report?.readyForExecution === true
|
|
@@ -556,6 +963,7 @@ export function buildRuntimeMaintenanceStatus(
|
|
|
556
963
|
'Pending approvals are not cancelled unless cancel_pending_approvals is explicitly enabled.',
|
|
557
964
|
'System temp cleanup only removes direct forge-prefixed children of approved temp roots after a 24-hour retention period and a fresh process-occupancy check.',
|
|
558
965
|
'Runtime lifecycle changes and source repair are outside the runtime maintenance executor.',
|
|
966
|
+
'Retained or unowned managed worktrees are review-only lifecycle blockers. Runtime maintenance never deletes or modifies their preserved implementation state.',
|
|
559
967
|
],
|
|
560
968
|
};
|
|
561
969
|
}
|
|
@@ -774,18 +1182,7 @@ export function applyRuntimeMaintenance(
|
|
|
774
1182
|
};
|
|
775
1183
|
}
|
|
776
1184
|
if (candidate.kind === 'stale_work_contract') {
|
|
777
|
-
|
|
778
|
-
if (!work || isTerminalWorkContractStatus(work.status)) {
|
|
779
|
-
return { ...candidate, applied: false, result: 'already_terminal' };
|
|
780
|
-
}
|
|
781
|
-
transitionWorkContractPhase({ controllerHome, repoId: repository.repoId }, work.workId, {
|
|
782
|
-
phase: 'cleanup',
|
|
783
|
-
status: 'cancelled',
|
|
784
|
-
state: 'skipped',
|
|
785
|
-
summary: 'Cancelled by explicit full maintenance stale Work cleanup; durable evidence retained.',
|
|
786
|
-
evidenceRefs: work.evidenceRefs,
|
|
787
|
-
});
|
|
788
|
-
return { ...candidate, applied: true, result: 'work_contract_cancelled_evidence_retained' };
|
|
1185
|
+
return applyStaleWorkContractMaintenanceCandidate(repository, controllerHome, candidate);
|
|
789
1186
|
}
|
|
790
1187
|
if (candidate.kind === 'stale_edit_session') {
|
|
791
1188
|
const cleaned = cleanupEditSession(repository.canonicalRoot, candidate.id, {
|
|
@@ -61,7 +61,6 @@ function failingJobClass(input: CapabilityRecoveryInput): RecoveryClass {
|
|
|
61
61
|
function suggestedActionsForClass(recoveryClass: RecoveryClass): RecoveryActionDescriptor[] {
|
|
62
62
|
if (recoveryClass === 'platform_blocked' || recoveryClass === 'dirty_worktree_conflict') return [RECOVERY_ACTIONS.createPatchHandoff];
|
|
63
63
|
if (recoveryClass === 'auth_required') return [RECOVERY_ACTIONS.workspaceAuthLoginPrepare];
|
|
64
|
-
if (recoveryClass === 'browser_domain_grant_required') return [RECOVERY_ACTIONS.browserDomainAccessPreview];
|
|
65
64
|
if (recoveryClass === 'external_filesystem_grant_required') return [RECOVERY_ACTIONS.externalFilesystemGrantPreview];
|
|
66
65
|
if (recoveryClass === 'agent_runtime_failure') return [RECOVERY_ACTIONS.reconcileJobs];
|
|
67
66
|
if (recoveryClass === 'source_defect_suspected' || recoveryClass === 'external_lifecycle_required') return [];
|